HORUS Benchmarks

Performance validation with real-world robotics workloads.

Benchmark Methodology

Measurement Approach

  • Statistical sampling: Criterion.rs micro-benchmarks, sample sizes 10-1000 depending on the group
  • Confidence intervals: Bootstrap 95% CI with Tukey IQR outlier detection
  • Controlled methodology: 2-10s measurement windows per group, Criterion's default 3s warm-up
  • Standalone binaries: RDTSC-timed, 50,000-100,000 samples per scenario
  • Comprehensive coverage: 4 robotics message types, 10 backend routes

Workload Testing

  • Real workloads: Control loops, sensor fusion, manipulator commands, lidar scans
  • Fault injection: Per-node FailurePolicy (Restart / Skip) recovery testing
  • Scale testing: Validated up to 100 concurrent nodes and 1,000 topics
  • Topology coverage: Same-thread, cross-thread and cross-process, 1:1 through N:N
  • Contention: Multi-producer and multi-consumer paths measured under contention

Executive Summary

HORUS delivers sub-microsecond latency for production robotics applications.

Measured with robotics_messages_benchmark on an Intel Core i7-10750H (6C/12T, powersave governor), 50,000 iterations per message type after 5,000 warmup iterations. These are the four message types the benchmark actually runs:

Message TypeSizeMedianp99ThroughputTypical RateHeadroom
CmdVel16 B75 ns135 ns12.14M msg/s1000 Hz12,140x
Imu304 B121 ns235 ns7.46M msg/s500 Hz14,920x
JointCommand928 B135 ns226 ns6.89M msg/s500 Hz13,780x
LaserScan1480 B210 ns283 ns4.42M msg/s40 Hz110,500x

Performance Highlights

Key Findings

  • Sub-microsecond latency for messages up to 1.5KB
  • Serde integration works flawlessly with complex nested structs
  • Graceful scaling with message size (predictable performance)
  • Massive headroom for all typical robotics frequencies

Production Readiness

  • Real-time control: 75 ns median latency supports 1000Hz+ control loops with over 12,000x headroom
  • Sensor fusion: 304-byte Imu messages at 121 ns median, 235 ns p99
  • Manipulator control: kilobyte-scale JointCommand payloads at 135 ns median
  • Multi-robot systems: multi-million msg/s throughput on a single node

Detailed Results

CmdVel (Motor Control Command)

Use Case: Real-time motor control @ 1000Hz Size: 16 bytes

Median latency:  75 ns
p99 latency:     135 ns
Throughput:      12.14M msg/s

Analysis: Sub-microsecond performance suitable for 1000Hz control loops with over 12,000x headroom.


Imu (Inertial Measurement Unit)

Use Case: Orientation and acceleration @ 500Hz Size: 304 bytes (size_of; 296 bytes declared)

Median latency:  121 ns
p99 latency:     235 ns
Throughput:      7.46M msg/s

Analysis: Sub-microsecond performance with complex nested arrays and three 9-element (3x3) covariance matrices.


JointCommand (Multi-DOF Control)

Use Case: Manipulator joint control @ 500Hz Size: 928 bytes (size_of; 1032 bytes declared)

Median latency:  135 ns
p99 latency:     226 ns
Throughput:      6.89M msg/s

Analysis: Sub-microsecond latency for kilobyte-scale command payloads, with room for 500Hz+ manipulator loops.


LaserScan (2D Lidar Data)

Use Case: 2D lidar sensor data @ 10-40Hz Size: 1480 bytes

Median latency:  210 ns
p99 latency:     283 ns
Throughput:      4.42M msg/s

Analysis: Still sub-microsecond at 1.5KB. Easily handles 40Hz lidar updates with enormous headroom.


Comparison with traditional frameworks

Latency Comparison

Measurement Note: HORUS values below are send-only (one-direction). For round-trip (send+receive), approximately double them. The DDS rows are the published reference values dds_comparison_benchmark uses when the dds feature is off (64-byte messages, same-process, sourced to REP 2014 and the Eclipse iceoryx benchmarks) — they are not measured on this machine.

