Facility is a resource that is used by entities for a finite duration. There is a limit on the number of entities that can use the facility at a given time. As an example, consider a barbershop (the facility) with m barbers (capacity of facility). The customers arrive at shop and wish to ‘use’ the resource (barber); if all barbers are busy, the customers wait until one barber is available.
The timeline of interactions between entities and facility is as follows:
An entity requests the simulator to use a facility for duration time (with useFacility() Extended Entity Prototype API).
If the facility is busy, the entity is placed in facility’s queue. It waits there until other entities in front of it are done using the facility (also see the Scheduling Disciplines below).
At the expiration of the timer, the entity is notified that it has just finished using the facility.
Note
The entity is notified only at the completion of the request (step 4 above).
It is not notified, for example, when it done waiting in the queue or when it is preempted (if supported by queuing discipline).
Note
The useFacility() request can only be cancelled or reneged while the entity is waiting in queue.
If the entity has started using the facility (step 3 above), the entity cannot be removed from the queue. That is, Sim.Request.cancel(), Sim.Request.waitUntil() and Sim.Request.unlessEvent() will have no effect after the entity has started using facility.
Scheduling Disciplines
Scheduling Discipline is the policy on how the entities wait in the queue and use the facility. Some disciplines are:
In the version 0.26 only FCFS, LCFS and Processor Sharing scheduling disciplines are supported. The other disciplines are planned for future releases.
Entities access the buffers through their Entity Prototype API:
Creates a new facility. name (string) is used for identifying the statistics in a report. discipline is the scheduling discipline; currently it can take one of these values:
numServers is the number of servers available in the facility. By default, only one server is available per facility. Currently, only Sim.Facility.FCFS uses this parameter.
Return the duration for which this facility has been in use.
Return Population statistics for the request in the system (queue + service time).
Return Population statistics for the request in the queue.
The M/M/c problem: There are c servers (e.g. bank tellers) whose services are requested by customers. There is only one queue in front of all servers, so a customer at the head of the queue will move if any one of the c servers is free. The customers arrival is Poisson process, and service time is exponentially distributed. Such kind of queuing systems and servers can be easily modeled with FCFS facilities.
We create a facility as:
var server = new Sim.Facility('Server', Sim.Facility.FCFS, nServers);
The customers arrive at intervals that is exponentially distributed with mean lambda, and they request service for exponentially distributed duration with mean mu. We model the customer as:
var rand = new Random(SEED);
var Customer = {
start: function () {
// the next customer will arrive at:
var nextCustomerInterval = rand.exponential(lamda);
// wait for nextCustomerInterval
this.setTimer(nextCustomerInterval).done(function () {
// customer has arrived.
var useDuration = rand.exponential(mu); // time to use the server
this.useFacility(server, useDuration);
// repeat for the next customer
this.start();
});
}
}
Finally we create the simulation and entity objects, and start the simulation.
var sim = new Sim("M/M/c"); // create simulator
sim.addEntity(Customer); // add entity
sim.simulate(SIMTIME); // start simulation
server.report(); // statistics
In the processor sharing service disciplines, all requesting entities get immediate access to the resource, however, their service time increases proportionally to the number of other entities already in the system.
As an example, consider CPU modeled as facility with Processor Sharing discipline. A single request to use CPU for 1 second will complete in 1 second. Two simultaneous requests to use CPU for 1 second each will finish in 2 seconds each (since the CPU is “shared” between the two requests).
Another example would be network connection link (e.g. Ethernet) with a given data rate. Entities request to use the resource, which in this case means sending data. If multiple overlapping requests are made then the network link is “shared” between all requests. Say, request one is initiated at time 0 to send data for 10 seconds. A second request is also made at time 5 seconds to send data for 1 second. In this case, the first request will finish at 11 seconds (0 - 5 sec at full capacity, 5 - 7 seconds at half capacity, and 7 - 11 sec at full capacity again), while the second request will finish at 7 seconds . We validate this as follows:
// create the facility
var network = new Sim.Facility("Network Cable", Sim.Facility.PS);
var Entity = {
start: function () {
// make request at time 0, to use network for 10 sec
this.useFacility(network, 10).done(function () {
assert(this.time(), 11);
});
// make request at time 5, to use the network for 1 sec
this.setTimer(5).done(function () {
this.useFacility(network, 1).done(function () {
assert(this.time(), 7);
});
});
}
};
var sim = new Sim();
sim.addEntity(Entity);
sim.simulate(100);
Buffer is a resource that can store a finite number of tokens. Any entity can store tokens in the buffer if there is free space, or retrieve existing tokens from the buffer if some are available. Queueing happens when:
Note
Buffer vs. Store
Buffers are resources that store “homogeneous” quantities. The buffers do not actually store any object, rather they keep a counter for the current usage, which is increment by putBuffer operation and decremented after getBuffer operation. If you wish to store real objects, consider using Store.
Buffers support two basic operations: put() to store tokens in the buffer, and get() to retrieve tokens from the buffers. The Buffer object has two queues: putQueue where the entities wait if their put() request cannot be immediately satisfied, and getQueue where the entities wait if their get() request cannot be immediately satisfied.
Entities access the buffers through their Entity Prototype API:
Creates a new buffer. name (string) is used for identifying the statistics in a report. The buffer has maxCapacity capacity and has initially initialAmount number of tokens. If initialAmount is omitted, then the buffer will be created empty.
The maximum capacity of the buffer.
The number of available tokens in the buffer.
Population Statistics for the put queue.
Population Statistics for the get queue.
The Buffer class does not directly provide any put() or get() API. Instead, entities must use their Entity Prototype functions (putBuffer() and getBuffer()) to access buffers.
The Producer-Consumer Problem: There are nProducers number of producer entities that produce tokens at rate of productionRate and stores them in a common buffer of bufferSize capacity. The producers must successfully store their produced items in buffer before they can begin on production of the next item. There are also nConsumers number of consumer entities that retrieve tokens from the same buffer and process them at rate of consumerRate.
We would like to study what is the average wait times for the producers and the consumers, given different values of the various parameters (such as bufferSize, productionRate etc).
We create the common buffer as:
var buffer = new Sim.Buffer("buffer", bufferSize);
We model the producers as entities that generate one token every t seconds, where t is exponential random number will mean productionRate.
Random rand = new Random(SEED);
var Producer = {
start: function () {
var timeToProduce = rand.exponential(1.0 / productionRate);
// Set timer to self (models the time spend in production)
this.setTimer(timeToProduce).done(function () {
// Timer expires => item is ready to be stored in buffer.
// When the item is successfully stored in buffer, we repeat
// the process by recursively calling the same function.
this.putBuffer(buffer, 1).done(this.start);
});
}
}
We model the consumers as entities that retrieve tokens from the buffers, and process them for t seconds, where t is exponential random number will mean consumptionRate.
var Consumer = {
start: function () {
// Retrieve one token from buffer
this.getBuffer(buffer, 1).done(function () {
// After an item has been retrieved, wait for some time
// to model the consumption time.
// After the waiting period is over, we repeat by
// recursively calling this same function.
var timeToConsume = rand.exponential(1.0 / consumptionRate);
this.setTimer(timeToConsume).done(this.start);
});
}
}
Finally we create the simulation and entity objects, and start the simulation.
// Create simulator
var sim = new Simulator("Producer Consumer Problem");
// Create producer entities
for (var i = 0; i < nProducers; i++) sim.addEntity(Producer);
// Create consumer entities
for (var i = 0; i < nConsumers; i++) sim.addEntity(Consumer);
// Start simulation
sim.simulate(SIMTIME);
// statistics
buffer.report();
Store is a resource that can store a finite number of JavaScript objects (actually any datatype: number, string, function, array, object etc). Any entity can store objects in the store if there is free space, or retrieve existing objects from the store if some are available. Queueing happens when:
Note
Store vs. Buffer
Stores are resources that store distinct JavaScript objects. If you do not wish to store actual objects, consider using Buffer.
Stores support two basic operations: put() to store objects in the store, and get() to retrieve objects from the stores. The Store object has two queues: putQueue where the entities wait if their put() request cannot be immediately satisfied, and getQueue where the entities wait if their get() request cannot be immediately satisfied.
Entities can retrieve objects from stores in two ways:
Entities access the stores through their Entity Prototype API:
The retrieved object can be accessed via the this.callbackMessage attribute in the callback function (see example below).
Creates a new store. name (string) is used for identifying the statistics in a report. The store has maxCapacity capacity. The store will be created empty.
The maximum capacity of the store.
The number of available objects in the store.
Population Statistics for the put queue.
Population Statistics for the get queue.
The Store class does not directly provide any put() or get() API. Instead, entities must use their Entity Prototype functions (putStore() and getStore()) to access stores.
// Create a store
var store = new Sim.Store("Example Store", 10);
var Entity = {
start: function () {
// Put an object
this.putStore(store, {myfield: "myvalue"});
// Put another object
this.putStore(store, {myfield: "othervalue"});
// arrays, numbers, strings etc can also be stored
this.putStore(store, "stored string");
// Retrieve object from store.
// Note 1: If filter function is not supplied, objects are returned in FIFO order
// Note 2: The object can be accessed via this.callbackMessage
this.getStore(store).done(function () {
assert(this.callbackMessage.myfield === "myvalue");
});
// Retrieve object from store using filter function
this.getStore(store, function (obj) {
return (obj === 'stored string');
})
.done(function () {
assert(this.callbackMessage === "stored string");
});
}
}