How you write your String matters: benchmarking PHP
Tags: php, benchmark, performance, webdev
Published: 2021-07-09 16:01:51

There are a lot of ways in which you can specify, use and print  [string in PHP](https://www.php.net/manual/en/language.types.string.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
<?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](https://docs.google.com/spreadsheets/d/1D381g94mJwLDyUt8gqXf4A4l_RSYD1sHfaQ5g97f8J8/edit?usp=sharing) , you can access a shared spreadsheet with the complete data.


 [![Screenshot 2021-07-09 at 11.39.27 PM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1625845740107/XcI6Qh9Ih.png)
](https://docs.google.com/spreadsheets/d/1D381g94mJwLDyUt8gqXf4A4l_RSYD1sHfaQ5g97f8J8/edit?usp=sharing) 

## System/Versions

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