PHP micro optimalisatie: arrays

  • ± 3 minuten

Wanneer je programmeert, ongeacht de taal, kom je niet onder het gebruik van arrays uit. Niet zelden kom ik ondoordachte code tegen, dat makkelijk sneller kan. Denk aan isset ten opzichte van in_array, om na te gaan of een bepaalde waarde in een array voor komt.

Dit is een vervolg op PHP optimalisatie: inleiding

PHP in_array alternatieven

Dat isset een goed alternatief is op in_array, is hopelijk niet nieuw. Indien wel, pluis direct je code na; in_array is vrij langzaam en probeer ik te allen tijde te vermijden. In_array doet er zelfs langer over naarmate een te vinden value verder in de array ligt (of dus afwezig is). Intern is in_array dus niets meer dan een foreach in combinatie met een break. Echter, gebruik van isset behoudt te allen tijde zijn snelheid. Voor grote arrays, of juist veel zoek-acties, loont het om op index te zoeken, in plaats van value. Het gebruik van isset( $array[ $value ] ) is zelfs (1.4 keer) sneller dan in_array( $array, $value ), wanneer de $value direct op de eerste positie ligt. De lengte van de array lijkt hierbij geen rol te spelen.

Wanneer de $value exact in het midden ligt van een array, is de snelheids factor van isset ten opzichte van in_array per array-lengte ongeveer als volgt:

array lengte:101001.00010.000
winst-factor tov in_array2x5x30x290x

PHP array naar unieke values

Unieke values uit een array kun je op meerdere manieren verkrijgen. Meest voor de hand liggende is array_unique. Toch lijkt dit de meest langzame methode, om dit te doen. array_flip presteert beduidend beter.

Array_flip

De factor van array_flip ten opzichte van array_unique is namelijk 25 keer sneller bij 100 array values. Bij 10.000 values is array_flip ongeveer 50 keer sneller.

Kanttekening is dat je de unique waarden vervolgens als indexen hebt in plaats van values. Heb je ze toch als values nodig voor vervolg handelingen? Gebruik dan nogmaals array_flip, om de verkregen indexen weer om te zetten als values. Overigens is array_keys( array_filter() ) net zo snel als een array twee maal door array_flip te gooien. Deze methode is respectievelijk 12 en 24 keer sneller dan array_unique.

array_combine

De meest snelle methode die ik getest heb, is echter array_combine. Deze functie ontvangt twee parameters, waarbij de eerste parameter als indexen worden gebruikt. Nog steeds geldt dat indexen uniek dienen te zijn, waardoor de de array die als eerste parameter wordt meegegeven, direct omgezet worden tot unieke indexen. Dit is respectievelijk een factor 35 en 80 keer sneller.

Associatieve array behouden

Is het werkelijk nodig dat de key en value-pairs intact blijven, zoals array_unique dit zou doen? Denk dan een stap verder: gebruik array_combine in combinatie met array_flip om je verkregen array terug te gooien:
array_flip( array_combine( $vars, array_keys( $vars ) ) )

Let wel, dat de resultaten af zullen kunnen wijken van array_unique. Deze laatste methode verwijdert elk andere reeds voorkomende key-value pair weg, terwijl de array_flip i.c.m. array_combine, juist eerdere key-value pairs zal overschrijven. De factor is ten opzichte van de andere array_unique alternatieven, overigens wel het kleinst. Respectievelijk 9 en 15 keer sneller dan array_unique.

Opmerkelijke verschuiving

Disclaimer is dat bovenstaande tests om array-values uniek te maken, heeft plaats gevonden met niet unieke values. Het wordt interessant wanneer we dubbele values toe gaan voegen binnen een array-lengte van 100. array_unique wordt steeds langzamer, terwijl elk ander genoemd alternatief juist sneller het doel bereikte. In tegenstelling tot wat zijn naam doet vermoeden, is array_unique dus het minst geschikt voor de taak.

PHP array alternatieven

Er zijn soms, om de impact op benodigd geheugen te beperken, alternatieven. Heb je een array met vaste lengte, waarbij de indexen enkel numeriek zullen zijn? Gebruik dan SplFixedArray. En alhoewel in de regel (o.a. door de release manager van PHP5) geclaimed wordt dat PHP objecten langzamer zijn dan arrays, lijkt het in specifieke scenario's zelfs een beter alternatief. Iemand die er dieper in is gedoken, komt tot de conclusie dat wanneer de class-properties daadwerkelijk benut worden, objecten een beter alternatief zijn dan associatieve arrays. Wel vergt het weer aanvullende functie aanroepen voor mutaties, waar je winst dus wellicht weer verloren gaat.

Omdat de winst in geheugen-beperking echter minimaal is, kun je de focus op snelheid leggen.