page header

Performance: If statements elimineren


Geplaatst door martijn_brekhof op 2012-01-13 11:35:21 | Permanente link | Categorie: Programmeren | Reacties: 0

Ik was in de blog Performance: lazy evaluation begonnen met het optimaliseren van onderstaand loopje.

for ( bug_number = 0; bug_number < MAX_BUGS; bug_number++ )
{
  for ( employee_id = 0; employee_id < MAX_EMPLOYEES; employee_id++ )
  {
    if( ( employee_employed[employee_id] == false ) && (employee_id == bug_assignee[bug_number]) )
    {
      printf("Bug %d assigned to non employed person\n", bug_number);
    }
  }
}

Door simpelweg de condities in de if-statement om te draaien behaalde ik een winst van 76%. In de blog Loopjes, loopjes, loopjes ging ik verder in op bovenstaand stukje code waar ik vooral de for loop onder de loep nam. Door simpelweg de binnenste loop alleen uit te voeren als het een medewerker betreft die niet meer in dienst is, behaalde ik een extra winst van 1 seconde. De uiteindelijke code laat ik hier voor het gemak nog even zien:

for ( employee_id = 0; employee_id < MAX_EMPLOYEES; employee_id++ )
{
  if (employee_employed[employee_id] == false)
  {
    for ( bug_number = 0; bug_number < MAX_BUGS; bug_number++ )
    {
      if (employee_id == bug_assignee[bug_number])
      {
        printf("Bug %d assigned to non employed person\n", bug_number);
      }
    }
  }
}

Ik zal nu de (voor mij) laatste slag in de optimalisatie van de code behandelen. Door het herschrijven van de code viel me namelijk iets anders op. Wellicht dat het je al was opgevallen, maar de twee if-statements gebruiken allebei de variabele employee_id.
De lijst bug_assignee geeft een employee_id terug en de lijst employee_employed geeft op basis van employee_id aan of deze nog in dienst is. Ik had in de blog Performance: lazy evaluation al aangegeven dat een bug altijd toegewezen is aan één persoon. Oftewel bug_assignee geeft altijd een valide medewerkersnummer terug (employee_id). We hoeven hier dus helemaal niet expliciet op te controleren en kunnen de twee for loops herschrijven tot één:

for ( bug_number = 0; bug_number < MAX_BUGS; bug_number++ )
{
  employee_id = bug_assignee[bug_number];
  if( employee_employed[employee_id] == false )
  {
    printf("Bug %d assigned to non employed person\n", bug_number);
  }
}

De code is nu een stuk leesbaarder (vind ik in ieder geval) en doet er nog maar 0.3 seconden over. Dit is dus uiteindelijk een winst van 99% ten opzichte van de code in mijn blog Performance: lazy evaluation.

Geluiden uit de keuken van Fedora (december)


Geplaatst door martijn_brekhof op 2012-01-02 13:54:42 | Permanente link | Categorie: Systeembeheer | Reacties: 0

In de keuken van Debian werd er niets besproken dat ik noemenswaardig vond en daarom beperk ik me deze keer tot alleen de geluiden uit de keuken van Fedora.

Andreas Tunek vraagt zich af of Fedora 17 weer bootable kan worden gemaakt op Macs (link). Fedora 15 werkte wel op Macs maar Fedora 16 weigert te installeren of na een upgrade van 15 te booten. Adam Williamson geeft aan dat hiervoor EFI ondersteuning voor het booten en voor anaconda (installer) moet worden ontwikkeld (link).

Giovanni Campagna vindt dat Fedora een goede tool mist om applicaties mee te installeren. Hij doelt hiermee op een tool dat het voor eindgebruikers makkelijk maakt om een gewenste applicatie te vinden en noemt als voorbeeld Ubuntu Software Center (link). Na veel positieve feedback heeft Giovanni Campagna besloten er een officiële feature voor Fedora 17 van te maken (link).

Eric Smith geeft aan dat hij bezig is om MATE voor Fedora te packagen. MATE is een fork van GNOME 2 en hij vraagt zich af of er veel animo is voor MATE. Hoewel er nogal wat gebruikers van Fedora zijn die GNOME 3 maar niets vinden is er ook maar weinig animo voor MATE. Dit komt vooral omdat het er niet naar uitziet dat MATE erg actief wordt ontwikkeld (link).

Geluiden uit de keukens van Debian en Fedora (november)


Geplaatst door martijn_brekhof op 2011-12-06 13:58:31 | Permanente link | Categorie: Systeembeheer | Reacties: 0

Debian

Bastien Roucaries laat weten dat het verplaatsen van de directory /tmp van disk naar tmpfs voor hem problemen oplevert met beeldverwerkingsoftware. De algemene reactie is dat /tmp niet bedoeld is om grote bestanden in op te slaan en dat de software het op een andere locatie moet opslaan, bijvoorbeeld in /var/tmp. Echter, het probleem met /var/tmp is dat deze niet bij elke boot wordt leeggemaakt. Bastien stelt voor om een nieuwe directory te introduceren bijvoorbeeld /var/tmp/nonpersistent (link).

