How you write your String matters: benchmarking PHP

Testing how different types of string specification compare in terms of execution speed in PHP

How you write your String matters: benchmarking PHP

There are a lot of ways in which you can specify, use and print string in PHP. They all have different benefits and drawbacks and that's not the content of this article. The focus of these tests is to determine how using different string specifications affects the performances of your PHP code.

TL;DR: Single quoted strings are the most performing, both for simple text and for concatenation with other variables.

Tests

Compared 3 types of string specification:

  • Single Quoted (e.g. echo 'string';)
  • Double Quoted (e.g. echo "string";)
  • HEREDOC (e.g. echo <<<EOT string EOT;)

For each type of specification, 2 tests:

  • Simple string (e.g. 'string')
  • String + a variable (e.g. 'string'.$var)

Iterations

In order to simulate a strong workload and have more data, the code ran 10 iterations with a 5-second pause. For each iteration 1 Million echo were executed, without any pause.

To make sure that the machine load doesn't skew the results, all the 3 different specifications are executed within the same iteration, one after the other, with the same 5-second delay. So at any point in time, the load should be equally distributed among the different specifications.

Buffer

Since the actual printing of the strings must not affect these tests, ob_start() and ob_end_clean() are used to not print anything, and ob_clean() is used within each iteration to free up the buffer.

The Code

This is the code used to run the tests.

<?php

define('MAX_PER_ITERATION', 10000000);
define('MAX_ITERATIONS', 10);
define('SLEEP_TIME', 5);

echo "TEST 1: Simple String\n";
$times = [
    'quote' => [],
    'doublequote' => [],
    'heredoc' => []
];
for ($k = 1; $k <= MAX_ITERATIONS; $k++) {
    echo "Iteration ".$k."\n";

    /* Start Single Quote tests */
    echo "\tQuote\n";
    ob_start();
    $start = microtime(true);
    for ($i = 0; $i < MAX_PER_ITERATION; $i++) {
        echo 'string';
        ob_clean();
    }
    $diff = (microtime(true) - $start);
    ob_end_clean();
    $times['quote'][] = $diff;
    sleep(SLEEP_TIME);

    /* Start Double Quote tests */
    echo "\tDouble Quote\n";
    ob_start();
    $start = microtime(true);
    for ($i = 0; $i < MAX_PER_ITERATION; $i++) {
        echo "string";
        ob_clean();
    }
    $diff = (microtime(true) - $start);
    ob_end_clean();
    $times['doublequote'][] = $diff;
    sleep(SLEEP_TIME);

    /* Start HEREDOC tests */
    echo "\tHeredoc\n";
    ob_start();
    $start = microtime(true);
    for ($i = 0; $i < MAX_PER_ITERATION; $i++) {
        echo <<< EOT
            string
        EOT;
        ob_clean();
    }
    $diff = (microtime(true) - $start);
    ob_end_clean();
    $times['heredoc'][] = $diff;
    sleep(SLEEP_TIME);
}
foreach ($times as $type => $results) {
    echo $type."\n";
    foreach ($results as $r) {
        echo "\t\t".$r."\n";
    }
    $avg = array_sum($results) / sizeof($results);
    echo "\tAvg: ".$avg."\n";
}

sleep(100);

echo "TEST 2: String with Variable\n";
$times = [
    'quote' => [],
    'doublequote' => [],
    'heredoc' => []
];
$variable = 'string2';
for ($k = 1; $k <= MAX_ITERATIONS; $k++) {
    echo "Iteration ".$k."\n";

    /* Start Single Quote tests */
    echo "\tQuote\n";
    ob_start();
    $start = microtime(true);
    for ($i = 0; $i < MAX_PER_ITERATION; $i++) {
        echo 'string'.$variable;
        ob_clean();
    }
    $diff = (microtime(true) - $start);
    ob_end_clean();
    $times['quote'][] = $diff;
    sleep(SLEEP_TIME);

    /* Start Double Quote tests */
    echo "\tDouble Quote\n";
    ob_start();
    $start = microtime(true);
    for ($i = 0; $i < MAX_PER_ITERATION; $i++) {
        echo "string $variable";
        ob_clean();
    }
    $diff = (microtime(true) - $start);
    ob_end_clean();
    $times['doublequote'][] = $diff;
    sleep(SLEEP_TIME);

    /* Start HEREDOC tests */
    echo "\tHeredoc\n";
    ob_start();
    $start = microtime(true);
    for ($i = 0; $i < MAX_PER_ITERATION; $i++) {
        echo <<< EOT
            string {$variable}
        EOT;
        ob_clean();
    }
    $diff = (microtime(true) - $start);
    ob_end_clean();
    $times['heredoc'][] = $diff;
    sleep(SLEEP_TIME);
}
foreach ($times as $type => $results) {
    echo $type."\n";
    foreach ($results as $r) {
        echo "\t\t".$r."\n";
    }
    $avg = array_sum($results) / sizeof($results);
    echo "\tAvg: ".$avg."\n";
}

Summary Results

Unsurprisingly, the Single Quoted strings are the fastest: as they don't require parsing for variables or special characters (e.g. \n), they run quicker than the others both for simple strings and for variables.

  • Simple String: 0.3838947773 seconds for 1 Million executions
  • String+Variable: 0.532834816 seconds for 1 Million executions

Heredoc performs a bit slower than Single Quoted strings, but faster than Double Quoted ones, especially when without variables.

  • Simple String: 0.387871933 seconds for 1 Million executions
  • String+Variable: 0.553892827 seconds for 1 Million executions

Finally Double Quoted strings:

  • Simple String: 0.3914105415 seconds for 1 Million executions
  • String+Variable: 0.5623492718 seconds for 1 Million executions

Complete Results

Below you can see a sneak peak of the final results. If you click on the image or here , you can access a shared spreadsheet with the complete data.

Screenshot 2021-07-09 at 11.39.27 PM.png

System/Versions

  • PHP 7.4.19
  • Amazon Linux 2 AMI
  • Kernel 4.14.232-177.418.amzn2.x86_64
  • 16GB Memory
  • 4 Virtual CPUs