Norm Matloff's JSim Discrete-Event Simulation Package

(Last updated: December 30, 2001.)

JSim is an event-oriented discrete-event simulation package It follows the classical approach, but is designed so that the source code is easily understood, facilitating student understanding and addition of new features.

Though process-oriented simulation is very popular (see C++Sim and my psim), the event-oriented approach has a number of advantages:

Contents of this page:

Assumptions and notices:

In this document we will assume a Unix environment. This will not come into play very much, and only slight changes to certain parts of this document need be made for other platforms. (The code itself does not need to be changed.)

Note: No guarantees of any kind are made regarding the accuracy of this software or its documentation.

Where to get it:

Go to Professor Matloff's simulation Web page. You may also find his Java Web page of interest.

How to install it:

Next, make a directory for JSim, say ~/jsim (if you choose some other directory for JSim, change everything below accordingly), and do the following from whatever directory you've unpacked the JSim source in.

cd Src
cp *.java ~/jsim

How to prepare JSim applications:

Before beginning, note that all classes, methods and variables which begin with "JS" denote JSim built-in entities. This will be important to keep in mind

Your file which contains main() will contain some application-specific "global" variables, and will do the following:

You will also have one or more source files which contain subclasses of the built-in JSim class JSElt. This class represents one work unit, e.g. one task for a machine or one message on a communications link. The reason for subclassing is first to enable adding application-specific information to the class -- in the examples described below, StopAndWait has JSElt subclasses FrameElt and TimeoutElt, and MM1 has a JSElt subclass MachineJob -- and most importantly, to declare the method JSEvntHandler(), which will override its counterpart in JSElt. This latter method will be explained by example below.

If your simulation includes resources for which jobs must queue, you may wish to the JSim class JSFacil. This class represents the resource, and again is typically subclassed in order to add application-specific information. For instance, in the MM1 example below, jobs queue up to be served by a machine, and the machine is simulated by a class Machine which extends JSFacil.

Command-line format for JSim applications:

Do the following once (say in your .cshrc or other shell startup file):

set JSimDir = ~/jsim
java -classpath .:$JSimDir main_class max_simtime debug_flag seed application_args

where "main_class" is the name of your class which contains main(), "debug_flag" is 1 for debugging, 0 otherwise, and "seed" is any integer (used to initialize the random number generators).

How to compile JSim applications:

Whenever you compile Main.java, type

javac -g -classpath .:$JSimDir main_class.java

Of course, various shortcuts to this can be set up in Makefiles, the CLASSPATH environment variable, shell aliases and the like.

JSim application tutorial-- the Stop-and-Wait protocol:

The model being simulated:

Stop-and-Wait is a standard protocol used in computer networks. In the system being modeled here, network node A is sending a message (a frame) to node B. We have scaled time so that it takes 1.0 unit of time for the message to get onto the communications link, and Alpha amount of time to propagate across the link. Node B sends back a short reply, taking negligible time to get onto the link and Alpha time to reach node A.

The message may have been corrupted when traveling from A to B, with probability P; if so, B will so say in the reply. Also, B may be busy and have some random delay before replying to A; if the delay is too long, A will timeout and assume the message had been lost, and then send again. We assume here that the sender at A always has a ready pool of messages to send, so as soon as one is successfully transmitted, the next one is sent immediately.

We are interested in the mean time it takes for a frame to be successfully sent, and also the mean number of tries it takes for success.

Analysis of the code in this example:

We've modularized a large part of the code into one method in Frame, which we have named SendFrame(). The method has a parameter, New, which indicates whether this is a new frame, i.e. our first attempt at sending this frame, or a retry. Here's the code:

   public void SendFrame(boolean New)

   {  JSName = "arrive at B";
      JSEvntTime = JSSim.JSSimTime + 1 + StopAndWait.Alpha;
      if (New)  {
         FrameNumber = StopAndWait.NextFrameToBeCreated++;
         StartTime = JSSim.JSSimTime;
         NTries = 1;
      }
      else NTries++;
      Trashed = false;
      JSInsertInEvntList();
      // set up the paired timeout
      StopAndWait.TmOt.JSEvntTime = JSSim.JSSimTime +
         StopAndWait.TimeoutTime;
      StopAndWait.TmOt.JSName = "timeout";
      StopAndWait.TmOt.JSInsertInEvntList();
      // pair them together to enable one to cancel the other
      JSPartnerEltNumber = StopAndWait.TmOt.JSEltNumber;
      StopAndWait.TmOt.JSPartnerEltNumber = JSEltNumber;
   }