FrameworkMedianp99
HORUS Topic (same process, 1:1)63 ns93 ns
HORUS Topic (cross process, 1:1)151 ns181 ns
iceoryx (C++)~80 ns~200 ns
CycloneDDS~1,500 ns~5,000 ns
FastDDS~2,000 ns~8,000 ns
ROS2 Default~5,000 ns~20,000 ns

Performance Advantage: against the ROS2 default reference, HORUS is roughly 33x faster cross-process and 79x faster same-process; against FastDDS, roughly 13x and 32x.


Latency by Message Size

Measurement Note: All latencies below are send-only (one-direction publish), measured by robotics_messages_benchmark with an auto-selected Topic backend. Sizes are size_of at runtime.

The ROS 2 column divides that send-only figure by ROS 2's ~5 µs REP 2014 reference, which is an end-to-end number we did not measure. The two sides are therefore not like-for-like and the ratios are indicative, not a benchmark result. For a one-way-to-one-way comparison use the cross-process row above (151 ns), which gives roughly 33x.

Message SizeMessage TypeMedianp99Throughputvs ROS2 default (~5 μs)
16 BCmdVel75 ns135 ns12.14 M msg/s~67x faster
304 BImu121 ns235 ns7.46 M msg/s~41x faster
928 BJointCommand135 ns226 ns6.89 M msg/s~37x faster
1,480 BLaserScan210 ns283 ns4.42 M msg/s~24x faster

Observation: Latency grows far more slowly than message size — 92x more bytes costs under 3x latency — reflecting efficient serialization and IPC.


Python Performance

The HORUS Python bindings (PyO3) call directly into the Rust shared memory layer, avoiding pickle serialization overhead. Python nodes and Rust nodes communicate through the same shared memory, enabling cross-language interoperability with minimal overhead.

Why Python HORUS is Fast:

  1. Zero-copy via Rust core: Python bindings call directly into Rust shared memory
  2. No pickle overhead: Messages use efficient binary serialization
  3. PyO3 efficiency: Minimal FFI overhead between Python and Rust

No published Python figures. Every number on this page comes from the Rust-side benchmarks; the repository publishes no reference latency or throughput figures for the Python bindings, so this section quotes none. To measure them on your own hardware run python3 horus_py/benchmarks/bench_python.py (message round-trip, node tick overhead, zero-copy image conversion, multi-node scheduler throughput) or python3 horus_py/benchmarks/research_bench_python.py --duration 30 for sustained runs with a full p50/p95/p99/p99.9 distribution and CSV/JSON output.

TensorPool

HORUS TensorPool provides shared memory tensors optimized for ML/AI workloads. Pre-mapped shared memory means no malloc() or zero-initialization on the hot path.

from horus import TensorPool
import numpy as np

# Create pool
pool = TensorPool(12345)  # pool_id

# Allocate tensor (pre-mapped shared memory)
h = pool.alloc([1024, 1024], 'float32')

# Zero-copy NumPy view
arr = h.numpy()  # No data copied

# Cross-process sharing via shared memory
descriptor = h.to_descriptor()

Key Advantages:

  • Cross-process sharing via shared memory
  • Pre-allocated pool — no malloc on hot path
  • Refcounted handles — safe concurrent access
  • Zero-copy NumPy.numpy() returns view

Running Rust Benchmarks

Quick Run

cd horus
cargo run --release -p horus_benchmarks --bin robotics_messages_benchmark

Available Benchmarks

BinaryDescription
robotics_messages_benchmarkIPC latency with real robotics message types
all_paths_latencyTopic latency across all backend routes (SpscShm, MpscShm, SpmcShm, PodShm, raw-atomic floor)
cross_process_benchmarkCross-process shared memory IPC
scalability_benchmarkScaling with producer/consumer thread counts
determinism_benchmarkExecution determinism and jitter
research_scalabilityNode-count (1-100) and topic-count (1-1,000) scaling
dds_comparison_benchmarkComparison with DDS middleware — runs against published reference values by default; add -F dds (and an installed CycloneDDS) for a live comparison

Run any benchmark with:

cargo run --release -p horus_benchmarks --bin <name>

# JSON output for CI/regression tracking
cargo run --release -p horus_benchmarks --bin <name> -- --json results.json

