Skip to content
or press P · arrows to step · Esc to leavetap or swipe to step

Discrete-Event Simulation by Building

Week 1 — From blocking to queues to multiple servers

Quisim

Week 1: From blocking to queues ​

This week ​

Goal ​

Understand why queues exist, what a queue does, and how several servers change a system: by building every model yourself, and by running it.

Arrive → Leave → + Perform: blocking → Bypass: loss → + Queue: M/D/1 → random service: M/M/1 → capacity k: M/M/k

Every step has the same rhythm:

  • Build it: a few blocks and connections on your own canvas
  • Predict: write down a number before you press Start
  • Run it: compare, and explain the difference
  • Your turn: 5–10 minutes with an example, a table to fill in, a question to answer

How to follow along in the free demo ​

Run an example

  • Pen icon: show the editor pane
  • Examples tab: click e.g. M/M/1
  • Type new values into the NumberInput fields, even while it runs
  • Animate (film icon) on, then Start clean (Ctrl+Enter)
  • Pause and Step for one event at a time
  • Monitor tab ▸ Events: the event list

Build your own

  • New model (Ctrl+N), then the Blocks tab
  • The demo kit: Arrive, Queue, Perform, Leave, 2 × RandomExponential, 2 × RandomUniform, NumberInput, Chart, Statistics
  • Drag from a socket onto another socket, or onto a socket of a block preview in the Blocks tab: a pipe (green) or wire (silver) appears
  • Delete: select a block or pipe, then the trash icon

Full version: open Model code under a picture and paste it into the editor instead. Times are in ms; a run lasts 10 000 ms, about 10 s of animation.

Step 1: Arrive → Leave ​

Build it: arrivals, and nothing else ​

  1. New model, Blocks tab ▸ Entity: click Arrive
  2. Drag Arrive's outlet O onto the inlet of the Leave preview
  3. Drag Arrive's D socket, hover the Random sub-tab, drop it on V of RandomExponential
  4. Drag its µ socket, hover Value, drop it on N of NumberInput
  5. Type 500 into the field: 1/λ=5001/\lambda = 500
Model codeOpen in Quisim
qml
NumberInput { id:num0; initValue:500; label:"1/λ" }
Wire { from:num0.measureValue; to:rnd0.parameterMean }
RandomExponential { id:rnd0; seed:2 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:leave0.inlet }
Leave { id:leave0 }

Predict: how many entities in one run? ​

Predict ​

Mean time between arrivals 1/λ=5001/\lambda = 500, run length 10 000. How many entities reach Leave? How many are inside the system at any moment?

Run it ​

  • About 10 000/500=2010\,000 / 500 = 20, plus one at time 0; hover Leave's socket N to read it
  • Every entity leaves in the instant it arrives: nobody waits, nothing is congested
  • The Arrive→Leave example in the Examples tab is the same model

Discuss ​

The model is correct, and useless. What is missing? Real customers are served.

Your turn: count the arrivals ​

Your model (or the Arrive→Leave example), 5 minutes ​

Predict first, then run each value twice and read N at Leave.

1/λ1/\lambda1000500250100
predicted N
N, run 1
N, run 2

Why is N not exactly 10 000⋅λ10\,000 \cdot \lambda, and why do the two runs differ?

Step 2: A server, and blocking ​

Build it: Arrive → Perform → Leave ​

  1. Select Leave (click its label), trash icon: Leave and its pipe are gone
  2. Drag Arrive's O onto the inlet of the Perform preview
  3. Drag Perform's O onto the inlet of the Leave preview
  4. Drag Perform's D socket, hover Value, drop it on N of a NumberInput preview
  5. Type 600: every service takes D=600D = 600
diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id:num0; initValue:500; label:"1/λ" }
Wire { from:num0.measureValue; to:rnd0.parameterMean }
RandomExponential { id:rnd0; seed:2 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:perform0.inlet }
NumberInput { id:num1; initValue:600; label:"D" }
Wire { from:num1.measureValue; to:perform0.parameterDuration }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }

Predict: what happens to the next customer? ​

Predict ​

A customer every 500 on average, a service takes 600. What happens to an entity that arrives while Perform is busy? How many reach Leave in 10 000?

Run it (Animate on) ​

  • It cannot enter Perform: it stays in Arrive, and the socket turns red
  • While it waits there, the next interarrival time does not even start
  • About 14 instead of 21 entities get through (the run on the next slide: 14): one cycle lasts on average E[max⁡(X,D)]=600+500 e−1.2≈750E[\max(X, D)] = 600 + 500\,e^{-1.2} \approx 750, and 10 000/750≈1310\,000 / 750 \approx 13, plus the first one