Yaroslav Halchenko vraagt zich af of het is toegestaan om onder /usr/bin subdirectories te hebben (link). Dit is algemeen niet toegestaan alhoewel er twee uitzonderingen zijn voor mailutils. De FHS is niet erg duidelijk in de bewoording waardoor er nogal wat discussie ontstaat of de FHS het nu wel of niet verbiedt. Cyril Brulebois maakt duidelijk dat de nieuwe FSH 3.0 standaard het expliciet verbiedt (link). De juiste locatie voor extra executables onder Debian zou zijn /usr/lib/<PACKAGENAAM>. Maar andere distro's gebruiken hier /usr/libexec voor. Debian was hier initieel op tegen omdat het onduidelijk is wat de bedoeling van /usr/libexec is. Het lijkt erop dat het in de FHS is opgenomen en dat Debian /usr/libexec zal gaan gebruiken voor executables die niet direct door gebruikers maar door andere executables worden aangeroepen.

Ben Hutchings vindt dat het tijd is om ondersteuning voor de 486 architectuur te stoppen. Reden is dat er nog maar weinig gebruik wordt gemaakt van 486 hardware. Door als minimale eis 586 te nemen kan er beter geoptimaliseerd worden en dat zal algemeen de performance verbeteren (link). Er zijn er natuurlijk wel een paar gebruikers die nog oude hardware draaien en bezwaar maken. Maar de meesten lijken het eens te zijn. Wellicht dat Wheezy (de volgende stable) al als minimale eis 586 krijgt.

Fedora

Daniel J Walsh doet een voorstel om services die met root-rechten draaien te verbieden files aan te maken onder /tmp en/of /var/tmp (link). Hier kan namelijk door "gewone" gebruikers misbruik van worden gemaakt. Wanneer de gebruiker vooraf de file die de service gebruikt aanmaakt onder /tmp (iedereen mag daar immers files aanmaken) en de service deze file inleest en uitvoert kan de gebruiker hiermee root-rechten verkrijgen. Door in de file bijvoorbeeld een script te verwerken zal het script namelijk met de rechten van de service draaien. De oplossing is om via systemd ieder proces een privé directory te geven onder /tmp en/of /var/tmp. Deze directories zullen dan een willekeurige naam krijgen. Hierdoor is de kans op misbruik minimaal doordat het erg moeilijk is vooraf te voorspellen welke directory wordt gebruikt door een bepaalde service.

Loopjes, loopjes, loopjes


Geplaatst door martijn_brekhof op 2011-11-22 12:41:37 | Permanente link | Categorie: Programmeren | Reacties: 0

Ditmaal een voorbeeld van een goedbedoelde optimalisatie techniek die geen verbetering maar een verslechtering opleverde. De optimalisatie houdt in dat je het aantal keer dat een for-loop moet worden opgebouwd minimaliseert. Dit speelt vooral wanneer je loops in loops codeert, zoals de twee loops uit mijn vorige blog. Hier als opfrisser de geoptimaliseerde code.

for ( bug_number = 0; bug_number < MAX_BUGS; bug_number++ )
{
  for ( employee_id = 0; employee_id < MAX_EMPLOYEES; employee_id++ )
  {
    if( ( employee_id == bug_assignee[bug_number] ) && 
        ( employee_employed[employee_id] == false ) )
    {
        printf("Bug %d assigned to non employed person\n", bug_number);
    }
  }
}

Als we kijken naar de buitenste for-loop dan wordt voor elke bug een for-loop opgestart die de toegewezen medewerker opspeurt. De binnenste for-loop wordt dus voor elke bug opgebouwd en dit opbouwen kost CPU cycles. Er valt winst te behalen als we dit opbouwen kunnen verminderen.

In ons geval zal MAX_EMPLOYEES meestal vele malen kleiner zijn dan MAX_BUGS. Het zou in ons geval dus lonen om de twee for-loops om te draaien. Dan hoeft er namelijk nog maar MAX_EMPLOYEES keer een for-loop te worden opgebouwd. Oftewel de code wordt als volgt:

for ( employee_id = 0; employee_id < MAX_EMPLOYEES; employee_id++ )
{
  for ( bug_number = 0; bug_number < MAX_BUGS; bug_number++ )
  {
    if( ( employee_id == bug_assignee[bug_number] ) && 
        ( employee_employed[employee_id] == false ) )
    {
        printf("Bug %d assigned to non employed person\n", bug_number);
    }
  }
}

Je zal helaas merken dat de code langzamer draait dan de voorgaande versie. In mijn geval 2 seconden langzamer. Dit komt omdat bug_assignee[bug_number] nu in elke iteratie van de binnenste loop een ander array element aanwijst. Bij de vorige bleef deze constant tijdens de binnenste loop en hierdoor maak je efficiënter gebruik van je CPU registers.

Dit was niet wat ik beloofd had, namelijk een optimalisatie die ervoor zorgt dat de code minder dan 1 seconde nodig heeft. Ik ga dat echter nu nog niet verklappen. Maar ik zal jullie ook niet achterlaten met een slechter werkende code. Er valt namelijk nog wel een seconde winst te behalen door op te merken dat het doel is om alleen voor medewerkers die niet in dienst zijn de bugs te tonen. We hoeven dus niet voor iedere medewerker de lijst van bugs langs te lopen. Als we de code aanpassen als volgt:

for ( employee_id = 0; employee_id < MAX_EMPLOYEES; employee_id++ )
{
  if (employee_employed[employee_id] == false)
  {
    for ( bug_number = 0; bug_number < MAX_BUGS; bug_number++ )
    {
      if (employee_id == bug_assignee[bug_number])
      {
        printf("Bug %d assigned to non employed person\n", bug_number);
      }
    }
  }
}

Dan wordt de code er zeker niet leesbaarder op maar haal ik wel een winst van 1 seconde ten opzichte van de geoptimaliseerde code aan het begin van deze blog. Wellicht dat bovenstaande aanpassing jullie al op een idee brengt om de code nog sneller te maken....

Geluiden uit de keukens van Fedora en Debian (oktober)


Geplaatst door martijn_brekhof op 2011-11-01 16:08:39 | Permanente link | Categorie: Systeembeheer | Reacties: 0

Debian

In het Debiankamp vraagt Thomas Hood zich af of Bash scripts kunnen/moeten worden geëlimineerd (link). Hij geeft een aantal redenen die al snel van tafel worden geveegd. De enige reden die overblijft is dat Dash (Debian Amquist Shell) minder afhankelijkheden heeft. Men vindt dit niet voldoende om Bash in de ban te doen.

Manjul Apratim merkt op dat Sid (unstable) eigenlijk erg bruikbaar is en vraagt zich af wat het verschil is tussen Sid en de stabiele versie van Debian. Daarnaast vraagt ie zich af waarom Sid nog steeds erg achter loopt met betrekking tot GNOME (link). Jonathan Nieder geeft aan dat unstable vooral betekent dat afhankelijkheden van pakketten wellicht nog niet bestaan. Er mag in principe geen instabiele software aan Sid worden toegevoegd. In dat opzicht is Sid dus vaak goed bruikbaar. Josselin Mouette maakt duidelijk dat GNOME 3 wel al in de experimentele versie zit. Echter, het kan pas in Sid worden opgenomen wanneer "alle" pakketten waaruit GNOME 3 bestaat in Sid kunnen worden opgenomen. Aangezien GNOME 3 uit heel veel pakketten bestaat duurt dit allemaal wat langer.

De database die de tijdzones bijhoudt is uitgezet (link). Dit kan grote gevolgen hebben voor localisatie van de tijd op Linux systemen. Maar men maakt zich er niet zo druk over aangezien er al een backup is gemaakt en de database op een andere locatie alweer online is (link).

Eric Dorland maakt bekend dat hij automake versie 1.7 zal verwijderen (link). Paul Wise vraagt waarom dan niet eerst versie 1.4 wordt verwijderd. Eric durft daar niet aan omdat er wellicht gebruikers zijn die nog oude software willen bouwen die alleen te bouwen is met automake versie 1.4.

Marco d'Itri vraagt zich af hoe complex het zal zijn om het idee van Fedora om alle software naar /usr te verplaatsen ook in Debian te implementeren (link) (link). Het doel van de directories /bin, /sbin, en /lib is om software te bevatten die vervolgens kan worden gebruikt om /usr te mounten. Het idee is om deze taak over te hevelen naar de initrd (initiele ramdisk). Men geeft aan dat er een hoop voorwaarden zijn maar deze lijken vooralsnog niet onoverkomelijk.

Fedora

Bij Fedora maakt Kevin Fenzy bekend dat iedere gebruiker van het Fedora Account System (FAS) zijn of haar wachtwoord moet wijzigen en een nieuwe publieke SSH sleutel moet uploaden (link). De reden is niet dat de systemen van Fedora zijn gehackt maar is in navolging van het grote aantal recente inbraken op andere belangrijke systemen (e.g. linuxfoundation.org, kernel.org, mysql.net). Men heeft weinig problemen met het wijzigen van het wachtwoord, maar het wijzigen van de sleutels vindt men meer problematisch. Dit aangezien vele één sleutel gebruiken voor meerdere diensten. Dit vereist dan dat men meerdere sleutels moet gaan beheren.

Daniel Drake geeft aan dat er nogal een verschil zit tussen de fontgroottes van Fedora 14 en Fedora 16. Hij vraagt zich af waardoor dit komt (link). Het blijkt dat deze hardcoded in GNOME zitten (link). Dit is zo gedaan omdat men niet kan vertrouwen op de schermresolutie in dotch per inch (DPI) die Xorg aangeeft. Dit komt weer omdat de correcte DPI niet eenduidig kan worden berekend omdat hardware leveranciers niet altijd correct de beeldscherm afmeting doorgeven (link).

Er wordt natuurlijk nog flink ontwikkeld aan systemd en dit levert de nodige discussies op. Meestal gaat dit over hoe het een en ander moet via systemd. Maar er wordt bijvoorbeeld ook gediscussieerd over de opstarttijd van CD (live) van een Fedora 16 en in vergelijking met Knoppix (link). Lennart Poettering geeft aan dat het wel een beetje appels met peren vergelijken is aangezien Fedora 16 veel enterprise software bevat en opstart terwijl Knoppix veel minder opstart. Desalniettemin is het interessant om te kijken wat er geoptimaliseerd kan worden. Het blijkt dat sommige hardware probes op andere hardware probes staan te wachten die eigenlijk niet afhankelijk zijn van elkaar. Hierdoor duurt het opzetten van bijvoorbeeld LVM erg lang dat wacht totdat alle hardware probes klaar zijn (link).

