Pool¶
pool NAME [ '[' N ']' ] {
cap expr;
block expr;
evict lru; | evict by (expr, …);
preempt none; | preempt lifo; | preempt by (expr, …) [requeue head | tail];
queue fifo; | queue by (key, …);
reserve held;
spill POOL via STAGE (expr) when (expr);
}
A pool holds units (tokens, blocks, slots) for the sessions that
hold it, keeps the prefixes they release as a cache, and
queues the sessions that do not yet fit. Every option is optional.
| Option | Argument types | Default | What it sets |
|---|---|---|---|
cap |
const |
inf |
capacity in units |
block |
const |
none | allocation and caching granularity |
evict |
lru | by (expr, …) |
lru |
which cache entry goes first |
preempt |
none | by (expr, …) [requeue head \| tail] | lifo |
none |
what a failed grow does: whom it preempts, and where the victim goes back |
queue |
fifo | by (expr, …) |
fifo |
admission order |
reserve held |
— | off | a hold's unallocated reserve counts against later admissions |
spill |
pool, stage, expr, expr |
none | evicted prefixes are written to a tier |
If either units or reserve is independent of deployment state and
exceeds capacity after block rounding, the hold is rejected and its session ends. State-dependent demand waits for admission and is reported as
over_cap if it still exceeds capacity
at the end of the run.
cap¶
Capacity in units. Cached prefixes never block an admission: they are evicted to make room.
block¶
Allocations round up, and cache entries are kept and evicted, in blocks of this many units. An entry is evicted from its tail, block by block.
evict¶
| Form | Order |
|---|---|
lru |
release time, then release order |
by (k1, …) |
ascending keys, then release order |
Keys are evaluated per cache entry at the Evict moment and may read size,
age, last and waiting (context variables), and the entry's
session's attributes: current while the session lives, as they were at its
last release after it has ended. The smallest key goes first.
preempt¶
What a grow (or a growing run) does when the pool has no room.
| Form | Behaviour |
|---|---|
none |
Wait for room, then resume. |
by (k, …) |
Preempt the candidate with the least keys, compared in order; ties go to the last admitted. |
lifo |
Shorthand for by (-admission) requeue head: preempt the last admitted. |
Candidates are holders resident in the stage whose memory is this pool.
If no stage names it as memory, candidates are holders with an active hold
scope. Leases are not candidates; the grower can be its own victim.
The victim leaves its stage, releases its hold and caches its computed prefix.
It re-enters the hold's first pool's queue to execute the hold again with
computed set to the position reached. requeue head (the default) places
it first, ahead of policy keys. requeue tail treats it as a newcomer:
FIFO places it last, while queue by places it by its keys, with waited
reset to 0.
Keys are read at the Victim moment, for each candidate: its visible
attributes, admission (its place in the candidates' admission order: the
engine's serving order, or the order the pool admitted its holders),
decoding (1 for a decoding resident) and position (the position its hold
has computed on the pool, context variables), constants, now
and pool and stage queries. A key may not draw, read budget_left(…), or
read computed, which is the position at the session's last preemption,
not where the candidate is now.
For a priority queue where larger priority values lose first:
Here priority and arrival time t0 are attributes set by the program.
queue¶
by reevaluates keys for every waiting hold before each admission attempt,
compares them lexicographically, and breaks ties by enqueue order. One key is
a list of one. waited supplies seconds since the current hold entered the
queue; it resets on re-entry. Keys may read visible session attributes,
now and pool/stage queries, and may not draw.
A preempted hold re-enters at the head, ahead of policy keys, unless its pool
says requeue tail. Only the selected
hold is tried: failure to fit blocks the rest. FIFO keeps enqueue order.
Selection occurs at admission attempts; there is no implicit aging timer.
For a stage-bound pool, budget_left(stage) reads the budget remaining for
that admission, and the policy is evaluated again after every admission.
For priority lanes with a three-second aging threshold:
This admits immediate requests, then aged long requests, then short requests,
then other long requests. serial orders requests within a lane in this
single-hold workload.
reserve held¶
By default, reserve is checked only at admission. With reserve held,
each live hold's unallocated reservation also counts against later
admissions and growth by other holds:
where \(a_i\) is allocated units and \(r_i\) is the larger of units and
reserve, evaluated at admission and rounded up to blocks. A new reservation \(r\) requires
\(\mathrm{used}+\sum_i u_i+r\leq\mathrm{cap}\). Growth by \(d\) requires
\(\mathrm{used}+\sum_{i\ne\mathrm{grower}}u_i+d\leq\mathrm{cap}\): the growing
hold can use its own reservation.
Each hold counts separately, including nested holds. Reservations end with
the hold's scope, release or preemption; leases retain none. Cached
prefixes are evicted when units are allocated, not when reserved. A hold
that grows beyond its reservation follows the pool's preempt rule.
admit via¶
The pool option admit via ENGINE; is refused. Who admits a pool is said in
its on: declare the capacity on the engine and write pool NAME on ENGINE,
pool NAME on ENGINE.DEVICE for the device's, or NAME[N] cap c; in the
engine for N queues it admits (Engine).
A pool on an engine is served in each iteration's admit waiting, after the
running requests have taken their tokens, while budget is left; without one,
admission is attempted whenever the simulation settles an event.
budget_left(ENGINE) is meaningful in the hold's header. An engine that
serves several pools tries them in declaration order.
With exclusive prefill, selected prefills stop further admission;
a fitting waiting prefill can displace tentative resident decodes. Its
header sees the full token budget, since the cancelled decodes consume none.
The usual fit, budget-exhaustion and preemption gates still apply.
spill¶
| Argument | Type | Moment | Description |
|---|---|---|---|
TIER |
pool |
Where the evicted prefix is written. | |
LINK |
stage |
The stage the write takes time on. | |
work |
expr |
Evict |
Work the write puts on LINK. |
pred |
expr |
Evict |
The prefix is spilled when this is non-zero. May read size. |
Examples¶
A complete program:
fn main() {
pool reqs { cap 2; }
stage svc : delay;
workload {
arrive batch(3);
}
server {
set t0 = now;
hold reqs (cost(reqs, 1)) { run svc (cost(svc, 2)); }
observe response = now - t0;
}
}
Save as model.sq, then run: