Last days I've read a lot of posts about the performance difference between PHP and Node.js but most of them was made with PHP 5 and not in real productive environment so I decide to test the performance of PHP 7 + Nginx vs PHP7 + Swoole vs Node.js vs Node.js + Nginx using WRK HTTP benchmark tool.

Configuration

I've runed all tests on 5$ intance of linode.com

#cpu

description: CPU

product: Intel(R) Xeon(R) CPU E5-2680 v3 @ 2.50GHz

vendor: Intel Corp.

physical id: 400

bus info: cpu@0

version: pc-i440fx-2.6

slot: CPU 0

size: 2GHz

capacity: 2GHz

width: 64 bits

capabilities: fpu fpu_exception wp vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp x86-64 constant_tsc arch_perfmon rep_good nopl eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt arat

configuration: cores=1 enabledcores=1 threads=1



#memory

description: System Memory

physical id: 1000

size: 1GiB

capacity: 1GiB







To run the tests I've installed the following packages:

sudo apt-get update

sudo apt-get install nginx

sudo apt-get install php-fpm

sudo apt-get install php-dev

sudo apt-get install php-mbstring

sudo apt-get install php7.0-intl

sudo apt-get install mysql-server

sudo apt-get install build-essential libssl-dev git -y

sudo apt-get install unzip

sudo apt-get install zip

sudo apt-get install wrk

PHP Swoole installation

git clone https://github.com/swoole/swoole-src.git

cd swoole-src

phpize

./configure

make

make install

sudo echo "extension=swoole.so" > /etc/php/7.0/cli/php.ini

Node.js installation:

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.4/install.sh | bash

source ~/.bashrc

nvm install node

sudo ln -s "$NVM_DIR/versions/node/$(nvm version)/bin/node" "/usr/local/bin/node"

sudo ln -s "$NVM_DIR/versions/node/$(nvm version)/bin/npm" "/usr/local/bin/npm"

I've set worker_connections of nginx to 1024 which is optimal for single core processor and turned off the access_log.

sudo nano /etc/nginx/nginx.conf



events {

worker_connections 1024;



}