Key insight: blocking ​

diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id:num0; initValue:500; label:"1/λ" }
Wire { from:num0.measureValue; to:rnd0.parameterMean }
RandomExponential { id:rnd0; seed:2 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:perform0.inlet }
NumberInput { id:num1; initValue:600; label:"D" }
Wire { from:num1.measureValue; to:perform0.parameterDuration }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }

Blocking ​

Without a place to wait, a busy server silently changes the arrival process: the model no longer has the demand we specified. Here 14 of the 21 customers of step 1 get through.

Your turn: how much demand gets through? ​

Your model, 1/λ=5001/\lambda = 500, 7 minutes ​

Change DD, run, read N at Leave. Watch for red sockets.

DD1003006001000
predicted N
N at Leave
red sockets: rarely / often?
  1. What is the largest N this server can ever reach in 10 000, whatever the arrivals?
  2. Name a real system that blocks like this. Is that acceptable there?

Doctor's office that turns patients away, a phone line with a busy signal, a machine that stops while the next station is occupied.

Step 3: The wrong fix ​

The Bypass example: a side door ​

diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id: number0; initValue:1000; label:"1/λ"; lower:1 }
Wire { from: number0.measureValue; to: rnd0.parameterMean }
RandomExponential { id:rnd0}
Wire { from: rnd0.measureRandomVariate; to: arrive0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:leave1.inlet }
Leave { id:leave1; label:"Reject" }
Pipe { from:arrive0.outlet; to:perform0.inlet }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }

Arrive has two pipes. The pipe defined last in the code is tried first, so an entity takes Perform (D=1000D = 1000) when it is free, and Reject otherwise.

Predict: is the problem solved? ​

Predict ​

Open Examples ▸ Bypass: 1/λ=10001/\lambda = 1000, D=1000D = 1000. Will you see red sockets? Which share of the customers is served?

Run it ​

  • No red sockets: every entity always has somewhere to go
  • But roughly half of them leave through Reject, unserved; with only about 10 customers per run the share jumps around, so run it a few times
  • Theory (Erlang loss, one server): served share =11+λD=12= \dfrac{1}{1 + \lambda D} = \dfrac{1}{2}

Key insight ​

We traded blocking for loss. What we need is a place to wait.

Your turn: how much do we lose? ​

Example Bypass, 7 minutes ​

Set 1/λ1/\lambda, run, read N at both Leave blocks.

1/λ1/\lambda20001000500250
served (N at Leave)
rejected (N at Reject)
served share
theory 1/(1+λD)1/(1+\lambda D)67%50%33%20%

Where would a company accept this loss on purpose? (Think of a full car park, or a call without a waiting line.)

Step 4: A queue, M/D/1 ​

Build it: a place to wait ​

Back to your model from step 2.

  1. Click the pipe Arrive → Perform, trash icon
  2. Drag Arrive's O onto the inlet of the Queue preview
  3. Drag Queue's outlet O onto Perform's inlet I
  4. Keep 1/λ=5001/\lambda = 500, D=600D = 600
Model codeOpen in Quisim
qml
NumberInput { id:num0; initValue:500; label:"1/λ" }
Wire { from:num0.measureValue; to:rnd0.parameterMean }
RandomExponential { id:rnd0; seed:2 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
NumberInput { id:num1; initValue:600; label:"D" }
Wire { from:num1.measureValue; to:perform0.parameterDuration }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }

Exponential arrivals (M), a fixed service time (D), 1 server: this is M/D/1.

Build it: measure the queue ​

  1. Drag Queue's L (queue length) into the Report sub-tab, onto P[0] of Chart
  2. Drag Leave's LS (sojourn time of the last entity) onto P[0] of Statistics
  3. Statistics shows the mean sojourn time of the run
diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id:num0; initValue:500; label:"1/λ" }
Wire { from:num0.measureValue; to:rnd0.parameterMean }
RandomExponential { id:rnd0; seed:2 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
NumberInput { id:num1; initValue:600; label:"D" }
Wire { from:num1.measureValue; to:perform0.parameterDuration }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }
Wire { from: queue0.measureLoad; to: chart0.parameters[0] }
Chart { id: chart0; height:66; width:384 }
Wire { from: leave0.measureSojourn; to: statistics0.parameters[0] }
Statistics { id: statistics0; height:48; width:128; label:"Sojourn\ntime" }

