Whose Turn to Think
A plan that takes thirty-six ticks
Every model call in this volume so far was made by hand. A bench picked a villager, picked a tick, ran the world to that boundary, asked, and judged the answer when it came back. Eight chapters of villagers thinking, and not one of them decided whose turn it was. The daemon has to, and the arithmetic it has to decide under is the arithmetic chapter 90 put beside the tick: a plan on the small model came back in 328 milliseconds when it stopped at 14 tokens, and the plans this volume has been asking for since, with a schema on the request and a situation to embed first, come back in three to four seconds. At ten ticks a second that is thirty to forty ticks a plan, and a tick is a hundred milliseconds that nothing inside it may spend on a socket.
Three people and one worker asking one question at a time is a round of about a hundred and ten ticks if every plan takes thirty-six. Twenty-four people is nine hundred. The village this volume runs is three, and the reason the number is in the file rather than the code is that the arithmetic is meant to bite at any size: what decides whether a village can think is not how many people are in it but how many ticks pass between one villager's turns, and that number has a cap on it already, because the sixth check refuses a proposal older than two hundred ticks. A scheduler that lets a villager's turn come round every three hundred ticks is a scheduler buying answers the seam throws away. The scheduler wakes outside the tick, on a cadence in ticks, with one worker, and it never makes a tick wait.
So the thing this chapter builds is small and its rules are few. It wakes on a plain count of ticks, at the daemon's own loop boundary, which volume 7 named as the one place where no phase is part way through, no view is open and no creature is half stepped, and which volume 8 hung its embedding job on. It orders the villagers by a score a reader can add up, hands the first of them to a worker with a send that gives up if the worker is busy, and returns. The worker calls the models; the answer comes back whenever it comes back; the boundary that finds it runs the six checks over the world as it stands at that boundary, and an accepted step goes into the villager's one-slot mailbox, values and not pointers, replacing what is there. The tick side takes it or does not.
Every decision a villager makes is a decision about a world that has already moved on, and the sixth check is what makes that safe. That sentence is the design and not a defect of it: a question asked at tick 2400 is answered at tick 2437, and the reading the checks are handed is 2437's, so a plan whose premises have gone is refused and the habit has the ticks. Three things the scheduler refuses fall out of the same arithmetic, and each is run below. A villager already with a worker is not queued again. A request that has waited in the queue past the staleness cap is dropped without a call, because a decision about a world that is gone is worse than no decision and costs the same. And a village cannot be accelerated: when the world is run faster than a call can land inside the cap, the scheduler says so on its own line and stops asking, and every villager runs the utility rule from there.
A hundred and forty-three conversations the memory allows
The worker count is computed before it is chosen, and the computation is volume 8's.
A model held at a context costs three sums: the weights, one key-and-value cache a
conversation in flight, and what the server keeps beside them, which was weighed
against this server at this context and committed. What is left of the ceiling
divided by one cache is how many further conversations fit, and lang.Plan.More
is that division.
$ go run ./cmd/turns -mode budget
turns: how many workers the budget allows, worked from volume 8's three sums
the ceiling 8,000,000,000 bytes
the context 4096 tokens, off configs/models.json
a cache number 2 bytes
model weights one cache overhead all three a cache more
small 397,807,936 50,331,648 320,032,704 768,172,288 50,331,648
hero 4,683,073,952 234,881,024 163,753,568 5,081,708,544 234,881,024
the small model resident alone, one conversation in flight
what it costs 768,172,288
left over 7,231,827,712
further conversations that fit 143
so the memory allows 144 workers on the small model, if memory were the only thing
both resident, one conversation of each in flight, which this server never does
what it costs 5,849,880,832
left over 2,150,119,168
further small conversations that fit 42
and the number the arithmetic has no term for: the server holds ONE model resident at
a time, so a second worker asking for the embedding model while the first is on the
small model evicts the small model and pays its load again when the first comes back
to it. Memory allows many; residency allows one model; whether a second worker buys
anything at all is not in this arithmetic and is measured by -mode live -workers 2
the file says 1 worker(s), and the measurement is what the file was set from
The small model resident alone costs 768,172,288 bytes of an eight-thousand-million
ceiling, and every further conversation of it is one more cache of 50,331,648: a
hundred and forty-three of them fit in what is left. Even with the hero model
resident beside it, which this server never allows, forty-two would. Memory is not
the constraint and the arithmetic says so in one division. What the arithmetic has
no term for is the rule volume 8's contract runs the server under,
OLLAMA_MAX_LOADED_MODELS=1: one model in memory, and a call on any other
evicts it. A plan is an embedding and then a generation, on two different models,
so one worker changes the resident model twice a plan whatever the count; a second
worker asking for the embedding model while the first is generating evicts the
small model under the first and makes it load again, and the load is paid on the
way in and on the way back. The count that memory allows is not the count residency
allows, and the file says one. Whether a second one buys anything is measured
below, in the worked failure, with the result reported whichever way it went.
The other number the cadence is set against is the round trip. A wake every fifty ticks is five seconds at the watched pace, and a plan lands in three to four, so the worker is free again at nearly every wake and the queue is handed one request a wake. A reflection is dearer: chapter 94 grouped its calls by model into three changes of resident and measured 7.476 seconds for one, which is seventy-five ticks, so a wake falls while it is in flight and hands nothing, and the villager behind it waits a hundred ticks for a turn instead of fifty. Three reflections owed at once, which is what a village founded on one tick owes every two thousand ticks of its life, are two hundred and forty ticks of worker, and the third in line is dropped unasked before it is reached. The recording below has that happen twice.
Every number here is in ticks, at ten ticks a second, because that is the pace every figure on this page was taken at; the same seconds in a world run a hundred times faster are a hundred times more ticks, and the last section runs that.
The budget first. What is left of the ceiling with the small model resident is
8,000,000,000 minus 768,172,288, which is 7,231,827,712. One more conversation is
one more cache, 50,331,648 bytes. Divide and drop the remainder: 143. That is
More, and it is the number of workers the memory would allow beyond
the first; residency allows one model, so the count is one.
The round next. One plan is a call of about 3.6 seconds, which is 36 ticks. Three villagers served one at a time is 3 × 36 = 108 ticks a round if the worker is never idle. The cadence is 50, so with one request handed a wake the round is really 3 × 50 = 150 ticks, and each villager's plan, which runs 60 ticks and then runs out, leaves it on the habit for about 90 of every 150. The cap is 200: a request queued at a wake and still not handed four wakes later has waited 200 ticks, and at the fifth it is 250, which is over the cap and dropped. A reflection is about 80 ticks, so two reflections ahead of a third request are 160 ticks of waiting before the third's own wake comes, and the recording shows the third dropped at 250.
The score last. A villager's place at a wake is the sum of three terms turned into ticks: how long its plan has been run out for; twenty ticks for every memory laid down for it since its last answer; and four hundred ticks if a person has come into its sight since. At wake 15 in the recording, Ander's plan had been out for 5 ticks, one memory had been laid down, and a stranger was in sight: 5 + 20 + 400 = 425. Halla's reflection had been waiting 153 ticks with four memories: 153 + 80 = 233. Mose: 121 + 0 = 121. Ander first, and the tie that never came would have gone to the lower identity.
more = (ceiling − small) ÷ cache, remainder dropped
score = stale + E × events + F × faced, best first, ties by identity
dropped when now − queued > cap; stopped when landed − asked > cap
A wake every fifty ticks
The scheduler is a new file in the village package, and the package's rule holds: it imports the coordinate package and the configs and nothing else, opens no connection, calls no model, reads no clock and draws no number. What it knows about the world it is told at the boundary, as counts and flags; what it hands a worker is a value; what comes back crosses a channel as a value. Its settings are a fifth block in the file the retrieval, reflection, planning and seam blocks already share.
// configs/thought.json — extend: a fifth block after seam
"scheduler": {
"every": 50,
"event": 20,
"faced": 400,
"workers": 1
}
// internal/village/schedule.go — create
package village
// Scheduling is the block of configs/thought.json the scheduler runs
// by: the cadence, in ticks; what one memory laid down since a villager
// last thought is worth against its staleness, in ticks; what a person
// standing in front of it is worth, in ticks; and how many workers ask
// on its behalf.
type Scheduling struct {
Every int `json:"every"`
Event int `json:"event"`
Faced int `json:"faced"`
Workers int `json:"workers"`
}
// Job is what a request is for. A plan is one embedding and one
// generation; a reflection is chapter 94's cycle, a generation, then
// the questions embedded, then a generation a question, then every
// accepted thought embedded; a line is one generation on the hero
// model, which is the only request that changes which model is
// resident twice on its own.
type Job string
const (
PlanJob Job = "plan"
ReflectJob Job = "reflection"
LineJob Job = "line"
)
// Priority is the three terms a villager is ordered by at a wake and
// the score they come to. Stale is how many ticks its plan has been
// ...
type Priority struct {
Stale int
Events int
Faced bool
Score int
}
// Request is one turn to think, as the scheduler hands it to a worker:
// whose, what for, the tick it was queued at, the tick it was handed
// at, the priority it was ordered by, and what the worker needs to
// know about the world at the tick it was handed, taken at that
// boundary and carried as values. A worker reads a Request and never
// the roster.
type Request struct {
Who sim.EntityID
Name string
Job Job
Queued int
Asked int
Priority Priority
Ask Asking
}
Two ticks on a request and not one, and the difference is the second refusal.
Queued is when the villager entered the queue and Asked is
when a worker was handed it; the prompt is rendered from the world at
Asked, and the sixth check ages a proposal from it, but the queue ages
a request from Queued, because a villager that has been waiting for its
turn since a wake four cadences ago is a villager whose reasons for the turn are
gone. Asking is the world as the worker gets it: the persona, the cell
and home, who the eyes returned at this boundary, the villager's rows, and a fresh
copy of the vectors. Every field is a value; the worker never holds a pointer into
the roster, for the reason chapter 91 gave for the mailbox.
// internal/village/schedule.go — add
// Priority is the three terms for one villager at tick now.
func (s *Scheduler) Priority(id sim.EntityID, now int) Priority {
st := s.st[id]
p := Priority{Events: st.events, Faced: st.facing && st.facedAt > st.landed}
if now > st.until {
p.Stale = now - st.until
}
p.Score = p.Stale + s.Event*p.Events
if p.Faced {
p.Score += s.Faced
}
return p
}
// rank sorts requests best first: a line owed to a person ahead of
// everything, then by score, highest first, and on a tie by identity,
// lowest first, so that the order is total and two wakes over the
// same facts are the same wake.
func (s *Scheduler) rank(now int) {
for i := range s.pending {
r := &s.pending[i]
r.Priority = s.Priority(r.Who, now)
if r.Job == PlanJob && s.st[r.Who].owed {
r.Job = ReflectJob
}
}
sort.SliceStable(s.pending, func(i, j int) bool {
a, b := s.pending[i], s.pending[j]
if (a.Job == LineJob) != (b.Job == LineJob) {
return a.Job == LineJob
}
if a.Priority.Score != b.Priority.Score {
return a.Priority.Score > b.Priority.Score
}
return a.Who < b.Who
})
}
The faced term is the one that had to be measured before it was right. The first draft scored a person in sight as four hundred ticks at every wake for as long as the person stood there, and in the first live run of it Halla, who could see the stranger from across the valley, was handed every turn from tick 3200 to the end while Mose's plan waited 313 ticks and Ander's 256. A term that never clears hands one villager the worker. What the term means now is a reason to think once: it is on from the tick a person came into sight until the villager's next answer lands, and off after that until the person leaves and comes back. The test below holds that, and the tie-break is the last line of the sort: two villagers at the same score are placed by identity, lowest first, so the order is a function of the facts and the queue at every wake replays.
// internal/village/schedule.go — add
// Wake runs one wake at the boundary of tick now, and hands off. It
// does four things, in this order, and then returns whatever the
// workers are doing.
// ...
func (s *Scheduler) Wake(now int, prepare func(Request) Request) Wake {
w := Wake{Tick: now}
s.Wakes++
kept := s.pending[:0]
for _, r := range s.pending {
if now-r.Queued > s.Stale {
s.Dropped++
w.Dropped = append(w.Dropped, r)
continue
}
kept = append(kept, r)
}
s.pending = kept
// A stopped scheduler queues nobody and hands nothing; the wake
// ranks the villagers as it would have, so a bench can print what
// the order would be, and says why nothing is done about it.
if s.Stopped != "" {
// ...
w.Said = "stopped: " + s.Stopped
return w
}
queued := map[sim.EntityID]bool{}
for _, r := range s.pending {
queued[r.Who] = true
}
for _, id := range s.people {
st := s.st[id]
if st.waiting || queued[id] {
continue
}
job := PlanJob
if st.owed {
job = ReflectJob
}
s.pending = append(s.pending, Request{Who: id, Name: st.name, Job: job, Queued: now})
}
s.rank(now)
handed := 0
offering := true
kept = s.pending[:0]
for _, r := range s.pending {
if offering && s.busy < s.Workers && !s.st[r.Who].waiting {
r.Asked = now
if prepare != nil {
r = prepare(r)
}
select {
case s.Inbox <- r:
s.busy++
s.Handed++
handed++
s.st[r.Who].waiting = true
w.Handed = append(w.Handed, r)
// ...
continue
default:
offering = false
}
}
kept = append(kept, r)
// ...
}
s.pending = kept
// ...
return w
}
Read the four parts in order, because the order is the whole of the first two
refusals. Requests that have waited past the cap are dropped before anything else,
unasked, and counted. Every villager that is neither in the queue nor with a worker
is queued, so a villager with a worker is skipped here and stays skipped until its
answer lands. The queue is ranked by the terms as they stand now, so a villager's
place moves with what has happened to it since the last wake, not with when it
entered. And the head is handed off with a select whose
default is to keep the request queued: Inbox is one slot a
worker, a put into an empty slot succeeds at once, and a put into a full one is not
made. Nothing in this function can wait, and the loop that calls it between two
ticks is the loop that runs the ticks.
// internal/village/schedule.go — add
// Take drains the landings channel with receives that give up, hands
// back every answer that has arrived and is due at this boundary, and
// keeps the rest for a later one. For each taken it records the
// ...
func (s *Scheduler) Take(now int, held []Landing) (taken, kept []Landing) {
for {
select {
case l := <-s.Landings:
held = append(held, l)
continue
default:
}
break
}
for _, l := range held {
if l.LandAt > now {
kept = append(kept, l)
continue
}
taken = append(taken, l)
s.busy--
s.Landed++
st := s.st[l.Request.Who]
st.waiting = false
st.landed = now
st.until = now
st.events = 0
if l.Request.Job == ReflectJob {
st.owed = false
}
if trip := now - l.Request.Asked; trip > s.Stale {
s.Late++
if s.Stopped == "" {
s.Stopped = fmt.Sprintf("at tick %d an answer asked for at tick %d landed %d ticks later, and the cap is %d: at this pace no answer can land in time, so the village runs the utility rule from here",
now, l.Request.Asked, trip, s.Stale)
s.pending = nil
}
}
}
return taken, kept
}
The third refusal is one comparison at the landing. An answer that took longer to
come back than the cap is an answer the sixth check refuses on arrival, and a
scheduler that has watched one do so knows what every later call at this pace would
do, so it writes down why and stops: the queue is emptied, no wake queues anybody,
and the habit has every tick. LandAt is for a replay, where the answer
is read off a file in a moment and has to be held until the boundary it landed on
when it was made; a live worker leaves it at nought and the answer is taken at the
first boundary after it arrives. What the scheduler does not do at a landing is
judge: the six checks need a reading of the world, the village imports no law, and
the boundary that called Take is the one that builds the reading and
runs them.
$ go test ./internal/village/ -run 'TestAWakeReturnsWhetherOrNotAWorkerIsThere|TestTheOrderIsByScoreThenIdentityAndReplays|TestAVillagerWaitingIsNotQueuedAgain|TestARequestPastTheCapIsDroppedUnasked|TestALateAnswerStopsTheAsking|TestAnAnswerIsTakenAtItsBoundaryAndNotBefore|TestAPersonInSightIsAReasonToThinkOnceAndNotAtEveryWake|TestADeadVillagerIsStruckFromTheQueue|TestTheShippedSchedulerIsACadenceTwoWeightsAndOneWorker|TestTheVillageStillImportsNeitherTheModelClientNorTheStore' -v
=== RUN TestTheVillageStillImportsNeitherTheModelClientNorTheStore
reflect_test.go:259: every file of the package read, and none of them names the model client or the store
--- PASS: TestTheVillageStillImportsNeitherTheModelClientNorTheStore (0.00s)
=== RUN TestAWakeReturnsWhetherOrNotAWorkerIsThere
schedule_test.go:65: no reader: one request into the slot, then a wake that handed nothing and said "every worker is busy; nothing handed"; a reader: the next queued at 150 handed at 250
--- PASS: TestAWakeReturnsWhetherOrNotAWorkerIsThere (0.00s)
=== RUN TestTheOrderIsByScoreThenIdentityAndReplays
schedule_test.go:100: faced 450, three events 110, stale alone 50; on a tie of 50, bodies 1, 2, 3
--- PASS: TestTheOrderIsByScoreThenIdentityAndReplays (0.00s)
=== RUN TestAVillagerWaitingIsNotQueuedAgain
schedule_test.go:128: body 1 handed at 150 is 'waiting' at 200 and out of the queue, which holds the other 2
--- PASS: TestAVillagerWaitingIsNotQueuedAgain (0.01s)
=== RUN TestARequestPastTheCapIsDroppedUnasked
schedule_test.go:152: two queued at 150, kept at 350, dropped unasked at 400 and queued afresh from 400, and no worker ever saw either
--- PASS: TestARequestPastTheCapIsDroppedUnasked (0.00s)
=== RUN TestALateAnswerStopsTheAsking
schedule_test.go:173: asked at 150, landed at 351; the wake at 400, with a worker reading, stopped: at tick 351 an answer asked for at tick 150 landed 201 ticks later, and the cap is 200: at this pace no answer can land in time, so the village runs the utility rule from here
--- PASS: TestALateAnswerStopsTheAsking (0.02s)
=== RUN TestAnAnswerIsTakenAtItsBoundaryAndNotBefore
schedule_test.go:191: held at 160, taken at 190, and the worker counted busy until it was
--- PASS: TestAnAnswerIsTakenAtItsBoundaryAndNotBefore (0.01s)
=== RUN TestTheShippedSchedulerIsACadenceTwoWeightsAndOneWorker
schedule_test.go:212: every 50 ticks, a memory worth 20 ticks, a person worth 400, 1 worker, under a cap of 200
--- PASS: TestTheShippedSchedulerIsACadenceTwoWeightsAndOneWorker (0.00s)
=== RUN TestAPersonInSightIsAReasonToThinkOnceAndNotAtEveryWake
schedule_test.go:241: in sight from 120: 450 at 150; answered at 160: 40 at 200 with the person still there; back into sight at 220: faced again at 250
--- PASS: TestAPersonInSightIsAReasonToThinkOnceAndNotAtEveryWake (0.01s)
=== RUN TestADeadVillagerIsStruckFromTheQueue
schedule_test.go:257: body 2 retired at 150 is in no order and no queue at 200; the queue holds body 3
--- PASS: TestADeadVillagerIsStruckFromTheQueue (0.00s)
PASS
ok theworld/internal/village 0.069s
The first of the nine new tests is the one the rule is held by, and it is a test
that would hang rather than fail: it opens a scheduler with nobody reading the
inbox, calls a wake, and waits two seconds for the call to come back. A wake that
waited for a worker would never come back, the test would time out, and
go test would report it, which is the one way a test can hold that
something does not block. With one slot and nobody reading, the first wake puts one
request in the slot and returns; the second finds the slot full, hands nothing,
says so, and returns; then a reader takes the slot's request, answers it, and the
next wake hands the head of the queue. The other eight are the order, the three
refusals, the faced term clearing, a dead villager struck off, the file's numbers
held against the cap, and the package's imports walked again with the tenth file
in it.
Sixteen answers off the recording
The scheduler is driven by a loop, and the loop is the daemon's from volume 7 with the boundary filled in. Between every two ticks, in this order: the rows the witnesses closed are weighed, numbered and counted to the scheduler; who has a stranger in sight and whose reflection has come due are told to it; every answer that has landed is taken and judged over a reading of the world built now; every running plan posts its step's intent; and, on the cadence, the wake. Then the tick. Nothing in that list waits for a worker, and the worker is the only thing that waits for a model.
// cmd/turns/loop.go — the boundary of tick now, elided to its order
func (l *loop) boundary(now int) error {
rows, laid, err := l.s.drain(l.w)
// ... the witnesses' rows, weighed and numbered
// ...
for id, n := range laid {
l.sch.Noticed(id, n)
}
// ...
for _, p := range l.w.people {
if p.Body.Dead {
// ... l.sch.Retire(p.Body.ID) ...
continue
}
l.sch.Facing(p.Body.ID, now, l.w.faced(p.Name, now))
if l.st.r.Due(p.Founded, now) {
l.sch.Owe(p.Body.ID)
}
}
// ...
taken, l.held = l.sch.Take(now, l.held)
for _, ld := range taken {
if err := l.land(ld, now); err != nil {
// ... the six checks over a reading taken now
return err
}
}
for _, p := range l.w.people {
if r, ok := l.plans[p.Body.ID]; ok {
if err := l.dispatch(p, r, now); err != nil {
// ... one intent a tick into the mailbox
return err
}
}
}
if l.sch.Due(now) && !l.closing {
w := l.sch.Wake(now, l.prepare(now))
l.showWake(w)
// ...
}
return nil
}
// cmd/turns/worker.go — one worker, reading a request and two channels and nothing else
func worker(n int, st settingsOf, s *village.Scheduler, o oracle, made *ledgerOf, resident lang.Role) {
for r := range s.Inbox {
l := village.Landing{Request: r}
t := time.Now()
calls := &calls{resident: resident}
var err error
switch r.Job {
case village.PlanJob:
err = plan(st, r, o, calls, &l)
case village.ReflectJob:
err = reflect(st, r, o, calls, &l)
case village.LineJob:
err = line(st, r, o, calls, &l)
default:
err = fmt.Errorf("no job called %q", r.Job)
}
resident = calls.resident
l.Loads = calls.loads
if err != nil {
l.Err = err.Error()
}
made.put(r, taken{worker: n, took: time.Since(t), gens: calls.gens, embeds: calls.embeds, vecs: calls.vecs, thoughtVecs: calls.thoughtVecs, landAt: o.landAt(r)})
l.LandAt = o.landAt(r)
s.Landings <- l
}
}
The worker's three jobs are the last four chapters' cycles moved into a goroutine. A plan embeds the situation and, while the embedding model is the one resident, every row of the villager's stream that has no vector yet, then retrieves, renders and generates: two changes of model a plan, and the rows laid down since the last turn are embedded on the way, grouped with the embedding the plan makes anyway, which is finding 19 applied to the queue. A reflection is chapter 94's four phases, with the citation check run in the worker over the stream as it stood when the request was handed, and the accepted thoughts sent back as events for the boundary to weigh and number. A line is one generation on the hero model. The oracle behind all three is a live server or the committed recording, the same code either way, and the recording is what the rest of this section runs on.
The recording is one stretch of one village: a thousand ticks from the boundary of tick 2400, which is inside the committed day, at ten ticks a second, with a stranger stood in front of Ander at tick 3100 and every call the worker made written down with the tick its answer was taken at. The day up to 2400 is lived again from the founding, row by row, and the seventy-four rows it lays down are held against the fixture; from 2400 on the village lives a stretch it has not lived before, and every row after the seventy-fourth is this run's own. Sixteen turns, no model server anywhere, and every order, verdict and count below is the engine's.
// exemplars/exemplars.go — the embed line gains the recording, and a name for it
//go:embed chronicle.jsonl hero.header.json small.header.json vectors.json memories.jsonl memory-vectors.json reflections.json plans.json turns.json
var Files embed.FS
// exemplars/turns.go — create
// ...
const Turns = "turns.json"
$ go run ./cmd/turns -mode day | tail -214 | head -14
# who job stale events faced score queued did
1 Halla plan 0 0 no 0 2400 handed: 1 generation(s), 1 embedding(s), 2 load(s) at most
2 Ander plan 0 0 no 0 2400 queued
3 Mose plan 0 0 no 0 2400 queued
tick 2437 Halla's plan landed: asked at 2400, 37 ticks in flight; 1 generation(s), 1 embedding(s), 2 load(s)
Halla stands on 9,3 carrying 106.6876 grams, with 2 Ander in sight; the grounding set was 1 Halla, 2 Ander, 3 Mose
the seam, at tick 2437 over the world as it stands: accepted
2 step(s) spanning 60 ticks: go to 2,7; wait -
tick 2450 wake 2: 1 with a worker, 2 queued after it
# who job stale events faced score queued did
1 Ander plan 50 1 no 70 2400 handed: 1 generation(s), 2 embedding(s), 2 load(s) at most
2 Mose plan 50 1 no 70 2400 queued
3 Halla plan 0 0 no 0 2450 queued
tick 2491 Ander's plan landed: asked at 2450, 41 ticks in flight; 1 generation(s), 2 embedding(s), 2 load(s)
Three villagers enrolled at the same tick with nothing laid down for any of them score nought each, and the queue is Halla, Ander, Mose by identity: the tie-break on the page at the first wake, before any term has had time to move. Halla is handed, priced at one generation and one embedding and two changes of model; her answer lands thirty-seven ticks later, at 2437, and the seam is run at 2437 over a reading in which she is carrying 106.6876 grams and can see Ander, not at 2400. At the second wake Ander and Mose have each been stale fifty ticks and had one memory laid down, seventy each, and Ander's lower identity places him first; Halla's plan is running and her staleness is nought.
$ go run ./cmd/turns -mode day | tail -115 | head -22
tick 2900 wake 11: 1 with a worker, 2 queued after it
# who job stale events faced score queued did
1 Mose reflection 53 0 no 53 2800 handed: 3 generation(s), 4 embedding(s), 3 load(s) at most
2 Halla reflection 0 1 no 20 2900 queued
3 Ander reflection 0 1 no 20 2850 queued
tick 2901 Ander's plan ran out; the habit has the ticks until the next answer
tick 2947 Halla's plan ran out; the habit has the ticks until the next answer
tick 2950 wake 12: 1 with a worker, 2 queued after it
# who job stale events faced score queued did
1 Ander reflection 49 1 no 69 2850 queued
2 Halla reflection 3 1 no 23 2900 queued
3 Mose 103 0 no 103 - waiting
every worker is busy; nothing handed
tick 2979 Mose's reflection landed: asked at 2900, 79 ticks in flight; 3 generation(s), 3 embedding(s), 3 load(s)
question 1: refused at the strict read: lang: qwen2.5:0.5b-instruct-q4_K_M answered with something that is no...
question 2: accepted, at depth 1
row 103 [thought] at tick 2900, weight 6, drawn from [92], a model's words: 92
tick 3000 wake 13: 1 with a worker, 2 queued after it
# who job stale events faced score queued did
1 Ander reflection 99 1 no 119 2850 handed: 3 generation(s), 6 embedding(s), 3 load(s) at most
2 Halla reflection 53 1 no 73 2900 queued
3 Mose plan 21 0 no 21 3000 queued
Tick 2900 is two thousand ticks into three lives founded at 901, so at that boundary all three reflections come due and every queued plan becomes a reflection, priced at three generations and four embeddings and three changes of model. Mose's is handed, because his plan has been out fifty-three ticks and the others' are still running. At 2950 the worker is still on it: the wake ranks the two queued, marks Mose as waiting and out of the queue, and hands nothing, and the line under the table says so. Mose's reflection lands at 2979, seventy-nine ticks after it was asked; the first question's statement did not parse and was refused at the strict read, the second was accepted at depth 1 and appended as row 103, and the statement the model wrote for it is the number 92, which is what a model of this size made of the question and which nothing in the engine reads. At 3000 Ander is handed his, ninety-nine ticks stale; Halla, whose reflection has been queued since 2900, is second; and Mose is back in the queue for a plan.
$ go run ./cmd/turns -mode day | tail -82 | head -19
tick 3099 a stranger, body 4, stood on 6,2, one step along Ander's heading, carrying 100 grams off the flag
tick 3100 wake 15: 1 with a worker, 2 queued after it
# who job stale events faced score queued did
1 Ander plan 5 1 yes 425 3100 handed: 1 generation(s), 3 embedding(s), 2 load(s) at most
2 Halla reflection 153 4 no 233 2900 queued
3 Mose plan 121 0 no 121 3000 queued
tick 3141 Ander's plan landed: asked at 3100, 41 ticks in flight; 1 generation(s), 3 embedding(s), 1 load(s)
2 row(s) of Ander's stream embedded on the way, so every row now has a vector
Ander stands on 7,2 carrying 168.2468 grams, with 1 Halla in sight; the grounding set was 1 Halla, 2 Ander, 3 Mose, 4 a stranger
the seam, at tick 3141 over the world as it stands: accepted
2 step(s) spanning 60 ticks: go to 7,2; wait -
tick 3150 dropped unasked: Halla's reflection, queued at tick 2900, 250 ticks ago, and the cap is 200
tick 3150 wake 16: 1 with a worker, 2 queued after it
# who job stale events faced score queued did
1 Halla reflection 203 4 no 283 3150 handed: 3 generation(s), 8 embedding(s), 3 load(s) at most
2 Mose plan 171 0 no 171 3000 queued
3 Ander plan 0 0 no 0 3150 queued
The stranger is stood one step along Ander's heading at 3099, Ander's eyes return it at the boundary of 3100, and his score is 5 + 20 + 400 = 425 against Halla's 233, which is a reflection that has been queued for two hundred ticks. Ander goes first; that is what the term is for. His answer lands at 3141, and by then his heading has moved on and the stranger is out of his sight while Halla is in it; the grounding set stored with the proposal still carries body 4, because the set is what he could name when he was asked. At 3150 Halla's reflection has been queued 250 ticks, which is over the cap, and it is dropped unasked: no call was made for it, the worker never saw it, and the same wake queues her afresh from 3150 and, the worker being free, hands her at once. The drop cost the village nothing but the bookkeeping and it is what the second refusal is: a request that old is asked again from now, not from then.
$ go run ./cmd/turns -mode day | tail -53 | head -10
tick 3250 dropped unasked: Mose's plan, queued at tick 3000, 250 ticks ago, and the cap is 200
tick 3250 wake 18: 1 with a worker, 2 queued after it
# who job stale events faced score queued did
1 Mose plan 271 0 no 271 3250 handed: 1 generation(s), 1 embedding(s), 2 load(s) at most
2 Ander plan 49 1 no 69 3150 queued
3 Halla plan 21 0 no 21 3250 queued
tick 3280 Mose's plan landed: asked at 3250, 30 ticks in flight; 1 generation(s), 1 embedding(s), 1 load(s)
Mose stands on 6,6 carrying 7.9358 grams, with nobody in sight; the grounding set was 1 Halla, 2 Ander, 3 Mose
the seam, at tick 3280 over the world as it stands: accepted
2 step(s) spanning 60 ticks: go to 6,6; wait -
$ go run ./cmd/turns -mode day | tail -27 | head -6
tick 3352 Mose is dead on 6,6, by the rule that has killed every animal in this book, and is struck from the queue
tick 3375 Ander's plan ran out; the habit has the ticks until the next answer
tick 3392 Halla's plan landed: asked at 3350, 42 ticks in flight; 1 generation(s), 2 embedding(s), 2 load(s)
1 row(s) of Halla's stream embedded on the way, so every row now has a vector
Halla stands on 9,2 carrying 83.9915 grams, with 2 Ander in sight; the grounding set was 1 Halla, 2 Ander, 4 a stranger
the seam, at tick 3392 over the world as it stands: accepted
Mose queued a plan at 3000, waited behind two reflections and a faced villager, and was dropped at 3250 and asked afresh, two hundred and seventy-one ticks stale. He is carrying 7.9358 grams when the answer lands at 3280, on the cell he has stood on since the founding with nothing in sight, and the plan the model wrote for him is to go to the cell he stands on and wait. It is accepted; the six checks have no term for a store. He starves on tick 3352 by the rule that has killed every animal in this book, the boundary finds his body dead and strikes him from the queue, and the last wake of the stretch ranks two people. The scheduler did not kill him. The habit found no plant in sight from 6,6 and spun him in place at the price of a turn a tick, the plans this model wrote kept him where he was, and nothing in the priority asks how much a villager is carrying. That last is a limit of the design as built, and the page says so where the run shows it.
$ go run ./cmd/turns -mode day | tail -19
wakes 20
requests handed to a worker 16
answers landed 16
ticks in flight, on average 48
of those, later than the cap 0
requests dropped unasked 2
plans accepted at the seam 12
plans refused at the seam 1
steps refused when they came due 0
plans that ran out 11
intents posted at the boundary 649
of those, taken out of a slot 648
ticks the villagers ran the habit 2304
memory rows laid down in the stretch 42
rows of the stream still without a vector 5
1000 ticks in 28ms, 35542 ticks a second (measured here; yours will differ)
every order, verdict and count above is the engine's over the recording; the words in the answers are the model's
Twenty wakes, sixteen requests handed and sixteen answers landed, forty-eight ticks in flight on average, two dropped unasked, none later than the cap. Twelve plans accepted, one refused, and the refusal is the strict read: at 2713 the model wrote six steps against a cap of five. Six hundred and forty-nine intents posted at the boundary and six hundred and forty-eight taken out of a slot, the one left being the intent posted at the last boundary with no tick after it. Two thousand three hundred and four villager-ticks on the habit out of a possible three thousand, which is the floor chapter 91 built doing most of the work while the queue does the rest. And the same numbers, at the same ticks, every time this command is run, because every answer lands at the boundary it landed on when the recording was made and the queue is a function of the facts.
Two hundred and fifty-three ticks without a thought
The live path is the same loop against the live models and the database: the day lived into the tables as it goes, the scheduler asking, one worker calling, every landing through the seam and into the ledger, and every duration on the page from here down measured on this machine, an eight-core AMD Ryzen 7 3700X with 30 GB of memory, rootless podman, the models answering on the processor with no graphics card in use; yours will differ, and so will a second run here, because when an answer lands depends on the wall clock and the queue moves with it. What does not differ is what is compared: every count, every verdict and every column of every row but the answer.
The containers are the last four chapters': Ollama on the closed bridge with
OLLAMA_MAX_LOADED_MODELS=1 and the three model files on a named volume,
the Go code in world-go, and the database in world-db,
which starts empty after podman kill and podman start
because its data directory is a tmpfs, and which the live mode refuses if a village
is already settled in it, because the numbers it gives its rows are the numbers an
empty database gives them. The small model is loaded before the clock starts. The
server's API reference is under github.com/ollama/ollama.
$ podman kill world-db && podman start world-db world-db world-db $ podman exec -w /bench world-go go run ./cmd/turns -mode live | tail -244 | head -6 the day lived again from tick 901 to 2400 and into the tables as it went: 74 rows, 74 agree with the fixture row for row, 74 in the table, 74 vectors copied in the small model loaded before the clock starts, at 4096 tokens of context the village at the boundary of tick 2400 Halla body 1 on 9,3 carrying 109.6476 grams, alive, 20 rows in its stream Ander body 2 on 7,2 carrying 180.2103 grams, alive, 33 rows in its stream Mose body 3 on 6,6 carrying 179.7334 grams, alive, 21 rows in its stream $ podman exec -w /bench world-go go run ./cmd/turns -mode live | tail -232 | head -5 tick 2437 Halla's plan landed: asked at 2400, 37 ticks in flight; 1 generation(s), 1 embedding(s), 2 load(s); 3.629s on worker 1 (measured here; yours will differ) Halla stands on 9,3 carrying 106.6876 grams, with 2 Ander in sight; the grounding set was 1 Halla, 2 Ander, 3 Mose the seam, at tick 2437 over the world as it stands: accepted 2 step(s) spanning 60 ticks: go to 2,7; wait - the row appended as proposal 1, accepted $ podman exec -w /bench world-go go run ./cmd/turns -mode live | tail -28 | head -26 wakes 20 requests handed to a worker 16 answers landed 16 ticks in flight, on average 48 of those, later than the cap 0 requests dropped unasked 2 plans accepted at the seam 12 plans refused at the seam 1 steps refused when they came due 0 plans that ran out 11 intents posted at the boundary 650 of those, taken out of a slot 649 ticks the villagers ran the habit 2303 memory rows laid down in the stretch 42 rows of the stream still without a vector 5 generations the server was asked for 22 embeddings the server was asked for 59 calls that found their model not resident 34 memory rows in the table at the end 119 ledger rows, read back ORDER BY id 13 of those, accepted 12 of those, refused 1 1000 ticks in 1m40.003s, 10 ticks a second (measured here; yours will differ) inside the ticks and their boundaries: 134ms in all, 134µs a tick on average, 3.97ms the longest, of the 100ms each tick has on the workers' clock: 4.771s a request on average, 9.562s the longest, over 16 requests
The same seventy-four rows agree with the fixture, and the same village stands at 2400 carrying the same grams, because up to that boundary nothing has happened that did not happen when the day was recorded. From there the run is its own. Halla's first answer took 3.629 seconds on the worker's clock and landed at 2437, which is the tick the recording landed it at too, and on an idle processor this run's sixteen landings fell within a tick or two of the recording's all the way down, so the queue handed the same sixteen requests; a second run here lands them elsewhere and the queue moves with it. The line that is the chapter's claim is the last but one: over a thousand ticks held to ten a second, the ticks and their boundaries spent 134 milliseconds in all, 134 microseconds a tick on average and 3.97 milliseconds at the longest, of the hundred milliseconds each tick has, while a worker beside them spent 4.771 seconds a request on the models and 9.562 on the longest. No tick waited. The thirty-four calls that found their model not resident are the server's own count of evictions, one GET before every call, and they are the price the split is about: two a plan, three or four a reflection, and every one of them a load.
The hero model is the one call in this volume that earns seconds, because a person is standing there, and chapter 101 is where it is asked for a line. What this page measures is what that call does to everybody else. The bench issues one, off a flag, at the boundary of tick 2700, for Ander, as a stand-in for the line: a request of the third kind, put at the head of the queue, served by the same worker on the hero model at twenty-four tokens. Nobody is in the world to have been spoken to, and the answer is kept and not printed.
$ podman kill world-db && podman start world-db world-db world-db $ podman exec -w /bench world-go go run ./cmd/turns -mode live -line 2700 | tail -144 | head -7 tick 2700 a line is owed by Ander: a request for the hero model goes to the head of the queue tick 2700 wake 7: 1 with a worker, 2 queued after it # who job stale events faced score queued did 1 Ander line 0 3 no 60 2700 queued 2 Mose plan 105 2 no 145 2550 queued 3 Halla 54 8 no 214 - waiting every worker is busy; nothing handed $ podman exec -w /bench world-go go run ./cmd/turns -mode live -line 2700 | tail -131 | head -20 tick 2750 wake 8: 1 with a worker, 2 queued after it # who job stale events faced score queued did 1 Ander line 49 4 no 129 2700 handed: 1 generation(s), 0 embedding(s), 2 load(s) at most 2 Mose plan 155 2 no 195 2550 queued 3 Halla plan 37 0 no 37 2750 queued tick 2800 dropped unasked: Mose's plan, queued at tick 2550, 250 ticks ago, and the cap is 200 tick 2800 wake 9: 1 with a worker, 2 queued after it # who job stale events faced score queued did 1 Mose plan 205 2 no 245 2800 queued 2 Halla plan 87 1 no 107 2750 queued 3 Ander 99 4 no 179 - waiting every worker is busy; nothing handed tick 2850 wake 10: 1 with a worker, 2 queued after it # who job stale events faced score queued did 1 Mose plan 255 2 no 295 2800 queued 2 Halla plan 137 1 no 157 2750 queued 3 Ander 149 5 no 249 - waiting every worker is busy; nothing handed tick 2876 Ander's line landed: asked at 2750, 126 ticks in flight; 1 generation(s), 0 embedding(s), 1 load(s); 12.59s on worker 1 (measured here; yours will differ) a line of 24 tokens from the hero model, kept and not printed: nobody is in the world to have been spoken to $ podman exec -w /bench world-go go run ./cmd/turns -mode live -line 2700 | tail -15 | head -2 the line: handed at tick 2750, landed at tick 2876; the next answer from the small model landed at tick 3003 so the village did not think for 253 ticks at 10 ticks a second (measured here; yours will differ)
The line goes to the head of the queue at 2700, above Mose's plan at 145, and finds the worker busy with Halla's plan, so it waits one wake: a line is owed and not hurried, because nothing at the boundary waits. At 2750 it is handed, and the worker is on the hero model for 12.59 seconds, which is the load of 4,683,073,952 bytes of weights that evicts the small model, then twenty-four tokens: 126 ticks at ten a second. The wakes at 2800 and 2850 find the worker busy and hand nothing, and the table says so under Ander's name, marked waiting. At 2800 Mose's plan, queued at 2550, has waited 250 ticks and is dropped unasked, which is the eviction's second cost: a request that would have been served at 2750 has gone stale behind a line. The line lands at 2876; the next wake is 2900, where all three reflections come due, and the next answer from the small model is Mose's reflection at 3003, which paid four loads because the worker came to it holding the hero model. From the line's hand-off at 2750 to that landing is 253 ticks in which nobody in the village was answered by the small model: while somebody is being spoken to, the village stops thinking, and this is the number chapter 101 pays.
The third refusal needs the world run faster than a call can land inside the cap, and volume 7's unattended valley runs a hundred times faster than the watched pace and then some. The bench governs to a thousand ticks a second, so that a tick is one millisecond and a call of four seconds is four thousand ticks, and runs six thousand ticks so that the one answer lands inside the stretch.
$ podman kill world-db && podman start world-db world-db world-db $ podman exec -w /bench world-go go run ./cmd/turns -mode live -hz 1000 -ticks 6000 -show 2400,2450,2650,6400 | tail -58 | head -26 tick 2450 wake 2: 1 with a worker, 2 queued after it # who job stale events faced score queued did 1 Ander plan 50 1 no 70 2400 queued 2 Mose plan 50 1 no 70 2400 queued 3 Halla 50 0 no 50 - waiting every worker is busy; nothing handed tick 2650 dropped unasked: Ander's plan, queued at tick 2400, 250 ticks ago, and the cap is 200 tick 2650 dropped unasked: Mose's plan, queued at tick 2400, 250 ticks ago, and the cap is 200 tick 2650 wake 6: 1 with a worker, 2 queued after it # who job stale events faced score queued did 1 Ander plan 250 3 no 310 2650 queued 2 Mose plan 250 3 no 310 2650 queued 3 Halla 250 0 no 250 - waiting every worker is busy; nothing handed tick 3099 a stranger, body 4, stood on 6,2, one step along Ander's heading, carrying 100 grams off the flag tick 3264 Mose is dead on 6,6, by the rule that has killed every animal in this book, and is struck from the queue tick 6056 Halla's plan landed: asked at 2400, 3656 ticks in flight; 1 generation(s), 1 embedding(s), 2 load(s); 3.655s on worker 1 (measured here; yours will differ) Halla stands on 9,2 carrying 179.5164 grams, with nobody in sight; the grounding set was 1 Halla, 2 Ander, 3 Mose the seam, at tick 6056 over the world as it stands: refused by freshness: asked at tick 2400, and it is tick 6056: 3656 ticks old, and the cap is 200 the row appended as proposal 1, refused tick 6056 the scheduler stopped asking: at tick 6056 an answer asked for at tick 2400 landed 3656 ticks later, and the cap is 200: at this pace no answer can land in time, so the village runs the utility rule from here tick 6400 wake 81: 0 with a worker, 0 queued after it # who job stale events faced score queued did 1 Ander reflection 4000 45 no 4900 - not asked 2 Halla reflection 344 3 no 404 - not asked stopped: at tick 6056 an answer asked for at tick 2400 landed 3656 ticks later, and the cap is 200: at this pace no answer can land in time, so the village runs the utility rule from here $ podman exec -w /bench world-go go run ./cmd/turns -mode live -hz 1000 -ticks 6000 -show 2400,2450,2650,6400 | tail -31 | head -18 wakes 120 requests handed to a worker 1 answers landed 1 ticks in flight, on average 3656 of those, later than the cap 1 requests dropped unasked 17 plans accepted at the seam 0 plans refused at the seam 1 steps refused when they came due 0 plans that ran out 0 intents posted at the boundary 0 of those, taken out of a slot 0 ticks the villagers ran the habit 12864 memory rows laid down in the stretch 117 of those, after the scheduler stopped asking 47 rows of the stream still without a vector 117 the scheduler stopped asking at tick 6056: at tick 6056 an answer asked for at tick 2400 landed 3656 ticks later, and the cap is 200: at this pace no answer can land in time, so the village runs the utility rule from here
One request handed at 2400 and one hundred and twenty wakes, every one of them finding the worker busy until the one answer landed. Ander's and Mose's requests are dropped at 2650, queued afresh, and dropped again every two hundred and fifty ticks, seventeen times between them, while the one call is in flight. The answer lands at 6056, 3,656 ticks after it was asked, which is 3.655 seconds at a thousand ticks a second, and the sixth check refuses it in the words it has for that: asked at 2400, it is 6056. The scheduler's own line follows on the same tick, and from there the village runs the utility rule; by wake 81 Ander's plan has become a reflection, two thousand ticks of a life having passed in two seconds, his score is four thousand nine hundred, and the wake ranks him and asks nobody. Thinking is the one thing in this world that cannot be sped up: the worker's clock is the wall clock, the world's is not, and at this pace the two do not meet inside the cap. What did not stop is the stream. A hundred and seventeen rows were laid down in the stretch, forty-seven of them after the asking stopped, and every one of the hundred and seventeen is in the table without a vector, because the vectors are made when a villager is next asked and nobody was. The village remembers a stretch it did not think through.
$ go run ./cmd/turns -mode pace -ticks 20000 | tail -13
20000 ticks in 75ms, 265687 ticks a second (measured here; yours will differ)
villagers on the habit alone: 3 people, witnesses writing rows, the scheduler off
20000 ticks in 197ms, 101769 ticks a second (measured here; yours will differ)
580 rows laid down, 57280 ticks on the habit
the scheduler running: the same people, the queue at every wake, every answer off the recording
and through the seam at the boundary it landed on, the dispatcher posting intents
20000 ticks in 250ms, 80008 ticks a second (measured here; yours will differ)
400 wakes, 320 answers landed, 12980 intents posted, 840 rows laid down, 46080 ticks on the habit
the third is the loop's own cost with nothing waited for: what a live model adds is
measured by -mode live, where the ticks are held to the watched pace and timed inside it
$ go run ./cmd/turns -mode draws | tail -26
on 1000 ticks from 2400: 20 wakes, 16 answers taken at the boundary, 649 intents posted, 2304 ticks on the habit
part off unpost on sha256 off sha256 unposted sha256 on
valley 753 753 753 64ccc86e490c4eac 64ccc86e490c4eac 3560df98a1f601b1
bed 3096 3096 3096 ae1b42796391cfa4 ae1b42796391cfa4 776ea64dd086d40e
stands 167 167 485 f5b3cbe130bdded5 f5b3cbe130bdded5 c302337de39853a4
bank 2280 2280 2248 5ed2f4b126d4b477 5ed2f4b126d4b477 3dc5ce70568add35
air 32 32 32 0d2a24b2a793b075 0d2a24b2a793b075 3ea3aec2c32490e4
pool 323 323 323 57ebc2ee63af0700 57ebc2ee63af0700 57ebc2ee63af0700
roster 475 475 475 fe553c3327488cb0 fe553c3327488cb0 8d23c93757b2dfb8
luck 518 518 518 f65504396694aa7a f65504396694aa7a ce0cf87f4d28a1f0
the luck part is the state of every stream the world reads, and all three villages read the same 9:
8 seed flight
9 germination
10 plant mortality
11 founding plants
16 mutation
17 crossover
18 mate choice
19 structural mutation
20 birth placement
off and unposted: the same 518 bytes of luck, part for part the same world; the scheduler woke 20 times,
ranked, handed, took and judged, and drew nothing
off and on: the luck differs, and so do 6 other parts: the villagers walked, ate and stood elsewhere,
and a plant eaten or left standing is a plant that draws or does not, off the world's own streams
Three numbers, one ground, the same thousand ticks from the same boundary twenty times over from a fresh world each, ungoverned, so that the number is what the loop can do and not what it is held to: 265,687 ticks a second with no villagers, 101,769 with three people on the habit and the witnesses writing rows, and 80,008 with the scheduler running over the recording, the queue ranked at four hundred wakes and three hundred and twenty answers judged and dispatched. Each is measured here and yours will differ, on the machine named above, on the processor. The third is the loop's own cost with nothing waited for, because the answers come off the recording; what a live model adds is not a cost to the ticks at all, which is the 150 microseconds a tick above, but a cost to the worker, and the worker is off the loop. And the second run is the sentence this chapter owes about luck. Three villages over the same stretch: one that never scheduled, one that scheduled, judged every answer and posted none, and one that scheduled and posted. All three read the same nine streams, because the scheduler claims none. The first two are the same world to the byte, luck part included, because the scheduler woke twenty times, ranked, handed, took and judged, and drew nothing. The third differs from them in seven parts, luck among them, because its villagers walked, ate and stood elsewhere, and a plant eaten or left standing is a plant that draws or does not, off the world's own streams: that is the world's luck moving with the world, which is what it is for.
The memory allows a hundred and forty-four and the file says one, so the obvious thing to try is two, and the bench tries it off a flag that prints itself as the counterfactual. Everything else is the same run as stage 13: the same stretch, the same models, the same machine within the same half hour.
$ podman kill world-db && podman start world-db world-db world-db $ podman exec -w /bench world-go go run ./cmd/turns -mode live -workers 2 | tail -270 | head -20 tick 2400 wake 1: 2 with a worker, 1 queued after it # who job stale events faced score queued did 1 Halla plan 0 0 no 0 2400 handed: 1 generation(s), 1 embedding(s), 2 load(s) at most 2 Ander plan 0 0 no 0 2400 handed: 1 generation(s), 1 embedding(s), 2 load(s) at most 3 Mose plan 0 0 no 0 2400 queued tick 2436 Halla's plan landed: asked at 2400, 36 ticks in flight; 1 generation(s), 1 embedding(s), 2 load(s); 3.575s on worker 1 (measured here; yours will differ) Halla stands on 9,3 carrying 106.7676 grams, with 2 Ander in sight; the grounding set was 1 Halla, 2 Ander, 3 Mose the seam, at tick 2436 over the world as it stands: accepted 2 step(s) spanning 60 ticks: go to 2,7; wait - the row appended as proposal 1, accepted tick 2450 wake 2: 2 with a worker, 1 queued after it # who job stale events faced score queued did 1 Mose plan 50 1 no 70 2400 handed: 1 generation(s), 2 embedding(s), 2 load(s) at most 2 Halla plan 0 1 no 20 2450 queued 3 Ander 50 1 no 70 - waiting tick 2490 Ander's plan landed: asked at 2400, 90 ticks in flight; 1 generation(s), 1 embedding(s), 2 load(s); 8.974s on worker 2 (measured here; yours will differ) Ander stands on 7,2 carrying 179.8878 grams, with nobody in sight; the grounding set was 1 Halla, 2 Ander, 3 Mose the seam, at tick 2490 over the world as it stands: accepted 2 step(s) spanning 60 ticks: go to 7,2; wait - the row appended as proposal 2, accepted $ podman exec -w /bench world-go go run ./cmd/turns -mode live -workers 2 | tail -28 | head -26 wakes 20 requests handed to a worker 22 answers landed 22 ticks in flight, on average 69 of those, later than the cap 0 requests dropped unasked 0 plans accepted at the seam 15 plans refused at the seam 4 steps refused when they came due 0 plans that ran out 12 intents posted at the boundary 703 of those, taken out of a slot 701 ticks the villagers ran the habit 2290 memory rows laid down in the stretch 55 rows of the stream still without a vector 2 generations the server was asked for 28 embeddings the server was asked for 84 calls that found their model not resident 48 memory rows in the table at the end 135 ledger rows, read back ORDER BY id 19 of those, accepted 15 of those, refused 4 1019 ticks in 1m41.904s, 10 ticks a second (measured here; yours will differ) inside the ticks and their boundaries: 177ms in all, 173µs a tick on average, 4.55ms the longest, of the 100ms each tick has on the workers' clock: 6.925s a request on average, 14.201s the longest, over 22 requests
Start from the symptom. Two requests are handed at the first wake, and Halla's lands in 3.575 seconds while Ander's, asked at the same tick on the other worker, takes 8.974. Nothing about Ander's prompt is dearer; his worker embedded while hers generated, the embedding model evicted the small model under her, hers loaded it back under him, and two calls that take three and a half seconds each alone took three and a half and nine. Reason from there to what the whole run shows. Twenty-two answers landed against sixteen with one worker, which is more, and nothing was dropped unasked where one worker dropped two; and every answer waited longer for it: 69 ticks in flight on average against 48, 6.925 seconds a request against 4.771, the longest 14.201 seconds, a reflection that was 143 ticks in flight and fifty-seven short of the cap, and the run overran its stretch by nineteen ticks waiting for the last answer. Forty-eight calls found their model not resident against thirty-four, which is the two workers evicting each other. So a second worker bought six more answers a thousand ticks and paid for each of them with forty-four per cent more waiting and fourteen more loads. Run once more with another job on the processor, which is a run this page does not quote, a reflection under two workers took 311 ticks to land and the scheduler stopped asking for the rest of the stretch; one worker on the same loaded processor stayed under the cap. The cause is the sentence the budget mode printed: memory allows many, residency allows one model, and two workers on one resident model are two workers taking turns to load. The file says one, and this is what it was set from: the answers a second worker buys are bought from the margin under the cap, and the margin is what an accelerated world spends first.
$ podman kill world-db && podman start world-db world-db world-db $ podman exec -w /bench world-go sh -c '/tmp/weeks -mode run -ticks 36000 -save 3600 -tape /tmp/weeks-80/killed-80.tape -die-at 19500; echo "exit status $?"' | tail -3 weeks: killed inside the creature phase of tick 19500 Killed exit status 137 $ podman exec -w /bench world-go /tmp/weeks -mode same -dir /tmp/weeks-80 -seam 18000 | tail -18 | head -5 weeks: killed and restarted, against run straight through bytes sha256, first 16 run straight through 1080 19f96bd7c7acf846 killed and restarted 1080 19f96bd7c7acf846 $ go test ./internal/store/ -run 'TestASnapshotIsEightPartsAndNoOthers|TestASaveOnEveryBoundaryChangesNothing|TestARestartFromAnOlderSaveLandsOnTheNewer' -v === RUN TestASaveOnEveryBoundaryChangesNothing keep_test.go:32: 200 saves taken between ticks, not one number moved, and 200 ticks on both worlds are at 8c501a6b2c3d0342 --- PASS: TestASaveOnEveryBoundaryChangesNothing (0.61s) === RUN TestARestartFromAnOlderSaveLandsOnTheNewer keep_test.go:78: restored from the older save, run 200 ticks, and all 8 parts came out the newer one: 314685 bytes --- PASS: TestARestartFromAnOlderSaveLandsOnTheNewer (0.16s) === RUN TestASnapshotIsEightPartsAndNoOthers save_test.go:211: 8 parts, every one of them once, and 280901 bytes between them --- PASS: TestASnapshotIsEightPartsAndNoOthers (0.10s) PASS ok theworld/internal/store 0.883s
Volume 7's daemon, built from this module, killed inside the creature phase of tick 19500 with SIGKILL and restarted out of the database, writes the same tape to the byte as the run nobody killed: 1,080 bytes, one digest, ten lines the same. The digests are this module's own, because chapter 90 widened the action table and every wiring in that valley grew with it; what holds is that the killed run and the straight run agree, which is the test as volume 7 wrote it and not a line of it changed. The three store tests are pinned to their names and say the same thing from the other side: a snapshot is the eight parts volume 7 named and no ninth, a save on every boundary moves nothing, and a restart from an older save lands on the newer. Nothing the scheduler holds is in any of those parts. A request in the queue, a request with the worker, an answer in the channel and an intent in a mailbox are all lost when the process is killed, and the villager falls back to the habit, which is what the fallback is for; everything a villager is lives in tables or is derived from them, and the queue is derived from them at the next boundary of the next process.
Why the worker boundary passes values
Take the village away and what is left is a way of putting something slow beside something that must not stop. The loop has one place where nothing is part way through, and everything that is not a tick happens there: the count of what happened, the offer of one request, the taking of whatever has come back. The offer and the taking are the two operations in the design that could wait, and neither is allowed to: the offer is a put into a slot that is either free or not, and the taking is a drain of a channel that is either holding something or not. Every other operation is arithmetic over values the boundary already holds. The worker sees a request and two channels, and a request is a copy of what the world looked like when the request was handed; the worker cannot reach the roster, cannot block the boundary, and cannot be blocked by it, because the channel it puts answers on has room.
The second thing that generalizes is the two clocks on a request, and it is what makes the sixth check the safety of the whole design rather than a filter on the end of it. A request is aged from the tick it was queued, and the queue drops it unasked when that age passes the cap, because the worker's time is the scarce thing and a call whose answer the seam will refuse should not be made. A proposal is aged from the tick it was asked, and the seam refuses it when that age passes the cap, because the world's state is the thing the answer is about. The recording has no landing refused for age and two requests dropped for it, which is the queue doing the seam's refusing before the call, and the accelerated run below has one landing refused for age and the asking stopped, which is the seam catching what the queue could not. Neither clock is the wall clock: the worker measured seconds, the boundary counted ticks, and only the ticks reached a verdict.
The third is the score as a sum in one unit, and it is a smaller point with a measured reason behind it. A term that is in ticks can be added to staleness, which is in ticks, and a reader can check the queue on paper: 5 + 20 + 400 is 425 at wake 15 and 153 + 80 is 233, and Ander goes first. A term that does not clear is a term that hands the worker to one villager, which the first draft of the faced term did for four wakes running. The order the queue prints is total because the last comparison is on identity, and it replays because every term is a count the boundary keeps and none is a clock or a draw; the snapshot's luck part is the proof that nothing drew, and the pace is the proof of what the arithmetic costs.
Checkpoint
- Work out from the three sums how many further conversations of the small model fit under the ceiling at 4,096 tokens, and say why the file says one worker when the memory would allow a hundred and forty-four.
- Compute the score of each villager at wake 15 from the three terms and the two weights, place them, and say which villager the tie-break would have put first had two scores been equal.
- Name the two ticks a request carries, say which of the two refusals ages a request from each, and say what the recording shows dropped at 3150 and why the same wake asked for the same villager again.
- Read the wake at 2950 and say why it handed nothing, why Mose is marked waiting and out of the queue, and what a wake that waited for the worker instead would have done to the ticks.
- Given the one-worker and two-worker runs, say what the second worker bought in answers landed, what it cost in ticks in flight, how close its longest request came to the cap, and what the count of calls that found their model not resident says about why.
- Say, from the accelerated run, which landing stopped the asking, how many rows were laid down after it stopped, and why those rows are in the table without a vector.
Exercise 1 — a person is worth less than a stale plan.
Set faced in the scheduler block to 100 and run the shipped test that
reads the block. Predict the message before you run it, then put 400 back.
TestTheShippedSchedulerIsACadenceTwoWeightsAndOneWorker fails with
a person in front is worth 100 ticks and the cap is 200: a person could be
outranked by a stale plan. The test pins the weight above the cap because a
villager can be at most 200 ticks stale before its request is dropped and asked
afresh, so a person worth less than 200 could lose to a plan that has merely run
out; at 400 a person in sight outranks any staleness the queue can hold. Put 400
back and the test passes. Replay the recording under 100 and wake 15 places
Halla's reflection at 233 above Ander at 125, the worker is handed a request the
tape has no turn for, and every landing from there is an error, in the same words
exercise 3 shows.
Exercise 2 — a wake every twenty ticks. Run the replay
with -every 20 and read the error, then explain from the round-trip
arithmetic what a cadence of twenty would change and what it would not.
The replay stops before the first tick: the recording was made from tick 2400 every 50 ticks with 1 worker(s), and this run is from 2400 every 20 with 1. A recording is a recording of one cadence. Run live instead and the round does not shorten: a plan still takes thirty to forty ticks and a reflection eighty, so most wakes find the worker busy, hand nothing, and print the line that says so, and the one thing that moves is how soon after an answer lands the next request is handed, from up to fifty ticks later to up to twenty. The cadence sets how long a free worker sits idle, never how fast the answers come.
Exercise 3 — the stretch with nobody to face. Replay the
recording with -stranger 0 and predict, before running, the first wake
at which the replay and the recording part.
$ go run ./cmd/turns -mode day -stranger 0 | tail -62 | head -5
tick 3100 wake 15: 1 with a worker, 2 queued after it
# who job stale events faced score queued did
1 Halla reflection 153 4 no 233 2900 handed: 3 generation(s), 8 embedding(s), 3 load(s) at most
2 Mose plan 121 0 no 121 3000 queued
3 Ander plan 5 0 no 5 3100 queued
Fourteen wakes replay to the tick, because up to 3099 the stranger was not in the world and the two runs are the same world. At 3100 Ander scores 5: no person in sight, and no memory laid down either, because the row the recording's Ander had at that wake was the sighting. Halla's reflection at 233 is handed instead, the tape holds no reflection for Halla asked at 3100, and the worker's landing carries the error the recording holds no reflection for Halla asked at tick 3100; -mode record makes one; so does every landing after it, and with no plan posted the habit spins Mose to death at 3309 instead of 3352. A recording is a recording of one world, and the first tick the worlds differ is the first tick the tape has nothing to say.
A stranger stood in front of Ander at tick 3099, and at 3100 the queue put him first and the model was asked what he does next: he walked to the cell he was standing on and waited. The hero model was asked once on this page for what he might say, and the answer was kept in a field nothing reads, because a line that reaches a reader needs a face to be said from and a box to be said in, and the village has neither. What it has is three people whose turns come round on a cadence a reader can count, a worker that answers when it answers, and a seam that judges every answer against the world it lands in; and a stretch of a thousand ticks, recorded once, in which all of that ran and not one tick waited.