BEAR.Async

Benchmark Results

This document presents benchmark results comparing synchronous execution with parallel execution using ext-parallel and ext-swoole.

Environment

Running Benchmarks

cd demo
docker compose up -d --wait parallel
docker compose exec parallel composer install

# Cold one-shot CLI reference
docker compose exec parallel composer parallel-benchmark

# Steady-state HTTP benchmark with wrk
docker compose exec parallel composer steady-state-parallel
composer steady-state-matrix

# Swoole uses the separate swoole image
docker compose up -d --wait swoole
docker compose exec swoole composer install
docker compose exec swoole composer swoole-benchmark
docker compose exec swoole composer steady-state-swoole

Simulation Methodology

Each embedded resource uses SlowQueryInterceptor to add artificial delay, simulating realistic I/O latency. This approach provides reproducible benchmarks that reflect real-world conditions:

Operation Typical Latency
Simple SELECT 1-5ms
JOIN/Aggregation 10-50ms
Network latency (RDS) 1-10ms
Template processing 1-10ms

The default 10ms delay represents a conservative, realistic per-resource overhead combining SQL execution, network latency, and processing time.

The demo now separates two benchmark profiles:

The demo ext-parallel HTTP server is a multi-process harness. Each HTTP worker process keeps its own long-running parallel\Runtime pool, so higher WRK_CONNECTIONS values can be used for HTTP concurrency measurements. Swoole uses its actual long-running HTTP server.

You can verify the cold CLI benchmark path in our CI benchmark workflow, which runs on every push and pull request.

When to Choose Parallel

For a read-only resource graph that embeds multiple independent GET resources, parallel execution should be the first candidate when the runtime supports it and the downstream database or API capacity is sized for the extra concurrency. This is the core design point of BEAR.Async: application code declares the resource graph with #[Embed], and the Linker implementation decides whether the graph is resolved sequentially, with Swoole coroutines, or with ext-parallel workers.

Preconditions

Adapter Guide

Situation Recommended adapter
Swoole HTTP server is acceptable and high throughput is needed Swoole adapter
A resident process can keep ext-parallel runtimes warm ext-parallel adapter
Extension support is unavailable or portability is the priority Sync adapter

Cases with Little or No Gain

Cold One-Shot CLI Results

These numbers are reference values for a single CLI invocation of the dashboard resource with 8 embeds. They include startup work such as DI lookup and, for ext-parallel, Runtime spawn.

Profile Runtime Time vs profile Sync
ext-parallel CLI Sync 137.97 ms baseline
ext-parallel CLI ext-parallel 201.87 ms +63.90 ms (0.68x)
Swoole CLI Sync 140.45 ms baseline
Swoole CLI Swoole 47.64 ms -92.81 ms (2.95x)

The ext-parallel cold result should not be used to judge steady-state per-request performance. It is useful for understanding one-shot behavior and for keeping the benchmark honest about startup cost.

Steady-State HTTP Results

The following numbers were measured locally with Docker Compose on 2026-05-13. Each row is the median of three 30-second wrk runs against /dashboard?user_id=1. The c=1,t=2 combination is omitted because wrk requires the number of connections to be greater than or equal to the number of threads.

The CPU, memory, and MySQL columns are a single mid-run sample for orientation; they are not peak measurements. Socket timeouts are summed across the three runs.

Runtime Config Connections Threads Runs Median req/s Median latency Socket timeouts Sample CPU Sample memory MySQL threads
ext-parallel workers=1,pool=8 1 1 3 21.79 45.82 ms 0 5.37% 96.74MiB / 7.652GiB 9
ext-parallel workers=4,pool=8 4 1 3 83.19 47.99 ms 0 15.22% 284MiB / 7.652GiB 33
ext-parallel workers=4,pool=8 4 2 3 83.38 47.90 ms 0 19.43% 284.5MiB / 7.652GiB 33
ext-parallel workers=16,pool=8 16 1 3 318.42 50.02 ms 0 32.01% 1.001GiB / 7.652GiB 129
ext-parallel workers=16,pool=8 16 2 3 317.58 50.26 ms 0 109.19% 1.001GiB / 7.652GiB 129
Swoole pdo_pool=64 1 1 3 54.10 18.48 ms 0 39.80% 69.89MiB / 7.652GiB 65
Swoole pdo_pool=64 4 1 3 158.73 25.18 ms 0 85.40% 56.07MiB / 7.652GiB 65
Swoole pdo_pool=64 4 2 3 157.75 25.33 ms 0 89.57% 59.73MiB / 7.652GiB 65
Swoole pdo_pool=64 16 1 3 279.66 57.16 ms 0 162.77% 71.85MiB / 7.652GiB 65
Swoole pdo_pool=64 16 2 3 279.15 57.25 ms 0 158.54% 64.89MiB / 7.652GiB 65
Swoole pdo_pool=8 1 1 3 54.27 18.41 ms 0 40.72% 54.27MiB / 7.652GiB 9
Swoole pdo_pool=8 4 1 3 59.62 67.02 ms 0 41.04% 54.14MiB / 7.652GiB 9
Swoole pdo_pool=8 4 2 3 59.32 67.32 ms 0 41.64% 53.79MiB / 7.652GiB 9
Swoole pdo_pool=8 16 1 3 60.07 265.27 ms 0 40.08% 347.1MiB / 7.652GiB 9
Swoole pdo_pool=8 16 2 3 59.90 265.73 ms 0 39.25% 61.7MiB / 7.652GiB 9

Interpretation

Benchmark Scenarios

Each benchmark tests the dashboard resource, which embeds 8 independent SQL resources. Each embedded resource simulates database latency.

Scenario Configuration

Scenario Embedded Resources Delay per Resource
Dashboard 8 10ms

Expected Results

Theoretical Performance

Scenario Sync Time Parallel Time Speedup
Dashboard (8 embeds) ~80ms + SQL/render overhead ~10ms + SQL/render/thread overhead up to 8x

In synchronous mode, total time approaches the sum of all embedded resource costs. With parallel execution, steady-state HTTP time approaches the maximum single embedded resource cost plus runtime overhead. Cold one-shot CLI results also include startup costs and should be read as reference values, not steady-state per-request latency.

Real-World Factors

Actual speedup varies based on:

Comparison: ext-parallel vs ext-swoole

Feature ext-parallel ext-swoole
Execution Model Thread pool Coroutines
Memory Isolation Isolated per thread Shared (requires pooling)
PDO Handling Each thread gets own PDO Must use connection pool
Best For Resident worker pools with isolated runtimes Long-running coroutine HTTP server
Use Case warmed worker process or benchmark harness Swoole HTTP Server

Module Selection Guide

Use the bin/async.php entrypoint (ext-parallel) when:

Use AsyncSwooleModule when:

Sample Output

BEAR.Async Parallel Benchmark
==============================
8 embedded SQL resources, cold one-shot reference
This includes DI lookup and one-time ext-parallel Runtime spawn cost.
Use composer steady-state-parallel for HTTP steady-state measurements.

Sync execution (prod-hal-app)...
  Elapsed: 120.00 ms

Parallel execution (prod-hal-app + ParallelRuntimeModule override)...
  Elapsed: 350.00 ms (includes one-time thread pool spawn cost)

Note: this one-shot CLI run includes ext-parallel Runtime spawn.
      It is a cold-start reference, not a steady-state per-request benchmark.