Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

To implement a strict priority or deficit round robin (DRR) scheduler in ns-3, write it as a QueueDisc subclass in the Traffic Control layer, the place where packets wait before they are handed to a NetDevice. Strict priority and DRR differ only in the rule that picks the next packet to send. Everything else, including how packets are classified into queues, how queue limits are enforced, and how the scheduler is tested, has to be designed explicitly.

ns-3 already contains two useful references. PrioQueueDisc is the built-in classful priority discipline, and FqCoDelQueueDisc uses a modified DRR scheduler with CoDel active queue management applied to each flow queue. A third option, a general DRR class you can reuse directly, is not established by the sources behind this guide, so check the module list of your release before assuming one exists. The examples below are pinned to the ns-3.45 queue-disc model documentation. Verify class names, method signatures and default values against the release you actually build.

Where the scheduler sits in ns-3 Traffic Control

The Traffic Control layer sits between the network protocols above it and the NetDevice below it. Packets handed down from the IP stack pass through a queue disc before the device transmits them. A queue disc is therefore the correct extension point when the goal is to decide the order in which packets reach a NetDevice.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

QueueDisc is an abstract base class. Concrete subclasses provide the behavior that matters for scheduling: enqueue, dequeue, peek, and a configuration check. The configuration check is called on your behalf and is where an invalid setup should be rejected. Statistics and a sojourn-time trace are also exposed through the base class, which is useful for verifying scheduler behavior in simulation.

Separate classification from scheduling policy

Most scheduler bugs in ns-3 come from mixing two decisions that should stay apart. The first decision is which queue or class receives an arriving packet. The second is which queued packet leaves next. The table below shows where each decision lives.

Decision Question it answers Where it lives What you must define
Classification Which queue does this packet enter? Packet filters attached to the discipline, or an explicit classifier function you write A stable mapping from traffic class to queue index
Scheduling policy Which non-empty queue is served next? The dequeue logic of your subclass Strict priority order, or DRR quanta and deficit rules
Admission and drops What happens when a queue is full? Queue size limits, and any AQM you attach Per-queue and total limits, and drop behavior

The ns-3 documentation states that a discipline with multiple queues or classes needs an external packet filter to classify traffic. Do not leave this wiring implicit. If the filter sends a packet to a queue index that your discipline does not have, or to no queue at all, the behavior should be a rejected configuration or a documented drop, not a silent misrouting.

What ns-3 already provides

Component Scheduling rule How to use it as a reference Limit to keep in mind
PrioQueueDisc Strict priority across child queues The built-in reference for priority treatment and queue ordering in ns-3 The older source reference does not settle attribute-to-queue mapping for every release. Read the mapping in your release.
FqCoDelQueueDisc Modified DRR across flow queues, with CoDel per queue The reference for DRR mechanics: byte deficits, new and old flow lists, and quantum handling It is not a plain DRR class. Its flow classification and CoDel behavior are part of what it does.

RFC 8290 (IETF, January 2018, Experimental) describes FQ-CoDel as a combined packet scheduler and AQM built on modified DRR, and notes reference implementations for ns-2 and ns-3. It is the right document to cite when you need to explain the algorithm, but it does not define ns-3 class names or defaults.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Strict priority

Strict priority always serves the highest-priority queue that has a packet waiting. A lower queue is served only when every higher queue is empty. The rule is simple, which makes it easy to get right and easy to test.

Define the class-to-queue mapping first

Before writing any dequeue code, fix the mapping from traffic class to queue index, and document the direction. A common convention is that index 0 is the highest priority, but you must confirm the convention used by the release you build against, including whatever PrioQueueDisc does. Keep the mapping in one place so the classifier and the scheduler cannot disagree.

The dequeue rule

On each dequeue, scan the priority queues from highest to lowest and return the head packet of the first non-empty queue. Illustrative logic, not release-specific code:

Dequeue():
  for i = 0 to N-1 (0 is highest priority):
    if queue[i] is not empty:
      return queue[i].DequeueHead()
  return nothing

Preemption happens only at packet boundaries. The scheduler chooses the next packet each time the device is ready to transmit. A packet that has already started transmitting on the link is not interrupted when a higher-priority packet arrives. The higher-priority packet waits for the current transmission to finish and then goes next.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Starvation is expected behavior

Under sustained backlog in the highest-priority queue, lower-priority queues receive no service at all. This is how strict priority is defined, not a bug. Your design must decide whether this is acceptable for the experiment. If it is not, strict priority is the wrong policy, and DRR or a rate-limited high-priority queue should be considered instead.

Deficit round robin

DRR gives each active queue a share of link bytes per round, measured in bytes rather than packets. Each queue carries a deficit counter. When a queue is visited, the scheduler adds a quantum to its deficit. The scheduler then sends head packets from that queue as long as each packet’s size fits within the remaining deficit, subtracting each sent size. A queue whose head packet is larger than its deficit keeps the packet and waits for credit in a later round.

Per-queue state

  • A byte deficit counter. Use a signed type or a type wide enough that it cannot overflow with your largest quantum and packet sizes.
  • A fixed quantum, shared across queues unless you deliberately weight them.
  • An active flag and a position in the active list.

