SlxvsH. Pdf

Yüklə 69,5 Kb.
Pdf görüntüsü
ölçüsü69,5 Kb.

A Comparison of SLX and GPSS/H

For Experienced GPSS/H Users

1.  Introduction


 In developing SLX, we wanted to preserve the conceptual strengths of GPSS/H, while going well beyond it in terms

of extensibility, modularity, and generality. If you know GPSS/H, virtually all of your knowledge about simulation and

modeling will be transferable to SLX, but since GPSS/H concepts have been implemented within a new and much

more general framework, you’ll have to shift gears a bit to get into SLX. To help you get started, this memo describes

some of the major differences between SLX and GPSS/H.


2.  The Layered Architecture

SLX was explicitly designed to support hierarchical modeling. We wanted users to be able to build constructs that

operate at a higher level than GPSS/H blocks, and then to be able to (re)use those constructs as components in still

higher-level constructs, and so on. We also wanted users to be able to develop new constructs at approximately the

same level as GPSS/H blocks, and – where desirable – at lower levels, too.

GPSS/H is essentially a single-layer software package. You can extend it to two layers by using external routines

written in C or Fortran, but doing that requires going outside of GPSS/H itself. The internals of GPSS/H blocks are

hidden from you, and only Wolverine Software can add new blocks (or control statements). MACROs provide a way

of “packaging” linear sequences of GPSS/H blocks and/or control statements for reuse, but the MACRO mechanism

was never designed to provide the kind of heavy-duty nested aggregation that hierarchical modeling requires.

To address the needs of hierarchical modeling, SLX was conceived from the beginning as having multiple layers.

Whether such an architecture will be successful depends on three properties. First, the individual layers must be well

conceived. Our twenty years’ experience designing, implementing, and working with GPSS/H provided plenty of

insight into what the layers should contain. Second, the layers must not be too far apart, so that shifting one’s

attention up or down from one layer to the next is a smooth transition, not a jarring loss of focus. Third, there must be

good mechanisms for getting from one layer to the next. If you’re working at a given layer, it should be relatively easy

for you to add to that layer by using components taken from lower layers. For example, if none of the prepackaged

GPSS/H-style “blocks” provided with SLX meets your needs, you can build your own “block” using the same lower-

level components and tools that we use. It should also be easy to build higher levels on top of your current level.

SLX provides three mechanisms for extensibility: objects, macros, and user-defined statements. A detailed

explanation of these mechanisms is beyond the scope of this memo; however, a brief summary follows. SLX is an

object-based language. It is not truly  object-oriented, because it lacks inheritance and procedural polymorphism

which are key components of the OOP paradigm. SLX utilizes a composition paradigm for construction of complex

objects; i.e., large objects are built by incorporating smaller objects into their definitions. Using this bottom-up

approach, one can construct objects of arbitrary complexity. For example, if you wanted to construct a “super-

Facility,” you could define an object which incorporated a GPSS/H Facility and additional properties of your own.

SLX has extremely powerful macro definition capabilities. SLX’s statement definition capability is an extension of the

macro facility which allows you to add your own statements to SLX. For both macros and statement definitions, you

supply a prototype which defines the components and syntax of the macro/statement, and you supply a  definition

section which expands the definition into SLX source code. Prototypes accommodate a wide variety of syntactic

forms, e.g., positional parameters, keyword parameters, optional parameters, punctuation characters, variable-length

arguments lists, etc. Any language which includes macros must include a  macro language for defining the rules by

which a macro is expanded. In most languages, the macro language is distinctly different from the host language.

Macro languages typically offer much weaker expressiveness than their host language. For example, #if, #else, and

#endif in the C/C++ preprocessor macro language are much weaker than the conditional branching and looping

language constructs available in the C/C++ host language. In SLX, the macro language is SLX itself; i.e., the full

range of SLX’s expressive power can be used in macro/statement definitions. The code that you provide in

macro/statement definitions is pre-compiled by SLX into machine instructions which execute as an extension of the

SLX compiler. In other words, you can write code which runs at  compile time. This approach affords virtually

unprecedented expressive power. An example which illustrates this power is the collection of statements we defined

for using Proof Animation in conjunction with SLX. We defined a “layout” statement which reads a Proof layout

(.lay) file at compile time and constructs a compile-time dictionary of Proof path and object class names. We defined a

collection of statements for writing commands to a Proof trace (.atf) file. For each command implemented, in contexts

where a path or object class name must be specified, the dictionary can be used to validate the existence of the

component during compilation of the SLX model. In other words, the SLX-Proof interface “knows” about the

contents of a layout.


Source Code Appearance

The format of GPSS/H statements resembles that of an assembly language (label, verb, comma-separated operands).

Statements must be in upper case, and operands cannot contain imbedded blanks. By contrast, SLX statements are

completely free-format and can use both lower- and upper-case letters. SLX’s syntax  is very similar to that of C. If

you’re familiar with C, the transition from GPSS/H to SLX will be easy for you. If not, you should find the syntax

reasonably intuitive, but you’ll have to learn to remember to put semicolons at the ends of statements.


Integer Indices

In GPSS/H, all entity classes (Facilities, Storages, Queues, etc.) are stored in pools whose sizes are fixed once a model

begins execution. Ultimately, every access of a GPSS/H entity is expressed using an integer index into the pool for the

given entity type. If symbolic names are used (SEIZE JOE), the names are converted by GPSS/H into integer indices.

A user can define the symbol-to-integer mapping in an EQU statement when desired, but otherwise GPSS/H will

automatically establish a mapping. When contiguous groups of entities are used in a model in combination with

indirect addressing, incorrect computation of integer indices can have serious consequences. For example, assume

that in a model of a computer network Facilities 1-10 are used to represent CPUs, and Facilities 11-20 are used to

represent disks. If a model erroneously attempts to SEIZE CPU number 11, disk number 1 will be SEIZEd instead, but

no error will be detected because 11 is a valid Facility number. Nor is the error easily detected by the modeler, since

one small integer (10) looks pretty much like another (11).

In SLX, on the other hand, instances of each entity type are no longer stored in a single pool. A modeler can

explicitly declare an array of objects, and each such array is stored (and addressed) separately. In the computer

network example, we would define two separate arrays of Facilities, each of size 10. An attempt to SEIZE CPU 11

would result in a clearly identified run-time error, since there are only 10 CPUs in the array. It’s even possible to

dynamically create and destroy GPSS/H entities during execution of a model. This makes it easy to build generic

models in which a wide variety of model configurations can be created in response to user inputs or specifications.


GPSS/H Transactions vs. SLX Active Objects

The original version of GPSS was designed by Geoffrey Gordon in the early 1960s. He invented the Transaction-flow

world view. His landmark achievement made it possible for “non-programmers” to easily describe processes that

operate in parallel. (Even in the 1960s, “programming vs. non-programming was a hot issue.) Although GPSS has

undergone many changes over the years, Transaction-flow is still the heart of the language.

In GPSS/H, a Transaction has two internal parts. The first is a “header”, which contains all of the information

necessary to support a Transaction’s movement through a model. Included in the header are the transaction’s

priority, a pointer to the next GPSS/H Block it is to execute, internal chain pointers, etc. The second part of a

Transaction is its “parameter area”, which contains all user-defined Transaction data. The numbers and kinds of

Parameters are specified in the GENERATE or SPLIT Block which creates the Transaction.

In SLX, the role of the GPSS/H Transaction has been split into two pieces. User-defined data consists of attributes

defined for an active object.  These attributes are not limited to integer and floating point values as is the case in

GPSS/H. Strings, boolean values, enumerated types can be used. Furthermore, arrays and sub-objects can be used..

The SLX equivalent to a GPSS/H Transaction header is called a puck. In order to further discuss the role of the puck,

which is central to SLX, we must first examine SLX objects in general.

SLX provides two kinds of objects – active and passive.  Passive objects are simply instances of named data