http {

access_log off;

I've activated opcache too:

opcache.enable=1

opcache.memory_consumption=512

opcache.interned_strings_buffer=64

opcache.max_accelerated_files=20000

opcache.validate_timestamps=0

opcache.save_comments=0

opcache.fast_shutdown=1

Tests

I've used single thread to run the tests because the CPU has only one core.

PHP version

root@localhost:~# php -v

PHP 7.0.22-0ubuntu0.16.04.1 (cli) ( NTS )

Node.js version

root@localhost:~# node -v

v8.6.0

Test 1: Hello World

PHP code

echo "Hello World!";

PHP Swoole code

$http = new swoole_http_server('0.0.0.0', 9000);



$http->on('request', function ($request, $response) {

$response->header('Content-Type', 'text/plain; charset=utf-8');

$response->end('Hello World');

});



echo "Server run on port 9000";



$http->start();

Node.js code

var http = require('http');

http.createServer(function (req, res) {

res.writeHead(200, {'Content-Type': 'text/plain'});

res.end('Hello World

');

}).listen(8000);

Results

PHP 7 + Nginx

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:3000

Running 1m test @ http://localhost:3000

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 3.70ms 445.51us 14.14ms 87.20%

Req/Sec 6.79k 434.42 7.69k 70.17%

405200 requests in 1.00m, 77.65MB read

Requests/sec: 6752.42

Transfer/sec: 1.29MB





PHP7+Swoole

root@localhost:~# wrk -t1 -d1m -c25 http://localhost:9000

Running 1m test @ http://localhost:9000

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 0.87ms 46.30us 4.54ms 93.99%

Req/Sec 28.70k 829.61 29.55k 96.17%

1713236 requests in 1.00m, 294.10MB read

Requests/sec: 28553.42

Transfer/sec: 4.90MB

Node.js

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:8000

Running 1m test @ http://localhost:8000

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 2.52ms 570.30us 21.97ms 96.42%

Req/Sec 10.04k 0.90k 10.94k 92.33%

599193 requests in 1.00m, 89.14MB read

Requests/sec: 9986.45

Transfer/sec: 1.49MB

Node.js + Nginx proxy

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:7000

Running 1m test @ http://localhost:7000

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 8.07ms 2.19ms 32.86ms 94.68%

Req/Sec 3.15k 332.57 3.66k 62.83%

187866 requests in 1.00m, 33.49MB read

Requests/sec: 3130.38

Transfer/sec: 571.51KB





Test 2: String concatenating

PHP code

$str = '';

for ($i = 0; $i < 1000; $i++) {

$str .= 's';

}

PHP Swoole code

$http = new swoole_http_server('0.0.0.0', 9001);



$http->on('request', function ($request, $response) {

$response->header('Content-Type', 'text/plain; charset=utf-8');

$str = '';

for ($i = 0; $i < 1000; $i++) {

$str .= 's';

}

$response->end();

});



echo "Server run on port 9001";



$http->start();

Node.js code

var http = require('http');

http.createServer(function (req, res) {

res.writeHead(200, {'Content-Type': 'text/plain'});

var str = '';

for (var i = 0; i < 1000; i++) {

str += 's';

}

res.end();

}).listen(8000);

Results

PHP 7 + Nginx

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:3001

Running 1m test @ http://localhost:3001

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 4.71ms 575.75us 18.54ms 90.33%

Req/Sec 5.34k 317.28 6.11k 77.83%

318642 requests in 1.00m, 55.90MB read

Requests/sec: 5310.43

Transfer/sec: 0.93MB

PHP7+Swoole

root@localhost:~# wrk -t1 -d1m -c25 http://localhost:9001

Running 1m test @ http://localhost:9001

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 2.22ms 128.30us 4.71ms 97.43%

Req/Sec 11.30k 462.90 11.82k 93.17%

674725 requests in 1.00m, 108.10MB read

Requests/sec: 11245.31

Transfer/sec: 1.80MB

Node.js

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:8000

Running 1m test @ http://localhost:8000

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 2.60ms 648.88us 22.61ms 97.30%

Req/Sec 9.75k 1.15k 11.85k 71.17%

582307 requests in 1.00m, 77.19MB read

Requests/sec: 9703.66

Transfer/sec: 1.29MB

Node.js + Nginx proxy

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:7000

Running 1m test @ http://localhost:7000

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 7.64ms 1.33ms 28.32ms 93.47%

Req/Sec 3.29k 341.92 3.79k 67.00%

196658 requests in 1.00m, 31.87MB read

Requests/sec: 3276.66

Transfer/sec: 543.82KB





Test 3: Numbers addition.

PHP code

$count = 0;

for ($i = 0; $i < 1000; $i++) {

$count++;

}

PHP Swoole code

$http = new swoole_http_server('0.0.0.0', 9002);



$http->on('request', function ($request, $response) {

$response->header('Content-Type', 'text/plain; charset=utf-8');

$count = 0;

for ($i = 0; $i < 1000; $i++) {

$count++;

}



$response->end();

});



echo "Server run on port 9002";



$http->start();

Node.js code

var http = require('http');

http.createServer(function (req, res) {

res.writeHead(200, {'Content-Type': 'text/plain'});

var count = 0;

for (var i = 0; i < 1000; i++) {

count++;

}

res.end();

}).listen(8000);

Results

PHP 7 + Nginx

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:3002

Running 1m test @ http://localhost:3002

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 3.73ms 370.34us 14.09ms 92.17%

Req/Sec 6.73k 366.62 7.31k 76.50%

401710 requests in 1.00m, 70.47MB read

Requests/sec: 6695.06

Transfer/sec: 1.17MB

PHP7+Swoole

root@localhost:~# wrk -t1 -d1m -c25 http://localhost:9002

Running 1m test @ http://localhost:9002

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 1.82ms 96.64us 3.55ms 94.52%

Req/Sec 13.79k 379.26 14.26k 92.17%

822827 requests in 1.00m, 131.83MB read

Requests/sec: 13713.45

Transfer/sec: 2.20MB

Node.js

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:8000

Running 1m test @ http://localhost:8000

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 2.07ms 516.25us 20.27ms 96.70%

Req/Sec 12.23k 1.54k 13.77k 70.22%

731643 requests in 1.00m, 96.99MB read

Requests/sec: 12173.77

Transfer/sec: 1.61MB

Node.js + Nginx proxy

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:7000

Running 1m test @ http://localhost:7000

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 6.73ms 790.93us 19.30ms 91.88%

Req/Sec 3.73k 280.68 4.16k 66.67%

222936 requests in 1.00m, 36.13MB read

Requests/sec: 3715.07

Transfer/sec: 616.58KB





Test 4: Filling an array

PHP code

$array = array();

for ($i = 0; $i < 1000; $i++) {

$array[] = 's';

}

PHP Swoole code

$http = new swoole_http_server('0.0.0.0', 9003);



$http->on('request', function ($request, $response) {

$response->header('Content-Type', 'text/plain; charset=utf-8');

$array = array();

for ($i = 0; $i < 1000; $i++) {

$array[] = 's';

}

$response->end();

});



echo "Server run on port 9003";



$http->start();

Node.js code

var http = require('http');

http.createServer(function (req, res) {

res.writeHead(200, {'Content-Type': 'text/plain'});

var array = [];

for (var i = 0; i < 1000; i++) {

array.push('s');

}

res.end();

}).listen(8000);

Results

PHP 7 + Nginx

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:3003

Running 1m test @ http://localhost:3003

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 4.17ms 519.84us 13.91ms 90.60%

Req/Sec 6.02k 512.37 6.64k 72.67%

359544 requests in 1.00m, 63.07MB read

Requests/sec: 5992.33

Transfer/sec: 1.05MB

PHP7+Swoole

root@localhost:~# wrk -t1 -d1m -c25 http://localhost:9003

Running 1m test @ http://localhost:9003

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 2.18ms 295.58us 5.35ms 91.43%

Req/Sec 11.51k 1.08k 12.52k 87.67%

687490 requests in 1.00m, 110.15MB read

Requests/sec: 11457.89

Transfer/sec: 1.84MB

Node.js

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:8000

Running 1m test @ http://localhost:8000

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 2.37ms 549.59us 22.29ms 95.52%

Req/Sec 10.66k 0.92k 11.60k 90.50%

636168 requests in 1.00m, 84.33MB read

Requests/sec: 10602.33

Transfer/sec: 1.41MB

Node.js + Nginx proxy

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:7000

Running 1m test @ http://localhost:7000

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 7.65ms 0.95ms 21.62ms 80.96%

Req/Sec 3.28k 293.56 3.86k 80.33%

195918 requests in 1.00m, 31.75MB read

Requests/sec: 3265.04

Transfer/sec: 541.89KB





Test 4: Filling an associative array

PHP code

$array = array();

for ($i = 0; $i < 1000; $i++) {

$array["s" . $i] = 's';

}

PHP Swoole code

$http = new swoole_http_server('0.0.0.0', 9004);



$http->on('request', function ($request, $response) {

$response->header('Content-Type', 'text/plain; charset=utf-8');

$array = array();

for ($i = 0; $i < 1000; $i++) {

$array["s" . $i] = 's';

}

$response->end();

});



echo "Server run on port 9004";



$http->start();

Node.js code

var http = require('http');

http.createServer(function (req, res) {

res.writeHead(200, {'Content-Type': 'text/plain'});

var array = {};

for (var i = 0; i < 1000; i++) {

array['s' + i] = 's';

}

res.end();

}).listen(8000);

Results

PHP 7 + Nginx

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:3004

Running 1m test @ http://localhost:3004

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 6.80ms 0.90ms 23.24ms 93.70%

Req/Sec 3.70k 250.61 4.18k 78.17%

220722 requests in 1.00m, 38.72MB read

Requests/sec: 3678.66

Transfer/sec: 660.83KB

PHP+Swoole

Running 1m test @ http://localhost:9004

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 4.99ms 0.97ms 13.19ms 90.62%

Req/Sec 5.03k 546.80 5.78k 80.50%

300325 requests in 1.00m, 48.12MB read

Requests/sec: 5005.39

Transfer/sec: 821.20KB

Node.js

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:8000

Running 1m test @ http://localhost:8000

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 15.61ms 1.56ms 39.78ms 93.53%

Req/Sec 1.61k 99.76 1.76k 84.33%

96108 requests in 1.00m, 12.74MB read

Requests/sec: 1601.61

Transfer/sec: 217.41KB

Node.js + Nginx proxy

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:7000

Running 1m test @ http://localhost:7000

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 20.74ms 1.87ms 69.53ms 94.15%

Req/Sec 1.21k 76.66 1.30k 78.67%

72321 requests in 1.00m, 11.72MB read

Requests/sec: 1205.15

Transfer/sec: 200.02KB





Test 5: Reading a file

PHP code

for ($i = 0; $i < 100; $i++) {

$fp = fopen("./demo.txt", "r");

$content = fread($fp, filesize("./demo.txt"));

fclose($fp);

}

PHP Swoole code

$http = new swoole_http_server('0.0.0.0', 9005);



$http->on('request', function ($request, $response) {

$response->header('Content-Type', 'text/plain; charset=utf-8');

for ($i = 0; $i < 100; $i++) {

$fp = fopen("./demo.txt", "r");

$content = fread($fp, filesize("./demo.txt"));

fclose($fp);

}

$response->end('');

});



echo "Server run on port 9005";



$http->start();

Node.js code

var http = require('http');

var fs = require('fs');

http.createServer(function (req, res) {

res.writeHead(200, {'Content-Type': 'text/plain'});

var content;

for (var i = 0; i < 100; i++) {

content = fs.readFileSync('./demo.txt');

}

res.end();

}).listen(8000);

Results

PHP 7 + Nginx

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:3005

Running 1m test @ http://localhost:3005

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 12.38ms 1.76ms 43.99ms 84.59%

Req/Sec 2.03k 181.19 2.37k 76.33%

121118 requests in 1.00m, 21.25MB read

Requests/sec: 2018.54

Transfer/sec: 362.61KB

PHP7+Swoole

root@localhost:~# wrk -t1 -d1m -c25 http://localhost:9005

Running 1m test @ http://localhost:9005

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 8.96ms 0.99ms 19.12ms 92.47%

Req/Sec 2.80k 180.41 3.04k 84.00%

167400 requests in 1.00m, 26.82MB read

Requests/sec: 2789.98

Transfer/sec: 457.73KB

Node.js

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:8000

Running 1m test @ http://localhost:8000

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 11.10ms 1.84ms 42.33ms 83.49%

Req/Sec 2.27k 308.10 2.80k 74.50%

135305 requests in 1.00m, 17.94MB read

Requests/sec: 2254.60

Transfer/sec: 306.04KB

Node.js + Nginx proxy

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:7000

Running 1m test @ http://localhost:7000

1 threads and 25 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 17.63ms 1.24ms 29.01ms 76.75%

Req/Sec 1.42k 88.52 1.74k 77.67%

85080 requests in 1.00m, 13.79MB read

Requests/sec: 1417.80

Transfer/sec: 235.31KB

Conclusion

Node.js is really powerful and very fast server-side environment but behind Nginx proxy does it not so well. PHP 7 + Nginx superior Node.js + Nginx in every test but on other side Node.js superior PHP 7 + Nginx in 4 from 5 tests. Making a conclusion was easy just use both PHP and Nginx combined. PHP is more suitable for the back-end and front-end part of the application but Node.js is more suitable for the real time communication, API implementation and time intensive operations.