Aller au contenu principal

Latency Budget QoS Policy

The Latency Budget policy provides a hint to the DDS middleware about the maximum acceptable delay from the time data is written to when it is received by the reader.

Purpose​

Latency Budget allows the middleware to optimize delivery:

  • Budget = 0: Deliver immediately, no batching
  • Budget > 0: The middleware may batch or defer delivery up to the specified duration for efficiency
  • Hint only: The middleware is not required to enforce the budget; it is advisory
info

Latency Budget is a hint, not a guarantee. The middleware uses it to decide whether to batch messages, flush buffers immediately, or apply other transport-level optimizations. It does not affect QoS compatibility between writers and readers.

Configuration​

use hdds::{Participant, QoS, TransportMode};

let participant = Participant::builder("control_app")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.build()?;

// Critical control data: deliver immediately (budget = 0ms)
let writer = participant
.topic::<ControlData>("control/commands")?
.writer()
.qos(QoS::reliable().latency_budget_millis(0))
.build()?;

// Analytics data: batching allowed (budget = 100ms)
let batch_writer = participant
.topic::<AnalyticsData>("analytics/metrics")?
.writer()
.qos(QoS::reliable().latency_budget_millis(100))
.build()?;
#include <hdds.h>

/* Critical data: zero latency budget (deliver immediately) */
struct HddsQoS* qos_rt = hdds_qos_reliable();
hdds_qos_set_latency_budget_ns(qos_rt, 0ULL);

struct HddsDataWriter* writer_rt = hdds_writer_create_with_qos(
participant, "control/commands", qos_rt);
hdds_qos_destroy(qos_rt);

/* Batched data: 100ms latency budget */
struct HddsQoS* qos_batch = hdds_qos_reliable();
hdds_qos_set_latency_budget_ns(qos_batch, 100000000ULL); /* 100ms */

struct HddsDataWriter* writer_batch = hdds_writer_create_with_qos(
participant, "analytics/metrics", qos_batch);
hdds_qos_destroy(qos_batch);

Default Value​

Default is zero (no specific latency requirement, deliver as fast as possible):

let qos = QoS::reliable();
// latency_budget = Duration::ZERO (immediate delivery hint)

Fluent Builder Methods​

MethodDescription
.latency_budget_millis(n)Set latency budget in milliseconds
.latency_budget_secs(n)Set latency budget in seconds

How Latency Budget Affects Delivery​

Budget = 0ms (immediate delivery):

Writer: [msg]-----> Middleware -----> Reader
(send now) (instant)

Budget = 100ms (batching allowed):

Writer: [msg1]--+
[msg2]--+--> Middleware ------> Reader
[msg3]--+ (batch up (receives batch
to 100ms) within 100ms)

With a non-zero budget, the middleware can:

  1. Batch multiple samples into a single network packet
  2. Defer transmission to coalesce with other pending writes
  3. Reduce syscalls by writing fewer, larger buffers

Compatibility Rules​

Latency Budget does not affect compatibility. Writers and readers always match regardless of their latency budget settings.

Writer BudgetReader BudgetMatch?
0ms0msYes
0ms100msYes
100ms0msYes
100ms200msYes

Use Cases​

Real-Time Control vs Analytics​

use hdds::{Participant, QoS, TransportMode};

let participant = Participant::builder("mixed_system")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.build()?;

// Control loop: immediate delivery
let control_writer = participant
.topic::<ControlData>("control/commands")?
.writer()
.qos(QoS::reliable().latency_budget_millis(0))
.build()?;

// Diagnostics: batching acceptable
let diag_writer = participant
.topic::<DiagData>("system/diagnostics")?
.writer()
.qos(QoS::best_effort().latency_budget_millis(500))
.build()?;

Emergency Alerts​

use hdds::{Participant, QoS, TransportMode};

let participant = Participant::builder("alert_system")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.build()?;

// Emergency: absolute minimum latency
let alert_writer = participant
.topic::<Alert>("system/alerts")?
.writer()
.qos(QoS::reliable().latency_budget_millis(0))
.build()?;

// Status reports: can tolerate delay
let status_writer = participant
.topic::<StatusReport>("system/status")?
.writer()
.qos(QoS::reliable().latency_budget_millis(200))
.build()?;

Bandwidth Optimization​

use hdds::{Participant, QoS, TransportMode};

let participant = Participant::builder("bandwidth_demo")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.build()?;

// High-frequency sensor: allow batching for bandwidth efficiency
let writer = participant
.topic::<SensorData>("sensors/temperature")?
.writer()
.qos(QoS::best_effort().keep_last(1).latency_budget_millis(50))
.build()?;

Interaction with Other Policies​

Latency Budget + Reliability​

Latency BudgetReliabilityBehavior
0msreliable()Immediate send + ACK/NACK overhead
100msreliable()May batch, but retransmission still applies
0msbest_effort()Immediate fire-and-forget
100msbest_effort()May batch for efficiency

Latency Budget + Transport Priority​

Both are transport-layer hints. Transport Priority affects packet scheduling, while Latency Budget affects send timing:

PriorityBudgetNet Effect
High0msImmediate send, preferential network handling
High100msMay batch, but packets get priority when sent
Low0msImmediate send, but lower network priority

Common Pitfalls​

  1. Treating it as a guarantee: Latency Budget is advisory. The middleware may not honor it exactly, especially under load.

  2. Setting budget on readers only: The budget is most effective on the writer side where batching decisions are made. Setting it on readers indicates the acceptable delivery window.

  3. Conflicting with Deadline: If Deadline is 100ms and Latency Budget is 200ms, the middleware might delay past the deadline. Ensure latency_budget < deadline when both are used.

  4. Zero budget overhead: A zero-budget hint forces the middleware to flush immediately on each write, which increases syscall frequency. For high-rate publishers, a small non-zero budget can significantly improve throughput.

Performance Notes​

  • Zero budget: lowest latency, highest syscall overhead
  • Non-zero budget: higher latency, better throughput via batching
  • The optimal budget depends on your publish rate and latency requirements
  • Typical values: 0ms for control, 10-100ms for telemetry, 500ms+ for logs

Next Steps​