I'm working on some PHP right now, and I want it as fast as possible. Naturally, I have to output some interesting stuff; concatenated strings, with integer or floating-point values in them.

The question becomes: is it faster to print values that must be formatted, such as the string "\n", or integers or floating-point numbers, by:

  1. Including them in one long double-quoted string
  2. Concatenating them between multiple single-quoted strings
  3. Using the heredoc format
  4. Using the sprintf() function

I made a quick PHP script to do the benchmark for me. The results were interesting.

Update 2008-05-02: source code!

This test was done on a Linux server running PHP 5.2.2. The Zend optimizer was installed.

All calculations are done by putting the value into a variable, outstr. Once the string is obtained, a simple print or echo can be used to output it. This way I don't actually have to generate a lot of output in the HTML, so I can run the test online.

Here's a snip of the output:

calculating a very long string using only single-quote concatenation... 319ms.
calculating the same string using a large double-quoted concatenation... 259ms.
calculating the same string using a heredoc... 282ms.
calculating the same string using an sprintf()... 401ms.
calculating the same string using an sprintf() with positional params... 456ms.

Graphical comparisons

Method (100000 iterations) Percent of time out of max (456 ms)
Single quote concat
Double quote eval
Heredoc eval
Simple sprintf()
sprintf() with positional params

I ran it multiple times, and this is pretty typical. Apparently double quotes are actually faster than concatenating a bunch of variables with single-quoted literals. How about that? Heredoc format is usually pretty close, but sprintf() is always the slowest of the bunch. Using sprintf() with positional parameters (like "%2$s") is always the slowest.

Update 2008-05-02: There have been multiple requests to see the code. I forgot where I found the original basis, but that timer function is slightly magic to me, so I haven't touched it. All the other stuff I modified to fit my own tests. While I won't tell you where I stored it, or what I named it (I can just see the spammers hitting this over and over), here's what the code looks like:

<?php
set_time_limit(0);

@$getnumtimes = $_GET['numtimes'];
print("Argument was: $getnumtimes<br />\n");
if (is_numeric($getnumtimes) and $getnumtimes > 0 and $getnumtimes < 1000000) {
print ("Argument was numeric<br />\n");
    $numtimes = $getnumtimes;
} else {
print("Argument was invalid.<br />\n");
    $numtimes = 1000;
}
$bgcolor = '#0000FF';

$data = 'This is data to be concatenated. It is long, so as to make it take longer to concatenate and hence widen the gap between the faster and the slower of the methods.';

$int = 1;
$float = 3.1415927;
$n = "\n";

$max = 1;

class timer
{
    var $_start;
    var $_current;
    function timer()
    {
        $this->_start = array_sum(explode(' ', microtime()));
    }
    function tick()
    {
        $this->_current = round((array_sum(explode(' ', microtime()))-$this->_start)*1000, 0);
        return $this->_current;
    }
    function reset()
    {
        $this->timer();
    }
}

print <<<EOS
<head>
<title>PHP string formatting benchmark</title>
</head>
<body>
<p>I'm working on some PHP right now, and I want it as fast as possible.  Naturally, I have to output some interesting stuff; concatenated strings, with integer or floating-point values in them.
</p><p>
The question becomes: is it faster to print values that must be formatted, such as the string "\\n", or integers or floating-point numbers, by:
</p><p>
<ol>
<li>Including them in one long double-quoted string</li>
<li>Concatenating them between multiple single-quoted strings</li>
<li>Using the heredoc format</li>
<li>Using the sprintf() function</li>
</ol>
<p>All calculations are done by putting the value into a variable, outstr.  Once the string is obtained, a simple print or echo can be used to output it.  This way I don't actually have to generate a lot of output in the HTML, so I can run the test online.
</p><p>
EOS;

