挑战
作者:Thomas J. Schriber(密歇根大学)、Daniel T. Brunner(Dan Brunner Associates)和 Jeffrey S. Smith(奥本大学)
在 2017 年冬季模拟大会上发表
本文向仿真从业人员和消费者介绍了离散事件仿真软件的工作原理。主题包括离散事件系统;实体、资源、控制元素和操作;仿真运行;实体状态;实体列表及其管理。本文介绍了这些通用思想在 AutoMod、SLX、ExtendSim 和 Simio 中的实现。本文最后列举了几个例子,说明建模人员了解其仿真软件如何工作的 "重要原因",包括对 AutoMod、SLX、ExtendSim、Simio、Arena、ProModel 和 GPSS/H 的讨论。
1 引言
离散事件仿真软件教学通常采用 "黑箱 "方法。对软件的外部特征进行了研究,但对软件所基于的基础却只是简单地触及或完全忽略(因为时间不够)。在实施基础时所作的选择以及这些选择对逐步执行模型的影响可能根本不会被研究。这样,当建模人员面临以下问题时,就可能无法深思熟虑:为复杂情况建模制定良好的方法;使用交互式工具来理解模型开发过程中出现的错误条件;以及使用交互式工具来验证复杂的系统逻辑是否已正确建模。因此,这里的目标是描述离散事件仿真的逻辑基础,并通过离散事件仿真软件的各种实现来说明这些材料。
在第 2、3 和 4 节中,我们将评述离散事件仿真的本质;实体、资源、控制元素和操作等基本仿真结构;以及模型的执行。第 5 节讨论了实体管理规则的四种具体实现方式。最后,第 6 节探讨了 "为什么重要 "的各个方面。在本文中,我们定义的术语在首次使用时以粗体标出。特定工具术语以大写字母标注,或在适当情况下以大写字母拼写。
本文是 1996 年冬季仿真会议论文集(Schriber 和 Brunner,1996 年)中类似论文的修订版。自 1996 年以来,每年都会在会议上提交 1996 年论文的一个版本,并不时进行更新,例如修改详细涵盖的语言集。例如,1996 年的论文涉及 SIMAN (Arena)、ProModel 和 GPSS/H 的实体表管理规则和 "为什么重要"。(Schriber 和 Brunner (1998) 对 1996 年的论文进行了大幅扩充,增加了数字、流程图和解释)。
解决方案
2 交易流世界观中的离散事件仿真
事务流世界观 "通常是离散事件仿真的基础。在这一世界观中,系统可视化为由离散的流量单元组成,这些流量单元在系统中的点与点之间移动(流动),同时相互竞争使用稀缺(容量受限)的资源。这些流量单位有时被称为 "事务",并由此产生了 "事务流 "一词。许多系统都符合上述描述。其中包括制造、材料处理、服务、医疗保健、通信和信息处理系统,以及其他一般排队系统。
从根本上说,离散事件模拟是一种模型状态仅在一组离散但可能随机的模拟时间点(称为事件时间)上发生变化的模拟。在同一时间点,往往需要对两个或多个交通单元进行操作。这种 "同步 "交通移动是通过在该时间点上连续操作交通单元来实现的。这就导致了逻辑上的复杂性,因为它提出了在给定模拟时间内处理两个或多个交通单元的具体实时顺序问题。
对于建模语言的设计者来说,建模者所面临的挑战会进一步升级。设计者必须将离散事件仿真的逻辑要求考虑在内。这就需要做出选择和权衡。因此,尽管离散事件仿真语言在广义上是相似的,但它们在细微但重要的细节上可能存在差异。我们试图在本文中扩展这一概念并提供具体实例。不过,在此之前,下文将提供更多的一般仿真概念。
3 实体、资源、控制元素和操作
这里所说的实体是指模型中的流量单位("事务")。实体引发事件并对事件做出响应。事件是改变模型(或系统)状态的瞬时事件。例如,在订单填写系统模型中,订单的到达(事件)可以通过将实体引入模型来模拟。有两种可能的实体,在此称为外部实体和内部实体。外部实体是指那些其创建和移动由建模者明确安排的实体(例如:订单到达订单处理点)。相比之下,内部实体是由模拟软件隐式创建和操作的。例如,在某些语言中,内部实体可能用于触发模拟机器故障和执行机器计划,而外部实体可能用于模拟使用机器加工零件。
资源一词指的是提供服务的系统元素(如钻头、自动导引车、工人或输入缓冲区中的空间)。实体通常会使用资源(例如,在制品实体需要输入缓冲区的空间,然后使用自动导引车将实体移动到输入缓冲区)。资源的容量通常是有限的,因此实体会争相使用资源,有时还必须等待使用,从而导致延迟(排队)。控制元件一词指的是根据系统状态支持其他类型延迟或逻辑替代方案的结构。控制元件的形式可以是开关、计数器、用户数据值以及建模工具内置的系统数据值。复杂的条件可通过算术和/或布尔表达式进行评估,以检查相关控制元件的状态。
操作是实体在系统中移动时执行的一个步骤(或一系列步骤)。适用于港口船只的操作可能包括:抵达港口;请求泊位;捕获泊位;请求拖船;捕获拖船;驶入泊位;释放拖船;装载货物;请求拖船;驶出泊位;释放泊位;驶入开阔水域;释放拖船;离开。
4 模型执行概述
模拟项目需要运行实验。实验通过在模型逻辑和/或输入数据中使用替代方案来区分。例如,在生产系统模型中可能会尝试使用不同的部件排序规则和/或改变各类机器(资源)的数量。同样,也可以改变港口装卸泊位的数量,以评估对系统性能的影响。每个实验由一个或多个重复(试验)组成。复制是一种模拟,它使用实验的模型逻辑和数据,但有自己独特的随机数集,因此会产生独特的统计结果,可作为一组此类复制(所有复制都是独立的)的一部分进行分析。复制包括初始化模型、运行模型直至满足运行结束条件并报告结果。运行 "阶段称为 "运行"。
在运行过程中,模拟时钟(内部管理、存储的数据值)会跟踪模拟时间的流逝。在运行过程中,时钟(自动)以离散的步长(通常大小不等)前进。在给定的模拟时间内采取了所有可能的行动后,时钟会前进到下一个最早事件发生的时间。然后在这个新的模拟时间执行相应的操作,等等。因此,运行的执行实质上是一个两阶段循环的形式:"在当前模拟时间执行所有可能的操作",然后 "前进时钟",这两个阶段不断重复,直到运行结束条件(通常是建模者指定的模拟时间、已处理实体的数量或其他条件)出现。这两个阶段在这里分别称为实体移动阶段(EMP)和时钟更新阶段(CUP)。
4.1 实体状态
随着仿真的执行,实体在模型中移动时会从一个状态迁移到另一个状态。通常使用的五种状态是
活动状态(Active)- 当前移动实体的状态即为活动状态。所谓 "当前移动的实体 "是指当前在模型中执行决策逻辑的实体。在挂钟时间的任何瞬间,只有一个实体在移动。这个实体会一直移动,直到遇到某种类型的延迟。然后,它会迁移到另一种状态。
就绪--在实体移动阶段,可能有多个实体准备移动,但实体只能逐个移动(处于活动状态)。就绪状态是实体在当前实体移动阶段等待进入活动状态的状态。
时间延迟--时间延迟状态是实体等待已知未来模拟时间的状态,这样它们就可以(重新)进入就绪状态。例如,"部分 "实体在等待对其执行的操作结束的未来模拟时间时,就处于延时状态。
条件延迟状态--条件延迟状态是实体延迟到某个特定条件出现时的状态,例如,"部件 "实体可能处于条件延迟状态,等待轮到它使用机器。当指定条件允许时,条件延迟实体会自动从条件延迟状态转入就绪状态。
休眠状态 - 有时,我们希望或有必要将实体置于这样一种状态,即模型条件的变化不会自动触发实体的逃逸。我们称这种状态为休眠状态。休眠状态实体依靠建模者提供的逻辑将其从休眠状态转入就绪状态。例如,工作票实体可能会进入休眠状态,直到操作员实体决定下一步拉哪个工作票,工作票实体才会随之转入就绪状态。在这种情况下,在操作员实体选择作业票之前,可能不知道选择作业票时的具体条件。
4.2 实体管理结构
在我们的通用模型中,我们通过指定一个活动实体和若干实体列表来管理实体。活动实体(活动状态实体)不停地移动,直到遇到一个(尝试的)步骤,使其迁移到另一个实体状态(转移到另一个列表)或从模型中删除。然后,就绪状态实体就会成为下一个活动状态实体。最终,在当前时间就没有准备状态实体了。这时,EMP 结束,CUP 开始。以下列表用于组织实体。(实体在这些列表中移动的动态过程将在与本教程相关的冬季模拟大会讲座中通过 PowerPoint 幻灯片序列进行说明)。
当前事件列表 - 处于就绪状态的实体保存在一个列表中,我们称之为当前事件列表(CEL)。实体从未来事件列表、延迟列表和用户管理列表(下文将分别介绍)迁移到 CEL。此外,从活动状态实体克隆的实体通常也从 CEL 开始存在。CEL 实体通常按先进先出(FIFO)顺序排列。一些软件工具提供了内置的实体 "优先级"(Priority)属性,用于按优先级对 CEL 上的实体排序(使用先进先出法解决并列问题)。
未来事件列表--处于时间延迟状态的实体属于一个单一列表,它们在基于时间的延迟开始时被插入该列表。该列表在这里称为未来事件列表(FEL),通常按实体移动时间的递增进行排序。移动时间是指实体计划再次尝试移动的模拟时间(例如,诊所中病人治疗时间的结束或制造系统中零件加工时间的结束)。在实体插入 FEL 时,实体的移动时间是通过将模拟时钟的值与已知的(采样的)时基延迟时间相加来计算的。EMP 结束后,CUP 会将时钟值设置(前进)为 FEL 排名最高(移动时间最小)的实体的移动时间。然后,该实体从 FEL 转移到 CEL,从延时状态转移到就绪状态,为下一次 EMP 的开始做好准备。上述声明假定 FEL 上没有其他实体的移动时间与时钟的更新值一致。在移动时间绑定的情况下,有些工具会在单个 CUP 期间将所有时间绑定的实体从 FEL 转移到 CEL,而其他工具则采取 "每个 CUP 只转移一个实体 "的方法。提供内部实体的语言通常使用 FEL 来支持这些实体的时序要求。(FEL 包括内部和外部实体)。
延迟列表--处于条件延迟状态的实体列表(可以有很多)称为延迟列表。这些实体正在等待(例如,等待轮到它们使用机器),直到它们的延迟得到解决,这样它们就能自动转入 CEL 的就绪状态。延迟列表通常由仿真软件自动创建,使用两种等待方式之一进行管理。如果延迟很容易与可能解决延迟的模型事件相关联,那么就可以使用相关等待来管理延迟列表。例如,假设一台机器的状态从繁忙变为空闲。对此,软件可以自动从相应的延迟列表中移除下一个等待实体,并将其置于 CEL 的就绪状态。相关等待是管理条件延迟的常用方法。如果延迟条件过于复杂,难以与可能解决它的事件联系起来,则可以使用轮询等待。使用轮询等待时,软件会定期检查实体是否能从一个或多个延迟列表转移到就绪状态。轮询等待适用的复杂延迟条件包括状态变化的布尔组合,例如泊位空闲和拖船闲置。
用户管理列表 处于休眠状态的实体列表(可以有很多)称为用户管理列表。建模者必须采取措施建立这样的列表,通常还必须提供将实体转入或转出列表所需的逻辑。除了系统中非常简单的单线、单服务器服务点外,底层软件无法知道为什么要把实体放到用户管理列表中,因此也就没有自动从列表中删除实体的依据。
5 四种工具的实施
本文选取了以下工具对实施细节进行评论:AutoMod(Phillips,1997 年);SLX(Henriksen,2000 年;Schulze,2008 年);ExtendSim(Imagine That Incorporated,2015 年;Krahl,2012 年);以及 Simio(Kelton 等人,2014 年)。本文的前一版本(Schriber 和 Brunner,1996 年)同样详细介绍了 SIMAN(Arena)(Kelton 等人,2014 年)、ProModel(ProModel Corporation,2015 年)和 GPSS/H(Henriksen 和 Crain,2000 年)。我们认为这些工具具有代表性,但显然并不详尽(参见 Swain 2015 年对众多可用工具的两年期调查)。
5.1 自动模式
在 AutoMod 中,当前事件列表被命名为当前事件列表(CEL)(见表 1)。克隆载荷、因时钟更新而离开 FEL 的载荷以及从订单列表中排序的载荷会被立即放到 CEL 上。插入规则是先按优先级排序(优先级是每个负载的内置属性),然后在优先级内先进先出。当 CEL 变空时,将检查条件延迟列表(见下文),并将负载从该列表转移到 CEL。这一过程一直持续到 CEL 为空且无法再传输负载为止,此时 EMP 结束并启动 CUP。

