qos β Quality of Service Memory Policies
Scheduling and prioritization framework for memory controllers β enabling fair, differentiated, and QoS-aware memory access across multiple requestors.
Table of Contents
Overview
📚 What is this & why does it exist?
Think of the QoS memory system as an airport with multiple priority lanes. Economy passengers (low-priority requestors) and business class passengers (high-priority requestors) both need to get through security (the memory controller). The Fixed Priority policy always lets business class go first. The Proportional Fair policy gives everyone a fair share based on their needs β if an economy passenger has been waiting a long time, they get bumped up. The Turnaround Policy decides when to switch the security lanes from departures (reads) to arrivals (writes) and back, minimizing the overhead of switching direction at the DRAM bus.
The qos/ directory provides a QoS framework for gem5 memory controllers. It sits between the Cache Hierarchy | gem5 Wiki" href="../cache/index.html">cache hierarchy and the DRAM/NVM backend, enabling differentiated memory service based on requestor identity, priority, or fairness objectives.
The framework consists of three key abstractions:
- QoS Memory System | gem5 Wiki" class="xref">MemCtrl β QoS-aware memory controller base class that integrates policies into the scheduling pipeline
- QoS Policy β Priority assignment algorithm (Fixed Priority, Proportional Fair)
- Turnaround Policy β Read/write bus direction switching policy
- Queue Policy β Per-requestor queue management and admission control
Architecture
flowchart TD
CPU0["CPU 0\n(Requestor 0)"]
CPU1["CPU 1\n(Requestor 1)"]
GPU["GPU\n(Requestor 2)"]
CACHE["Cache Hierarchy"]
XBAR["Coherent XBar"]
QMC["QoS MemCtrl"]
subgraph QoS_Pipeline["QoS Pipeline"]
POLICY["Priority Policy\n(FixedPrio / PF)"]
QPOL["Queue Policy\n(Admission Control)"]
SCHED["Request Scheduler"]
TP["Turnaround Policy"]
end
READQ["Read Queue"]
WRITEQ["Write Queue"]
DRAM["DRAM Interface"]
CPU0 --> CACHE
CPU1 --> CACHE
GPU --> CACHE
CACHE --> XBAR
XBAR --> QMC
QMC --> POLICY
POLICY --> QPOL
QPOL --> SCHED
SCHED --> TP
TP --> READQ
TP --> WRITEQ
READQ --> DRAM
WRITEQ --> DRAM
QoS Request Flow
sequenceDiagram
participant REQ as Requestor
participant MC as QoS MemCtrl
participant POL as Policy
participant QP as Queue Policy
participant TP as Turnaround Policy
participant DRAM as DRAM
REQ->>MC: Memory Request (pkt)
MC->>POL: schedule(requestorId, pkt)
POL-->>MC: priority level
MC->>QP: enqueue(pkt, priority)
QP-->>MC: accepted / rejected
Note over MC: Request placed in
priority-ordered queue
MC->>TP: selectBusState()
TP-->>MC: READ or WRITE
MC->>DRAM: issue(pkt)
DRAM-->>MC: response
MC-->>REQ: Response (pkt)
Narrated Walkthrough: QoS-Aware Memory Access
requestorId identifying the source (CPU 0, CPU 1, GPU, etc.).Policy::schedule() to determine the request's priority. With FixedPriority, priority is a static mapping from requestorId. With ProportionalFair, priority is dynamically adjusted based on each requestor's recent bandwidth consumption.QueuePolicy decides if the request can enter the queue. It may reject low-priority requests when the queue is congested, or reorder entries to maintain priority ordering.TurnaroundPolicy determines whether to switch the DRAM bus between read and write mode. The IdealTurnaround policy minimizes bus turnaround overhead by batching reads and writes.Key Classes Reference
| Class | File | Description |
|---|---|---|
qos::MemCtrl | mem_ctrl.hh | QoS-aware memory controller base class. Extends ClockedObject and integrates QoS policies into request scheduling. Maintains per-requestor priority mappings, read/write queues with priority ordering, and detailed per-requestor statistics. All QoS-enabled memory controllers inherit from this. |
qos::MemSinkCtrl | mem_sink.hh | Atomic and Timing Simple CPUs β gem5 Wiki" href="../../cpu_doc/simple/index.html">Simple QoS memory controller implementation for testing. Acts as a "sink" that consumes requests with configurable latency β useful for isolating QoS policy behavior from DRAM timing complexity. |
qos::Policy | policy.hh | Abstract base class for QoS scheduling policies. Defines the schedule(requestorId, pkt) interface that maps a request to a priority level. Subclasses implement specific priority assignment algorithms. |
qos::FixedPriorityPolicy | policy_fixed_prio.hh | Static priority mapping β each requestor is assigned a fixed priority level at configuration time. Simple and deterministic; suitable when workload priorities are known a priori. |
qos::PropFairPolicy | policy_pf.hh | Proportional Fair scheduling β dynamically adjusts priority based on each requestor's recent bandwidth usage. Requestors that have consumed less bandwidth relative to their "fair share" receive higher priority. Balances throughput and fairness. |
qos::QueuePolicy | q_policy.hh | Queue management policy controlling admission and ordering within the read/write queues. Can enforce per-requestor queue limits to prevent monopolization. |
qos::TurnaroundPolicy | turnaround_policy.hh | Abstract base for read/write bus direction policies. Determines when to switch the DRAM data bus between reading and writing to minimize turnaround penalty. |
qos::TurnaroundPolicyIdeal | turnaround_policy_ideal.hh | Ideal turnaround policy that switches bus direction with minimal overhead β serves as an upper bound for turnaround efficiency. |
Scheduling Policies Deep Dive
Fixed Priority Policy
The simplest QoS policy: each requestor has a statically configured priority level. The memory controller always serves higher-priority requestors first.
flowchart LR
REQ["Incoming Request"]
MAP["Priority Map\n(requestorId β priority)"]
PRI["Assigned Priority"]
Q["Priority Queue"]
REQ --> MAP
MAP --> PRI
PRI --> Q
MAP -.- CFG["Configuration:\nCPU0 β 3 (high)\nCPU1 β 2\nGPU β 1 (low)"]
Characteristics:
- Zero runtime overhead β priority is a table lookup
- Deterministic behavior β easy to reason about
- Risk of starvation for low-priority requestors under heavy load
- Best for: systems with well-defined priority hierarchies (e.g., real-time CPU over background GPU)
Proportional Fair Policy
A dynamic policy inspired by wireless networking's proportional fair scheduling. Each requestor's priority is inversely proportional to its recent bandwidth consumption.
flowchart TD
REQ["Incoming Request"]
HIST["Bandwidth History\n(per requestor)"]
SCORE["Score = Weight / Recent_BW"]
RANK["Rank by Score"]
PRI["Priority Assignment"]
REQ --> HIST
HIST --> SCORE
SCORE --> RANK
RANK --> PRI
Characteristics:
- Adapts to changing workload behavior at runtime
- Prevents starvation β underserved requestors naturally receive higher priority
- Configurable weight per requestor to bias the "fair share"
- Tracks bandwidth over a sliding time window
- Best for: multi-tenant workloads where fairness matters
Turnaround Policy
DRAM bus direction switching is expensive (typically 5-15ns turnaround penalty). The turnaround policy batches reads and writes to minimize switches:
- IdealTurnaround β Switches only when the current direction's queue is empty or a high-priority request in the other direction is waiting. Minimizes turnaround count.
- Custom policies can implement hysteresis, threshold-based switching, or age-based urgency.
Queue Management
The QueuePolicy controls how requests are admitted into and ordered within the memory controller's queues:
| Aspect | Mechanism | Purpose |
|---|---|---|
| Per-requestor limits | Maximum queue entries per requestorId | Prevents one requestor from monopolizing the queue |
| Priority ordering | Requests sorted by assigned QoS priority | Ensures higher-priority requests are served first |
| Admission control | Reject/stall low-priority requests when queue is full | Back-pressure to prevent buffer overflow |
| Separate read/write | Independent queues for reads and writes | Enables read-priority or write-draining modes |
QoS Statistics
The QoS framework collects detailed per-requestor statistics:
| Statistic | Description |
|---|---|
avgPriority | Average priority level assigned to each requestor |
readBW / writeBW | Bandwidth consumed by each requestor (reads and writes separately) |
totalReadLatency / totalWriteLatency | Cumulative latency per requestor |
numReads / numWrites | Request count per requestor |
busStateSwitch | Number of readβwrite bus direction switches |
priorityDistribution | Histogram of assigned priority levels per requestor |
queueOccupancy | Average queue depth per requestor |
Configuration Examples
// Python configuration: Fixed Priority QoS
system.mem_ctrl = MemCtrl(
dram = DDR4_2400_16x4(),
qos_policy = QoSFixedPriority(
# CPU cores get high priority, GPU gets low
qos_priorities = {
'system.cpu0': 3,
'system.cpu1': 3,
'system.gpu': 1,
'system.dma': 0
}
),
qos_turnaround_policy = QoSIdealTurnaround()
)
// Python configuration: Proportional Fair QoS
system.mem_ctrl = MemCtrl(
dram = DDR4_2400_16x4(),
qos_policy = QoSPropFairPolicy(
# Weight each requestor's fair share
weight = {
'system.cpu0': 2.0, # Double share
'system.cpu1': 1.0, # Normal share
'system.gpu': 0.5 # Half share
},
# Sliding window for bandwidth tracking
scoring_interval = '10us'
),
qos_turnaround_policy = QoSIdealTurnaround()
)
// C++ internals: Policy interface
namespace qos {
class Policy : public SimObject {
public:
// Called for each incoming memory request
// Returns priority level (0 = lowest, N = highest)
virtual uint8_t schedule(const RequestorID id,
const uint64_t data) = 0;
};
class FixedPriorityPolicy : public Policy {
// priority_map: RequestorID -> priority level
std::map<RequestorID, uint8_t> priority_map;
public:
uint8_t schedule(const RequestorID id,
const uint64_t data) override {
auto it = priority_map.find(id);
return (it != priority_map.end()) ? it->second : 0;
}
};
} // namespace qos
Cross-Module Connections
flowchart LR
subgraph Cache["mem/cache/"]
BC["BaseCache"]
end
subgraph QoS["mem/qos/"]
QMC["qos::MemCtrl"]
POL["qos::Policy"]
TP["TurnaroundPolicy"]
QP["QueuePolicy"]
end
subgraph MemCtrl["mem/"]
MC["MemCtrl"]
DI["DRAMInterface"]
end
subgraph Core["sim/"]
CO["ClockedObject"]
end
CO --> QMC
QMC --> POL
QMC --> TP
QMC --> QP
BC -->|"Writeback / Miss"| QMC
QMC --> MC
MC --> DI
The QoS framework integrates at the memory controller level:
- Upstream: Receives requests from the cache hierarchy via
ResponsePort - Policies: Pluggable policy objects attached to the controller
- Downstream: Issues scheduled requests to
DRAMInterfaceorNVMInterface - Base class:
qos::MemCtrlextendsClockedObjectand can be used as a base for custom memory controllers
Configuration Reference
| Parameter | SimObject | Description | Default |
|---|---|---|---|
qos_policy | QoSMemCtrl | QoS scheduling policy object | None (no QoS) |
qos_turnaround_policy | QoSMemCtrl | Bus turnaround policy object | None |
qos_q_policy | QoSMemCtrl | Queue management policy | None |
qos_priorities | FixedPriorityPolicy | Map of requestor β priority level | All priority 0 |
weight | PropFairPolicy | Per-requestor fair-share weight | 1.0 |
scoring_interval | PropFairPolicy | Bandwidth measurement window | System-dependent |
read_buffer_size | QoSMemCtrl | Read queue capacity | 32 |
write_buffer_size | QoSMemCtrl | Write queue capacity | 64 |
write_high_thresh | QoSMemCtrl | Write drain trigger threshold (%) | 85% |
write_low_thresh | QoSMemCtrl | Write drain stop threshold (%) | 50% |
Directory Map
Glossary
| Term | Definition |
|---|---|
| Requestor | A unique source of memory requests, identified by RequestorID. Typically one per CPU core, GPU CU, or DMA engine. |
| Priority Level | Integer value (0 = lowest) assigned to requests by the QoS policy. Higher-priority requests are scheduled before lower ones. |
| Proportional Fair | Scheduling algorithm that balances fairness and throughput by favoring requestors that have recently received less service. |
| Bus Turnaround | The time penalty incurred when switching the DRAM data bus direction between reads and writes (typically tWTR or tRTW). |
| FR-FCFS | First-Ready First-Come-First-Served β common DRAM scheduling policy that prioritizes row-hit requests, then by arrival order. |
| Write Drain | Mode where the controller exclusively processes writes to free write buffer space, triggered when the write queue exceeds a threshold. |