Since we are sending to B, we've named the event type "arrive at B". The time that that occurs will be at JSSim.JSSimTime + 1 + Alpha, where the built-in JSim variable JSSim.JSSimTime is the current simulated time.

If this is a new frame, we give it the next available frame number. We record the present time as this frame's start time, i.e. the time at which we first start trying to send it. Also, we set Frame.NTries to 1, as this is our first try. On the other hand, if this is not a new frame, we increment Frame.NTries.

Next, we reset Frame.Trashed, which records whether the frame is corrupted. Then we call JSElt.JSInsertInSchedList() (Frame is a subclass of JSElt) to add this element to the event list (or schedule list). The event list is a linear linked list of pending events, ordered by time, with the earliest event at the head of the list (pointed to by JSSim.ScheduleListHd). The items in the event list have not occurred yet; they are merely scheduled to occur.

Finally, we set up a timeout element, and add it to the event list too.

Now, let's look at some of the event handler code. Recall that JSSim.JSMainLoop() is called from Main.main(). JSSim.JSMainLoop() then does the simulation, returning to Main.main() when the simulation is finished. Then Main.main() can print out the results. But JSSim.JSMainLoop() calls another method in Main, EvntHandler(), as can be seen in the following outline of JSSim.JSMainLoop():

   {  while (JSSimTime <= JSMaxSimTime)  {
         JSElt.JSGetNext();
         JSSimTime = JSCurrEvnt.JSEvntTime;
         ...
         Main.EvntHandler();
         ...
      }
   }

In other words, JSSim.JSMainLoop() (a) uses JSElt.JSGetNext() to pull the earliest event from the schedule list, then (b) advances the simulated time to the time of the pulled event -- which simulates that the event has now occurred -- and then (c) calls Main.EvntHandler() to perform whatever actions that event entails.

Main.EvntHandler() is very much application-specific. Here it is in this case:

   public static void EvntHandler()  // application-specific; required

   {

      if (JSSim.JSCurrEvnt.JSEvntMatch("arrive at B"))
         {DoArrivalAtB(); return;}
      if (JSSim.JSCurrEvnt.JSEvntMatch("done with delay"))
         {DoDelayDone(); return;}
      if (JSSim.JSCurrEvnt.JSEvntMatch("arrive back at A"))
         {DoArrivalBackToA(); return;}
      if (JSSim.JSCurrEvnt.JSEvntMatch("timeout"))
         {DoTimeout(); return;}
      System.out.println("shouldn't reach this point");
      System.exit(1);
   }

