目录

[TOC]
image-20220926224435487|575

基类

首先来看 FIFO 基类定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
namespace fifo {

/**
* Abstract base class for single-server queues. Subclasses should
* define startService() and endService(), and may override other
* virtual functions.
*/
class AbstractFifo : public cSimpleModule
{
protected:
cMessage *msgServiced = nullptr;
cMessage *endServiceMsg = nullptr;
cQueue queue;
simsignal_t qlenSignal; // queue length
simsignal_t busySignal; // signal status
simsignal_t queueingTimeSignal; // queue top begin processing time

public:
virtual ~AbstractFifo();

protected:
virtual void initialize() override;
virtual void handleMessage(cMessage *msg) override;

// hook functions to (re)define behavior
virtual void arrival(cMessage *msg) {}
virtual simtime_t startService(cMessage *msg) = 0;
virtual void endService(cMessage *msg) = 0;
};

}; //namespace

在成员变量中可以看到 cQueue queue; 定义,是整个 FIFO 的核心,围绕着这个数据结构展开 signle-server queues 的框架实现。

基本上所有的处理都在 AbstractFifo 实现了,只有 startService 和 endService 需要在派生类中进行重载。

initialize() 函数

初始化几个统计信号,分别负责 queue 不同状态下的记录统计;初始化 endServiceMsg 消息;发送当前 queue 的长度以及设置 busySignal 。

1
2
3
4
5
6
7
8
9
10
11
void AbstractFifo::initialize()
{
endServiceMsg = new cMessage("end-service");
queue.setName("queue");

qlenSignal = registerSignal("qlen");
busySignal = registerSignal("busy");
queueingTimeSignal = registerSignal("queueingTime");
emit(qlenSignal, queue.getLength());
emit(busySignal, false);
}

handleMessage() 函数

这里只看 AbstractFifo ,其余 module 都很简单不再细看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void AbstractFifo::handleMessage(cMessage *msg)
{
if (msg == endServiceMsg) { // 执行1
EV << "endServiceMsg" << endl;
endService(msgServiced);
if (queue.isEmpty()) {
msgServiced = nullptr;
emit(busySignal, false);
}
else {
msgServiced = (cMessage *)queue.pop();
emit(qlenSignal, queue.getLength());
emit(queueingTimeSignal, simTime() - msgServiced->getTimestamp());
simtime_t serviceTime = startService(msgServiced);
scheduleAt(simTime()+serviceTime, endServiceMsg);
}
}
else if (!msgServiced) { // 执行2
EV << "!msgServiced" << endl;
arrival(msg);
msgServiced = msg;
emit(queueingTimeSignal, SIMTIME_ZERO);
simtime_t serviceTime = startService(msgServiced);
scheduleAt(simTime()+serviceTime, endServiceMsg);
emit(busySignal, true);
}
else { // 执行3
EV << "arrival" << endl;
arrival(msg);
queue.insert(msg);
msg->setTimestamp();
emit(qlenSignal, queue.getLength());
}
}

这里的三个判断初看不太好理解,额外加了些打印,结合 queue 的操作一起来看。实际的执行顺序如下
image-20220928213618022|575

当 source 模块在低速发包的时候,可以看到基本不会调用到 queue 的 insertpop 操作,msgServiced 的 reset 值总为 nullptr,之后执行 2 startService 操作开始加入耗时。如下可以看到系统设置的 source 发包和 fifo 处理的时长,大约每 0.2s 发包一次并且每 0.1s 执行一次 startService,因此可以看到系统运行时基本不会出现入队和出队。

1
2
3
4
5
[Fifo1]
description = "low job arrival rate"
network = SingleQueue
**.source.interarrivalTime = exponential(0.2s)
**.fifo.serviceTime = 0.1s

但当把 interarrivalTime 调整为 0.1s 后,就可以发现有 fifo 的 service 处理不完的情况。

1
2
3
4
5
[Fifo2]
description = "high job arrival rate"
network = SingleQueue
**.source.interarrivalTime = exponential(0.1s)
**.fifo.serviceTime = 0.1s

Tandem Queue

Tandem Queue 的组成结构如下

image-20220929223855832

除此之外几乎没有啥明细不同了,fifo 也是依次串联。

1
2
3
4
5
6
7
8
9
10
11
[TandemQueues]
network = TandemQueues
**.interarrivalTime = exponential(2s)
**.serviceTime = exponential(2s)

[TandemQueueExperiment] # this config is used by test/scave/, do not modify!
network = TandemQueues
**.interarrivalTime = exponential(2s)
**.serviceTime = truncnormal(${serviceTimeMean=1.5s, 2.5s},1s)
repeat = 2
sim-time-limit = 200s