Predict: does the queue settle down? ​

Predict ​

Still 1/λ=5001/\lambda = 500 and D=600D = 600, but now with a queue. Sketch the queue-length chart for the 10 s run. What would it look like after an hour?

Run it ​

  • No red sockets, nobody lost, but once the queue has formed it never empties again: later customers wait much longer than the first ones
  • The run on the previous slide: mean sojourn time about 2200, almost four times DD
  • 20 customers are few: type 1/λ=1001/\lambda = 100, D=120D = 120 for the same ratio and five times the customers

Run it: 1/λ=1001/\lambda = 100, D=120D = 120 ​

diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id:num0; initValue:100; label:"1/λ" }
Wire { from:num0.measureValue; to:rnd0.parameterMean }
RandomExponential { id:rnd0; seed:2 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
NumberInput { id:num1; initValue:120; label:"D" }
Wire { from:num1.measureValue; to:perform0.parameterDuration }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }
Wire { from: queue0.measureLoad; to: chart0.parameters[0] }
Chart { id: chart0; height:66; width:384 }
Wire { from: leave0.measureSojourn; to: statistics0.parameters[0] }
Statistics { id: statistics0; height:48; width:128; label:"Sojourn\ntime" }

The queue climbs and does not come back (about 30 waiting at the end of this run): on average 1 arrival every 100, 1 departure every 120. A longer run makes it worse.

Utilization: the one number that decides ​

Utilization ​

ρ=λμ=λ⋅D=D1/λthe queue is stable only if ρ<1\rho = \frac{\lambda}{\mu} = \lambda \cdot D = \frac{D}{1/\lambda} \qquad \text{the queue is stable only if } \rho < 1

1/λ1/\lambdaDDρ\rhoqueue
what we just ran5006001.2grows without bound
next slide5004000.8comes and goes

Key insight ​

A queue absorbs short-term fluctuations, not a long-term lack of capacity.

Run it: a stable M/D/1, D=400D = 400 ​

diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id:num0; initValue:500; label:"1/λ" }
Wire { from:num0.measureValue; to:rnd0.parameterMean }
RandomExponential { id:rnd0; seed:2 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
NumberInput { id:num1; initValue:400; label:"D" }
Wire { from:num1.measureValue; to:perform0.parameterDuration }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }
Wire { from: queue0.measureLoad; to: chart0.parameters[0] }
Chart { id: chart0; height:66; width:384 }
Wire { from: leave0.measureSojourn; to: statistics0.parameters[0] }
Statistics { id: statistics0; height:48; width:128; label:"Sojourn\ntime" }

ρ=0.8\rho = 0.8: waiting comes in waves that clear again. Long-run mean sojourn time (theory): D+ρ D2(1−ρ)=400+800=1200D + \dfrac{\rho\,D}{2(1-\rho)} = 400 + 800 = 1200. This run: about 940, from only 21 customers.

Your turn: find the boundary ​

Example M/D/1 (1/μ=D=1001/\mu = D = 100), 10 minutes ​

Predict whether the chart keeps returning to 0. Then set 1/λ1/\lambda, run, record.

1/λ1/\lambda20012511010090
ρ=D/(1/λ)\rho = D/(1/\lambda)0.50.8
chart returns to 0?
mean sojourn time
theory (long run)150300600∞∞
  1. Between which two values does the system tip over?
  2. Write the stability condition with λ\lambda and DD.

Look inside: the event list ​

  • Monitor tab ▸ Events, Animate on; Start clean, then Pause
  • Step (top right) runs exactly one event; each row shows a time and the block that owns it

This is discrete-event simulation ​

  • The state changes only at events: an arrival, the end of a service
  • Between events nothing happens, so the clock jumps to the next one
  • Take the earliest event, execute it, schedule new ones, repeat

Your turn: be the simulator ​

Your M/D/1 model, 1/λ=5001/\lambda = 500, D=400D = 400, 7 minutes ​

Pause right after the start. Before each Step, predict which event comes next.

step12345678
clock time
block (Arrive / Perform)
events in the list after it
  1. How many events are in the list while the server is idle? While it is busy?
  2. An end of service is always exactly 400 after... what?

Step 5: Random service, M/M/1 ​

Build it: a random service time ​

  1. Select the D NumberInput, trash icon: it and its wire are gone
  2. Drag Perform's D into Random, onto V of RandomExponential
  3. Drag its µ onto N of a new NumberInput
  4. Type 400: the mean service time 1/μ1/\mu is still 400
diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id:num0; initValue:500; label:"1/λ" }
Wire { from:num0.measureValue; to:rnd0.parameterMean }
RandomExponential { id:rnd0; seed:2 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
NumberInput { id:num1; initValue:400; label:"1/µ" }
Wire { from:num1.measureValue; to:rnd1.parameterMean }
RandomExponential { id:rnd1; seed:5 }
Wire { from:rnd1.measureRandomVariate; to:perform0.parameterDuration }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }
Wire { from: queue0.measureLoad; to: chart0.parameters[0] }
Chart { id: chart0; height:66; width:384 }
Wire { from: leave0.measureSojourn; to: statistics0.parameters[0] }
Statistics { id: statistics0; height:48; width:128; label:"Sojourn\ntime" }

Exponential arrivals, exponential service, 1 server: M/M/1.

Predict: same means, same waits? ​

Predict ​

Same arrivals, same mean service time 400, same ρ=0.8\rho = 0.8. Does the mean sojourn time go up, down, or stay at about 1200?

Run it ​

  • It goes up, clearly: the run on the previous slide gives about 2000, the M/D/1 run about 940; long-run theory says 2000 instead of 1200
  • A few very long services hold up everybody behind them
  • Short services cannot make up for it: the server cannot work ahead of arrivals

Key insight: variability matters ​

Same averages, different behaviour. Randomness in the service alone makes the system worse.

The M/D/1 and M/M/1 examples side by side ​

diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id: number0; initValue:125; label:"1/λ"; lower:1 }
NumberInput { id: number1; initValue:100; label:"1/µ"; lower:1 }
Wire { from: number0.measureValue; to: rnd0.parameterMean }
Wire { from: number1.measureValue; to:perform0.parameterDuration }
RandomExponential { id:rnd0 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }
Wire { from: queue0.measureLoad; to: chart0.parameters[0] }
Statistics { id: statistics0; height:48; width:128; label:"Sojourn\ntime" }
Wire { from: leave0.measureSojourn; to: statistics0.parameters[0] }
Chart { id: chart0; height:66; width:384 }
diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id: number0; initValue:125; label:"1/λ"; lower:1 }
NumberInput { id: number1; initValue:100; label:"1/µ"; lower:1 }
Wire { from: number0.measureValue; to: rnd0.parameterMean }
Wire { from: number1.measureValue; to: rnd1.parameterMean }
RandomExponential { id:rnd0 }
RandomExponential { id:rnd1 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Wire { from:rnd1.measureRandomVariate; to:perform0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }
Wire { from: queue0.measureLoad; to: chart0.parameters[0] }
Statistics { id: statistics0; height:48; width:128; label:"Sojourn\ntime" }
Wire { from: leave0.measureSojourn; to: statistics0.parameters[0] }
Chart { id: chart0; height:66; width:384 }

Both: 1/λ=1251/\lambda = 125, 1/μ=1001/\mu = 100, ρ=0.8\rho = 0.8, about 80 customers per run. The only difference is one RandomExponential. Every run draws new random numbers, so your values will differ from these pictures.

Your turn: same means, different waits ​

Examples M/D/1 and M/M/1, 10 minutes ​

Run each example three times, record the mean sojourn time. Then set 1/μ=1101/\mu = 110 in both and repeat.

M/D/1M/M/1M/D/1M/M/1
1/μ1/\mu100100110110
run 1 / 2 / 3
theory (long run)300500513917
  1. Is M/M/1 worse in every single run, or only on average?
  2. Which grows faster when ρ\rho goes from 0.8 to 0.88?

How much worse? Pollaczek–Khinchine ​

For one server with Poisson arrivals, cSc_S = coefficient of variation of the service time:

Wq=1+cS22⋅ρ1−ρ⋅E[S]W_q = \frac{1 + c_S^2}{2} \cdot \frac{\rho}{1-\rho} \cdot E[S]

examplecSc_SWqW_q at ρ=0.8\rho = 0.8, E[S]=100E[S] = 100W=Wq+E[S]W = W_q + E[S]
M/D/10200300
M/M/11400500

Exponential service doubles the waiting time, and ρ1−ρ\frac{\rho}{1-\rho} explains the explosion near ρ=1\rho = 1.

Build it: something in between ​

In your M/M/1 model:

  1. Delete the service RandomExponential and its NumberInput
  2. Drag Perform's D onto V of RandomUniform
  3. Drag its max onto N of a NumberInput, type 800
  4. Service is uniform on [0,800)[0, 800): mean 400 again, cS2=1/3c_S^2 = 1/3
diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id:num0; initValue:500; label:"1/λ" }
Wire { from:num0.measureValue; to:rnd0.parameterMean }
RandomExponential { id:rnd0; seed:2 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
NumberInput { id:num1; initValue:800; label:"max" }
Wire { from:num1.measureValue; to:rnd1.parameterUpper }
RandomUniform { id:rnd1; seed:5 }
Wire { from:rnd1.measureRandomVariate; to:perform0.parameterDuration }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }
Wire { from: queue0.measureLoad; to: chart0.parameters[0] }
Chart { id: chart0; height:66; width:384 }
Wire { from: leave0.measureSojourn; to: statistics0.parameters[0] }
Statistics { id: statistics0; height:48; width:128; label:"Sojourn\ntime" }