Ook bij Fedora wordt er druk gediscussieerd over het verplaatsen van /bin, /sbin, en /lib naar /usr en wat voor impact het heeft (link). Algemeen ziet iedereen deze opzet wel zitten. Deze opzet maakt het mogelijk om /usr bijvoorbeeld read-only te mounten en het root filesysteem read-writable. Daarnaast kan dan makkelijk het OS gedeeld worden over meerdere machines door /usr te exporteren.

Performance: lazy evaluation


Geplaatst door martijn_brekhof op 2011-10-12 12:15:19 | Permanente link | Categorie: Programmeren | Reacties: 0

In een vorige blog heb ik laten zien dat de volgorde van if-statements van invloed kan zijn op de prestaties van je programma. Dat dit op meerdere manieren tot uiting kan komen in je code wil ik graag illustreren aan de hand van een ander praktijkvoorbeeld. De volgende code is onderdeel van een routine die een lijst van bugs moet tonen die toegewezen zijn aan medewerkers die niet meer in dienst zijn.

for ( bug_number = 0; bug_number < MAX_BUGS; bug_number++ )
{
  for ( employee_id = 0; employee_id < MAX_EMPLOYEES; employee_id++ )
  {
    if( ( employee_employed[employee_id] == false ) && (employee_id == bug_assignee[bug_number]) )
    {
      printf("Bug %d assigned to non employed person\n", bug_number);
    }
  }
}

De code is geschreven op basis van een zogenaamde "user story" en is recht toe recht aan geprogrammeerd.

De code doet precies wat de gebruiker wilde, het was echter erg traag. Gemiddeld moest de gebruiker 30 seconden wachten op het eerste resultaat. De routine waarin bovenstaande for-loop zat had een aandeel van 28 seconden. Dit was onacceptabel. Het resultaat mocht niet langer dan 10 seconden op zich laten wachten. Laten we eens kijken of we dit kunnen bereiken.

Allereerst neem ik de if-statement onder de loep. In een volgend blog zal ik de code verder analyseren en optimaliseren. De if-statement bevat twee condities, namelijk:

employee_employed[employee_id] == false

en

employee_id == bug_assignee[bug_number]

De condities zijn verbonden via && oftewel een logische AND. Dit betekent dat de gecombineerde conditie alleen slaagt als beide condities waar zijn. Als één van beide condities onwaar is, zal de gecombineerde conditie ook onwaar zijn. Wanneer de conditie voor de && faalt, zal dus de gecombineerde conditie ook falen. Het programma hoeft de conditie na de && dan ook niet te evalueren. Dit wordt door de meeste programmeertalen ondersteund; men noemt dat lazy evaluation. Dit stelt ons in staat om dit stukje code te optimaliseren door hier ook een frequentie-analyse te doen.

De buitenste loop itereert over de lijst van bugs, terwijl de binnenste loop itereert over de lijst van medewerkers. In de binnenste loop gaan we op zoek naar de medewerker die is toegewezen aan de bug en controleren we of de medewerker nog in dienst is. Nu is het zo dat elke bug altijd is toegewezen aan maximaal één medewerker. Oftewel de conditie employee_id == bug_assignee[bug_number] zal in de binnenste loop maar één keer slagen. Er zijn meerdere medewerkers inmiddels niet meer in dienst. Dit betekent dat de conditie employee_employed[employee_id] == false veel vaker slaagt. Dit heeft tot gevolg dat tijdens de binnenste loop een aantal keer onnodig alle twee de condities worden geëvalueerd. Namelijk voor die situaties waarbij de medewerker niet meer in dienst is en hij of zij niet aan de bug is toegewezen. Dit kunnen we voorkomen door de condities om te draaien.

...
if( (employee_id == bug_assignee[bug_number]) && (employee_employed[employee_id] == false) )
...

Deze simpele aanpassing leverde in dit geval een winst op van bijna 23 seconden en voldeed daarmee aan de wensen van de gebruiker.

Er is echter nog veel meer winst te behalen. Het lukte mij om de routine te herschrijven zodat deze minder dan één seconde nodig had. Ik zal hier in een volgend blog verder op ingaan.

Geluiden uit de keukens van Fedora en Debian (september)


Geplaatst door martijn_brekhof op 2011-10-05 13:31:38 | Permanente link | Categorie: Systeembeheer | Reacties: 0

In het Debiankamp vraagt Christoph Anton Mitterer zich af of Debian's kernel gevolgen ondervindt van de kernel.org hack (link). Ben Hutchings laat weten dat hij de ondertekeningen controleert en dat de sleutels niet op kernel.org staan. Naar zijn inziens is de code dus niet gecompromitteerd.

Jon Dowland vraagt zich af of ondersteuning voor encrypted filesystemen niet gebruikersvriendelijker kan (link). Nu is het zo dat je tijdens installatie de optie hebt voor encryptie van het volledige filesysteem. Hij wil graag dat er ook een optie komt om alleen de home directory te versleutelen. Men heeft hier bedenkingen bij, aangezien het de gebruiker wellicht een vals gevoel van veiligheid geeft. Sommige gevoelige informatie zoals sleutels in het geheugen of bijvoorbeeld printjobs onder /var/spool/cups worden dan niet versleuteld. Wanneer de gebruiker zijn systeem laat "hibernaten" worden deze gegevens onversleuteld naar de swapruimte geschreven.

