PHP string looping

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" );

?>