Csim 通用型仿真工具

Csim 通用型仿真工具

导师不久前给笔者安排了一个很不同寻常的任务,在一个拥有大量磁盘的主机中,仿真其I/O使用情况。

因此笔者开始学习Csim。将学习历程记录在此。

一、概述

什么是CSIM?

CSIM是通用目的的系统仿真环境:

  • 离散事件仿真——包括时间和序列
  • 层次化框图仿真
  • 模型函数均用C语言表示。
  • 模型集成,包括并发(concurrency)和时间协调。

它拥有大量模型库,可以快速建模以及测试。支持多种抽象层次结构。

1.1 模型的描述

有三个维度的信息可以描述功能,结构和时间。

结构:实例化模型,展示细节。

功能:展示模型的功能,通过公式或者代码表述。

时间:时延,速率,关系,序列…

1.2 一般的仿真流程

我们首先要配置模型需要使用的资源,包括两个部分:设备(单服务器,多服务器),存储单元。

它们通常倍声明为全局变量,在使用之前声明。

之后我们就要设计仿真的进程了:

  • 第一个进程一般是sim,如果它没有完成,我们就需要提供main进程。sim一般被两个参数调用argcargv
  • 一个CSIM20进程是一个执行create的C进程。进程之间没有预定义的关系,一个进程随时可以打断另一个进程,除了sim只能被打断一次
  • CSIM20进程不是UNIX进程,它更像是一个线程或者轻量级进程,并且不能返回值。
  • 一个CSIM20进程可以通过事件、邮箱、存储块来与其它进程同步。

1.3 安装和使用CSIM

笔者下载的是Mesquite公司的CSIM20。一般下载完成后就一个文件夹csim20_linux,里面有DocumentationLinux,里面有32位和64位的分别对应gcc,g++的库。比如我们进入/csim20_linux/Linux/64-bit/gcc.

csim.out  ex2.c  gensys.c
csim.gcc  ex1.c     ex3.c  lib

然后在lib下面有

csim.gcc.a  csim.h

我们一般先运行ex1.c进行测试:

./csim.gcc ex1.c

如果你没有遇到错误说明一切正常。但如果像笔者一样遇到错误:

