Performance benchmarks for poker hand evaluators
A benchmarking tool that compares @pokertools/evaluator against other popular poker hand evaluators to validate performance claims.
- π Benchmark Results
- π¦ Installation
- π Running the Benchmark
- π§ How It Works
- π¦ Dependencies
- ποΈ Architecture
- π¬ Methodology
- π Interpreting Results
- π§ͺ Testing
- π API, Worker, and Socket Load/Soak Benchmarks
- π Related Packages
- π License
The bench package is not published β it runs from source within the monorepo.
npm run benchcd packages/bench
npx ts-node index.tsNote:
ts-nodeis provided by the monorepo rootdevDependencies. The package uses"type": "commonjs"(CJS) and is not published β it runs from source viats-node.
Note: The results below are illustrative. Actual throughput depends on CPU speed, Node.js version, V8 JIT tier, memory speed, and system load. Run the benchmark on your own hardware for accurate measurements.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β BENCHMARK RESULTS (7-card hands) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Library β Performance β Input Type β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β π₯ @pokertools/evaluator β ~17M hands/sec β Integer codes β
β π₯ phe β ~16M hands/sec β Integer codes β
β π₯ poker-evaluator β ~1.3M hands/sec β String arrays β
β π₯ pokersolver β ~70K hands/sec β String arrays β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Performance comparison:
| Library | Speed | vs @pokertools |
|---|---|---|
@pokertools/evaluator |
17M hands/sec | baseline |
phe |
16M hands/sec | ~1x |
poker-evaluator |
1.3M hands/sec | ~13x slower |
pokersolver |
70K hands/sec | ~240x slower |
npm run benchcd packages/bench
npm run bench| Script | Command | Description |
|---|---|---|
bench |
ts-node index.ts |
Standard evaluator comparison benchmark (1,000 hands). |
bench:load |
ts-node load.ts |
Short load test against a live API stack (health, queues, optional sockets/actions). |
bench:soak |
BENCH_DURATION_MS=300000 BENCH_CONCURRENCY=32 ts-node load.ts |
Extended soak test (5 min, 32 concurrent workers). |
| Variable | Default | Description |
|---|---|---|
POKERTOOLS_API_BASE |
http://localhost:3000 |
PokerTools API base URL. |
POKERTOOLS_WS_URL |
(derived from API_BASE) | WebSocket endpoint for real-time benchmarks. |
POKERTOOLS_TOKEN |
β | JWT for authenticated WebSocket joins and actions. |
POKERTOOLS_TABLE_ID |
β | Table ID for game-action benchmarks. |
REDIS_URL |
redis://localhost:6379 |
Redis URL for queue-depth benchmarks. |
BENCH_DURATION_MS |
30000 |
Duration of load test in ms (soak default: 300000). |
BENCH_CONCURRENCY |
16 |
Number of concurrent workers (soak default: 32). |
Generating 1000 hands...
π₯ Warming up CPU and JIT...
----------------------------------------------------------------
phe (Int) | 16,574,257 hands/sec | Β±2.26%
poker-evaluator (Str) | 1,375,495 hands/sec | Β±0.33%
pokersolver (Str) | 70,980 hands/sec | Β±0.70%
@pokertools (Int) | 17,915,292 hands/sec | Β±1.56%
----------------------------------------------------------------
π WINNER: @pokertools (Int)
- Generates 1,000 random 7-card hands
- Warms up CPU and JIT compiler (5,000 iterations)
- Runs each evaluator on all hands per benchmark cycle
- Reports hands/second with statistical margin of error
| Library | Type | Description |
|---|---|---|
@pokertools/evaluator |
Integer | Our evaluator using perfect hash tables |
phe |
Integer | Popular integer-based evaluator |
poker-evaluator |
String | Traditional string-based evaluator |
pokersolver |
String | Full-featured poker solver with comparison |
{
"@pokertools/evaluator": "1.0.16",
"benchmark": "^2.1.4",
"bullmq": "^5.79.2",
"ws": "^8.21.0",
"phe": "^0.6.0",
"poker-evaluator": "^2.1.1",
"pokersolver": "^2.1.4"
}Note:
ts-nodeis required to run the benchmark scripts and is provided by the monorepo rootdevDependencies. The package uses"type": "commonjs"(CJS) and is not published β it runs from source viats-node.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β BENCHMARK FLOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββ
β Generate Deck β 52 cards: ["2s", "3s", ..., "Ac"]
ββββββββββ¬βββββββββ
β
βΌ
βββββββββββββββββββ
β Shuffle & Deal β 1,000 random 7-card hands
ββββββββββ¬βββββββββ
β
ββββββ΄βββββ¬βββββββββββββββββ
β β β
βΌ βΌ βΌ
βββββββββ βββββββββ βββββββββββββ
βString β β Int β β Int PHE β
βArrays β β Codes β β Format β
βββββ¬ββββ βββββ¬ββββ βββββββ¬ββββββ
β β β
β ββββββ΄βββββ β
β β β β
βΌ βΌ βΌ βΌ
ββββββββββββ ββββββββββββ ββββββββββββ
βpoker-evalβ β@pokertoolsβ β phe β
βpokersolvrβ β evaluator β β β
ββββββ¬ββββββ ββββββ¬ββββββ ββββββ¬ββββββ
β β β
ββββββββββββββ΄βββββββββββββ
β
βΌ
ββββββββββββββββββ
β Benchmark.js β
β Statistics β
ββββββββββββββββββ
β
βΌ
ββββββββββββββββββ
β Results: β
β hands/sec Β±% β
ββββββββββββββββββ
String-based evaluation:
"As" β parse rank β parse suit β lookup β evaluate
Integer-based evaluation:
48 β direct lookup β evaluate
Overhead per card: ~10-20 nanoseconds for parsing
For 7 cards Γ millions of hands = significant difference
The benchmark includes a warm-up phase to ensure:
- V8 JIT compiler has optimized hot paths
- CPU frequency scaling has reached maximum
- Garbage collection has stabilized
- Benchmark.js runs multiple cycles automatically
- Reports margin of error (Β±%) for each measurement
- Runs until statistically significant results achieved
- hands/sec: Number of complete 7-card hand evaluations per second
- Β±X%: Relative margin of error (95% confidence interval)
| Factor | Impact |
|---|---|
| CPU Speed | Linear correlation |
| Node.js Version | V8 optimizations vary |
| Memory Speed | Affects lookup table access |
| Other Processes | Can cause variance |
- Integer vs String: Integer-based evaluators have an inherent advantage due to no parsing overhead
- Feature Set:
pokersolveroffers more features (hand comparison, wildcards) which adds overhead - Memory Usage: Lookup table evaluators trade memory for speed
The bench package has no test suite β it is the testing and benchmarking infrastructure for the monorepo. To verify it works correctly:
npm run bench -w @pokertools/bench
npm run bench:load -w @pokertools/benchA passing run will output evaluator throughput statistics (hands/sec with margin of error) and load-test CSV results for CI ingestion.
Run short load checks against a live PokerTools stack:
# From the monorepo root:
POKERTOOLS_API_BASE=http://localhost:3000 npm run bench:load -w @pokertools/benchTo include authenticated WebSocket joins and game actions, provide a JWT and table ID:
POKERTOOLS_TOKEN=<jwt> POKERTOOLS_TABLE_ID=<table-id> \
npm run bench:load -w @pokertools/benchLonger soak profile (5 minutes, 32 workers):
npm run bench:soak -w @pokertools/benchResults are emitted as CSV (name,count,ok,failed,p50_ms,p95_ms,max_ms) for CI ingestion.
| Package | Description |
|---|---|
| @pokertools/evaluator | The evaluator being benchmarked |
| @pokertools/engine | Uses the evaluator for showdown |
MIT Β© A.Aurelius