Stefano Zacchiroli (de huidige Debian projectleider) geeft een verslag van zijn presentatie op de GNU Hackers Meeting (GHM) in Parijs (link). Hij heeft daar lang gepraat met Steve White over Debians procedure om bugs door te spelen naar de originele ontwikkelaars (upstream). Veel eindgebruikers melden bugs bij Debian aan die niet door de packager maar door de originele ontwikkelaar moeten worden opgelost. Steve White kwam met het voorstel om het bug tracking systeem en/of het package tracking systeem van Debian aan te passen, zodat upstream ontwikkelaars makkelijker hun software in Debian in de gaten kunnen houden. Hiermee een betere link leggende tussen de eindgebruikers en de originele ontwikkelaars (link).

Didier Raboud geeft een verslag van de Mobile UX discussie sessie (Birds Of a Feather (BoF)) (link). Belangrijkste conclusie is dat er veel initiatieven zijn om Debian op Mobiele platformen (denk aan smartphones en tablets) te krijgen, maar dat deze nu verdeeld zijn over meerdere groepen. Er moet een overkoepelende organisatie komen waardoor de inzet beter kan worden gecoördineerd.

Bij Fedora is er wat ophef ontstaan over het openssh pakket dat in Rawhide (ontwikkelversie van Fedora) na een update niet meer werkte (link). Men vraagt zich af hoe het kan dat een pakket door de tests is gekomen en zo in Rawhide is opgenomen. Jan F. Chadima geeft eerlijk toe dat hij de oorzaak van het probleem is (link). Hij gebruikt wel degelijk tests om het pakket te controleren en begrijpt niet waarom de tests bij hem slaagden terwijl na installatie de software niet werkte. Het is duidelijk dat Rawhide een ontwikkelversie is en stuk kan gaan. Maar men vindt dat er toch beter getest moet worden voordat het wordt opgenomen in Rawhide. Men heeft goede hoop dat testen via het autoQA project dit soort problemen in de toekomst kan voorkomen.

Matyas Selmeci wil graag weten hoe hij prioriteiten in packages kan verwerken zodat yum op bepaalde machines selectief pakketten installeert (link). Richard Hughes raadt hem zif aan en raakt daarmee een gevoelige snaar. Het programma zif is net als yum een package manager. Zif gebruikt echter andere regels om te bepalen welk pakket moet worden geïnstalleerd dan de regels die yum gebruikt. Men vindt dit onacceptabel en men heeft liever dat verbeteringen worden aangedragen bij yum. Daarnaast is men erg sceptisch of zif wel beter is in het afhandelen van pakket-afhankelijkheden dan yum. Hierover blijft Richard Hughes helaas stil. Kevin Kofler legt uit dat de logica in yum om een pakket te kiezen zo complex is geworden, dat het als een random algoritme moet worden beschouwd. De nieuwe package manager zif probeert dit te verbeteren (link).

Tom Callaway laat weten dat de Fedora Packaging Guidelines zijn geactualiseerd (link). Ten behoeve van hardening is een sectie Position Independent Executable (PIE) toegevoegd. Dit maakt het voor aanvallers moeilijker om vooraf te bepalen waar bepaalde code in het geheugen wordt geplaatst. Als dit wel bekend is, kunnen ze ervoor zorgen dat hun eigen code wordt uitgevoerd via een zogenaamde return-to-libc aanval. Daarnaast is er een voorbeeld toegevoegd waarbij expliciete afhankelijkheden zijn toegestaan en md5-peslyak is toegevoegd waarin bundelen van bibliotheken wel is toegestaan.

Paul Wouters doet een oproep om dns-trigger te testen (link). Dit pakket maakt het mogelijk om een (desktop)gebruiker te waarschuwen wanneer DNS niet te vertrouwen is via DNSSEC.

Performance: volgorde van condities


Geplaatst door martijn_brekhof op 2011-09-19 15:17:20 | Permanente link | Categorie: Programmeren | Reacties: 0

Er zijn een heleboel zaken die de performance van een programma kunnen beïnvloeden. Eén daarvan is je code en simpelweg geldt dat hoe minder instructies nodig zijn om iets uit te voeren hoe sneller het programma zal draaien. In deze en toekomstige blogs zal ik wat voorbeelden laten zien waarbij de performance soms iets en soms aanzienlijk verbeterd kan worden door de code net iets anders op te schrijven. Ik gebruik voor de voorbeelden de programmeertaal C. Echter zijn de meeste concepten ook in andere talen van toepassing. Let op, soms zal het herschrijven van de code tot gevolg hebben dat het niet meer voldoet aan een gebruikte programmeerstijl of standaard. Daar maak ik me even geen zorgen over. Het gaat me nu vooral om de performance.

In deze eerste blog omtrent performance wil ik het hebben over de volgorde van if-then-else statements. Bekijk het volgende stukje C-code eens.

#include<stdio.h>

#define AMOUNT_OF_ITERATIONS 1000000000