Predict with the formula: between 1200 (M/D/1) and 2000 (M/M/1), where? Theory: 400+23⋅4⋅400≈1470400 + \frac{2}{3} \cdot 4 \cdot 400 \approx 1470. This run: about 1170, between the M/D/1 run (940) and the M/M/1 run (2000).

Why does my run not show 300 and 500? ​

  • A 10 s run sees only about 80 customers, and starts with an empty queue
  • Two runs of the same model give noticeably different means
  • Near ρ=1\rho = 1 the run is far too short to settle at all

Discuss ​

Which run is the "true" answer? How many runs would you trust? That is week 2.

Step 6: Several servers, M/M/k ​

Build it: give Perform a capacity ​

Back to your M/M/1 model (exponential service, 1/λ=5001/\lambda = 500). Built the uniform one? Swap RandomUniform back for RandomExponential.

  1. Type 1/μ=8001/\mu = 800: now ρ=800/500=1.6\rho = 800/500 = 1.6
  2. Drag Perform's C (capacity) onto N of a NumberInput preview
  3. Type 2: Perform serves up to k=2k = 2 entities at once
diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id:num0; initValue:500; label:"1/λ" }
Wire { from:num0.measureValue; to:rnd0.parameterMean }
RandomExponential { id:rnd0; seed:2 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
NumberInput { id:num1; initValue:800; label:"1/µ" }
Wire { from:num1.measureValue; to:rnd1.parameterMean }
RandomExponential { id:rnd1; seed:5 }
Wire { from:rnd1.measureRandomVariate; to:perform0.parameterDuration }
NumberInput { id:num2; initValue:2; label:"k" }
Wire { from:num2.measureValue; to:perform0.parameterCapacity }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }
Wire { from: queue0.measureLoad; to: chart0.parameters[0] }
Chart { id: chart0; height:66; width:384 }
Wire { from: leave0.measureSojourn; to: statistics0.parameters[0] }
Statistics { id: statistics0; height:48; width:128; label:"Sojourn\ntime" }

Predict: how many servers do we need? ​

Predict ​

1/λ=5001/\lambda = 500, 1/μ=8001/\mu = 800. With k=1k = 1 the queue explodes (ρ=1.6\rho = 1.6). With k=2k = 2? k=3k = 3? Does each extra server help equally?

Run it ​

  • Utilization per server: ρ=λkμ\rho = \dfrac{\lambda}{k\mu}; k=2k = 2 gives ρ=0.8\rho = 0.8: stable again
  • k=2k = 2 rescues the system, but customers still wait (the run on the previous slide: about 2270); with k=3k = 3 the queue is mostly empty
  • Long-run mean sojourn (Erlang C): k=2k = 2: 2222, k=3k = 3: 956, k=4k = 4: 830; the service alone takes 800

The M/M/k example ​

diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id: number0; initValue:55; label:"1/λ"; lower:1 }
NumberInput { id: number1; initValue:500; label:"1/µ"; lower:1 }
Wire { from: number0.measureValue; to: rnd0.parameterMean }
Wire { from: number1.measureValue; to: rnd1.parameterMean }
RandomExponential { id:rnd0 }
RandomExponential { id:rnd1 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Wire { from:rnd1.measureRandomVariate; to:perform0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
NumberInput { id: number2; initValue:10; label:"Capacity"; lower:1 }
Wire { from: number2.measureValue; to: perform0.parameterCapacity }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }
Wire { from: queue0.measureLoad; to: chart0.parameters[0] }
Wire { from: perform0.measureLoad; to: chart0.parameters[1] }
Statistics { id: statistics0; height:48; width:128; label:"Sojourn\ntime" }
Wire { from: leave0.measureSojourn; to: statistics0.parameters[0] }
Chart { id: chart0; numParameters:2; height:66; width:384 }

The chart shows two lines: queue length and busy servers. Default: 10 servers, ρ=500/(55⋅10)≈0.91\rho = 500 / (55 \cdot 10) \approx 0.91.

Your turn: diminishing returns ​

Example M/M/k, 10 minutes ​

Set 1/λ=1251/\lambda = 125 and 1/μ=2001/\mu = 200. Predict, then run each Capacity twice.

Capacity kk1234
ρ=λ/(kμ)\rho = \lambda / (k\mu)1.60.8
mean sojourn, run 1 / 2
busy servers on the chart: mostly …
theory (Erlang C, long run)∞556239208
  1. How much sojourn time does the 2nd server save? The 3rd? The 4th?
  2. When is a 4th server worth paying for?

Key insight: diminishing returns ​

diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id: number0; initValue:125; label:"1/λ"; lower:1 }
NumberInput { id: number1; initValue:200; label:"1/µ"; lower:1 }
Wire { from: number0.measureValue; to: rnd0.parameterMean }
Wire { from: number1.measureValue; to: rnd1.parameterMean }
RandomExponential { id:rnd0 }
RandomExponential { id:rnd1 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Wire { from:rnd1.measureRandomVariate; to:perform0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
NumberInput { id: number2; initValue:3; label:"Capacity"; lower:1 }
Wire { from: number2.measureValue; to: perform0.parameterCapacity }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }
Wire { from: queue0.measureLoad; to: chart0.parameters[0] }
Wire { from: perform0.measureLoad; to: chart0.parameters[1] }
Statistics { id: statistics0; height:48; width:128; label:"Sojourn\ntime" }
Wire { from: leave0.measureSojourn; to: statistics0.parameters[0] }
Chart { id: chart0; numParameters:2; height:66; width:384 }

Key insight ​

The first extra server rescues the system; each further one buys less. This is the core of capacity planning.

Look inside: parallel service ​

  • M/M/k example, Capacity 3: Pause, open Monitor ▸ Events, Step
  • Several service ends are scheduled at once: parallel service in the event list
  • Select Perform, Monitor ▸ Entities: who is being served right now

Discuss ​

Banks, airport check-in and call centres use one shared queue for several counters. Why not one queue per counter? (Week 3 builds both.)

Wrap-up ​

What we built this week ​

Six models, each one fixing the last ​

  • Arrive → Leave: correct, and useless
  • + Perform: a busy server blocks the arrivals
  • Bypass: no blocking, but customers are lost
  • + Queue, M/D/1: stable only if ρ=λD<1\rho = \lambda D < 1; the event list drives it all
  • M/M/1: same means, more variability, longer waits
  • M/M/k: more servers help, with diminishing returns

Look back at your predictions: which one was furthest off?

Take-home ​

Example M/M/k at its defaults: 1/λ=551/\lambda = 55, 1/μ=5001/\mu = 500 ​

  1. Set Capacity to 9. Predict, run, explain: what is ρ\rho?
  2. Find the smallest Capacity that keeps the system stable. What is ρ\rho then?
  3. Record the mean sojourn time for that Capacity and the next three:
Capacity10111213
mean sojourn, your runs
theory (Erlang C)883618548521
  1. Bonus, own model: M/M/2 with uniform service (as in step 5). Better or worse than exponential?

Next week ​

For the lecturer ​

Running the session ​

  • Students build alongside you in their own browser; every block used in the build steps is in the demo kit
  • Timing, about 100 minutes: Arrive→Leave 10, blocking 15, Bypass 10, M/D/1 25 (incl. event list), M/M/1 20, M/M/k 20
  • Short on time? Keep the Build it and Predict slides; give Your turn: be the simulator and something in between as homework
  • Ask for a written prediction before every run, and collect a few aloud: wrong ones are the most useful

Notes ​

  • The demo code editor is read-only and examples cannot be extended: students build on an empty canvas, or change NumberInput values in an example
  • Building, students' NumberInputs have no labels; the Model code boxes show which is which
  • Short runs will not match the theory rows; let that discomfort lead into week 2
  • 1/λ=5001/\lambda = 500 gives only about 20 customers per run; switch to the examples (80 customers) or scale both times down by 5 when a pattern is hard to see
  • Erlang loss, Pollaczek–Khinchine, Erlang C and Little's law (L=λWL = \lambda W) are in the handbook