As you can see, it is essentially a switch statement (we can't use a real switch, since strings are not scalar types). We are using JSEvntMatch() in a manner similar to the C library's strcmp().

When a new frame is created, or we do a retry on an old frame, the next event will be "arrive at B"; when that event occurs, DoArrivalAtB() will be called. As you can see from the code in that method,

   static void DoArrivalAtB()

   {  Frame.JSName = "done with delay";
      Frame.JSEvntTime = JSSim.JSSimTime + JSSim.JSExpon(MeanDelay);
      Frame.JSInsertInSchedList();
   }

the random delay is generated (modeling that node B is too busy to process the incoming frame immediately), the event type is changed to "done with delay", and that event is added to the event list. Remember, the latter event has not occurred yet; it is merely scheduled.

What that event does finally occur, DoDelayDone() is called:

   void DoDelayDone()

   {  Frame.Trashed = (JSRnd() < P);
      Frame.JSName = "arrive back at A";
      Frame.JSEvntTime = JSSimTime + Alpha;
      Frame.JSInsertInSchedList();
   }

Node B first checks to see whether the frame arrived intact. Remember, there is a probability P that it is corrupted, a situation we simulate here by calling JSRnd(), which generates random numbers uniformly distributed on the interval (0,1). Then we set up to send the reply back to A, and add that new event to the event list.

Recall that we set a timeout. If the reply from B does not get back to A within the timeout period, A will give up the frame for lost, and retransmit it. The occurrence of a timeout is modeled by DoTimeout():

   void DoTimeout()

   {  Frame.JSCancel(TmOt.FrameEltNumber);
      SendFrame(false);
   }

Note that DoTimeout() must remove the other event, "arrive back at A", from the event list. The reason for this is that if DoTimeout() was called, it was because the timeout occurred before B's reply got back to A. So the "arrive back at A" event, which is still on the schedule list, must be canceled. We accomplish that by calling JSCancel(). Then DoTimeout() calls SendFrame() again, so as to start a new attempt to send the frame successfully to B.

On the other hand, if the reply from B does reach A in time, DoArrivalBackToA() is called:

   void DoArrivalBackToA()

   {  JSElt.JSCancel(Frame.TimeoutEltNumber);
      if (Frame.Trashed)
         SendFrame(false);  // failed, try sending again
      else  {
         // succeeded
         // need to do some bookkeeping
         TotFrames++;
         TotWait += JSSim.JSSimTime - Frame.StartTime;
         TotTries += Frame.NTries;
         // done, so send the next frame
         SendFrame(true);

   }

In this case, it is the timeout event which must be canceled. The method then checks for a report that the frame had been corrupted. If so, we need to send it again; if it was received intact, we do our bookkeeping, and send out a new frame. If not, we try sending this frame again.

Note that we subclassed JSElt:

class FrameElt extends JSElt

{
   int FrameNumber;
   float StartTime;  // time at which this frame is first sent
   int NTries;  // number of attempts at sending this frame so far
   boolean Trashed;  // true means erroneous
   int TimeoutEltNumber;  // JSEltNumber for the paired timeout

   public void JSPrintElt()

   {  super.JSPrintElt();
      System.out.println("  frame "+this.FrameNumber+":  number of tries = "
         +this.NTries+", trashed = "+this.Trashed);
   }

}

We added several fields specific to this application, e.g. NTries, and overrode JSPrintElt() in order to get additional, application-specific debugging information.

Running this example:

java -classpath .:$JSimDir Main timelim dbgflag seed alph timeout p mndly

In a typical run (recall the effect of randomness), we got the following results:

% java Main 1000 0 99999 0.2 2.5 0.0 0.2
total frames sent = 622
mean wait = 1.6076623
mean number of xmits per frame = 1.0016078

The first two command-line arguments, timelim and dbgflag, are general JSim quantities, while the remaining arguments are specific to this application. The command says to run the simulation up to a simulated time of 1000, with no debugging information, with alpha = 0.2, etc.

To solidify your understanding of the role of the event list, you should rerun the simulation, in this case setting the debug flag to 1 (pipe the output through the more command).

JSim application tutorial:

JSim applications are written in the usual manner of event-oriented discrete-event simulation programs. I am preparing a tutorial. Please send me e-mail at matloff@cs.ucdavis.edu if you need it right away, but if you have done this kind of programming before, you should be able to quickly see how to use JSim by perusing the M/M/1 queue example included in the package, as well as the JSim reference list below.

The main point to keep in mind is that you must have one of more application-specific subclasses of JSElt, and in each one must override the dummy method JSElt.JSEvntHandler(). This method processes the event given in JSSim.JSCurrEvnt.

Specifically, your method main() calls the core of the simulation, JSSim.JSMainLoop(), which looks like this:

   // forms the main loop of the entire simulation
   static void JSMainLoop(String[] Argv)

   {
      // while current simulated time is less than the max specified by
      // the user, do:
      while (JSSimTime <= JSMaxSimTime)  {
         JSElt.JSGetNext();  // get next event
         JSSimTime = JSCurrEvnt.JSEvntTime;  // advance the simulation clock
         JSCurrEvnt.JSEvntHandler();  // process the current event
      }
   }

JSEvntHandler() then will take whatever actions are appropriate to the occurrence of this current event. For example, in the M/M/1 queue application, the application-specific class Job, representing a single job in the system, has JSEvntHandler() check whether this event is an arrival of a job or a job completion, and calls application-specific methods accordingly:

   public void JSEvntHandler()

   {  if (JSName.equals("arrive"))
         DoArrival();
      else
         DoDone();
   }

Debugging aids:

As with any programs, JSim code should be debugged with the aid of a good debugging tool. See my debugging Web page, especially regarding the JSwat Java debugging tool.

Debugging simulation programs tends to be difficult, as with any program dealing with multiple concurrent activities. The best strategy to find a bug--and in fact, to verify the correctness of the program--is to step through the simulation with the debugging tool on a small problem, verifying that the different variables do have the correct values at the times you expect them to.

JSim also has a couple of debugging aids of its own (which should be used in conjunction with a debugging tool such as JSwat)::

If the "debug" command-line argument is set to 1, the event list is printed out immediately before and immediately after each event is executed. You can also call the functions JSPrintSchedList() and JSPrintElt() directly; note that the latter can be overridden so as to tailor it to your own application. There is a similar variable for printing server information.

JSim reference list -- library methods, classes and variables:

Methods:

Classes:

"Global" variables: