Skip to content

Discrete-event simulation

How an event calendar advances a queue simulation, with an auditable three-job FCFS example, time-weighted queue length and finite-horizon caveats.

Discrete-event simulation advances a model from one scheduled event to the next, updating its state when an event occurs. A quant developer can use it to investigate how bursts of pricing requests build a queue, how service rules change waiting times, or how a processing delay propagates through a trading workflow. The answer is conditional on the supplied arrivals, service requirements and operating rules.

The defining feature is the simulation clock. It jumps to the next event time instead of checking the system at every fixed time step. Between events, the queue state in the example below stays unchanged. “Discrete” describes those state changes; event times can still be fractional seconds.

Follow the calendar, then serve the queue

A basic simulator keeps three things: the current state, the current simulation time, and a future-event calendar. For a single-server model, the state might contain the waiting jobs and the job being served.

At each iteration it:

  1. Removes the earliest event from the calendar and advances the clock to its timestamp.
  2. Accumulates statistics for the interval just elapsed, using the state that held during that interval.
  3. Applies the event: an arrival may join the waiting line; a completion may release the server and start the next job.
  4. Schedules any resulting future events, such as the completion of newly started work.
A priority queue, often implemented by a min-heap, can select the earliest calendar event. That calendar is separate from the queue of jobs awaiting service. The first orders events by time; the second follows a service discipline such as first-come, first-served (FCFS). SimPy’s basics guide explains the event dispatcher and environment.

This is also distinct from event-driven architecture, which describes how software components communicate and react to events. A live application can use that architecture without simulating a clock.

A three-job run you can audit by hand

Consider a fictional risk-calculation worker with one server, initially empty and idle. All times are seconds. Jobs run to completion without interruption; switching jobs takes no time. These three arrivals are the entire workload, and the simulation ends after the last departure.

JobArrivalRequired serviceService startsDepartsQueue wait
A02020
B11231
C1.51341.5

At time 0, A starts and its completion is scheduled for time 2. B arrives at 1 and waits. C arrives at 1.5 and joins behind B. At 2, A leaves and B starts; B's departure at 3 starts C, which departs at 4. The clock visits 0, 1, 1.5, 2, 3 and 4. It does no queue-state updates at intermediate times such as 0.5.

For arrival aia_i, service requirement sis_i, start bib_i and departure did_i, this FCFS schedule can also be checked recursively:

bi=max⁡(ai,di−1),di=bi+si,wi=bi−ai,b_i=\max(a_i,d_{i-1}),\qquad d_i=b_i+s_i,\qquad w_i=b_i-a_i,

with d0=0d_0=0 for this initially idle run. The mean queue wait across all three jobs is

wˉ=0+1+1.53=56 seconds≈0.8333 seconds.\bar w=\frac{0+1+1.5}{3}=\frac{5}{6}\text{ seconds}\approx0.8333\text{ seconds}.

Queue wait excludes service. A job's arrival-to-departure time includes both; the measurement boundary matters when reporting latency.

Fictional FCFS server: jobs A, B and C arrive at 0, 1 and 1.5 seconds, start at 0, 2 and 3, and depart at 2, 3 and 4. Waits are 0, 1 and 1.5 seconds. Waiting-queue area over 0 to 4 seconds is 2.5 job-seconds; average queue is 0.625 jobs.
Fictional FCFS server: jobs A, B and C arrive at 0, 1 and 1.5 seconds, start at 0, 2 and 3, and depart at 2, 3 and 4. Waits are 0, 1 and 1.5 seconds. Waiting-queue area over 0 to 4 seconds is 2.5 job-seconds; average queue is 0.625 jobs.
Original deterministic example with one initially idle server, no interruption and no switching cost. Dashed pale bars show waiting; solid bars show service. The lower panel integrates only waiting jobs.

Average jobs and average seconds have different denominators

Let Q(t)Q(t) count waiting jobs, excluding the job in service. It is 0 before time 1, then 1 on [1,1.5)[1,1.5), 2 on [1.5,2)[1.5,2), 1 on [2,3)[2,3), and 0 on [3,4][3,4]. Its area is

∫04Q(t) dt=1(0.5)+2(0.5)+1(1)=2.5 job-seconds.\int_0^4 Q(t)\,dt =1(0.5)+2(0.5)+1(1) =2.5\text{ job-seconds}.
Divide by the four-second observation window to obtain the time-average queue length, 2.5/4=0.6252.5/4=0.625 jobs. Averaging queue counts observed at event timestamps would give a different weighting and would generally miss this time average. SimPy’s monitoring guide distinguishes monitoring at intervals from monitoring state changes.

Here the area also equals the sum of the three complete waiting times. The completed-cohort throughput is 3/43/4 jobs per second, so

34 jobs/second×56 seconds=0.625 jobs.\frac{3}{4}\text{ jobs/second}\times\frac{5}{6}\text{ seconds}=0.625\text{ jobs}.

This is exact finite-cohort accounting: the run starts empty, follows every job through completion, and contains no other jobs. Its resemblance to Little's law does not establish a steady-state model. Nor do three stipulated jobs establish a population latency distribution or a tail percentile. The illustration needs no Poisson arrivals, exponential service times or equilibrium assumptions from queueing theory.

Rules that belong in the model

Simultaneous events need an explicit order. A general calendar can sort by timestamp, then declared event-type priority, then a unique increasing event ID. One defensible queue convention processes completions before arrivals at the same timestamp, with IDs breaking remaining ties; it must also say how tied arrivals enter the FCFS line. The worked run has no tied calendar events. Starting B at A's completion is an immediate action within that completion event. SimPy's documented FIFO tie rule uses increasing IDs for equally timed events of equal priority; a simulator's deterministic choice still needs to represent the intended system.
Randomness is optional. This example is deterministic. A stochastic model might sample arrivals and service times, making repeated runs a Monte Carlo simulation experiment. Record the inputs, distributions, random seed, generator and event-order rules for reproducibility. Replaying a seed verifies repeatability; independent replications help measure sampling uncertainty. Neither validates the model against actual operations.
The observation window changes the question. A finite workload starting empty can legitimately include startup behaviour. To estimate steady-state performance, investigate initialisation bias, a justified warm-up interval, run length and output dependence. Deleting a warm-up period cannot make an unstable model stationary. The Simio textbook's discussion of terminating and steady-state simulations explains why the study's objective determines the treatment of startup.
At a fixed horizon, some jobs may still be waiting or in service. Report them and distinguish observed partial waits from complete waits. A mean computed only from jobs completed by the cutoff can favour shorter jobs. Specify whether arrivals stop at the horizon and the system then drains, or whether observation ends immediately. Also state whether an event exactly at the endpoint is processed: for example, SimPy's numeric stopping-time convention excludes events at that exact time. Our hand-worked cohort explicitly includes C's departure at time 4.
For the calendar's underlying data structure, Heapsort and Priority Queues develops heap operations and selecting the smallest key. The public page is a preview; the full lesson requires membership.

Sources

Last updated
  • simulation
  • event calendar
  • queueing
  • waiting times
  • finite horizon
← Back to the glossaryBack to top ↑