Criterion micro-benchmarks:

cd horus
cargo bench -p horus_benchmarks

Expected Output

╔══════════════════════════════════════════════════════════════════╗
║        HORUS Robotics Message Types Benchmark                    ║
╠══════════════════════════════════════════════════════════════════╣
║  Testing real-world robotics message latency (REP 2014)          ║
╚══════════════════════════════════════════════════════════════════╝

Platform: Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz (12 cores)
Iterations: 50000
Warmup: 5000

╔═════════════════════════════════════════════════════════════════════════════╗
║ Message Type    │ Size (bytes) │ Typical Rate │ Use Case                    ║
╠═════════════════════════════════════════════════════════════════════════════╣
║ CmdVel          │           16 │ 1000+ Hz     │ Velocity control commands   ║
║ Imu             │          296 │ 500+ Hz      │ IMU sensor fusion           ║
║ LaserScan       │         1480 │ 10-40 Hz     │ 2D lidar navigation         ║
║ JointCommand    │         1032 │ 500+ Hz      │ Manipulator control         ║
╚═════════════════════════════════════════════════════════════════════════════╝

[Topic] Running benchmarks...
─────────────────────────────────────────────────
  CmdVel              16 bytes │ median:      75ns │ p99:     135ns │ CV: 1.3146
  Imu                304 bytes │ median:     121ns │ p99:     235ns │ CV: 1.1833
  LaserScan         1480 bytes │ median:     210ns │ p99:     283ns │ CV: 0.6795
  JointCommand       928 bytes │ median:     135ns │ p99:     226ns │ CV: 0.8705

The run also prints a SUMMARY BY MESSAGE TYPE table with throughput and a REAL-TIME SUITABILITY ANALYSIS block. The sizes in the banner table (296 B for Imu, 1032 B for JointCommand) are hard-coded labels in the benchmark header; the per-type result lines print the real std::mem::size_of values (304 B and 928 B).


Use Case Selection

Message Type Guidelines

CmdVel — 16 B, 75 ns median / 135 ns p99

  • Motor control @ 1000Hz
  • Real-time actuation commands
  • Safety-critical control loops

Imu — 304 B, 121 ns median / 235 ns p99

  • High-frequency sensor fusion @ 500Hz
  • State estimation pipelines
  • Orientation tracking

JointCommand — 928 B, 135 ns median / 226 ns p99

  • Multi-DOF manipulator control @ 500Hz
  • Coordinated joint trajectories
  • Arm and gripper command streams

LaserScan — 1480 B, 210 ns median / 283 ns p99

  • 2D lidar @ 10-40Hz
  • Obstacle detection
  • SLAM front-end

Performance Characteristics

Strengths

  1. Sub-microsecond latency for messages up to 1.5KB
  2. Consistent performance across message types (low variance)
  3. Graceful scaling with message size
  4. Production-ready throughput with large headroom
  5. Serde integration handles complex nested structs efficiently

Additional Notes

  • Complex structs (Imu with three 9-element covariance matrices): 121 ns median — still sub-microsecond
  • Large messages (LaserScan at 1480 B): 210 ns median — a 92x size increase over CmdVel costs under 3x latency

Real-World Applications

ApplicationMessageFrequencyHORUS medianROS2 default (REP 2014 ref)Speedup
Motor controlCmdVel (16 B)1000 Hz75 ns~5 μs~67x
Sensor fusionImu (304 B)500 Hz121 ns~5 μs~41x
Manipulator controlJointCommand (928 B)500 Hz135 ns~5 μs~37x
Lidar SLAMLaserScan (1480 B)10-40 Hz210 ns~5 μs~24x

Methodology

Benchmark Pattern: Send-Side and One-Way

HORUS measures latency in one direction only — no acknowledgement leg is sent:

Loading diagram...
Send-side and one-way latency measurement

Why one-way?

  • Comparable: matches the one-way figures published by REP 2014 and the iceoryx benchmarks
  • No return-path bias: a round-trip number blends two directions plus the consumer's wake-up cost
  • Cross-core: producer and consumer are pinned to separate physical cores, so cache-coherency cost is included
  • Conservative for cross-process: the timestamp is written before send() and read after recv(), so serialization, IPC and deserialization are all inside the measurement

