PHP string looping

From Meta, a Wikimedia project coordination wiki
Jump to: navigation, search

A quick hacky benchmark to measure speed of character-by-character string building in PHP. Each test (except do_nothing()) builds a string of 100,000 "*" characters. do_repeat() uses the built-in function str_repeat() so represents the most ideal possible case of optimized C code, though most parsing will not produce large arrays of the same character. ;)

do_repeat2() adds an empty for loop, demonstrating the best we're likely to see in PHP that goes character-by-character.

The assessment[edit]

Simply looping through 100,000 iterations in PHP is hella slow. Appending characters to a string with .= is the speediest PHP-based construct I could get working; it adds less than the loop overhead itself.

Pre-allocating a string and using indexed offsets is somewhat slower than the append. Working with a big array of short strings is rather slower, at least for this simple case. Copying the strings around on every operation ($result = $result . ".") gives the absolute worst results -- this should be avoided.

The results[edit]

Running on PHP 5.0.1 on an Athlon XP 2400:

Benchmarking 100 runs of do_nothing...
3.1685829162598E-06 seconds per run
315598.4951091 runs per second

Benchmarking 100 runs of do_repeat...
3.054141998291E-05 seconds per run
32742.419984387 runs per second

Benchmarking 100 runs of do_repeat2...
0.092702879905701 seconds per run
10.787151391815 runs per second

Benchmarking 100 runs of do_prealloc...
0.14803943157196 seconds per run
6.7549570366589 runs per second

Benchmarking 100 runs of do_append...
0.1359907412529 seconds per run
7.353441791602 runs per second

Benchmarking 100 runs of do_combine...
3.2336623096466 seconds per run
0.30924688611325 runs per second

Benchmarking 100 runs of do_array...
0.49620581150055 seconds per run
2.0152928015413 runs per second

The code[edit]

<?php

define( 'TEST_SIZE', 100000 );

function wfTime(){
        $st = explode( ' ', microtime() );
        return (float)$st[0] + (float)$st[1];
}

function benchmark( $function, $runs = 100 ) {
        print "Benchmarking $runs runs of $function...\n";
        $start = wfTime();
        for( $i = 0; $i < $runs; $i++)
                $function();
        $delta = wfTime() - $start;
        $avg = $delta / $runs;
        $hertz = $runs / $delta;
        print "$avg seconds per run\n";
        print "$hertz runs per second\n\n";
}

function do_nothing() {
}

function do_repeat() {
        $result = str_repeat( "*", TEST_SIZE );
        return $result;
}

function do_repeat2() {
        $result = str_repeat( "*", TEST_SIZE );
        for( $i = 0; $i < TEST_SIZE; $i++ ) {
                # NOP
        }
        return $result;
}

function do_prealloc() {
        $result = str_repeat( " ", TEST_SIZE );
        for( $i = 0; $i < TEST_SIZE; $i++ ) {
                $result{$i} = "*";
        }
        return $result;
}

function do_append() {
        $result = "";
        for( $i = 0; $i < TEST_SIZE; $i++ ) {
                $result .= "*";
        }
        return $result;
}

function do_combine() {
        $result = "";
        for( $i = 0; $i < TEST_SIZE; $i++ ) {
                $result = $result . "*";
        }
        return $result;
}

function do_array() {
        $arr = array();
        for( $i = 0; $i < TEST_SIZE; $i++ ) {
                $arr[$i] = "*";
        }
        $result = implode( $arr );
        return $result;
}

benchmark( "do_nothing" );
benchmark( "do_repeat" );
benchmark( "do_repeat2" );
benchmark( "do_prealloc" );
benchmark( "do_append" );
benchmark( "do_combine" );
benchmark( "do_array" );

?>