Performance & Profiling
Relevant source files
The following files were used as context for generating this wiki page:
The performance and profiling infrastructure in bbus (bubus) is designed to ensure the event bus remains high-throughput and memory-efficient across different runtimes (Python, Node.js, Bun, and Deno). It includes automated stress tests, runtime-specific benchmarking harnesses, and a suite of scenarios targeting edge cases like high-concurrency fanouts, recursive forwarding, and queue-jump logic.
Sources: tests/performance_runtime.py:11-20, bubus-ts/tests/performance.runtime.ts:18-22, tests/test_stress_20k_events.py:167-170
Performance Measurement Architecture
The system utilizes a cross-runtime benchmarking architecture where core scenarios are implemented for both Python and TypeScript. The test harnesses measure several key metrics:
- Throughput: Events processed per second.
- Latency: Milliseconds per event (p50 and p95).
- Memory Growth: Peak RSS (Resident Set Size) and Heap delta per event.
- Fairness: Even distribution of event processing across multiple buses under contention.
Benchmark Execution Flow
The following diagram illustrates the lifecycle of a performance scenario execution:
Performance Scenarios Matrix
The profiling suite defines specific "worst-case" and "high-load" scenarios to identify regressions.
| Scenario ID | Description | Focus Area |
|---|---|---|
50k-events | 1 bus x 50k events x 1 handler | Raw throughput & history eviction. |
500-buses-x-100-events | 500 ephemeral buses x 100 events each | Bus lifecycle & context cleanup. |
1-event-x-50k-parallel-handlers | 1 bus x 1 event x 50k parallel handlers | Fanout logic & registration overhead. |
50k-one-off-handlers | 1 bus x 50k events x 50k one-off handlers | Handler registration/removal churn. |
worst-case-forwarding-timeouts | N buses x N events x N handlers + timeouts | Forwarding, queue-jumps, & timeout races. |
cleanup-equivalence | destroy() vs out-of-scope collection | GC reliability and weak reference cleanup. |
Sources: tests/performance_runtime.py:14-20, bubus-ts/tests/performance.scenarios.js:630-637
Implementation Details
Memory Tracking and GC Control
To obtain accurate memory metrics, the harnesses explicitly trigger Garbage Collection (GC) and wait for the runtime to settle. In Python, this involves gc.collect(), while in TypeScript runtimes, it uses global.gc() (if exposed) or Bun.gc(true).
async def _measure_heap_delta_after_gc(input: PerfInput) -> float | None:
process = psutil.Process(os.getpid())
before = float(process.memory_info().rss)
for _ in range(4):
input.force_gc()
await input.sleep(15)
after = float(process.memory_info().rss)
return max(0.0, (after - before) / (1024.0 * 1024.0))
Sources: tests/performance_scenarios.py:657-670, bubus-ts/tests/performance.runtime.ts:51-64
Concurrency Modes and Throughput Floors
The Python implementation includes a regression guard for different handler concurrency modes. Parallel mode is expected to provide a speedup for I/O-bound fanout workloads.
Profiling Hot Paths
A specialized MethodProfiler (in Python) monkeypatches key methods to capture call counts and execution time for low-level optimization.
Key methods targeted for profiling include:
ReentrantLock.__aenter__/__aexit__EventBus._get_applicable_handlersEventBus._execute_handlersBaseEvent._process_self_on_all_buses
Sources: tests/test_stress_20k_events.py:175-225, tests/test_stress_20k_events.py:734-750
Performance Constraints and Targets
The following table summarizes the performance targets and CI limits:
| Metric | Target / Limit | Context |
|---|---|---|
| Max History Size | 100-512 (default) | Prevents unbounded memory growth. |
| Throughput (Parallel) | > 500 events/sec | Floor for Python parallel mode. |
| Throughput (Serial) | > 600 events/sec | Floor for Python serial mode. |
| Memory Growth | < 140MB (peak) | Limit for 20k event stress test in CI. |
| Tail Latency (p95) | < 80ms - 1000ms | CI ceiling for completion latencies. |
Sources: tests/test_stress_20k_events.py:155-165, tests/test_stress_20k_events.py:410-415, bubus-ts/tests/performance.scenarios.js:15-30
Summary
Performance and profiling in bbus are prioritized via a unified runtime strategy. By leveraging shared scenarios and runtime-specific memory probes, the project ensures that high-concurrency event patterns remain predictable and that memory remains bounded through strict history management and eviction policies.
Sources: TODO_python.md:9-15, TODO_ts.md:12-20