CSIM programming
Extension to C++ (or C, or Java, or ....) available from Mesquite
Software
Offers additional language support for:
- parallel programming in general
- simulation model creation
- random variables
- measurement and instrumentation
Uses preprocessor to apply complicated program rewriting rules to
- break up your source code into "chunks" at CSIM statement
boundaries
- schedule the execution of those "chunks" to create interleaved parallelism
- individual chunks execute without
interruption but ...
- executions of chunks belonging to different CSIM processes are
interleaved by time
- In all CSIM programs, the true main function is
reserved for the hidden "chunk scheduler"
- it is first to run and outlives all other functions / processes
- what YOU think should be your main function is always called
the sim
function
- name is hard-coded into hidden "chunk scheduler"
- allows you to access command line arguments via argc and argv as usual
- for historical reasons (i.e., first version of CSIM was based
on C, not C++) it looks like
- All CSIM processes (including your sim function)
need to run "independently" from
- hidden "chunk scheduler"
- all other CSIM processes
- This is accomplished using the CSIM create statement.
- example: create ("sim");
- string parameter is just a pretty name for the process ID to
be used in CSIM reports and debugging
- semantics are just like Unix fork command:
- a running
process upon executing this statement returns immediately
from this function call and keeps executing
- a new
child process is created and scheduled to begin execution
"immediately", starting from the next statement in this function
- actual execution of the child process will be delayed
until (at least) the parent process stops
- no matter how many other processes run before the child
process, and how many instructions they execute, simulated time will not
change until after the child process has begun execution
- child process terminates when it reaches the normal exit
for the function where it was created.
- process can also kill itself at any time using CSIM
statement: terminate();
- Your entire CSIM program terminates when either:
- the special "sim" process terminates
- when it dies, all its children die too
- the program reaches a deadlock state,
where no process will ever be able to execute again
- example: Alice is waiting for Bob to do something, and vice-versa
- does
not mean all processes
are stopped now, but some will wake up at some future time
Here is what the trivial "hello world" program
looks like in CSIM:
1
#include <iostream>
2 #include "cpp.h"
3 #include <string.h>
4 using namespace std;
5
6 extern "C" void
sim() //
main process
7 {
8 int i;
9
create("sim"); //
separate from background system
10
for(i=1;i<10;i++)
11 {
12
cout << "Alice says hello " << i << " times."
<< endl;
13 }
14 }
Preprocessor splits the sim function
into two parts at line 9. "Chunk scheduler" begins executing the first
chunk as a normal function call until it encounters the replacement
code for create.
"Chunk scheduler" (a) adds "sim" to the list of simulated processes,
(b) schedules "sim" to execute the the second part of the original
"sim" function at the current simulated time, and (c) returns from
function call. Later, the "chunk scheduler" sets the identity of the
active process to "sim" and calls the function made from lines 10-14 of
the original source code. When that that function execution
completes, the "chunk scheduler" deletes "sim" from the list of
simulated processes. Since there are no other processes, the main
program terminates also terminates.
Here is a simple command line script
for compiling CSIM programs. When compiled, the previous program
produces this output:
[mart@bass ~]$ CPP_COMPILE_SCRIPT
oneworld.c
[mart@bass ~]$ a.out
Alice says hello 1 times.
Alice says hello 2 times.
Alice says hello 3 times.
Alice says hello 4 times.
Alice says hello 5 times.
Alice says hello 6 times.
Alice says hello 7 times.
Alice says hello 8 times.
Alice says hello 9 times.
[mart@bass ~]$
Here is
a slightly more complicated version of the same CSIM program, which
includes two processes (Alice and Bob) that each prints a series of
"hello world" messages.
1
#include <iostream>
2 #include "cpp.h"
3 #include <string.h>
4 using namespace std;
5
6 void
Bob();
// Another process
7
8 extern "C" void
sim() //
Alice is the main process
9 {
10 int i;
11
create("Alice"); // Alice
separates from background system
12
Bob();
// Alice gives Bob a chance to do the same
13
for(i=1;i<10;i++)
14 {
15
//
hold(uniform(0,10));
16
cout << "Alice says hello " << i << " times."
<< endl;
17 }
18 }
19
20 void Bob()
21 {
22 int i;
23 create("Bob");
24
for(i=1;i<10;i++)
25 {
26
//
hold(uniform(0,10));
27
cout << "Bob says hello " << i << " times." <<
endl;
28 }
29 }
When compiled exactly as above
(with lines 15 and 26 commented out), we obtain the following output:
[mart@bass ~]$ CPP_COMPILE_SCRIPT
twoworld.c
[mart@bass ~]$ a.out
Alice says hello 1 times.
Alice says hello 2 times.
Alice says hello 3 times.
Alice says hello 4 times.
Alice says hello 5 times.
Alice says hello 6 times.
Alice says hello 7 times.
Alice says hello 8 times.
Alice says hello 9 times.
[mart@bass ~]$
Notice that process Bob never had a
chance to run, even though process Alice called
function Bob
at line 12 and thus the child process Bob was created
when line 23 was executed. However, you must keep in mind that CSIM
programs run as a single thread of execution at the operating system
level, even if your underlying hardware has multiple CPU cores
available and hence could support true parallel execution. The illusion
of parallelism in CSIM programs comes about by interleaving the
execution of chunks taken from different functions and hence belonging
to different (simulated) processes.
- process Alice
returns immediately when it encounters the create statement in function
Bob
- the "chunk scheduler" adds the new process Bob to the
model, with the understanding Bob will begin
executing at line 24 before simulated time changes
- whenever you want, your code can look at the current value of
simulated time through the CSIM variable clock
- you are not allowed to change the value of clock directly
- however, if your code "takes time" to execute, then the value
of clock will be larger when you look at it
again
- because of the semantics of the "create" statement, Alice keeps
going while Bob
waits
- Alice
does not run out of things to do at the current value of simulated time
until it reaches the end the sim function and terminates
- When Alice
terminates, all of her child processes (including Bob) also
terminate, so the program execution is over
If we uncomment lines 15 and 26, then the CSIM statement hold (uniform (0, 10));
is executed by both processes before each cout statement in their
respective loops. Whenever a process executes a CSIM hold( )
function, its execution is suspended until simulated time advances by
the amount specified by its parameter. Obviously, the specified time
increment must be non-negative, or else executing the model would
violate causality! In this case, the time increment is given by the
CSIM function uniform
(a, b), which randomly choses any real number from the closed
interval between a
and b.
[mart@bass ~]$ CPP_COMPILE_SCRIPT
twoworld.c
[mart@bass ~]$ a.out
Bob says hello 1 times.
Bob says hello 2 times.
Alice says hello 1 times.
Bob says hello 3 times.
Bob says hello 4 times.
Alice says hello 2 times.
Alice says hello 3 times.
Bob says hello 5 times.
Bob says hello 6 times.
Bob says hello 7 times.
Alice says hello 4 times.
Alice says hello 5 times.
Bob says hello 8 times.
Alice says hello 6 times.
Alice says hello 7 times.
Bob says hello 9 times.
Alice says hello 8 times.
Alice says hello 9 times.
[mart@bass ~]$
It is instructive (but tediously
verbose!) to see what happens when we insert the CSIM statement trace_on( );
between lines 10 and 11 in source code for the sim function.
From this point onwards (or until the CSIM statement trace_off( ); is
encountered), the hidden "chunk scheduler" prints one line of
diagnostic output whenever it runs, as shown below.
Such trace output provides a lot of detail about model execution
details.
[mart@bass ~]$ CPP_COMPILE_SCRIPT
twoworld.c
[mart@bass ~]$ a.out
time
process id pri status
0.000 Alice
1 1 create Alice 1
0.000 Alice
1 1 join class default
0.000 Alice
1 1 sched proc: t = 0.000, id
= 2
0.000 Alice
1 1 create Bob 2
0.000
Bob 2 1 join
class default
0.000 Alice
1 1 hold for 5.139
0.000 Alice
1 1 sched proc: t = 5.139, id
= 1
0.000
Bob 2 1 hold for
1.757
0.000
Bob 2 1 sched
proc: t = 1.757, id = 2
Bob says hello 1 times.
1.757
Bob 2 1 hold for
3.087
1.757
Bob 2 1 sched
proc: t = 3.087, id = 2
Bob says hello 2 times.
4.844
Bob 2 1 hold for
5.345
4.844
Bob 2 1 sched
proc: t = 5.345, id = 2
Alice says hello 1 times.
5.139 Alice
1 1 hold for 9.476
5.139 Alice
1 1 sched proc: t = 9.476, id
= 1
Bob says hello 3 times.
10.189
Bob 2 1 hold for
1.717
10.189
Bob 2 1 sched
proc: t = 1.717, id = 2
Bob says hello 4 times.
11.907
Bob 2 1 hold for
7.022
11.907
Bob 2 1 sched
proc: t = 7.022, id = 2
Alice says hello 2 times.
14.615
Alice 1 1 hold
for 2.264
14.615
Alice 1 1 sched
proc: t = 2.264, id = 1
Alice says hello 3 times.
16.879
Alice 1 1 hold
for 4.948
16.879
Alice 1 1 sched
proc: t = 4.948, id = 1
Bob says hello 5 times.
18.929
Bob 2 1 hold for
1.247
18.929
Bob 2 1 sched
proc: t = 1.247, id = 2
Bob says hello 6 times.
20.176
Bob 2 1 hold for
0.839
20.176
Bob 2 1 sched
proc: t = 0.839, id = 2
Bob says hello 7 times.
21.015
Bob 2 1 hold for
3.896
21.015
Bob 2 1 sched
proc: t = 3.896, id = 2
Alice says hello 4 times.
21.827
Alice 1 1 hold
for 2.772
21.827
Alice 1 1 sched
proc: t = 2.772, id = 1
Alice says hello 5 times.
24.599
Alice 1 1 hold
for 3.681
24.599
Alice 1 1 sched
proc: t = 3.681, id = 1
Bob says hello 8 times.
24.912
Bob 2 1 hold for
9.834
24.912
Bob 2 1 sched
proc: t = 9.834, id = 2
Alice says hello 6 times.
28.280
Alice 1 1 hold
for 5.354
28.280
Alice 1 1 sched
proc: t = 5.354, id = 1
Alice says hello 7 times.
33.634
Alice 1 1 hold
for 7.657
33.634
Alice 1 1 sched
proc: t = 7.657, id = 1
Bob says hello 9 times.
34.746
Bob 2 1 terminate
process
Alice says hello 8 times.
41.291
Alice 1 1 hold
for 6.465
41.291
Alice 1 1 sched
proc: t = 6.465, id = 1
Alice says hello 9 times.
47.756
Alice 1 1 sim
exited
47.756
Alice 1 1 rerun
model
0.000 Alice
1 1 delete all pcb's
0.000 Alice
1 1 delete all events
0.000 Alice
1 1 delete all facilities
0.000 Alice
1 1 delete all storages
0.000 Alice
1 1 delete all mailboxes
0.000 Alice
1 1 delete all meters
0.000 Alice
1 1 delete all boxes
0.000 Alice
1 1 delete all tables
0.000 Alice
1 1 delete all qtables
0.000 Alice
1 1 delete event list
time
process id pri status
0.000 Alice
1 1 delete all classes
[mart@bass ~]$