structures that are acted upon by an SLX program. (They’re like C structs.) For example, in a model of a networked

printer, we could define a passive object class representing jobs to be processed by the printer as follows (note

availability of the enum data type):

class printer_job


enum(normal, legal) paper_type;




Active objects have, in addition to any necessary data items, an actions property specified in their class definitions.

The actions property describes the behavior of the object over its lifetime. The actions property corresponds to a

sequence of GPSS/H Blocks following a GENERATE or SPLIT Block. The following example illustrates a laser printer

modeled as an active object. In the example, the printer spends its entire lifetime circulating through a forever loop.

The printer waits for at least one printer_job object to show up in the input queue, removes the first printer_job from

the queue, prints the job represented by the printer_job object (advances by the print time),  then destroys the

printer_job object. In the 


 statement, note that the job object’s paper_type, which is of 


 type, can be

used directly as an index into an array of printing speeds which depend on paper_type.

class printer()


pointer(printer_job) job;





wait until (printer_queue.size > 0);

job = first printer_job in printer_queue;

remove job from printer_queue;

advance (job->job_page_count * time_per_page[job->paper_type]);

destroy job;




SLX objects – both passive and active – are dynamically created using the  new operator. Pointer variables are used

for keeping track of dynamically created objects:

pointer(printer_job) p;

p = new printer_job;

p->paper_type = legal;

p->page_count = 27;

place p into printer_queue;

Active objects must be activated once they have been created. The activate verb creates a puck for the just-created

printer object and then places the puck on the Current Events Chain, poised to execute the first statement of the



 property. Pucks are the schedulable entities of SLX. Pucks are placed on the Current Events Chain

or Future Events Chain, and when blocking conditions occur in a model, pucks wait for the blockages to be removed.

Creation and activation of an active object can be accomplished by:



ptr = new printer;

activate ptr;

The above sequence can be abbreviated as:

activate new printer;

It is beyond the scope of this memo to describe all the actions pucks can perform or have performed on them. With a

rough idea of the role of SLX pucks in mind, however, we can resume our comparison of GPSS/H and SLX.

A GPSS/H Transaction may be thought of as combining an SLX active object and a puck into a single, indivisible

unit. In GPSS/H, all scheduling operations are performed using Transactions. Transactions are placed on the Current

and Future Events Chains, and Transactions wait for blocking conditions to be removed. Transactions all have

identical formats; they differ only in their numbers and kinds of numeric parameters (fullword, halfword, byte, and

floating point). If we were to describe GPSS/H Transactions as SLX active objects, all Transactions could be

described as a single object class, treating parameters as numeric arrays with variable dimensions. In contrast, SLX

active objects have unlimited variety..As we’ll see in the next section, multiple operations can be scheduled

simultaneously for any given active object; i.e., an active object can have more than one puck. In short, the

expressive power of SLX objects is far greater than that of GPSS/H Transactions.

SLX’s “activate new” most closely resembles GPSS/H’s SPLIT block. In GPSS/H, the SPLIT block is executed by a

parent Transaction and creates one or more offspring Transactions. The offspring Transactions execute code whose

label is given as the B-operand of the SPLIT block. In SLX, the type of object executing an “activate new” may differ

from the type of object which is created and activated; hence there is no implicit copying of attributes from parent to

offspring. (If desired, such copying must be done explicitly.) The offspring object executes the code defined in its

object class’s actions property, rather than having its behavior specified by the object responsible for its creation.

6.  Describing Parallel Activities


 The reason that we use simulation languages, rather than procedural languages, for writing discrete event simulation

models is that simulation languages provide easy-to-use, built-in features for describing parallel activities. One

measure of the quality of simulation software is the ease with which this can be accomplished. In GPSS/H, parallelism

always takes the form of interactions among Transactions. For example, many Transactions can be in ADVANCE

blocks at the same time. Similarly, some Transactions can be in control of Facilities and Storages, while other

Transactions must wait before they can SEIZE or ENTER, respectively, those Facilities and Storages.


 In SLX, there are two levels at which parallelism can be modeled. Large-scale and small-scale (or local) parallelism are

