FastIPC FastIPC v1.0.0
bench/alternatives · bench/zig

Performance

FastIPC, iceoryx2, Aeron, ZeroMQ, NNG, Boost.Interprocess, cpp-ipc and the operating system's sockets, run through the same cases on the same machine: a stream between two processes, a broadcast to several, a work queue shared by producers and consumers, and a request-reply service. Every process does the same work with each library, and each library runs in its fastest configuration. The harness fetches and builds every library at a pinned version, and reruns all of it on your machine. Numbers from different machines don't compare, so every chart sets the libraries side by side on each machine. Below them: what to expect beyond these cases, and what each language's binding costs.

Stream: one process to another

stream-N · rtt-N

One sender, one receiver, each in a process of its own: messages per second one way, and the round trip when the receiver answers each message at once. A sender writes each message from its own buffer, and the receiver ends with it in its own; libraries that lend out shared memory have the receiver copy the message out of it.

Stream: 16-byte messages per second, one way

FastIPC
50.3M
52.5M
82.1M
72.4M
Aeron
56.5M*
47.3M
57.6M
50.6M
ZeroMQ
7.84M
8.44M
7.38M
9.73M
Boost.Interprocess
7.22M
3.57M
10.2M
5.82M
iceoryx2
3.63M
3.23M
6.27M
5.59M
cpp-ipc
2.34M
1.01M
—
2.59M
Unix socket
2.17M
986.9K
2.00M
2.33M
TCP loopback
558.8K
115.2K
548.6K
572.3K
NNG
19.4K
52.5K
92.8K
135.4K

Round trip, 16 B, p50 (shorter is better; log scale)

FastIPC
163 ns
160 ns
125 ns
125 ns
Aeron
192 ns
191 ns
125 ns
125 ns
iceoryx2
854 ns
897 ns
583 ns
625 ns
Boost.Interprocess
1.6 µs
680 ns
375 ns
1.5 µs
cpp-ipc
1.2 µs
2.3 µs
—
1.0 µs
Unix socket
1.7 µs
3.2 µs
1.5 µs
1.6 µs
TCP loopback
3.7 µs
14 µs
9.0 µs
2.9 µs
ZeroMQ
78 µs
53 µs
43 µs
30 µs
NNG
118 µs
47 µs
24 µs
135 µs

Stream: 1 KiB messages per second, one way

FastIPC
28.8M
30.0M
29.5M
25.9M
Aeron
18.8M
22.7M
25.8M
23.2M
iceoryx2
3.17M
3.05M
4.82M
4.82M
Boost.Interprocess
1.85M
2.90M
7.21M
3.02M
ZeroMQ
1.68M
1.28M
2.40M
2.06M
Unix socket
1.85M
939.9K
1.83M
1.33M
cpp-ipc
691.2K
198.3K
—
472.4K
TCP loopback
527.5K
113.5K
532.9K
512.8K
NNG
18.3K
49.9K
87.6K
124.2K

Stream: 64 KiB messages per second, one way

FastIPC
489.3K
492.7K
642.4K
661.0K
iceoryx2
411.0K
408.9K
408.4K
449.1K
Aeron
190.8K
216.7K
249.6K
230.7K
cpp-ipc
322.9K
221.3K
—
79.1K
Boost.Interprocess
54.4K
262.8K
298.8K
167.8K
Unix socket
167.1K
196.5K
100.4K
156.3K
TCP loopback
118.2K
52.1K
130.6K
129.8K
ZeroMQ
81.8K
55.3K
102.2K
106.4K
NNG
16.6K
26.3K
45.9K
35.9K

Broadcast: one publisher, many subscribers

broadcast-S-lossless-N · broadcast-S-lossy-N

One publisher process and 1 or 4 subscriber processes; every subscriber receives every message. Lossless: a subscriber that falls behind holds the publisher back. Lossy: it loses messages instead, and the publisher never waits. The rate is the slowest subscriber's; a lossy library's figure counts the messages that arrived, and the share lost is beside it.

Broadcast to 4 subscribers, lossless: 16-byte messages per second

FastIPC
44.4M
38.3M
50.7M
40.8M
Aeron
45.6M*
32.5M
41.9M
23.8M
iceoryx2
1.36M
1.29M
972.2K
1.51M
cpp-ipc
13.3K
45.1K
—
12.0K

Broadcast to 4 subscribers, lossy: 16-byte messages received per second

FastIPC
32.3M · 0% lost
30.7M · 1% lost
47.0M · 26% lost
38.9M · 27% lost
ZeroMQ
3.06M · 46% lost
3.21M · 70% lost
3.43M · 38% lost
3.68M
iceoryx2
1.37M
1.27M
962.3K
1.40M
NNG
7.69K
21.2K · 83% lost
36.5K · 47% lost
98.6K · 67% lost

Work queue: producers and consumers

queue-PxC-N

Producer and consumer processes, each message to exactly one consumer: 1 producer and 1 consumer, and 4 of each. The rate is every consumer's messages together.

Work queue, 4 producers × 4 consumers: 16-byte messages per second

FastIPC
68.0M
65.8M
83.5M
—
ZeroMQ
36.0M
32.3M
14.6M
—
Boost.Interprocess
4.72M
1.11M
7.12M
—
NNG
46.8K
134.2K
105.2K
—

Work queue, 1 producer × 1 consumer: 16-byte messages per second

FastIPC
31.0M
29.8M
32.6M
34.8M
ZeroMQ
7.74M
7.68M
7.17M
9.43M
Boost.Interprocess
4.82M
3.90M
10.3M
5.89M
NNG
20.3K
51.7K
79.8K
35.6K

