fix(landscape): guard redelivery, detach terminal writes, bound workers
F2: Execute had no status guard, so a redelivered or stale message reran a finished run. Runs that are neither queued nor running are logged and acked without work; "running" still executes so a worker that crashed mid-run recovers. F4: failRun, finishNodeFailed and MarkRunFailed wrote the terminal status on the caller's context, which is exactly the context that is already cancelled when the run failed because of a shutdown or a sibling node — the write was refused and the run stayed stuck in "running". They now write on a context.WithoutCancel copy with a 10s timeout; the consumer's best-effort MarkRunFailed does the same. F5: peak memory scaled with the host's core count. LP_MAX_WORKERS (default 8) is now the total window-worker budget of a run and LP_MAX_NODES (default 2) the node fan-out; each node gets max(1, workers/nodes) through the new landscape.ComputeWithWorkers. Compute and service.ComputeFunc keep their signatures: Compute delegates with MaxWorkersPerNode, and a legacy ComputeFunc handed to NewLandscapeService is wrapped. F6: the stage loop used a plain errgroup, so a failing node let its siblings run to completion. It now uses errgroup.WithContext and passes the group context into runNode, cancelling siblings mid-compute. F7: wrap every float32 product in an explicit conversion so the compiler cannot fuse it into an FMA, keeping the kernels bit-identical to numpy's per-op rounding. F8: README's builder package list now matches the Dockerfile (adds g++), and the Landscape section documents the memory model and the two new knobs. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -34,7 +34,9 @@ func kernelZIntegrated(in [][]float64, out [][]int16, _ Params) {
|
||||
out[0][i] = NoData
|
||||
continue
|
||||
}
|
||||
score := float32(0.25)*float32(z1) + float32(0.25)*float32(z2) + float32(0.25)*float32(z3) + float32(0.25)*float32(z4)
|
||||
// explicit conversions forbid FMA fusion so results match numpy's per-op rounding
|
||||
score := float32(float32(0.25)*float32(z1)) + float32(float32(0.25)*float32(z2)) +
|
||||
float32(float32(0.25)*float32(z3)) + float32(float32(0.25)*float32(z4))
|
||||
out[0][i] = classify(score)
|
||||
}
|
||||
}
|
||||
@@ -49,14 +51,15 @@ func kernelUIntegrated(in [][]float64, out [][]int16, _ Params) {
|
||||
out[0][i] = NoData
|
||||
continue
|
||||
}
|
||||
num := float32(0.25)*float32(water) + float32(0.25)*float32(soil)
|
||||
// explicit conversions forbid FMA fusion so results match numpy's per-op rounding
|
||||
num := float32(float32(0.25)*float32(water)) + float32(float32(0.25)*float32(soil))
|
||||
den := float32(0.5)
|
||||
if cropOK {
|
||||
num += float32(0.25) * float32(crop)
|
||||
num += float32(float32(0.25) * float32(crop))
|
||||
den += 0.25
|
||||
}
|
||||
if vegOK {
|
||||
num += float32(0.25) * float32(veg)
|
||||
num += float32(float32(0.25) * float32(veg))
|
||||
den += 0.25
|
||||
}
|
||||
out[0][i] = classify(num / den)
|
||||
|
||||
Reference in New Issue
Block a user