modeled using different mechanisms. Large-scale parallelism is modeled as interactions among SLX active objects,

and closely resembles the GPSS/H “interacting Transactions” approach. Local parallelism, on the other hand, is

modeled using the SLX fork statement, which creates an additional puck for the current active object. When a fork

statement is executed, both the parent and offspring pucks share access to the same active object. For example,

suppose that we model a complex machine as an SLX active object. Within the machine, several subsystems may

operate in parallel. Other subsystems may remain idle until external sensors are activated or deactivated. By using

fork statements to create multiple pucks for a given machine, all of these activities can take place in parallel within the

single SLX object used to model the machine. Each puck has direct, albeit shared, access to the machine’s properties.

I we have multiple machines, each is characterized by data local to the machine and a family of pucks with shared

access to that data.


 In GPSS/H there is a two-level hierarchy for sharing model data: all data is either local to a given Transaction, or it is

global and accessible to all Transactions. The sharing of data local to Transactions (Transaction Parameters) can

require considerable effort in GPSS/H, because Transactions do not have direct access to other Transactions’

parameters. If we were to use GPSS/H to model the complex machine from the previous example, we would have to

model the parallel activities by SPLITting off a number of offspring Transactions. The data to be shared among those

Transactions would have to be global, since placing it in any one of the Transactions would be impractical. If we only

had one such machine, we could use a collection of Savevalues. If we had more than one such machine, we would

have to use Matrix Savevalues and maintain integer indices into the matrix rows or columns. Alternatively, we could

use indirect addressing to access contiguous ranges of (scalar) Savevalues. In either case, considerable effort can be

required to accomplish the goal.


 In SLX, there is a three-level hierarchy for data sharing. Data can be global, local within procedures (functions and

subroutines), or local within objects. Using objects and pointers to them, sharing data is very straightforward. Any

object has easy access to all of the local data of other objects (except data that is defined as private to a given

object). In addition, as we discussed above, if multiple pucks are created for a given object, they all have direct

access to the object’s data.


7.  Active Objects as Switch Hitters

One of the fundamental decisions that must be made when developing a simulation model is which components need

to be modeled as passive components and which need to be modeled as active components. The natural world view

of GPSS/H is “passive resource, active consumer.” In GPSS/H, consumers of resources are almost always modeled as

Transactions, while the resources themselves are modeled as Facilities or Storages which have no active behavior of

their own and can only be acted upon. When the behavior of a resource is complex, however, it is often necessary to

model the resource using a Transaction. For example, consider how a butcher would be represented in a model of a

supermarket. In a simple model, the butcher could be modeled as a Facility. Requests for butcher service would take

the familiar form of “QUEUE, SEIZE, DEPART, ADVANCE, RELEASE.” In a more detailed model, the complexity of a

“real” butcher’s activities would have to be taken into account. During otherwise idle periods, the butcher must

rearrange meat in the display cases, cut new meat, prepare meat for the deli department, and so on. Modeling such

activities would almost always necessitate representing the butcher using a Transaction. Unfortunately, a butcher

Transaction cannot also function as a butcher Facility, so a Facility and a Transaction would have to be used in


In SLX, active objects can easily  assume  passive roles. This is almost always accomplished by having an active

object place its puck into a set and then execute a wait statement. A wait statement with no until clause is treated as

a “put me to sleep” operation. Pucks that execute such unconditional wait statements are said to be in an indefinite

wait condition. They wait until they are reactivated (awakened) by another puck that examines the set into which the

waiting puck placed itself. Consider the problem of representing a job that is making its way through an assembly

process. If the process is complex, we might want to model the job using an active object. But suppose that at certain

times the job has to be transported from point A to point B by a conveyor, forklift, or other agent. If the actions of the

transporter are complex, then it, too, should be modeled using an active object. During the time that the assembly job

is being transported, it can assume a passive role and be manipulated by the transporter object.. After the job arrives

