🧮 Lab 3 — Introduction to Queuing Theory & Java Modelling Tools (JMT)
Duration: ~1 hour
Environment: Windows, Linux, or macOS (JMT is Java-based)
🎯 Learning Objectives
- Understand the basic components of a queuing system (arrivals, service, queues, customers).
- Learn to use the Java Modelling Tools (JMT) suite.
- Build, simulate, and analyze simple queuing networks.
- Compare system metrics: utilization, throughput, average queue length, response time.
⚙️ Setup Instructions
- Download JMT: https://jmt.sourceforge.net/
- Requires Java (≥ JDK 8)
- Unzip and run JSIMgraph.jar(for simulation models)
Tools Overview
| Tool | Description | 
|---|---|
| JSIMgraph | Build and simulate queuing network models | 
| JMVA | Analyze models analytically | 
| JABA | Build models programmatically in Java | 
🧩 Part 0 — Quick Recap: Queuing Theory Basics
Key Symbols:
- λ (lambda) = arrival rate (customers per second)
- μ (mu) = service rate (customers served per second)
- ρ (rho) = utilization = λ / μ
For M/M/1 queue:
- Average number in system: L = ρ / (1 − ρ)
- Average waiting time: W = 1 / (μ − λ)
🧩 Example 1 — RAID-0 style Parallel I/O (Fork–Join)
A request is split across k disks and the reply is sent after all parts finish (synchronous join). RAID (Redundant Array of Independent Disks) is a data storage technology that combines multiple physical disks into a single logical unit to improve performance, fault tolerance, or both. Different RAID levels (e.g., RAID 0, 1, 5) determine how data is split, mirrored, or parity-protected across the disks.
🧠 Learning outcomes
By the end of this exercise, students should:
- Understand the concept of fork–join synchronization in queuing networks.
- Observe that variability (heavy tail) in response time increases with more parallel disks.
⚙️ Model overview
| Element | Description | 
|---|---|
| Source | Generates incoming I/O requests (arrival rate λ). | 
| Fork | Splits each request into k sub-tasks (1 per disk). | 
| Disks | Modeled as queueing stations with hyperexponential service times. | 
| Join | Waits for all k sub-tasks to finish before reassembling the request. | 
| Sink | Collects completed requests. | 
Step-By-Step Instructions
Step 1. Create a new model
- Open JSIMgraph → click File → New Model.
- From the toolbar, drag a Source, Fork, Join, k Queueing Stations, and a Sink node onto the canvas.
- Connect them in this order:
- Source → Fork → (Disk1, Disk2, Disk3, …) → Join → Sink
Step 2. Create a Class
- Define distribution (e.g., exponentital, hperexponential etc). Choose λ, mean.
Step 3. Define Performance Indeces (what you want to measure)
Step 4. Run the simulation
🧩 Example 2 — Load Balancer Policies per Class
A router (LB) dispatches to a pool of servers. Compare Random, Round-Robin, and Shortest-Queue (SQ). Use two customer classes with different SLAs.
⚙️ Model overview
| Element | Description | 
|---|---|
| Source | Generates incoming I/O requests (arrival rate λ). | 
| Router | Dispatches requests to servers. | 
| Servers | Modeled as queueing stations | 
| Sink | Collects completed requests. | 
Step-By-Step Instructions
Step 1. Create a new model
- Open JSIMgraph → click File → New Model.
- From the toolbar, drag a Source, Router, k Queueing Stations, and a Sink node onto the canvas.
- Connect them in this order:
- Source → Router → (Server1, Server1, Server1, …) → Sink
Step 2. Create Classes representing different type of requests
- Define distribution (e.g., exponentital). Choose λ, mean.
Step 3. Define Routing Strategies for each class
Step 3. Define Performance Indeces (what you want to measure)
Step 4. Run the simulation
🧩 Example - Middleware Node with Prioritization and Network Link
🎯 Goal
In this experiment, we model a middleware system that receives messages of two types—High-priority (alerts) and Low-priority (telemetry)—and forwards them across a shared network link.
We want to study how priority scheduling and message filtering affect throughput and response time for each class.
- Processing: models message parsing or validation
- ClassSwitch: filters 20% of Low messages
- Prioritization: optional lightweight classification stage
- NetworkLink: single-server queue representing the network; uses class-based priority scheduling
- Sink: collects transmitted messages
- DropSink: collects filtered-out Low messages
⚙️ Model Configuration
| Parameter | High Priority | Low Priority | Description | 
|---|---|---|---|
| Arrival rate (λ) | 2 msg/s | 8 msg/s | Incoming traffic intensity | 
| Processing time | 0.020 s | 0.020 s | Exponential (shared CPU) | 
| Filtering probability | 1.0 | 0.8 keep / 0.2 drop | Implemented via ClassSwitch | 
| Prioritization delay | 0.001 s | 0.001 s | Deterministic | 
| NetworkLink service time | 0.008 s | 0.032 s | Deterministic (based on packet size) | 
| Scheduling policy | Priority (non-preemptive) | — | High = 1 (highest), Low = 2 | 
📊 Performance Indices to Collect
For both classes and the system:
- Throughput [msg/s]
- Response time [s]
- Utilization [%] (of Processing and NetworkLink)
- Number of customers [msg] in queue and in service
- Drop rate [%] for Low messages (from DropSink throughput)
🔬 Experiments to Perform
- 
Baseline scenario - λ_H = 2/s, λ_L = 8/s
- Measure throughput and response times.
- Observe how High messages experience consistently low delay.
 
- 
Increased load - Increase both arrival rates (e.g., λ_H = 5/s, λ_L = 20/s).
- Observe queue buildup at NetworkLink.
- High messages still get through quickly; Low messages queue longer or drop.
 
- 
Remove filtering - Change Low’s filtering probability to 1.0 (no drops).
- Compare new response times—expect larger delays due to congestion.
 
⚙️ Example 5 — Parallel Multicore and Multithreaded Server
🎯 Goal
In this advanced example, we model a parallel server that executes multithreaded tasks on multiple CPU cores, all sharing a common memory bus.
This setup represents a multicore processor where threads run concurrently but contend for shared memory bandwidth.
The goal is to evaluate the scalability of the system and observe how resource contention limits performance.
🧩 System Architecture
Tasks → Fork (Create threads) → ([Core 1] [Core 2] [Core 3] [Core 4]) → MemoryBus →Join
Each job (user request) is decomposed into multiple threads, which are processed on different cores.
All threads share a single MemoryBus before synchronizing at the Join node.
⚙️ Model Configuration
🧠 Workload
| Parameter | Value | Description | 
|---|---|---|
| Workload type | Closed classes | Fixed number | 
| Population (N) | 10 | Active jobs in the system | 
🧩 Routing Configuration
| From → To | Probability | Notes | 
|---|---|---|
| Task of type 1 to Core 1 | 1.0 | Pin thread to Core 1 | 
| Task of type 2 to Core 2 | 1.0 | Pin thread to Core 2 | 
| Task of type 3 to Core 3 | 1.0 | Pin thread to Core 3 | 
| Task of type 4 to Core 4 | 1.0 | Pin thread to Core 4 |