Service: requests and replies

service-CxS-N

Client and server processes; each client sends a request, waits for its reply, and sends the next. 1 client and 1 server, 4 clients and 1 server, and 4 of each, where the servers share the requests. The rate is every client's calls together; a call's p50 is its round trip.

Service, 1 client × 1 server: 16-byte calls per second

FastIPC
3.36M
3.01M
4.27M
3.85M
iceoryx2
933.1K
912.5K
1.34M
1.24M
ZeroMQ
12.3K
18.2K
23.0K
24.9K
NNG
8.32K
19.6K
40.4K
7.20K

Service, 4 clients × 4 servers: 16-byte calls per second

FastIPC
5.00M
5.33M
5.45M
—
ZeroMQ
30.8K
50.4K
53.7K
—
NNG
17.0K
42.6K
42.6K
—

Case by case

FastIPC against the fastest of the others
CaseLinux x86-64WindowsmacOSLinux ARM64
Round trip, 16 B, p50163 ns 1.18× faster than Aeron's 192 ns160 ns 1.19× faster than Aeron's 191 ns125 ns 1.00× as fast as Aeron's 125 ns125 ns 1.00× as fast as Aeron's 125 ns
Stream, 16 B, one way50.3M 1.12× slower than Aeron's 56.5M*52.5M 1.11× faster than Aeron's 47.3M82.1M 1.42× faster than Aeron's 57.6M72.4M 1.43× faster than Aeron's 50.6M
Stream, 1 KiB, one way28.8M 1.54× faster than Aeron's 18.8M30.0M 1.32× faster than Aeron's 22.7M29.5M 1.14× faster than Aeron's 25.8M25.9M 1.11× faster than Aeron's 23.2M
Stream, 64 KiB, one way489.3K 1.19× faster than iceoryx2's 411.0K492.7K 1.20× faster than iceoryx2's 408.9K642.4K 1.57× faster than iceoryx2's 408.4K661.0K 1.47× faster than iceoryx2's 449.1K
Broadcast to 4, lossless, 16 B44.4M 1.03× slower than Aeron's 45.6M*38.3M 1.18× faster than Aeron's 32.5M50.7M 1.21× faster than Aeron's 41.9M40.8M 1.71× faster than Aeron's 23.8M
Work queue 4 × 4, 16 B68.0M 1.89× faster than ZeroMQ's 36.0M65.8M 2.04× faster than ZeroMQ's 32.3M83.5M 5.71× faster than ZeroMQ's 14.6M—
Service 1 × 1, 16 B, calls3.36M 3.60× faster than iceoryx2's 933.1K3.01M 3.30× faster than iceoryx2's 912.5K4.27M 3.18× faster than iceoryx2's 1.34M3.85M 3.10× faster than iceoryx2's 1.24M
Service 4 × 4, 16 B, calls5.00M 163× faster than ZeroMQ's 30.8K5.33M 106× faster than ZeroMQ's 50.4K5.45M 101× faster than ZeroMQ's 53.7K—

Aeron in detail

* Aeron leads FastIPC on Linux x86-64 in the 16-byte stream and the lossless 16-byte broadcast to 4 subscribers; on Linux ARM64 in the lossless 1 KiB broadcast to 1 subscriber.

Both libraries can let a receiver sleep once it has spun briefly on an empty buffer; they differ in what ends the sleep. FastIPC's sender wakes a sleeping receiver as it sends (a futex on Linux, its counterparts on Windows and macOS), so the message is taken as soon as the wake-up lands. Aeron's receive never blocks: between empty polls, the application's idle strategy spins, yields or sleeps for a set time, and nothing cuts a sleep short, so a message that arrives during one waits for it to end. An Aeron receiver that sleeps frees its core at the cost of that delay; one that never sleeps keeps a core 100% busy even while no messages arrive. Every benchmark here polls Aeron without a pause, its lowest-latency setting. Each FastIPC figure is the better of its waiting receive and a polling one, which keeps a core busy like Aeron's.

Aeron also needs its media driver running, as a process of its own (as here) or embedded in one of the applications. The driver's conductor, which moves a publisher's flow-control limit, spins on Windows and backs off into sleeps when idle on Linux x86-64, macOS and Linux ARM64, the fastest setting measured on each. Where it spins, it keeps a second core busy. The receiver polls the same way under either setting.

Batched: FastIPC and Aeron, both ends batching

run.py --batch 16

Everything above sends and receives one message per call, with every library. FastIPC and Aeron also have calls that move several messages at once, so here the two of them run with both ends batching, in the same run, each in its configuration from above. FastIPC's sender calls fipc_send_many with 16 messages, and its receiver fipc_recv_many with room for 16: it waits for the first message, then takes every further one already there, up to 16. Aeron's sender offers 16 messages as one block (aeron_exclusive_publication_offer_block), and its receiver polls with a fragment limit of 16. FastIPC counts the faster of a waiting and a polling receive, as above; Aeron always polls. A round trip has one message at a time to send, so it isn't here. Each cell is FastIPC's figure and how it compares with Aeron's; the last rows batch 64 messages at both ends, and hovering a cell shows both configurations. Batch both ends: a batching sender whose receiver takes one message per call runs slower than one that sends one message per call, as the receiver falls behind and the ring stays full.

