Admit workers from the CPU inventory
Inspect available CPU resources and run deterministic native simulations within admitted limits.
// Uses Philox addressed sampling with explicit seed and draw identities.
// CPU comparison: ordinary sequential loop, one worker, and multiple workers.
// Run: aner run examples/parallel/cpu_inventory_simulation.aner --max-steps 500000000
// Standalone timings are one trial; run_cpu_cases.py repeats and varies execution order.
import aner.parallel
import aner.random
// Warehouse planning: replay independent demand years for one stocked SKU.
// Synthetic inputs represent days and integer stock units, not calibrated sales data.
// Parameters: years per task, days per year, supplier lead days, reorder point
// (units), order-up-to level (units), and explicit master demand seed.
fn simulate_inventory(parameters: List, iteration: Int64, seed: Int64) -> List {
let years = cast<Int64>(parameters.get(0))
let days_per_year = cast<Int64>(parameters.get(1))
let lead_days = cast<Int64>(parameters.get(2))
let reorder_point = cast<Int64>(parameters.get(3))
let order_up_to = cast<Int64>(parameters.get(4))
// The logical task identity controls demand. The injected executor seed is
// intentionally unused, allowing a normal sequential loop to replay this task.
let demand_seed = cast<Int64>(parameters.get(5)) + iteration
var lost_units = 0
var on_hand_unit_days = 0
var demand_units = 0
var order_count = 0
var year = 0
while year < years {
var on_hand = order_up_to
var on_order = 0
let arrivals = Array<Int64>(lead_days, 0)
var day = 0
while day < days_per_year {
// Orders placed after today's sales arrive at the start of day+lead.
let slot = day % lead_days
let arriving = arrivals.get(slot)
arrivals.set(slot, 0)
on_order = on_order - arriving
on_hand = on_hand + arriving
// A synthetic discrete demand histogram plus calendar effects.
// Two distinct draw IDs per date also distinguish every replay year.
let draw = 2 * (year * days_per_year + day)
let u = random.uniform(seed: demand_seed, draw: draw)
var demand = 12
if u >= 0.15 { demand = 20 }
if u >= 0.50 { demand = 30 }
if u >= 0.80 { demand = 45 }
if u >= 0.95 { demand = 70 }
if day % 7 >= 5 { demand = demand + 8 }
if day >= days_per_year - 35 { demand = demand + 10 }
let promotion = random.uniform(seed: demand_seed, draw: draw + 1)
if promotion < 0.04 { demand = demand + 40 }
demand_units = demand_units + demand
var served = demand
if served > on_hand { served = on_hand }
lost_units = lost_units + demand - served
on_hand = on_hand - served
on_hand_unit_days = on_hand_unit_days + on_hand
// Inventory position includes every replenishment already in transit.
// The ring slot is now empty; its new order is due lead_days later.
let inventory_position = on_hand + on_order
if inventory_position <= reorder_point {
let quantity = order_up_to - inventory_position
arrivals.set(slot, quantity)
on_order = on_order + quantity
order_count = order_count + 1
}
day = day + 1
}
year = year + 1
}
// Columns: mean lost-sales units/year, mean end-of-day stock units,
// mean requested units/year, and mean replenishment orders/year.
return List(Float64(lost_units) / Float64(years),
Float64(on_hand_unit_days) / Float64(years * days_per_year),
Float64(demand_units) / Float64(years),
Float64(order_count) / Float64(years))
}
// Suggested harness inputs: 64 tasks and List(8, 365, 5, 180, 420, 6501).
// Keep lead_days > 0, years > 0, days_per_year >= 35, and
// 0 <= reorder_point < order_up_to when adapting the scenario.
// Change only these settings to try a larger batch or another CPU allocation.
let jobs = 64
let columns = 4
// 0 = automatic admitted CPU allocation; 1 = serial control.
let requested_workers = 0
// Runner rotates 0/1/2 to vary which method executes first.
let benchmark_order = 0
let parameters = List(32, 365, 5, 180, 420, 2026)
fn require(condition: Bool, message: String) -> Unit {
if !condition {
let failure: Result<Bool, String> = Result.err(message)
print(failure.unwrap())
}
}
// NO PARALLELISM: finish one job before calling the same function for the next.
fn measure_sequential(parameters: List, jobs: Int64, columns: Int64) -> List {
let started = Duration.monotonic()
let values = Array<Float64>(jobs * columns, 0.0)
for iteration in Range(jobs) {
let row = simulate_inventory(parameters, iteration, 2026)
for column in Range(columns) {
values[iteration * columns + column] = cast<Float64>(row.get(column))
}
}
let seconds = Duration.monotonic().subtract(started).total_seconds()
return List(values, seconds)
}
// PARALLELISM: the task body is unchanged. The executor collects ordered rows.
// workers=1 is the control; workers=0 selects the machine's admitted allocation.
fn measure_repeat(parameters: List, jobs: Int64, workers: Int64) -> List {
let started = Duration.monotonic()
let results = parallel.repeat(iterations: jobs, task: simulate_inventory,
parameters: parameters, execution: parallel.local(workers: workers), seed: 2026)
let seconds = Duration.monotonic().subtract(started).total_seconds()
return List(results, seconds)
}
// One untimed task exercises the model before the comparison.
let warmup = simulate_inventory(parameters, 0, 2026)
var sequential_run = List()
var one_worker_run = List()
var parallel_run = List()
for phase in Range(3) {
let mode = (benchmark_order + phase) % 3
if mode == 0 { sequential_run = measure_sequential(parameters, jobs, columns) }
if mode == 1 { one_worker_run = measure_repeat(parameters, jobs, 1) }
if mode == 2 { parallel_run = measure_repeat(parameters, jobs, requested_workers) }
}
let sequential_values = cast<Array<Float64>>(sequential_run.get(0))
let one_worker = cast<SimulationResults>(one_worker_run.get(0))
let concurrent = cast<SimulationResults>(parallel_run.get(0))
let sequential_seconds = cast<Float64>(sequential_run.get(1))
let one_worker_seconds = cast<Float64>(one_worker_run.get(1))
let parallel_seconds = cast<Float64>(parallel_run.get(1))
// Verification and printing happen after all timers stop.
require(one_worker.rows() == jobs && concurrent.rows() == jobs &&
one_worker.cols() == columns && concurrent.cols() == columns &&
one_worker.workers() == 1, "Incomplete results or incorrect serial allocation")
require(sequential_seconds > 0.0 && one_worker_seconds > 0.0 && parallel_seconds > 0.0,
"Clock resolution is insufficient for this batch")
for iteration in Range(jobs) {
require(one_worker.seed(iteration) == concurrent.seed(iteration), "Task seed mismatch")
for column in Range(columns) {
let expected = sequential_values[iteration * columns + column]
require(expected == one_worker.value(iteration, column) &&
expected == concurrent.value(iteration, column), "Output differs across execution methods")
}
require(concurrent.value(iteration, 0) >= 0.0 && concurrent.value(iteration, 1) >= 0.0 &&
concurrent.value(iteration, 2) > 0.0 && concurrent.value(iteration, 0) <= concurrent.value(iteration, 2) &&
concurrent.value(iteration, 3) > 0.0, "Invalid inventory metrics")
}
print("case=cpu_inventory_simulation")
print("jobs=" + String(jobs))
print("columns=" + String(columns))
print("workers=" + String(concurrent.workers()))
print("verified=true")
// Caller elapsed times include allocation, CPU context startup and result collection.
print("sequential_seconds=" + String(sequential_seconds))
print("one_worker_seconds=" + String(one_worker_seconds))
print("parallel_seconds=" + String(parallel_seconds))
print("speedup=" + String(sequential_seconds / parallel_seconds))
print("executor_speedup=" + String(one_worker.seconds() / concurrent.seconds()))
for column in Range(columns) {
print("output_" + String(column) + "_mean=" + String(concurrent.mean(column)))
}
print("summary_json=" + concurrent.summary())
print("one_worker_summary_json=" + one_worker.summary())
Make it your experiment.
With Aner 0.0.3 or newer installed, save this program as examples/parallel/cpu_inventory_simulation.aner inside a folder for your experiment. Open a terminal in that folder, then check and run the program.
aner check examples/parallel/cpu_inventory_simulation.aner
aner run examples/parallel/cpu_inventory_simulation.anerThe aner command must be on your PATH. Follow the installation guide if your terminal cannot find it.
Requires the current native Aner runtime with the documented API. Keep the source directory structure and review the guide for data, optional map packs and output files before running. Earlier release binaries may predate these features.
Bundled datasets retain their own source attribution and license notices. Example outcomes are not comparative benchmarks or evidence of clinical validity.
Dataset sources & attribution