print('calculating a very long string using only single-quote concatenation...');
$single_quote_timer = new timer();
for($i = 0; $i < $numtimes; $i++) {
    $outstr = 'literal' . $n . $data . $int . $data . $float . $n;
}
$single_quote_timer = $single_quote_timer->tick();
if ($single_quote_timer > $max) { $max = $single_quote_timer; }
print(' '.$single_quote_timer.'ms.<br />');
$single_quote_outstr = $outstr;
//print("$single_quote_outstr<br /><br />\n");

print('calculating the same string using a large double-quoted concatenation...');
$double_quote_timer = new timer();
for($i = 0; $i < $numtimes; $i++) {
    $outstr = "literal$n$data$int$data$float$n";
}
$double_quote_timer = $double_quote_timer->tick();
if ($double_quote_timer > $max) { $max = $double_quote_timer; }
print(' '.$double_quote_timer.'ms.<br />');
$double_quote_outstr = $outstr;
//print("$double_quote_outstr<br /><br />\n");

print('calculating the same string using a heredoc...');
$heredoc_timer = new timer();
for($i = 0; $i < $numtimes; $i++) {
    $outstr =<<<EOS
literal$n$data$int$data$float$n
EOS;
}
$heredoc_timer = $heredoc_timer->tick();
if ($heredoc_timer > $max) { $max = $heredoc_timer; }
print(' '.$heredoc_timer.'ms.<br />');
$heredoc_outstr = $outstr;
//print("$heredoc_outstr<br /><br />\n");

print('calculating the same string using an sprintf()...');
$sprintf_timer = new timer();
for($i = 0; $i < $numtimes; $i++) {
    $outstr = sprintf('literal%s%s%d%s%f%s', $n, $data, $int, $data, $float, $n);
}
$sprintf_timer = $sprintf_timer->tick();
if ($sprintf_timer > $max) { $max = $sprintf_timer; }
print(' '.$sprintf_timer.'ms.<br />');
$sprintf_outstr = $outstr;
//print("$sprintf_outstr<br /><br />\n");

print('calculating the same string using an sprintf() with positional params...');
$pos_sprintf_timer = new timer();
for($i = 0; $i < $numtimes; $i++) {
    $outstr = sprintf('literal%s%5$s%2$d%3$s%4$f%s', $n, $int, $data, $float, $data, $n);
}
$pos_sprintf_timer = $pos_sprintf_timer->tick();
if ($pos_sprintf_timer > $max) { $max = $pos_sprintf_timer; }
print(' '.$pos_sprintf_timer.'ms.<br />');
$pos_sprintf_outstr = $outstr;
//print("$pos_sprintf_outstr<br /><br />\n");

// Calculate percents
$single_quote_percent = ($single_quote_timer * 100)/$max;
$double_quote_percent = ($double_quote_timer * 100)/$max;
$heredoc_percent = ($heredoc_timer * 100)/$max;
$sprintf_percent = ($sprintf_timer * 100)/$max;
$pos_sprintf_percent = ($pos_sprintf_timer * 100)/$max;
print <<<EOS
</p>
<h3>Graphical comparisons</h3>
<p>
<table id="toptable" border="1">
<tr><th>Method ($numtimes repetitions)</th><th>Percent of time out of max ($max ms)</th></tr>
<tr><td>Single quote concat</td><td><table width='$single_quote_percent%' style='background: $bgcolor'><tr><td></td></tr></table></td></tr>
<tr><td>Double quote eval</td><td><table width='$double_quote_percent%' style='background: $bgcolor'><tr><td></td></tr></table></td></tr>
<tr><td>Heredoc eval</td><td><table width='$heredoc_percent%' style='background: $bgcolor'><tr><td></td></tr></table></td></tr>
<tr><td>Simple sprintf()</td><td><table width='$sprintf_percent%' style='background: $bgcolor'><tr><td></td></tr></table></td></tr>
<tr><td>sprintf() with positional params</td><td><table width='$pos_sprintf_percent%' style='background: $bgcolor;'><tr><td></td></tr></table></td></tr>
</table>
</p>
EOS;

/*
print('<hr />php version: '. phpversion()); 
print('<br />php info: ');
phpinfo();
print('<br /><hr />');
*/