CaseLinux x86-64WindowsmacOSLinux ARM64
Stream, 16 B102.2M 1.19× faster than Aeron's 86.1M111.5M 1.07× slower than Aeron's 119.4M117.6M 1.02× slower than Aeron's 119.4M128.0M 1.14× faster than Aeron's 112.1M
Stream, 1 KiB30.7M 1.49× faster than Aeron's 20.6M34.5M 1.29× faster than Aeron's 26.8M35.3M 2.68× faster than Aeron's 13.1M34.9M 2.28× faster than Aeron's 15.3M
Broadcast to 1, lossless, 16 B101.2M 1.11× faster than Aeron's 91.3M106.1M 1.02× slower than Aeron's 107.9M108.3M 1.00× faster than Aeron's 108.2M114.0M 1.19× faster than Aeron's 95.7M
Broadcast to 4, lossless, 16 B83.6M 1.04× faster than Aeron's 80.6M86.4M 1.09× faster than Aeron's 79.5M72.8M 1.04× faster than Aeron's 70.3M58.7M 1.35× faster than Aeron's 43.6M
Stream, 16 B, batches of 64121.8M 1.28× faster than Aeron's 94.9M124.4M 1.02× slower than Aeron's 126.6M123.5M 1.03× faster than Aeron's 120.3M132.3M 1.13× faster than Aeron's 117.1M
Stream, 1 KiB, batches of 6427.1M 1.71× faster than Aeron's 15.9M28.0M 1.54× faster than Aeron's 18.2M35.4M 1.42× faster than Aeron's 25.0M32.3M 1.81× faster than Aeron's 17.9M
Broadcast to 1, lossless, 16 B, batches of 64109.3M 1.10× faster than Aeron's 99.6M111.3M 1.07× slower than Aeron's 119.6M117.8M 1.01× faster than Aeron's 117.2M108.9M 1.08× faster than Aeron's 101.1M
Broadcast to 4, lossless, 16 B, batches of 6488.4M 1.02× faster than Aeron's 86.9M92.5M 1.04× slower than Aeron's 96.6M77.2M 1.05× faster than Aeron's 73.4M60.4M 1.47× faster than Aeron's 41.2M

Who is faster with batches of 16 at both ends: in the 16-byte stream FastIPC on Linux x86-64 (by 19%) and Linux ARM64 (by 14%), Aeron on Windows (by 7%) and macOS (by 2%); in the 1 KiB stream FastIPC on Linux x86-64 (by 49%), Windows (by 29%), macOS (by 168%) and Linux ARM64 (by 128%); in the lossless 16-byte broadcast to 1 subscriber FastIPC on Linux x86-64 (by 11%) and Linux ARM64 (by 19%), Aeron on Windows (by 2%), the two level on macOS; in the lossless 16-byte broadcast to 4 subscribers FastIPC on Linux x86-64 (by 4%), Windows (by 9%), macOS (by 4%) and Linux ARM64 (by 35%).

The libraries

what each one has

Which of the four patterns each library has, and how its messages travel. A pattern a library lacks shows as a dash in the results: the harness doesn't build one out of something else (a work queue from a broadcast, say), because the library's users couldn't either.