AutoMod 未来事件列表 (FEL) 与其他工具中的未来事件列表类似。负载通过执行 WAIT FOR 语句以延时状态到达 FEL。AutoMod 允许在 WAIT FOR 语句中指定时间单位(天、小时、分钟、秒)。如果多个负载在最早移动时间上并列,AutoMod CUP 会将它们从 FEL 中移除,并逐个插入 CEL 的适当位置。AutoMod 中还有一些内部实体,称为逻辑负载,它们的作用是在 FEL 上等待触发计划的换班。
延迟列表(Delay Lists,DL)是等待要求五个有限容量元素(资源、队列、块、计数器或流程流量限制;见表 1)中任何一个元素提供的容量的负载列表。模型中的每个有限容量元素都有一个与之相关的 DL。这五种情况下产生的等待是相关等待。每当释放容量时,该元素 DL 首部的一个负载就会被暂时放置在 CEL 上(但在 DL 上会留下一个占位符)。当 EMP 期间遇到该负载时,它会尝试申请所需的容量。如果失败(例如,它需要两个单位,但只有一个单位空闲),它将返回到原来位置的 DL(这是默认行为,建模者可以覆盖)。有关更多一般性讨论,请参见第 6.2 节)。评估结束后,如果仍有未使用的容量,相关 DL 上的下一个负载(如果有的话)将被立即置于 CEL 上。然后继续处理当前负载,接着评估下一个暂存负载。以此类推,暂定 EMP 期间的每个下一个负载(如有)。
对于上述五种情况之外的条件等待,AutoMod 还有一个 WAIT UNTIL 语句,可以实现轮询等待。WAIT UNTIL 条件可以使用布尔操作符进行复合。如果加载执行了 WAIT UNTIL,且条件为假,则该加载会被放入一个名为条件延迟列表(CDL)的单个全局 AutoMod 列表中。在 CEL 清空后,但在模拟时钟更新前,如果 CDL 上任何负载正在等待的至少一个相同通用类型(如队列)的元素发生了状态变化,CDL 上的所有负载都会被移至 CEL(实际上,CDL "成为 "了 CEL)(这种机制主要是 "轮询 "机制,轮询过程由至少一个相同通用类型元素的状态变化触发)。
如果 CEL 现在非空,则 EMP 恢复。如果 CEL 负载等待的条件尚未满足,AutoMod 会将该负载从 CEL 移回 CDL。在某些情况下,CDL 可能会在一次 EMP 期间多次清空,直到最终 CEL 被清空,而 CDL 上的任何负载都不会触发相关的状态变化。这时就会发生 CUP。由于 WAIT UNTIL 有可能导致重复列表迁移,AutoMod 的供应商鼓励使用订单列表或其他明确的控制机制来管理复杂的等待。
AutoMod 使用订单列表实现休眠状态,订单列表是由用户管理的负载列表。当负载将自己放入订单列表后(通过执行 "等待被订购 "操作),它只能被另一个执行 "订购 "操作的负载(或另一个活动模型元素,如车辆)移除。一个 "订购 "操作可以指定载荷的数量,或指定一个载荷必须满足的条件(如果该载荷要被订购),或两者兼而有之。订购成功的装载会被立即放置到 CEL 上(根据从订单列表中选择装载的方式一次放置一个,并按优先级在 CEL 上排序,优先级相同的以先进先出的方式解决)。与 CDL 等待相比,订单列表可以提高性能,因为除非有明确请求,否则永远不会扫描订单列表。AutoMod 订单列表提供了几种有趣的功能,包括:如果订单数量未得到满足,订购负载可以下一个后订单;订单列表上的负载可以被命令继续执行下一个操作而不是流程(此功能对于控制握手非常有用);为订单列表上的每个负载调用一个函数(通过使用 ORDER...SATISFYING 操作)。
AutoMod 有几种材料处理结构与装载移动集成在一起。对于车辆系统,还有其他三种类型的列表(不包括在表 1 中)。载荷就绪列表 (LRL) 上的载荷(每个车辆系统一个列表)正在等待车辆提取。已被车辆认领(但尚未提取)的货物在车辆的车辆认领清单(VCL)上。已提取的已认领装载位于车辆的 "车载列表"(VOL)中。然后,车辆成为活动 "负载",并在 AutoMod 的列表(FEL、CEL,可能还有 DL)中移动,而不是在负载本身中移动。
5.2 SLX
SLX 是一种分层语言,与大多数其他仿真语言相比,它的内置原语级别较低,便于用户(或开发人员)定义许多系统元素的行为。这种设计理念允许 SLX 用户(或开发人员)创建更高层次的建模工具,其构造具有精确定义的可修改行为。表 2 列出了低级 SLX 用户使用的通用术语的对等词。例如,SLX 使用控制变量来充当控制元件。控制 "修饰符可以附加到任何数据类型(整数、实数、字符串等)的变量上。控制变量可以是全局变量,也可以是在对象的类定义中声明的局部变量。(在其他工具中,类定义的变量是一种属性)。
SLX 有两种类型的对象:主动型和被动型。这两种对象的区别在于主动对象的类定义中是否包含动作(可执行语句)(即使没有动作,被动对象本身也很有用,可以作为用户定义的复杂数据结构使用)。表 3 显示了基于 SLX 的高级工具如何利用 SLX 的定义功能。

