Facebook
Twitter
Google+
Kommentare
0

strncmp() ist schneller als substr()

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?

Über den Autor

Gjero Krsteski

Link erfolgreich vorgeschlagen.

Vielen Dank, dass du einen Link vorgeschlagen hast. Wir werden ihn sobald wie möglich prüfen. Schließen