int func1() { return 10; }
int func2() { return 12; }
int func3() { return 23; }
int func4() { return 11; }

int parse_message(char *msg)
{
  if(strcmp("potato", msg) == 0)
  {
          return func1();
  }
  else if(strcmp("tomato", msg) == 0)
  {
          return func2();
  }
  else if(strcmp("banana", msg) == 0)
  {
          return func3();
  }
  else if(strcmp("orange", msg) == 0)
  {
          return func4();
  }
}


int main(int argc, char* argv[])
{
  unsigned long int i;
  int rate;

  for(i = 0; i < AMOUNT_OF_ITERATIONS; i++)
  {
        if( random()%2 )
      rate = parse_message(argv[1]);
    else
      rate = parse_message(argv[2]);
  }
  printf("Rate=%d\n", rate);
  return 0;
}

De code doet niet echt veel zinnigs. Het is een aangepaste versie van code van een daemon waarin zich een performance probleem voordeed. Maar dit soort code, waarbij op basis van een reeks if-then-else statements (of in een switch) bepaalde code wel of niet wordt aangeroepen, kom ik geregeld tegen. De main functie is alleen bedoeld om het effect van de volgorde van de condities in de functie parse_message te demonstreren. Dat we hier op "willekeurige" basis bepalen of het eerste of het tweede argument moet worden gebruikt is nodig om te voorkomen dat compiler-optimalisaties roet in het eten gooien. De code is als volgt te compileren met gcc.

$ gcc -O3 freqs.c -o freqs

De optie -O3 zorgt ervoor dat gcc zoveel mogelijk optimalisatie zal toepassen. Dit doe ik om te laten zien dat je als programmeur toch ook echt invloed kan hebben. Het programma kan ik als volgt uitvoeren.

$ time ./freqs orange orange
Rate=11

real  0m15.747s
user  0m15.741s
sys   0m0.000s

Ik gebruik het programma time om de hoeveelheid tijd te laten zien die het programma in beslag heeft genomen. Ik roep het programma dus aan met twee keer "orange" als argument. Dit zorgt ervoor dat altijd de laatste conditie slaagt in de functie parse_message. Laten we nu eens het programma uitvoeren met als argumenten potato en potato:

$ time ./freqs potato potato
Rate=10

real  0m6.231s
user  0m6.228s
sys   0m0.000s

Dit is een performance verbetering van maar liefst 60 procent. De winst wordt behaald doordat in de tweede run altijd de eerste conditie slaagt. Bij de eerste run slaagde telkens de laatste conditie en werd dus tijdens iedere aanroep van parse_message ook gecontroleerd of message gelijk is aan potato, tomato, of banana. Deze controle wordt in de tweede run niet gedaan waardoor deze dus vele malen sneller is.

Vaak zullen condities met een bepaalde frequentie slagen gedurende normaal gebruik van het programma. Stel nu dat uit tests blijkt dat het programma voor 90 procent wordt aangeroepen met orange en tomato. Dan zal het dus lonen om de condities voor orange en tomato bovenaan te zetten. Het analyseren van het slagingspercentage van de verschillende condities wordt ook wel een frequentie-analyse genoemd. Soms kun je direct uit de programmacode de frequenties afleiden. Maar meestal zul je met een zogenaamde coverage tool of profiler aan de slag moeten. Je voert dan het programma in een realistische omgeving uit en de coverage tool of profiler laat dan zien hoe vaak bepaalde onderdelen van je code zijn uitgevoerd. Om dit te illustreren zal ik de GNU profiler gebruiken, genaamd gprof. Om gprof te kunnen gebruiken dien je het programma te hercompileren met profile informatie (gcc optie -pg) als volgt:

$ gcc -pg freqs.c -o freqs

Let op dat je geen level 3 optimalisatie meegeeft. Je zult merken dat anders de functies niet meer worden aangeroepen, ze zijn namelijk inline gecompileerd in de main functie. Het gevolg is dat je dan geen frequentie-analyse kan doen.

Door het programma nogmaals uit te voeren wordt er profiling informatie naar het bestand gmon.out geschreven.

$ ./freqs tomato banana

Met gprof kun je vervolgens het bestand gmon.out uitlezen.

$ gprof -b -Q freqs
Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self               self     total           
 time   seconds   seconds    calls   ns/call  ns/call  name    
 67.68      1.78     1.78                              main
 26.62      2.48     0.70 100000000     7.00     8.35  parse_message
  3.04      2.56     0.08 49996838      1.60     1.60  func2
  2.09      2.62     0.06 50003162      1.10     1.10  func3
  0.57      2.63     0.01                              func1

Voor iedere functie wordt de rekentijd en het aantal aanroepen getoond. Voor ons is nu alleen het aantal aanroepen van belang. Dit wordt getoond in de kolom calls. Vervolgens kun je deze informatie gebruiken om de beste volgorde van je if-statements te bepalen. In bovenstaand geval zouden dus eerst de condities voor "tomato" en "banana" moeten komen en dan de overige.

Wanneer je geen functies aanroept in je if-statements kun je beter gcov gebruiken. Als je toch gprof wilt gebruiken kun je dummy-functies toevoegen aan je code.

void func1() { return; }
void func2() { return; }
...