What we measure:

  • send — producer-side send() latency via RDTSC with calibrated overhead subtraction (same-process scenarios)
  • one-way — producer → consumer latency from an RDTSC timestamp carried in CmdVel.timestamp_ns (cross-process scenarios)
  • broadcast — one-way latency on latest-value PodShm paths, plus read freshness and skip-ahead counts

What we DON'T measure:

  • Round-trip / request-response time — no scenario sends an ACK
  • Burst throughput (no backpressure)
  • Same-core communication (unrealistic for multi-process IPC)

Test Environment

  • Build: cargo build --release with full optimizations
  • CPU Governor: performance mode recommended; the figures quoted on this page were captured under powersave, which inflates cross-process latencies
  • CPU Affinity: Producer and consumer pinned to separate physical cores (all_paths_latency uses main=0, aux=2, pub2=4, cons=6, cons2=8)
  • Process Isolation: Dedicated topics per benchmark
  • Warmup: 5,000 iterations discarded before measurement (10,000 for determinism_benchmark / dds_comparison_benchmark)
  • Measurement: RDTSC (cycle-accurate timestamps)

Message Realism

  • Actual HORUS library message types
  • Serde serialization (production path)
  • Realistic field values and sizes
  • Complex nested structures (Imu, LaserScan, JointCommand)

Statistical Methodology

  • 50,000 iterations per message type (robotics_messages_benchmark, override with --iterations); 100,000 samples per scenario (all_paths_latency, determinism_benchmark, dds_comparison_benchmark)
  • Median, p95, p99, p99.9 latency tracking
  • Tukey IQR outlier removal (1.5x fence) and bootstrap 95% confidence intervals (10K resamples)
  • Variance tracking (min/max ranges, coefficient of variation)
  • Multiple message sizes

Measurement Details

RDTSC Calibration:

  • Null cost (back-to-back rdtsc): ~36 cycles
  • Target on modern x86_64: 20-30 cycles
  • Timestamp embedded directly in message payload

Cross-Core Testing:

  • Producer and consumer on different CPU cores
  • Simulates real multi-process robotics systems
  • Includes cache coherency overhead (~60 cycles theoretical minimum)

Scheduler Performance

Scheduler Features

Key capabilities:

  • Execution classes: .compute() moves long-running nodes off the tick path, .async_io() for blocking I/O, .rate() and .order() for per-node pacing and ordering
  • Fault Tolerance: per-node FailurePolicyFatal, Restart { max_restarts, initial_backoff }, Skip { max_failures, cooldown }, Ignore
  • Deterministic mode: opt-in sequential execution with reproducible runs — Scheduler::new().deterministic(true) (default is false)
  • Safety Monitoring: tick budgets, deadline-miss policies (Miss), watchdogs and a blackbox recorder

Scalability Performance

research_scalability sweeps 1 → 100 nodes and 1 → 1,000 topics. Node counts above 100 are not benchmarked.

Topic scaling (send + recv latency, Intel Core i7-10750H, powersave governor):

Topicsp50p99
166 ns80 ns
1065 ns88 ns
5065 ns85 ns
10065 ns80 ns
50064 ns77 ns
1,00064 ns83 ns

Key Insights:

  • Topic lookup is O(1) — latency is flat from 1 to 1,000 topics
  • The repository README reports near-linear scheduler scaling to 100 nodes (~14% degradation), measured on an Intel i9-14900K rather than the reference CPU used elsewhere on this page
  • Node-scaling timings are dominated by OS scheduling noise under the powersave governor and vary substantially run-to-run; measure on your own hardware with the performance governor before quoting numbers
cargo run --release -p horus_benchmarks --bin research_scalability

Real-Time Performance

Real-Time Node Configuration

HORUS provides real-time facilities for safety-critical applications:

RT Features:

  • Tick budget enforcement: .budget(Duration) per node, reporting a BudgetViolation when a tick overruns
  • Deadline handling: .deadline(Duration) with .on_miss(Miss::Warn | Miss::Skip | Miss::SafeMode | Miss::Stop), plus Scheduler::max_deadline_misses()
  • Failure policies: per-node FailurePolicy::Fatal, Restart { max_restarts, initial_backoff }, Skip { max_failures, cooldown }, Ignore
  • Watchdog Timers: .watchdog(Duration) on the scheduler or on individual nodes, to detect hung or crashed nodes

