FrankenVSRoadRunner
Benchmark application comparing FrankenPHP and RoadRunner on the Symfony 8 & PHP 8.4 stack. Includes multiple scenarios testing CPU, I/O, memory, and serialization performance in containerized environments.
Install / Use
/learn @mattleads/FrankenVSRoadRunnerREADME
Symfony 8 Benchmark: FrankenPHP vs. RoadRunner
This project is a comprehensive benchmark application built with Symfony 8, designed to compare the performance, memory management, and stability of two popular PHP application servers: FrankenPHP and RoadRunner.
It includes a suite of API endpoints that simulate various real-world workloads, from simple health checks to CPU-intensive image processing and memory-heavy serialization tasks.
Key Features
- Multiple Benchmark Scenarios: Test different aspects of server performance.
- Dockerized Environments: Easy-to-use, isolated setups for both FrankenPHP and RoadRunner.
- Symfony 8 & PHP 8.4: Built with modern PHP and the latest Symfony framework.
- Best Practices: Demonstrates correct state management (
ResetInterface) for long-running worker environments. - Realistic Workloads: Includes tests for CPU, I/O, memory pressure, and stateful services.
Technologies Used
- PHP 8.4
- Symfony 8.0
- Doctrine ORM
- FrankenPHP (via Docker)
- RoadRunner (via Docker)
- Docker & Docker Compose
Prerequisites
- Docker
- Docker Compose
Installation
-
Clone the repository:
git clone https://github.com/mattleads/FrankenVSRoadRunner.git cd FrankenVSRoadRunner -
Install PHP dependencies: This is necessary for your IDE to recognize classes and for local development. The Docker images will handle their own vendor installations.
composer install
Database Setup
The project is pre-configured to use a local SQLite database, which will be created inside the var/ directory. Before running the benchmarks, you need to create the database schema and load the sample data.
-
Create the Database Schema: Run the following commands to generate and apply the necessary database migrations for the
Product,Order, andOrderItementities.php bin/console make:migration php bin/console doctrine:migrations:migrate(You may be asked to confirm the execution.)
-
Load Fixture Data: This command will purge the database and then seed it with a large set of sample products and complex orders for the "Heavy Framework" benchmark.
php bin/console doctrine:fixtures:load(You will be asked to confirm that you want to purge the database.)
Running the Application
You can run the application using either FrankenPHP or RoadRunner. The Docker Compose setups are configured to run on the same port (8080), so only run one at a time or you can change it.
Option 1: Running with FrankenPHP
This will build a custom Docker image with the required PHP extensions and start the FrankenPHP server.
docker-compose -f compose.frankenphp.yaml up -d --build
- URL:
http://localhost:8080
Option 2: Running with RoadRunner
This will build a multi-stage, optimized Docker image and start the RoadRunner server.
docker-compose -f compose.roadrunner.yaml up -d --build
- URL:
http://localhost:8080
Usage Guide: Benchmark Endpoints
All endpoints are available under the /api/v1 prefix.
1. Ping / Health Check
- Description: A simple "hello world" endpoint to test basic request/response throughput and worker state.
- Method:
GET - Endpoint:
/api/v1/ping
Example:
curl http://localhost:8080/api/v1/ping
Response:
{
"status": "pong",
"timestamp": 1678886400,
"request_count_for_this_worker": 1
}
2. Basic Database Interaction
- Description: Tests the overhead of database connections and object creation.
- Method:
POST - Endpoint:
/api/v1/products
Example:
curl -X POST http://localhost:8080/api/v1/products
-H "Content-Type: application/json"
-d '{"name": "Benchmark Pro", "price": 1999}'
3. The "Number Cruncher" (CPU Bound)
- Description: Measures raw execution speed by performing a heavy matrix multiplication.
- Method:
GET - Endpoint:
/api/v1/math/matrix/{size}(e.g.,/api/v1/math/matrix/100)
Example:
curl http://localhost:8080/api/v1/math/matrix/100
4. The "Big Data Stream" (I/O & Memory Bound)
- Description: Tests the server's ability to stream a large response without buffering it into memory. This generates and streams a 100,000-row CSV file.
- Method:
GET - Endpoint:
/api/v1/report/stream
Example (saves the output to a file):
curl -O http://localhost:8080/api/v1/report/stream
5. The "Pixel Cruncher" (Image Processing)
- Description: Simulates a heavy, blocking image manipulation task (resize and grayscale).
- Method:
POST - Endpoint:
/api/v1/media/process
Example (assumes you have an image named my-photo.jpg):
# The -F flag sends a multipart/form-data request
curl -X POST -F "image=@my-photo.jpg"
http://localhost:8080/api/v1/media/process
-o processed_image.jpg
6. The "Heavy Framework" Boot Test (Serialization)
- Description: Stresses the serializer and memory manager by fetching and serializing a collection of 50 complex, nested Doctrine entities.
- Method:
GET - Endpoint:
/api/v1/heavy/orders
Example:
curl http://localhost:8080/api/v1/heavy/orders
7. The "Keep-Alive" & Memory Leak Stress Test
- Description: Intentionally leaks 1MB of memory on each request to test how gracefully the server handles worker recycling (based on memory limits, TTL, or max requests).
- Method:
GET - Endpoint:
/api/v1/leak
Example:
# Run this command multiple times to observe the "memory" value increasing
curl http://localhost:8080/api/v1/leak