/usr/bin/ld: lib/csim.gcc.a(event.o): relocation R_X86_64_32 against `.rodata' can not be used when making a PIE object; recompile with -fPIE

那么需要把编译命令需要改为:

./csim.gcc ex1.c -no-pie
./a.out

即可编译成功,-no-pie这里的意思是不生成fPIE可执行文件,详见https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html。

             CSIM Simulation Report (Version 20.0 for Linux x86-64)

                                  M/M/1 Queue

                           Wed Feb  3 20:43:52 2021


                     Ending simulation time:     10041.661
                     Elapsed simulation time:    10041.661
                     CPU time used (seconds):        0.003


FACILITY SUMMARY 

facility     service    service          through-    queue     response   compl
name           disc      time    util.     put       length      time     count
--------------------------------------------------------------------------------
facility     fcfs       0.99206  0.494    0.49793    0.99059    1.98943     5000



TABLE 1:  resp tms

      minimum         0.000145          mean                    1.989433
      maximum        14.273079          variance                3.813342
      range          14.272934          standard deviation      1.952778
      observations        5000          coefficient of var      0.981575

QTABLE 1:  num in sys

      initial       0      minimum       0      mean                    0.990590
      final         0      maximum      13      variance                1.937727
      entries    5000      range        13      standard deviation      1.392022
      exits      5000                           coeff of variation      1.405246

                                             cumulative
        number    total time    proportion   proportion

             0    5081.38161     0.506030     0.506030   ********************
             1    2426.95194     0.241688     0.747718   **********          
             2    1238.22169     0.123308     0.871027   *****               
             3     667.95025     0.066518     0.937545   ***                 
             4     350.00001     0.034855     0.972399   *                   
             5     152.62571     0.015199     0.987599   *                   
             6      69.33696     0.006905     0.994504   .
             7      25.09331     0.002499     0.997003   .
             8       9.84005     0.000980     0.997982   .
             9      10.69388     0.001065     0.999047   .
 >=         10       9.56521     0.000953     1.000000   .




			M/M/1 Theoretical Results


		Inter-arrival time =      2.000
		Service time       =      1.000
		Utilization        =      0.500
		Throughput rate    =      0.500
		Mn nbr at queue    =      1.000
		Mn queue length    =      0.500
		Response time      =      2.000
		Time in queue      =      1.000
CSIM MODEL STATISTICS

CPU time used (sec):         0.003 
Events processed:            17485 
Memory allocated:            23026 bytes 
Calls to malloc:                43 
Processes 
     Started:                 5001 
     Saved:                   9105 
     Terminated:              5000 
     High water mark:           14 
Stacks 
     Allocated:               5001 
     High water mark:          318 words 
     Average:                   22 words 
     Maximum:                   23 words 
     Current:                   19 words

这是一个仿真报告,里面的参数我们之后详细讲解。

1.4 测试程序的方法

有三种方式可以完成调试:

  • 我们可以用printf打印我们感兴趣的变量。
  • 事件追踪(event trace)可以对各个进程的行为进行描述。通过在CLI添加-T指令,或者在程序内部添加trace-on

traceoff指令来开启或关闭。

  • 通过dump_status打印资源和进程的状态。

CSIM20库的例子将输出打印到三个文件:1. set_output_file: 报告和状态信息,2. set_trace_file: 事件追踪,3. set_error_file: 错误信息。


C++编程案例

  1. 单服务队列模型

最基本的仿真模型是单个服务器,并与到达的客户排队。 在某些限制下,这是众所周知的M / M / 1队列。 在此模型的CSIM 20版本中,存在由单个服务器和单个队列组成的功能。 此外,还有客户来源。 当客户到达时,如果服务器闲置(未使用),它将抓住(使用)服务器;如果服务器已经繁忙(正在使用),它将加入等待客户的队列。 当一个客户离开服务器时,队列中的下一个客户开始使用该服务器。
此模型中的关键参数是:

  • 客户到达之间的时间间隔
  • 服务器使用间隔

该模型输出的结果是:

  • 客户的平均应答时间;
  • 客户吞吐量(单位时间服务的客户数);
  • 服务器使用率(服务器忙的时间的占比);

  • 平均队列的长度。

//singleCS.cpp
#include "lib/cpp.h"
facility *f;
void customer()
{
	create("custumer");
	f->use(exponential(0.5));
}

extern "C" void sim()
{
	create("sim");
	f = new facility("f");
	while(simtime() < 5000.0)
	{
		hold(exponential(1.0));
		customer();
	}
	report();
}

./csim.gpp singleCS.cpp -no-pie -o singleCS
./singleCS.out

输出结果如下

           C++/CSIM Simulation Report (Version 20.0 for Linux x86-64)

                           Wed Feb  3 21:00:35 2021


                     Ending simulation time:      5000.234
                     Elapsed simulation time:     5000.234
                     CPU time used (seconds):        0.004


FACILITY SUMMARY 

facility     service    service          through-    queue     response   compl
name           disc      time    util.     put       length      time     count
--------------------------------------------------------------------------------
f            fcfs       0.49398  0.488    0.98815    0.99145    1.00334     4941

我们可以在报告中看到,设备名称,服务描述,服务时间,使用率,吞吐量,队列长度,响应时间,完成计数等表项。

在这个例子中,我们有两个主要的进程:

  • 主要的sim进程,初始化模型并且保证客户来自于不同的间隔;
  • customer进程,也就是f。

create的作用在于,建立将语句作为独立的,准备运行的过程执行的过程,以及将控制权返回给调用过程。

  • 一个设备由”new facility()” 创建;
  • CSIM变量clock包含当前的仿真时间。时间是double精度的。
  • “ hold”语句将执行给定的时间。hold(exponential(1.0)) 报表对客户到达之间的时间间隔进行建模。
  • 在许多仿真模型中,指定具有概率分布的时间间隔序列是适当的。 在M / M / 1队列中,从负指数分布“采样”到达间隔和服务间隔。 在CSIM中,exponential()函数提供了此类示例。
  • 通过f->use(exponential(0.0))语句对设施的使用进行建模(在这种情况下,所使用的设备为f)
  • 此外我们可以列一个时间表,看看每个进程在每个时间点到底在做什么:

image-20210203231334076

CSIM的目标

  • 进程-用于对工作负载,客户端和服务器或系统的任何其他活动组件的元素进行建模

  • 设备-用于对由流程占用(使用)的资源进行建模

  • 存储-用于对部分分配给流程的资源进行建模
  • 缓冲区-用于对有限容量的缓冲区进行建模
  • 事件-用于同步和控制进程之间的交互
  • 邮箱-用于在进程之间交换信息
  • 表,Qtables,计量器和框-用于收集显式统计信息(注意: 设施和存储块的使用情况会自动收集)
  • 流程类-用于隔离设施使用情况统计信息
  • 随机数流-用于根据指定的概率分布生成多个样本流

CSIM流程控制

理解CSIM流程控制非常的重要,当程序调用create()指令,下列事件会发生:

  • 一个对应于程序的进程控制块被创建并且被放置于“下一个事件表”
  • 控制权随即被返回给进程
  • 只有父进程声明hold,新进程才会执行。

关于设备(Facility)

1 设施

Facility可以被定义为:

  • 单服务器设备(同一时间只能服务一个进程)
  • 多服务器设备(同一时间能服务n个进程)
  • 一个但服务器设备数组

(a)声明一个单服务器

facility *single_server;
single_server = new facility("nf");
single_server->use(service_time);//记录服务时间
single_server->reserve();
hold(service_time);
single_server->release();

进程按其进程优先级的顺序排列在等待进程的队列中,其中头部优先级最高。优先级相同时采用FIFO。

(b)再比如定义多服务器:

const long NUM_SERVER = 10
facility_ms* multi_server = new facility_ms("ms",NUM_SERVER);
multi_server->use(service_time);

(c)单服务器数组

const long NUM_SERVER = 10
facility_set* facs = new facility_set("facs",NUM_SERVER);
(*facs)[i]->use(service_time);//指定使用下标i的服务器

(d)给定的时间预留

const double TIME_OUT = 5.0;
st = single_server->time_reserve(TIME_OUT);
if(st!=TIME_OUT)
{//如果设备被预留,就仿真客户service-time时间
	hold(service_time);
	single_server->release();
}
else
{
	...
}

(e)使用同步的facility

const double PHASE = 0.5 //时钟相位
const double PERIOD = 1.0 //时钟的周期
facility *bus = new facility("bus");
bus->synchronize(PHASE,PERIIOD);
bus->reserve();
bus->release();

(f)定义设施的抢占式恢复服务规则

facility* cpu;
cpu = new facility("cpu");
cpu->set_servicefunc(pre_res);
priprity = 100;//设置高优先级以抢占低优先级
cpu->use(service_time);

重要的是要注意,当在设施中使用调度顺序而不是FIFO时,则“ use()”方法是使用设施的唯一方法。 这意味着进程无法保留此类设施,然后执行其他操作而不使用该设施。

2 存储

存储包括一个计数器,用于统计可用资源,以及一个队列,存储需要资源的请求。

const long STORE_AMT = 100;	 
const int NUM_STORES = 5;
storage *mem;
mem = new storage("mem", STORE_AMT);
//amt is to be allocated
mem->alloc(amt);
//unallocated
mem->alloc(amt);

storage_set* mems;
mems = new storage_set("mems",STORE_AMT,NUM_STORES);
(*mems)[0].alloc(amt);

//在指定时间分配资源
st = mem->timed_alloc(amt,1.0);
if(st != TIMED_OUT)
{
	//normal process
}

3 缓冲器

缓冲器由一个计数器(指示缓冲区中的请求数量)和两个队列组成,一个队列用于等待从缓冲区中获取令牌的进程,另一个队列用于等待空间放入(或返回)到缓冲区。

const long BUFFER_AMT = 100;
buffer* buf = new buffer("buffer", BUFFER_AMT);
buff->get(amt);
buff->put(amt);

4 事件

事件被用于同步和控制不同的进程。事件(event)有两种状态:发生(OCC)和不发生(NOT_OCC),一个进程可以等待事件发生。

event *ev;
ev = new event("ev");
ev->wait();
ev->queue();
ev->set();
//to monitor an event
ev->monitor();

event_set *evs;

i = ev_arr->queue_any();

5 信箱

信箱运行CSIM进程之间同步交换数据。信箱包括两个队列,分别放置未接收的信息和等待的进程。在任意时间至少有一个队列是空的。

消息可以是单个整数,也可以是指向其他数据对象的指针。 如果某个进程发送了一个指针,则该进程负责维护被引用数据的完整性,直到接收和处理该数据为止。

//声明创建一个信箱
mailbox * mb;
long msg_r, msg_s;
mb = new mailbox("mb");
mb->receive(&msg_r);//从信箱mb接收信息,这里是右值引用?
mb->send(mb,msg_s);

mb->monitor();
//声明信箱组
const long NUM_MBOXES = 25;
mailbox_set *mbox_arr = new mailbox_set("mbox set", NUM_MBOXES);
i = mbox_arr->receieve_any(&msg_r);

(*mbox_arr)[3].send(msg);
st = mbox_arr->time_receieve_any(&msg_r, 1.0);
if(st != TIME_OUT)

//发送消息,并且在对方接收之前一直等待
mb->synchronize_send(msg);

6 Tables & QTables

Table,可以轻松的对不同数据进行统计。并且可以打印直方图,以及指定置信区间。

Qtable,记录状态变化(比如队列中的进程数),并且可以打印直方图,以及指定置信区间。

可以将表定义为永久性或非永久性。 永久表不受重置统计信息或重新运行模型的请求的影响,因此可以用于跨模型的多次运行收集数据。

table *tbl;
tbl = new table("tbl");
tbl->add_histogram(10,0.0,20.0);//0-20,步长为2
t = clock;//获取当前时间
single_server->reserve();
x = clock - t;
tbl->calculate(x);

qtable *qtbl;
qtbl = new qtable("qtbl");
qtbl->add_histogram(20,0,20);
qtbl->note_entry();
single_server->reserve();
qtbl->note_exit();
hold(exponential(2.5));
//添加置信区间
table_confidence(tbl);
qtable_confidence(qtbl);
#define NTERM 5500
#define ND 92
#define P_EXIT (1.0/11.0)
#define S_CPU 0.0005
#define S_DISK 0.030
#define Z 30.0

#define RUNTIME 200.0

FACILITY disk[ND],cpu;
TABLE rt;
EVENT done;

void sim()
{
create("sim");
init();
for(i=0; i<NTERM; i++){
trans(i);
}
wait(done);
report();
md1stat();
}

void init()
{
max_processes(NTERM+1);
facility_set(disk,"d",ND);
cpu = facility("cpu");
rt = TABLE("resp_tm");
done = EVENT("done");
}

void trans(int nt)
{
float t,x;
int i;

create("trans");
while(clock<RUNTIME){
hold(expnt1(Z));
t = clock;
do {
reserve(cpu);
     hold(expnt1(S_CPU));
    release(cpu);
x=prob();
if(x>P_EXIT){
i = random(0,ND-1);
reserve(disk[i]);
    hold(expnt1(S_DISK));
    release(disk[i]);
}
}while(x>P_EXIT);
record(clock-t,rt);
}
set(done);
}