Skip to content

cracksalad/PHP-Runtime-Benchmark

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PHP Runtime Benchmarks

There are quite a few PHP runtimes right now and all of them address performance as one of the main concerns. So the obvious question is, which one is actually the fastest. Since runtimes are hard to compare in total, we have to start somewhere: I chose HTTP Server as the first use case to compare the runtimes in.

This whole benchmark is oriented on "Performance benchmark of PHP runtimes" by Dzmitry Kazbiarovich from January 2024, which lacks AMPHP and ReactPHP as runtime alternatives and is pretty focused on Symfony.

So this benchmarks work independent of Symfony. The actual measurements are performed by k6 by Grafana Labs.

Featuring...

  • AMPHP
  • FrankenPHP (classic and worker mode)
  • OpenSwoole
  • ReactPHP
  • RoadRunner
  • Swoole
  • Workerman

If you want to see other alternatives, please let me know!

As references I chose Apache mod_php with mpm_prefork as well as Nginx with PHP-FPM as baseline, Rust ActiX Web as some kind of upper limit and NodeJS as the probably main competitor.

AMPHP

AMPHP uses modern PHP features like Fibers to provide pseudo-parallel execution with Coroutines.

This benchmark currently does not look at event loop extensions, which are supported by Revolt (which is internally used by AMPHP). See https://revolt.run/extensions.

FrankenPHP and RoadRunner

FrankenPHP and RoadRunner are different Go implementations of the PHP runtime.

ReactPHP

ReactPHP is a PHP library for event-driven programming introducing an event loop.

Swoole and OpenSwoole

Swoole and OpenSwoole are C++ extensions for PHP which include e.g. an HTTP server and provide Coroutine, Thread and Process based concepts. OpenSwoole is actually a fork of Swoole.

Results

During the benchmark, the servers handled as many requests as they can in a fixed amount of time and with different amounts of concurrent requests. The servers respond with a simple "Hello, world!" and a Content-Type: text/plain header as well as a status code 200. The following numbers have been measured/calculated by k6:

Requests per Second Average Response Time

Raw numbers

All HTTP servers run in an Alpine-based PHP 8.4 Docker image limited to a single CPU core to get comparable results. Memory is not limited since it is not expected to make any difference here.

Runtime VUS Requests per second Average response time (ms)
Apache mod_php mpm_prefork 10 8,122 1.17
Apache mod_php mpm_prefork 100 6,873 14.47
Apache mod_php mpm_prefork 1000 3,401 176
Nginx PHP-FPM 10 4,054 2.41
Nginx PHP-FPM 100 3,755 26.5
Nginx PHP-FPM 1000 4,222 235
AMPHP (amphp/[email protected]) 10 240 41.5
AMPHP (amphp/[email protected]) 100 2,407 41.5
AMPHP (amphp/[email protected]) 1000 10,455 95.3
FrankenPHP classic mode ([email protected]) 10 9,033 1.07
FrankenPHP classic mode ([email protected]) 100 8,501 11.7
FrankenPHP classic mode ([email protected]) 1000 8,669 115
FrankenPHP worker mode ([email protected]) 10 11,115 0.87
FrankenPHP worker mode ([email protected]) 100 10,395 9.58
FrankenPHP worker mode ([email protected]) 1000 10,237 97.4
OpenSwoole ([email protected], 1 reactor thread, 1 worker process) 10 22,555 0.41
OpenSwoole ([email protected], 1 reactor thread, 1 worker process) 100 22,214 4.46
OpenSwoole ([email protected], 1 reactor thread, 1 worker process) 1000 20,469 48.7
OpenSwoole ([email protected], 1 reactor thread, 2 worker processes) 10 19,013 0.490
OpenSwoole ([email protected], 1 reactor thread, 2 worker processes) 100 19,260 5.13
OpenSwoole ([email protected], 1 reactor thread, 2 worker processes) 1000 19,949 49.95
OpenSwoole ([email protected], 2 reactor threads, 2 worker processes) 10 16,427 0.571
OpenSwoole ([email protected], 2 reactor threads, 2 worker processes) 100 16,611 5.96
OpenSwoole ([email protected], 2 reactor threads, 2 worker processes) 1000 15,907 62.56
ReactPHP (react/[email protected]) 10 37,935 0.236
ReactPHP (react/[email protected]) 100 38,903 2.52
ReactPHP (react/[email protected]) 1000 32,651 30.5
RoadRunner ([email protected], http.pool.num_workers=1) 10 6,327 1.55
RoadRunner ([email protected], http.pool.num_workers=1) 100 6,080 16.4
RoadRunner ([email protected], http.pool.num_workers=1) 1000 5,868 170
RoadRunner ([email protected], http.pool.num_workers=2) 10 5,732 1.71
RoadRunner ([email protected], http.pool.num_workers=2) 100 5,588 17.85
RoadRunner ([email protected], http.pool.num_workers=2) 1000 5,402 184
RoadRunner ([email protected], http.pool.num_workers=3) 10 5,188 1.88
RoadRunner ([email protected], http.pool.num_workers=3) 100 5,038 19.8
RoadRunner ([email protected], http.pool.num_workers=3) 1000 4,863 205
Swoole ([email protected]) 10 42,548 0.209
Swoole ([email protected]) 100 39,665 2.48
Swoole ([email protected]) 1000 35,634 28
Workerman (workerman/[email protected], 1 worker, no event/swoole/swow) 10 70,911 0.105
Workerman (workerman/[email protected], 1 worker, no event/swoole/swow) 100 79,957 1.2
Workerman (workerman/[email protected], 1 worker, no event/swoole/swow) 1000 63,015 15.7
Workerman (workerman/[email protected], 2 workers, no event/swoole/swow) 10 61,787 0.136
Workerman (workerman/[email protected], 2 workers, no event/swoole/swow) 100 69,186 1.36
Workerman (workerman/[email protected], 2 workers, no event/swoole/swow) 1000 57,131 16.5
Rust ActiX Web (v4.12.1) 10 69,655 0.109
Rust ActiX Web (v4.12.1) 100 71,923 1.34
Rust ActiX Web (v4.12.1) 1000 60,664 16.34
NodeJS (v25.2.1) 10 50,208 0.172
NodeJS (v25.2.1) 100 47,932 2.05
NodeJS (v25.2.1) 1000 39,121 25.5

Notes

First of all, it should be noted, that I am comparing mostly stock configurations here. There are most probably ways to tweak the performance of the individual runtimes. Feel free to look at the server implementations and test your own configurations (and please let me know if you find something interesting!).

  • The average response time seems to be roughly proportional to the amount of concurrent requests.
  • AMPHP is really bad below 1000 parallel requests, but it outperformes FrankenPHP and RoadRunner at 1000 parallel requests.
  • Although ReactPHP is plain PHP - no Go, no C++ - it is way faster than I expected.
  • Why is OpenSwoole about half as fast as Swoole? They are expected to be quite similar.

How to benchmark

  1. Start a server of your choice from the src folder.
    1. cd src/<runtime>
    2. docker build -t cracksalad/php-runtime-benchmark-http-server-<runtime> .
    3. docker run --rm --cpus 1 -p 1337:1337 -it cracksalad/php-runtime-benchmark-http-server-<runtime>
      • for Apache, use container port 80
      • for Nginx, use container port 8080
  2. Run k6 run --vus <VUS> bench/mark.ts with <VUS> being the number of parallel executions.
  3. Wait 30 seconds and voilà!

Docs of different runtimes

About

Let's compare HTTP server performance of Swoole, ReactPHP, AMPHP etc.

Topics

Resources

License

Stars

Watchers

Forks