at point B, it can resume its active role. This kind of “switch hitting” occurs in many modeling contexts and is

handled easily in SLX.


The Current Events Chain

Like GPSS/H, SLX has a Current Events Chain (CEC). In SLX, however, the operation of the CEC is much simpler. In

GPSS/H, Transactions that are experiencing non-unique blockages (e.g., at refusal-mode TEST blocks) are all retained

on the Current Events Chain. Each time that such a Transaction is examined during a CEC scan, the blocking

condition is retested. Transactions that are experiencing unique blocking conditions (e.g., waiting to SEIZE a Facility)

are also retained on the CEC, but their “scan-skip indicators” are turned on, so they are efficiently skipped over

during CEC scans.

In SLX, all blocking conditions are unique. SLX’s wait until statement – a true breakthrough in simulation language

design – uses one or more control variables in an expression describing a blocking condition. When a blocking

condition exists, so that the expression evaluates to FALSE, the puck that must wait is removed from the CEC and

placed into a reactivation list associated with each control variable for which the puck must wait. When the value of a

control variable is changed, a test is made to see whether any pucks are waiting for a change in that variable’s state.

If so, the waiting pucks (kept on the control variable’s reactivation list) are reinserted into the CEC and allowed to

retest their wait-until expressions. SLX thus combines tremendous ease in specifying arbitrarily complex blocking

conditions with optimally efficient determination of when the conditions have been removed.

Consider Tom Schriber’s barbershop model, the grandfather of all GPSS models. The first barbershop model that a

student develops typically has unrealistic shutdown conditions. At 5:00, the model simply stops, ignoring a haircut

in progress and any customers waiting in the queue. In a second model, more realistic shutdown conditions can be

implemented. At 5:00, the barbershop door is closed, and the current haircut (if any) is completed, and haircuts are

given to customers in the queue (if any) before the model is shut down. SLX and GPSS/H source code for this

approach are shown in Appendix A.


Practicing Safe Simulation

One of GPSS/H’s greatest features is its total run-time error checking. It is impossible to access beyond the end of a

MATRIX SAVEVALUE, for example, without getting a run-time error. SLX carries on this tradition. Given the greater

computational complexity of SLX, this presented a major challenge in its development, but we think that you’ll be

pleased with the results. For example,  all pointer-variable references are validated at run time. For those of you

who’ve ever had to track down a reference to a NULL or uninitialized pointer-variable in a C program, this feature

should be greatly appreciated (and a real eye-opener as well).

10. Subroutines, Modules, Files

In GPSS/H, models are most often written as “one big program,” contained in a single file. In part, this is because

GPSS/H does not support “full-blown” subroutines as they occur in general-purpose programming languages.

Although TRANSFER SBR provides a mechanism for calling and returning from subroutines, all arguments to the

subroutine must be placed into the parameters of the Transaction that will execute it. Any local data items in the

subroutine must also be stored in parameters of the Transaction executing it.

SLX is a marriage of GPSS/H-like simulation capabilities with the rich expressiveness of C. Consequently, SLX

supports true subroutines. In addition, SLX incorporates modules, which are collections of subroutines and (data

and object) definitions. Modules are the principal packaging mechanism of SLX, and they allow access to the internal

details of a module to be carefully controlled. You can make accessible only those features that need to be accessible

from outside the module, while hiding the internal implementation detail from the outside world.

11. Conclusions

SLX preserves much of the best of GPSS/H’s rich legacy and familiar concepts, but in a new framework which offers

much greater power and generality. Although some effort is required to learn the new language, that effort can pay

large dividends.

Appendix A — GPSS/H and SLX Barbershop Models

On the next page, source code is shown for a GPSS/H implementation of a barbershop with realistic shutdown

conditions. Since this memo is intended for experienced GPSS/H users, we will not discuss the GPSS/H model. On the

following page is an SLX implementation of the same model. Salient features of the SLX model will be highlighted in

the paragraphs which follow. The first statement of the program, “import

”, imports h4.slx, a file which defines an

