PHP: in_array ist langsam (UPDATE)

Suche in großen Arrays in Bruchteilen von Sekunden.

Heute habe ich an einer kleinen Anwendung herumgehackt. Dabei muss ich mehrmals täglich zwei Arrays miteinander vergleichen.
Als Testdaten hatte ich ein paar Einträge und es lief alles einwandfrei. Doch als ich dann die echten Dateien mit über 60.000 Datensätzen hernahm wurde die Verarbeitung extrem langsam. Und das ganze soll natürlich auf Knopfdruck passieren und niemand soll darauf warten.

Recht schnell konnte ich das Problem auf die Performanceschnecke in_array() zurückführen.
Rein interessehaber habe ich stattdessen das Array umgedreht und isset() eingebaut.
Und augenblicklich war die Verarbeitung in einem Augenblick fertig.

Hier ein Demoskript um den Unterschied zu veranschaulichen:

# Vorbereitetes Array mit 10.000 Einträgen einlesen.
# (10.000 zufällige SHA1-Hashes)
$array = unserialize(file_get_contents('array'));

$found = 0;
$start = microtime(true);
foreach($array as $val){
 if(in_array($val,$array)){ $found++; }
}
$end = microtime(true);

echo 'Habe '.$found.' Einträge in ';
echo (round($end-$start,3))." Sekunden gefunden.\n";
# Habe 10000 Einträge in 9.417 Sekunden gefunden.

# Index und Wert vertauschen
$array_flip = array_flip($array);

$found = 0;
$start = microtime(true);
foreach($array as $key => $val){
 if(isset($array_flip[$val])){ $found++; }
}
$end = microtime(true);

echo 'Habe '.$found.' Einträge in ';
echo (round($end-$start,3))." Sekunden gefunden.\n";
# Habe 10000 Einträge in 0.004 Sekunden gefunden.

Also 9,4 Sekunden gegenüber 0,004 Sekunden. Deutlicher könnte der Unterschied zwischen in_array() und isset() nicht sein.

Ich habe dann noch eine dritte Variante probiert, welche ich im Internet gefunden habe.
Hierbei wird das Array in einen String gewandelt, und nur in diesem String gesucht.

# Array in String umwandeln
$array_string = ' '.implode(' ',$array);

$found = 0;
$start = microtime(true);
foreach($array as $key => $val){
  if(strpos($array_string,$val)>0){ $found++; }
}
$end = microtime(true);

echo 'Habe '.$found.' Einträge in ';
echo (round($end-$start,3))." Sekunden gefunden.\n";
# Habe 10000 Einträge in 5.129 Sekunden gefunden.

Laut diesem Eintrag sollte dies noch schneller sein als meine isset() Variante.
Aber bei meinen ersten Tests war diese deutlich langsamer. Ca. 5 Sekunden anstatt der 0,004.
Ich sah dann, dass dieser Programmierer lediglich kurze Werte (max. 4 Zeichen) verwendete. Dadurch war der String deutlich kürzer als bei meinen 40-stelligen SHA1-Hashes.

Das bedeutet, dass besonders bei größeren Datenmengen und längeren Suchmustern die Variante mit isset() bisher die schnellste ist.

Wenn die Strings in Binärdaten umgewandelt werden, dann ergibt sich im dritten Beispiel eine deutliche Steigerung der Leistung, aber dies trifft auch auf das zweite Beispiel zu und diese wurde auch um ca. 20% schneller mit meinen Testdaten.

Es wäre schön wenn man in PHP die Funktionen überladen könnte.

UPDATE:
Schaut mal in den PHP-Source, dann seht ihr warum das so langsam ist.
Zusammenfassung: in_array() und array_search() teilen sich eine Funktion.

  1. JavaScript und ForEach
  2. Gütesiegel bieten keine Sicherheit
  3. 26c3 Quotes
  4. Postfix Mailqueue löschen
  5. Peter Kleissner bei der DeepSec

Empfehlungen