在 SLX 中,当前事件列表被命名为 "当前事件链(CEC)"。CEC 的成员被命名为有趣的 Puck。什么是 Puck?SLX 将 Active Object(带有相关本地数据)的概念与 Puck 区分开来,Puck 是 "移动实体",它执行操作,携带自己的实体调度数据,并从一个列表迁移到另一个列表。这种分离的效果是,一个对象可以 "拥有 "多个 Puck。单个对象拥有的所有 Puck 共享该对象的本地数据(属性)。例如,这种 "局部并行 "特性(与其他语言中的 "克隆 "或 "拆分 "操作所提供的 "全局并行 "特性相比)的一种应用是,在原始 Puck 等待某些条件时,使用第二个 Puck 来模拟停顿时间。(如果条件在等待时间结束前出现,则不会发生 "逡巡";反之,则会发生 "逡巡")。
激活一个新对象会创建一个 Puck,并启动该 Puck 开始行动。在许多情况下,不会为该对象创建额外的 Puck,激活对象及其 Puck 的组合相当于一个实体(被动对象没有动作,因此也不拥有 Puck)。新激活的 Puck、因时钟更新而离开 FEC 的 Puck 以及重新激活的 Puck(见下文)都会被放置在 CEC 上,并按优先级以先进先出的方式排序。EMP 结束时,CEC 为空。
SLX 未来事件链 (FEC) 与其他工具中的未来事件列表类似。通过执行 ADVANCE 语句,小球以延时状态到达 FEC。如果多个 Puck 在最早移动时间上并列,SLX CUP 将从 FEC 中移除这些 Puck,并将它们逐一插入 CEC 的适当位置。由于 SLX 内核功能不包括停机时间,甚至不包括重复的 Puck 生成(预定到达),因此 SLX FEC 上的所有活动都是按照 SLX 模型开发人员的指定进行的。更一般地说,如果用户使用的模型(或正在使用的模型生成器)包含由开发人员定义的高层基元,那么就有可能在幕后发生各种事情,而高层用户是看不到的。SLX 中的延迟列表(DL)是等待(通过 WAIT UNTIL)控制变量和模拟时钟值任意组合的状态变化的 Puck 列表。等待涉及两个或两个以上控制变量的复合条件的 Puck 会被列入多个 DL 中。开发人员定义的所有高层构造都可以使用这种机制。每个控制变量(可以是局部变量,在这种情况下,类中的每个对象都有一个控制变量)都有一 个与其关联的单独 DL。
DL 按插入顺序排序。每当相关控制变量的值发生变化时,DL 上的所有小球都会被移除,并逐个插入 CEC。被移除的 Puck 在等待复合条件时,也会暂时从它们所属的其他延迟列表中移除。当这些 Puck 在 EMP 期间出现在 CEC 上时,那些未能通过 WAIT UNTIL 的 Puck 会返回到延迟列表中,因为这些控制变量仍然会导致条件的虚假性。对于包含时钟引用的条件,必要时会将 Puck 插入 FEC,如果其他控制变量发生变化导致条件成真,则会提前从 FEC 中移除 Puck。这种基于控制变量的低级相关等待机制是 SLX 模拟各类简单或复合条件延迟状态的默认方法。
SLX 以一种独特的方式处理休眠状态。SLX 将此操作分为两部分,而不是在同一操作中将 Puck 从活动状态移至用户管理列表并暂停。首先,Puck 通常会加入一个集合。但加入一个集合并不会自动暂停 Puck。一个 Puck 可以属于任意多个 Set。加入 "集合 "只是让其他 Puck 可以访问成员 Puck。要进入休眠状态,Puck 需要执行 WAIT 语句。然后,它将无限期地暂停,不在任何特定列表中,直到另一个 Puck 识别出等待的 Puck 并为其执行 REACTIVATE 语句。通常情况下,另一个 Puck 会扫描一个集合来找到要重新激活的 Puck,但在我们的术语中,集合与用户管理的列表并不完全相同。休眠状态的 Puck 可能不是某个集合的成员(只要指向它的指针已存储在某个地方),也可能是一个或多个集合的成员。SLX 开发人员可以使用 Sets、WAIT 和 REACTIVATE 作为构建模块,轻松定义用户管理列表结构,既可以模仿其他语言的结构,也可以提供自己的独特功能。
5.3 扩展模拟
ExtendSim(原名 Extend)使用基于消息的架构进行离散事件仿真。各种类型的消息用于安排事件、推动项目(实体)通过模型、执行模型中的逻辑以及强制计算。消息的发送方和接收方都是模块(操作),包括执行模块(主控制器)。在 ExtendSim 中,块的执行是被调度的(例如,当块执行时,会触发块之间来回发送消息,从而使项在模型中沿着基于块的路径移动)。表 4 总结了前面通用讨论中引入的术语在 ExtendSim 中的对应关系。

