@@ -24,7 +24,7 @@ between runs.
2424
2525| Binding | Language / Runtime | How it reaches the wire |
2626| ---| ---| ---|
27- | ** OMQ** 0.21.0 | MRI Ruby 4.0.2 + YJIT | pure Ruby, Async fibers, no native dep |
27+ | ** OMQ** 0.23.1 | MRI Ruby 4.0.2 + YJIT | pure Ruby, Async fibers, no native dep |
2828| ** ffi-rzmq** 2.0.7 | MRI Ruby 4.0.2 + YJIT | FFI → libzmq 4.3.5 |
2929| ** CZTop** 2.0.2 | MRI Ruby 4.0.2 + YJIT | FFI → CZMQ → libzmq 4.3.5 |
3030| ** pyzmq** 26.4.0 | CPython 3.13.5 | Cython → libzmq 4.3.5 |
@@ -35,22 +35,22 @@ between runs.
3535| Binding | 128 B msg/s | 128 B MB/s | 1024 B msg/s | 1024 B MB/s |
3636| ---| ---:| ---:| ---:| ---:|
3737| JeroMQ (JRuby) | ** 959,677** | 122.8 | 410,420 | 420.3 |
38- | OMQ (MRI, pure Ruby) | 350,564 | 44.9 | 217,282 | 222 .5 |
38+ | OMQ (MRI, pure Ruby) | 332,030 | 42.5 | 224,148 | 229 .5 |
3939| pyzmq (CPython) | 326,067 | 41.7 | 287,211 | 294.1 |
4040| CZTop (MRI) | 268,342 | 34.3 | 225,171 | 230.6 |
4141| ffi-rzmq (MRI) | 39,840 | 5.1 | 32,854 | 33.6 |
4242
4343### Observations
4444
45- - ** JeroMQ runs away with throughput.** ~ 2.7 × OMQ on small messages, ~ 1.9 × on
45+ - ** JeroMQ runs away with throughput.** ~ 2.9 × OMQ on small messages, ~ 1.8 × on
4646 1 KB. The JVM JIT on a tight ` send(byte[], 0) ` loop against a pure-Java
4747 transport is hard to beat, and JeroMQ is mature (derived from the same
4848 codebase pedigree as libzmq itself).
49- - ** OMQ ties pyzmq on small messages** (326k vs 351k ) — a pure-Ruby fiber
49+ - ** OMQ edges pyzmq on small messages** (332k vs 326k ) — a pure-Ruby fiber
5050 pipeline keeps up with Cython-wrapped libzmq when per-message overhead
5151 dominates. pyzmq pulls ahead at 1 KB because libzmq's C framing is still
5252 faster per byte than Ruby.
53- - ** CZTop is close to OMQ at 1 KB** (230 vs 222 MB/s) — at that size both
53+ - ** CZTop matches OMQ at 1 KB** (231 vs 230 MB/s) — at that size both
5454 are bandwidth-bound on loopback, and the FFI-per-call overhead
5555 disappears against the memcpy cost.
5656- ** ffi-rzmq is ~ 10× slower than everything else.** Not libzmq's fault —
@@ -62,7 +62,7 @@ between runs.
6262
6363| Binding | 128 B rtt/s | 128 B µs/rtt | 1024 B rtt/s | 1024 B µs/rtt |
6464| ---| ---:| ---:| ---:| ---:|
65- | OMQ (MRI, pure Ruby) | ** 14,819 ** | ** 67.5 ** | ** 13,382 ** | ** 74.7 ** |
65+ | OMQ (MRI, pure Ruby) | ** 14,167 ** | ** 70.6 ** | ** 12,902 ** | ** 77.5 ** |
6666| JeroMQ (JRuby) | 14,948 | 66.9 | 13,118 | 76.2 |
6767| pyzmq (CPython) | 14,232 | 70.3 | 13,925 | 71.8 |
6868| ffi-rzmq (MRI) | 11,729 | 85.3 | 10,895 | 91.8 |
@@ -82,8 +82,9 @@ between runs.
8282## Headline takeaways
8383
84841 . ** JeroMQ wins throughput** , by a wide margin on small messages.
85- 2 . ** OMQ is the fastest Ruby option on both benches** — beats ffi-rzmq and
86- CZTop on throughput * and* REQ/REP, despite being pure Ruby.
85+ 2 . ** OMQ is the fastest Ruby option overall** — beats ffi-rzmq everywhere,
86+ beats CZTop on small-message throughput and on REQ/REP, ties CZTop at
87+ 1 KB throughput. All despite being pure Ruby.
87883 . ** On REQ/REP latency, runtime choice barely matters** — OMQ, JeroMQ and
8889 pyzmq all cluster around 65–75 µs. At that scale the bottleneck is TCP
8990 loopback, not the binding.
0 commit comments