The dequeue procedure

  1. If no queue is currently being served, take the queue at the front of the active list and add the quantum to its deficit.
  2. If that queue is empty, reset its deficit to zero, remove it from the active list, and move on.
  3. If the head packet’s size is no greater than the deficit, subtract the size from the deficit and return the packet. Stay on the same queue for the next call.
  4. If the head packet is larger than the deficit, move the queue to the tail of the active list and continue with the next queue. Its quantum is added again on its next visit.
Dequeue():
  loop:
    if current is none:
      if active list is empty: return nothing
      current = pop front of active list
      current.deficit += quantum
    q = current
    if q is empty:
      q.deficit = 0
      current = none
      continue
    if size(head(q)) <= q.deficit:
      q.deficit -= size(head(q))
      return DequeueHead(q)
    push back q onto active list tail
    current = none

On enqueue, a queue that goes from empty to non-empty joins the tail of the active list with a deficit of zero. A queue that is already active does not join the list a second time. Get these two transitions right before anything else, because most DRR bugs appear as starvation or duplicated service after a queue empties and refills.

Choosing the quantum

The quantum determines how many bytes a queue may send per visit. Its trade-offs are practical:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • A quantum at least as large as the largest packet lets every visit send at least one packet from a queue with a head packet of that size. Smaller quanta give finer-grained fairness but cause more loop iterations per dequeue.
  • A very large quantum lets one queue send a burst before others are visited. Short-term service becomes uneven even when long-run shares are equal.
  • A packet larger than the quantum is still sent, but only after several rounds of deficit accumulation. This is the standard DRR behavior, but document it, because your measured delay for large packets will reflect it.

FQ-CoDel’s documented default is a quantum equal to the device MTU, set at initialization, with a setter to choose another value. Use that as a starting point when you want a comparable baseline, and state the value you chose in every experiment.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Should you write a new QueueDisc or modify FQ-CoDel?

The answer depends on what you need to change. Use the table to choose.

Goal Recommended approach Why
Strict priority among a small set of classes Check whether PrioQueueDisc matches your mapping. If not, write a classful QueueDisc with one child queue per class and the strict priority dequeue above. The dequeue rule is short, and writing it yourself gives full control of the mapping.
DRR across your own traffic classes, not flows A custom QueueDisc with child queues, byte deficits, and the active-list procedure above. FQ-CoDel’s flow hashing and per-queue CoDel are not what you asked for.
Per-flow fairness with CoDel AQM Use FqCoDelQueueDisc as configured in your release. This is the behavior it implements. No new scheduler is needed.
A changed DRR rule that FQ-CoDel cannot express Modify the scheduling code in a separate module, starting from the FQ-CoDel documentation and RFC 8290, and keep the change isolated. Changing a shared algorithm affects every experiment that depends on it, so isolate the change.

If you are not sure, start with a custom classful discipline for strict priority and a separate DRR discipline for comparison, and use the same classifier for both.

Verify the implementation with deterministic tests

Test the scheduler through the order of dequeued packets, not only through aggregate throughput. Throughput can look correct while the service order is wrong.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Strict priority tests

  • Enqueue distinguishable packets in several classes. Confirm that the highest-priority packet leaves first whenever its queue is non-empty.
  • Keep the highest-priority queue continuously backlogged. Confirm that lower-priority queues receive no service during that period. This is the expected starvation behavior, and the test shows it is implemented rather than accidental.
  • Empty the high-priority queue and confirm that the next lower non-empty queue is served without delay.

DRR tests

  • Use unequal packet sizes and a known quantum. After each dequeue, confirm that the queue’s deficit dropped by exactly the transmitted byte count.
  • Put a head packet larger than the current deficit at the front of a queue. Confirm that it waits for additional credit and that it is sent after the rule says it should be.
  • Confirm that active queues receive service over successive rounds, and that a queue receiving service does not starve another active queue.
  • Empty a queue, refill it, and confirm it rejoins the active list once, with a zero deficit.
  • Fill a queue to its configured limit and confirm that the next packet is dropped or rejected according to your documented rule. Repeat for a packet that is requeued, if your discipline requeues packets.

Controlled comparisons

When comparing strict priority with DRR, hold these constant across runs: the classifier, packet sizes, queue limits, link rate, offered load, and simulation duration. Measure per-class or per-flow throughput, sojourn time, drops, and a fairness measure appropriate to your goal. Report the quantum used for DRR. Two sources of difference deserve separate explanation: the starvation behavior of strict priority under persistent high-priority load, and the short-term unevenness that DRR shows when the quantum is large relative to packet sizes.

Troubleshooting common failures

  • Lower-priority traffic never moves. Check that the classifier is sending packets to the intended queue index. Then check whether the high-priority queue is truly empty or simply full of traffic that never drains.
  • A queue gets more than its share. Check that the deficit is subtracted by the packet size in bytes, not by a packet count, and that the quantum is applied once per visit.
  • A flow stalls after being idle. Check the empty-to-active transition. A queue that returns to the active list with a stale deficit, or never returns, will misbehave.
  • Configuration is accepted but traffic is misrouted. Check the configuration check in your subclass. It should reject missing child queues, invalid index mappings, and unsupported combinations of queues and filters.
  • Results differ between releases. Confirm the release, default attribute values, and the queue ordering used by PrioQueueDisc. Generated APIs and defaults change between versions.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.