在 ExtendSim 中,"块 "是基本的建模结构。每个区块都有一个图标、信息传递连接器、对话功能和行为定义代码。当模拟时间流逝时,驻留区块可以保留项目,而传递区块则不能(项目在零模拟时间内通过传递区块)。可以从 ExtendSim 的块库中选择预编程块来构建模型。建模者还可以修改库中模块的源代码(ExtendSim 基本版本中的所有模块都是开源的)。最后,建模人员还可以使用 ExtendSim 提供的开发工具,从零开始创建自定义块(用户编程块)。
ExtendSim 使用时间阵列(Time Array)来安排未来块的执行。对于一个给定的模型,时间阵列包含每个块的一个或多个元素。时间阵列元素记录了已安排执行该程序块的未来时间。在第 7 版(当前版本为第 8 版)中,"块 "语言增强了拥有多个 "时间阵列 "元素的可能性。当一个程序块有多个不同的事件时,例如在传送带建模中,该功能就非常有用。
通过在时间数组中记录任意大的时间值,可以暂时 "屏蔽 "当前未安排在未来执行的区块。可容纳多个项目的驻留区块在内部管理相应的事件时间,时间阵列中只保留该区块最早的事件时间。这是一个两阶段的事件列表,因为区块可以包含优化的链接列表,作为自己的未来事件列表。区块的执行会导致对未来区块执行的调度。例如,如果传递的信息导致一个项目进入单位容量的 "驻留区块",而 "驻留区块 "的设计目的是将该项目保留到取样的模拟时间结束,那么 "驻留区块 "的 "时间阵列 "条目就会相应地设置其值。
给定模型中的区块数量是恒定的,这意味着时间阵列的大小是固定且相对较小的。由于规模较小,时间数组的搜索是为了找到即将发生的事件时间,而不是按照排序顺序保存。这样,区块就可以直接更改其事件时间,因为不需要搜索事件列表。下一时间数组用于管理已通过时间数组安排执行的程序块的执行。下一时间数组在区块执行阶段(相当于 ExtendSim 的 EMP)之前填充,具体如下。在每个 CUP 中,搜索 "时间阵列 "以查找已安排执行块的最早时间。然后将相应区块(或多个区块,如果时间相同)的标识符放入下一时间数组。然后开始区块执行阶段(BEP),由执行人员向 "下一时间 "数组中资格最老的区块发送信息,开始执行。
当前事件数组用于管理在区块执行阶段暂时中止执行的区块的恢复执行。例如,假设区块发送了一条信息,接收区块立即向发送区块回复(返回控制权)(即使接收区块仍需在相关模拟时间内进行额外处理)。在这种情况下,接收数据块的标识符会被添加到当前事件数组中。当发送区块执行完毕后,执行程序会向当前事件数组中资质最高的区块发送信息,以继续执行。最终,"当前事件数组 "变为空。然后,执行者再次转向 "下一时间 "数组,向资质最高的区块发送信息,让其开始执行。
在区块执行阶段,区块可以安排自己在当前模拟时间(即正在进行的 BEP 期间)执行。在这种情况下,"当前事件阵列 "也会发挥作用,管理区块的执行。例如,如果一个容量受限的区块因其他区块的执行而变得不完整,则不完整的区块会将其标识符放入 "当前事件阵列"。执行程序稍后(但在同一模拟时间)将向区块发送信息,要求其开始执行。然后,程序块将尝试把等待进入程序块的项目(如果有的话)拉入自己的程序块(在 ExtendSim 中,项目可以通过模型拉入或推入)。当 "当前事件数组 "和 "下一时间数组 "都为空时,ExtendSim 的程序块执行阶段结束。然后进行下一个 CUP 和 BEP,如此反复,直到满足模拟结束条件为止。延迟列表由延迟在驻留区块中的项目组成,这些项目等待着被拉动或推入下一个(多个)区块。在模型条件允许的情况下,使用消息传递来完成拉动和推动。ExtendSim 可根据用户指定的先进先出(FIFO)、后进先出(LIFO)、优先级、属性、弃权、匹配和基于等式的替代方案,对这些项目进行相关等待管理。在 ExtendSim 中,等待复合条件的解决通常是通过适当组合块和利用 ExtendSim 基于消息的架构来实现的。我们将其视为相关等待的一种形式,因为它是由底层值的变化引发的对首先导致等待的条件的重新评估。
由于 ExtendSim 的消息传递架构,轮询等待通常是不必要的。当值发生变化时,会发送一条消息,并在此时对任何条件进行评估。等待基于时钟的事件可以通过使用调度事件的块来实现,例如,Shift;Lookup Table;Equation。这些程序块会在预定时间发送信息。不过,使用门块并选择 "在每个事件中检查需求 "选项,也可以实现轮询等待。
建模者可以使用用户编程块来创建和管理建模者自己设计的列表。自定义程序块的代码可以通过编写来实现建模者在这方面的目标,就像 ExtendSim 预编程块的代码是为了指定这些程序块的行为而编写的一样。ExtendSim 提供了一些函数,可用于块与其他块共享列表(数组),从而进一步支持模型中的自定义列表管理。
5.4 Simio
Simio 是一种面向对象的语言,其中所有的模型构造都是从同一个基本对象派生出来的 Simio 对象。大多数 Simio 用户将主要使用 Simio 标准库中的对象来构建模型。实体、资源、服务器、工作站、源、汇、节点和连接器是标准库中的常用对象。在此基础上,Simio 基于由令牌执行的单个流程步骤组成的流程。标准库对象的行为(由对象设计者)通过流程来实现。虽然大多数模型都是使用这些高级对象构建的,但用户(和开发人员)可以完全访问 Simio 流程,并使用 Simio 流程开发完整的模型和可重复使用的对象。此外,用户还可以使用附加流程和/或通过子类化现有对象来增强现有对象的行为。Simio 模型本身实际上就是可嵌入其他模型的对象。Simio 的对象结构有助于开发自定义对象库,从而简化不同应用领域的建模过程。
表 5 列出了 Simio 用户使用的通用术语的对等词。请注意,通过将 "资源对象 "属性设置为 True,任何 Simio 对象都可以充当资源。在本文中,标准库中的资源对象类似于资源。Simio 实体对象与代币之间的关系在概念上类似于 SLX 中活动对象与冰球之间的关系。具体来说,当对象发生交互时(例如,实体对象通过服务器对象排队并访问有限容量资源),与这些对象相关联的一个或多个令牌正在执行构成对象行为的流程(步骤序列)。让多个令牌代表同一对象执行不同流程的功能提供了建模的灵活性。
对象属性和状态是 Simio 的控制元素。属性和状态都是对象的属性。属性在运行开始时设置,运行过程中不会更改,而状态可在模型运行过程中随时设置和更改。属性通常在模型构建和实验过程中使用,而状态通常在模型运行过程中用于控制流程和/或跟踪和报告统计数据。与其他软件包相比,Simio 中的事件更为抽象。在 Simio 中,事件的特征包括执行时间(事件发生时的模拟时间)、过程调用引用(事件发生时执行)和对象引用(必要时为过程提供附加数据)。事件的引用对象可能是也可能不是用户可见的实体或相关令牌。对于与实体移动相关的事件,事件对象指的是与实体相关的令牌,事件过程执行的是流程步骤(称为 "令牌到达步骤 "事件)。模型运行过程中发生的大多数事件都属于这种类型。其他内部事件的例子包括运行结束事件、状态跨越阈值事件、实体碰撞事件等。Simio 还支持用户自定义事件,这些事件可以被触发、等待并用于触发流程。

