Network Backends
HORUS automatically selects the optimal communication backend based on topology — no configuration needed. Every topic is shared-memory backed; cross-machine replication is available separately via the opt-in net feature.
HORUS ships transparent LAN replication over UDP in the horus_net crate, but shared memory is the only transport until you turn it on — see Network Transport (LAN replication) below.
Automatic Backend Selection
When you call Topic::new("name"), HORUS automatically detects the optimal backend based on the number of publishers, subscribers, and whether the message type is POD:
use horus::prelude::*;
// Just create a topic — backend is auto-selected
let topic = Topic::<CmdVel>::new("motors.cmd_vel")?;
topic.send(CmdVel::new(1.0, 0.0));
No configuration needed. The backend upgrades and downgrades dynamically as participants join and leave.
Shared-Memory Backends
Every topic is shared-memory backed, so every backend is cross-process. There are five SHM backends; Topic::new() picks one of the four auto-selected ones from the publisher/subscriber topology and whether the message type is POD:
| Backend | Latency | Detection Criteria |
|---|---|---|
| FanoutShm | ~40ns | subs > 1, non-POD (broadcast) |
| PodShm | ~50ns | subs > 1, POD (broadcast) |
| MpscShm | ~65ns | pubs > 1, subs <= 1 |
| SpmcShm | ~70ns | never auto-selected — see below |
| SpscShm | ~85ns | pubs <= 1, subs <= 1 |
SpmcShm exists as a backend but detection never chooses it: its consumers share one tail and compete for messages, so a fast consumer would starve the others — the wrong semantics for pub/sub broadcast. Multi-consumer topics get PodShm or FanoutShm instead, where each subscriber reads the stream independently and a fast consumer cannot starve a slow one. Independent is not the same as lossless: these rings are latest-wins, so a subscriber that falls a full lap behind skips the messages the publisher overwrote.
The backend is selected based on:
- Topology: Number of publishers and subscribers
- Data type: POD (fixed-size, memcpy-able) types get the dedicated zero-copy path; variable-size types are serialized with bincode, which costs more per message than the POD path
Dynamic Migration
HORUS dynamically migrates between backends as topology changes:
Single publisher + single subscriber
→ SpscShm, ~85ns
Second subscriber joins (non-POD payload)
→ FanoutShm, ~40ns
A second publisher joins as well
→ FanoutShm, ~40ns (no migration — non-POD broadcast already uses the fanout matrix)
The extra subscriber leaves — two publishers, one subscriber
→ MpscShm, ~65ns
All but one publisher and one subscriber disconnect
→ SpscShm, ~85ns
Migration is transparent — send() and recv() calls are unaffected.
Performance Characteristics
| Metric | Shared Memory |
|---|---|
| Latency | 40-85ns backend transport; non-POD payloads add bincode serialization on top |
| Throughput | Millions msg/s |
| Zero-copy | Yes for POD types (via mmap); non-POD types are bincode-serialized |
| Cross-machine | Not by shared memory itself — add the opt-in net feature / horus run --net for ~50μs LAN replication |
Network Transport (LAN replication)
Cross-machine topics are replicated by the horus_net UDP replicator. It is opt-in — build the horus crate with --features net, or run horus run --net:
| Transport | Latency | Use Case |
|---|---|---|
| Shared Memory | 40-85ns | Same-machine IPC |
UDP LAN replication (net feature) | ~50μs | Multi-robot, cross-machine topics |
Topic::new() continues to auto-select the optimal SHM backend either way. With the net feature enabled, replication auto-starts on scheduler.run() and discovers peers by UDP multicast (default group 224.0.69.72, port 9100); topics you subscribe to but do not publish are imported by default (import = "auto"). Configure it with the [network] section of horus.toml or the HORUS_NET_* environment variables: opt out with .network(false), HORUS_NO_NETWORK=1, or [network] enabled = false; narrow the surface with [network] import / HORUS_NET_IMPORT and [network] deny_export.
Zenoh is not the transport. It is an optional bridge package (horus install horus-zenoh) for cases UDP multicast cannot reach — cross-subnet or WiFi links.