strncmp() ist schneller als substr()

23. Februar 2011 Besucher gesamt: 1520

In diversen Foren und Blogs wird schon seit 2007 geschrieben, dass strncmp() schneller als substr() sei. Und zwar, stolze 20%!!! Ist das denn so? Stimmt die Berechnung auch? Nun, wie wir alle es wissen, Codefragmente die viel mit string-Typen zu tun haben, sind nicht Ressourcen-schonend und können den gesamten Prozess sehr verlangsamen. Um das Ganze besser vergleichen zu können, habe ich diese einfache Klasse „Benchmark_Substr_vs_Strncmp“ geschrieben. Mittels der oben genannten Funktionen wird versucht aus einem Test-Array, mit zwanzig Einträgen, den Array-Key „GETME__“ zu ermitteln. Diese Suche wird mit beiden Funktionen je 10.000-mal ausgeführt. Bei jedem Testlauf wird die Dauer für die Suche in eine CSV Datei festgehalten.

< ?php
class Benchmark_Substr_vs_Strncmp
{
    const LOOP = 10000;

    protected $_resultSubstr = 0;
    protected $_resultStrncmp = 0;

    protected $_testData = array(
        'GETME__3' => 3,
        'GETME__4' => 4,
        'DONT_GETME__1' => 1,
        'DONT_GETME__2' => 2,
        'DONT_GETME__3' => 3,
        'DONT_GETME__4' => 4,
        'GETME__5' => 5,
        'GETME__6' => 6,
        'DONT_GETME__6' => 6,
        'DONT_GETME__7' => 7,
        'DONT_GETME__8' => 8,
        'GETME__7' => 7,
        'GETME__8' => 8,
        'GETME__9' => 9,
        'DONT_GETME__5' => 5,
        'DONT_GETME__9' => 9,
        'GETME__1' => 1,
        'GETME__2' => 2,
		'GETME__1234' => 1,
        'GETME__2234' => 2,
    );

    protected function _substr_data()
    {
        $out = array();

        foreach ($this->_testData as $key => $val)
        {
            if (substr($key, 0, 7) == 'GETME__')
            {
                $out[$key] = $val;
            }
        }
    }

    protected function _strncmp_data()
    {
        $out = array();

        foreach ($this->_testData as $key => $val)
        {
            if (!strncmp($key, 'GETME__', 7))
            {
                $out[$key] = $val;
            }
        }
    }

    public function runBenchmarkOfStrncmp()
    {
        $beginn = microtime(true);

        $i = 0;

        while($i < self::LOOP)
        {
            $this->_strncmp_data();

            ++$i;
        }

        $this->_resultStrncmp = sprintf('%.3f', microtime(true) - $beginn);
    }

    public function runBenchmarkOfSubstr()
    {
        $beginn = microtime(true);

        $i = 0;

        while($i < self::LOOP)
        {
            $this->_substr_data();

            ++$i;
        }

        $this->_resultSubstr = sprintf('%.3f', microtime(true) - $beginn);
    }

    public function saveData()
    {
        $fp = fopen(dirname(__FILE__).'benchmark_substr_vs_strncmp.csv', 'a');

        fputcsv($fp, array($this->_resultSubstr, $this->_resultStrncmp), ';');
        fclose($fp);

        $this->_resultSubstr = 0;
        $this->_resultStrncmp = 0;

        print '.';
    }
}

Hier ein Testszenario:

$benchmark = new Benchmark_Substr_vs_Strncmp();
$benchmark->runBenchmarkOfSubstr();
$benchmark->runBenchmarkOfStrncmp();
$benchmark->saveData();

Für den Test habe ich auf das System dafür gesorgt, dass dieser Test die einzige Klasse im System ist die was mit PHP macht. Hier das Ergebnis nach zwanzig Testszenarien.

Substr	Strncmp		Differenz in Sek.	Differenz in %
--------------------------------------------------------------
0,227	0,2		0,027				11,89
0,222	0,199		0,023				10,36
0,216	0,198		0,018				8,33
0,217	0,199		0,018				8,29
0,232	0,213		0,019				8,19
0,217	0,201		0,016				7,37
0,223	0,207		0,016				7,17
0,213	0,199		0,014				6,57
0,21	0,198		0,012				5,71
0,215	0,204		0,011				5,12
0,207	0,198		0,009				4,35
0,205	0,198		0,007				3,41
0,207	0,202		0,005				2,42
0,2	0,198		0,002				1,00
0,209	0,207		0,002				0,96
0,199	0,198		0,001				0,50
0,202	0,201		0,001				0,50
0,206	0,205		0,001				0,49
0,199	0,199		0				0,00

Der Aufruf von substr() ist zwar relativ schnell, aber die widerholende Ausführung summiert sich. Ich habe bei meiner Berechnung die besagten 20% nicht erreicht, jedoch stolze 5%. Liegt es eventuell daran dass PHP5 schneller als PHP4 ist? Wie auch immer, ich bin mir sicher, dass man gut behaupten darf, dass schlecht implementierte Zeichenkettenanalysen in umfassenden Anwendungen oder Frameworks einen großen Teil der Zeit auf diese Funktion verwenden. Die strncmp() ist nur dann ein natürlicher Ersatz für substr(), wenn es um Zeichenvergleich am Anfang von Zeichenketten, handelt.

Die substr() Funktion muss erst ihren Rückgabewert zuweisen und schreiben, und dann einen Vergleich durchführen, während strncmp() nur Zeichen für Zeichen vergleicht. Wie die Speicherverwaltung bei PHP im Einzelnen funktioniert, das bleibt uns verborgen, dennoch werden wir die Zeit für die Zuweisung immer deutlich spüren.

Frage: Im Zend-Framework wird substr() alleine 209-mal verwendet. Der Großteil davon wird verwendet um Zeichenketten am Anfang der Zeichenkette zu vergleichen. Und strncmp(), hingegen wird nur 2-mal verwendet. Was meint Ihr, würde ein Umbau auf strncmp(), eine Anwendung die komplett auf Zend-Framework aufbaut, beschleunigen?

¬ geschrieben von gjerokrsteski in PHP Tricks und Tipps  ¬ Erzähl´s weiter Twitter  | Mr. Wong  | Delicious  | Del.icio.us  | Google  | Facebook

«

» 

5 Kommentare zu 'strncmp() ist schneller als substr()'

  1. Thomas sagte am 23. Februar 2011 um 12:14 Uhr:

    Interessanter Versuch, ich habe bisher immer nur substr benutzt und kannte strncmp noch garnicht.
    Allerdings macht es wohl nur in wenigen Ausnahmesituationen Sinn, substr durch strncmp zu ersetzen.
    5% oder gar 20% klingt zwar erstmal viel, aber bei deinem Test macht das bei 200.000 Durchläufen ja nur ca. 10 Millisekunden aus – bei “normaler” benutzung merkt man also keinen Unterschied ;)

  2. KingCrunch sagte am 23. Februar 2011 um 12:45 Uhr:

    Das im Laufe des Tests einfach der Speicher zunehmend voll läuft, beeinträchtigt die Ergebnisse nicht?

  3. QueenCrunch sagte am 23. Februar 2011 um 21:25 Uhr:

    @KingCrunch …aus dem Bauch heraus, würde ich auf 5 bis 6 Byte bei jeder Iteration.

  4. David Müller sagte am 23. Februar 2011 um 22:41 Uhr:

    Ich würde das mal getrost unter Micro Optimization ablegen. Bei 5% muss man schon ein paar tausend Strings vergleichen, dass sich das wirklich rechnet. Da kann man auch dran gehen und alle Hochkommata in echo’s ersetzen. Trotzdem nett mal drüber gelesen zu haben.

  5. gk sagte am 24. Februar 2011 um 14:54 Uhr:

    @David Müller
    Bei diesem Test habe ich einen Unterschied bis von 5% bis 11% erreichen können. Habe den selben Test auf einem Server ausgeführt, der sehr viel mit PHP zu tun hat, und habe dort unterscheide von 6% bis 32% festgestellt. Ja, es bleibt spannend :-)

Hinterlasse einen Kommentar

*Codebeispiele können im CODE-Tag angegeben werden.

Powered by Wordpress • Abonniere den RSS Feed