在模型执行过程中,一旦 CUP 发生,Simio 进入 EMP,所有计划在当前仿真时间内发生的事件都会从未来事件堆(FEH)中移除,并放置在当前事件列表(CEL)中。堆数据结构的计算效率高,因此被用于未来事件。Simio 会(按顺序)执行 CEL 上的所有事件,然后再将模拟时间推进到下一个事件时间。CEL 上的事件按事件类型(正常、早期和晚期)排列优先级。事件过程可以创建其他事件(未来事件以及为当前模拟时间安排的事件)、更新系统状 态、触发统计记录以及终止模型运行。分配队列是等待资源的实体列表。当资源可用时,Simio 会执行一个标准的重新分配流程,根据该流程,分配队列中的 "第一个 "实体(由队列排序规则决定)将被放在 CEL 的前面,释放资源的实体将被逐级处理,直到达到延迟(时间延迟、条件延迟或用户指定的存储)为止。在此过程中,最后一个释放资源的实体将被处理,直到达到延迟为止,而最后一个被分配了资源容量的实体将首先开始处理(在同一模拟时间)。Simio 使用 "存储 "或 "站 "元素实现休眠状态。存储元件实现了一个逻辑队列,用户可将对象 "放置 "在队列中,以便日后移除。存储元件没有物理位置,一个给定的对象可以同时存在于多个存储空间中。另一方面,"站 "元素定义了一个容量受限的物理位置,实体对象可以存储在这里,以便日后移除和处理。
6 为何重要
在第 6.1-6.5 节中,我们描述了一些情况,揭示了 Arena(使用 SIMAN 语言和处理器)、ProModel、GPSS/H、AutoMod、SLX、ExtendSim 和 Simio 在实现细节上的一些实际差异。上述替代方法在本质上没有 "对 "或 "错 "之分。建模人员只需了解所使用的仿真软件中的替代方法,并与之配合以产生所需的结果。否则,就有可能对情况进行错误建模,而自己却没有意识到。在第 6.6 节中,我们将讨论如何利用软件内部知识来有效使用模型检出工具。最后,在第 6.7 节中,我们将指出内部知识有助于理解性能监控。
6.1 尝试立即重新捕获资源
假设灵活作业车间中的一个作业释放了一台机器(其他作业正在等待),然后,作为下一步,它决定重新抓取这台机器。是该作业立即重新夺取机器,还是等待中的作业(即使资质较差或资质相当)代替它夺取机器?这里值得关注的是资源释放后的事件顺序。至少有三种选择:(1) 与资源释放事件同时发生的是立即选择资源的下一个用户,而释放实体尚未进行下一步操作;(2) 推迟选择下一个资源用户,直到释放实体有可能成为竞争者;(3) 释放实体不理会其他竞争者,立即夺回闲置资源。Arena、ExtendSim 和 Simio(默认行为)实现了 (1)。ProModel 执行 (2)。GPSS/H 和 AutoMod 默认执行 (3)。在 SLX 中,使用控制变量作为资源状态,结果也是 (3)。(在某些工具中,建模者可以实现更高层次的资源结构或使用附加指令,这样模型就可以按照建模者在这方面的选择来运行)。
6.2 排在第一位的仍然是延迟的
假设有两个条件延迟的实体在延迟列表中等待,因为没有特定资源的单位闲置。假设第一个实体需要两个单位的资源,而第二个实体只需要一个单位。现在假设有一个单位的资源闲置。第一个清单实体的需求还不能得到满足,但第二个实体的需求可以得到满足。会发生什么情况呢?至少有三种可能的选择:(1) 两个实体都不申请闲置资源单位;(2) 第一个实体申请一个闲置资源单位,并等待第二个单位;(3) 第二个实体申请闲置资源单位,并迁移到就绪状态。如第 6.1 节所述,上述每种选择都会在本文所考虑的工具中发挥作用。Arena (SEIZE) 和 ProModel (GET 或 USE) 默认分别执行 (1) 和 (2)。AutoMod (GET 或 USE)、GPSS/H (ENTER 或 TEST) 和 SLX (WAIT UNTIL on a Control Variable) 默认执行 (3)。ExtendSim 默认也执行(3)。但是,ExtendSim 允许建模者选择在本地为建模者指定的资源执行 (1)。建模者可以通过检查每个此类资源的 "只将资源池分配给排名最高的项 "选项来实现这一点。Simio 允许建模者通过使用 "对象数量 "属性和 "抓取 "步骤中的 "重复抓取资源 "选项来选择 (1) 或 (2)。
6.3 暂时放弃控制权
假设活动实体希望将控制权交给一个或多个就绪状态实体,但又需要在模拟时钟前进之前再次 成为活动实体。例如,如果活动实体打开了一个开关,允许一组其他实体移动到模型中的某一点,然后需要在其他实体完成向前移动后重新关闭开关,这种情况就可能出现。举例来说,一组口味相同的冰淇淋纸箱要从堆积点转移到传送带上,然后进行每箱一种口味的包装操作。
在 Arena 中,可以通过延迟(DELAY)近似实现这种效果,它可以在任意短的模拟时间内使活动实体进入延时状态,但模拟时间不能为零。在 ProModel 中,"WAIT 0"(等待 0)可用于将活动实体放回 FEC。稍后(挂钟时间,但模拟时间相同)该实体将由 CUP 返回活动状态。在 GPSS/H 中,活动 Xact(事务)可以执行一个 YIELD 块,立即从活动状态迁移到就绪状态(在其优先级中位于最后),并强制重新开始 CEC 扫描。然后,在屈服的 Xact 于同一模拟时间再次进入活动状态之前,排名较高的 CEC Xact 将有机会进入活动状态。SLX (YIELD)、AutoMod (Wait For 0) 和 Simio (使用 Math.Epsilon 的延迟步骤) 都提供了这样的解决方案:在 CEC 上,处于活动状态的 Puck (SLX)、Load (AutoMod) 或 Token (Simio) 被移到其优先级的后面,在那里等待在时钟前进之前再次成为活动实体。在 ExtendSim 中,"屈服并最终恢复 "是架构的一部分。当一个项目移入或移出一个区块时,会通过相应的区块连接器发出一条信息。该信息会传播到其他连接的区块,从而改变系统状态或将项目从一个区块移动到另一个区块。发端块最终收到回复后,会继续处理原始项。
6.4 涉及时钟的条件
每种语言都为 FEL 等待提供了延时功能。当实体需要等待某个已知时钟值到达时,这种功能非常有效。但是,如果一个实体需要等待一个涉及时钟的复合条件,例如 "等待直到指定的输入缓冲区为空或正好是下午 5:00",该怎么办呢?典型的解决方法是克隆一个虚拟("影子")实体来执行基于时间的等待。这种虚拟实体的管理可能很麻烦,特别是对于非常复杂的规则。ProModel 不使用轮询等待,因此虚拟实体是最好的方法。(否则,在复合条件的其他组件发生值变化之前,不会检查该条件)。ExtendSim 也不使用轮询等待,因此类似的情况也适用于 ExtendSim,任何可以调度事件的 Block 都可以使用。在 Simio 中,实体对象可以有多个关联标记,每个标记都在等待复合条件的不同组件。这与使用虚拟实体的概念类似,但不需要额外的实体。
当存在轮询等待机制时,如果单个实体试图等待涉及时钟的复合条件,就会出现一个有趣的问题。这是因为下一次轮询时间可能与目标时钟时间不匹配。Arena 和 AutoMod 通过其 EMP 结束轮询机制检测复合条件的真实性。GPSS/H 也能通过其轮询等待版本(refusalmode TEST)检测到真相。但是,如果没有在 FEL 上精确等待到下午 5:00 的方法(即上文为 ProModel、ExtendSim 和 Simio 推荐的方法),所有这三种工具都有可能在时钟值大于下午 5:00 时出现第一个发现条件为真的 EMP。如果下午 5:00 的准确性很重要,这可能会造成问题。
SLX 将时钟识别为相关的等待直至目标。使用未来时钟值的 WAIT UNTIL(等待直到)会导致 Puck 被安排到 FEL 上,以便在引用的精确时间强制执行 EMP。这就解决了时间大于期望时间的问题。请注意,该 Puck 可能还在一个或多个延迟列表上等待。
6.5 混合模式等待
假设许多实体都在等待捕获某个资源,而用户创建的控制器实体正在等待复合条件 "轮班状态为'下班',等待人数小于 6,且资源当前未被使用",以便采取某些行动(例如,在允许用户定义实体关闭资源的语言中,关闭资源;或显示状态消息)。我们如何保证控制器实体能够在适当的模拟时间(在闲置资源被夺回之前)"插到 "等待实体的前面?
一种处理方法是在提供实体优先权功能的语言中使用实体优先权。不过,如下所述,即使控制器具有相对较高的优先级,这种方法也可能行不通。关键问题在于实现等待的方法。如果等待捕获资源的实体采用 "相关 "方式,而等待复合条件的控制器实体采用 "轮询 "方式(这就是我们所说的 "混合模式等待"),情况就会变得复杂。每次资源空闲时,都会立即从 Arena 中的延迟列表和 AutoMod 中的 CEL 中选择一个新实体,在这两种情况下,都会先于轮询等待条件的 EMP 结束检查(从而忽略控制器的实体优先级)。如果需要,有一些方法可以解决这个问题,例如使用不同类型的操作来强制希望使用资源的实体进行轮询等待。
在 GPSS/H 中,在拒绝模式 TEST Block 使用高优先级控制器 Xact 时,控制器会在 CEC 前端等待。设施 RELEASE 会触发扫描重启,而控制器则完成其工作。ProModel 中不存在轮询等待,但在涉及变量的复合条件下可能存在相关等待。必须为布尔条件的每个元素定义和操作变量,而且为了确保平等竞争,等待获取资源的实体可能还必须使用 WAIT UNTIL 而不是 GET 或 USE。使用 ProModel 的另一种可能性是让释放资源的实体立即进行一些状态检查(实际上成为控制器的代理)。这是因为 ProModel 使用了延迟选择方法(参见第 6.2 节)。
在 SLX 的相关等待中,等待复合条件的 Puck 将被登记在那些(且仅那些)导致条件虚假的控制变量的延迟列表中。SLX 架构(在该架构中,只有全局或局部控制变量和时钟可以在任何类型的最底层条件等待中被引用)确保了在被监控的状态变化背后已经存在变量。建模者将它们定义为控制变量。
与 ProModel 和 SLX 一样,ExtendSim 将使用相关等待来检测并立即响应复合条件的变化。由于在重新分配资源之前,资源的状态变化会立即以消息的形式进行广播,因此该消息可用于控制项目选择的顺序和逻辑。除非同时存在项目的下游空间和资源可用性,否则不会分配资源,因此阻塞队列的输出路径将阻止资源分配。
在 Simio 中,资源对象可以使用附加进程实现自己的逻辑,因此无需使用 "控制器实体 "来实现控制机制。在这种情况下,可以使用 "下班 "和 "释放 "附加流程来检查涉及轮班状态、队列大小和资源状态的复杂条件。请注意,这等同于相关等待,因为只有在资源脱班和/或单位容量释放时才会检查该条件。
6.6 互动模型验证
我们现在简要谈谈为什么对 "仿真软件如何工作 "的详细了解有助于对仿真模型行为进行交互式探查。一般来说,仿真模型可以交互式或批处理模式运行。交互式运行适用于在模型构建过程中检查(验证)模型逻辑,以及在执行错误时排除模型故障。批处理模式则用于生产运行。
交互式运行可以在仿真执行时将其放大。建模人员可以逐步跟踪活动实体,显示当前和未来事件列表、延迟和用户管理列表以及模型的其他方面。对于了解基本概念的建模人员来说,这些活动能为模型行为提供有价值的见解。如果没有这些知识,建模人员可能无法充分利用软件的交互式工具,更有甚者,甚至可能不会使用这些工具。
6.7 性能问题
仿真实验会消耗大量的计算机时间。在其他条件相同的情况下(包括模型创建者的技能),计算机时间需求取决于用于创建模型的软件的设计和实施。性能是一个非常重要的问题,它促使一些仿真软件(如 ExtendSim、SLX 和 Simio)提供性能剖析器,例如,它可以生成直方图,显示模型执行过程中 CPU 时间的消耗情况。
鸣谢
本文中的许多信息由供应商提供。作者感谢 Deb Sadowski、Vivek Bapat、Charles Harrell (ProModel)、Kenneth Farnsworth 和 Tyler Phillips (AutoMod)、Robert C. Crain 和 James O. Henriksen (GPSS/H 和 SLX)、David Krahl (ExtendSim) 以及 David T. Sturrock 和 C. Dennis Pegden(两人最初都在 Arena/SIMAN 工作,现在在 Simio 工作)的大力支持。

