Time and Scheduling
时间是如何流逝的?事件是如何被安排(schedule)和处理(process)的?
时间是什么?
时间本身并不容易把握。维基百科是这样描述的:
Time is the indefinite continued progress of existence and events that occur in apparently irreversible succession from the past through the present to the future. Time is a component quantity of various measurements used to sequence events, to compare the duration of events or the intervals between them, and to quantify rates of change of quantities in material reality or in the conscious experience. Time is often referred to as the fourth dimension, along with the three spatial dimensions.
时间有何问题?
通常,事件(在现实世界中)看起来“同时”发生,而事实上它们发生的时间略有不同。这里有一个明显的例子:爱丽丝和鲍勃在同一天过生日。如果你的时间刻度是天,那么两个生日活动都会同时发生。如果你把时钟的分辨率提高到分钟,你可能会意识到爱丽丝实际上出生在凌晨0:42,鲍勃出生在11:14,这两件事的时间相差很大。
在计算机上进行模拟也会遇到类似的问题。整数(和浮点数)是离散的数字,它们之间有很多空位。因此,如果映射到整数标度,则在现实世界中相继发生的事件(例如,$t_1=0.1$和$t_2=0.2$)可能会在“同一”时间发生(例如,在$t=0$)。
另一方面,SimPy(像大多数模拟框架一样)是一个单线程的、确定性的库。它按顺序处理事件——一个接一个。如果同时安排两个事件,则首先安排的事件也将是最先处理的事件(FIFO)。
这非常重要。建模/模拟器中的进程可能“并行”运行,但当模拟在CPU上运行时,所有事件都会按顺序进行确定处理。多次运行模拟,总是会得到相同的结果(如果不使用random)。
因此请记住:
- 在现实世界中,通常没有同时发生的事情
- 时间尺度的离散化可以使事件看起来同时发生
- SimPy处理一个接一个的事件,即使它们有相同的时间
SimPy中的Events和时间
Event可能处在的状态:
- untriggered:事件队列未侦测到它
- triggered:在时间t调度并插入到事件队列中
- processed:已从事件队列中删除
SimPy的事件队列被实现为堆队列:“堆是二叉树,每个父节点的值都小于或等于其任何子节点的值。”因此,如果我们将事件作为元组(t,event)(t是计划时间)插入其中,那么队列中的第一个元素将始终是t最小的元素,这也是即将被处理的元素。
但是,如果同时安排两个事件,则存储(t,event)元组将不起作用,因为事件不可比较。为了解决这个问题,我们还存储了一个严格递增的事件ID:(t,eid,event)。这样,如果两个事件被安排在同一时间,那么首先安排的一个事件(eid较小)将始终被首先处理。