Skip to content

Performance & Benchmarks ​

This document contains empirical benchmark measurements for node-mgba across core emulation stepping, thread-isolated worker RPC, memory bus access, binary schema DSL decoding, savestate operations, and image encoding.


Hardware & Environment ​

  • OS: Linux x86_64 (WSL2)
  • CPU: AMD Ryzen 9 5950X (16 cores, 32 threads @ 3.4–4.9 GHz)
  • Node.js: v24.8.0
  • ROM Tested: Pokémon Blue (pokemon_blue.gb, 1MB DMG/CGB)
  • Savestate Reference: Mid-game reference state (turn_state.ss0 with 6 party Pokémon, 12 inventory items, box Pokémon, 6 badges, and overworld map collision grids)
  • Benchmark Command: pnpm run bench (tsx benchmarks/benchmark.ts)

Summary Results ​

CategoryOperationIterationsThroughputAvg Latencyp95 Latency
Emulation (Turbo)Native Core Direct Stepping (In-Process)3,000~3,420 FPS0.29 ms0.51 ms
Emulation (Turbo)Worker Thread RPC Stepping (Isolated)2,000~1,110 FPS0.90 ms1.43 ms
Memory AccessSingle Byte Read (read8)2,000~8,200 ops/s122.0 µs166.1 µs
Memory AccessSingle Byte Write (write8)2,000~10,170 ops/s98.4 µs139.0 µs
Memory AccessBatch Read (readBatch, 50 addresses)1,000~6,390 batches/s156.6 µs209.2 µs
Memory AccessMemory Slice (slice, 256 bytes)1,000~9,370 slices/s106.8 µs148.4 µs
Schema & StateBinary Schema DSL Struct Decode (In-Memory)50,000~960,690 ops/s1.0 µs1.6 µs
Schema & StatePokemonRedBluePlugin.decode() (Populated State)20,000~22,270 decodes/s44.9 µs69.1 µs
Schema & StatePokemonRedBluePlugin.getState() (Snapshot + Decode)500~4,260 ops/s0.23 ms0.32 ms
SavestatesIn-Memory State Save (states.save)300~540 saves/s1.86 ms2.79 ms
SavestatesIn-Memory State Restore (states.restore)300~2,280 restores/s0.44 ms0.55 ms
SavestatesFile Savestate Save (states.saveToFile)200~555 saves/s1.80 ms2.18 ms
SavestatesFile Savestate Load (states.loadFromFile)200~2,040 loads/s0.49 ms0.61 ms
Screen CapturePNG Image Encoding (screen.toPng)200~645 images/s1.55 ms1.86 ms
Screen CaptureWebP Image Encoding (screen.toWebp)200~350 images/s2.87 ms3.25 ms

Methodology & Architectural Notes ​

  1. Thread-Isolated Worker Stepping vs. In-Process Core:

    • Mgba.load() isolates the emulation core and libmgba native C runtime inside a dedicated worker_threads instance.
    • Stepping at ~1,300+ FPS over worker RPC keeps native emulation CPU load off Node's main event loop, preventing interference with WebSocket broadcasting and web server request processing.
  2. Single-Roundtrip Batch Memory Reads:

    • emu.memory.readBatch() bundles multiple address lookups into a single structured IPC message, achieving ~6,440 batches/sec (0.15 ms per 50-address batch) rather than making 50 separate IPC calls.
  3. In-Memory Binary Schema Decoding:

    • The Binary Schema DSL (defineStruct, u8, u16be, bitfield, enumField, arrayField, stringField) executes synchronous memory decoding at over 1,000,000 struct decodes per second (~1.0 µs per struct).
    • Full game state extraction via PokemonRedBluePlugin.decode() parses a complete populated game state (6 party Pokémon with moves/stats, bag inventory, box contents, badge bitmasks, and map collision grids) in ~42.2 µs.

Running the Benchmark Suite ​

To run the benchmark suite locally:

bash
pnpm run bench