Safety-Critical Configuration

Running with full safety monitoring enabled:

use horus::prelude::*;

let mut scheduler = Scheduler::new()
    .tick_rate(1000_u64.hz())
    .prefer_rt()
    .watchdog(10_u64.ms())
    .max_deadline_misses(100);

scheduler
    .add(motor_controller)
    .order(0)
    .rate(1000_u64.hz())
    .budget(200_u64.us())
    .on_miss(Miss::SafeMode)
    .failure_policy(FailurePolicy::Fatal)
    .build()?;

No published overhead figures. The repository contains no benchmark that measures watchdog resolution, emergency-stop time, deadline-detection jitter or budget-tracking overhead, so this page does not quote any. For scheduler timing behaviour run cargo bench -p horus_benchmarks --bench scheduler_jitter, which measures RT tick interval jitter at 500 Hz with and without competing compute nodes.


All-Routes Latency

HORUS automatically selects the backend from the producer/consumer counts and whether the message type is POD. Every topic is shared-memory backed, so the same-thread, cross-thread and cross-process scenarios below can resolve to the same backend — what changes between them is where the two ends run, not the transport. This benchmark measures the latency of each automatically-selected route.

Benchmark Results

Measured with all_paths_latency on an Intel Core i7-10750H (6C/12T, powersave governor), 100K samples per scenario, RDTSC timing with calibrated overhead subtraction. The benchmark defines no target values — these are the scenario names and columns it actually reports.

ScenarioTypeBackendp50p99
SameThreadsendSpscShm20 ns26 ns
CrossThread-1P1CsendSpscShm63 ns93 ns
CrossThread-MP1CsendMpscShm226 ns429 ns
CrossThread-1PMCsendPodShm74 ns120 ns
CrossThread-MPMCsendPodShm190 ns354 ns
CrossProc-1P1Cone-waySpscShm151 ns181 ns
CrossProc-2P1Cone-wayMpscShm279 ns374 ns
CrossProc-1PMCone-waySpmcShm191 ns209 ns
CrossProc-PodShmbroadcastPodShm223 ns270 ns
RawAtomic (hardware floor)one-wayraw shm79 ns102 ns

MP1C/MPMC scenarios are contended (2 producers, or 2 producers and 2 consumers). Framework overhead is the gap to the RawAtomic floor — 72 ns for cross-process 1:1.

Key Achievements

  • 20 ns same-thread and 63 ns cross-thread for uncontended 1:1 paths
  • ~200 ns for contended multi-producer same-process paths (226 ns MP1C, 190 ns MPMC)
  • Sub-200ns for cross-process 1:1 (151 ns)
  • ~280 ns for multi-producer cross-process (2 producers → 1 consumer)
  • Zero configuration — optimal path selected automatically
  • Seamless migration — path upgrades transparently as topology changes

Running the Benchmark

cd horus
cargo build --release -p horus_benchmarks
./target/release/all_paths_latency

Summary

HORUS provides production-grade performance for real robotics applications:

Automatic Path Selection (all_paths_latency, p50):

  • 20 ns — Same-thread (SpscShm)
  • 63 ns — Cross-thread, 1:1 (SpscShm)
  • 151 ns — Cross-process, 1:1 (SpscShm)
  • 279 ns — Cross-process, 2 producers → 1 consumer (MpscShm)
  • 223 ns — Cross-process broadcast, N:N (PodShm)
  • 79 ns — Raw shared-memory atomic (hardware floor)

Robotics Message Types (robotics_messages_benchmark, median):

  • 75 ns — CmdVel, 16 B (motor control)
  • 121 ns — Imu, 304 B (sensor fusion)
  • 135 ns — JointCommand, 928 B (manipulator control)
  • 210 ns — LaserScan, 1480 B (2D lidar)

Ready for production deployment in demanding robotics applications requiring real-time performance with complex data types.


Next Steps

Build faster. Debug easier. Deploy with confidence.