LibraryVersionMessages travel throughStreamBroadcastWork queueService
FastIPC1.0.0shared memory rings, no broker or daemonyeslossless or lossyyesa pool of servers sharing the load
iceoryx20.10.0shared memory, zero-copy samples, no brokerpublish-subscribelossless or lossyno (every subscriber gets every sample)request-response, one server (a request goes to every server)
Aeron1.53.3shared memory log buffers (aeron:ipc), a media driver processyeslosslessnono
ZeroMQ4.3.5a local socket (ipc://) or TCP, I/O threadsPAIRlossy (PUB/SUB)PUSH/PULLREQ/REP, servers in turn
NNG2.0.0a local socket (ipc://; Windows: a named pipe) or TCPpairlossy (pub/sub)push/pullreq/rep, servers in turn
cpp-ipc1.4.1shared memory (Linux, Windows, FreeBSD)routelosslessnono
Boost.Interprocess1.92.0a shared memory queue under a mutexmessage_queuenomessage_queueno
Unix socket, TCPthe OS'sthe kernelyesnonono

iceoryx2 also has events and a blackboard, Aeron an archive and a cluster, and ZeroMQ and NNG more socket patterns; this page compares the four patterns FastIPC has. Language bindings: FastIPC has C, C++, Zig, Python, C#, Java, Rust, Lua, JavaScript and Go; this comparison calls every library from C++, and iceoryx2 from Rust, its own language.

Every case

each library's best figure

Streams and round trips

CaseLibraryLinux x86-64WindowsmacOSLinux ARM64
Stream, 16 BFastIPC50.3M52.5M82.1M72.4M
Aeron56.5M*47.3M57.6M50.6M
ZeroMQ7.84M8.44M7.38M9.73M
Boost.Interprocess7.22M3.57M10.2M5.82M
iceoryx23.63M3.23M6.27M5.59M
cpp-ipc2.34M1.01M—2.59M
Unix socket2.17M986.9K2.00M2.33M
TCP loopback558.8K115.2K548.6K572.3K
NNG19.4K52.5K92.8K135.4K
Stream, 1 KiBFastIPC28.8M30.0M29.5M25.9M
Aeron18.8M22.7M25.8M23.2M
iceoryx23.17M3.05M4.82M4.82M
Boost.Interprocess1.85M2.90M7.21M3.02M
ZeroMQ1.68M1.28M2.40M2.06M
Unix socket1.85M939.9K1.83M1.33M
cpp-ipc691.2K198.3K—472.4K
TCP loopback527.5K113.5K532.9K512.8K
NNG18.3K49.9K87.6K124.2K
Stream, 64 KiBFastIPC489.3K492.7K642.4K661.0K
iceoryx2411.0K408.9K408.4K449.1K
Aeron190.8K216.7K249.6K230.7K
cpp-ipc322.9K221.3K—79.1K
Boost.Interprocess54.4K262.8K298.8K167.8K
Unix socket167.1K196.5K100.4K156.3K
TCP loopback118.2K52.1K130.6K129.8K
ZeroMQ81.8K55.3K102.2K106.4K
NNG16.6K26.3K45.9K35.9K
Round trip, 16 BFastIPC163 ns p99 250 ns160 ns p99 226 ns125 ns p99 125 ns125 ns p99 167 ns
Aeron192 ns p99 311 ns191 ns p99 269 ns125 ns p99 208 ns125 ns p99 208 ns
iceoryx2854 ns p99 1.1 µs897 ns p99 1.1 µs583 ns p99 667 ns625 ns p99 750 ns
Boost.Interprocess1.6 µs p99 17 µs680 ns p99 1.5 µs375 ns p99 1.4 µs1.5 µs p99 7.2 µs
cpp-ipc1.2 µs p99 1.4 µs2.3 µs p99 16 µs—1.0 µs p99 1.3 µs
Unix socket1.7 µs p99 2.1 µs3.2 µs p99 4.8 µs1.5 µs p99 1.9 µs1.6 µs p99 2.0 µs
TCP loopback3.7 µs p99 6.3 µs14 µs p99 19 µs9.0 µs p99 34 µs2.9 µs p99 3.5 µs
ZeroMQ78 µs p99 129 µs53 µs p99 81 µs43 µs p99 79 µs30 µs p99 92 µs
NNG118 µs p99 174 µs47 µs p99 79 µs24 µs p99 34 µs135 µs p99 177 µs
Round trip, 1 KiBFastIPC406 ns p99 628 ns383 ns p99 591 ns292 ns p99 375 ns250 ns p99 333 ns
Aeron616 ns p99 924 ns634 ns p99 869 ns375 ns p99 625 ns375 ns p99 667 ns
iceoryx21.1 µs p99 1.3 µs1.1 µs p99 1.3 µs667 ns p99 792 ns708 ns p99 792 ns
Boost.Interprocess1.8 µs p99 20 µs815 ns p99 1.0 µs542 ns p99 1.1 µs1.8 µs p99 13 µs
Unix socket2.1 µs p99 2.5 µs3.8 µs p99 4.9 µs1.7 µs p99 2.1 µs2.2 µs p99 2.9 µs
cpp-ipc2.1 µs p99 25 µs3.6 µs p99 22 µs—2.0 µs p99 2.4 µs
TCP loopback4.1 µs p99 9.4 µs16 µs p99 20 µs9.1 µs p99 25 µs3.2 µs p99 6.0 µs
ZeroMQ78 µs p99 130 µs57 µs p99 91 µs44 µs p99 80 µs31 µs p99 73 µs
NNG115 µs p99 180 µs49 µs p99 84 µs25 µs p99 35 µs133 µs p99 170 µs

Broadcasts

CaseLibraryLinux x86-64WindowsmacOSLinux ARM64
1 subscriber, lossless, 16 BFastIPC52.8M52.0M64.7M69.7M
Aeron50.8M42.6M59.4M55.4M
iceoryx23.34M3.17M6.36M5.52M
cpp-ipc2.43M1.05M—2.55M
1 subscriber, lossy, 16 BFastIPC51.5M 1.4% lost49.0M72.2M 13.6% lost68.9M 20.8% lost
ZeroMQ7.13M 46.3% lost8.10M 12.7% lost7.29M 58.8% lost8.61M
iceoryx23.36M3.15M6.43M5.50M
NNG17.6K52.8K84.2K 93.0% lost141.0K 67.8% lost
1 subscriber, lossless, 1 KiBFastIPC26.7M30.3M24.4M21.8M
Aeron13.2M21.3M16.5M22.0M*
iceoryx23.04M2.87M4.83M4.86M
cpp-ipc682.2K212.8K—478.7K
1 subscriber, lossy, 1 KiBFastIPC20.6M 33.2% lost30.6M 0.3% lost22.9M22.3M
iceoryx23.09M2.94M 1.2% lost4.84M4.83M
ZeroMQ533.9K 85.7% lost1.19M 88.9% lost1.80M 77.3% lost1.26M 70.7% lost
NNG17.5K 97.7% lost50.8K 25.1% lost86.2K 91.2% lost82.5K 88.0% lost
4 subscribers, lossless, 16 BFastIPC44.4M38.3M50.7M40.8M
Aeron45.6M*32.5M41.9M23.8M
iceoryx21.36M1.29M972.2K1.51M
cpp-ipc13.3K45.1K—12.0K
4 subscribers, lossy, 16 BFastIPC32.3M 0.2% lost30.7M 0.6% lost47.0M 25.9% lost38.9M 26.5% lost
ZeroMQ3.06M 46.0% lost3.21M 69.9% lost3.43M 38.1% lost3.68M
iceoryx21.37M1.27M962.3K1.40M
NNG7.69K21.2K 82.9% lost36.5K 46.6% lost98.6K 67.1% lost
4 subscribers, lossless, 1 KiBFastIPC18.6M20.7M12.6M11.8M
Aeron10.9M17.6M8.33M7.44M
iceoryx21.33M1.27M806.3K1.48M
cpp-ipc13.7K8.23K—2.19K
4 subscribers, lossy, 1 KiBFastIPC17.2M 28.1% lost20.2M 5.0% lost11.5M 8.4% lost8.59M 36.1% lost
iceoryx21.36M1.31M 0.3% lost819.7K1.58M
ZeroMQ94.9K 97.7% lost303.0K 96.9% lost364.2K 92.8% lost253.5K 93.4% lost
NNG7.34K 3.1% lost20.1K 91.7% lost19.4K 45.0% lost36.5K 87.4% lost

Work queues

CaseLibraryLinux x86-64WindowsmacOSLinux ARM64
1 × 1, 16 BFastIPC31.0M29.8M32.6M34.8M
ZeroMQ7.74M7.68M7.17M9.43M
Boost.Interprocess4.82M3.90M10.3M5.89M
NNG20.3K51.7K79.8K35.6K
1 × 1, 1 KiBFastIPC21.0M20.8M23.3M20.2M
Boost.Interprocess2.38M2.56M7.11M3.14M
ZeroMQ1.49M1.23M2.17M1.84M
NNG19.5K51.3K78.2K35.0K
4 × 4, 16 BFastIPC68.0M65.8M83.5M—
ZeroMQ36.0M32.3M14.6M—
Boost.Interprocess4.72M1.11M7.12M—
NNG46.8K134.2K105.2K—
4 × 4, 1 KiBFastIPC43.8M44.5M33.3M—
ZeroMQ3.63M3.92M2.59M—
Boost.Interprocess1.66M886.5K1.20M—
NNG48.5K141.3K83.0K—

Services

CaseLibraryLinux x86-64WindowsmacOSLinux ARM64
1 client × 1 server, 16 BFastIPC3.36M p50 262 ns3.01M p50 318 ns4.27M p50 250 ns3.85M p50 250 ns
iceoryx2933.1K p50 1.1 µs912.5K p50 1.1 µs1.34M p50 750 ns1.24M p50 833 ns
ZeroMQ12.3K p50 79 µs18.2K p50 54 µs23.0K p50 44 µs24.9K p50 35 µs
NNG8.32K p50 115 µs19.6K p50 49 µs40.4K p50 24 µs7.20K p50 131 µs
1 client × 1 server, 1 KiBFastIPC1.90M p50 448 ns1.87M p50 449 ns2.95M p50 333 ns2.46M p50 417 ns
iceoryx2764.8K p50 1.3 µs744.4K p50 1.3 µs1.16M p50 875 ns1.14M p50 875 ns
ZeroMQ12.6K p50 76 µs17.7K p50 55 µs22.5K p50 45 µs27.6K p50 32 µs
NNG8.28K p50 116 µs19.1K p50 50 µs39.6K p50 25 µs7.40K p50 133 µs
4 clients × 1 server, 16 BFastIPC5.27M p50 706 ns5.50M p50 681 ns5.51M p50 583 ns7.38M p50 375 ns
iceoryx21.68M p50 1.7 µs1.48M p50 1.9 µs1.80M p50 917 ns1.62M p50 1.3 µs
ZeroMQ33.1K p50 108 µs53.4K p50 72 µs50.9K p50 77 µs34.0K p50 35 µs
NNG11.3K p50 334 µs33.2K p50 117 µs33.9K p50 113 µs25.1K p50 146 µs
4 clients × 1 server, 1 KiBFastIPC3.36M p50 1.1 µs3.08M p50 1.2 µs4.07M p50 792 ns3.62M p50 583 ns
iceoryx21.25M p50 2.2 µs1.16M p50 1.8 µs1.45M p50 1.2 µs1.40M p50 1.4 µs
ZeroMQ31.5K p50 124 µs52.4K p50 73 µs49.9K p50 79 µs29.7K p50 125 µs
NNG11.3K p50 343 µs33.4K p50 116 µs33.8K p50 111 µs21.6K p50 160 µs
4 clients × 4 servers, 16 BFastIPC5.00M p50 756 ns5.33M p50 700 ns5.45M p50 583 ns—
ZeroMQ30.8K p50 123 µs50.4K p50 77 µs53.7K p50 70 µs—
NNG17.0K p50 224 µs42.6K p50 90 µs42.6K p50 89 µs—
4 clients × 4 servers, 1 KiBFastIPC3.68M p50 1.0 µs3.41M p50 1.2 µs4.31M p50 792 ns—
ZeroMQ29.9K p50 126 µs48.2K p50 82 µs42.5K p50 80 µs—
NNG17.7K p50 216 µs42.0K p50 89 µs40.4K p50 94 µs—

Method

bench/alternatives/README.md
  • Processes: every role runs in a process of its own, as IPC does: a stream's sender and receiver, a broadcast's publisher and each subscriber, each producer and consumer, each client and server. No process is pinned to a core.
  • The same work: a sender starts with each message in a buffer of its own; a receiver ends with it in memory of its own, and checks its length. A library that copies receives into the receiver's buffer; one that lends out shared memory (iceoryx2, Aeron, cpp-ipc, FastIPC's zero-copy calls) has the message copied out of it.
  • Each library at its best: every receiver ran twice, once waiting in the library's receive and once polling it in a loop, and the faster counts (iceoryx2's and Aeron's receives never block, so they always poll). FastIPC counts the faster of its copying and zero-copy calls, ZeroMQ and NNG the faster of their local socket and TCP. Each figure's tooltip names the configuration.
  • Configuration: each library runs in its fastest configuration for the pattern on each platform, chosen from sweeps on that platform: its buffer (64 KiB, 512 KiB, 4 MiB and 32 MiB per case, alternating), and for Aeron its media driver's conductor (spinning, which keeps one core busy, or backing off into sleeps when idle, Aeron's default). The configuration with the best geometric mean over the pattern's cases wins (a tie: the smaller buffer, then Aeron's default). The buffer is FastIPC's ring, Aeron's window (half its term), and the bytes of messages iceoryx2's subscriber buffers, Boost's queue, NNG's socket buffers and ZeroMQ's high-water marks hold, at least 8 messages (NNG holds at most 8192). cpp-ipc's queue is fixed and the sockets' buffers are the OS's. Every other option is the library's default. Each platform's configuration is listed below.
  • One message per receive: every library's receive returns one message, so Aeron's subscription polls take one each (a fragment limit of 1; Aeron can take several per poll, which no other library's receive does). The comparison measures each library's synchronization per message, not batching; Batched sets FastIPC and Aeron side by side with both ends batching. A platform whose run took more per poll says so under the configuration table.
  • Windows power throttling: the harness turns it off for itself and for every process it starts, so that Windows doesn't move a process to the efficiency cores or lower its clock when the harness runs without focus.
  • Timing: each run sends for 0.3 s of warm-up and then 1 s that counts; the figure is the median of 3 runs. Throughput is timed by the receiver on the CPU's counter, read once per 64 messages; one round trip in 16 is sampled, chosen at random, the clock reads included. The counter ticks at 24 MHz on the Apple M1, so its round trips are measured in steps of 42 ns.
  • iceoryx2 runs through its Rust API, its own language. Through its C++ binding it ran up to 5 times slower here (16 B one way on Windows: 0.61M against 3.21M messages per second), so the C++ figures aren't shown.
  • Order: every chart and table lists FastIPC first, then the others ranked by their figure's geometric mean over the machines they ran on, the best first, so that the closest rivals sit side by side and no machine's scale outweighs another's.
  • The harness's own cost: about 7 ns per message on the receiving side (a virtual call and the length and sequence checks), the same for every library. FastIPC's own benchmarks run without it: By language, below.
  • Reproduce with python bench/alternatives/deps.py (fetches and builds every library), a CMake build, and run.py: the README has the commands, and results/ the raw medians of every run.

Buffers

Each library's buffer per pattern; for FastIPC and Aeron, the shared memory it maps in the charted case (a stream, a broadcast to four subscribers, a work queue and a service of 4 × 4). The other libraries' sizes are bytes of messages: at 16 B, 64 KiB is 4096 messages. Under it, Aeron's conductor and fragment limit on each platform.

LibraryStreamBroadcastWork queueService
FastIPCLinux x86-64, Windows, Linux ARM64: 4 MiB maps 8 MiB
macOS: 512 KiB maps 1 MiB
Linux x86-64: copying calls: 512 KiB maps 768 KiB, zero-copy calls: 4 MiB maps 6 MiB
Windows: copying calls: 4 MiB maps 6 MiB, zero-copy calls: 512 KiB maps 768 KiB
macOS, Linux ARM64: 32 MiB maps 48 MiB
Linux x86-64, Windows, macOS: 4 MiB maps 30 MiB
Linux ARM64: copying calls: 4 MiB maps 30 MiB, zero-copy calls: 32 MiB maps 240 MiB
Linux x86-64: 512 KiB maps 3 MiB
Windows, macOS: 64 KiB maps 384 KiB
Linux ARM64: copying calls: 64 KiB maps 384 KiB, zero-copy calls: 4 MiB maps 24 MiB
iceoryx2Linux x86-64, Windows, Linux ARM64: 4 MiB
macOS: 512 KiB
Linux x86-64, macOS: 4 MiB
Windows: 64 KiB
Linux ARM64: 32 MiB
—Linux x86-64, Windows, macOS: 16 KiB
Linux ARM64: 64 KiB
AeronLinux x86-64, Windows: 512 KiB maps 3 MiB
macOS, Linux ARM64: 4 MiB maps 24 MiB
Linux x86-64: 4 MiB maps 24 MiB
Windows: 512 KiB maps 3 MiB
macOS, Linux ARM64: 32 MiB maps 192 MiB
——
ZeroMQLinux x86-64: ipc://: 64 KiB, TCP: 4 MiB
Windows: 4 MiB
macOS, Linux ARM64: ipc://: 512 KiB, TCP: 4 MiB
Linux x86-64, macOS: 32 MiB
Windows: ipc://: 4 MiB, TCP: 512 KiB
Linux ARM64: ipc://: 512 KiB, TCP: 32 MiB
Linux x86-64, Windows: 512 KiB
macOS, Linux ARM64: ipc://: 32 MiB, TCP: 512 KiB
Linux x86-64: ipc://: 4 MiB, TCP: 32 MiB
Windows, macOS: 512 KiB
Linux ARM64: ipc://: 512 KiB, TCP: 32 MiB
NNG64 KiB64 KiB64 KiB64 KiB
cpp-ipcits fixed queueits fixed queue——
Boost.InterprocessLinux x86-64, macOS, Linux ARM64: 32 MiB
Windows: 4 MiB
—Linux x86-64, Windows: 4 MiB
macOS, Linux ARM64: 32 MiB
—
Unix socketthe OS's———
TCP loopbackthe OS's———

Aeron's media driver conductor, which moves a publisher's flow-control limit: Linux x86-64, macOS, Linux ARM64: backs off into sleeps when idle (Aeron's default); Windows: spins.

The most messages one of Aeron's subscription polls took (its fragment limit; a message up to its MTU is one fragment): 1 (stream), 1 (broadcast).

Machines

The Linux x86-64 and Windows columns ran on one ASUS Zenbook 14 laptop on AC power (Intel Core i9-13900H: 6 P-cores with Hyper-Threading and 8 E-cores, 32 GB), one after the other: Windows 11, then Ubuntu 24.04 in WSL2. A thread's wake-up is slow in WSL2 (about 50 µs), which costs most the libraries that hand each message between threads (ZeroMQ, NNG). The macOS and Linux ARM64 columns ran on a MacBook Air (Apple M1: 4 performance and 4 efficiency cores, 8 GB), one after the other: macOS 26.5, then Ubuntu 24.04 in a virtual machine (Lima on Apple's Virtualization framework, 4 vCPUs, 4 GB). On macOS a case with more spinning processes than the 4 performance cores runs some of them on the efficiency cores. The VM's four vCPUs leave out the cases of 4 producers and 4 consumers and of 4 clients and 4 servers, eight spinning processes each (a dash in its column), and its broadcast to four subscribers runs five spinning processes on them (Aeron's media driver, which backs off into sleeps when idle there, is a sixth).