Deze functies dien je vervolgens in je if-statements aan te roepen.

Helaas levert frequentie-analyse niet altijd een performance winst op. Dat het in bovenstaand voorbeeld wel winst oplevert komt voor het grootste deel door de functie strcmp. Deze routine vereist nogal wat CPU tijd. Wanneer de condities een simpele vergelijking bevatten (bijvoorbeeld amount == 1) zul je merken dat de winst erg tegenvalt. Daarnaast moet er uit de frequentie analyse een duidelijke winnaar komen. Als de condities ongeveer met dezelfde frequentie slagen zal de winst ook tegenvallen.

Geluiden uit de keukens van Debian en Fedora (augustus)


Geplaatst door martijn_brekhof op 2011-09-09 12:35:22 | Permanente link | Categorie: Systeembeheer | Reacties: 0

Bij Debian meldt Roger Leigh dat Fedora een aparte groep "lock" heeft geïntroduceerd om de directory /var/lock niet meer world-writable te hoeven maken. Hij vraagt zich af of dit onder Debian ook zo kan worden opgezet. Het zal vooral aanpassingen aan de init-scripts vereisen waarbij de opgestarte daemon niet onder root-rechten zal draaien. De benodigde lock directory moet dan eerst aangemaakt worden en van de juiste permissies worden voorzien voordat de daemon wordt opgestart (link).

Asheesh Laroia laat weten dat de nieuwe mentor website up is (link). De site is bedoeld om het makkelijker te maken voor niet officiële Debian ontwikkelaars om een zogenaamde sponsor te vinden voor hun package. De sponsor zal het pakket dan controleren en namens de ontwikkelaar in de Debian distributie opnemen.

Jan Möbius heeft een bug aangemeld met betrekking tot rpc.statd dat poort 631 in gebruik neemt terwijl dit de poort is voor CUPS. Ben Hutchings laat weten dat het een bekend probleem is met het gebruik van de bindresvport functie. Dit is een functie die een willekeurige poort opent tussen 512 en 1023. Hij maakt vervolgens meteen gebruik van de gelegenheid om systemd te promoten door te stellen dat overstappen op systemd de juiste oplossing is. Deze oplossing wordt snel van tafel geveegd. Er is namelijk de mogelijkheid om bepaalde poorten uit te sluiten voor bindresvport. Echter wordt dit voor de IPv6 versie van bindresvport niet gebruikt. Ben Hutchings levert vervolgens een patch aan om dit op te lossen (link, link).

Er wordt druk gediscussieerd over het nut van xz compressie ten opzichte van de veel gebruikte gz compressie voor de bestanden in de directory /usr/share/doc. Het blijkt dat de winst van xz ten opzichte van de al veel gebruikte bz2 compressie minimaal is en soms zelfs slechter (link). Er wordt dan ook geopperd om wellicht bz2 te gaan gebruiken in plaats van over te stappen op xz.

Nanakos Chrysostomos heeft na het aanbrengen van een patch voor een lang openstaande bug zichzelf als copyrighthouder voor de yubico PAM module toegevoegd. Hier was de originele auteur niet van gediend. Nanakos vroeg zich af of dit correct was. Hij kreeg helaas geen bijval en iedereen vond zijn bijdrage ook te klein om als copyright houder te worden opgenomen (link).

Bij Fedora meldt Josef Bacik in navolging van de heftige discussie rondom BTRFS vorige maand dat BTRFS niet het standaard filesysteem wordt in Fedora Core 16 (link).

Geluiden uit de keukens van Debian en Fedora (juli)


Geplaatst door martijn_brekhof op 2011-08-11 16:50:59 | Permanente link | Categorie: Systeembeheer | Reacties: 0

In het Debiankamp meldt Juliusz Chroboczek zijn bevindingen met systemd (link). Hij somt een hoop voordelen en nadelen op. Voordelen zijn; het werkt onder Debian, het is goed gedocumenteerd, steekt logisch in elkaar, begrijpt hoe services beheerd moeten worden, en het is makkelijk zelf services te maken. Nadelen zijn: systemd is groot en wil teveel, het interacteert veel met hoger niveau lagen, heeft speciale handelingen hard gecodeerd (in plaats van via een script), gebruikt geen shell meer om opstart scripts in te schrijven, en is Linux-specifiek. Oh ja, en hij vindt systemd's auteur irritant. De opgesomde nadelen worden al snel ontkracht en Lennart Poettering (systemd's auteur) vindt het maar FUD (link). Het grootste probleem dat men heeft met systemd is dat het Linux-specifiek is en het herschreven moet worden voor bijvoorbeeld kfreebsd. Het ziet er niet naar uit dat er al een duidelijke keuze is gemaakt of men het SysV init systeem zal vervangen door systemd. Wellicht toch overstappen op upstart of helemaal niet overstappen?

Moritz Mühlenhoff oppert om naast de installatie ISOs ook virtualistie images van Debian releases te leveren. Hij vraagt zich af welke virtualisatie platformen moeten worden ondersteund (link). Jon Dowland laat weten dat het probleem vooral zit in hoe de virtualisatie techniek een virtuele machine definieert. Bijvoorbeeld, Qemu doet alles via argumenten op de commando-regel, terwijl VMware hiervoor een XML bestand gebruikt (link). Ook wordt er gesproken over images voor cloud-diensten. Charles Plessy geeft aan dat hier al wat werk voor verricht is (link).

