业界事件驱动仿真器 dispatch 机制调研
目的:解答 G5 RC Link 仿真器中 segment "待发队列 → 链路层 TX" 的喂入应当 polling-driven 还是 event-driven,从 5 个业界主流开源仿真器中提取可直接借鉴的实现模式。
TL;DR
| 仿真器 | 仿真范式 | Dispatch 模式 | 关键机制 | wake-dep 风险 |
|---|---|---|---|---|
| ns-3 (HPCC/RDMA) | event-driven (DES) | 纯 event-driven | Simulator::Schedule(qp->m_nextAvail - Now, &DequeueAndTransmit) | 无 — 下一次发送时刻被精确 schedule,不轮询 |
| BookSim2 | cycle-driven | event-on-cycle (混合) | _active flag + 每 cycle _InternalStep(),flit/credit 到达置 _active=true | 无 — 但范式不同 |
| Garnet (gem5) | cycle-driven | event-driven within cycle | scheduleEvent(Cycles(1)) 仅在 checkReschedule() 发现有 work 时才下一周期重唤醒 | 无 — 显式条件检查 |
| htsim | event-driven (DES) | 纯 event-driven | eventlist().sourceIsPendingRel(self, drainTime),queueWasEmpty triggered | 无 — 队列空 → enqueue 立即 schedule completeService |
| OMNeT++ INET | event-driven (DES) | push/pull + self-message | handleCanPullPacketChanged() 反应式拉取 + scheduleAfter(txDuration, txTimer) | 无 — 上下游 callback 取代 polling |
综合:业界事件驱动阵营(ns-3 / htsim / OMNeT++)的 dispatch 全部是 push-driven,没有任何一家使用 "周期性 wake 然后 scan ready 队列" 的 polling 模式。共同套路是:每次 dispatch 完成后,根据"下一帧可发时刻"(受 rate / credit / link-busy 三者决定)一次性 Schedule(nextAvail, dispatchFn);新事件(ACK / credit / enqueue)到达时若发现需要更早唤醒,则取消旧事件重新 schedule,或直接调用 dispatch。G5 RcLinkWake polling 模式属于业界异常做法,其 wake 频率漂移导致 latency 不稳定的现象在以上 5 个仿真器中均不存在,因为它们根本不存在"wake 频率"这个变量。
各仿真器详解
1. ns-3 (重点 RDMA 模块:QbbNetDevice + RdmaHw)
TL;DR
QbbNetDevice 的 dispatch 是 纯 event-driven:每次发送结束 (TransmitComplete) 或被 PFC Resume / 新 QP 入队时调用 DequeueAndTransmit(),若所有 QP 都被 rate-limit 阻塞,则计算 min(qp->m_nextAvail) 并 Simulator::Schedule() 唤醒,不依赖任何周期性 wake。
物理事件 → 仿真事件映射
| 物理事件 | 仿真事件 |
|---|---|
| 帧发完(传输时延到) | Simulator::Schedule(txCompleteTime, &TransmitComplete) → 内部直接调 DequeueAndTransmit |
| 速率受限(DCQCN 降速)下一帧可发时刻 | Simulator::Schedule(qp->m_nextAvail - Now, &DequeueAndTransmit) |
PFC Pause 解除 | Resume() 直接同步调 DequeueAndTransmit |
| 新 QP 入队 | AddQueuePair 路径同步调 DequeueAndTransmit |
代码片段(HPCC qbb-net-device.cc 中 DequeueAndTransmit 关键段)
void QbbNetDevice::DequeueAndTransmit() {
if (!m_linkUp) return;
if (m_txMachineState == BUSY) return; // 链路忙,等 TransmitComplete 重入
Ptr<Packet> p;
if (m_node->GetNodeType() == 0) { // NIC
int qIndex = m_rdmaEQ->GetNextQindex(m_paused);
if (qIndex != -1024) {
// 找到可发 QP,立即发
p = m_rdmaEQ->DequeueQindex(qIndex);
TransmitStart(p);
return;
}
// 所有 QP 都受限:计算最早可发时刻并 schedule
Time t = Simulator::GetMaximumSimulationTime();
for (uint32_t i = 0; i < m_rdmaEQ->GetFlowCount(); i++) {
Ptr<RdmaQueuePair> qp = m_rdmaEQ->GetQp(i);
if (qp->GetBytesLeft() == 0) continue;
t = Min(qp->m_nextAvail, t); // 物理就绪时刻
}
if (m_nextSend.IsExpired() &&
t < Simulator::GetMaximumSimulationTime() &&
t > Simulator::Now()) {
m_nextSend = Simulator::Schedule(
t - Simulator::Now(),
&QbbNetDevice::DequeueAndTransmit, this);
}
}
}
void QbbNetDevice::TransmitStart(Ptr<Packet> p) {
m_txMachineState = BUSY;
Time txCompleteTime = m_bps.CalculateBytesTxTime(p->GetSize());
Simulator::Schedule(txCompleteTime,
&QbbNetDevice::TransmitComplete, this);
m_channel->TransmitStart(p, this, txCompleteTime);
}
void QbbNetDevice::TransmitComplete() {
m_txMachineState = READY;
DequeueAndTransmit(); // 链路空闲 → 立刻再 dispatch
}
来源:alibaba-edu/High-Precision-Congestion-Control/simulation/src/point-to-point/model/qbb-net-device.cc(DequeueAndTransmit ~ 行 320-400,TransmitStart/Complete ~ 行 420-460;行号近似,HPCC 仓库自首次发布以来基本未改动该段)。
如何避免 wake-dep
ns-3 的关键设计:m_nextSend 是单一未完成事件句柄,靠 IsExpired() 防重复 schedule;每次 dispatch 决定 "要么发,要么 schedule 一次精确唤醒",不存在"以固定频率 polling"的代码路径。即使把仿真器加速 100×,发送时刻仍精确等于 qp->m_nextAvail,因为该时刻是被 Schedule 钉死的事件,与 wake 频率无关。
参考引用
- HPCC paper: Li et al., "HPCC: High Precision Congestion Control", SIGCOMM'19 — DCQCN/HPCC 全部基于 HPCC 这套 NIC 模型
- 源码:https://github.com/alibaba-edu/High-Precision-Congestion-Control/tree/master/simulation/src/point-to-point/model
- 早期版本:bobzhuyb/ns3-rdma (同一作者 Yibo Zhu)
2. BookSim2 (NoC, cycle-driven 但带 active flag 优化)
TL;DR
BookSim2 是 cycle-driven 仿真器(每 cycle 全网 tick 一遍),但通过 _active flag 跳过 idle router:当 input/credit channel 无新到达且所有 pending VC 队列为空时,router 不进入 _InternalStep()。这是 "cycle-driven 仿真器优化空转" 的标准做法,与 event-driven 仿真器架构差异较大。
物理事件 → 仿真事件映射
| 物理事件 | 仿真事件 |
|---|---|
| Flit 到达 input channel | _ReceiveFlits() 置 _active=true |
| Credit 到达 input channel | _ReceiveCredits() 置 _active=true |
| VC 处于 routing/vc_alloc/sw_alloc/crossbar 任一阶段 | 对应 _route_vcs/_vc_alloc_vcs/_sw_alloc_vcs/_crossbar_flits 非空,_active 保持 |
| 全部队列空 + 无入站 flit/credit | _active=false,router 跳过当 cycle 处理 |
代码片段 (router.cpp Evaluate + iq_router.cpp _InternalStep)
// Router::Evaluate (router.cpp, ~line 70)
void Router::Evaluate() {
_partial_internal_cycles += _internal_speedup;
while (_partial_internal_cycles >= 1.0) {
_InternalStep();
_partial_internal_cycles -= 1.0;
}
}
// IQRouter::_InternalStep (iq_router.cpp, ~line 200)
void IQRouter::_InternalStep() {
if (!_active) { return; } // 关键优化:idle 跳过
bool have_flits = _ReceiveFlits();
bool have_credits = _ReceiveCredits();
_active = _active || have_flits || have_credits;
_InputQueuing(); // flit → input buffer
while (!_proc_credits.empty()) { /* ... */ } // 处理 credit
_RouteEvaluate(); _VCAllocEvaluate();
_SWAllocEvaluate(); _SwitchEvaluate();
_RouteUpdate(); _VCAllocUpdate();
_SWAllocUpdate(); _SwitchUpdate();
_OutputQueuing();
_active = !_in_queue_flits.empty()
|| !_route_vcs.empty()
|| !_vc_alloc_vcs.empty()
|| !_sw_alloc_vcs.empty()
|| !_crossbar_flits.empty()
|| !_out_queue_credits.empty();
}
来源:booksim/booksim2/src/routers/iq_router.cpp (_InternalStep 约 line 195-260),src/routers/router.cpp (Evaluate 约 line 65-75)。
如何避免 wake-dep
BookSim2 本质是 cycle-driven,不存在 "wake 频率" 这个概念——所有 router 在每个 cycle 被 trafficmanager 主动 tick。_active flag 只是性能优化(跳过 idle router 的 _InternalStep),不影响时序。因此 latency 计算是确定性的:flit 到达 cycle T → cycle T+1 进 input buffer → cycle T+2 进 SA → ... 与"wake 频率"无关,因为根本没有 wake,是统一时钟。
对 G5 借鉴价值有限:G5 是 event-driven,不能直接照搬 cycle-driven 的"per-cycle scan + active flag"做法。
参考引用
- Jiang et al., "A Detailed and Flexible Cycle-Accurate Network-on-Chip Simulator", ISPASS'13 — BookSim2 主论文
- 源码:https://github.com/booksim/booksim2
3. Garnet (gem5, cycle-driven NoC)
TL;DR
Garnet 是 cycle-driven(基于 gem5 Clocked 类),但采用 "event-driven within cycle" 的混合优化:router/NI 不每个 cycle 必然 wake,而是通过 checkReschedule() 检查是否有 work,仅当有 work 时 scheduleEvent(Cycles(1)) 注册下一 cycle 的 wakeup。无 work 时停摆,靠新事件(flit/credit/protocol msg 到达)重新激活。
物理事件 → 仿真事件映射
| 物理事件 | 仿真事件 |
|---|---|
| 上游 cache controller 入队新 message | dequeueCallback → scheduleEventAbsolute(clockEdge(Cycles(1))) 唤醒 NI |
| Flit 到达 input link | inNetLink->isReady() true → checkReschedule 触发下一 cycle wakeup |
| Credit 到达 | inCreditLink->isReady() true → 同上 |
| VC 内仍有未发 flit | niOutVc.isReady() true → 同上 |
| 全部空 | 不 schedule,NI 停摆直到外部事件 |
代码片段 (NetworkInterface.cc checkReschedule + dequeueCallback)
// dequeueCallback: 上游 protocol buffer 有新消息时由 RubyController 调用
void NetworkInterface::dequeueCallback() {
scheduleEventAbsolute(clockEdge(Cycles(1))); // 下一周期 wakeup
}
// wakeup 末尾调用 checkReschedule(),决定是否还需要下一周期
void NetworkInterface::checkReschedule() {
for (const auto& it : inNode_ptr) { // 1. protocol 输入 buffer
if (it == nullptr) continue;
while (it->isReady(clockEdge())) {
scheduleEvent(Cycles(1));
return;
}
}
for (auto& ni_out_vc : niOutVcs) { // 2. NI 输出 VC 还有 flit
if (ni_out_vc.isReady(clockEdge(Cycles(1)))) {
scheduleEvent(Cycles(1));
return;
}
}
for (auto &iPort : inPorts) { // 3. 入站 link 有 flit
NetworkLink *inNetLink = iPort->inNetLink();
if (inNetLink->isReady(curTick())) {
scheduleEvent(Cycles(1));
return;
}
}
for (auto &oPort : outPorts) { // 4. 入站 credit
CreditLink *inCreditLink = oPort->inCreditLink();
if (inCreditLink->isReady(curTick())) {
scheduleEvent(Cycles(1));
return;
}
}
// 都没有 → 不 schedule, NI 停摆
}
来源:gem5/src/mem/ruby/network/garnet/NetworkInterface.cc — dequeueCallback (line ~106),checkReschedule (line ~564-601)。Router 端类似:Router::wakeup() 末尾 schedule_wakeup(Cycles(1)) 也是有 work 才调。
如何避免 wake-dep
Garnet 的核心保险:scheduleEvent(Cycles(1)) 的参数是固定的 1 cycle,且只有 "存在 ready 的输入" 这种确定条件下才下次 wake;不存在 "有时 0.5 cycle wake,有时 2 cycle wake" 这种漂移。如果是空转就根本不 schedule。所以 latency 是 cycle-accurate 确定的。
这是 G5 最值得借鉴的 event-driven NI 模型:dispatch 后立刻检查所有 "可能产生下一动作" 的输入源,对每一个源精确 schedule,而不是"等下个 wake tick 再扫一次"。
参考引用
- Agarwal et al., "GARNET: A Detailed On-Chip Network Model inside a Full-System Simulator", ISPASS'09
- Bharadwaj et al., "Garnet2.0: A Detailed On-Chip Network Model inside a Full-System Simulator", 2018 — 重点
- 源码:https://github.com/gem5/gem5/tree/stable/src/mem/ruby/network/garnet
- 文档:https://www.gem5.org/documentation/general_docs/ruby/garnet-2/
4. htsim (数据中心 / RDMA 流量仿真器)
TL;DR
htsim 是 纯 event-driven,dispatch 完全 push-driven:Queue::receivePacket 入队时若队列空则立刻 beginService,beginService 计算 drainTime 并 sourceIsPendingRel;completeService 内若队列非空再次 beginService 链式。EQDS EqdsPullPacer 也用同一模式做 rate-limited credit 发送。
物理事件 → 仿真事件映射
| 物理事件 | 仿真事件 |
|---|---|
| Packet enqueue 到空队列 | receivePacket → beginService → sourceIsPendingRel(self, drainTime) |
| 当前 packet drain 完 | EventList 触发 doNextEvent → completeService → 队列非空时再 beginService |
Pacer 收到 credit 请求且 !_active | sourceIsPendingRel(self, 0),_active = true |
| Pacer 发完一个 pull packet | sourceIsPendingRel(self, _pktTime),下一帧精确间隔 |
代码片段 (sim/queue.cpp)
void Queue::receivePacket(Packet& pkt) {
if (_queuesize + pkt.size() > _maxsize) { /* drop */ return; }
bool queueWasEmpty = _enqueued.empty();
_enqueued.push(&pkt);
_queuesize += pkt.size();
if (queueWasEmpty) {
beginService(); // 空 → 非空:启动服务
}
}
void Queue::beginService() {
assert(!_enqueued.empty());
eventlist().sourceIsPendingRel(*this,
drainTime(_enqueued.back())); // 链路速率推出 drain 时刻
}
void Queue::completeService() {
Packet* pkt = _enqueued.back();
_enqueued.pop();
_queuesize -= pkt->size();
pkt->sendOn(); // 推到下游
if (!_enqueued.empty()) {
beginService(); // 链式 schedule 下一帧
}
}
// EQDS 的 pacer 同样使用此 pattern:
// EqdsPullPacer::doNextEvent (sim/eqds.cpp)
sink->getNIC()->sendControlPacket(pullPkt);
_active = true;
eventlist().sourceIsPendingRel(*this, _pktTime); // 固定间隔 rate-limit
来源:Broadcom/csg-htsim/sim/queue.cpp (receivePacket/beginService/completeService 约 line 80-180),sim/eqds.cpp (EqdsPullPacer 约 line 400+)。
如何避免 wake-dep
htsim 完全不存在"polling wake"概念——EventList 是纯 priority-queue DES,只有 sourceIsPendingRel(obj, delta) 注册的事件会被触发。Queue 每发完一帧立即根据下一帧的 drainTime 精确 schedule,因此 latency 完全由物理参数(linkSpeed、pktSize)决定,与仿真器步进无关。
_servicing flag 仅在 PriorityQueue 子类中用于跟踪 "当前在 drain 哪个优先级",不是 polling 控制。
参考引用
- Handley et al., "Re-architecting datacenter networks and stacks for low latency and high performance" (NDP), SIGCOMM'17
- Olteanu et al., "An edge-queued datagram service for all datacenter traffic" (EQDS), NSDI'22
- 源码:https://github.com/Broadcom/csg-htsim
5. OMNeT++ INET (EthernetCsmaMac + PacketServer)
TL;DR
OMNeT++ INET 是 纯 event-driven (DES),dispatch 采用 push/pull callback + self-message timer 两层架构:上游 queue 状态变化通过 handleCanPullPacketChanged(gate) 反应式通知 MAC(取代 polling),MAC 内部用 scheduleAfter(duration, txTimer) 注册传输完成事件(self-message)。
物理事件 → 仿真事件映射
| 物理事件 | 仿真事件 |
|---|---|
| 上游 queue 从空变非空 | handleCanPullPacketChanged callback → 触发 dequeuePacket + handleUpperPacket |
| 帧传输完成 | scheduleAfter(txDuration, txTimer) → handleSelfMessage(txTimer) |
| IFG 时长 | scheduleAfter(ifgDuration, ifgTimer) |
| CSMA backoff | scheduleAfter(backoffDuration, backoffTimer) |
| 下游可推 | handleCanPushPacketChanged callback |
代码片段 (EthernetCsmaMac.cc)
void EthernetCsmaMac::handleSelfMessage(cMessage *message) {
handleWithFsm(message->getKind(), message); // FSM 统一处理 timer
}
void EthernetCsmaMac::handleUpperPacket(Packet *packet) {
handleWithFsm(UPPER_PACKET, packet);
}
// 上游 queue 状态变化的 callback - 替代 polling
void EthernetCsmaMac::handleCanPullPacketChanged(const cGate *gate) {
if (!carrierSense && currentTxFrame == nullptr
&& fsm.getState() == IDLE && canDequeuePacket()) {
Packet *packet = dequeuePacket();
handleUpperPacket(packet); // 反应式拉取
}
}
// 启动一次传输:精确 schedule 完成事件
simtime_t duration = (packet->getDataLength()
+ ETHERNET_PHY_HEADER_LEN
+ phy->getEsdLength()).get<b>() / mode.bitrate;
scheduleAfter(duration, txTimer);
来源:inet-framework/inet/src/inet/linklayer/ethernet/basic/EthernetCsmaMac.cc,相关 callback 在 INET PacketProcessorBase 基类中定义。
如何避免 wake-dep
OMNeT++ 的设计哲学:模块间通过明确的 contract(push: pushPacket / pull: pullPacket + canPullPacketChanged notifier)通信,不允许"周期性 polling 上游队列"。当上游 queue 入队时,它显式调用下游 handleCanPullPacketChanged,下游只有在收到 callback 时才会尝试拉取。这从框架层杜绝了 wake-dep。INET 还在 base class 中提供 subscribe(packetPushedSignal, this) 等机制保证 callback 触发的原子性。
参考引用
- Varga & Hornig, "An overview of the OMNeT++ simulation environment", SIMUTools'08
- INET queueing framework: https://inet.omnetpp.org/docs/users-guide/ch-queueing.html
- 源码:https://github.com/inet-framework/inet/tree/master/src/inet/linklayer/ethernet
共同模式总结
跨 5 个仿真器,event-driven 阵营(ns-3 / htsim / OMNeT++)以及"event-driven within cycle"的 Garnet 共享以下设计模式:
Pattern A: "Drain-Time Self-Scheduling"(ns-3 / htsim 共有)
发完一帧后,根据物理参数(rate、credit、link-busy 释放时刻)计算下一帧最早可发时刻,对该时刻精确 Schedule(dispatchFn)。不依赖任何"周期性 wake"。
对应 G5 应改造:每次 segment 喂入后,在已发生 segment 的 tx_done_time 和未发 segment 的 earliest_ready_ns 中取 min,精确 schedule 下次 dispatch,而非等 RcLinkWake 周期触发。
Pattern B: "Queue-Empty Edge Trigger"(htsim / INET 共有)
新 packet 入队时检测 "队列是否从空变非空",仅在 edge(empty→non-empty)触发 beginService。队列已在 service 状态时仅追加,不重复 schedule。
对应 G5 应改造:segment_queue enqueue 时若 RcLinkTx 当前空闲,立即 schedule dispatch;若正在 service 状态,靠 service 结束的链式 schedule 接续。
Pattern C: "Push/Pull Callback Contract"(INET / Garnet 共有)
模块间状态变化通过显式 callback (handleCanPullPacketChanged / dequeueCallback) 通知,取代下游周期性 polling 上游。
对应 G5 应改造:CDMA 注入新 segment 到 segment_queue 时,应直接调一次 RcLinkTx 的 dispatch 函数(或 schedule 一个 SegmentReady(now) 事件),而非靠 RcLinkWake 扫描。
Pattern D: "Conditional Reschedule"(Garnet 独有,对 G5 最直接借鉴)
dispatch 完成后扫描所有"可能产生下一动作"的输入源列表,任一非空即 scheduleEvent(Cycles(1)),否则停摆。
对应 G5 应改造:feed_rc_link 完成时检查 (1) segment_queue 有 earliest_ready_ns > now 的 segment(schedule 到该时刻),(2) RcLinkTx slot 是否将释放(schedule 到 ACK 到达时刻),(3) datapath 是否将空闲(schedule 到 frame_send_delay 后)。三者取 min,只 schedule 一个事件,不 self-reschedule polling。
反模式总结
5 个仿真器没有任何一个采用 G5 当前的 RcLinkWake 模式:固定周期 schedule self,wake 时 scan 全部队列。这种模式在业界被认为是 cycle-driven 仿真器(如 BookSim2)的优化空间——它们用 _active flag 跳过 idle,但仍是统一时钟驱动。在 event-driven 仿真器里,"周期 wake + scan" 等同于把 DES 退化成 cycle-driven,白白损失精度(wake 时机与物理就绪时机不对齐)。
对 G5 RC Link 重构的建议(基于业界经验)
1. 用 Pattern A + D 重构 segment dispatch(核心改动)
移除 RcLinkWake self-reschedule 循环。feed_rc_link() 改为以下契约:
fn feed_rc_link(now):
# 1. 喂入所有 ready 的 segment
while let Some(seg) = segment_queue.peek():
if seg.earliest_ready_ns > now: break
rc_link_tx.try_enqueue(seg) # 入 vc_pending
segment_queue.pop()
# 2. 计算下一次必须 dispatch 的时刻 = min of:
# - segment_queue 中下一个 segment 的 earliest_ready_ns
# - rc_link_tx 当前 datapath busy 的释放时刻
# - 任何 slot 等 ACK 的预计返回时刻(可选,若 ACK 是 hop-deterministic)
next_t = min(
segment_queue.front().earliest_ready_ns or +inf,
rc_link_tx.datapath_busy_until or +inf,
)
if next_t < +inf and next_t > now:
schedule_event(SegmentDispatch{link_id}, next_t)
2. ACK 到达事件直接触发 dispatch(Pattern C)
ACK handler 释放 slot 后立即调一次 feed_rc_link(now),不依赖 RcLinkWake。同理 on_tx_done(datapath 释放)也直接调。CDMA 新 segment 入 segment_queue 时同样直接调一次。
3. 删除 RcLinkWake 事件类型(清理)
完成 1 + 2 后,RcLinkWake 不再有任何 producer,可整类型删除。这是最强的"消除 wake-dep"证据——根本没有 wake。
4. 借鉴 ns-3 m_nextSend.IsExpired() 防重复 schedule
第 1 条 schedule 之前检查 "已有未触发的 SegmentDispatch 事件" → 若新计算的 next_t 更早,cancel + 重 schedule;若不更早,跳过。避免同一 link 多次 schedule 同一 dispatch。
5. 测试验证 wake-frequency 独立性
重构后,故意把仿真时间步长改成 ×10 / ×0.1,跑 21 个拓扑测试,latency 数值应当 bit-exact 一致。这是 polling 模式做不到的。
6. (可选)参考 htsim _servicing pattern 处理多 VC 仲裁
若多 VC 同时 ready 时需要轮转,可借鉴 htsim PriorityQueue 的 _servicing flag 跟踪当前 VC,而不是每次 dispatch 都重新扫所有 VC。这是性能优化,不影响时序正确性。
优先级
1 + 2 + 3 是必须的(消除 wake-dep 根因);4 是工程鲁棒性;5 是回归保障;6 是性能优化。Type 2 决策,5 分钟可回滚 — 建议直接做 1+2+3,5 验证,4+6 视实现复杂度按需加。
引用清单
源代码
- ns-3 HPCC: https://github.com/alibaba-edu/High-Precision-Congestion-Control/blob/master/simulation/src/point-to-point/model/qbb-net-device.cc
- ns-3 RDMA (Yibo Zhu 原始):https://github.com/bobzhuyb/ns3-rdma
- BookSim2 IQRouter: https://github.com/booksim/booksim2/blob/master/src/routers/iq_router.cpp
- BookSim2 Router base: https://github.com/booksim/booksim2/blob/master/src/routers/router.cpp
- Garnet NetworkInterface: https://github.com/gem5/gem5/blob/stable/src/mem/ruby/network/garnet/NetworkInterface.cc
- Garnet Router: https://github.com/gem5/gem5/blob/stable/src/mem/ruby/network/garnet/Router.cc
- htsim queue: https://github.com/Broadcom/csg-htsim/blob/master/sim/queue.cpp
- htsim EQDS: https://github.com/Broadcom/csg-htsim/blob/master/sim/eqds.cpp
- INET EthernetCsmaMac: https://github.com/inet-framework/inet/blob/master/src/inet/linklayer/ethernet/basic/EthernetCsmaMac.cc
- INET PacketServer: https://github.com/inet-framework/inet/blob/master/src/inet/queueing/server/PacketServer.cc
Paper
- Li et al., "HPCC: High Precision Congestion Control", SIGCOMM 2019 — DOI 10.1145/3341302.3342085
- Mittal et al., "TIMELY: RTT-based Congestion Control for the Datacenter", SIGCOMM 2015
- Zhu et al., "Congestion Control for Large-Scale RDMA Deployments" (DCQCN), SIGCOMM 2015
- Jiang et al., "A Detailed and Flexible Cycle-Accurate Network-on-Chip Simulator" (BookSim2), ISPASS 2013
- Agarwal et al., "GARNET: A Detailed On-Chip Network Model inside a Full-System Simulator", ISPASS 2009
- Bharadwaj et al., "Garnet2.0: A Detailed On-Chip Network Model", 2018
- Handley et al., "Re-architecting datacenter networks and stacks for low latency and high performance" (NDP), SIGCOMM 2017
- Olteanu et al., "An edge-queued datagram service for all datacenter traffic" (EQDS), NSDI 2022
- Varga & Hornig, "An overview of the OMNeT++ simulation environment", SIMUTools 2008
官方文档
- gem5 Garnet 2.0: https://www.gem5.org/documentation/general_docs/ruby/garnet-2/
- gem5 Clocked class: https://doxygen.gem5.org/release/current/classgem5_1_1Clocked.html
- INET queueing framework: https://inet.omnetpp.org/docs/users-guide/ch-queueing.html
- OMNeT++ cSimpleModule: https://doc.omnetpp.org/omnetpp/api/classomnetpp_1_1cSimpleModule.html
- htsim Broadcom: https://github.com/Broadcom/csg-htsim/blob/master/README.md