Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 118 additions & 1 deletion .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ on:
- java
- ruby
- node
- php

jobs:
benchmark-java:
Expand Down Expand Up @@ -164,6 +165,122 @@ jobs:
path: ${{ steps.names.outputs.result_file }}
retention-days: 30

test-php:
# Fast, server-free PHP engine tests. Uses the recording driver, so it needs
# neither the valkey_glide extension nor a running server.
if: ${{ inputs.engines == 'all' || inputs.engines == 'php' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: json, pcntl
tools: composer

- name: Install PHP dependencies
run: cd php && composer install --no-interaction --prefer-dist

- name: Run PHP unit tests
run: cd php && vendor/bin/phpunit --testsuite unit

- name: Run PHP integration tests (server-free)
run: cd php && vendor/bin/phpunit --testsuite integration

benchmark-php:
if: ${{ inputs.engines == 'all' || inputs.engines == 'php' }}
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
driver:
- configs/drivers/default/valkey-glide-php.json
workload:
- configs/workloads/reference/basic-standalone-single-client-1M-reqs.json

steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: json, pcntl, bcmath
tools: composer

- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential php-dev pkg-config \
libssl-dev libffi-dev libprotobuf-c-dev protobuf-c-compiler unzip

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable

- name: Install cbindgen
run: cargo install cbindgen

- name: Build & install valkey_glide extension
run: |
# Build the Valkey GLIDE PHP extension from the upstream repo.
git clone --recurse-submodules https://github.com/valkey-io/valkey-glide-php.git /tmp/vg-php
cd /tmp/vg-php
python3 utils/patch_proto_and_rust.py
(cd valkey-glide/ffi && cargo build --release)
phpize
./configure --enable-valkey-glide
make build-modules-pre
make -j"$(nproc)"
sudo make install
echo "extension=valkey_glide" | sudo tee -a "$(php -i | grep '^Loaded Configuration File' | awk '{print $NF}')"
php -m | grep valkey_glide

- name: Install PHP dependencies
run: cd php && composer install --no-interaction --prefer-dist --optimize-autoloader

- name: Start Valkey server
run: |
make server-standalone-start
sleep 2
work/valkey/bin/valkey-cli ping
work/valkey/bin/valkey-cli CONFIG GET save

- name: Extract names for result file
id: names
run: |
DRIVER_NAME=$(basename ${{ matrix.driver }} .json)
WORKLOAD_NAME=$(basename ${{ matrix.workload }} .json)
echo "driver_name=$DRIVER_NAME" >> $GITHUB_OUTPUT
echo "workload_name=$WORKLOAD_NAME" >> $GITHUB_OUTPUT
echo "result_file=results/github-runner/reference/${DRIVER_NAME}-${WORKLOAD_NAME}.ndjson" >> $GITHUB_OUTPUT

- name: Run benchmark
run: |
mkdir -p results/github-runner/reference
php php/bin/resp-bench \
--server localhost:6379 \
--driver ${{ matrix.driver }} \
--workload ${{ matrix.workload }} \
--metrics ${{ steps.names.outputs.result_file }} \
--commit-id ${{ github.sha }}

- name: Stop Valkey server
if: always()
run: |
make server-standalone-stop || true

- name: Upload results
uses: actions/upload-artifact@v4
with:
name: benchmark-php-${{ steps.names.outputs.driver_name }}-${{ steps.names.outputs.workload_name }}
path: ${{ steps.names.outputs.result_file }}
retention-days: 30

benchmark-node:
if: ${{ inputs.engines == 'all' || inputs.engines == 'node' }}
runs-on: ubuntu-latest
Expand Down Expand Up @@ -239,7 +356,7 @@ jobs:
retention-days: 30

generate-graphs:
needs: [benchmark-java, benchmark-ruby, benchmark-node]
needs: [benchmark-java, benchmark-ruby, benchmark-php, benchmark-node]
# A skipped `needs` job would skip this one by default, so an engine-scoped
# run would produce no graphs at all. Individual graph steps are gated on the
# same input instead.
Expand Down
63 changes: 63 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ WORK_DIR=$(shell pwd)/work
python-build python-test python-run python-clean \
ruby-build ruby-test ruby-run ruby-clean ruby-info \
csharp-build csharp-test csharp-run csharp-clean csharp-info \
php-build php-test php-integration-test php-test-live php-run php-clean php-info \
node-build node-deps node-test node-unit-test node-integration-test \
node-run node-clean node-info \
config-editor-build config-editor-dev
Expand Down Expand Up @@ -396,6 +397,68 @@ csharp-clean:
csharp-info: csharp-build
dotnet run --project $(CSHARP_PROJECT) -c Release -- --info

# ============================================================================
# ============================================================================
# PHP Engine
# ============================================================================

# Prefer composer's autoloader; the CLI falls back to a minimal PSR-4 loader,
# so php-run/php-info work even before `composer install`.
PHP?=php

php-build:
cd php && if command -v composer >/dev/null 2>&1; then \
composer install --no-interaction --prefer-dist --optimize-autoloader; \
else \
echo "composer not found; using the bundled minimal autoloader (php/vendor/autoload.php)"; \
fi

php-test:
cd php && if [ -f vendor/bin/phpunit ]; then \
vendor/bin/phpunit --testsuite unit; \
elif [ -f /tmp/phpunit.phar ]; then \
$(PHP) /tmp/phpunit.phar --testsuite unit; \
else \
echo "PHPUnit not installed. Run 'make php-build' (composer) or download phpunit.phar."; \
exit 1; \
fi

php-integration-test:
cd php && if [ -f vendor/bin/phpunit ]; then \
vendor/bin/phpunit --testsuite integration; \
elif [ -f /tmp/phpunit.phar ]; then \
$(PHP) /tmp/phpunit.phar --testsuite integration; \
else \
echo "PHPUnit not installed. Run 'make php-build' (composer) or download phpunit.phar."; \
exit 1; \
fi

# Live integration tests for the valkey-glide-php driver. Requires the
# valkey_glide extension AND a reachable server (VALKEY_HOST/VALKEY_PORT,
# default localhost:6379). Tests skip cleanly if either is missing.
php-test-live:
cd php && if [ -f vendor/bin/phpunit ]; then \
vendor/bin/phpunit --testsuite integration --filter 'LiveClientTest|PhpRedisLiveTest'; \
elif [ -f /tmp/phpunit.phar ]; then \
$(PHP) /tmp/phpunit.phar --testsuite integration --filter 'LiveClientTest|PhpRedisLiveTest'; \
else \
echo "PHPUnit not installed. Run 'make php-build' (composer) or download phpunit.phar."; \
exit 1; \
fi

php-run: php-build
$(PHP) php/bin/resp-bench \
--server $(SERVER) \
--driver $(DRIVER) \
--workload $(WORKLOAD) \
--metrics $(METRICS_OUTPUT)

php-clean:
cd php && rm -rf vendor composer.lock .phpunit.cache output

php-info:
$(PHP) php/bin/resp-bench --info

# ============================================================================
# Node.js Engine
# ============================================================================
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ Thread-based system metrics collector that runs alongside benchmarks, collecting
| Java | ✅ Ready | Jedis, Lettuce, Valkey-Glide, Redisson, Spring Data Valkey/Redis |
| Ruby | ✅ Ready | redis-rb, valkey-glide-ruby |
| C# | ✅ Ready | valkey-glide-csharp, StackExchange.Redis |
| PHP | ✅ Ready | valkey-glide-php, PHPRedis |
| Node.js | ✅ Ready | valkey-glide-node, ioredis, iovalkey |
| Python | 🚧 Planned | redis-py, aioredis, valkey-glide |

Expand Down Expand Up @@ -135,6 +136,7 @@ resp-bench/
├── java/ # Java benchmark engine
├── ruby/ # Ruby benchmark engine
├── csharp/ # C# (.NET 10) benchmark engine
├── php/ # PHP benchmark engine
├── node/ # Node.js (TypeScript) benchmark engine
├── docs/
│ ├── ARCHITECTURE.md # System architecture
Expand All @@ -144,6 +146,7 @@ resp-bench/
│ ├── BENCHMARKS_JAVA.md # Java benchmark details
│ ├── BENCHMARKS_CSHARP.md # C# benchmark details
│ ├── BENCHMARKS_RUBY.md # Ruby benchmark details
│ ├── BENCHMARKS_PHP.md # PHP benchmark details
│ └── BENCHMARKS_NODE.md # Node.js benchmark details
└── graphs/interactive/ # Generated HTML graphs
```
Expand Down Expand Up @@ -201,6 +204,7 @@ See [docs/CONFIG_SPECIFICATION.md](docs/CONFIG_SPECIFICATION.md) for full detail
| `make java-test` | Run Java unit tests |
| `make ruby-test` | Run Ruby tests |
| `make csharp-test` | Run C# tests |
| `make php-test` | Run PHP unit tests |
| `make node-test` | Run Node.js tests (unit + integration) |

### Engines
Expand All @@ -210,6 +214,7 @@ See [docs/CONFIG_SPECIFICATION.md](docs/CONFIG_SPECIFICATION.md) for full detail
| `make java-run` | Run Java engine (DRIVER, WORKLOAD, SERVER) |
| `make ruby-run` | Run Ruby engine (DRIVER, WORKLOAD, SERVER) |
| `make csharp-run` | Run C# engine (DRIVER, WORKLOAD, SERVER) |
| `make php-run` | Run PHP engine (DRIVER, WORKLOAD, SERVER) |
| `make node-run` | Run Node.js engine (DRIVER, WORKLOAD, SERVER) |
| `make java-build` | Build Java JAR |
| `make csharp-build` | Build C# executable |
Expand Down
7 changes: 7 additions & 0 deletions configs/drivers/default/phpredis.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"schema_version": "1.0",
"description": "PHPRedis (ext-redis) - default configuration",
"driver_id": "phpredis",
"mode": "standalone",
"specific_driver_config": {}
}
7 changes: 7 additions & 0 deletions configs/drivers/default/valkey-glide-php.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"schema_version": "1.0",
"description": "Valkey GLIDE PHP - default configuration",
"driver_id": "valkey-glide-php",
"mode": "standalone",
"specific_driver_config": {}
}
7 changes: 7 additions & 0 deletions configs/drivers/example-phpredis-standalone.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"schema_version": "1.0",
"description": "PHPRedis (ext-redis) client - standalone mode",
"driver_id": "phpredis",
"mode": "standalone",
"specific_driver_config": {}
}
7 changes: 7 additions & 0 deletions configs/drivers/example-valkey-glide-php-standalone.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"schema_version": "1.0",
"description": "Valkey GLIDE PHP client - standalone mode",
"driver_id": "valkey-glide-php",
"mode": "standalone",
"specific_driver_config": {}
}
7 changes: 7 additions & 0 deletions configs/drivers/high-throughput/phpredis.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"schema_version": "1.0",
"description": "PHPRedis (ext-redis) - high-throughput configuration",
"driver_id": "phpredis",
"mode": "standalone",
"specific_driver_config": {}
}
7 changes: 7 additions & 0 deletions configs/drivers/high-throughput/valkey-glide-php.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"schema_version": "1.0",
"description": "Valkey GLIDE PHP - high-throughput configuration",
"driver_id": "valkey-glide-php",
"mode": "standalone",
"specific_driver_config": {}
}
19 changes: 19 additions & 0 deletions configs/matrices/php-driver-comparison.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"description": "Compare PHP clients (Valkey GLIDE PHP vs PHPRedis) across client counts",
"x_axis": "connections",
"workload_template": "configs/workloads/reference/basic-standalone-single-client-1M-reqs.json",
"iterations": 5,
"dimensions": {
"connections": [
1,
2,
4,
8,
16
],
"driver_config": [
"configs/drivers/high-throughput/valkey-glide-php.json",
"configs/drivers/high-throughput/phpredis.json"
]
}
}
69 changes: 69 additions & 0 deletions docs/BENCHMARKS_PHP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# PHP Client Benchmarks — Full Details

Performance benchmarking of PHP client libraries against RESP-compatible servers,
using the resp-bench PHP engine.

> **Note**: Graphs are generated by CI once benchmark runs are published. Until
> then, this document describes the engine, drivers, and workload. See
> [../php/README.md](../php/README.md) for engine internals.

## Drivers Tested

| Driver | Package | Description |
|--------|---------|-------------|
| `valkey-glide-php` | [ext-valkey_glide](https://github.com/valkey-io/valkey-glide-php) | Valkey GLIDE PHP client — a native PHP extension backed by a Rust core, exposing a PHPRedis-compatible API |

The `recording` driver is used for server-free CI tests and is not part of the
performance comparison.

## Engine Notes

- **Language/runtime**: PHP 8.2+ (GLIDE extension tested on 8.2/8.3).
- **Concurrency**: process-per-connection via `pcntl` (capped at 256 workers).
Each worker owns one client connection, opened *after* forking, and keeps a
single request in flight — matching the "one in-flight request per connection"
model used by the Java, Ruby, C#, and Node engines so results are comparable.
- **Metrics**: HdrHistogram range `(1, 600_000_000, 3)`; per-worker histograms
are merged losslessly in the parent process and emitted as Java-compatible V2
compressed base64 payloads.
- **Rate limiting**: leaky-bucket, with phase-level `rps_limit` divided across
workers.

## Prerequisites for live runs

- The `valkey_glide` PHP extension installed and enabled (`extension=valkey_glide`).
- `ext-pcntl` (standard on Linux/macOS CLI PHP).

See [../php/README.md](../php/README.md#installing-the-valkey-glide-php-extension)
for installation.

## Running

```bash
make server-standalone-start
make php-run \
DRIVER=configs/drivers/example-valkey-glide-php-standalone.json \
WORKLOAD=configs/workloads/reference/basic-standalone-single-client-1M-reqs.json \
SERVER=localhost:6379 \
METRICS_OUTPUT=output/php.ndjson
```

Or through the matrix orchestrator (the `valkey-glide-php` driver id maps to the
`php` engine):

```bash
python scripts/run_benchmark_matrix.py \
--matrix configs/matrices/<your-matrix>.json \
--output-dir results/php-run \
--server-host localhost
```

## Workload Configuration

PHP runs use the same reference workloads as the other engines (see
`configs/workloads/reference/`), so cross-language comparisons are apples-to-apples:
- 1M requests per steady phase
- 1M keys, 16-byte keys, `bench:` prefix
- 512-byte SET values
- GET/SET mix via uniform-random key selection
- No rate limiting for max-throughput runs
Loading