Bij Fedora wijst Itamar Reis Peixoto iedereen op een artikel over hoe MacOS X snel DHCP leases verkrijgt en vraagt zich af of het niet ook onder Fedora te implementeren is (link). NetworkManager bevat al veel optimalisaties. Waar nog wat te winnen valt is de situatie waarin de client nog een geldige lease heeft. Nu is het zo dat NetworkManager ook bij een nog geldige lease de DHCP server om bevestiging vraagt. In plaats van telkens een bevestiging te vragen zou de geldige lease meteen gebruikt kunnen worden.

Het standaard toevoegen van ~/.local/bin aan de PATH variabele voor alle gebruikers zorgt voor nogal wat discussie aangezien dit in Fedora Core 15 na de release is doorgevoerd. Sommige waren hierdoor een beetje verrast (link). De bedoeling van de ~/.local/bin directory is dat daar programma's kunnen worden geplaatst die door de gebruiker zelf worden geinstalleerd. Python maakt bijvoorbeeld al gebruik van de directories onder ~/.local (link). De directories onder de ~/.local directory zijn deels onderdeel van de XDG specificatie. De ~/.local/bin is echter nog geen onderdeel van deze specificatie. Men stelt voor om deze directory ook in de XDG specificatie te krijgen om zo meer draagvlak te krijgen voor het gebruik van ~/.local/bin.

Miloslav Trmac maakt bekend dat in Fedora Core 16 de gebruiker IDs zullen beginnen bij 1000 in plaats van 500 (link). Debian doet dit trouwens al veel langer.

Jason Baron werkt aan een grafische tool, genaamd cg-manager, om cgroups te kunnen beheren (link). Cgroups is een kernel eigenschap om het systeemgebruik te limiteren per groep processen. Hierbij moet je denken aan geheugen-, cpu-, en I/O gebruik, maar ook prioriteiten en isolatie van processen. Met Jason's tool wordt het voor de gebruiker makkelijker om hier inzicht in te krijgen en dit naar eigen inzichten aan te passen.

Neal Becker haalt een blog van Lennart Poettering aan waarin het gebruik van /etc/sysconfig (/etc/default op Debian) in de ban wordt gedaan (link). Het blijkt dat het gebruik van /etc/sysconfig ongewenst is en dat daemons die er van afhankelijk zijn moeten worden herschreven. De /etc/sysconfig directory is ooit in het leven geroepen om configuratie-parameters voor init-scripts te beheren. Volgens Lennart Poettering is dit niet meer nodig met systemd.

Matthew Garret vraagt zich af hoe men het beste Trusted Boot (TXT) via tboot in Fedora kan implementeren (link). Eric Paris legt uit dat de impact voor de distributie niet al te groot zou moeten zijn. Het enige wat tboot doet is een hash van de kernel en initrd maken en deze in het TPM laden voordat de kernel wordt ingeladen. Zo kan, nadat de kernel is opgestart, geverifieerd worden dat het systeem veilig is opgestart en dat (nog steeds) de juiste kernel draait. Hij ziet vooral problemen met betrekking tot hardware die claimt TXT te ondersteunen maar het eigenlijk niet doet. Hij stelt dan ook voor om het niet standaard te installeren. Er wordt ook veel gediscussieerd over de toepassingen van TPM. Eén van de toepassingen is namelijk Digital Rights Management en men is bang dat TPM gebruikt kan worden om de vrijheden van de gebruiker in te perken. Vooralsnog lijkt het dat tboot dit niet in de hand werkt.

Manuel Escudero maakt duidelijk dat het gebruik van BTRFS de performance van zijn systeem aanzienlijk verslechtert. Het gebruik van virtuele machines is helemaal niet te doen (link). BTRFS staat gepland om als standaard filesysteem in Fedora 16 te dienen. Josef Bacik legt uit dat er nogal wat problemen met BTRFS zijn. Voornamelijk heeft BTRFS problemen met kleine I/O opdrachten doordat het voor praktisch alles threads gebruikt. Elke keer als een proces een I/O opdracht doet wordt deze overgedragen aan een thread en wacht het proces totdat de thread wakker wordt om de opdracht te accepteren. Vervolgens zal wanneer de IO opdracht klaar is deze weer aan een thread worden gegeven. Dit zorgt voor nogal wat wachttijden. Bij grote IO opdrachten is dit niet echt merkbaar, maar bij kleine IO opdrachten juist te meer. Josef probeert dit probleem op te lossen en geeft aan dat BTRFS niet als standaard filesysteem zal worden opgenomen als het niet goed bruikbaar is (link en link).

Misha Shnurapet vraagt zich af of ondertekeningen van upstream tarballs worden gecontroleerd bij het packagen. Dit naar aanleiding van de achterdeur in vsftpd die recentelijk is ontdekt (link). Op dit moment is het de verantwoordelijkheid van de package maintainer om de ondertekening te controleren. Het mooist zou zijn als de rpmbuild tool automatisch een beschikbare ondertekening controleert. Een ander voorstel is om het te automatiseren via een auto-qa test.