Skip to content

toadbeatz/PHP-Runtime-Benchmark

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 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 (6.1.6 & 6.2 with epoll and io_uring variants)
  • 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)
AMPHP (amphp/http-server@3.4.4) 10 2,992 3.25
AMPHP (amphp/http-server@3.4.4) 100 2,233 44.63
Apache mod_php mpm_prefork 10 5,652 1.45
Apache mod_php mpm_prefork 100 7,064 13.61
FrankenPHP classic mode (frankenphp@1.11.2) 10 1,345 7.30
FrankenPHP classic mode (frankenphp@1.11.2) 100 1,595 62.37
FrankenPHP worker mode (frankenphp@1.11.2) 10 1,450 6.76
FrankenPHP worker mode (frankenphp@1.11.2) 100 1,745 57.12
Nginx PHP-FPM 10 5,460 1.49
Nginx PHP-FPM 100 7,634 12.69
NodeJS (v25-alpine) 10 1,283 7.61
NodeJS (v25-alpine) 100 5,288 18.80
OpenSwoole (ext-openswoole@25.2.0, 1 reactor thread, 1 worker process) 10 5,394 1.76
OpenSwoole (ext-openswoole@25.2.0, 1 reactor thread, 1 worker process) 100 4,362 22.80
ReactPHP (react/http@1.11.0) 10 6,108 1.54
ReactPHP (react/http@1.11.0) 100 8,991 11.02
RoadRunner (rr@2025.1.6, http.pool.num_workers=1) 10 1,128 8.75
RoadRunner (rr@2025.1.6, http.pool.num_workers=1) 100 1,087 91.67
Rust ActiX Web (v4.12.1) 10 8,390 1.10
Rust ActiX Web (v4.12.1) 100 15,403 6.40
Rust Rocket (v0.5.1) 10 6,521 1.45
Rust Rocket (v0.5.1) 100 8,908 11.13
Swoole (ext-swoole@6.1.6) 10 2,778 3.45
Swoole (ext-swoole@6.1.6) 100 3,659 27.17
Swoole 6.2 epoll (ext-swoole@6.2-dev, epoll) 10 6,818 1.37
Swoole 6.2 epoll (ext-swoole@6.2-dev, epoll) 100 2,674 37.22
Swoole 6.2 io_uring (ext-swoole@6.2-dev, io_uring) 10 5,672 1.65
Swoole 6.2 io_uring (ext-swoole@6.2-dev, io_uring) 100 9,290 10.61
Swoole epoll (ext-swoole@6.1.6, epoll) 10 3,736 2.55
Swoole epoll (ext-swoole@6.1.6, epoll) 100 3,379 29.39
Swoole io_uring (ext-swoole@6.1.6, io_uring) 10 4,543 2.09
Swoole io_uring (ext-swoole@6.1.6, io_uring) 100 3,943 25.21
Workerman (workerman/workerman@5.1.9, 1 worker) 10 5,306 1.77
Workerman (workerman/workerman@5.1.9, 1 worker) 100 15,256 6.46

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 15 seconds and voilà!

Docs of different runtimes

About

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

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • PHP 91.6%
  • Rust 4.1%
  • TypeScript 2.5%
  • JavaScript 1.8%