In practice

bench/zig · 2026-10-24/25

What the cases above don't show, from FastIPC's own benchmark suite on the same machines: a receiver that sleeps, the lowest latency, messages longer than the ring, and setting up a connection.

latency-sleep · wakeup

Waking a sleeper

A receiver that waits in a receive call spins for 100 to 250 µs, then sleeps. A message that comes later wakes it in about 28 to 44 µs on Windows, 57 to 60 µs on Linux x86-64 in WSL2, 26 µs on macOS and 35 µs on Linux ARM64, depending on how deeply its core slept.

stream pace

The receiver sets the pace

A receiver that keeps up with its sender pulls the ring's cache lines away from the sender at every look, so each publish waits for them to come back. So a receiver that waits for a stream holds back its first looks (64 pauses on Windows, 32 on Linux x86-64, 512 on ARM64, macOS and Linux, about 4 µs on the M1), which lifts copied 16 to 64 B messages 1.4 to 2.4 times, and 1 KiB on the M1 more than 3 times; one that waits for an answer looks at once. zig build -Dstream_pace=N builds the library for other hardware.

any length

Messages longer than the ring

A message longer than the ring travels in pieces, so its length is limited only by the receiver's buffer, and copy and zero-copy then run alike: 1.5 MiB messages through 512 KiB rings at 8.6 to 14 thousand per second.