SLX-based dialect of GPSS/H. The SLX model, per se, is divided into three sections: global declarations, customer

flow, and run control.

In the global declarations section, the Facility and Queue used in the model are declared. In SLX, all variables must be

declared prior to their first usage. This is in sharp contrast to GPSS/H, in which variables are implicitly defined by

their occurrence in a model. Random number streams used in the model are defined. Note that the specification of

random number stream starting positions is optional. If no starting position is specified, SLX will choose the next

higher multiple of 100,000 beyond the highest starting position encountered in previously specified streams, if any.

The final declaration, stop_time, is provided only for enhanced readability.

The customer flow section of the model comprises an SLX active object class named customer. The sequence of

statements is directly analagous to the GPSS/H model, apart from the absence of an SLX equivalent to the GPSS/H


The run control portion of the model is provided by the SLX main program. The  arrivals statement specifies that

customer objects are to be created and activated with uniformly distributed interarrival times until the model’s

stop_time has been reached. The arrivals statement provides the functionality of the GPSS/H GENERATE Block. In

GPSS/H, GENERATE Blocks can be used on a free-standing basis. Each GENERATE Block produces a stream of

Transactions which flows into the Blocks which follow. In SLX, the arrivals statement acts more like a GPSS/H control

statement than a Block. The arrivals statement expands into a fork statement which creates a second puck for the

main program. The body of the fork is a loop which is executed until the stop_time is reached or exceeded. Each time

through the loop, main’s second puck executes an  advance  statement to model the interarrival time between

successive customers, and it activates a new customer object, e.g. “activate new customer”. Each customer created

and activated begins its execution with the first statement of the actions property of the customer object class.

The “report(system)” statement produces standard output for the SLX model. “system” is an SLX set. The members

of the system set are entity class container sets. In this model, there is are entity class container sets for facilities,

queues, and random_streams. “joe” is the only member of the facility set; and “joeq” is the only mamber of the queue

set. “Arrivals” and “Service” belong to the random_stream set. In SLX, an object class can have a report property.

(This is the second example of an object property; we’ve already discussed the actions property above.)  A report

property does the obvious. In h4.slx, we have defined report properties for each GPSS/H entity class. The report

statement, e.g., “report(system);” invokes the report property for the specified object or set of objects. The report

property for a set invokes the report property for each member of the set (if any). Thus, “report(system)” issues a

report for each entity of each GPSS/H entity class. This provides for the SLX equivalent to GPSS/H standard output.

Finally, the SLX model is terminated by an optional C-style “exit(0);” statement. If this statement were not provided,

flow of execution off the end of the main program would be interpreted as a normal program exit.






        GENERATE        RVUNI(1,18,6)   ARRIVALS EVERY 18 +- 6 MINUTES


        QUEUE           JOEQ            GET IN LINE

        SEIZE           JOE             GRAB THE BARBER

        DEPART          JOEQ            EXIT LINE

        ADVANCE         RVUNI(2,15,3)   HAIRCUT TAKES 15 +- 3 MINUTES

        RELEASE         JOE             FREE THE BARBER

        TERMINATE       0               EXIT THE SHOP




        GENERATE        ,,48000,1,-1    RUN FOR 800 HRS (IN MINUTES)


        TEST E          Q$JOEQ,0        DRAIN QUEUE

        GATE NU         JOE             WAIT UNTIL JOE IS IDLE

        TERMINATE       1               SHUT DOWN

        START           1




SLX Barbershop Model



module barb13




Global Declarations













constant float stop_time = 48000;



Customer Object


object customer












(Service, 12.0, 18.0);








Run Control


procedure main



: customer

iat = 


(Arrivals, 12.0, 24.0)

until_time = stop_time;

wait until (time >= stop_time and 


(joeq) == 0 and 







// End of barb13 module

Yüklə 69,5 Kb.

Dostları ilə paylaş:

Verilənlər bazası müəlliflik hüququ ilə müdafiə olunur © 2023
rəhbərliyinə müraciət

    Ana səhifə