Benchmarks for ext-websocket against PHP WebSocket libraries.
These benchmarks compare the current native protocol helpers and native server runtime with PHP WebSocket libraries.
The protocol AMPHP entry point installs amphp/websocket-server and measures the RFC 6455 parser/compiler it uses through amphp/websocket.
The protocol OpenSwoole entry point measures OpenSwoole\WebSocket\Server::pack() and OpenSwoole\WebSocket\Server::unpack().
OpenSwoole is optional because it is a PHP extension installed outside Composer.
The protocol suite measures hot paths:
- server-side text frame encoding
- masked client text frame decoding
The server runtime suite measures WebSocket HTTP Upgrade close paths.
The message runtime suite measures complete WebSocket server scenarios:
- upgraded idle connections
- pipelined echo messages
- broadcast fanout deliveries
- direct
ws://transport andwss://through the same local TLS terminator
Environment: PHP 8.4.21, xdebug.mode=off, zend.assertions=-1, Apple Silicon macOS, 100,000 iterations for 64B payloads and 20,000 iterations for 1024B payloads. Results from May 18, 2026.
AMPHP WebSocket Server v4.0.0, Ratchet v0.4.0, and Workerman v5.2.0 were installed from Composer. OpenSwoole v26.2.0 was installed from PECL.
| Benchmark | amphp/websocket-server | ratchet/rfc6455 | workerman/workerman | openswoole | ext-websocket |
|---|---|---|---|---|---|
encode text 64B |
3,102,715 ops/sec | 1,493,649 ops/sec | 4,406,847 ops/sec | 2,679,178 ops/sec | 15,134,317 ops/sec |
decode masked text 64B |
592,968 ops/sec | 566,960 ops/sec | 1,776,096 ops/sec | 4,058,675 ops/sec | 7,020,335 ops/sec |
encode text 1024B |
2,467,892 ops/sec | 1,323,974 ops/sec | 3,537,032 ops/sec | 6,056,783 ops/sec | 12,041,548 ops/sec |
decode masked text 1024B |
358,676 ops/sec | 278,047 ops/sec | 846,768 ops/sec | 3,694,837 ops/sec | 5,155,027 ops/sec |
Environment: PHP 8.4.21, xdebug.mode=off, zend.assertions=-1, Apple Silicon macOS, 1,000 connections, average of 3 runs. Results from May 23, 2026. ext-websocket used the native kqueue driver; OpenSwoole was built with kqueue enabled.
| Benchmark | amphp/websocket-server | workerman/workerman | openswoole | cboden/ratchet | ext-websocket |
|---|---|---|---|---|---|
websocket upgrade/close |
3,244 connections/sec | 10,525 connections/sec | 10,158 connections/sec | 6,364 connections/sec | 10,959 connections/sec |
client upgrade loop |
2,510 connections/sec | 5,594 connections/sec | 3,135 connections/sec | 4,548 connections/sec | 6,174 connections/sec |
websocket subprotocol upgrade/close |
3,245 connections/sec | 9,342 connections/sec | 9,212 connections/sec | 6,333 connections/sec | 10,468 connections/sec |
client subprotocol upgrade loop |
2,543 connections/sec | 5,189 connections/sec | 2,831 connections/sec | 4,544 connections/sec | 6,016 connections/sec |
This starts a fresh server process, then measures the current server runtime surface up to the last accepted connection. All rows perform real HTTP Upgrades. Application message throughput lives in the message-runtime table below.
ratchet/rfc6455is not listed here because the benchmarked package exposes protocol helpers, not a server runtime.
Environment: PHP 8.4.21, xdebug.mode=off, zend.assertions=-1, Apple Silicon macOS, 50 upgraded connections, 1,000 pipelined echo/broadcast source messages, 1024B text payloads, average of 3 runs. Results from May 18, 2026. The wss rows use the same local TLS terminator in front of each server, so the comparison isolates WebSocket runtime behavior under encrypted transport rather than comparing different TLS implementations.
ws://
| Benchmark | amphp/websocket-server | workerman/workerman | openswoole | ext-websocket |
|---|---|---|---|---|
idle upgraded connections |
2,785 connections/sec | 5,363 connections/sec | 4,783 connections/sec | 5,082 connections/sec |
echo pipelined messages |
45,502 messages/sec | 51,332 messages/sec | 50,515 messages/sec | 70,059 messages/sec |
broadcast fanout deliveries |
186,907 deliveries/sec | 227,944 deliveries/sec | 229,098 deliveries/sec | 818,959 deliveries/sec |
wss:// with the shared local TLS terminator:
| Benchmark | amphp/websocket-server | workerman/workerman | openswoole | ext-websocket |
|---|---|---|---|---|
idle upgraded connections |
356 connections/sec | 428 connections/sec | 412 connections/sec | 442 connections/sec |
echo pipelined messages |
24,783 messages/sec | 29,501 messages/sec | 31,391 messages/sec | 29,735 messages/sec |
broadcast fanout deliveries |
105,500 deliveries/sec | 163,485 deliveries/sec | 210,075 deliveries/sec | 610,215 deliveries/sec |
composer installOpenSwoole is optional and is installed as a PHP extension:
pecl install openswoole-26.2.0On Homebrew macOS, if pcre2.h is not found during compilation:
CPPFLAGS="-I/opt/homebrew/include" LDFLAGS="-L/opt/homebrew/lib" pecl install -f openswoole-26.2.0From the repository root, build the extension in the sibling php-websocket checkout first, then run the benchmark with the same PHP binary.
If the extension lives elsewhere, pass WEBSOCKET_EXTENSION=/path/to/websocket.so for server and message runtime benchmarks.
# ext-websocket
php -d xdebug.mode=off -d zend.assertions=-1 -d extension="../php-websocket/modules/websocket.so" protocol/websocket.php [iterations]
php -d xdebug.mode=off -d zend.assertions=-1 -d extension="../php-websocket/modules/websocket.so" server-runtime/websocket.php [connections] [rounds]
php -d xdebug.mode=off -d zend.assertions=-1 -d extension="../php-websocket/modules/websocket.so" server-runtime/websocket-subprotocol.php [connections] [rounds]
php -d xdebug.mode=off -d zend.assertions=-1 -d extension="../php-websocket/modules/websocket.so" message-runtime/websocket.php [connections] [messages] [rounds] [ws|wss|both] [payload-bytes]
# PHP libraries
php -d xdebug.mode=off -d zend.assertions=-1 protocol/amphp.php [iterations]
php -d xdebug.mode=off -d zend.assertions=-1 server-runtime/amphp.php [connections] [rounds]
php -d xdebug.mode=off -d zend.assertions=-1 server-runtime/amphp-subprotocol.php [connections] [rounds]
php -d xdebug.mode=off -d zend.assertions=-1 message-runtime/amphp.php [connections] [messages] [rounds] [ws|wss|both] [payload-bytes]
php -d xdebug.mode=off -d zend.assertions=-1 protocol/ratchet.php [iterations]
php -d xdebug.mode=off -d zend.assertions=-1 server-runtime/ratchet.php [connections] [rounds]
php -d xdebug.mode=off -d zend.assertions=-1 server-runtime/ratchet-subprotocol.php [connections] [rounds]
php -d xdebug.mode=off -d zend.assertions=-1 protocol/workerman.php [iterations]
php -d xdebug.mode=off -d zend.assertions=-1 server-runtime/workerman.php [connections] [rounds]
php -d xdebug.mode=off -d zend.assertions=-1 server-runtime/workerman-subprotocol.php [connections] [rounds]
php -d xdebug.mode=off -d zend.assertions=-1 message-runtime/workerman.php [connections] [messages] [rounds] [ws|wss|both] [payload-bytes]
# Native OpenSwoole extension, when installed
php -d xdebug.mode=off -d zend.assertions=-1 protocol/openswoole.php [iterations]
php -d xdebug.mode=off -d zend.assertions=-1 server-runtime/openswoole.php [connections] [rounds]
php -d xdebug.mode=off -d zend.assertions=-1 server-runtime/openswoole-subprotocol.php [connections] [rounds]
php -d xdebug.mode=off -d zend.assertions=-1 message-runtime/openswoole.php [connections] [messages] [rounds] [ws|wss|both] [payload-bytes]With Homebrew PHP 8.3:
cd ../php-websocket
/opt/homebrew/opt/php@8.3/bin/phpize
./configure --enable-websocket --with-php-config=/opt/homebrew/opt/php@8.3/bin/php-config
make -j"$(sysctl -n hw.ncpu)"
cd ../php-websocket-bench
/opt/homebrew/opt/php@8.3/bin/php \
-d zend.assertions=-1 \
-d extension="../php-websocket/modules/websocket.so" \
protocol/websocket.phpDefault: 100,000 protocol iterations, 1,000 server-runtime connections over 3 rounds, or 50 message-runtime connections with 1,000 messages, 3 rounds, ws, and 64B payloads.
| Benchmark | What it tests |
|---|---|
encode text 64B |
Server-side text frame encoding for a small payload |
decode masked text 64B |
Masked client text frame decoding for a small payload |
encode text 1024B |
Server-side text frame encoding for a larger payload |
decode masked text 1024B |
Masked client text frame decoding for a larger payload |
| Benchmark | What it tests |
|---|---|
websocket upgrade/close |
Native WebSocket\Server::run() HTTP Upgrade path and connection cleanup |
websocket subprotocol upgrade/close |
Native HTTP Upgrade path with Sec-WebSocket-Protocol selection and connection cleanup |
client upgrade loop |
Client-side loop overhead while opening and upgrading benchmark WebSocket connections |
client subprotocol upgrade loop |
Client-side loop overhead while opening, upgrading, and validating negotiated subprotocols |
| Benchmark | What it tests |
|---|---|
idle upgraded connections |
Open and hold upgraded WebSocket connections |
echo pipelined messages |
Receive many outstanding text messages, dispatch onMessage, send replies, and read them client-side |
broadcast fanout deliveries |
One incoming message fanned out to all connected clients |
| File | Description |
|---|---|
protocol/common.php |
Shared RFC 6455 encode/decode benchmark logic |
protocol/websocket.php |
ext-websocket protocol benchmark |
protocol/amphp.php |
amphp/websocket-server protocol benchmark |
protocol/ratchet.php |
ratchet/rfc6455 protocol benchmark |
protocol/workerman.php |
workerman/workerman protocol benchmark |
protocol/openswoole.php |
OpenSwoole protocol benchmark |
server-runtime/common.php |
Shared HTTP Upgrade benchmark runner |
server-runtime/websocket.php |
ext-websocket HTTP Upgrade benchmark |
server-runtime/websocket-subprotocol.php |
ext-websocket HTTP Upgrade benchmark with subprotocol negotiation |
server-runtime/amphp.php |
AMPHP WebSocket Server HTTP Upgrade benchmark |
server-runtime/amphp-subprotocol.php |
AMPHP WebSocket Server HTTP Upgrade benchmark with subprotocol negotiation |
server-runtime/ratchet.php |
cboden/ratchet HTTP Upgrade benchmark |
server-runtime/ratchet-subprotocol.php |
cboden/ratchet HTTP Upgrade benchmark with subprotocol negotiation |
server-runtime/workerman.php |
Workerman HTTP Upgrade benchmark |
server-runtime/workerman-subprotocol.php |
Workerman HTTP Upgrade benchmark with subprotocol negotiation |
server-runtime/openswoole.php |
OpenSwoole HTTP Upgrade benchmark |
server-runtime/openswoole-subprotocol.php |
OpenSwoole HTTP Upgrade benchmark with subprotocol negotiation |
server-runtime/servers/*.php |
Isolated server processes used by the runtime benchmarks |
message-runtime/common.php |
Shared WebSocket message-runtime benchmark runner |
message-runtime/tls_proxy.php |
Local TLS terminator used by wss message-runtime benchmarks |
message-runtime/websocket.php |
ext-websocket message-runtime benchmark |
message-runtime/amphp.php |
AMPHP WebSocket Server message-runtime benchmark |
message-runtime/workerman.php |
Workerman message-runtime benchmark |
message-runtime/openswoole.php |
OpenSwoole message-runtime benchmark |
message-runtime/servers/*.php |
Isolated server processes used by the message-runtime benchmarks |