fipc_stream_accept · fipc_stream_connect

Connection setup

Listen, connect, accept, close both ends and stop listening, with a 1 MiB ring: 2.5 thousand cycles per second on Linux x86-64, 7.8 thousand on Windows, 4.1 thousand on macOS and 2.2 thousand on Linux ARM64. Set up a connection once, not for each exchange.

Windows throttling · macOS background

Quality of service

Started from a background process, Windows 11 runs a process on its efficiency cores unless power throttling is off for it: 64 KiB messages fall from about 430 to 210 to 280 thousand per second. macOS has its counterpart, the background state, which runs a process and its children on the M1's efficiency cores (copied 1 KiB messages at 7 million per second instead of 27 million). The benchmarks turn the throttling off and leave the background state; an application that needs the speed should do the same.

Lowest latency: busy-polling

For the lowest latency at any message rate, poll instead of waiting: call fipc_recv (or fipc_recv_acquire, or a binding's receive) with timeout 0 in a loop, and call again on FIPC_TIMEOUT. A call that finds nothing makes no system call and reads no clock: it looks at the ring's indices, pauses for a spin hint and looks once more, checks the connection's state and returns, in about 50 ns on x86. The polling thread holds one core at 100%.

One 32 B message per millisecond, its age on arrivalLinux x86-64 p50p99Windows p50p99
Receiver waits in fipc_recv (wakeup)58.5 µs143 µs30.3 µs140 µs
Receiver polls with timeout 0 (wakeup-poll)222 ns18.7 µs217 ns8.4 µs

Unpinned, 2026-10-25. On the M1 a polling receiver gets the message in 83 ns under macOS and 125 ns under Linux. The p99 is the operating system's: interrupts and the scheduler take the polling core away for 1 to 2% of the time in gaps over a microsecond, and a message sent during one waits for the thread to run again.

By language

bench/zig · bench/c · bench/csharp · bench/java · bench/python · bench/rust · bench/lua · bench/js · bench/go

What each language's binding costs: copied messages per second between two processes, and under each the bytes of message per second, through each language's own benchmark, on the same machines as the comparison. Short messages show the cost of each call into the library; at 64 KiB the copy itself dominates and most languages run close to C. Medians of 5 runs round-robin, release builds, 512 KiB rings; Linux x86-64 and Windows 2026-10-25, macOS and Linux ARM64 the same night, 2026-10-24 to 25; every size and the zero-copy calls are in bindings-baseline.md.

Language16 B64 KiB
Linux x86-64WindowsmacOSLinux ARM64Linux x86-64WindowsmacOSLinux ARM64
Zig48.75M744 MiB/s50.03M763 MiB/s108.25M1652 MiB/s107.53M1641 MiB/s448.9K27.4 GiB/s442.6K27.0 GiB/s640.5K39.1 GiB/s662.5K40.4 GiB/s
C50.04M764 MiB/s50.14M765 MiB/s100.63M1535 MiB/s127.84M1951 MiB/s459.2K28.0 GiB/s447.7K27.3 GiB/s623.4K38.1 GiB/s653.5K39.9 GiB/s
C++41.41M632 MiB/s44.55M680 MiB/s98.59M1504 MiB/s99.16M1513 MiB/s437.1K26.7 GiB/s403.8K24.6 GiB/s618.0K37.7 GiB/s663.0K40.5 GiB/s
Rust42.35M646 MiB/s40.57M619 MiB/s98.58M1504 MiB/s99.63M1520 MiB/s457.7K27.9 GiB/s434.8K26.5 GiB/s639.6K39.0 GiB/s664.9K40.6 GiB/s
Lua (LuaJIT)49.24M751 MiB/s50.92M777 MiB/s122.02M1862 MiB/s127.46M1945 MiB/s427.7K26.1 GiB/s397.0K24.2 GiB/s596.7K36.4 GiB/s546.3K33.3 GiB/s
C#37.42M571 MiB/s30.85M471 MiB/s65.43M998 MiB/s72.86M1112 MiB/s462.3K28.2 GiB/s444.7K27.1 GiB/s545.6K33.3 GiB/s606.9K37.0 GiB/s
Java13.70M209 MiB/s14.90M227 MiB/s35.45M541 MiB/s26.71M408 MiB/s432.4K26.4 GiB/s421.2K25.7 GiB/s598.7K36.5 GiB/s632.9K38.6 GiB/s
Go8.76M134 MiB/s10.49M160 MiB/s10.27M157 MiB/s10.42M159 MiB/s416.5K25.4 GiB/s446.7K27.3 GiB/s602.3K36.8 GiB/s594.5K36.3 GiB/s
JavaScript2.45M37 MiB/s2.10M32 MiB/s3.53M54 MiB/s2.21M34 MiB/s361.2K22.0 GiB/s354.1K21.6 GiB/s543.8K33.2 GiB/s249.7K15.2 GiB/s
Python1.23M19 MiB/s1.47M22 MiB/s1.92M29 MiB/s1.10M17 MiB/s323.0K19.7 GiB/s341.9K20.9 GiB/s458.9K28.0 GiB/s375.0K22.9 GiB/s
  • Runtimes: Zig is the library's own suite, which calls fipc.h directly, on Zig 0.17.0. C and C++ are one harness, the same loops through fipc.h and through fipc.hpp, built ReleaseFast by Zig's bundled Clang 22. Rust 1.99 through its safe API; LuaJIT 2.1; C# on .NET 9 through the raw layer Fipc; Java on JDK 25 (Temurin); Go 1.27.1 without cgo; JavaScript on Node.js 22 through the async calls, each awaited; CPython 3.13 on Windows and macOS, 3.12 on Linux.
  • Timing: each case is timed after an untimed warm-up of at least 0.2 s (0.5 s on the JITs: C#, Java, LuaJIT, Node.js), from a start marker the client sends after it. The 16 B case times 2 million messages, tens of milliseconds for a native language, so scheduling noise is large (±20 to 30%).
  • Reproduce with python devtool.py bench <language>-fastipc (c-, cpp-, python-, csharp-, java-, rust-, lua-, js-, go-), or bench zig-all for the suite.
  • Go calls into the library through purego, each call behind a mutex of its direction: that costs more than copying a short message. Zero-copy takes two calls per message on each side where a copy takes one, so in Go it runs slower than copying for short messages (5.5 to 7.4 million 16-byte messages per second); from 64 KiB on, Go runs as fast as the other languages.
  • C#: the object layer (FipcConnection) adds about 25 to 50 ns per message over the raw layer on both OSes, nearly all of it the SafeHandle's reference count: for 16-byte copies, 84 ns against 40 ns per send and receive on Windows, 77 ns against 41 ns on Linux, in one process with no waiting.
  • JavaScript runs about twice as many short messages through the Sync calls as through the async calls shown.

Questions

FAQ
What is the fastest IPC library?

It depends on the pattern and the message. FastIPC is the fastest, or level with the fastest, in 28 of the 30 measurements of the case-by-case table; Aeron leads the other 2. Of the libraries here, only FastIPC has all four patterns (stream, broadcast, work queue and service) in shared memory. (Case by case.)

How does FastIPC compare with Aeron?

Aeron is the closest rival. FastIPC has the lower round-trip latency on Linux x86-64 and Windows, and the two are about even on macOS and Linux ARM64. FastIPC leads 16-byte one-way throughput on Windows, macOS and Linux ARM64, and Aeron leads on Linux x86-64 (by 12%). FastIPC leads it at 1 KiB on every platform here. Aeron also works across machines, over UDP. On one machine FastIPC is simpler to set up: Aeron needs a media driver running, as a process of its own or embedded in one of the applications. Either library can let an idle receiver sleep, but a sleeping Aeron receiver sees a new message only when its sleep ends, whereas FastIPC's sender wakes its receiver as soon as it sends. Aeron has no work queue or request-reply service. (Charts above; details in Aeron in detail.)

Is FastIPC faster than ZeroMQ?

Yes, in every one of the 72 measurements where both ran: a 16-byte round trip, for example, is 240 to 478 times as quick. ZeroMQ passes each message through a socket and its own I/O threads, while FastIPC writes it into memory both processes map. ZeroMQ also works across machines; FastIPC works within one.

How does FastIPC compare with iceoryx2?

Both are shared-memory libraries without a broker. FastIPC is faster in every one of the 68 measurements where both ran; its request-reply calls, for example, run 3.1 to 3.6 times as fast. iceoryx2 has no work queue and delivers each request to every server, so its servers don't share a load. It has events and a blackboard, and runs on more platforms (QNX, FreeBSD and others).

How does FastIPC compare with NNG (nanomsg)?

NNG passes each message through its own threads, and every hand-over between threads costs a wake-up, so it is far slower here: a 16-byte round trip is 195 to 1,077 times as quick with FastIPC. NNG works across machines; FastIPC works within one.

Is shared memory faster than a Unix domain socket?

Yes. Here a 16-byte round trip through FastIPC's shared memory is 11 to 20 times as quick as over a Unix domain socket. A socket costs a system call and a copy through the kernel for every send and receive; shared memory needs neither once the connection is set up.

Can I run these benchmarks myself?

Yes. bench/alternatives in the repository fetches and builds every library at the version shown and builds the harness with CMake, and run.py runs every case and writes the medians. The README there has the commands, and the method above says what each case does.