diff --git a/RUNBOOK.md b/RUNBOOK.md index 67d8ecdc2a..b604f2b308 100644 --- a/RUNBOOK.md +++ b/RUNBOOK.md @@ -168,6 +168,12 @@ The server logs `heat_wait_s`, a cancel ends the wait, and the reply's tokens do On three GB10s serving GLM-5.3-Flash NVFP4 at TP=3, a 1,035,295-token prompt without the bands kept one host's hottest zone at 94 C or more for most of the prompt (97.7 C at the top); `TF_HEAT_HIGH=92` and `TF_HEAT_LOW=88` held all three at 93.0 C or under, and the first token came 10% later. +`TF_GRAPH_CACHE=0` (or `off`, any case) runs every step eagerly in any family that installs the keyed CUDA graph cache, +without a rebuild. Set it alike on every tensor-parallel rank: an off rank never joins the cache's per-miss vote, so a +rank left on would wait at its first miss, and only the off rank logs the switch. Ranks that share a cache compare +`Settings.digest` of their effective settings at start, before building it, and refuse to start when they differ. +A tensor-parallel family passes its cache settings in the TP config; the bootstrap then compares them and refuses to +start on every rank, naming TF_GRAPH_CACHE among the settings that must match. Archives whose names contain `host-only` are CPU verification artifacts and cannot serve CUDA inference. ## Flash Next and paired Metal serving diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index db27613ce9..3706d0d4fe 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -183,6 +183,12 @@ the one-shot all-gather protocol of [b12x](https://github.com/local-inference-la sequence parity and a timeout that fails the transport. Copyright 2026 Luke Alonso and the b12x contributors, Apache License 2.0 (see [the license text](LICENSES/Apache-2.0.txt)); b12x ships no NOTICE file. +The Zig tensor-parallel fabric's RoCE path (`zig/src/fabric/tp/mailbox.cu`, `roce.zig`, `roce_verbs.zig`) adapts +the one-shot all-gather protocol of [b12x](https://github.com/local-inference-lab/b12x)'s RoCEnante +(`b12x/comm/roce/`, PRs #295 and #315): a device-resident epoch, sequence flags after each payload, two slots by +sequence parity and a timeout that fails the transport. Copyright 2026 Luke Alonso and the b12x contributors, +Apache License 2.0 (see [the license text](LICENSES/Apache-2.0.txt)); b12x ships no NOTICE file. + ## Vendored code and weights In the Python engine, frozen at 0.6.6, `src/tensorfold/drafters/vendor/z_lab_dflash/model_mlx.py` is the unmodified diff --git a/build.zig b/build.zig index 8361b06746..7b7979b4b9 100644 --- a/build.zig +++ b/build.zig @@ -19,6 +19,11 @@ const golden_build = @import("zig/build/golden.zig"); const contract_build = @import("zig/build/kernel_contract.zig"); const video_build = @import("zig/build/video.zig"); const sys_build = @import("zig/build/sys.zig"); +const tp_build = @import("zig/build/tp.zig"); +const dsv41_build = @import("zig/build/dsv41.zig"); +const sessions_build = @import("zig/build/sessions.zig"); +const kv_build = @import("zig/build/kv.zig"); +const sampling_build = @import("zig/build/sampling.zig"); comptime { const required = std.mem.trim(u8, @embedFile(".zig-version"), "\r\n"); @@ -202,6 +207,14 @@ pub fn build(b: *std.Build) void { b.step("test-learn-warning", "CPU production-log refusal, backoff and recovery test").dependOn(&warning_run.step); grammar_build.steps(b, target, xgrammar); contract_build.hostTests(b, gemma4_contract_host); + tp_build.add(b, target, optimize, test_step); + sessions_build.add(b, test_step); + // DeepSeek-V4.1's host code reads POSIX and Linux APIs (pthreads, clock_gettime, io_uring) that Windows lacks + if (b.graph.host.result.os.tag != .windows) { + dsv41_build.add(b, test_step); + kv_build.add(b, target, optimize, test_step); // the family's KV over sessions and the TP fabric + sampling_build.add(b, test_step); + } sys_build.wire(b); @import("zig/build/checkpoint_config.zig").wire(b); } diff --git a/docs/recipes/README.md b/docs/recipes/README.md index e8094405d7..70299c896e 100644 --- a/docs/recipes/README.md +++ b/docs/recipes/README.md @@ -18,6 +18,7 @@ For agent-assisted contributions, read the [TensorFold contributor skill](SKILL. | Gemma 4 31B | [Native Metal, affine 4-bit](gemma-4-native.md) | | Gemma 4 12B | [Native Metal, affine 4-bit, MTP assistant](gemma-4-12b-native.md) | | DeepSeek-V4-Flash | [MLX on a 256 GB Mac, DSpark and MTP drafts](deepseek-v4-flash.md) | +| DeepSeek-V4.1-Flash | [CUDA (Zig): tools and checks](deepseek-v4.1-cuda.md) | | Qwen3.6-35B-A3B | [MLX with MTP drafts, one-GPU CUDA](qwen3.6-moe.md) | | Step-5-Preview | [Native Metal on one 256 GB Mac: 2-bit experts, MTP drafts, images](step-5-preview.md) | diff --git a/docs/recipes/deepseek-v4.1-cuda.md b/docs/recipes/deepseek-v4.1-cuda.md new file mode 100644 index 0000000000..bf79d505a5 --- /dev/null +++ b/docs/recipes/deepseek-v4.1-cuda.md @@ -0,0 +1,46 @@ +# DeepSeek-V4.1-Flash on CUDA (Zig): tools and checks + +The `deepseek_v41` family runs on the native Zig engine with its own CUDA kernels (`zig/kernels/cuda/deepseek_v41/`). +Its development tools are Python and live outside this repository, in jayleaton/deepseek-v41-tensorfold-spark, +pinned to commit c1cb91840fb8: +[`tools/tensorfold-dsv41/`](https://github.com/jayleaton/deepseek-v41-tensorfold-spark/tree/c1cb91840fb8ff5db0bd27418fc7cc766a222cbc/tools/tensorfold-dsv41). +That folder's README lists each tool's dependencies (the TensorFold Python engine, PyTorch, Triton, a GPU where +noted) and `MAP.tsv` names the TensorFold path each file came from; its `check.py` runs the tools' own checks without +a GPU. + +Nothing in `zig build` or `zig build test` runs Python. The checks below read fixtures committed here; references, +captures, AOT compiles, SASS identity and GPU measurements are separate receipts made with the tools on real hardware. + +## SASS identity of the kernel copies + +The family's kernel copies (line 1 of each names its source) must compile to the reference extensions' SASS. With +Docker and `nvcr.io/nvidia/pytorch:26.07-py3`, from a TensorFold checkout: + +``` +OUT=/scratch/sass SM=121 TF_DSV41_KERNELS=$PWD/zig/kernels/cuda/deepseek_v41 TF_DSV41_REF_SRC=/path/to/reference \ + /path/to/tools/tensorfold-dsv41/kernels/sass_check.sh [name ...] +``` + +`TF_DSV41_REF_SRC` (and optionally `TF_DSV41_REF_SRC_R1` / `_X3GM2` with `TF_DSV41_REF_REV*`) names the reference +repository and revision; the check stops before building without them. +`-Dnvcc=zig/kernels/cuda/deepseek_v41/nvcc-docker.sh` builds the images themselves through the same container. + +## Stored fixtures checked natively + +Paths under `zig/src/families/deepseek_v41/`. Host suites (`zig build test-dsv41`, part of `zig build test`; no GPU): +- `fixtures/widths-exl3-2p8.txt` and `fixtures/config.json`: a 2.8 bpw pack's expert widths and release config + (`host_test.zig`). +- `fixtures/lanes-ref.bin`: the run-time lane layout (`prepare.zig`). +- `fixtures/engram-hash-ref.bin` and `fixtures/engram-rows-ref.txt`: Engram's hasher and rows (`engram_host.zig`, + `engram_rows_test.zig`). +- Not stored: the device kernel fixtures (`cuda_tests/oracle.py`, `cuda_tests/dsv41_capture.py` in the tools) + that `tf-dsv41-test all DIR` and `tf-dsv41-test replay DIR` compare on a GPU. +- `kv/sched_golden.json`, `kv/session_slots_golden.json`, `kv/session_slots_split_golden.json`: the scheduler and + session slots against the reference (`kv/sched_test.zig`, `kv/session_slots_test.zig`; `zig build test-kv`). +- `fixtures/dense-k2.txt`, `fixtures/expert-k2.txt`: the K2 width listings the emitters and plans are tested on; + `fixtures/chain-op-cap16.json`: the drafting chain's op cap (`slot_drafts_chain_test.zig`). +- `fixtures/serve/sampling.jsonl`: keyed sampling against the reference rule (`serve/golden_test.zig`; + `zig build test-dsv41-sampling`). +- `draft/fixtures/{golden,calib-golden,calib-measure-golden,lookup-golden,spec-golden}.json`: drafting depth, + calibration, lookup and speculation against the reference engine's traces (`draft/*_test.zig`; + `zig build test-dsv41-draft`). diff --git a/zig/build/cuda.zig b/zig/build/cuda.zig index 5fadb17f06..3112eb7238 100644 --- a/zig/build/cuda.zig +++ b/zig/build/cuda.zig @@ -105,6 +105,8 @@ pub fn targets(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.bu runner.addImport("glm_cuda", glm_cuda); const runner_exe = b.addExecutable(.{ .name = "tf-cuda-test", .root_module = runner }); b.installArtifact(runner_exe); + b.installArtifact(@import("dsv41.zig").runner(b, target, optimize, cuda, strip)); + @import("sampling.zig").gpu(b, target, optimize, cuda, @import("dsv41.zig").kernels(b, target, optimize, cuda)); // tf-dsv41-samp: the keyed sampler's device steps // tf-tp-watch-check: the ranks' lines with no GPU or NCCL; a failing or dying rank stops every rank b.installArtifact(b.addExecutable(.{ .name = "tf-tp-watch-check", .root_module = b.createModule(.{ .root_source_file = b.path("zig/tests/cuda/tp_watch_check.zig"), .target = target, .optimize = optimize, .link_libc = true, .strip = strip, .imports = &.{.{ .name = "cuda", .module = cuda }} }) })); flashnextChecks(b, target, optimize, cuda, mods.core, mods.lanes, mods.qwen_vision); diff --git a/zig/build/dsv41.zig b/zig/build/dsv41.zig new file mode 100644 index 0000000000..c15e7bf504 --- /dev/null +++ b/zig/build/dsv41.zig @@ -0,0 +1,102 @@ +//! DeepSeek-V4.1's build: the family's host side (zig/src/families/deepseek_v41) and its host tests on any OS. + +const std = @import("std"); +const tp = @import("tp.zig"); +const kv = @import("kv.zig"); +const sampling = @import("sampling.zig"); + +/// The backend-neutral core (the safetensors reader the pack headers use) for the family's host side. +fn core(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Module { + const tokenizer = b.createModule(.{ .root_source_file = b.path("zig/src/core/tokenizer/tokenizer.zig"), .target = target, .optimize = optimize, .link_libc = true }); + const m = b.createModule(.{ .root_source_file = b.path("zig/src/core/root.zig"), .target = target, .optimize = optimize, .link_libc = true }); + m.addImport("tokenizer", tokenizer); + return m; +} + +/// The CUDA runtime without kernel images: the family's launch bindings and device loads compile on any host. +fn runtime(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Module { + const options = b.addOptions(); + options.addOption(bool, "with_kernels", false); + const cuda = b.createModule(.{ .root_source_file = b.path("zig/src/cuda/root.zig"), .target = target, .optimize = optimize, .link_libc = true }); + cuda.addOptions("kernel_options", options); + cuda.addImport("stagger", b.createModule(.{ .root_source_file = b.path("zig/src/core/stagger.zig"), .target = target, .optimize = optimize })); + return cuda; +} + +/// The family's kernel bindings and loader (`dsv41_kernels`, kernels.zig) over `cuda`, without fatbins (host-only). +pub fn kernels(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, cuda: *std.Build.Module) *std.Build.Module { + const options = b.addOptions(); + options.addOption(bool, "with_kernels", false); + options.addOption(usize, "dsv41_images", 0); // also keeps this options file apart from the runtime's + const m = b.createModule(.{ .root_source_file = b.path("zig/src/families/deepseek_v41/kernels.zig"), .target = target, .optimize = optimize, .link_libc = true }); + m.addImport("cuda", cuda); + m.addOptions("dsv41_kernel_options", options); + // the optional images (keyed sampler, two-tile linears) are embedded as empty files + const empty = b.addWriteFiles().add("empty.fatbin", ""); + for ([_][]const u8{ "sampling", "lin2" }) |n| m.addAnonymousImport(b.fmt("dsv41_fatbin_dsv41_{s}", .{n}), .{ .root_source_file = empty }); + return m; +} + +/// The family's host side (config, pack headers, weight plan, kernel bindings, the forward over `tp` and `kv`). +pub fn module(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Module { + return rooted(b, target, optimize, "deepseek_v41.zig"); +} + +/// The family's sources with their imports, rooted at `file` (a file of zig/src/families/deepseek_v41). +fn rooted(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, file: []const u8) *std.Build.Module { + const m = b.createModule(.{ .root_source_file = b.path(b.fmt("zig/src/families/deepseek_v41/{s}", .{file})), .target = target, .optimize = optimize, .link_libc = true }); + const cuda = runtime(b, target, optimize); + m.addImport("core", core(b, target, optimize)); + m.addImport("cuda", cuda); + m.addImport("dsv41_kernels", kernels(b, target, optimize, cuda)); + const tp_module = tp.module(b, target, optimize, cuda); + m.addImport("tp", tp_module); + m.addImport("kv", kv.module(b, target, optimize, cuda, tp_module)); + // the lane core's sampling types, shared with the serving module's exact sampler + const lanes = b.createModule(.{ .root_source_file = b.path("zig/src/core/lanes/lanes.zig"), .target = target, .optimize = optimize, .link_libc = true }); + m.addImport("lanes", lanes); + m.addImport("dsv41_serve", sampling.serveModule(b, target, optimize, lanes)); + return m; +} + +/// `tf-dsv41-test`, the device test runner (zig/tests/cuda/dsv41_main.zig), over `cuda` and host-only family kernels. +pub fn runner(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, cuda: *std.Build.Module, strip: bool) *std.Build.Step.Compile { + const m = b.createModule(.{ .root_source_file = b.path("zig/tests/cuda/dsv41_main.zig"), .target = target, .optimize = optimize, .link_libc = true, .strip = strip }); + m.addImport("cuda", cuda); + m.addImport("dsv41_kernels", kernels(b, target, optimize, cuda)); + return b.addExecutable(.{ .name = "tf-dsv41-test", .root_module = m }); +} + +pub fn add(b: *std.Build, test_step: *std.Build.Step) void { + const host = b.graph.host; + const step = b.step("test-dsv41", "DeepSeek-V4.1's host-side unit tests (no GPU work)"); + const family = b.addRunArtifact(b.addTest(.{ .root_module = module(b, host, .debug) })); + const bindings = b.addRunArtifact(b.addTest(.{ .root_module = kernels(b, host, .debug, runtime(b, host, .debug)) })); + for ([_]*std.Build.Step.Run{ family, bindings }) |run| { + step.dependOn(&run.step); + test_step.dependOn(&run.step); + } + // the prefill emitter's own suites, each its own test root (also in test-dsv41 and `zig build test`) + const suites = [_][3][]const u8{ + .{ "test-dsv41-gm2pf", "gm2pf_test.zig", "DeepSeek-V4.1 x3gm v2 (TF_DSV41_GM_V2) on the real prefill emitter, host only" }, + .{ "test-dsv41-longpf", "longpf_test.zig", "DeepSeek-V4.1 long prompts (stream top-k, split KV's row blocks) on the real prefill emitter, host only" }, + }; + for (suites) |x| { + const run = b.addRunArtifact(b.addTest(.{ .root_module = rooted(b, host, .debug, x[1]) })); + b.step(x[0], x[2]).dependOn(&run.step); + step.dependOn(&run.step); + test_step.dependOn(&run.step); + } + // drafting (DSpark depth, trees, the lanes backend) on the CPU twin and the reference's golden traces + const lanes = b.createModule(.{ .root_source_file = b.path("zig/src/core/lanes/lanes.zig"), .target = host, .optimize = .debug, .link_libc = true }); + const draft = b.createModule(.{ .root_source_file = b.path("zig/src/families/deepseek_v41/draft/draft.zig"), .target = host, .optimize = .debug, .link_libc = true }); + draft.addImport("lanes", lanes); + const draft_run = b.addRunArtifact(b.addTest(.{ .root_module = draft })); + b.step("test-dsv41-draft", "DeepSeek-V4.1's drafting and lanes backend on the CPU twin (no GPU work)").dependOn(&draft_run.step); + step.dependOn(&draft_run.step); + test_step.dependOn(&draft_run.step); + // the device runner compiles on any host; running it needs a GPU and every family image + const compiled = &runner(b, host, .debug, runtime(b, host, .debug), false).step; + step.dependOn(compiled); + test_step.dependOn(compiled); +} diff --git a/zig/build/kv.zig b/zig/build/kv.zig new file mode 100644 index 0000000000..5c25a2371a --- /dev/null +++ b/zig/build/kv.zig @@ -0,0 +1,65 @@ +//! DeepSeek-V4.1's KV memory build: the family's `kv` module over `sessions`, `cuda` and `tp`, host tests on any OS. + +const std = @import("std"); +const sessions = @import("sessions.zig"); +const tp = @import("tp.zig"); + +/// The CUDA runtime without the engine's kernels: kv needs only the driver, memory and streams. +fn runtime(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Module { + const options = b.addOptions(); + options.addOption(bool, "with_kernels", false); + const cuda = b.createModule(.{ .root_source_file = b.path("zig/src/cuda/root.zig"), .target = target, .optimize = optimize, .link_libc = true }); + cuda.addOptions("kernel_options", options); + cuda.addImport("stagger", b.createModule(.{ .root_source_file = b.path("zig/src/core/stagger.zig"), .target = target, .optimize = optimize })); + return cuda; +} + +/// The `kv` module: imports sessions, cuda and tp (an engine passes its own runtime and tp modules). +pub fn module(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, cuda: *std.Build.Module, tp_module: *std.Build.Module) *std.Build.Module { + const m = b.createModule(.{ .root_source_file = b.path("zig/src/families/deepseek_v41/kv/kv.zig"), .target = target, .optimize = optimize, .link_libc = true }); + m.addImport("sessions", sessions.module(b, target, optimize)); + m.addImport("cuda", cuda); + m.addImport("tp", tp_module); + return m; +} + +/// `tf-kv-split-test`, split KV's exchange on two GPUs (kv/split_gpu.zig) over `cuda`, `sessions` and `tp`. +fn splitRunner(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { + const rt = runtime(b, target, optimize); + const m = b.createModule(.{ .root_source_file = b.path("zig/src/families/deepseek_v41/kv/split_gpu.zig"), .target = target, .optimize = optimize, .link_libc = true }); + m.addImport("sessions", sessions.module(b, target, optimize)); + m.addImport("cuda", rt); + m.addImport("tp", tp.module(b, target, optimize, rt)); + return b.addExecutable(.{ .name = "tf-kv-split-test", .root_module = m }); +} + +/// `tf-kv-gpu-test`, the device pool's runner (kv/gpu_main.zig) over `cuda` and `sessions`. +fn gpuRunner(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { + const m = b.createModule(.{ .root_source_file = b.path("zig/src/families/deepseek_v41/kv/gpu_main.zig"), .target = target, .optimize = optimize, .link_libc = true }); + m.addImport("sessions", sessions.module(b, target, optimize)); + m.addImport("cuda", runtime(b, target, optimize)); + return b.addExecutable(.{ .name = "tf-kv-gpu-test", .root_module = m }); +} + +pub fn add(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, test_step: *std.Build.Step) void { + const host = b.graph.host; + const cuda = runtime(b, host, .debug); + const run = b.addRunArtifact(b.addTest(.{ .root_module = module(b, host, .debug, cuda, tp.module(b, host, .debug, cuda)) })); + const step = b.step("test-kv", "DeepSeek-V4.1 KV's host tests: families, sessions, scheduler, split KV on in-process ranks (no GPU)"); + step.dependOn(&run.step); + test_step.dependOn(&run.step); + // the device runners compile on any host; running them needs one or two GPUs + for ([_]*std.Build.Step{ &gpuRunner(b, host, .debug).step, &splitRunner(b, host, .debug).step }) |compiled| { + step.dependOn(compiled); + test_step.dependOn(compiled); + } + const bench = b.createModule(.{ .root_source_file = b.path("zig/src/families/deepseek_v41/kv/bench.zig"), .target = target, .optimize = .ReleaseFast, .link_libc = true }); + bench.addImport("sessions", sessions.module(b, target, .ReleaseFast)); + const exe = b.addExecutable(.{ .name = "tf-kv-bench", .root_module = bench }); + b.step("tf-kv-bench", "Page ops and a 1M-token session parked to / restored from a real NVMe tier (host pool)").dependOn(&b.addInstallArtifact(exe, .{}).step); + if (target.result.os.tag != .linux) return; + const gexe = gpuRunner(b, target, optimize); + b.step("tf-kv-gpu-test", "The device pool on one GPU: tables, a 1M-token session through device memory and the NVMe tier, copy on write").dependOn(&b.addInstallArtifact(gexe, .{}).step); + const sexe = splitRunner(b, target, optimize); + b.step("tf-kv-split-test", "Split KV's exchange on two GPUs (one process a rank): dense, graph replays, unions vs the replicated rows").dependOn(&b.addInstallArtifact(sexe, .{}).step); +} diff --git a/zig/build/sampling.zig b/zig/build/sampling.zig new file mode 100644 index 0000000000..0955cdf84b --- /dev/null +++ b/zig/build/sampling.zig @@ -0,0 +1,83 @@ +//! Keyed sampling on the GPU path: `test-dsv41-sampling` on in-process ranks, `tf-dsv41-samp` on one GPU (Linux). + +const std = @import("std"); +const tp_build = @import("tp.zig"); +const dsv41_build = @import("dsv41.zig"); + +/// The CUDA runtime without the engine's kernels (tp's in-process ranks need only its types). +fn runtime(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Module { + const options = b.addOptions(); + options.addOption(bool, "with_kernels", false); + const cuda = b.createModule(.{ .root_source_file = b.path("zig/src/cuda/root.zig"), .target = target, .optimize = optimize, .link_libc = true }); + cuda.addOptions("kernel_options", options); + cuda.addImport("stagger", b.createModule(.{ .root_source_file = b.path("zig/src/core/stagger.zig"), .target = target, .optimize = optimize })); + return cuda; +} + +/// The family's serving module (`dsv41_serve`: the exact sampler and its golden) over `lanes`. +pub fn serveModule(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, lanes: *std.Build.Module) *std.Build.Module { + const fixtures = b.createModule(.{ .root_source_file = b.path("zig/src/families/deepseek_v41/fixtures/serve/fixtures.zig"), .target = target, .optimize = optimize }); + return b.createModule(.{ + .root_source_file = b.path("zig/src/families/deepseek_v41/serve/root.zig"), + .target = target, + .optimize = optimize, + .link_libc = true, + .imports = &.{ + .{ .name = "lanes", .module = lanes }, + .{ .name = "dsv41_serve_fixtures", .module = fixtures }, + }, + }); +} + +/// The family's serving module (the exact sampler) with the modules it needs, over `lanes`. +pub fn serve(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, lanes: *std.Build.Module) struct { serve: *std.Build.Module, json: *std.Build.Module } { + const json = b.createModule(.{ .root_source_file = b.path("zig/src/server/json.zig"), .target = target, .optimize = optimize }); + return .{ .serve = serveModule(b, target, optimize, lanes), .json = json }; +} + +pub fn add(b: *std.Build, test_step: *std.Build.Step) void { + const host = b.graph.host; + const lanes = b.createModule(.{ .root_source_file = b.path("zig/src/core/lanes/lanes.zig"), .target = host, .optimize = .debug, .link_libc = true }); + const s = serve(b, host, .debug, lanes); + const m = b.createModule(.{ + .root_source_file = b.path("zig/src/families/deepseek_v41/sampling_test.zig"), + .target = host, + .optimize = .debug, + .link_libc = true, + .imports = &.{ + .{ .name = "tp", .module = tp_build.module(b, host, .debug, runtime(b, host, .debug)) }, + .{ .name = "lanes", .module = lanes }, + .{ .name = "dsv41_serve", .module = s.serve }, + .{ .name = "json", .module = s.json }, + }, + }); + const step = b.step("test-dsv41-sampling", "DeepSeek-V4.1's keyed sampling pipeline on in-process ranks vs the Python engine's choices (no GPU)"); + // the pipeline over the golden, and the serving module's own tests (the host rule) + for ([_]*std.Build.Module{ m, s.serve }) |root| step.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = root })).step); + test_step.dependOn(step); + // tf-dsv41-samp compiles on any host; running it needs a GPU and the sampling image + const rt = runtime(b, host, .debug); + test_step.dependOn(&exe(b, host, .debug, rt, dsv41_build.kernels(b, host, .debug, rt)).step); +} + +/// `tf-dsv41-samp` (Linux): the device steps on one GPU against the host reference, over cuda.zig's modules. +pub fn gpu(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, cuda: *std.Build.Module, kernels: *std.Build.Module) void { + b.installArtifact(exe(b, target, optimize, cuda, kernels)); +} + +fn exe(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, cuda: *std.Build.Module, kernels: *std.Build.Module) *std.Build.Step.Compile { + const lanes = b.createModule(.{ .root_source_file = b.path("zig/src/core/lanes/lanes.zig"), .target = target, .optimize = optimize, .link_libc = true }); + const m = b.createModule(.{ + .root_source_file = b.path("zig/src/families/deepseek_v41/sampling_check.zig"), + .target = target, + .optimize = optimize, + .link_libc = true, + .imports = &.{ + .{ .name = "cuda", .module = cuda }, + .{ .name = "dsv41_kernels", .module = kernels }, + .{ .name = "tp", .module = tp_build.module(b, target, optimize, cuda) }, + .{ .name = "dsv41_serve", .module = serve(b, target, optimize, lanes).serve }, + }, + }); + return b.addExecutable(.{ .name = "tf-dsv41-samp", .root_module = m }); +} diff --git a/zig/build/sessions.zig b/zig/build/sessions.zig new file mode 100644 index 0000000000..5dd2331845 --- /dev/null +++ b/zig/build/sessions.zig @@ -0,0 +1,20 @@ +//! Session memory: portable bookkeeping tests everywhere, NVMe tier tests on Linux. + +const std = @import("std"); + +pub fn module(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Module { + return b.createModule(.{ .root_source_file = b.path("zig/src/sessions/sessions.zig"), .target = target, .optimize = optimize, .link_libc = true }); +} + +pub fn add(b: *std.Build, test_step: *std.Build.Step) void { + const host = b.graph.host; + const tests = if (host.result.os.tag == .linux) module(b, host, .debug) else b.createModule(.{ + .root_source_file = b.path("zig/src/sessions/portable_test.zig"), + .target = host, + .optimize = .debug, + .link_libc = true, + }); + const run = b.addRunArtifact(b.addTest(.{ .root_module = tests })); + b.step("test-sessions", "Session memory's host tests: pool, prefix trie, RAM tier, NVMe tier on Linux (no GPU)").dependOn(&run.step); + test_step.dependOn(&run.step); +} diff --git a/zig/build/tp.zig b/zig/build/tp.zig new file mode 100644 index 0000000000..e07230abbc --- /dev/null +++ b/zig/build/tp.zig @@ -0,0 +1,88 @@ +//! Tensor parallelism's build: Linux host tests, and on Linux the `tf-tp-test` GPU runner with the mailbox kernel. + +const std = @import("std"); + +/// The CUDA runtime without the engine's kernels (tp only needs the driver, memory, streams and NCCL). +fn runtime(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Module { + const options = b.addOptions(); + options.addOption(bool, "with_kernels", false); + const cuda = b.createModule(.{ .root_source_file = b.path("zig/src/cuda/root.zig"), .target = target, .optimize = optimize, .link_libc = true }); + cuda.addOptions("kernel_options", options); + cuda.addImport("stagger", b.createModule(.{ .root_source_file = b.path("zig/src/core/stagger.zig"), .target = target, .optimize = optimize })); + return cuda; +} + +/// The `tp` module over `cuda` (an engine passes its own runtime module); verbs (zig/src/fabric/verbs.zig) for RoCE. +pub fn module(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, cuda: *std.Build.Module) *std.Build.Module { + const m = b.createModule(.{ .root_source_file = b.path("zig/src/fabric/tp/tp.zig"), .target = target, .optimize = optimize, .link_libc = true }); + m.addImport("cuda", cuda); + m.addImport("verbs", b.createModule(.{ .root_source_file = b.path("zig/src/fabric/verbs.zig"), .target = target, .optimize = optimize, .link_libc = true })); + return m; +} + +/// mailbox.cu as a fatbin: sm_120 (RTX PRO 6000), sm_121 (GB10), and compute_90 PTX for anything else. +fn mailboxFatbin(b: *std.Build, nvcc: []const u8) std.Build.LazyPath { + const run = b.addSystemCommand(&.{ nvcc, "-fatbin", "-O3", "-std=c++17", "-gencode=arch=compute_120,code=sm_120", "-gencode=arch=compute_121,code=sm_121", "-gencode=arch=compute_90,code=compute_90", "-o" }); + const out = run.addOutputFileArg("tp_mailbox.fatbin"); + run.addFileArg(b.path("zig/src/fabric/tp/mailbox.cu")); + return out; +} + +/// `pub const mailbox: []const u8`, the fatbin's bytes or empty. +fn kernelModule(b: *std.Build, image: ?std.Build.LazyPath) *std.Build.Module { + const wf = b.addWriteFiles(); + if (image) |file| { + _ = wf.addCopyFile(file, "tp_mailbox.fatbin"); + // the driver takes 8-byte aligned images (Module.load refuses others); @embedFile alone promises nothing + return b.createModule(.{ .root_source_file = wf.add("tp_kernel.zig", "const bytes align(16) = @embedFile(\"tp_mailbox.fatbin\").*;\npub const mailbox: []const u8 = &bytes;\n") }); + } + return b.createModule(.{ .root_source_file = wf.add("tp_kernel.zig", "pub const mailbox: []const u8 = \"\";\n") }); +} + +pub fn add(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, test_step: *std.Build.Step) void { + const host = b.graph.host; + const cli_tests = b.addRunArtifact(b.addTest(.{ .root_module = b.createModule(.{ .root_source_file = b.path("zig/src/fabric/tp/gpu_cli.zig"), .target = host, .optimize = optimize }) })); + const cli_step = b.step("test-tp-cli", "TP runner command validation (no GPU)"); + cli_step.dependOn(&cli_tests.step); + test_step.dependOn(&cli_tests.step); + if (host.result.os.tag == .linux) { + const tests = b.addRunArtifact(b.addTest(.{ .root_module = module(b, host, .debug, runtime(b, host, .debug)) })); + const tp_step = b.step("test-tp", "Tensor parallelism's host tests: bootstrap, fate channel, in-process ranks (no GPU)"); + tp_step.dependOn(&tests.step); + tp_step.dependOn(cli_step); + test_step.dependOn(&tests.step); + } + if (target.result.os.tag != .linux) return; + const nvcc = b.option([]const u8, "tp-nvcc", "nvcc that builds the TP mailbox kernel"); + const prebuilt = b.option([]const u8, "tp-fatbin", "absolute path of a prebuilt tp_mailbox.fatbin to embed"); + const image: ?std.Build.LazyPath = if (prebuilt) |p| b.graph.cwdRelativePath(p) else if (nvcc) |tool| mailboxFatbin(b, tool) else null; + const cuda = runtime(b, target, optimize); + const runner = b.createModule(.{ .root_source_file = b.path("zig/src/fabric/tp/gpu_main.zig"), .target = target, .optimize = optimize, .link_libc = true }); + runner.addImport("cuda", cuda); + runner.addImport("tp", module(b, target, optimize, cuda)); + runner.addImport("tp_kernel", kernelModule(b, image)); + const exe = b.addExecutable(.{ .name = "tf-tp-test", .root_module = runner }); + if (host.result.os.tag == .linux and target.query.isNative()) { + const cli = @import("../src/fabric/tp/gpu_cli.zig"); + for (cli.invalid) |args| { + const check = b.addRunArtifact(exe); + check.addArgs(args); + check.setEnvironmentVariable("TF_TP_WORLD", "invalid"); + check.expectExitCode(2); + check.expectStdErrEqual(cli.usage); + cli_step.dependOn(&check.step); + test_step.dependOn(&check.step); + } + if (image == null) { + const kernel = b.addRunArtifact(exe); + kernel.addArg("kernel"); + kernel.setEnvironmentVariable("TF_TP_WORLD", "invalid"); + kernel.expectExitCode(1); + kernel.expectStdErrEqual("mailbox kernel 0 B\n"); + cli_step.dependOn(&kernel.step); + test_step.dependOn(&kernel.step); + } + } + b.installArtifact(exe); + b.step("tf-tp-test", "TP=2 on two GPUs: bootstrap, every collective bit-exact, graph capture, fail-fast, latency").dependOn(&b.addInstallArtifact(exe, .{}).step); +} diff --git a/zig/kernels/cuda/deepseek_v41/attn_cuda.cu b/zig/kernels/cuda/deepseek_v41/attn_cuda.cu new file mode 100644 index 0000000000..ec43c81ecb --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/attn_cuda.cu @@ -0,0 +1,541 @@ +// Copy of src/tensorfold/families/deepseek_v41/cuda/csa2/attn_cuda.cu (blob f4be064c7368): sass_check.sh +#define DSV41_ATTN_NO_TORCH + + + +// CSA2 decode attention (chunks + merge) in ONE launch, TF_DSV41_ATTN_CUDA=1: the Triton pair's ops, same bits +#include +#include +#include +#include + +namespace dsv41_attn { + +constexpr int D = 512, CH = 128, NCOMP = 4, NCH = 5, BMQ = 16, WINDOW = 128; // tiles of 32 keys +constexpr int NOPE = 448, VB = 576, SB = 8, NT = 256; +constexpr int SM_Q = 0; // q A fragments [32 steps][32 lanes] uint4 16384 +constexpr int SM_KV = 16384; // 128 rows x 576 B, 8-byte pieces swizzled by key & 7 73728 +constexpr int SM_SC = SM_KV + CH * VB; // 128 x 8 scale bytes 1024 +constexpr int SM_P = SM_SC + CH * SB; // P A fragments [4 tiles][2 steps][32 lanes] uint4 4096 +constexpr int SM_TMAX = SM_P + 4096; // [4 tiles][4 n8][16 rows] fp32 1024 +constexpr int SM_WSUM = SM_TMAX + 1024; // same shape: the warps' row sums 1024 +constexpr int SM_ROW = SM_WSUM + 1024; // [4 tiles][16 rows] nm, alpha, active 768 +constexpr int SM_PHYS = SM_ROW + 768; // 128 physical rows (int32, -1: masked) 512 +constexpr int SM_MISC = SM_PHYS + 512; // final m, l [16] each + the ticket flag 256 +constexpr int SMEM = SM_MISC + 256; // 98,816 B (sm_121: <= 101,376 a block) +static_assert(SMEM <= 101376, "shared memory"); +constexpr unsigned FULL = 0xffffffffu; + +struct Args { + const uint16_t* q; // [R, H, 512] bf16 (RoPE'd) + const uint8_t* cv; const uint8_t* csc; long long cvs, css; // compressed rows (values, scales), row strides + const int* tok; long long ts; const int* cnt; // selection [R, ts] int32 (-1 pad), counts [R] + const uint8_t* sv; const uint8_t* ssc; long long svs, sss; // SWA rings + const int* lo; const int* hi; // [R] window starts; window ends (HAS_HI) or null + const void* pos; // int32 [1] (POS + r) or int64 [R] (ROWS) + const long long* sl; // ROWS: the rows' slots [R] + const int* pt; long long pts; int psh; // page table of the compressed rows (psh 0: contiguous) + const float* sink; const float* cs; long long csst; // [H] fp32, RoPE table fp32 [max_pos, 64] + float* po; float* pm; float* pl; // partials (attn.Scratch layout) + uint16_t* out; // [R, H, 512] bf16 + int* ticket; // [R, H / 16] (self-cleaning) + int R, H, ring, has_comp; + int qrope; // split_kernel: q arrives un-rotated, RoPE'd as it loads +}; + +__device__ __forceinline__ float ex2(float x) { + float y; + asm("ex2.approx.f32 %0, %1;" : "=f"(y) : "f"(x)); + return y; +} +__device__ __forceinline__ float div_full(float a, float b) { + float y; + asm("div.full.f32 %0, %1, %2;" : "=f"(y) : "f"(a), "f"(b)); + return y; +} +__device__ __forceinline__ float texp(float x) { return ex2(__fmul_rn(x, __int_as_float(0x3FB8AA3B))); } +__device__ __forceinline__ float pow2b(uint32_t s) { return __int_as_float(static_cast(s) << 23); } + +__device__ __forceinline__ void mma(float (&d)[4], const uint4& a, uint32_t b0, uint32_t b1) { + asm volatile("mma.sync.aligned.m16n8k16.row.col.f32.bf16.bf16.f32 {%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, " + "{%0,%1,%2,%3};" + : "+f"(d[0]), "+f"(d[1]), "+f"(d[2]), "+f"(d[3]) + : "r"(a.x), "r"(a.y), "r"(a.z), "r"(a.w), "r"(b0), "r"(b1)); +} + +// two e4m3 bytes (low byte = the lower k) with their own scales -> bf16x2 (exact: e4m3 x 2^e is a bf16) +__device__ __forceinline__ uint32_t deq2(uint32_t two, float s0, float s1) { + uint32_t h2; + asm("cvt.rn.f16x2.e4m3x2 %0, %1;" : "=r"(h2) : "h"(static_cast(two))); + const __half2 h = *reinterpret_cast(&h2); + const __nv_bfloat162 b = __floats2bfloat162_rn(__fmul_rn(__low2float(h), s0), __fmul_rn(__high2float(h), s1)); + return *reinterpret_cast(&b); +} + +// a byte offset x of row `key` in the staged rows: 8-byte pieces swizzled within groups of 8 (bank-conflict free) +__device__ __forceinline__ int swz(int key, int x) { + const int ch = x >> 3; + return key * VB + ((((ch & ~7) | ((ch & 7) ^ (key & 7)))) << 3) + (x & 7); +} + +// Triton's dot-operand k order: step s (16 k) of a 32-k block takes k = klo(s), klo + 1 and khi(s), khi + 1 +template __device__ __forceinline__ int klo(int s, int t) { + return KW == 4 ? 32 * (s >> 1) + 4 * t + 2 * (s & 1) : 16 * s + 2 * t; +} +template __device__ __forceinline__ int khi(int s, int t) { return klo(s, t) + (KW == 4 ? 16 : 8); } +// where element k (< 32) of a row sits in a 2-step A fragment: step, quad lane, high half (+16 / +8) +template __device__ __forceinline__ void frag_of(int k, int& step, int& tq, int& hi) { + if (KW == 4) { step = (k >> 1) & 1; hi = (k >> 4) & 1; tq = (k & 15) >> 2; } + else { step = k >> 4; hi = (k >> 3) & 1; tq = (k & 7) >> 1; } +} + +// the bf16 pair (k, k + 1) of staged row `key` (S's B operand: k along the row) +__device__ __forceinline__ uint32_t pair_k(const uint8_t* sm, int key, int k) { + if (k < NOPE) { + const uint32_t two = *reinterpret_cast(sm + SM_KV + swz(key, k)); + const float s = pow2b(sm[SM_SC + key * SB + (k >> 6)]); + return deq2(two, s, s); + } + return *reinterpret_cast(sm + SM_KV + swz(key, NOPE + 2 * (k - NOPE))); +} + +// value (key, n) of the staged rows as a bf16 bit pattern / its e4m3 byte (PV's B operand: k along the keys) +__device__ __forceinline__ uint32_t pair_n(const uint8_t* sm, int k0, int k1, int n) { + if (n < NOPE) { + const uint32_t two = sm[SM_KV + swz(k0, n)] | (static_cast(sm[SM_KV + swz(k1, n)]) << 8); + return deq2(two, pow2b(sm[SM_SC + k0 * SB + (n >> 6)]), pow2b(sm[SM_SC + k1 * SB + (n >> 6)])); + } + const int x = NOPE + 2 * (n - NOPE); + return *reinterpret_cast(sm + SM_KV + swz(k0, x)) | + (static_cast(*reinterpret_cast(sm + SM_KV + swz(k1, x))) << 16); +} + +__device__ __forceinline__ void cp8(uint32_t dst, const void* src, bool ok) { + asm volatile("cp.async.ca.shared.global [%0], [%1], 8, %2;" :: "r"(dst), "l"(src), "r"(ok ? 8 : 0)); +} + +template __device__ __forceinline__ int row_pos(const Args& a, int r) { + return ROWS ? static_cast(static_cast(a.pos)[r]) : static_cast(a.pos)[0] + r; +} + +// how many chunks of row r have keys (the merge's ticket target; the others are Triton's empty partial) +__device__ __forceinline__ bool chunk_works(const Args& a, int r, int c) { + return c == NCOMP || (a.has_comp && c * CH < a.cnt[r]); +} + +template +__device__ void merge(const Args& a, uint8_t* sm, int r, int hb) { + const int tid = threadIdx.x, H = a.H, R = a.R; + float* ca = reinterpret_cast(sm + SM_TMAX); // [5][16] a, [5][16] b, [16] af, [16] den + float* cb = ca + NCH * BMQ; + float* fa = cb + NCH * BMQ; + float* fd = fa + BMQ; + if (tid < BMQ) { // a head's scalar chain (Triton's _merge, unrolled) + const int h = hb * BMQ + tid; + float m = -INFINITY, l = 0.f; + for (int c = 0; c < NCH; ++c) { + const bool w = chunk_works(a, r, c); + const long long base = (static_cast(c) * R + r) * H + h; + const float cm = w ? __ldcg(a.pm + base) : -INFINITY, cl = w ? __ldcg(a.pl + base) : 0.f; + const bool act = cl > 0.f; + float aa, bb; + if (c == 0) { + const float nm = act ? fmaxf(cm, -INFINITY) : -INFINITY; + bb = act ? texp(__fsub_rn(cm, nm)) : 0.f; + aa = 0.f; + l = __fmaf_rn(bb, cl, 0.f); + m = nm; + } else { + const float nm = act ? fmaxf(m, cm) : m; + aa = act ? (m == -INFINITY ? 0.f : texp(__fsub_rn(m, nm))) : 1.f; + bb = act ? texp(__fsub_rn(cm, nm)) : 0.f; + l = __fmaf_rn(l, aa, __fmul_rn(bb, cl)); + m = nm; + } + ca[c * BMQ + tid] = aa; + cb[c * BMQ + tid] = bb; + } + const float sink = a.sink[h]; + const float top = fmaxf(m, sink); + const float af = m == -INFINITY ? 0.f : texp(__fsub_rn(m, top)); + const float e = sink == -INFINITY ? 0.f : texp(__fsub_rn(sink, top)); + fa[tid] = af; + fd[tid] = __fmaf_rn(l, af, e); + } + __syncthreads(); + const int at = row_pos(a, r); + const float* csr = a.cs + static_cast(at) * a.csst; + for (int u = tid; u < BMQ * (D / 4); u += NT) { // float4 units: head hl, columns d .. d + 3 + const int hl = u / (D / 4), d = (u % (D / 4)) * 4, h = hb * BMQ + hl; + float o[4]; + for (int c = 0; c < NCH; ++c) { + float4 v = make_float4(0.f, 0.f, 0.f, 0.f); + if (chunk_works(a, r, c)) + v = __ldcg(reinterpret_cast(a.po + ((static_cast(c) * R + r) * H + h) * D + + d)); + const float co[4] = {v.x, v.y, v.z, v.w}; + const float aa = ca[c * BMQ + hl], bb = cb[c * BMQ + hl]; + for (int i = 0; i < 4; ++i) + o[i] = c == 0 ? __fmaf_rn(bb, co[i], 0.f) : __fmaf_rn(o[i], aa, __fmul_rn(co[i], bb)); + } + float x[4]; + for (int i = 0; i < 4; ++i) x[i] = div_full(__fmul_rn(o[i], fa[hl]), fd[hl]); + uint16_t ob[4]; + for (int i = 0; i < 4; i += 2) { // pair (d + i, d + i + 1): GPT-J, inverse + const int p = (d + i) >> 1; + const float c = p >= 224 ? csr[p - 224] : 1.f, s = p >= 224 ? csr[32 + p - 224] : 0.f; + const float ne = __fmaf_rn(x[i], c, __fmul_rn(x[i + 1], s)); + const float no = __fmaf_rn(x[i + 1], c, -__fmul_rn(x[i], s)); + const __nv_bfloat16 b0 = __float2bfloat16_rn(ne), b1 = __float2bfloat16_rn(no); + ob[i] = *reinterpret_cast(&b0); + ob[i + 1] = *reinterpret_cast(&b1); + } + uint2 w; + w.x = ob[0] | (static_cast(ob[1]) << 16); + w.y = ob[2] | (static_cast(ob[3]) << 16); + *reinterpret_cast(a.out + (static_cast(r) * H + h) * D + d) = w; + } +} + +template +__global__ void __launch_bounds__(NT, 1) attn_kernel(Args a) { + extern __shared__ __align__(16) uint8_t sm[]; + const int c = blockIdx.x, hb = blockIdx.y, r = blockIdx.z; + const int tid = threadIdx.x, lane = tid & 31, warp = tid >> 5, g = lane >> 2, t = lane & 3; + const int H = a.H, R = a.R; + if (!chunk_works(a, r, c)) return; + int* phys = reinterpret_cast(sm + SM_PHYS); + const long long sl = ROWS ? a.sl[r] : 0; + if (tid < CH) { // the chunk's 128 keys -> physical rows (-1: masked) + int row = -1; + if (c < NCOMP) { + const int idx = c * CH + tid; + if (idx < a.cnt[r]) { + const int tk = a.tok[static_cast(r) * a.ts + idx]; + if (tk >= 0) { + row = tk; + if (a.psh > 0) { + const int* ptr = a.pt + (ROWS ? sl * a.pts : 0); + row = (ptr[tk >> a.psh] << a.psh) + (tk & ((1 << a.psh) - 1)); + } + } + } + } else { + const int hi = HAS_HI ? a.hi[r] : row_pos(a, r); + const int lo = max(a.lo[r], hi - (WINDOW - 1)); + const int p = hi - (WINDOW - 1) + tid; + if (p >= lo && p >= 0) row = (p % a.ring) + (ROWS ? static_cast(sl) * a.ring : 0); + } + phys[tid] = row; + } + { // q -> A fragments (Triton's k order) + const uint16_t* qb = a.q + (static_cast(r) * H + hb * BMQ) * D; + for (int i = tid; i < 32 * 32; i += NT) { + const int s = i >> 5, ln = i & 31, gg = ln >> 2, tt = ln & 3; + const int k0 = klo(s, tt), k1 = khi(s, tt); + uint4 f; + f.x = *reinterpret_cast(qb + gg * D + k0); + f.y = *reinterpret_cast(qb + (gg + 8) * D + k0); + f.z = *reinterpret_cast(qb + gg * D + k1); + f.w = *reinterpret_cast(qb + (gg + 8) * D + k1); + reinterpret_cast(sm + SM_Q)[i] = f; + } + } + __syncthreads(); + { // the rows: 72 value pieces + 1 scale piece a key + const bool comp = c < NCOMP; + const uint8_t* V = comp ? a.cv : a.sv; + const uint8_t* S = comp ? a.csc : a.ssc; + const long long vs = comp ? a.cvs : a.svs, ss = comp ? a.css : a.sss; + const uint32_t base = static_cast(__cvta_generic_to_shared(sm)); + for (int i = tid; i < CH * 73; i += NT) { + const int key = i / 73, pc = i % 73, row = phys[key]; + const bool ok = row >= 0; + const long long rr = ok ? row : 0; + if (pc < 72) cp8(base + SM_KV + swz(key, pc * 8), V + rr * vs + pc * 8, ok); + else cp8(base + SM_SC + key * SB, S + rr * ss, ok); + } + asm volatile("cp.async.commit_group;\ncp.async.wait_group 0;" ::: "memory"); + } + __syncthreads(); + const float NINF = -INFINITY; + const int n8 = warp & 3, ta = warp >> 2; // this warp: S of tiles ta and ta + 2, block n8 + float sv[2][4] = {{0.f, 0.f, 0.f, 0.f}, {0.f, 0.f, 0.f, 0.f}}; + { + const int kA = 32 * ta + 8 * n8 + g, kB = kA + 64; + const uint4* qf = reinterpret_cast(sm + SM_Q); +#pragma unroll 4 + for (int s = 0; s < 32; ++s) { + const uint4 af = qf[s * 32 + lane]; + const int k0 = klo(s, t), k1 = khi(s, t); + mma(sv[0], af, pair_k(sm, kA, k0), pair_k(sm, kA, k1)); + mma(sv[1], af, pair_k(sm, kB, k0), pair_k(sm, kB, k1)); + } + } + float* tmax = reinterpret_cast(sm + SM_TMAX); + float* wsum = reinterpret_cast(sm + SM_WSUM); + float* rowv = reinterpret_cast(sm + SM_ROW); // [tile][row]: nm, alpha, active at 0 / 64 / 128 + bool val[2][2]; + for (int j = 0; j < 2; ++j) { + const int tile = ta + 2 * j, kc = 32 * tile + 8 * n8 + 2 * t; + val[j][0] = phys[kc] >= 0; + val[j][1] = phys[kc + 1] >= 0; + for (int e = 0; e < 4; ++e) + sv[j][e] = val[j][e & 1] ? __fmul_rn(sv[j][e], __int_as_float(0x3D3504F3)) : NINF; + float m0 = fmaxf(sv[j][0], sv[j][1]), m1 = fmaxf(sv[j][2], sv[j][3]); + m0 = fmaxf(m0, __shfl_xor_sync(FULL, m0, 2)); + m0 = fmaxf(m0, __shfl_xor_sync(FULL, m0, 1)); + m1 = fmaxf(m1, __shfl_xor_sync(FULL, m1, 2)); + m1 = fmaxf(m1, __shfl_xor_sync(FULL, m1, 1)); + if (t == 0) { + tmax[(tile * 4 + n8) * 16 + g] = m0; + tmax[(tile * 4 + n8) * 16 + g + 8] = m1; + } + } + __syncthreads(); + if (tid < BMQ) { // the running max over the tiles, a row a thread + float m = NINF; + for (int tile = 0; tile < 4; ++tile) { + const float* tm4 = tmax + tile * 64 + tid; + const float tm = fmaxf(fmaxf(tm4[0], tm4[16]), fmaxf(tm4[32], tm4[48])); + const bool act = tm != NINF; + const float nm = act ? fmaxf(m, tm) : m; + const float al = act ? (m == NINF ? 0.f : texp(__fsub_rn(m, nm))) : 1.f; + rowv[tile * 16 + tid] = nm; + rowv[64 + tile * 16 + tid] = al; + rowv[128 + tile * 16 + tid] = act ? 1.f : 0.f; + m = nm; + } + reinterpret_cast(sm + SM_MISC)[tid] = m; + } + __syncthreads(); + uint32_t* pf = reinterpret_cast(sm + SM_P); // [tile][step][lane][4] + for (int j = 0; j < 2; ++j) { // p, the thread / quad sums, bf16(p) -> fragments + const int tile = ta + 2 * j; + float p[4]; + for (int e = 0; e < 4; ++e) { + const int row = g + (e >> 1) * 8; + const bool act = rowv[128 + tile * 16 + row] != 0.f; + p[e] = (val[j][e & 1] && act) ? texp(__fsub_rn(sv[j][e], rowv[tile * 16 + row])) : 0.f; + } + float a0 = __fadd_rn(p[0], p[1]), a1 = __fadd_rn(p[2], p[3]); + a0 = __fadd_rn(a0, __shfl_xor_sync(FULL, a0, 2)); + a0 = __fadd_rn(a0, __shfl_xor_sync(FULL, a0, 1)); + a1 = __fadd_rn(a1, __shfl_xor_sync(FULL, a1, 2)); + a1 = __fadd_rn(a1, __shfl_xor_sync(FULL, a1, 1)); + if (t == 0) { + wsum[(tile * 4 + n8) * 16 + g] = a0; + wsum[(tile * 4 + n8) * 16 + g + 8] = a1; + } + int step, tq, hi; + frag_of(8 * n8 + 2 * t, step, tq, hi); + const __nv_bfloat162 lo2 = __floats2bfloat162_rn(p[0], p[1]), hi2 = __floats2bfloat162_rn(p[2], p[3]); + uint32_t* f = pf + ((tile * 2 + step) * 32 + g * 4 + tq) * 4; + f[2 * hi] = *reinterpret_cast(&lo2); // reg 0 / 2: row g; 1 / 3: row g + 8 + f[2 * hi + 1] = *reinterpret_cast(&hi2); + } + __syncthreads(); + if (tid < BMQ) { // l: fma(l, alpha, (W0 + W2) + (W1 + W3)) + float l = 0.f; + for (int tile = 0; tile < 4; ++tile) { + const float* w4 = wsum + tile * 64 + tid; + const float sum = __fadd_rn(__fadd_rn(w4[0], w4[32]), __fadd_rn(w4[16], w4[48])); + l = __fmaf_rn(l, rowv[64 + tile * 16 + tid], sum); + } + reinterpret_cast(sm + SM_MISC)[16 + tid] = l; + } + float acc[8][4]; + for (int j = 0; j < 8; ++j) + for (int e = 0; e < 4; ++e) acc[j][e] = 0.f; + for (int tile = 0; tile < 4; ++tile) { // o = mma(P, kv, rn(o * alpha)), 2 k steps a tile + const float al0 = rowv[64 + tile * 16 + g], al1 = rowv[64 + tile * 16 + g + 8]; + for (int j = 0; j < 8; ++j) { + acc[j][0] = __fmul_rn(acc[j][0], al0); + acc[j][1] = __fmul_rn(acc[j][1], al0); + acc[j][2] = __fmul_rn(acc[j][2], al1); + acc[j][3] = __fmul_rn(acc[j][3], al1); + } + for (int step = 0; step < 2; ++step) { + const uint4 af = reinterpret_cast(pf)[(tile * 2 + step) * 32 + lane]; + const int k0 = 32 * tile + klo(step, t), k1 = 32 * tile + khi(step, t); +#pragma unroll + for (int j = 0; j < 8; ++j) { + const int n = 64 * j + 8 * warp + g; + mma(acc[j], af, pair_n(sm, k0, k0 + 1, n), pair_n(sm, k1, k1 + 1, n)); + } + } + } + const long long pbase = (static_cast(c) * R + r) * H + hb * BMQ; + for (int j = 0; j < 8; ++j) { + const int n = 64 * j + 8 * warp + 2 * t; + *reinterpret_cast(a.po + (pbase + g) * D + n) = make_float2(acc[j][0], acc[j][1]); + *reinterpret_cast(a.po + (pbase + g + 8) * D + n) = make_float2(acc[j][2], acc[j][3]); + } + if (tid < BMQ) { + a.pm[pbase + tid] = reinterpret_cast(sm + SM_MISC)[tid]; + a.pl[pbase + tid] = reinterpret_cast(sm + SM_MISC)[16 + tid]; + } + int* flag = reinterpret_cast(sm + SM_MISC) + 32; + __threadfence(); + __syncthreads(); + if (tid == 0) { + int need = 0; + for (int cc = 0; cc < NCH; ++cc) need += chunk_works(a, r, cc) ? 1 : 0; + *flag = atomicAdd(a.ticket + r * (H / BMQ) + hb, 1) == need - 1; + } + __syncthreads(); + if (!*flag) return; + __threadfence(); + if (tid == 0) atomicExch(a.ticket + r * (H / BMQ) + hb, 0); // self-cleaning: the next launch / replay + merge(a, sm, r, hb); +} + +template +cudaError_t launch_one(const Args& a, cudaStream_t stream) { + static bool init = false; + if (!init) { + const cudaError_t e = cudaFuncSetAttribute(attn_kernel, + cudaFuncAttributeMaxDynamicSharedMemorySize, SMEM); + if (e != cudaSuccess) return e; + init = true; + } + attn_kernel<<>>(a); + return cudaGetLastError(); +} + +#include "attn_split.cuh" + +template +cudaError_t launch_split(const Args& a, cudaStream_t stream) { + using G = Split

; + static bool init = false; + if (!init) { + const cudaError_t e = cudaFuncSetAttribute(split_kernel, + cudaFuncAttributeMaxDynamicSharedMemorySize, G::SMEM); + if (e != cudaSuccess) return e; + init = true; + } + cudaLaunchConfig_t cfg = {}; + cfg.gridDim = dim3(NCH * P, a.H / BMQ, a.R); + cfg.blockDim = dim3(NT); + cfg.dynamicSmemBytes = G::SMEM; + cfg.stream = stream; + cudaLaunchAttribute at[1]; + at[0].id = cudaLaunchAttributeClusterDimension; + at[0].val.clusterDim.x = P; + at[0].val.clusterDim.y = 1; + at[0].val.clusterDim.z = 1; + cfg.attrs = at; + cfg.numAttrs = P > 1 ? 1 : 0; + return cudaLaunchKernelEx(&cfg, split_kernel, a); +} + +template inline cudaError_t dispatch_split(const Args& a, int kw, bool rows, bool has_hi, cudaStream_t s) { + switch ((kw == 4 ? 4 : 0) + (rows ? 2 : 0) + (has_hi ? 1 : 0)) { + case 0: return launch_split<2, false, false, P>(a, s); + case 1: return launch_split<2, false, true, P>(a, s); + case 2: return launch_split<2, true, false, P>(a, s); + case 3: return launch_split<2, true, true, P>(a, s); + case 4: return launch_split<4, false, false, P>(a, s); + case 5: return launch_split<4, false, true, P>(a, s); + case 6: return launch_split<4, true, false, P>(a, s); + default: return launch_split<4, true, true, P>(a, s); + } +} + +// split: 0 attn_kernel (the default launch), 1 / 4 split_kernel with that many CTAs a chunk (the same bits) +inline cudaError_t dispatch(const Args& a, int kw, bool rows, bool has_hi, cudaStream_t stream, int split = 0) { + if (a.R < 1 || a.H % BMQ || a.ring < WINDOW || (a.ring & (a.ring - 1))) return cudaErrorInvalidValue; + if (split == 1) return dispatch_split<1>(a, kw, rows, has_hi, stream); + if (split == 4) return dispatch_split<4>(a, kw, rows, has_hi, stream); + if (split != 0) return cudaErrorInvalidValue; + switch ((kw == 4 ? 4 : 0) + (rows ? 2 : 0) + (has_hi ? 1 : 0)) { + case 0: return launch_one<2, false, false>(a, stream); + case 1: return launch_one<2, false, true>(a, stream); + case 2: return launch_one<2, true, false>(a, stream); + case 3: return launch_one<2, true, true>(a, stream); + case 4: return launch_one<4, false, false>(a, stream); + case 5: return launch_one<4, false, true>(a, stream); + case 6: return launch_one<4, true, false>(a, stream); + default: return launch_one<4, true, true>(a, stream); + } +} + +} // namespace dsv41_attn + +#ifndef DSV41_ATTN_NO_TORCH // the compile test builds the kernels alone (nvcc, no torch headers) +#include +#include +#include + +namespace { +template T* tp(const at::Tensor& t) { return t.numel() ? static_cast(t.data_ptr()) : nullptr; } +} + +void dsv41_attn_run_cuda(const at::Tensor& q, const at::Tensor& cv, const at::Tensor& csc, const at::Tensor& tok, + const at::Tensor& cnt, const at::Tensor& sv, const at::Tensor& ssc, const at::Tensor& lo, + const at::Tensor& hi, const at::Tensor& pos, const at::Tensor& sl, const at::Tensor& pt, + const at::Tensor& sink, const at::Tensor& cs, at::Tensor& po, at::Tensor& pm, + at::Tensor& pl, at::Tensor& out, at::Tensor& ticket, int64_t ring, int64_t psh, + int64_t pts, int64_t kw, int64_t split, int64_t qrope) { + using namespace dsv41_attn; + Args a{}; + a.q = tp(q); + a.has_comp = cv.numel() ? 1 : 0; + a.cv = tp(cv); a.csc = tp(csc); + a.cvs = cv.numel() ? cv.stride(0) : 0; a.css = csc.numel() ? csc.stride(0) : 0; + a.tok = tp(tok); a.ts = tok.numel() ? tok.stride(0) : 0; a.cnt = tp(cnt); + a.sv = tp(sv); a.ssc = tp(ssc); a.svs = sv.stride(0); a.sss = ssc.stride(0); + a.lo = tp(lo); a.hi = tp(hi); + a.pos = pos.data_ptr(); + a.sl = tp(sl); + a.pt = tp(pt); a.pts = pts; a.psh = static_cast(psh); + a.sink = tp(sink); a.cs = tp(cs); a.csst = cs.stride(0); + a.po = tp(po); a.pm = tp(pm); a.pl = tp(pl); + a.out = tp(out); a.ticket = tp(ticket); + a.R = static_cast(q.size(0)); a.H = static_cast(q.size(1)); a.ring = static_cast(ring); + a.qrope = qrope && split > 0 ? 1 : 0; + TORCH_CHECK(!qrope || split > 0, "attn_cuda: q's RoPE folds into the split kernel only"); + C10_CUDA_CHECK(dispatch(a, static_cast(kw), sl.numel() > 0, hi.numel() > 0, + at::cuda::getCurrentCUDAStream(), static_cast(split))); +} + +std::vector dsv41_attn_info_cuda(int64_t device) { // GB10's limits for the two launches + int sms = 0, optin = 0, cl = 0, blocks = 0; + cudaDeviceGetAttribute(&sms, cudaDevAttrMultiProcessorCount, static_cast(device)); + cudaDeviceGetAttribute(&optin, cudaDevAttrMaxSharedMemoryPerBlockOptin, static_cast(device)); + cudaDeviceGetAttribute(&cl, cudaDevAttrClusterLaunch, static_cast(device)); + cudaFuncSetAttribute(dsv41_attn::attn_kernel<4, true, false>, cudaFuncAttributeMaxDynamicSharedMemorySize, + dsv41_attn::SMEM); + cudaOccupancyMaxActiveBlocksPerMultiprocessor(&blocks, dsv41_attn::attn_kernel<4, true, false>, dsv41_attn::NT, + dsv41_attn::SMEM); + return {sms, optin, cl, blocks, dsv41_attn::SMEM}; +} +#endif + +// The instantiations dispatch() launches: attn_kernel. +template __global__ void dsv41_attn::attn_kernel<2, false, false>(dsv41_attn::Args); +template __global__ void dsv41_attn::attn_kernel<2, false, true>(dsv41_attn::Args); +template __global__ void dsv41_attn::attn_kernel<2, true, false>(dsv41_attn::Args); +template __global__ void dsv41_attn::attn_kernel<2, true, true>(dsv41_attn::Args); +template __global__ void dsv41_attn::attn_kernel<4, false, false>(dsv41_attn::Args); +template __global__ void dsv41_attn::attn_kernel<4, false, true>(dsv41_attn::Args); +template __global__ void dsv41_attn::attn_kernel<4, true, false>(dsv41_attn::Args); +template __global__ void dsv41_attn::attn_kernel<4, true, true>(dsv41_attn::Args); +// dispatch_split's split_kernel at P 1 and 4 (a cluster of 4) +template __global__ void dsv41_attn::split_kernel<2, false, false, 1>(dsv41_attn::Args); +template __global__ void dsv41_attn::split_kernel<2, false, true, 1>(dsv41_attn::Args); +template __global__ void dsv41_attn::split_kernel<2, true, false, 1>(dsv41_attn::Args); +template __global__ void dsv41_attn::split_kernel<2, true, true, 1>(dsv41_attn::Args); +template __global__ void dsv41_attn::split_kernel<4, false, false, 1>(dsv41_attn::Args); +template __global__ void dsv41_attn::split_kernel<4, false, true, 1>(dsv41_attn::Args); +template __global__ void dsv41_attn::split_kernel<4, true, false, 1>(dsv41_attn::Args); +template __global__ void dsv41_attn::split_kernel<4, true, true, 1>(dsv41_attn::Args); +template __global__ void dsv41_attn::split_kernel<2, false, false, 4>(dsv41_attn::Args); +template __global__ void dsv41_attn::split_kernel<2, false, true, 4>(dsv41_attn::Args); +template __global__ void dsv41_attn::split_kernel<2, true, false, 4>(dsv41_attn::Args); +template __global__ void dsv41_attn::split_kernel<2, true, true, 4>(dsv41_attn::Args); +template __global__ void dsv41_attn::split_kernel<4, false, false, 4>(dsv41_attn::Args); +template __global__ void dsv41_attn::split_kernel<4, false, true, 4>(dsv41_attn::Args); +template __global__ void dsv41_attn::split_kernel<4, true, false, 4>(dsv41_attn::Args); +template __global__ void dsv41_attn::split_kernel<4, true, true, 4>(dsv41_attn::Args); diff --git a/zig/kernels/cuda/deepseek_v41/attn_split.cuh b/zig/kernels/cuda/deepseek_v41/attn_split.cuh new file mode 100644 index 0000000000..cd70b1594a --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/attn_split.cuh @@ -0,0 +1,538 @@ +// attn_cuda.cu's split_kernel (TF_DSV41_ATTN_SPLIT): its helpers and the cluster kernel, inside namespace dsv41_attn +#pragma once +// ---- split_kernel (TF_DSV41_ATTN_SPLIT=P, 1 / 4): a chunk over a CLUSTER of P CTAs, attn_kernel's bits at any P --- +constexpr int MAXP = 4; // the ticket's ranks a (row, head tile) + +__host__ __device__ constexpr int col2byte(int n) { return n <= NOPE ? n : NOPE + 2 * (n - NOPE); } + +template struct Split { + static constexpr int TPC = 4 / P; // S tiles a CTA + static constexpr int KS = 32 * TPC; // S keys a CTA + static constexpr int W = D / P; // PV / merge columns a CTA + static constexpr int J = 8 / P; // n8 blocks a warp in PV + static constexpr int NPW = TPC == 4 ? 2 : 1; // S chains a warp ((tile, n8) pairs: 4 TPC over 8 warps) + static constexpr int SLB = P == 1 ? VB : col2byte(D) - col2byte(D - W); // slice pitch (the widest rank's) + static constexpr int SM_Q = 0; // q A fragments 16384 + static constexpr int SM_KP = 16384; // PV slice: 128 keys x SLB (P = 1: the whole rows) + static constexpr int SM_KS = P == 1 ? SM_KP : SM_KP + CH * SLB; // S rows: KS keys x 576 (P = 1: the slice) + static constexpr int SM_SC = SM_KS + KS * VB; // 128 keys x 8 scale bytes 1024 + static constexpr int SM_KB = SM_SC + CH * SB; // P > 1: S's B operands as bf16 pairs [KS][256] words + static constexpr int SM_P = SM_KB + (P == 1 ? 0 : KS * D * 2); // P A fragments [4][2][32] uint4 4096 + static constexpr int SM_TMAX = SM_P + 4096; // [4 tiles][4 n8][16 rows] fp32 (merge: ca, cb, ...) + static constexpr int SM_WSUM = SM_TMAX + 1024; + static constexpr int SM_ROW = SM_WSUM + 1024; // [4 tiles][16 rows] nm, alpha, active + static constexpr int SM_PHYS = SM_ROW + 768; // 128 physical rows (-1: masked) + static constexpr int SM_MISC = SM_PHYS + 512; // final m, l [16] each + the ticket flag + static constexpr int SMEM = SM_MISC + 256; // P = 1: 98,816 B; P = 4: 100,864 B + static constexpr int SM_VB = SM_KB; // P > 1, after S: PV's B pairs [64 key pairs][W] words + static_assert(P == 1 || (CH / 2) * W * 4 <= KS * D * 2, "PV's pairs fit where S's were"); + static_assert(SMEM <= 101376, "shared memory"); + static_assert(SLB % 64 == 0 && W % 128 == 0, "16-byte swizzle groups; whole n8 blocks a warp"); +}; + +__device__ __forceinline__ int fsw(int key) { return ((key >> 1) ^ (key >> 3)) & 3; } +__device__ __forceinline__ int swz16(int key, int x, int pitch) { + const int c = x >> 4; + return key * pitch + (((c & ~3) | ((c & 3) ^ fsw(key))) << 4) + (x & 15); +} + +// S's B pair (k, k + 1) of the S row `key` (local to the S rows; `ck`: its chunk key, for the scale) +template +__device__ __forceinline__ uint32_t spair_k(const uint8_t* sm, int key, int ck, int k) { + using G = Split

; + if (k < NOPE) { + const uint32_t two = *reinterpret_cast(sm + G::SM_KS + swz16(key, k, VB)); + const float s = pow2b(sm[G::SM_SC + ck * SB + (k >> 6)]); + return deq2(two, s, s); + } + return *reinterpret_cast(sm + G::SM_KS + swz16(key, NOPE + 2 * (k - NOPE), VB)); +} + +// P > 1: S's B operands dequantized once into bf16 pairs, word k / 2 of S row `key` at w ^ bsw(key): conflict-free +template __device__ __forceinline__ int bsw(int key) { + return (KW == 4 ? (key & 1) : ((key & 1) << 2)) | (((key >> 1) & 3) << 3); +} +template +__device__ __forceinline__ uint32_t sbf_k(const uint8_t* sm, int key, int k) { + return reinterpret_cast(sm + Split

::SM_KB)[key * (D / 2) + ((k >> 1) ^ bsw(key))]; +} + +// PV's B pair: value (k0, n), (k1, n) of the slice (byte x0: the rank's first byte) +template +__device__ __forceinline__ uint32_t spair_n(const uint8_t* sm, int k0, int k1, int n, int x0) { + using G = Split

; + if (n < NOPE) { + const int x = n - x0; + const uint32_t two = sm[G::SM_KP + swz16(k0, x, G::SLB)] | + (static_cast(sm[G::SM_KP + swz16(k1, x, G::SLB)]) << 8); + return deq2(two, pow2b(sm[G::SM_SC + k0 * SB + (n >> 6)]), pow2b(sm[G::SM_SC + k1 * SB + (n >> 6)])); + } + const int x = col2byte(n) - x0; + return *reinterpret_cast(sm + G::SM_KP + swz16(k0, x, G::SLB)) | + (static_cast(*reinterpret_cast(sm + G::SM_KP + swz16(k1, x, G::SLB))) << 16); +} + +// P > 1: PV's B pairs dequantized once, word (key pair kp, column x) at x ^ vsw(kp): conflict-free +template __device__ __forceinline__ int vsw(int kp) { return (KW == 4 ? (kp >> 1) & 3 : kp & 3) << 3; } + +// TF_DSV41_ATTN_ROPE: q's rope pair j in place, (rn(e c) - rn(o s), rn(o c) + rn(e s)), no contraction, then bf16 +__device__ __forceinline__ uint32_t rope_word(uint32_t w, const float* csr, int j) { + const float e = __uint_as_float(w << 16), o = __uint_as_float(w & 0xffff0000u); + const float c = csr[j], sn = csr[32 + j]; + const __nv_bfloat162 b = __floats2bfloat162_rn(__fsub_rn(__fmul_rn(e, c), __fmul_rn(o, sn)), + __fadd_rn(__fmul_rn(o, c), __fmul_rn(e, sn))); + return *reinterpret_cast(&b); +} + +__device__ __forceinline__ void cp16(uint32_t dst, const void* src, bool ok) { + asm volatile("cp.async.cg.shared.global [%0], [%1], 16, %2;" :: "r"(dst), "l"(src), "r"(ok ? 16 : 0)); +} +__device__ __forceinline__ void cl_arrive() { asm volatile("barrier.cluster.arrive.release.aligned;" ::: "memory"); } +__device__ __forceinline__ void cl_wait() { asm volatile("barrier.cluster.wait.acquire.aligned;" ::: "memory"); } +__device__ __forceinline__ uint32_t cl_rank() { + uint32_t r; + asm volatile("mov.u32 %0, %%cluster_ctarank;" : "=r"(r)); + return r; +} +// store word v at shared address `p` of cluster rank q (DSMEM) +__device__ __forceinline__ void st_peer(void* p, uint32_t q, uint32_t v) { + uint32_t a = static_cast(__cvta_generic_to_shared(p)), b; + asm volatile("mapa.shared::cluster.u32 %0, %1, %2;" : "=r"(b) : "r"(a), "r"(q)); + asm volatile("st.shared::cluster.u32 [%0], %1;" :: "r"(b), "r"(v) : "memory"); +} +// word i of shared array `p` in cluster rank q (DSMEM) +__device__ __forceinline__ uint32_t ld_peer(const void* p, uint32_t q) { + uint32_t a = static_cast(__cvta_generic_to_shared(p)), b, v; + asm volatile("mapa.shared::cluster.u32 %0, %1, %2;" : "=r"(b) : "r"(a), "r"(q)); + asm volatile("ld.shared::cluster.u32 %0, [%1];" : "=r"(v) : "r"(b) : "memory"); + return v; +} + +// stage row bytes [b0, b0 + 16 n) at key slot `key` (16-byte pieces, or 8-byte halves without v16); masked: zeros +__device__ __forceinline__ void stage_piece(uint32_t base, int region, int key, int pitch, int pc, const uint8_t* src, + bool ok, bool v16) { + const uint32_t dst = base + region + swz16(key, pc * 16, pitch); + if (v16) { + cp16(dst, src, ok); + } else { + cp8(dst, src, ok); + cp8(dst + 8, src + 8, ok); + } +} + +// the merge of columns [c0, c0 + NC) of (row r, head tile hb): attn_kernel's merge per element, loads first +template +__device__ void merge_cols(const Args& a, uint8_t* sm, int smt, int r, int hb, int c0) { + const int tid = threadIdx.x, H = a.H, R = a.R; + float* ca = reinterpret_cast(sm + smt); // [5][16] a, [5][16] b, [16] af, [16] den + float* cb = ca + NCH * BMQ; + float* fa = cb + NCH * BMQ; + float* fd = fa + BMQ; + bool cw[NCH]; +#pragma unroll + for (int c = 0; c < NCH; ++c) cw[c] = chunk_works(a, r, c); + constexpr int UB = BMQ * (NC / 4) / NT; // float4 units a thread (8 at P = 1, 2 at P = 4) + static_assert(UB * NT == BMQ * (NC / 4), "whole units"); + float4 pv[UB][NCH]; +#pragma unroll + for (int ub = 0; ub < UB; ++ub) { // the partials in flight first + const int u = tid + ub * NT, hl = u / (NC / 4), d = c0 + (u % (NC / 4)) * 4, h = hb * BMQ + hl; +#pragma unroll + for (int c = 0; c < NCH; ++c) + pv[ub][c] = cw[c] ? __ldcg(reinterpret_cast( + a.po + ((static_cast(c) * R + r) * H + h) * D + d)) + : make_float4(0.f, 0.f, 0.f, 0.f); + } + if (tid < BMQ) { // a head's scalar chain (Triton's _merge, unrolled) + const int h = hb * BMQ + tid; + float pmv[NCH], plv[NCH]; +#pragma unroll + for (int c = 0; c < NCH; ++c) { + const long long base = (static_cast(c) * R + r) * H + h; + pmv[c] = cw[c] ? __ldcg(a.pm + base) : -INFINITY; + plv[c] = cw[c] ? __ldcg(a.pl + base) : 0.f; + } + const float sink = a.sink[h]; + float m = -INFINITY, l = 0.f; +#pragma unroll + for (int c = 0; c < NCH; ++c) { + const float cm = pmv[c], cl = plv[c]; + const bool act = cl > 0.f; + float aa, bb; + if (c == 0) { + const float nm = act ? fmaxf(cm, -INFINITY) : -INFINITY; + bb = act ? texp(__fsub_rn(cm, nm)) : 0.f; + aa = 0.f; + l = __fmaf_rn(bb, cl, 0.f); + m = nm; + } else { + const float nm = act ? fmaxf(m, cm) : m; + aa = act ? (m == -INFINITY ? 0.f : texp(__fsub_rn(m, nm))) : 1.f; + bb = act ? texp(__fsub_rn(cm, nm)) : 0.f; + l = __fmaf_rn(l, aa, __fmul_rn(bb, cl)); + m = nm; + } + ca[c * BMQ + tid] = aa; + cb[c * BMQ + tid] = bb; + } + const float top = fmaxf(m, sink); + const float af = m == -INFINITY ? 0.f : texp(__fsub_rn(m, top)); + const float e = sink == -INFINITY ? 0.f : texp(__fsub_rn(sink, top)); + fa[tid] = af; + fd[tid] = __fmaf_rn(l, af, e); + } + __syncthreads(); + const float* csr = a.cs + static_cast(row_pos(a, r)) * a.csst; +#pragma unroll + for (int ub = 0; ub < UB; ++ub) { + const int u = tid + ub * NT, hl = u / (NC / 4), d = c0 + (u % (NC / 4)) * 4, h = hb * BMQ + hl; + float o[4]; +#pragma unroll + for (int c = 0; c < NCH; ++c) { + const float4 v = pv[ub][c]; + const float co[4] = {v.x, v.y, v.z, v.w}; + const float aa = ca[c * BMQ + hl], bb = cb[c * BMQ + hl]; +#pragma unroll + for (int i = 0; i < 4; ++i) + o[i] = c == 0 ? __fmaf_rn(bb, co[i], 0.f) : __fmaf_rn(o[i], aa, __fmul_rn(co[i], bb)); + } + float x[4]; +#pragma unroll + for (int i = 0; i < 4; ++i) x[i] = div_full(__fmul_rn(o[i], fa[hl]), fd[hl]); + uint16_t ob[4]; +#pragma unroll + for (int i = 0; i < 4; i += 2) { // pair (d + i, d + i + 1): GPT-J, inverse + const int p = (d + i) >> 1; + const float c = p >= 224 ? csr[p - 224] : 1.f, s = p >= 224 ? csr[32 + p - 224] : 0.f; + const float ne = __fmaf_rn(x[i], c, __fmul_rn(x[i + 1], s)); + const float no = __fmaf_rn(x[i + 1], c, -__fmul_rn(x[i], s)); + const __nv_bfloat16 b0 = __float2bfloat16_rn(ne), b1 = __float2bfloat16_rn(no); + ob[i] = *reinterpret_cast(&b0); + ob[i + 1] = *reinterpret_cast(&b1); + } + uint2 w; + w.x = ob[0] | (static_cast(ob[1]) << 16); + w.y = ob[2] | (static_cast(ob[3]) << 16); + *reinterpret_cast(a.out + (static_cast(r) * H + h) * D + d) = w; + } +} + +template +__global__ void __launch_bounds__(NT, 1) split_kernel(Args a) { + using G = Split

; + extern __shared__ __align__(16) uint8_t sm[]; + const int k = P == 1 ? 0 : static_cast(cl_rank()); + const int c = blockIdx.x / P, hb = blockIdx.y, r = blockIdx.z; + const int tid = threadIdx.x, lane = tid & 31, warp = tid >> 5, g = lane >> 2, t = lane & 3; + const int H = a.H, R = a.R; + if (!chunk_works(a, r, c)) return; // the whole cluster: no barrier is pending + uint4 qv[32 * 32 / NT]; // q's A fragments: every load in flight at once + { + const uint16_t* qb = a.q + (static_cast(r) * H + hb * BMQ) * D; +#pragma unroll + for (int u = 0; u < 32 * 32 / NT; ++u) { + const int i = tid + u * NT, s = i >> 5, ln = i & 31, gg = ln >> 2, tt = ln & 3; + const int k0 = klo(s, tt), k1 = khi(s, tt); + qv[u].x = *reinterpret_cast(qb + gg * D + k0); + qv[u].y = *reinterpret_cast(qb + (gg + 8) * D + k0); + qv[u].z = *reinterpret_cast(qb + gg * D + k1); + qv[u].w = *reinterpret_cast(qb + (gg + 8) * D + k1); + } + if (a.qrope) { // the rope dims (k >= 448): their pair's rotation + const float* csr = a.cs + static_cast(row_pos(a, r)) * a.csst; +#pragma unroll + for (int u = 0; u < 32 * 32 / NT; ++u) { + const int i = tid + u * NT, s = i >> 5, tt = (i & 31) & 3; + const int k0 = klo(s, tt), k1 = khi(s, tt); + if (k0 >= NOPE) { + qv[u].x = rope_word(qv[u].x, csr, (k0 - NOPE) >> 1); + qv[u].y = rope_word(qv[u].y, csr, (k0 - NOPE) >> 1); + } + if (k1 >= NOPE) { + qv[u].z = rope_word(qv[u].z, csr, (k1 - NOPE) >> 1); + qv[u].w = rope_word(qv[u].w, csr, (k1 - NOPE) >> 1); + } + } + } + } + int* phys = reinterpret_cast(sm + G::SM_PHYS); + const long long sl = ROWS ? a.sl[r] : 0; + if (tid < CH) { // attn_kernel's row map + int row = -1; + if (c < NCOMP) { + const int idx = c * CH + tid; + if (idx < a.cnt[r]) { + const int tk = a.tok[static_cast(r) * a.ts + idx]; + if (tk >= 0) { + row = tk; + if (a.psh > 0) { + const int* ptr = a.pt + (ROWS ? sl * a.pts : 0); + row = (ptr[tk >> a.psh] << a.psh) + (tk & ((1 << a.psh) - 1)); + } + } + } + } else { + const int hi = HAS_HI ? a.hi[r] : row_pos(a, r); + const int lo = max(a.lo[r], hi - (WINDOW - 1)); + const int p = hi - (WINDOW - 1) + tid; + if (p >= lo && p >= 0) row = (p % a.ring) + (ROWS ? static_cast(sl) * a.ring : 0); + } + phys[tid] = row; + } + __syncthreads(); + { // the rows in flight before q's fragments + const bool comp = c < NCOMP; + const uint8_t* V = comp ? a.cv : a.sv; + const uint8_t* S = comp ? a.csc : a.ssc; + const long long vs = comp ? a.cvs : a.svs, ss = comp ? a.css : a.sss; + const bool v16 = ((reinterpret_cast(V) | static_cast(vs)) & 15) == 0; + const uint32_t base = static_cast(__cvta_generic_to_shared(sm)); + const int x0 = col2byte(G::W * k), nsl = (col2byte(G::W * k + G::W) - x0) / 16; // the slice's pieces a key + for (int i = tid; i < CH * nsl; i += NT) { + const int key = i / nsl, pc = i % nsl, row = phys[key]; + const long long rr = row >= 0 ? row : 0; + stage_piece(base, G::SM_KP, key, G::SLB, pc, V + rr * vs + x0 + pc * 16, row >= 0, v16); + } + if (P > 1) { // the S tiles' whole rows + for (int i = tid; i < G::KS * (VB / 16); i += NT) { + const int key = i / (VB / 16), pc = i % (VB / 16), row = phys[G::KS * k + key]; + const long long rr = row >= 0 ? row : 0; + stage_piece(base, G::SM_KS, key, VB, pc, V + rr * vs + pc * 16, row >= 0, v16); + } + } + for (int key = tid; key < CH; key += NT) { + const int row = phys[key]; + cp8(base + G::SM_SC + key * SB, S + (row >= 0 ? row : 0) * ss, row >= 0); + } + asm volatile("cp.async.commit_group;" ::: "memory"); + } +#pragma unroll + for (int u = 0; u < 32 * 32 / NT; ++u) reinterpret_cast(sm + G::SM_Q)[tid + u * NT] = qv[u]; + asm volatile("cp.async.wait_group 0;" ::: "memory"); + __syncthreads(); + if (P > 1) { // S's operands -> bf16 pairs, 4 k a step (deq2) + uint32_t* kb = reinterpret_cast(sm + G::SM_KB); + for (int i = tid; i < G::KS * (D / 4); i += NT) { + const int key = i / (D / 4), k4 = (i % (D / 4)) * 4, w = k4 >> 1; + uint32_t w0, w1; + if (k4 < NOPE) { + const uint32_t b = *reinterpret_cast(sm + G::SM_KS + swz16(key, k4, VB)); + const float sc = pow2b(sm[G::SM_SC + (key + G::KS * k) * SB + (k4 >> 6)]); + w0 = deq2(b & 0xffffu, sc, sc); + w1 = deq2(b >> 16, sc, sc); + } else { + const uint2 b = *reinterpret_cast(sm + G::SM_KS + swz16(key, NOPE + 2 * (k4 - NOPE), VB)); + w0 = b.x; + w1 = b.y; + } + kb[key * (D / 2) + (w ^ bsw(key))] = w0; + kb[key * (D / 2) + ((w + 1) ^ bsw(key))] = w1; + } + __syncthreads(); + } + const float NINF = -INFINITY; + float* tmax = reinterpret_cast(sm + G::SM_TMAX); + float* wsum = reinterpret_cast(sm + G::SM_WSUM); + float* rowv = reinterpret_cast(sm + G::SM_ROW); + // S: chain pair w (+ 8): local tile w >> 2, block n8 = w & 3 (P = 1: attn_kernel's tiles ta and ta + 2) + const int n8 = warp & 3; + const bool sw = warp < 4 * G::TPC; // this warp has S chains + float sv[G::NPW][4]; + bool val[G::NPW][2]; + int tile_of[G::NPW]; +#pragma unroll + for (int j = 0; j < G::NPW; ++j) { + tile_of[j] = G::TPC * k + (warp >> 2) + 2 * j; + for (int e = 0; e < 4; ++e) sv[j][e] = 0.f; + } + if (sw) { + const uint4* qf = reinterpret_cast(sm + G::SM_Q); + int lk[G::NPW]; +#pragma unroll + for (int j = 0; j < G::NPW; ++j) lk[j] = 32 * (tile_of[j] - G::TPC * k) + 8 * n8 + g; +#pragma unroll 4 + for (int s = 0; s < 32; ++s) { + const uint4 af = qf[s * 32 + lane]; + const int k0 = klo(s, t), k1 = khi(s, t); +#pragma unroll + for (int j = 0; j < G::NPW; ++j) { + if (P == 1) { + mma(sv[j], af, spair_k

(sm, lk[j], lk[j], k0), spair_k

(sm, lk[j], lk[j], k1)); + } else { + mma(sv[j], af, sbf_k(sm, lk[j], k0), sbf_k(sm, lk[j], k1)); + } + } + } +#pragma unroll + for (int j = 0; j < G::NPW; ++j) { + const int tile = tile_of[j], kc = 32 * tile + 8 * n8 + 2 * t; + val[j][0] = phys[kc] >= 0; + val[j][1] = phys[kc + 1] >= 0; + for (int e = 0; e < 4; ++e) + sv[j][e] = val[j][e & 1] ? __fmul_rn(sv[j][e], __int_as_float(0x3D3504F3)) : NINF; + float m0 = fmaxf(sv[j][0], sv[j][1]), m1 = fmaxf(sv[j][2], sv[j][3]); + m0 = fmaxf(m0, __shfl_xor_sync(FULL, m0, 2)); + m0 = fmaxf(m0, __shfl_xor_sync(FULL, m0, 1)); + m1 = fmaxf(m1, __shfl_xor_sync(FULL, m1, 2)); + m1 = fmaxf(m1, __shfl_xor_sync(FULL, m1, 1)); + if (t == 0) { + tmax[(tile * 4 + n8) * 16 + g] = m0; + tmax[(tile * 4 + n8) * 16 + g + 8] = m1; + } + } + } + if (P > 1) { // this rank's tile maxima pushed to the others + __syncthreads(); + for (int i = tid; i < 64 * G::TPC * (P - 1); i += NT) { + const int e = 64 * G::TPC * k + i % (64 * G::TPC), q = (k + 1 + i / (64 * G::TPC)) % P; + st_peer(tmax + e, q, __float_as_uint(tmax[e])); + } + cl_arrive(); + const int x0 = col2byte(G::W * k); // meanwhile: PV's B pairs, the fp8 slice -> bf16, + uint32_t* vb = reinterpret_cast(sm + G::SM_VB); // 4 columns a step (deq2 per column) + for (int i = tid; i < (CH / 2) * (G::W / 4); i += NT) { + const int kp = i / (G::W / 4), x = (i % (G::W / 4)) * 4, n = G::W * k + x, k0 = 2 * kp, k1 = k0 + 1; + uint32_t o[4]; + if (n < NOPE) { + const uint32_t b0 = *reinterpret_cast(sm + G::SM_KP + swz16(k0, n - x0, G::SLB)); + const uint32_t b1 = *reinterpret_cast(sm + G::SM_KP + swz16(k1, n - x0, G::SLB)); + const float s0 = pow2b(sm[G::SM_SC + k0 * SB + (n >> 6)]), s1 = pow2b(sm[G::SM_SC + k1 * SB + (n >> 6)]); +#pragma unroll + for (int e = 0; e < 4; ++e) o[e] = deq2(((b0 >> (8 * e)) & 0xffu) | (((b1 >> (8 * e)) & 0xffu) << 8), s0, s1); + } else { + const uint2 a0 = *reinterpret_cast(sm + G::SM_KP + swz16(k0, col2byte(n) - x0, G::SLB)); + const uint2 a1 = *reinterpret_cast(sm + G::SM_KP + swz16(k1, col2byte(n) - x0, G::SLB)); + o[0] = (a0.x & 0xffffu) | (a1.x << 16); + o[1] = (a0.x >> 16) | (a1.x & 0xffff0000u); + o[2] = (a0.y & 0xffffu) | (a1.y << 16); + o[3] = (a0.y >> 16) | (a1.y & 0xffff0000u); + } +#pragma unroll + for (int e = 0; e < 4; ++e) vb[kp * G::W + ((x + e) ^ vsw(kp))] = o[e]; + } + cl_wait(); + } + __syncthreads(); + if (tid < BMQ) { // the running max over the tiles, a row a thread + float m = NINF; + for (int tile = 0; tile < 4; ++tile) { + const float* tm4 = tmax + tile * 64 + tid; + const float tm = fmaxf(fmaxf(tm4[0], tm4[16]), fmaxf(tm4[32], tm4[48])); + const bool act = tm != NINF; + const float nm = act ? fmaxf(m, tm) : m; + const float al = act ? (m == NINF ? 0.f : texp(__fsub_rn(m, nm))) : 1.f; + rowv[tile * 16 + tid] = nm; + rowv[64 + tile * 16 + tid] = al; + rowv[128 + tile * 16 + tid] = act ? 1.f : 0.f; + m = nm; + } + reinterpret_cast(sm + G::SM_MISC)[tid] = m; + } + __syncthreads(); + uint32_t* pf = reinterpret_cast(sm + G::SM_P); // [tile][step][lane][4] + if (sw) { +#pragma unroll + for (int j = 0; j < G::NPW; ++j) { // p, the quad sums, bf16(p) -> fragments + const int tile = tile_of[j]; + float p[4]; + for (int e = 0; e < 4; ++e) { + const int row = g + (e >> 1) * 8; + const bool act = rowv[128 + tile * 16 + row] != 0.f; + p[e] = (val[j][e & 1] && act) ? texp(__fsub_rn(sv[j][e], rowv[tile * 16 + row])) : 0.f; + } + float a0 = __fadd_rn(p[0], p[1]), a1 = __fadd_rn(p[2], p[3]); + a0 = __fadd_rn(a0, __shfl_xor_sync(FULL, a0, 2)); + a0 = __fadd_rn(a0, __shfl_xor_sync(FULL, a0, 1)); + a1 = __fadd_rn(a1, __shfl_xor_sync(FULL, a1, 2)); + a1 = __fadd_rn(a1, __shfl_xor_sync(FULL, a1, 1)); + if (t == 0) { + wsum[(tile * 4 + n8) * 16 + g] = a0; + wsum[(tile * 4 + n8) * 16 + g + 8] = a1; + } + int step, tq, hi; + frag_of(8 * n8 + 2 * t, step, tq, hi); + const __nv_bfloat162 lo2 = __floats2bfloat162_rn(p[0], p[1]), hi2 = __floats2bfloat162_rn(p[2], p[3]); + uint32_t* f = pf + ((tile * 2 + step) * 32 + g * 4 + tq) * 4; + f[2 * hi] = *reinterpret_cast(&lo2); + f[2 * hi + 1] = *reinterpret_cast(&hi2); + } + } + if (P > 1) { // this rank's row sums and fragments pushed + __syncthreads(); + constexpr int NW = G::TPC * (64 + 256); // words of this rank's tiles + for (int i = tid; i < NW * (P - 1); i += NT) { + const int e = i % NW, q = (k + 1 + i / NW) % P; + uint32_t* w = e < 64 * G::TPC ? reinterpret_cast(wsum) + 64 * G::TPC * k + e + : pf + 256 * G::TPC * k + (e - 64 * G::TPC); + st_peer(w, q, *w); + } + cl_arrive(); // the last cluster access: no barrier before exit + cl_wait(); + } + __syncthreads(); + if (tid < BMQ) { // l: fma(l, alpha, (W0 + W2) + (W1 + W3)) + float l = 0.f; + for (int tile = 0; tile < 4; ++tile) { + const float* w4 = wsum + tile * 64 + tid; + const float sum = __fadd_rn(__fadd_rn(w4[0], w4[32]), __fadd_rn(w4[16], w4[48])); + l = __fmaf_rn(l, rowv[64 + tile * 16 + tid], sum); + } + reinterpret_cast(sm + G::SM_MISC)[16 + tid] = l; + } + const int x0 = col2byte(G::W * k); + float acc[G::J][4]; +#pragma unroll + for (int j = 0; j < G::J; ++j) + for (int e = 0; e < 4; ++e) acc[j][e] = 0.f; + for (int tile = 0; tile < 4; ++tile) { // o = mma(P, kv, rn(o * alpha)), 2 k steps a tile + const float al0 = rowv[64 + tile * 16 + g], al1 = rowv[64 + tile * 16 + g + 8]; +#pragma unroll + for (int j = 0; j < G::J; ++j) { + acc[j][0] = __fmul_rn(acc[j][0], al0); + acc[j][1] = __fmul_rn(acc[j][1], al0); + acc[j][2] = __fmul_rn(acc[j][2], al1); + acc[j][3] = __fmul_rn(acc[j][3], al1); + } + for (int step = 0; step < 2; ++step) { + const uint4 af = reinterpret_cast(pf)[(tile * 2 + step) * 32 + lane]; + const int k0 = 32 * tile + klo(step, t), k1 = 32 * tile + khi(step, t); +#pragma unroll + for (int j = 0; j < G::J; ++j) { + const int n = 64 * (G::J * k + j) + 8 * warp + g; + if (P == 1) { + mma(acc[j], af, spair_n

(sm, k0, k0 + 1, n, x0), spair_n

(sm, k1, k1 + 1, n, x0)); + } else { + const uint32_t* vb = reinterpret_cast(sm + G::SM_VB); + const int x = n - G::W * k; + mma(acc[j], af, vb[(k0 >> 1) * G::W + (x ^ vsw(k0 >> 1))], + vb[(k1 >> 1) * G::W + (x ^ vsw(k1 >> 1))]); + } + } + } + } + const long long pbase = (static_cast(c) * R + r) * H + hb * BMQ; +#pragma unroll + for (int j = 0; j < G::J; ++j) { + const int n = 64 * (G::J * k + j) + 8 * warp + 2 * t; + *reinterpret_cast(a.po + (pbase + g) * D + n) = make_float2(acc[j][0], acc[j][1]); + *reinterpret_cast(a.po + (pbase + g + 8) * D + n) = make_float2(acc[j][2], acc[j][3]); + } + if (tid < BMQ) { // every rank: the same (m, l), so each merge sees it + a.pm[pbase + tid] = reinterpret_cast(sm + G::SM_MISC)[tid]; + a.pl[pbase + tid] = reinterpret_cast(sm + G::SM_MISC)[16 + tid]; + } + int* flag = reinterpret_cast(sm + G::SM_MISC) + 32; + int* tk = a.ticket + (r * (H / BMQ) + hb) * MAXP + k; + __threadfence(); + __syncthreads(); + if (tid == 0) { + int need = 0; + for (int cc = 0; cc < NCH; ++cc) need += chunk_works(a, r, cc) ? 1 : 0; + *flag = atomicAdd(tk, 1) == need - 1; + } + __syncthreads(); + if (!*flag) return; + __threadfence(); + if (tid == 0) atomicExch(tk, 0); // self-cleaning: the next launch / replay + merge_cols(a, sm, G::SM_TMAX, r, hb, G::W * k); +} diff --git a/zig/kernels/cuda/deepseek_v41/decode.cuh b/zig/kernels/cuda/deepseek_v41/decode.cuh new file mode 100644 index 0000000000..2a663ae1c9 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/decode.cuh @@ -0,0 +1,166 @@ +// Copy of src/tensorfold/cuda/exl3/decode.cuh (blob 1acca8f0457c): sass_check.sh + +// After ExLlamaV3, MIT, Copyright (c) 2025 Turboderp. + +// EXL3 tiles: lane L decodes a tile's values 8L..8L+7 straight into its two mma.m16n8k16 B fragments, bit for bit. + +#pragma once + +#include +#include + +namespace tf_exl3 { + +enum Codebook : int { CB_3INST = 0, CB_MCG = 1, CB_MUL1 = 2 }; + +// 32-bit words of a tile: 256 values of K2 / 2 bits. +template +__host__ __device__ constexpr int tile_words() { + return 4 * K2; +} + +// E(p): one past the last stream bit of value p's 16-bit window (stream_ends). +template +__host__ __device__ constexpr int stream_end(int p) { + return (K2 & 1) ? ((p + 1) * K2 - ((p + 1) & 1)) / 2 : (p + 1) * (K2 / 2); +} + +// Words a lane reads for its 8 values: the most any lane's windows span (2, or 3 for 3.5 bits and 5 to 8 bits). +template +__host__ __device__ constexpr int lane_words() { + int most = 0; + for (int l = 0; l < 32; ++l) { + const int first = (stream_end(8 * l) - 16 + 128 * K2) % 32; + const int need = first + stream_end(8 * l + 7) - stream_end(8 * l) + 16; + most = need > most ? need : most; + } + return (most + 31) / 32; +} + +// The index of the first word lane `lane` reads, and the bit offset of its first window in that word. +template +__device__ __forceinline__ void lane_start(int lane, int& word, int& offset) { + const int first = 4 * lane * K2 + K2 / 2 - 16 + 128 * K2; // E(8 lane) - 16, made non-negative + word = (first >> 5) % tile_words(); + offset = first & 31; +} + +// This lane's words of a tile (tile: the tile's first 32-bit word; global or shared memory). +template +__device__ __forceinline__ void load_lane_words(const uint32_t* tile, int lane, uint32_t (&w)[lane_words()]) { + int word, offset; + lane_start(lane, word, offset); +#pragma unroll + for (int i = 0; i < lane_words(); ++i) w[i] = tile[(word + i) % tile_words()]; +} + +// The same from global memory through the read-only cache. +template +__device__ __forceinline__ void ldg_lane_words(const uint32_t* tile, int lane, uint32_t (&w)[lane_words()]) { + int word, offset; + lane_start(lane, word, offset); +#pragma unroll + for (int i = 0; i < lane_words(); ++i) w[i] = __ldg(tile + (word + i) % tile_words()); +} + +// Bits [d, d + 16) of the 96-bit big-endian stream hi:mid:lo (d a compile-time constant after unrolling). +__device__ __forceinline__ uint32_t window16(uint32_t hi, uint32_t mid, uint32_t lo, int d) { + if (d <= 16) return (hi >> (16 - d)) & 0xffffu; + if (d < 32) return __funnelshift_l(mid, hi, d) >> 16; + if (d <= 48) return (mid >> (48 - d)) & 0xffffu; + return __funnelshift_l(lo, mid, d - 32) >> 16; +} + +// The 16-bit states of this lane's 8 values (8 lane + j, j = 0..7) from its words. +template +__device__ __forceinline__ void lane_states(const uint32_t (&w)[lane_words()], int lane, uint32_t (&s)[8]) { + int word, offset; + lane_start(lane, word, offset); + const uint32_t c = lane_words() > 2 ? w[lane_words() > 2 ? 2 : 0] : 0u; + const uint32_t hi = __funnelshift_l(w[1], w[0], offset); // the stream from the lane's first window on + const uint32_t mid = __funnelshift_l(c, w[1], offset); + const uint32_t lo = c << offset; +#pragma unroll + for (int j = 0; j < 8; ++j) s[j] = window16(hi, mid, lo, stream_end(j) - stream_end(0)); +} + +// Two codebook values from two 16-bit states, as a half2 in a uint32 (the first state in the low half). +template +__device__ __forceinline__ uint32_t decode2(uint32_t s0, uint32_t s1) { + if constexpr (CB == CB_MUL1) { + const uint32_t x0 = s0 * 0x83DCD12Du, x1 = s1 * 0x83DCD12Du; + const uint32_t h0 = __dp4a(x0, 0x01010101u, 0x6400u); // fp16 bits of 1024 + the byte sum + const uint32_t h1 = __dp4a(x1, 0x01010101u, 0x6400u); + const uint32_t hh = (h0 & 0xffffu) | (h1 << 16); + const half2 r = __hfma2(*reinterpret_cast(&hh), __half2half2(__ushort_as_half(0x1eee)), + __half2half2(__ushort_as_half(0xc931))); + return *reinterpret_cast(&r); + } else { + uint32_t x0, x1; + if constexpr (CB == CB_MCG) { + x0 = s0 * 0xCBAC1FEDu; + x1 = s1 * 0xCBAC1FEDu; + } else { + x0 = s0 * 89226354u + 64248484u; + x1 = s1 * 89226354u + 64248484u; + } + x0 = (x0 & 0x8FFF8FFFu) ^ 0x3B603B60u; + x1 = (x1 & 0x8FFF8FFFu) ^ 0x3B603B60u; + const uint32_t lo = __byte_perm(x0, x1, 0x5410); + const uint32_t hi = __byte_perm(x0, x1, 0x7632); + const half2 r = __hadd2(*reinterpret_cast(&lo), *reinterpret_cast(&hi)); + return *reinterpret_cast(&r); + } +} + +// One codebook value. +template +__device__ __forceinline__ half decode1(uint32_t s) { + const uint32_t v = decode2(s, s); + return __ushort_as_half(static_cast(v & 0xffffu)); +} + +// This lane's 8 values of a tile as the B fragments of the tile's two n8 halves (columns 0-7 in b0, 8-15 in b1). +template +__device__ __forceinline__ void decode_lane(const uint32_t (&w)[lane_words()], int lane, uint32_t (&b0)[2], + uint32_t (&b1)[2]) { + uint32_t s[8]; + lane_states(w, lane, s); + b0[0] = decode2(s[0], s[1]); + b0[1] = decode2(s[2], s[3]); + b1[0] = decode2(s[4], s[5]); + b1[1] = decode2(s[6], s[7]); +} + +// Row and column in the 16x16 tile (row = k, column = n) of this lane's value j (0..7). +__device__ __forceinline__ int value_row(int lane, int j) { return 2 * (lane & 3) + (j & 1) + 8 * ((j >> 1) & 1); } +__device__ __forceinline__ int value_col(int lane, int j) { return (lane >> 2) + 8 * (j >> 2); } + +// mma.m16n8k16, fp16 inputs, fp32 accumulators: d += a @ b. +__device__ __forceinline__ void mma16816(float (&d)[4], const uint32_t (&a)[4], const uint32_t (&b)[2]) { + asm volatile("mma.sync.aligned.m16n8k16.row.col.f32.f16.f16.f32 {%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, " + "{%0,%1,%2,%3};\n" + : "+f"(d[0]), "+f"(d[1]), "+f"(d[2]), "+f"(d[3]) + : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b[0]), "r"(b[1])); +} + +// Unscaled Walsh-Hadamard transform of 128 fp32 values, 4 a lane, in a fixed butterfly order (multiply by 1/sqrt(128)). +__device__ __forceinline__ void fwht128(float (&v)[4], int lane) { + const float a = v[0] + v[1], b = v[0] - v[1], c = v[2] + v[3], d = v[2] - v[3]; + v[0] = a + c; + v[1] = b + d; + v[2] = a - c; + v[3] = b - d; +#pragma unroll + for (int m = 1; m < 32; m <<= 1) { +#pragma unroll + for (int j = 0; j < 4; ++j) { + const float o = __shfl_xor_sync(0xffffffffu, v[j], m); + v[j] = (lane & m) ? o - v[j] : v[j] + o; + } + } +} + +constexpr float HAD_SCALE = 0.08838834764831845f; // 1 / sqrt(128) + +} // namespace tf_exl3 diff --git a/zig/kernels/cuda/deepseek_v41/dense3.cu b/zig/kernels/cuda/deepseek_v41/dense3.cu new file mode 100644 index 0000000000..cd908255da --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/dense3.cu @@ -0,0 +1,402 @@ +// Copy of src/tensorfold/families/deepseek_v41/cuda/dense3.cu (blob 21c3bed265fe): sass_check.sh +#define DSV41_DENSE3_NO_TORCH + + + +// Dense EXL3 linears v3 (TF_DSV41_DENSE_V3): x3seg's programs and bits, trellis read from a repacked "lanes" layout. + +// Lanes load geometry after antirez/ds4 (cuda/mmq/ds4_mmq.cu q8_0_aligned_dense_vec), which measured it on GB10. + +#define DSV41_X3SEG_NO_TORCH // x3seg.cu's device code only (its tables, helpers and statements) +#include "x3seg.cu" +#ifndef DSV41_DENSE3_NO_TORCH // the kernels alone (nvcc without torch) +#include +#include +#include +#endif + +namespace dsv41_dense3 { + +using namespace tf_exl3; +using dsv41_x3seg::LinSeg; +using dsv41_x3seg::LinTable; +using dsv41_x3seg::MAXSEG; +using dsv41_x3seg::pdl_trigger; +using dsv41_x3seg::pdl_wait; + +// k steps a load group and a lane's words of a group (whole 16-byte chunks); kernels built for K2 4-12, layout to 16 +__host__ __device__ constexpr int group_steps(int K2) { return K2 == 16 ? 1 : 2; } +template +__host__ __device__ constexpr int gwords() { return group_steps(K2) * K2; } + +// Bits [bo, bo + n) (MSB first) of words R[wi], R[wi + 1] as the low n bits of a 64-bit value (bo + n <= 64) +template +__device__ __forceinline__ uint64_t take_bits(const uint32_t (&R)[GW], int p, int n) { + const int wi = p >> 5, bo = p & 31; + const uint64_t x = ((uint64_t)R[wi] << 32) | (wi + 1 < GW ? (uint64_t)R[wi + 1] : 0ull); + return n == 64 ? x : (x << bo) >> (64 - n); +} + +// B fragments of tiles j, j + 1 of step g from group words R: own bits + 16 prefix bits of lane - 1 (one shuffle) +template +__device__ __forceinline__ void pair_frags(const uint32_t (&R)[gwords()], int g, int j, int lane, + uint32_t (&b)[2][2][2]) { + constexpr int n = 4 * K2; + const uint64_t o0 = take_bits(R, (g * 8 + j) * n, n), o1 = take_bits(R, (g * 8 + j + 1) * n, n); + const uint32_t tails = (uint32_t)(o0 & 0xffffu) | ((uint32_t)(o1 & 0xffffu) << 16); + const uint32_t pre = __shfl_sync(0xffffffffu, tails, (lane + 31) & 31); +#pragma unroll + for (int h = 0; h < 2; ++h) { + const uint64_t o = h ? o1 : o0; + const uint64_t p = h ? (pre >> 16) : (pre & 0xffffu); + uint32_t s[8]; +#pragma unroll + for (int v = 0; v < 8; ++v) { + const int sh = (7 - v) * K2 / 2; // the window of value 8 lane + v: bits [sh, sh + 16) + uint64_t w = o >> sh; + if (n - sh < 16) w |= p << (n - sh); + s[v] = (uint32_t)(w & 0xffffu); + } + b[h][0][0] = decode2(s[0], s[1]); + b[h][0][1] = decode2(s[2], s[3]); + b[h][1][0] = decode2(s[4], s[5]); + b[h][1][1] = decode2(s[6], s[7]); + } +} + +// a lane's chunks of one group: GW / 4 coalesced 16-byte loads (warp: 512 contiguous bytes each) +template +__device__ __forceinline__ void load_group(const uint32_t* lane_base, uint32_t (&R)[GW]) { +#pragma unroll + for (int c = 0; c < GW / 4; ++c) { + const uint4 u = __ldg(reinterpret_cast(lane_base + c * 128)); + R[4 * c] = u.x; R[4 * c + 1] = u.y; R[4 * c + 2] = u.z; R[4 * c + 3] = u.w; + } +} + +__device__ __forceinline__ void load_a(const half* x0, const half* x1, int kt, int t, uint32_t (&a)[4]) { + a[0] = __ldg(reinterpret_cast(x0 + kt * 16 + 2 * t)); + a[1] = __ldg(reinterpret_cast(x1 + kt * 16 + 2 * t)); + a[2] = __ldg(reinterpret_cast(x0 + kt * 16 + 2 * t + 8)); + a[3] = __ldg(reinterpret_cast(x1 + kt * 16 + 2 * t + 8)); +} + +// x3seg's seg_linear_kernel with the lanes layout's loads, the rest verbatim; needs per_warp % group_steps(K2) == 0 +template +__global__ void __launch_bounds__(WK * 32, MINB) lanes_linear_kernel(const LinTable tab, int y_dtype, int M) { + constexpr int G = group_steps(K2), GW = gwords(); + extern __shared__ __align__(16) float red[]; // WK * RH * 128 floats + __shared__ int last; + const int RH = min(M, 8); + + LinSeg s = tab.s[0]; +#pragma unroll + for (int i = 1; i < MAXSEG; ++i) + if (i < tab.n && (int)blockIdx.x >= tab.s[i].p0) s = tab.s[i]; + const half* __restrict__ xh = s.xh; + const half* __restrict__ svh = s.svh; + void* __restrict__ y = s.y; + float* __restrict__ Z = s.Z; + int* __restrict__ counters = s.counters; + const int K = s.K, N = s.N, SK = s.SK, wk = s.wk, ld = s.ld; + + const int NB = N >> 7, q = (int)blockIdx.x - s.p0, nb = q % NB, split = q / NB; + const int warp = threadIdx.x >> 5, lane = threadIdx.x & 31; + const int g = lane >> 2, t = lane & 3; + const bool on = warp < wk; // a WK = 4 segment in an 8-warp launch: warps 4-7 idle + const int per_warp = (K >> 4) / SK / wk; + const int kt0 = split * (per_warp * wk) + warp * per_warp; + const int steps = on ? per_warp : 0, groups = steps / G; + const uint32_t* base = s.T + nb * s.stride_nb + (size_t)kt0 * s.stride_k + 4 * lane; + const size_t gstride = (size_t)G * s.stride_k; + const int col0 = nb * 128; + + for (int m0 = 0, pass = 0; m0 < M; m0 += 16, ++pass) { + const int R = min(16, M - m0); + + float acc[8][2][4]; +#pragma unroll + for (int i = 0; i < 8; ++i) +#pragma unroll + for (int h = 0; h < 2; ++h) +#pragma unroll + for (int c = 0; c < 4; ++c) acc[i][h][c] = 0.f; + + const int r0 = m0 + (g < R ? g : R - 1), r1 = m0 + (g + 8 < R ? g + 8 : R - 1); + const half* x0 = xh + (size_t)r0 * K; + const half* x1 = xh + (size_t)r1 * K; + uint32_t cur[GW], nxt[GW]; + if (groups > 0) load_group(base, cur); + if (pass == 0) pdl_wait(); // weights above, xh / Z / counters below + uint32_t a[4], an[4]; + if (steps > 0) load_a(x0, x1, kt0, t, a); +#pragma unroll 1 + for (int gi = 0; gi < groups; ++gi) { + if (gi + 1 < groups) load_group(base + (size_t)(gi + 1) * gstride, nxt); +#pragma unroll + for (int st = 0; st < G; ++st) { + const int i = gi * G + st; + if (i + 1 < steps) load_a(x0, x1, kt0 + i + 1, t, an); // the next step's input, one step early +#pragma unroll + for (int j = 0; j < 8; j += 2) { + uint32_t b[2][2][2]; + pair_frags(cur, st, j, lane, b); + mma16816(acc[j][0], a, b[0][0]); + mma16816(acc[j][1], a, b[0][1]); + mma16816(acc[j + 1][0], a, b[1][0]); + mma16816(acc[j + 1][1], a, b[1][1]); + } +#pragma unroll + for (int c = 0; c < 4; ++c) a[c] = an[c]; + } +#pragma unroll + for (int c = 0; c < GW; ++c) cur[c] = nxt[c]; + } + + if (m0 + 16 >= M) pdl_trigger(); // the last pass's weights are read + + // -- x3seg's statements from here on (the warps' sums in warp order, Z, the last arriver, the epilogue) -- + for (int rlo = 0; rlo < R; rlo += 8) { + const int rn = min(R - rlo, 8); + __syncthreads(); + if (on && g < RH) { +#pragma unroll + for (int i = 0; i < 8; ++i) +#pragma unroll + for (int h = 0; h < 2; ++h) { + const int col = i * 16 + h * 8 + 2 * t; + *reinterpret_cast(red + (warp * RH + g) * 128 + col) = + rlo ? make_float2(acc[i][h][2], acc[i][h][3]) : make_float2(acc[i][h][0], acc[i][h][1]); + } + } + __syncthreads(); + + if (SK == 1) { + for (int r = warp; r < rn; r += WK) { + float v[4]; + const float4 u = *reinterpret_cast(red + r * 128 + 4 * lane); + v[0] = u.x; v[1] = u.y; v[2] = u.z; v[3] = u.w; + for (int w = 1; w < wk; ++w) { + const float4 qv = *reinterpret_cast(red + (w * RH + r) * 128 + 4 * lane); + v[0] += qv.x; v[1] += qv.y; v[2] += qv.z; v[3] += qv.w; + } + dsv41_x3seg::finish(v, lane, svh, nullptr, col0 + 4 * lane); + dsv41_x3seg::store4(y, y_dtype, (size_t)(m0 + rlo + r) * ld + col0 + 4 * lane, v); + } + } else { + for (int idx = threadIdx.x; idx < rn * 32; idx += WK * 32) { + const int r = idx >> 5, c = 4 * (idx & 31); + float4 sm = *reinterpret_cast(red + r * 128 + c); + for (int w = 1; w < wk; ++w) { + const float4 qv = *reinterpret_cast(red + (w * RH + r) * 128 + c); + sm.x += qv.x; sm.y += qv.y; sm.z += qv.z; sm.w += qv.w; + } + *reinterpret_cast(Z + ((size_t)split * M + m0 + rlo + r) * N + col0 + c) = sm; + } + } + } + if (SK > 1) { + __threadfence(); + __syncthreads(); + if (threadIdx.x == 0) last = atomicAdd(counters + pass * NB + nb, 1) == SK - 1; + __syncthreads(); + if (last) { + __threadfence(); + for (int r = warp; r < R; r += WK) { + const size_t at = ((size_t)m0 + r) * N + col0 + 4 * lane; + float4 sm = __ldcg(reinterpret_cast(Z + at)); + for (int qq = 1; qq < SK; ++qq) { + const float4 u = __ldcg(reinterpret_cast(Z + (size_t)qq * M * N + at)); + sm.x += u.x; sm.y += u.y; sm.z += u.z; sm.w += u.w; + } + float v[4] = {sm.x, sm.y, sm.z, sm.w}; + dsv41_x3seg::finish(v, lane, svh, nullptr, col0 + 4 * lane); + dsv41_x3seg::store4(y, y_dtype, (size_t)(m0 + r) * ld + col0 + 4 * lane, v); + } + if (threadIdx.x == 0) counters[pass * NB + nb] = 0; + } + } + __syncthreads(); + } +} + +// Load-time relayout, a thread a dst word: group gi holds lane L's word w at gi G 32 K2 + ((w / 4) 32 + L) 4 + w % 4 +__device__ __forceinline__ uint32_t bit_of(const uint32_t* tile, int sb) { return (tile[sb >> 5] >> (31 - (sb & 31))) & 1u; } + +__global__ void to_lanes_kernel(const uint32_t* __restrict__ src, uint32_t* __restrict__ dst, long long total, int K2, + int KT) { + const long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x; + if (i >= total) return; + const int G = group_steps(K2), n = 4 * K2, TW = 4 * K2; + const long long strip_words = (long long)KT * 32 * K2, gw = (long long)G * 32 * K2; + const long long strip = i / strip_words, r = i % strip_words, gi = r / gw; + const int qd = (int)(r % gw), c = qd / 128, L = (qd % 128) / 4, w = 4 * c + qd % 4; + const uint32_t* s0 = src + strip * strip_words; + uint32_t out = 0; + for (int b = 0; b < 32; ++b) { + const int lb = 32 * w + b, tt = lb / n, ob = lb % n; + const long long kt = gi * G + tt / 8; + out |= bit_of(s0 + kt * 32 * K2 + (tt % 8) * TW, n * L + ob) << (31 - b); + } + dst[i] = out; +} + +__global__ void to_strips_kernel(const uint32_t* __restrict__ src, uint32_t* __restrict__ dst, long long total, int K2, + int KT) { + const long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x; + if (i >= total) return; + const int G = group_steps(K2), n = 4 * K2, TW = 4 * K2; + const long long strip_words = (long long)KT * 32 * K2; + const long long strip = i / strip_words, r = i % strip_words, kt = r / (32 * K2); + const int tw = (int)(r % (32 * K2)), j = tw / TW, wd = tw % TW; + const long long gi = kt / G; + const int tt = (int)(kt % G) * 8 + j; + const uint32_t* s0 = src + strip * strip_words + gi * (long long)G * 32 * K2; + uint32_t out = 0; + for (int b = 0; b < 32; ++b) { + const int sb = 32 * wd + b, L = sb / n, lb = tt * n + sb % n, w = lb >> 5; + out |= ((s0[((w >> 2) * 32 + L) * 4 + (w & 3)] >> (31 - (lb & 31))) & 1u) << (31 - b); + } + dst[i] = out; +} + +// W_q [K, N] fp16 from the lanes layout (upstream unpack_kernel's values): one warp a (16-column tile, k step) +template +__global__ void __launch_bounds__(32) lanes_unpack_kernel(const uint32_t* __restrict__ T, half* __restrict__ W, int N, + int64_t stride_k, int64_t stride_nb) { + constexpr int G = group_steps(K2), GW = gwords(); + const int kt = blockIdx.y, nt = blockIdx.x, lane = threadIdx.x, j = nt & 6; + uint32_t R[GW]; + load_group(T + (nt >> 3) * stride_nb + (size_t)(kt / G) * G * stride_k + 4 * lane, R); + uint32_t b[2][2][2]; + pair_frags(R, kt % G, j, lane, b); + const int h = nt & 1; +#pragma unroll + for (int v = 0; v < 8; ++v) { + const uint32_t x = b[h][v >> 2][(v >> 1) & 1]; + const unsigned short u = (v & 1) ? (unsigned short)(x >> 16) : (unsigned short)(x & 0xffffu); + W[(size_t)(kt * 16 + value_row(lane, v)) * N + nt * 16 + value_col(lane, v)] = __ushort_as_half(u); + } +} + +} // namespace dsv41_dense3 + +#ifndef DSV41_DENSE3_NO_TORCH + +using namespace dsv41_dense3; + +namespace { +template +void launch_ex(Kn kernel, dim3 grid, dim3 block, int smem, bool pdl, A... args) { + cudaLaunchConfig_t cfg = {}; + cfg.gridDim = grid; + cfg.blockDim = block; + cfg.dynamicSmemBytes = (size_t)smem; + cfg.stream = at::cuda::getCurrentCUDAStream(); + cudaLaunchAttribute attr[1]; + attr[0].id = cudaLaunchAttributeProgrammaticStreamSerialization; + attr[0].val.programmaticStreamSerializationAllowed = 1; + cfg.attrs = attr; + cfg.numAttrs = pdl ? 1 : 0; + C10_CUDA_CHECK(cudaLaunchKernelEx(&cfg, kernel, args...)); +} +} // namespace + +// x3seg's linear launcher (meta: 11 ints a segment) for words in the lanes layout +void dsv41_dense3_linear_cuda(const at::Tensor& xh, const std::vector& T, + const std::vector& svh, const std::vector& ys, + const c10::optional& Z, at::Tensor& counters, + const std::vector& meta, int64_t programs, int64_t K2, int64_t WK, + int64_t minb, bool pdl) { + LinTable tab{}; + tab.n = (int)T.size(); + const int M = (int)ys[0].size(0); + const auto st = ys[0].scalar_type(); + const int y_dtype = st == at::kFloat ? dsv41_x3seg::F32 : st == at::kBFloat16 ? dsv41_x3seg::BF16 : dsv41_x3seg::F16; + float* zptr = Z ? Z->data_ptr() : nullptr; + for (int i = 0; i < tab.n; ++i) { + const int64_t* m = meta.data() + 11 * i; + const int64_t per_warp = m[0] / 16 / m[2] / m[3]; + TORCH_CHECK(per_warp % group_steps((int)K2) == 0, "dense3: ", per_warp, " k steps a warp do not split into ", + group_steps((int)K2), "-step load groups (dense3.why_not)"); + const size_t esz = ys[i].element_size(); + tab.s[i] = LinSeg{reinterpret_cast(xh.data_ptr()) + m[6], + reinterpret_cast(T[i].data_ptr()), (long long)m[4], (long long)m[5], + reinterpret_cast(svh[i].data_ptr()), + static_cast(ys[i].data_ptr()) + (size_t)m[9] * esz, + m[2] > 1 ? zptr + m[7] : nullptr, counters.data_ptr() + m[8], + (int)m[0], (int)m[1], (int)m[2], (int)m[3], (int)m[10], (int)ys[i].stride(0)}; + } + const int smem = (int)(WK * std::min(M, 8) * 128 * sizeof(float)); +#define TF_D3_LAUNCH(K2_, WK_, MINB_) \ + if (K2 == K2_ && WK == WK_ && minb == MINB_) { \ + auto kernel = lanes_linear_kernel; \ + if (smem > 48 * 1024) cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, smem); \ + launch_ex(kernel, dim3((unsigned)programs), dim3((unsigned)(WK_ * 32)), smem, pdl, tab, y_dtype, M); \ + return; \ + } +#define TF_D3_WIDTH(K2_) TF_D3_LAUNCH(K2_, 4, 3) TF_D3_LAUNCH(K2_, 4, 4) TF_D3_LAUNCH(K2_, 8, 1) TF_D3_LAUNCH(K2_, 8, 2) + TF_D3_WIDTH(4) TF_D3_WIDTH(6) TF_D3_WIDTH(8) TF_D3_WIDTH(10) TF_D3_WIDTH(12) // mul1; 8 bits: no attention matrix +#undef TF_D3_WIDTH +#undef TF_D3_LAUNCH + TORCH_CHECK(false, "dense3: unsupported EXL3 width / warps / blocks: K2=", K2, " WK=", WK, " MINB=", minb); +} + +// src -> dst (int32 words of one matrix, [N/128, K/16, 8, 4 K2]); to_lanes or back +void dsv41_dense3_relayout_cuda(const at::Tensor& src, at::Tensor& dst, int64_t K2, int64_t K, bool lanes) { + const long long total = src.numel(); + const unsigned blocks = (unsigned)((total + 255) / 256); + auto stream = at::cuda::getCurrentCUDAStream(); + const auto* s = reinterpret_cast(src.data_ptr()); + auto* d = reinterpret_cast(dst.data_ptr()); + if (lanes) + to_lanes_kernel<<>>(s, d, total, (int)K2, (int)(K / 16)); + else + to_strips_kernel<<>>(s, d, total, (int)K2, (int)(K / 16)); + C10_CUDA_KERNEL_LAUNCH_CHECK(); +} + +void dsv41_dense3_unpack_cuda(const at::Tensor& T, at::Tensor& W, int64_t stride_k, int64_t stride_nb, int64_t K2) { + const int K = (int)W.size(0), N = (int)W.size(1); + dim3 grid((unsigned)(N / 16), (unsigned)(K / 16)); + auto stream = at::cuda::getCurrentCUDAStream(); +#define TF_D3_UNPACK(K2_) \ + if (K2 == K2_) { \ + lanes_unpack_kernel<<>>(reinterpret_cast(T.data_ptr()), \ + reinterpret_cast(W.data_ptr()), N, stride_k, \ + stride_nb); \ + C10_CUDA_KERNEL_LAUNCH_CHECK(); \ + return; \ + } + TF_D3_UNPACK(4) TF_D3_UNPACK(6) TF_D3_UNPACK(8) TF_D3_UNPACK(10) TF_D3_UNPACK(12) +#undef TF_D3_UNPACK + TORCH_CHECK(false, "dense3: unsupported EXL3 width K2=", K2); +} +#endif // DSV41_DENSE3_NO_TORCH + +// The instantiations dsv41_dense3_linear_cuda / _unpack_cuda launch (TF_D3_WIDTH, TF_D3_UNPACK): mul1. +template __global__ void dsv41_dense3::lanes_linear_kernel<4, 2, 4, 3>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_linear_kernel<4, 2, 4, 4>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_linear_kernel<4, 2, 8, 1>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_linear_kernel<4, 2, 8, 2>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_unpack_kernel<4, 2>(const uint32_t*, half*, int, int64_t, int64_t); +template __global__ void dsv41_dense3::lanes_linear_kernel<6, 2, 4, 3>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_linear_kernel<6, 2, 4, 4>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_linear_kernel<6, 2, 8, 1>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_linear_kernel<6, 2, 8, 2>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_unpack_kernel<6, 2>(const uint32_t*, half*, int, int64_t, int64_t); +template __global__ void dsv41_dense3::lanes_linear_kernel<8, 2, 4, 3>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_linear_kernel<8, 2, 4, 4>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_linear_kernel<8, 2, 8, 1>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_linear_kernel<8, 2, 8, 2>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_unpack_kernel<8, 2>(const uint32_t*, half*, int, int64_t, int64_t); +template __global__ void dsv41_dense3::lanes_linear_kernel<10, 2, 4, 3>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_linear_kernel<10, 2, 4, 4>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_linear_kernel<10, 2, 8, 1>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_linear_kernel<10, 2, 8, 2>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_unpack_kernel<10, 2>(const uint32_t*, half*, int, int64_t, int64_t); +template __global__ void dsv41_dense3::lanes_linear_kernel<12, 2, 4, 3>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_linear_kernel<12, 2, 4, 4>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_linear_kernel<12, 2, 8, 1>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_linear_kernel<12, 2, 8, 2>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_dense3::lanes_unpack_kernel<12, 2>(const uint32_t*, half*, int, int64_t, int64_t); diff --git a/zig/kernels/cuda/deepseek_v41/exl3_experts.cu b/zig/kernels/cuda/deepseek_v41/exl3_experts.cu new file mode 100644 index 0000000000..a0959e216b --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/exl3_experts.cu @@ -0,0 +1,283 @@ +// Copy of src/tensorfold/cuda/exl3/experts.cu (blob 8ce66ca83bce): sass_check.sh + +// EXL3 routed experts, a width per expert: fixed-order splits, slots and butterflies, no atomics, so bit-exact. + +#include +#include +#include // what the cut torch includes provided: the fixed-width integers + +#include "experts_grouped.cuh" + +namespace tf_exl3_experts { + +constexpr float HAD_SCALE = 0.08838834764831845f; // 1 / sqrt(128) + +// Grouping in one block: distinct experts (< E) in id order, members row * 32 + slot in row order, -1 after the last. +constexpr int GROUP_THREADS = 1024; +constexpr int GROUP_PER_THREAD = 4; + +__global__ void __launch_bounds__(GROUP_THREADS) group_kernel(const int* __restrict__ pick, int* __restrict__ uids, + int* __restrict__ ucount, int* __restrict__ members, + int R, int slots, int E, int maxm) { + extern __shared__ int sh_pick[]; + __shared__ int warp_tot[GROUP_THREADS / 32]; + const int n = R * slots; + for (int i = threadIdx.x; i < n; i += GROUP_THREADS) sh_pick[i] = pick[i]; + __syncthreads(); + int cnt[GROUP_PER_THREAD]; + int used = 0; +#pragma unroll + for (int q = 0; q < GROUP_PER_THREAD; ++q) { + const int e = threadIdx.x * GROUP_PER_THREAD + q; + int c = 0; + if (e < E) + for (int i = 0; i < n; ++i) c += sh_pick[i] == e; + cnt[q] = c; + used += c > 0; + } + // exclusive scan of `used` over threads + const int lane = threadIdx.x & 31, warp = threadIdx.x >> 5; + int inc = used; +#pragma unroll + for (int o = 1; o < 32; o <<= 1) { + int v = __shfl_up_sync(0xffffffffu, inc, o); + if (lane >= o) inc += v; + } + if (lane == 31) warp_tot[warp] = inc; + __syncthreads(); + if (warp == 0) { + int v = warp_tot[lane]; + int s = v; +#pragma unroll + for (int o = 1; o < 32; o <<= 1) { + int x = __shfl_up_sync(0xffffffffu, s, o); + if (lane >= o) s += x; + } + warp_tot[lane] = s - v; // exclusive per warp + if (lane == 31) ucount[0] = s; + } + __syncthreads(); + int place = warp_tot[warp] + inc - used; +#pragma unroll + for (int q = 0; q < GROUP_PER_THREAD; ++q) { + if (cnt[q] == 0) continue; + const int e = threadIdx.x * GROUP_PER_THREAD + q; + uids[place] = e; + int j = 0; + for (int i = 0; i < n && j < maxm; ++i) + if (sh_pick[i] == e) members[place * maxm + j++] = (i / slots) * 32 + (i % slots); + for (; j < maxm; ++j) members[place * maxm + j] = -1; + ++place; + } +} + +// Walsh-Hadamard of 128 values, 4 a lane, fixed butterfly order (strides 1, 2 in registers, 4..64 across lanes). +__device__ __forceinline__ void fwht128(float (&v)[4], int lane) { + float a = v[0] + v[1], b = v[0] - v[1], c = v[2] + v[3], d = v[2] - v[3]; + v[0] = a + c; v[1] = b + d; v[2] = a - c; v[3] = b - d; +#pragma unroll + for (int m = 1; m < 32; m <<= 1) { +#pragma unroll + for (int j = 0; j < 4; ++j) { + float o = __shfl_xor_sync(0xffffffffu, v[j], m); + v[j] = (lane & m) ? o - v[j] : v[j] + o; + } + } +} + +template __device__ __forceinline__ float to_f(T v); +template <> __device__ __forceinline__ float to_f<__nv_bfloat16>(__nv_bfloat16 v) { return __bfloat162float(v); } +template <> __device__ __forceinline__ float to_f(half v) { return __half2float(v); } + +// Program (member row, 128-block of K, matrix): Xh = fp16((x * suh) @ H) for gate and up of every routed slot. +template +__global__ void rot_in_kernel(const TIN* __restrict__ x, int x_stride, const int* __restrict__ pick, + const half* __restrict__ suh0, const half* __restrict__ suh1, half* __restrict__ out0, + half* __restrict__ out1, int K, int slots, int E) { + const int p = blockIdx.x, blk = blockIdx.y, mat = blockIdx.z; + const int row = p / slots; + const int e = pick[p]; + if (e < 0 || e >= E) return; + const int lane = threadIdx.x; + const half* suh = (mat ? suh1 : suh0) + (size_t)e * K + blk * 128 + 4 * lane; + const TIN* xr = x + (size_t)row * x_stride + blk * 128 + 4 * lane; + float v[4]; +#pragma unroll + for (int j = 0; j < 4; ++j) v[j] = to_f(xr[j]) * __half2float(suh[j]); + fwht128(v, lane); + half* o = (mat ? out1 : out0) + (size_t)p * K + blk * 128 + 4 * lane; +#pragma unroll + for (int j = 0; j < 4; ++j) o[j] = __float2half_rn(v[j] * HAD_SCALE); +} + +__device__ __forceinline__ float bf16r(float x) { return __bfloat162float(__float2bfloat16_rn(x)); } + +// Program (row, 128-block of width): ordered splits, rotate, * svh, SwiGLU (0: bf16 roundings, 1: fp32), act -> Xd. +__global__ void gateup_epilogue_kernel(const float* __restrict__ Z, const int* __restrict__ pick, + const half* __restrict__ svh_g, const half* __restrict__ svh_u, + const half* __restrict__ suh_d, half* __restrict__ xd, int P, int N, int SK, + int E, float limit, int act_mode) { + const int p = blockIdx.x, blk = blockIdx.y; + const int e = pick[p]; + if (e < 0 || e >= E) return; + const int lane = threadIdx.x; + const int n = blk * 128 + 4 * lane; + float gv[4], uv[4]; +#pragma unroll + for (int j = 0; j < 4; ++j) { + float sg = 0.f, su = 0.f; + for (int s = 0; s < SK; ++s) { + sg += Z[((size_t)(0 * SK + s) * P + p) * N + n + j]; + su += Z[((size_t)(1 * SK + s) * P + p) * N + n + j]; + } + gv[j] = sg; + uv[j] = su; + } + fwht128(gv, lane); + fwht128(uv, lane); + float v[4]; +#pragma unroll + for (int j = 0; j < 4; ++j) { + float act; + if (act_mode == 0) { + float gg = fminf(bf16r(gv[j] * HAD_SCALE * __half2float(svh_g[(size_t)e * N + n + j])), limit); + float uu = fminf(fmaxf(bf16r(uv[j] * HAD_SCALE * __half2float(svh_u[(size_t)e * N + n + j])), -limit), + limit); + act = bf16r(bf16r(gg / (1.f + expf(-gg))) * uu); + } else { + float gg = fminf(gv[j] * HAD_SCALE * __half2float(svh_g[(size_t)e * N + n + j]), limit); + float uu = fminf(fmaxf(uv[j] * HAD_SCALE * __half2float(svh_u[(size_t)e * N + n + j]), -limit), limit); + act = gg / (1.f + expf(-gg)) * uu; + } + v[j] = act * __half2float(suh_d[(size_t)e * N + n + j]); + } + fwht128(v, lane); + half* o = xd + (size_t)p * N + n; +#pragma unroll + for (int j = 0; j < 4; ++j) o[j] = __float2half_rn(v[j] * HAD_SCALE); +} + +// Program (member row, 128-block of the model width): Y = (splits summed in order) @ H * svh_d, fp32. +__global__ void down_epilogue_kernel(const float* __restrict__ Z, const int* __restrict__ pick, + const half* __restrict__ svh_d, float* __restrict__ y, int P, int D, int SK, + int E) { + const int p = blockIdx.x, blk = blockIdx.y; + const int e = pick[p]; + if (e < 0 || e >= E) return; + const int lane = threadIdx.x; + const int n = blk * 128 + 4 * lane; + float v[4]; +#pragma unroll + for (int j = 0; j < 4; ++j) { + float s = 0.f; + for (int k = 0; k < SK; ++k) s += Z[((size_t)k * P + p) * D + n + j]; + v[j] = s; + } + fwht128(v, lane); + float* o = y + (size_t)p * D + n; +#pragma unroll + for (int j = 0; j < 4; ++j) o[j] = v[j] * HAD_SCALE * __half2float(svh_d[(size_t)e * D + n + j]); +} + +// out[r][d] = sum over slots in order of wts[r][k] * y[r * slots + k][d] (fp32, fma chain from 0). +__global__ void combine_kernel(const float* __restrict__ y, const float* __restrict__ wts, float* __restrict__ out, + int D, int slots) { + const int r = blockIdx.x; + const int d = blockIdx.y * blockDim.x + threadIdx.x; + if (d >= D) return; + float acc = 0.f; + for (int k = 0; k < slots; ++k) acc = fmaf(wts[r * slots + k], y[((size_t)r * slots + k) * D + d], acc); + out[(size_t)r * D + d] = acc; +} + +// down_epilogue_kernel then combine_kernel in one launch, the same arithmetic in the same order (the same bits). +__device__ __forceinline__ void store_out(float* o, float v) { *o = v; } +__device__ __forceinline__ void store_out(__nv_bfloat16* o, float v) { *o = __float2bfloat16_rn(v); } + +// OutT bf16: the fp32 sum rounded to nearest even as it is stored (same bits as fp32 out then to bfloat16) +template +__global__ void down_combine_kernel(const float* __restrict__ Z, const int* __restrict__ pick, + const half* __restrict__ svh_d, float* __restrict__ y, + const float* __restrict__ wts, OutT* __restrict__ out, int P, int D, int SK, + int E, int slots) { + __shared__ float4 part[32][32]; // [slot][lane]: the slot's 4 outputs of the lane + const int r = blockIdx.x, blk = blockIdx.y; + const int k = threadIdx.x >> 5, lane = threadIdx.x & 31; + const int n = blk * 128 + 4 * lane; + const int p = r * slots + k; + const int e = pick[p]; + float o[4]; + if (e >= 0 && e < E) { + float v[4]; +#pragma unroll + for (int j = 0; j < 4; ++j) { + float s = 0.f; + for (int q = 0; q < SK; ++q) s += Z[((size_t)q * P + p) * D + n + j]; + v[j] = s; + } + fwht128(v, lane); +#pragma unroll + for (int j = 0; j < 4; ++j) { + o[j] = v[j] * HAD_SCALE * __half2float(svh_d[(size_t)e * D + n + j]); + y[(size_t)p * D + n + j] = o[j]; + } + } else { +#pragma unroll + for (int j = 0; j < 4; ++j) o[j] = y[(size_t)p * D + n + j]; + } + part[k][lane] = make_float4(o[0], o[1], o[2], o[3]); + __syncthreads(); + if (k != 0) return; + float acc[4] = {0.f, 0.f, 0.f, 0.f}; + for (int q = 0; q < slots; ++q) { + const float w = wts[r * slots + q]; + const float4 u = part[q][lane]; + acc[0] = fmaf(w, u.x, acc[0]); + acc[1] = fmaf(w, u.y, acc[1]); + acc[2] = fmaf(w, u.z, acc[2]); + acc[3] = fmaf(w, u.w, acc[3]); + } +#pragma unroll + for (int j = 0; j < 4; ++j) store_out(out + (size_t)r * D + n + j, acc[j]); +} + +} // namespace tf_exl3_experts + +// upstream grouped_kernel at mul1 for every (nt, warps, pf) x TF_RANGES, and dequant_kernel<2, K2> (tests) +#define TF_EXL3X_INST(NT, W, PF, LO, HI) template __global__ void tf_exl3x::grouped_kernel<2, NT, W, PF, LO, HI>( \ + const half*, const half*, const int64_t*, const int64_t*, const int*, const int*, const int*, const int*, \ + const int*, float*, int, int, int, int, int, int); +TF_EXL3X_INST(8, 4, 1, 8, 8) +TF_EXL3X_INST(8, 4, 1, 2, 10) +TF_EXL3X_INST(8, 4, 1, 2, 16) +TF_EXL3X_INST(8, 4, 2, 8, 8) +TF_EXL3X_INST(8, 4, 2, 2, 10) +TF_EXL3X_INST(8, 4, 2, 2, 16) +TF_EXL3X_INST(4, 4, 2, 8, 8) +TF_EXL3X_INST(4, 4, 2, 2, 10) +TF_EXL3X_INST(4, 4, 2, 2, 16) +template __global__ void tf_exl3x::dequant_kernel<2, 2>(const uint32_t*, half*, int, int); +template __global__ void tf_exl3x::dequant_kernel<2, 3>(const uint32_t*, half*, int, int); +template __global__ void tf_exl3x::dequant_kernel<2, 4>(const uint32_t*, half*, int, int); +template __global__ void tf_exl3x::dequant_kernel<2, 5>(const uint32_t*, half*, int, int); +template __global__ void tf_exl3x::dequant_kernel<2, 6>(const uint32_t*, half*, int, int); +template __global__ void tf_exl3x::dequant_kernel<2, 7>(const uint32_t*, half*, int, int); +template __global__ void tf_exl3x::dequant_kernel<2, 8>(const uint32_t*, half*, int, int); +template __global__ void tf_exl3x::dequant_kernel<2, 9>(const uint32_t*, half*, int, int); +template __global__ void tf_exl3x::dequant_kernel<2, 10>(const uint32_t*, half*, int, int); +template __global__ void tf_exl3x::dequant_kernel<2, 11>(const uint32_t*, half*, int, int); +template __global__ void tf_exl3x::dequant_kernel<2, 12>(const uint32_t*, half*, int, int); +template __global__ void tf_exl3x::dequant_kernel<2, 13>(const uint32_t*, half*, int, int); +template __global__ void tf_exl3x::dequant_kernel<2, 14>(const uint32_t*, half*, int, int); +template __global__ void tf_exl3x::dequant_kernel<2, 15>(const uint32_t*, half*, int, int); +template __global__ void tf_exl3x::dequant_kernel<2, 16>(const uint32_t*, half*, int, int); +// down_combine_kernel at both outputs the binding takes (fp32, bf16: TF_DSV41_MOE_BF16) +template __global__ void tf_exl3_experts::down_combine_kernel(const float*, const int*, const half*, float*, \ + const float*, float*, int, int, int, int, int); +template __global__ void tf_exl3_experts::down_combine_kernel<__nv_bfloat16>(const float*, const int*, const half*, float*, \ + const float*, __nv_bfloat16*, int, int, int, int, int); +// rot_in_kernel for both inputs the binding takes +template __global__ void tf_exl3_experts::rot_in_kernel<__nv_bfloat16>(const __nv_bfloat16*, int, const int*, const half*, const half*, half*, \ + half*, int, int, int); +template __global__ void tf_exl3_experts::rot_in_kernel(const half*, int, const int*, const half*, const half*, half*, \ + half*, int, int, int); diff --git a/zig/kernels/cuda/deepseek_v41/experts_grouped.cuh b/zig/kernels/cuda/deepseek_v41/experts_grouped.cuh new file mode 100644 index 0000000000..ab7591ac2e --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/experts_grouped.cuh @@ -0,0 +1,311 @@ +// Copy of src/tensorfold/cuda/exl3/experts_grouped.cuh (blob 57be4c027785): sass_check.sh + +// After ExLlamaV3, MIT, Copyright (c) 2025 Turboderp. + +// Grouped EXL3 expert GEMV: rows stay independent, K ranges fixed by shape, warps summed in order. + +#pragma once + +#include +#include +#include // what the cut torch includes provided: the fixed-width integers + +namespace tf_exl3x { + +// Two codebook values (CB 0 3inst, 1 mcg, 2 mul1) as a half2, bit-identical to ExLlamaV3's decode_3inst_2. +template +__device__ __forceinline__ uint32_t cb_pair(uint32_t s0, uint32_t s1) { + if constexpr (CB == 2) { + const uint32_t x0 = s0 * 0x83DCD12Du, x1 = s1 * 0x83DCD12Du; + const uint32_t sum0 = __dp4a(x0, 0x01010101u, 0x6400u); + const uint32_t sum1 = __dp4a(x1, 0x01010101u, 0x6400u); + const uint32_t hv = __byte_perm(sum0, sum1, 0x5410); + half2 h = *reinterpret_cast(&hv); + half2 r = __hfma2(h, __half2half2(__ushort_as_half(0x1eee)), __half2half2(__ushort_as_half(0xc931))); + return *reinterpret_cast(&r); + } else { + uint32_t x0, x1; + if constexpr (CB == 1) { + x0 = s0 * 0xCBAC1FEDu; + x1 = s1 * 0xCBAC1FEDu; + } else { + x0 = s0 * 89226354u + 64248484u; + x1 = s1 * 89226354u + 64248484u; + } + x0 = (x0 & 0x8FFF8FFFu) ^ 0x3B603B60u; + x1 = (x1 & 0x8FFF8FFFu) ^ 0x3B603B60u; + uint32_t lo = __byte_perm(x0, x1, 0x5410); + uint32_t hi = __byte_perm(x0, x1, 0x7632); + half2 r = __hadd2(*reinterpret_cast(&lo), *reinterpret_cast(&hi)); + return *reinterpret_cast(&r); + } +} + +// K2 half-bits a value: a tile is 4 * K2 words; a lane's eight windows fall in NG runs of GV within two words. +template +struct Fmt { + static constexpr int TW = 4 * K2; + static constexpr int LW = (TW + 31) / 32; + // windows sharing one 64-bit merge (tested for every K2) + static constexpr int GV = (K2 >= 13) ? 2 : ((K2 == 7 || (K2 >= 9 && K2 <= 12) || K2 == 16) ? 4 : 8); + static constexpr int NG = 8 / GV; + __host__ __device__ static constexpr int end(int p) { return (p >> 1) * K2 + ((p & 1) ? K2 : (K2 >> 1)); } + // right shift of window j of a run (run starts at an even position) relative to the run's last window + __host__ __device__ static constexpr int off(int j) { return end(GV - 1) - end(j); } +}; + +template +struct LaneMap { + int hi[Fmt::NG], lo[Fmt::NG], sh[Fmt::NG]; + __device__ __forceinline__ explicit LaneMap(int lane) { + constexpr int TW = Fmt::TW, GV = Fmt::GV; +#pragma unroll + for (int g = 0; g < Fmt::NG; ++g) { + const int last_end = Fmt::end(8 * lane + g * GV + GV - 1) + 128 * K2; + const int hr = (last_end - 1) >> 5; + hi[g] = hr % TW; + lo[g] = (hr + TW - 1) % TW; + sh[g] = (hr + 1) * 32 - last_end; + } + } +}; + +template +__device__ __forceinline__ uint32_t fetch(const uint32_t (&w)[LW], int idx) { + if constexpr (LW == 1) { + return __shfl_sync(0xffffffffu, w[0], idx); + } else { + const uint32_t a = __shfl_sync(0xffffffffu, w[0], idx & 31); + const uint32_t b = __shfl_sync(0xffffffffu, w[1], idx & 31); + return idx < 32 ? a : b; + } +} + +// This lane's eight values of a tile as the B fragments of its two n8 halves. +template +__device__ __forceinline__ void decode_tile(const uint32_t (&w)[Fmt::LW], const LaneMap& m, int lane, + uint32_t (&b0)[2], uint32_t (&b1)[2]) { + uint32_t st[8]; + if constexpr (K2 == 8) { + // 4 bits: lane L's windows are exactly words L-1 and L (as the 4-bit EXL3 decode) + const uint32_t p = __shfl_sync(0xffffffffu, w[0], (lane + 31) & 31); + const uint32_t s = __funnelshift_r(w[0], p, 20); + st[0] = (s >> 8) & 0xffffu; + st[1] = (s >> 4) & 0xffffu; + st[2] = s & 0xffffu; + st[3] = w[0] >> 16; + st[4] = (w[0] >> 12) & 0xffffu; + st[5] = (w[0] >> 8) & 0xffffu; + st[6] = (w[0] >> 4) & 0xffffu; + st[7] = w[0] & 0xffffu; + } else { + constexpr int GV = Fmt::GV, NG = Fmt::NG; +#pragma unroll + for (int g = 0; g < NG; ++g) { + const uint32_t whi = fetch::LW>(w, m.hi[g]); + const uint32_t wlo = fetch::LW>(w, m.lo[g]); + const uint64_t mm = ((((uint64_t)wlo) << 32) | whi) >> m.sh[g]; +#pragma unroll + for (int j = 0; j < GV; ++j) st[g * GV + j] = (uint32_t)(mm >> Fmt::off(j)) & 0xffffu; + } + } + b0[0] = cb_pair(st[0], st[1]); + b0[1] = cb_pair(st[2], st[3]); + b1[0] = cb_pair(st[4], st[5]); + b1[1] = cb_pair(st[6], st[7]); +} + +__device__ __forceinline__ void mma16816(float (&d)[4], const uint32_t (&a)[4], const uint32_t (&b)[2]) { + asm volatile("mma.sync.aligned.m16n8k16.row.col.f32.f16.f16.f32 {%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, " + "{%0,%1,%2,%3};\n" + : "+f"(d[0]), "+f"(d[1]), "+f"(d[2]), "+f"(d[3]) + : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b[0]), "r"(b[1])); +} + +__device__ __forceinline__ uint32_t load_pair(const half* x, bool ok) { + return ok ? *reinterpret_cast(x) : 0u; +} + +template +__device__ __forceinline__ void load_words(uint32_t (&dst)[Fmt::LW], const uint32_t* p, int lane) { + constexpr int TW = Fmt::TW; +#pragma unroll + for (int l = 0; l < Fmt::LW; ++l) { + if constexpr ((TW % 32) == 0) + dst[l] = __ldg(p + l * 32); + else + dst[l] = (l * 32 + lane < TW) ? __ldg(p + l * 32) : 0u; + } +} + +// One warp's k tiles [kt0, kt0 + nkt) of an expert matrix into acc, PF tiles in flight. +template +__device__ __forceinline__ void warp_tiles(const uint32_t* __restrict__ T, int NTILES, int kt0, int nkt, int nt0, + const half* x0, const half* x1, bool ok0, bool ok1, int lane, + float (&acc)[NT][2][4]) { + constexpr int TW = Fmt::TW, LW = Fmt::LW; + const LaneMap map(lane); + const size_t kstride = (size_t)NTILES * TW; + const uint32_t* tp = T + ((size_t)kt0 * NTILES + nt0) * TW + lane; + + uint32_t pf[PF][NT][LW]; +#pragma unroll + for (int d = 0; d < PF; ++d) + if (d < nkt) +#pragma unroll + for (int i = 0; i < NT; ++i) load_words(pf[d][i], tp + d * kstride + i * TW, lane); + + for (int ib = 0; ib < nkt; ib += PF) { +#pragma unroll + for (int d = 0; d < PF; ++d) { + const int it = ib + d; + if (it < nkt) { + uint32_t w[NT][LW]; +#pragma unroll + for (int i = 0; i < NT; ++i) +#pragma unroll + for (int l = 0; l < LW; ++l) w[i][l] = pf[d][i][l]; + if (it + PF < nkt) +#pragma unroll + for (int i = 0; i < NT; ++i) + load_words(pf[d][i], tp + (size_t)(it + PF) * kstride + i * TW, lane); + const int k = (kt0 + it) * 16; + uint32_t a[4] = {load_pair(x0 + k, ok0), load_pair(x1 + k, ok1), load_pair(x0 + k + 8, ok0), + load_pair(x1 + k + 8, ok1)}; +#pragma unroll + for (int i = 0; i < NT; ++i) { + uint32_t b0[2], b1[2]; + decode_tile(w[i], map, lane, b0, b1); + mma16816(acc[i][0], a, b0); + mma16816(acc[i][1], a, b1); + } + } + } + } +} + +// The K2 values an instance covering [LO, HI] compiles (half-bits 2..16). +__host__ __device__ constexpr bool k2_supported(int k2) { + return k2 >= 2 && k2 <= 16; +} + +// Program (expert u, n block, split, member tile): up to 16 members x W_q over the split's K range; warps in order. +template +__global__ void __launch_bounds__(W * 32) grouped_kernel( + const half* __restrict__ X0, const half* __restrict__ X1, const int64_t* __restrict__ TP0, + const int64_t* __restrict__ TP1, const int* __restrict__ K2_0, const int* __restrict__ K2_1, + const int* __restrict__ uids, const int* __restrict__ ucount, const int* __restrict__ members, + float* __restrict__ Z, int K, int N, int P, int SK, int maxm, int slots) { + const int u = blockIdx.x; + if (u >= ucount[0]) return; + const int MT = (maxm + 15) / 16; + const int mtile = blockIdx.z % MT; + const int split = (blockIdx.z / MT) % SK; + const int mat = blockIdx.z / MT / SK; + const half* X = mat ? X1 : X0; + const int e = uids[u]; + const uint32_t* T = reinterpret_cast(mat ? TP1[e] : TP0[e]); + const int k2 = mat ? K2_1[e] : K2_0[e]; + const int warp = threadIdx.x >> 5, lane = threadIdx.x & 31; + const int g = lane >> 2, t = lane & 3; + const int KT = K >> 4, NTILES = N >> 4; + + __shared__ int rows_sh[16]; + if (threadIdx.x < 16) { + const int m = mtile * 16 + threadIdx.x; + const int code = m < maxm ? members[u * maxm + m] : -1; + rows_sh[threadIdx.x] = code >= 0 ? (code >> 5) * slots + (code & 31) : -1; + } + __syncthreads(); + if (rows_sh[0] < 0) return; // members come first, so this tile is empty + const int r0 = rows_sh[g], r1 = rows_sh[g + 8]; + const half* x0 = X + (size_t)(r0 < 0 ? 0 : r0) * K + 2 * t; + const half* x1 = X + (size_t)(r1 < 0 ? 0 : r1) * K + 2 * t; + + const int per_split = KT / SK, per_warp = per_split / W; + const int kt0 = split * per_split + warp * per_warp; + const int nt0 = blockIdx.y * NT; + + float acc[NT][2][4]; +#pragma unroll + for (int i = 0; i < NT; ++i) +#pragma unroll + for (int h = 0; h < 2; ++h) +#pragma unroll + for (int c = 0; c < 4; ++c) acc[i][h][c] = 0.f; + + switch (k2) { +#define TF_EXL3X_CASE(K2_) \ + case K2_: \ + if constexpr (K2_ >= LO && K2_ <= HI) \ + warp_tiles(T, NTILES, kt0, per_warp, nt0, x0, x1, r0 >= 0, r1 >= 0, lane, acc); \ + else \ + __trap(); \ + break; + TF_EXL3X_CASE(2) + TF_EXL3X_CASE(3) + TF_EXL3X_CASE(4) + TF_EXL3X_CASE(5) + TF_EXL3X_CASE(6) + TF_EXL3X_CASE(7) + TF_EXL3X_CASE(8) + TF_EXL3X_CASE(9) + TF_EXL3X_CASE(10) + TF_EXL3X_CASE(11) + TF_EXL3X_CASE(12) + TF_EXL3X_CASE(13) + TF_EXL3X_CASE(14) + TF_EXL3X_CASE(15) + TF_EXL3X_CASE(16) +#undef TF_EXL3X_CASE + default: + __trap(); + } + + // warps' partial sums through shared memory, added in warp order + __shared__ float red[W][16][NT * 16]; +#pragma unroll + for (int i = 0; i < NT; ++i) +#pragma unroll + for (int h = 0; h < 2; ++h) { + const int col = i * 16 + h * 8 + 2 * t; + red[warp][g][col] = acc[i][h][0]; + red[warp][g][col + 1] = acc[i][h][1]; + red[warp][g + 8][col] = acc[i][h][2]; + red[warp][g + 8][col + 1] = acc[i][h][3]; + } + __syncthreads(); + for (int idx = threadIdx.x; idx < 16 * NT * 16; idx += W * 32) { + const int row = idx / (NT * 16), col = idx % (NT * 16); + const int r = rows_sh[row]; + if (r < 0) continue; + float s = red[0][row][col]; +#pragma unroll + for (int w = 1; w < W; ++w) s += red[w][row][col]; + Z[(((size_t)mat * SK + split) * P + r) * N + nt0 * 16 + col] = s; + } +} + +// W_q [K, N] fp16 of one matrix through the same lane decode (tests; not on the forward path). +template +__global__ void dequant_kernel(const uint32_t* __restrict__ T, half* __restrict__ out, int K, int N) { + const int kt = blockIdx.x, nt = blockIdx.y, lane = threadIdx.x; + const int NTILES = N >> 4; + uint32_t w[Fmt::LW]; + load_words(w, T + ((size_t)kt * NTILES + nt) * Fmt::TW + lane, lane); + const LaneMap map(lane); + uint32_t b0[2], b1[2]; + decode_tile(w, map, lane, b0, b1); + const int g = lane >> 2, t = lane & 3; + uint32_t v[4] = {b0[0], b0[1], b1[0], b1[1]}; +#pragma unroll + for (int q = 0; q < 4; ++q) { + half2 h = *reinterpret_cast(&v[q]); + const int col = nt * 16 + g + 8 * (q >> 1); + const int row = kt * 16 + 2 * t + 8 * (q & 1); + out[(size_t)row * N + col] = __low2half(h); + out[(size_t)(row + 1) * N + col] = __high2half(h); + } +} + +} // namespace tf_exl3x diff --git a/zig/kernels/cuda/deepseek_v41/glue.cu b/zig/kernels/cuda/deepseek_v41/glue.cu new file mode 100644 index 0000000000..ad589080e5 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/glue.cu @@ -0,0 +1,403 @@ +// DeepSeek-V4.1 window glue: torch's CUDA arithmetic per element (fp32 opmath, bf16 / f16 rn casts, --fmad=false). + +#include +#include +#include + +namespace dsv41_glue { + +__device__ __forceinline__ float bf(uint16_t b) { return __uint_as_float(static_cast(b) << 16); } +__device__ __forceinline__ uint16_t to_bf(float x) { return __bfloat16_as_ushort(__float2bfloat16(x)); } + +// rows [n, D] = bf16_rn(f32 embed[ids - lo]) from the rank's slice [V, D], +0 off it (NaN -> 0x7FFF) +__global__ void __launch_bounds__(256) embed_send_kernel(const long long* __restrict__ ids, int n, + const uint16_t* __restrict__ embed, long long vocab_lo, int V, + int D, uint16_t* __restrict__ rows) { + const long long total = (long long)n * D; + for (long long e = (long long)blockIdx.x * blockDim.x + threadIdx.x; e < total; e += (long long)gridDim.x * blockDim.x) { + const long long i = e / D, d = e - i * D; + const long long local = ids[i] - vocab_lo; + rows[e] = (local >= 0 && local < V) ? to_bf(bf(embed[local * D + d])) : (uint16_t)0; + } +} + +// recv [W, n, D] bf16 summed in rank order (fp32 adds) -> out [n, 4 D] bf16, the sum repeated x4 +__global__ void __launch_bounds__(256) embed_sum_kernel(const uint16_t* __restrict__ recv, int W, int n, int D, + uint16_t* __restrict__ out) { + const long long total = (long long)n * D, plane = total; + for (long long e = (long long)blockIdx.x * blockDim.x + threadIdx.x; e < total; e += (long long)gridDim.x * blockDim.x) { + float s = bf(recv[e]); + for (int w = 1; w < W; ++w) s = __fadd_rn(s, bf(recv[w * plane + e])); + const uint16_t b = to_bf(s); + const long long i = e / D, d = e - i * D; + uint16_t* o = out + i * 4 * D + d; +#pragma unroll + for (int r = 0; r < 4; ++r) o[r * D] = b; + } +} + +// y = bf16_rn(x) (the MoE partial) +__global__ void __launch_bounds__(256) cast_bf16_kernel(const float* __restrict__ x, uint16_t* __restrict__ y, + long long n) { + for (long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x; i < n; i += (long long)gridDim.x * blockDim.x) + y[i] = to_bf(x[i]); +} + +// y = f32(f16_rn(x)) +__global__ void __launch_bounds__(256) kit_weights_kernel(const float* __restrict__ x, float* __restrict__ y, + long long n) { + for (long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x; i < n; i += (long long)gridDim.x * blockDim.x) + y[i] = __half2float(__float2half(x[i])); +} + +// x = f32(bf16_rn(x)) in place +__global__ void __launch_bounds__(256) kit_logits_kernel(float* __restrict__ x, long long n) { + for (long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x; i < n; i += (long long)gridDim.x * blockDim.x) + x[i] = bf(to_bf(x[i])); +} + +// pos[0] = start; pos64[i] = start + i; lo[i] = 0 +__global__ void __launch_bounds__(256) positions_kernel(int start, int n, int* __restrict__ pos, + long long* __restrict__ pos64, int* __restrict__ lo) { + const int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i == 0) pos[0] = start; + if (i < n) { + pos64[i] = (long long)start + i; + lo[i] = 0; + } +} + +// a graphed window's positions: `start` from the window's staged statics, so one captured graph serves every window +__global__ void __launch_bounds__(256) positions_dev_kernel(const int* __restrict__ start, int n, int* __restrict__ pos, + long long* __restrict__ pos64, int* __restrict__ lo) { + const int i = blockIdx.x * blockDim.x + threadIdx.x; + const int s = *start; + if (i == 0) pos[0] = s; + if (i < n) { + pos64[i] = (long long)s + i; + lo[i] = 0; + } +} + +// recv [W, n, c] -> out [n, W c] (bf16 words, any 2-byte type) +__global__ void __launch_bounds__(256) gather_cols_kernel(const uint16_t* __restrict__ recv, int W, int n, int c, + uint16_t* __restrict__ out) { + const long long total = (long long)W * n * c; + for (long long e = (long long)blockIdx.x * blockDim.x + threadIdx.x; e < total; e += (long long)gridDim.x * blockDim.x) { + const long long i = e / ((long long)W * c), r = e - i * W * c, w = r / c, j = r - w * c; + out[e] = recv[(w * n + i) * c + j]; + } +} + +// compressor carry [cols] fp32 <- bf16 row `row` of src (row stride ld); the row from the device when `accepted` is set +__global__ void __launch_bounds__(256) carry_kernel(const uint16_t* __restrict__ src, long long ld, + const int* __restrict__ accepted, int row, int cols, + float* __restrict__ carry) { + const int r = accepted ? *accepted : row; + for (int j = blockIdx.x * blockDim.x + threadIdx.x; j < cols; j += gridDim.x * blockDim.x) + carry[j] = bf(src[(long long)r * ld + j]); +} + +// bf16 -> fp32 (exact: the bits shifted into the high half), n elements +__global__ void __launch_bounds__(256) widen_f32_kernel(const uint16_t* __restrict__ x, float* __restrict__ y, + long long n) { + for (long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x; i < n; i += (long long)gridDim.x * blockDim.x) + y[i] = bf(x[i]); +} + +// DSpark candidates: a CTA per fp32 logit row [rows, V] (stride ld); the best k by (value desc, column asc), exact. +constexpr int DS_NT = 1024, DS_KMAX = 128; + +__device__ __forceinline__ unsigned long long ds_key(float x, int col) { // unique in a row; -0.0 == +0.0 + const unsigned int b = __float_as_uint(x + 0.0f); // -0.0 + 0.0 = +0.0 + const unsigned int mono = (b & 0x80000000u) ? ~b : (b | 0x80000000u); + return (static_cast(mono) << 32) | (0xFFFFFFFFull - static_cast(col)); +} + +__global__ void __launch_bounds__(DS_NT) ds_cands_kernel(const float* __restrict__ lg, long long ld, int V, int k, + int id0, float* __restrict__ out) { + __shared__ int hist[256]; + __shared__ unsigned long long s_prefix, s_mask; + __shared__ int s_need, s_done, s_n; + __shared__ unsigned long long key_sh[DS_KMAX]; + const int tid = threadIdx.x; + const float* row = lg + (long long)blockIdx.x * ld; + float* o = out + (long long)blockIdx.x * 2 * k; // k values, then id0 + col as int bits; padding -inf, -1 + const int c = V < k ? V : k; + if (tid == 0) { s_prefix = 0; s_mask = 0; s_need = c; s_done = 0; } + __syncthreads(); + for (int shift = 56; shift >= 0 && c > 0; shift -= 8) { // radix select of the k-th key, 8 bits a pass + if (tid < 256) hist[tid] = 0; + __syncthreads(); + const unsigned long long prefix = s_prefix, mask = s_mask; + for (int i = tid; i < V; i += DS_NT) { + const unsigned long long u = ds_key(row[i], i); + if ((u & mask) == prefix) atomicAdd(&hist[(u >> shift) & 255], 1); + } + __syncthreads(); + if (tid == 0) { + int need = s_need, d = 255; + for (; d > 0 && hist[d] < need; --d) need -= hist[d]; + s_need = need; + s_prefix = prefix | ((unsigned long long)d << shift); + s_mask = mask | (255ull << shift); + s_done = hist[d] == need; // the digit's keys: all winners + } + __syncthreads(); + if (s_done) break; + } + const unsigned long long prefix = s_prefix, mask = s_mask; + if (tid == 0) s_n = 0; + __syncthreads(); + for (int i = tid; i < V && c > 0; i += DS_NT) { + const unsigned long long u = ds_key(row[i], i); + if ((u & mask) >= prefix) { + const int at = atomicAdd(&s_n, 1); + if (at < DS_KMAX) key_sh[at] = u; + } + } + __syncthreads(); + int m = 1; + while (m < c) m <<= 1; + for (int i = c + tid; i < m; i += DS_NT) key_sh[i] = 0; // sorts last + __syncthreads(); + for (int size = 2; size <= m; size <<= 1) { // bitonic sort, descending + for (int stride = size >> 1; stride > 0; stride >>= 1) { + for (int i = tid; i < m; i += DS_NT) { + const int j = i ^ stride; + if (j > i) { + const bool desc = (i & size) == 0; + const unsigned long long a = key_sh[i], b = key_sh[j]; + if (desc ? a < b : a > b) { key_sh[i] = b; key_sh[j] = a; } + } + } + __syncthreads(); + } + } + for (int i = tid; i < k; i += DS_NT) { + if (i < c) { + const int col = (int)(0xFFFFFFFFull - (key_sh[i] & 0xFFFFFFFFull)); + o[i] = row[col]; + o[k + i] = __int_as_float(id0 + col); + } else { + o[i] = -__int_as_float(0x7F800000); + o[k + i] = __int_as_float(-1); + } + } +} + +// pairs[r] = (f64 vals[r], f64 (cols[r] + id0)), a rank's best; one thread a row +__global__ void __launch_bounds__(256) pick_pack_kernel(const float* __restrict__ vals, const long long* __restrict__ cols, + int n, long long id0, double* __restrict__ pairs) { + const int r = blockIdx.x * blockDim.x + threadIdx.x; + if (r >= n) return; + pairs[2 * r] = static_cast(vals[r]); + pairs[2 * r + 1] = static_cast(cols[r] + id0); +} + +// out[r]: best id over ranks (tie / NaN: lower rank), then accepted, bonus, next position; one block, n <= 1024 +__global__ void __launch_bounds__(1024) pick_merge_kernel(const double* __restrict__ all, int W, int n, + const long long* __restrict__ ids, long long start, + long long* __restrict__ out) { + const int r = threadIdx.x; + if (r < n) { + int bk = 0; + for (int k = 1; k < W; k++) + if (all[2 * (k * n + r)] > all[2 * (bk * n + r)]) bk = k; + out[r] = static_cast(all[2 * (bk * n + r) + 1]); + } + __syncthreads(); + if (r == 0) { + int acc = 0; + if (ids != nullptr) + while (acc + 1 < n && ids[acc + 1] == out[acc]) acc++; + out[n] = acc; + out[n + 1] = out[acc]; + out[n + 2] = start + acc + 1; + } +} + +// one block: DSpark's one-slot statics from the device pick, (anchor, start) = (bonus, next position); rest as staged +__global__ void __launch_bounds__(256) ds_stage_kernel(const long long* __restrict__ pick, int nw, long long* __restrict__ s64, + int* __restrict__ s32, int n, int W, int ring, long long valid, + int noise, int o_pos, int o_tok, int o_cnt, int o_lo, int o_hi, + int o_anchor) { + const long long A = pick[nw + 1], P = pick[nw + 2]; + const long long lo_w = P > W ? P - W : 0; // p -| window + const long long first = lo_w > valid ? lo_w : valid; + const int count = P > first ? (int)(P - first) : 0; + const int t = threadIdx.x; + for (int i = t; i < n; i += blockDim.x) { + s64[i] = i == 0 ? A : (long long)noise; // ids (offset 0) + s64[o_pos + i] = P + i; + s32[o_cnt + i] = count; + s32[o_lo + i] = (int)P; + s32[o_hi + i] = (int)(P + n - 1); + } + for (int e = t; e < n * W; e += blockDim.x) { + const int j = e % W; + s32[o_tok + e] = j < count ? (int)((first + j) % ring) : -1; + } + if (t == 0) { + s32[0] = (int)P; // start (offset 0) + s32[o_anchor] = (int)A; + } +} + +// one thread a slot (segs [k][3]: first row, rows, start): pick_merge's chain walk -> acc [k][3] +__global__ void __launch_bounds__(32) ds_accept_rows_kernel(const long long* __restrict__ picks, const long long* __restrict__ ids, + const long long* __restrict__ segs, int k, long long* __restrict__ acc) { + const int s = threadIdx.x; + if (s >= k) return; + const long long a = segs[3 * s], n = segs[3 * s + 1], start = segs[3 * s + 2]; + long long c = 0; + while (c + 1 < n && ids[a + c + 1] == picks[a + c]) c++; + acc[3 * s] = c; + acc[3 * s + 1] = picks[a + c]; + acc[3 * s + 2] = start + c + 1; +} + +// one block: a pass's members (mem [g][3]: acc index, slot, valid), each one's n rows from its (bonus, next position) +__global__ void __launch_bounds__(256) ds_stage_rows_kernel(const long long* __restrict__ acc, const long long* __restrict__ mem, + int g, long long* __restrict__ s64, int* __restrict__ s32, int n, + int W, int ring, int noise, int o_ids, int o_pos, int o_tok, + int o_cnt, int o_lo, int o_hi) { + const int t = threadIdx.x; + for (int m = 0; m < g; m++) { + const long long j = mem[3 * m], slot = mem[3 * m + 1], valid = mem[3 * m + 2]; + const long long A = acc[3 * j + 1], P = acc[3 * j + 2]; + const long long lo_w = P > W ? P - W : 0; + const long long first = lo_w > valid ? lo_w : valid; + const int count = P > first ? (int)(P - first) : 0; + const int a = m * n; + for (int i = t; i < n; i += blockDim.x) { + s64[o_ids + a + i] = i == 0 ? A : (long long)noise; + s64[o_pos + a + i] = P + i; + s32[o_cnt + a + i] = count; + s32[o_lo + a + i] = (int)P; + s32[o_hi + a + i] = (int)(P + n - 1); + } + for (int e = t; e < n * W; e += blockDim.x) { + const int jj = e % W; + s32[o_tok + a * W + e] = jj < count ? (int)(slot * ring + (first + jj) % ring) : -1; + } + } +} + +__device__ __forceinline__ unsigned long long vs_mix(unsigned long long x) { + x ^= x >> 30; + x *= 0xBF58476D1CE4E5B9ull; + x ^= x >> 27; + x *= 0x94D049BB133111EBull; + return x ^ (x >> 31); +} + +__device__ __forceinline__ double vs_uniform(unsigned long long seed, unsigned long long pos, unsigned long long id) { + unsigned long long x = vs_mix(seed + 0x9E3779B97F4A7C15ull); + x = vs_mix(x ^ (pos * 0xD1B54A32D192ED03ull)); + x = vs_mix(x ^ id); + return (double)(x >> 11) * 0x1p-53 + 0x1p-54; +} + +__device__ __forceinline__ float vs_value(unsigned long long k) { + const unsigned int mono = (unsigned int)(k >> 32); + return __uint_as_float((mono & 0x80000000u) ? (mono & 0x7FFFFFFFu) : ~mono); +} + +// numpy's float64 pairwise sum (leaves of 8..128, halves cut to x8); depth a template, so no runtime recursion +__device__ __forceinline__ double vs_leaf(const double* a, int n) { + if (n < 8) { + double r = -0.0; + for (int i = 0; i < n; i++) r += a[i]; + return r; + } + double r[8]; + for (int j = 0; j < 8; j++) r[j] = a[j]; + int i = 8; + for (; i < n - (n % 8); i += 8) + for (int j = 0; j < 8; j++) r[j] += a[i + j]; + double res = ((r[0] + r[1]) + (r[2] + r[3])) + ((r[4] + r[5]) + (r[6] + r[7])); + for (; i < n; i++) res += a[i]; + return res; +} + +template __device__ __forceinline__ double vs_pairwise(const double* a, int n) { + if (n <= 128) return vs_leaf(a, n); + int n2 = n / 2; + n2 -= n2 % 8; + return vs_pairwise(a, n2) + vs_pairwise(a + n2, n - n2); +} + +template <> __device__ __forceinline__ double vs_pairwise<0>(const double* a, int n) { return vs_leaf(a, n); } + +constexpr int VS_NT = 256, VS_MAX = 2048; + +// one block a row: speculative keyed pick over g [W][n][2 k] (values, ids as int bits); the host's pick decides +__global__ void __launch_bounds__(VS_NT) vs_choose_kernel(const float* __restrict__ g, int W, int n, int k, int count, + unsigned long long seed, long long pos0, int top_k, double temp, + double top_p, double min_p, long long* __restrict__ out) { + __shared__ unsigned long long keys[VS_MAX]; + __shared__ double work[1024]; + const int r = blockIdx.x, t = threadIdx.x, C = W * k; + if (C > VS_MAX) { if (t == 0) out[r] = -1; return; } + for (int i = t; i < C; i += VS_NT) { + const int w = i / k, j = i - w * k; + const float* row = g + ((long long)w * n + r) * 2 * k; + const unsigned int bits = __float_as_uint(row[j] + 0.0f); + const unsigned int mono = (bits & 0x80000000u) ? ~bits : (bits | 0x80000000u); + const unsigned int id = __float_as_uint(row[k + j]); + keys[i] = ((unsigned long long)mono << 32) | (0xFFFFFFFFull - id); + } + int m = 1; + while (m < C) m <<= 1; + for (int i = C + t; i < m; i += VS_NT) keys[i] = 0; + __syncthreads(); + for (int size = 2; size <= m; size <<= 1) + for (int stride = size >> 1; stride > 0; stride >>= 1) { + for (int i = t; i < m; i += VS_NT) { + const int j = i ^ stride; + if (j > i) { + const bool desc = (i & size) == 0; + const unsigned long long a = keys[i], b = keys[j]; + if (desc ? a < b : a > b) { keys[i] = b; keys[j] = a; } + } + } + __syncthreads(); + } + if (t != 0) return; + const long long id0 = (long long)(0xFFFFFFFFull - (keys[0] & 0xFFFFFFFFull)); + if (!(temp > 0.0)) { out[r] = id0; return; } // greedy: the first merged candidate + if (top_k == 0 && top_p > 0.0 && top_p < 1.0) { out[r] = -1; return; } // a nucleus row: the host's statistics + const int width = (count > 0 && count < C) ? count : C; // count 0: all W k + int kk = top_k != 0 ? (top_k < width ? top_k : width) : width; + if (kk < 1) kk = 1; + if (kk > 1024) kk = 1024; + const double tt = temp > 1e-6 ? temp : 1e-6; + const double s0 = (double)vs_value(keys[0]) / tt; + int keep = kk; + if (top_p > 0.0 && top_p < 1.0) { + for (int j = 0; j < kk; j++) work[j] = exp((double)vs_value(keys[j]) / tt - s0); + const double total = vs_pairwise<3>(work, kk); // kk <= 1024 + double cum = 0.0; + int below = 0; + for (int j = 0; j < kk; j++) { + cum += work[j] / total; + if (cum < top_p) below++; + } + keep = below + 1; + } + const double floor_ = s0 + (min_p > 0.0 ? log(min_p) : -INFINITY); + const int lim = keep < kk ? keep : kk; + int best = 0; + double best_score = -INFINITY; + for (int j = 0; j < lim; j++) { + const double x = (double)vs_value(keys[j]) / tt; + if (min_p > 0.0 && x < floor_) continue; + const unsigned long long id = 0xFFFFFFFFull - (keys[j] & 0xFFFFFFFFull); + const double score = x - log(-log(vs_uniform(seed, (unsigned long long)(pos0 + r), id))); + if (j == 0 || score > best_score) { best = j; best_score = score; } + } + out[r] = (long long)(0xFFFFFFFFull - (keys[best] & 0xFFFFFFFFull)); +} + +} // namespace dsv41_glue diff --git a/zig/kernels/cuda/deepseek_v41/kv_glue.cu b/zig/kernels/cuda/deepseek_v41/kv_glue.cu new file mode 100644 index 0000000000..f5f7591060 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/kv_glue.cu @@ -0,0 +1,126 @@ +// Locally owned SWA KV glue, bit-exact with AOT Triton _rms/_kv_store: K=512 bf16, fp32 RMS weight, one CTA a row. +#include +#include +#include +#include +#include + +namespace dsv41_kv_glue { +struct Args { + uint64_t x, w, cs, v, s, pos, sl, norm_out; + int64_t x_stride, cs_stride, v_stride, s_stride; + float inv_k, eps; + int32_t ring, rows; +}; +static_assert(sizeof(Args) == 112 && offsetof(Args, x_stride) == 64 && offsetof(Args, inv_k) == 96, + "Zig/CUDA KV glue ABI"); + +// Matches the AOT _rms tree: 4 contiguous bf16 a thread, ((a0+a1)+a2)+a3, xor 16..1, (w0+w2)+(w1+w3), fp64 tail. +__device__ __forceinline__ double row_rn(const __nv_bfloat16* xr, float inv_k, float eps) { + __shared__ double ws[4]; + const int t = threadIdx.x; + double a[4]; +#pragma unroll + for (int i = 0; i < 4; ++i) { + const double v = static_cast(__bfloat162float(xr[4 * t + i])); + a[i] = __dadd_rn(0.0, __dmul_rn(v, v)); + } + double sum = __dadd_rn(__dadd_rn(__dadd_rn(a[0], a[1]), a[2]), a[3]); +#pragma unroll + for (int o = 16; o > 0; o >>= 1) sum = __dadd_rn(sum, __shfl_xor_sync(0xffffffffu, sum, o)); + if ((t & 31) == 0) ws[t >> 5] = sum; + __syncthreads(); + const double ss = __dadd_rn(__dadd_rn(ws[0], ws[2]), __dadd_rn(ws[1], ws[3])); + const double var = __dadd_rn(__dmul_rn(ss, static_cast(inv_k)), static_cast(eps)); + double rn; + asm("rsqrt.approx.f64 %0, %1;" : "=d"(rn) : "d"(var)); + return rn; +} + +// e4m3_rne op for op: explicit fp32 rounding before the hardware conversion, edge inputs included. +__device__ __forceinline__ uint8_t e4m3_rne(float x) { + const uint32_t bits = __float_as_uint(x); + const float near = __uint_as_float((bits + 0x7ffffu + ((bits >> 20) & 1u)) & 0xfff00000u); + const float abs_x = __uint_as_float(bits & 0x7fffffffu); + const float small_abs = __fsub_rn(__fadd_rn(abs_x, 24576.0f), 24576.0f); + const float small = __uint_as_float(__float_as_uint(small_abs) | (bits & 0x80000000u)); + return __nv_cvt_float_to_fp8(abs_x < 0.015625f ? small : near, __NV_SATFINITE, __NV_E4M3); +} + +// Matches the AOT _kv_store: FMNMX's NaN-as-number max then floor 0x38d1b717, exponent from bits (all-NaN tiles too). +__device__ __forceinline__ int tile_exponent(float even, float odd) { + float peak = fmaxf(fabsf(even), fabsf(odd)); +#pragma unroll + for (int o = 16; o > 0; o >>= 1) peak = fmaxf(peak, __shfl_xor_sync(0xffffffffu, peak, o)); + const uint32_t bits = __float_as_uint(fmaxf(peak, __uint_as_float(0x38d1b717u))); + return static_cast((bits >> 23) & 255u) - 135 + ((bits & 0x7fffffu) > 0x600000u); +} + +// C signed remainder like the AOT ring (not q & 255 for negative q); caller ensures a power-of-two ring, no division. +__device__ __forceinline__ int ring_offset(int q, int ring) { + const uint32_t mask = static_cast(ring - 1); + if (q >= 0) return static_cast(static_cast(q) & mask); + return -static_cast((0u - static_cast(q)) & mask); +} +} // namespace dsv41_kv_glue + +// Grid (R), block 128, no dynamic shared; optional debug norm_out bf16 [R, 512] is written even for skipped rows. +extern "C" __global__ void __launch_bounds__(128) kv_norm_store(const dsv41_kv_glue::Args a) { + using namespace dsv41_kv_glue; + __shared__ __align__(16) __nv_bfloat16 latent[512]; + const int t = threadIdx.x, lane = t & 31, warp = t >> 5; + const int64_t r = blockIdx.x; + const auto* xr = reinterpret_cast(a.x) + r * a.x_stride; + const auto* w = reinterpret_cast(a.w); + const double rn = row_rn(xr, a.inv_k, a.eps); +#pragma unroll + for (int i = 0; i < 4; ++i) { + const int k = t + 128 * i; + const double y = __dmul_rn(__dmul_rn(static_cast(__bfloat162float(xr[k])), rn), + static_cast(w[k])); + const __nv_bfloat16 norm = __float2bfloat16_rn(__double2float_rn(y)); + latent[k] = norm; + if (a.norm_out) reinterpret_cast<__nv_bfloat16*>(a.norm_out)[r * 512 + k] = norm; + } + __syncthreads(); + const int64_t sl = a.rows ? reinterpret_cast(a.sl)[r] : 0; + if (sl < 0) return; // uniform across the CTA, after every optional norm write + const int q = a.rows ? static_cast(reinterpret_cast(a.pos)[r]) + : static_cast(static_cast(*reinterpret_cast(a.pos)) + + static_cast(r)); + const int64_t row = sl * a.ring + ring_offset(q, a.ring); + auto* v = reinterpret_cast(a.v) + row * a.v_stride; + auto* scales = reinterpret_cast(a.s) + row * a.s_stride; + const auto* cs = reinterpret_cast(a.cs) + static_cast(q) * a.cs_stride; +#pragma unroll + for (int j = 0; j < 2; ++j) { + const int pair = t + 128 * j; + const float even = __bfloat162float(latent[2 * pair]); + const float odd = __bfloat162float(latent[2 * pair + 1]); + float ne, no; + if (pair >= 224) { + const float c = cs[pair - 224], s = cs[32 + pair - 224]; + // AOT SASS: round odd*s and odd*c, then fuse EVEN*c and EVEN*s. + ne = __fmaf_rn(even, c, -__fmul_rn(odd, s)); + no = __fmaf_rn(even, s, __fmul_rn(odd, c)); + } else { + // Preserve the AOT NoPE operands (including NaN and signed zero). + const float neg_odd_zero = -__fmul_rn(0.0f, odd); + asm("fma.rn.f32 %0, %1, %2, %3;" : "=f"(ne) : "f"(even), "f"(1.0f), "f"(neg_odd_zero)); + asm("fma.rn.f32 %0, %1, %2, %3;" : "=f"(no) : "f"(0.0f), "f"(even), "f"(odd)); + } + const int tile = warp + 4 * j; + if (tile < 7) { // uniform within each warp + const int e = tile_exponent(ne, no); + const float inv_scale = __int_as_float((127 - e) << 23); + v[2 * pair] = e4m3_rne(__fmul_rn(ne, inv_scale)); + v[2 * pair + 1] = e4m3_rne(__fmul_rn(no, inv_scale)); + if (lane == 0) scales[tile] = static_cast(e + 127); + } else { + auto* rope = reinterpret_cast<__nv_bfloat16*>(v + 448); + rope[2 * pair - 448] = __float2bfloat16_rn(ne); + rope[2 * pair - 448 + 1] = __float2bfloat16_rn(no); + if (lane == 0) scales[7] = 0; + } + } +} diff --git a/zig/kernels/cuda/deepseek_v41/kvsplit.cu b/zig/kernels/cuda/deepseek_v41/kvsplit.cu new file mode 100644 index 0000000000..f8b5409d35 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/kvsplit.cu @@ -0,0 +1,319 @@ +// Split KV exchange copies (reference: kv/split.zig HostKernels): pure 8-byte-word copies, bit-exact, graph-capturable. + +// dense: selected rows packed in list order; gather: rows phys[i]; pack (TF_DSV41_KV_SPLIT_COMPACT): owned rows first. + +// pack: one launch for world <= 8, block b counts owners before its 256 entries; the last block writes lens. + +#include + +namespace dsv41_kvsplit { + +// kv/split.zig DenseArgs (extern, by value): the same fields at the same offsets +struct DenseArgs { + const int* sel; // int32 [R, K] logical rows (-1: none) + uint32_t rows, k; + const int* table; // int32 split page tables: [pages], or row mode's stacked [S, pts] + uint32_t pts; + const int* rslot; // int32 [R] or null + uint32_t psh; + const uint8_t* base; // the family's local rows + uint32_t row_bytes, world; + uint8_t* send; // [R K, row bytes] + int* tok; // int32 [R, K] +}; +static_assert(sizeof(DenseArgs) == 80, "kv/split.zig DenseArgs"); + +__global__ void __launch_bounds__(256) dense_kernel(const DenseArgs a) { + const uint32_t wpr = a.row_bytes >> 3; // 8-byte words a row + const long long n = (long long)a.rows * a.k; + const long long words = n * wpr; + for (long long w = (long long)blockIdx.x * blockDim.x + threadIdx.x; w < words; w += (long long)gridDim.x * blockDim.x) { + const long long i = w / wpr; + const uint32_t q = (uint32_t)(w - i * wpr); + const int sv = a.sel[i]; + const uint32_t t = sv > 0 ? (uint32_t)sv : 0u; + const long long s = a.rslot ? (long long)a.rslot[i / a.k] : 0; + const uint32_t local = (uint32_t)a.table[s * a.pts + (t >> a.psh)]; + const unsigned long long row = ((unsigned long long)local << a.psh) | (t & ((1u << a.psh) - 1u)); + const uint64_t* src = reinterpret_cast(a.base + row * a.row_bytes); + reinterpret_cast(a.send + (unsigned long long)i * a.row_bytes)[q] = src[q]; + if (q == 0) a.tok[i] = sv < 0 ? -1 : (int)((long long)((t >> a.psh) % a.world) * n + i); + } +} + +// kv/split.zig PackArgs: DenseArgs with rank in psh's padding and lens after tok +struct PackArgs { + const int* sel; + uint32_t rows, k; + const int* table; + uint32_t pts; + const int* rslot; + uint32_t psh, rank; + const uint8_t* base; + uint32_t row_bytes, world; + uint8_t* send; // [R K, row bytes]: this rank's owned rows in list order first + int* tok; // int32 [R, K] + int* lens; // int32 [world]: bytes +}; +static_assert(sizeof(PackArgs) == 88, "kv/split.zig PackArgs"); + +constexpr int kMaxWorld = 8; // kv/split.zig max_world +constexpr int kPackThreads = 256; // entries a block; the launch's block size + +__device__ __forceinline__ uint32_t owner_of(int sv, uint32_t psh, uint32_t world) { + return sv < 0 ? world : ((uint32_t)sv >> psh) % world; // world: no owner +} + +__global__ void __launch_bounds__(kPackThreads) pack_kernel(const PackArgs a) { + __shared__ uint32_t s_before[kMaxWorld]; // each owner's entries before this block's + __shared__ uint32_t s_warp[kMaxWorld][kPackThreads / 32]; // each warp's entries of each owner + __shared__ unsigned long long s_row[kPackThreads]; // this block's owned rows by place (from the first) + const uint32_t tid = threadIdx.x, lane = tid & 31u, warp = tid >> 5; + const uint32_t W = a.world; + const long long n = (long long)a.rows * a.k; + const long long start = (long long)blockIdx.x * kPackThreads; + if (tid < kMaxWorld) s_before[tid] = 0u; + + // 1. the entries before this block, by owner + uint32_t c[kMaxWorld]; +#pragma unroll + for (int q = 0; q < kMaxWorld; q++) c[q] = 0u; + for (long long j = tid; j < start; j += kPackThreads) { + const uint32_t o = owner_of(a.sel[j], a.psh, W); +#pragma unroll + for (int q = 0; q < kMaxWorld; q++) c[q] += (uint32_t)(o == (uint32_t)q); + } + __syncthreads(); +#pragma unroll + for (int q = 0; q < kMaxWorld; q++) { + if ((uint32_t)q < W) { + uint32_t v = c[q]; +#pragma unroll + for (int d = 16; d > 0; d >>= 1) v += __shfl_xor_sync(0xffffffffu, v, d); + if (lane == 0u && v != 0u) atomicAdd(&s_before[q], v); + } + } + + // 2. this block's entries: a ballot an owner gives the place inside the warp, the warps before add theirs + const long long i = start + tid; + const int sv = i < n ? a.sel[i] : -1; + const uint32_t o = owner_of(sv, a.psh, W); + const uint32_t lt = (1u << lane) - 1u; + uint32_t below = 0u; +#pragma unroll + for (int q = 0; q < kMaxWorld; q++) { + if ((uint32_t)q < W) { + const uint32_t m = __ballot_sync(0xffffffffu, o == (uint32_t)q); + if (o == (uint32_t)q) below = __popc(m & lt); + if (lane == 0u) s_warp[q][warp] = __popc(m); + } + } + __syncthreads(); + if (sv >= 0) { + uint32_t at = below; + for (uint32_t w = 0; w < warp; w++) at += s_warp[o][w]; + a.tok[i] = (int)((long long)o * n + s_before[o] + at); + if (o == a.rank) { + const uint32_t t = (uint32_t)sv; + const long long s = a.rslot ? (long long)a.rslot[i / a.k] : 0; + const uint32_t local = (uint32_t)a.table[s * a.pts + (t >> a.psh)]; + s_row[at] = ((unsigned long long)local << a.psh) | (t & ((1u << a.psh) - 1u)); + } + } else if (i < n) { + a.tok[i] = -1; + } + uint32_t mine = 0u; +#pragma unroll + for (int w = 0; w < kPackThreads / 32; w++) mine += s_warp[a.rank][w]; + __syncthreads(); + + // 3. this block's owned rows to send[first place ..] + const uint32_t wpr = a.row_bytes >> 3; + const unsigned long long first = s_before[a.rank]; + for (uint32_t w = tid; w < mine * wpr; w += kPackThreads) { + const uint32_t j = w / wpr; + const uint32_t q = w - j * wpr; + const uint64_t* src = reinterpret_cast(a.base + s_row[j] * a.row_bytes); + reinterpret_cast(a.send + (first + j) * a.row_bytes)[q] = src[q]; + } + + // 4. the last block has counted every entry + if (blockIdx.x == gridDim.x - 1 && tid < W) { + uint32_t total = s_before[tid]; +#pragma unroll + for (int w = 0; w < kPackThreads / 32; w++) total += s_warp[tid][w]; + a.lens[tid] = (int)(total * a.row_bytes); + } +} + +__global__ void __launch_bounds__(256) gather_kernel(const uint8_t* __restrict__ base, uint32_t row_bytes, + const uint32_t* __restrict__ phys, uint32_t n, + uint8_t* __restrict__ send) { + const uint32_t wpr = row_bytes >> 3; + const long long words = (long long)n * wpr; + for (long long w = (long long)blockIdx.x * blockDim.x + threadIdx.x; w < words; w += (long long)gridDim.x * blockDim.x) { + const long long i = w / wpr; + const uint32_t q = (uint32_t)(w - i * wpr); + const uint64_t* src = reinterpret_cast(base + (unsigned long long)phys[i] * row_bytes); + reinterpret_cast(send + (unsigned long long)i * row_bytes)[q] = src[q]; + } +} + +// dedup (TF_DSV41_KV_SPLIT_DEDUP): pack each slot's unique rows once; an entry's token is its key's first entry's place +struct DedupArgs { + PackArgs p; + int* map; // int32 [map_rows], key slot x (pts << psh) + row: INT_MAX between calls + int* first; // int32 [R K]: each entry's first entry of its key (-1: none) + int* place; // int32 [R K]: code (owner, or world: not counted), then a first entry's place + uint32_t* bcnt; // uint32 [blocks, kMaxWorld]: each block's counted entries by owner +}; +static_assert(sizeof(DedupArgs) == 120, "kv/split.zig DedupArgs"); + +__device__ __forceinline__ long long dedup_key(const PackArgs& a, long long i, int sv) { + return (a.rslot ? (long long)a.rslot[i / a.k] * ((long long)a.pts << a.psh) : 0ll) + sv; +} + +// mark: map[key] = the key's first entry (atomicMin), then flags, pack (places by ballots) and tok (puts map back) +__global__ void __launch_bounds__(256) dedup_mark_kernel(const DedupArgs d) { + const long long n = (long long)d.p.rows * d.p.k; + for (long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x; i < n; i += (long long)gridDim.x * blockDim.x) { + const int sv = d.p.sel[i]; + if (sv >= 0) atomicMin(&d.map[dedup_key(d.p, i, sv)], (int)i); + } +} + +__global__ void __launch_bounds__(kPackThreads) dedup_flags_kernel(const DedupArgs d) { + __shared__ uint32_t s_cnt[kMaxWorld]; + const uint32_t tid = threadIdx.x, W = d.p.world; + const long long n = (long long)d.p.rows * d.p.k; + const long long i = (long long)blockIdx.x * kPackThreads + tid; + if (tid < kMaxWorld) s_cnt[tid] = 0u; + __syncthreads(); + uint32_t code = W; + if (i < n) { + const int sv = d.p.sel[i]; + int f = -1; + if (sv >= 0) { + f = d.map[dedup_key(d.p, i, sv)]; + if ((long long)f == i) code = owner_of(sv, d.p.psh, W); + } + d.first[i] = f; + d.place[i] = (int)code; + } + if (code < W) atomicAdd(&s_cnt[code], 1u); + __syncthreads(); + if (tid < kMaxWorld) d.bcnt[(long long)blockIdx.x * kMaxWorld + tid] = s_cnt[tid]; +} + +__global__ void __launch_bounds__(kPackThreads) dedup_pack_kernel(const DedupArgs d) { + __shared__ uint32_t s_before[kMaxWorld]; + __shared__ uint32_t s_warp[kMaxWorld][kPackThreads / 32]; + __shared__ unsigned long long s_row[kPackThreads]; + const PackArgs& a = d.p; + const uint32_t tid = threadIdx.x, lane = tid & 31u, warp = tid >> 5, W = a.world; + const long long n = (long long)a.rows * a.k; + const long long i = (long long)blockIdx.x * kPackThreads + tid; + if (tid < kMaxWorld) { + uint32_t v = 0u; + for (uint32_t b = 0; b < blockIdx.x; b++) v += d.bcnt[(long long)b * kMaxWorld + tid]; + s_before[tid] = v; + } + const uint32_t o = i < n ? (uint32_t)d.place[i] : W; + const uint32_t lt = (1u << lane) - 1u; + uint32_t below = 0u; +#pragma unroll + for (int q = 0; q < kMaxWorld; q++) { + if ((uint32_t)q < W) { + const uint32_t m = __ballot_sync(0xffffffffu, o == (uint32_t)q); + if (o == (uint32_t)q) below = __popc(m & lt); + if (lane == 0u) s_warp[q][warp] = __popc(m); + } + } + __syncthreads(); + if (o < W) { + uint32_t at = below; + for (uint32_t w = 0; w < warp; w++) at += s_warp[o][w]; + d.place[i] = (int)(s_before[o] + at); + if (o == a.rank) { + const uint32_t t = (uint32_t)a.sel[i]; + const long long s = a.rslot ? (long long)a.rslot[i / a.k] : 0; + const uint32_t local = (uint32_t)a.table[s * a.pts + (t >> a.psh)]; + s_row[at] = ((unsigned long long)local << a.psh) | (t & ((1u << a.psh) - 1u)); + } + } + uint32_t mine = 0u; +#pragma unroll + for (int w = 0; w < kPackThreads / 32; w++) mine += s_warp[a.rank][w]; + __syncthreads(); + const uint32_t wpr = a.row_bytes >> 3; + const unsigned long long first = s_before[a.rank]; + for (uint32_t w = tid; w < mine * wpr; w += kPackThreads) { + const uint32_t j = w / wpr; + const uint32_t q = w - j * wpr; + const uint64_t* src = reinterpret_cast(a.base + s_row[j] * a.row_bytes); + reinterpret_cast(a.send + (first + j) * a.row_bytes)[q] = src[q]; + } + if (blockIdx.x == gridDim.x - 1 && tid < W) { + uint32_t total = s_before[tid]; +#pragma unroll + for (int w = 0; w < kPackThreads / 32; w++) total += s_warp[tid][w]; + a.lens[tid] = (int)(total * a.row_bytes); + } +} + +__global__ void __launch_bounds__(256) dedup_tok_kernel(const DedupArgs d) { + const long long n = (long long)d.p.rows * d.p.k; + for (long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x; i < n; i += (long long)gridDim.x * blockDim.x) { + const int f = d.first[i]; + if (f < 0) { + d.p.tok[i] = -1; + continue; + } + const int sv = d.p.sel[i]; + d.p.tok[i] = (int)((long long)owner_of(sv, d.p.psh, d.p.world) * n + d.place[f]); + if ((long long)f == i) d.map[dedup_key(d.p, i, sv)] = 0x7fffffff; + } +} + +// history union (TF_DSV41_KV_SPLIT_HISTORY): this rank's rows of the mapped pages in page order, tokens by place +struct HistArgs { + const int* sel; // int32 [R, K] logical rows (-1: none) + uint32_t rows, k; + const int* table; // int32 [pages] split page table + uint32_t pages, psh, rank, world, m, row_bytes; + const uint8_t* base; + uint8_t* send; // [M, row bytes] + int* tok; // int32 [R, K] +}; +static_assert(sizeof(HistArgs) == 72, "kv/split.zig HistArgs"); + +__global__ void __launch_bounds__(256) hist_gather_kernel(const HistArgs a) { + const uint32_t wpr = a.row_bytes >> 3; + const uint32_t mask = (1u << a.psh) - 1u; + const long long words = (long long)a.m * wpr; + for (long long w = (long long)blockIdx.x * blockDim.x + threadIdx.x; w < words; w += (long long)gridDim.x * blockDim.x) { + const uint32_t i = (uint32_t)(w / wpr); + const uint32_t q = (uint32_t)(w - (long long)i * wpr); + const uint32_t lp = a.rank + a.world * (i >> a.psh); + if (lp >= a.pages) continue; + const unsigned long long row = ((unsigned long long)(uint32_t)a.table[lp] << a.psh) | (i & mask); + const uint64_t* src = reinterpret_cast(a.base + row * a.row_bytes); + reinterpret_cast(a.send + (unsigned long long)i * a.row_bytes)[q] = src[q]; + } +} + +__global__ void __launch_bounds__(256) hist_tok_kernel(const HistArgs a) { + const long long n = (long long)a.rows * a.k; + const uint32_t mask = (1u << a.psh) - 1u; + for (long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x; i < n; i += (long long)gridDim.x * blockDim.x) { + const int sv = a.sel[i]; + if (sv < 0) { + a.tok[i] = -1; + continue; + } + const uint32_t pg = (uint32_t)sv >> a.psh; + a.tok[i] = (int)((pg % a.world) * a.m + (((pg / a.world) << a.psh) | ((uint32_t)sv & mask))); + } +} + +} // namespace dsv41_kvsplit diff --git a/zig/kernels/cuda/deepseek_v41/linear.cu b/zig/kernels/cuda/deepseek_v41/linear.cu new file mode 100644 index 0000000000..b30ad5e4fa --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/linear.cu @@ -0,0 +1,363 @@ +// Copy of src/tensorfold/cuda/exl3/linear.cu (blob 83a8dd081226): sass_check.sh + +// EXL3 linear, 1-128 rows: a row's bits depend only on it (mma keeps rows apart, fixed K ranges, fixed-order sums). + +#include +#include +#include // what the cut torch includes provided: the fixed-width integers +#include + +#include "decode.cuh" + +using namespace tf_exl3; + +namespace tf_exl3_linear { + +enum DType : int { F16 = 0, BF16 = 1, F32 = 2 }; + +__device__ __forceinline__ void load4(const void* p, int dtype, size_t i, float (&v)[4]) { + if (dtype == F32) { + const float4 u = *reinterpret_cast(static_cast(p) + i); + v[0] = u.x; v[1] = u.y; v[2] = u.z; v[3] = u.w; + } else if (dtype == BF16) { + const uint2 u = *reinterpret_cast(static_cast(p) + i); + const float2 a = __bfloat1622float2(*reinterpret_cast(&u.x)); + const float2 b = __bfloat1622float2(*reinterpret_cast(&u.y)); + v[0] = a.x; v[1] = a.y; v[2] = b.x; v[3] = b.y; + } else { + const uint2 u = *reinterpret_cast(static_cast(p) + i); + const float2 a = __half22float2(*reinterpret_cast(&u.x)); + const float2 b = __half22float2(*reinterpret_cast(&u.y)); + v[0] = a.x; v[1] = a.y; v[2] = b.x; v[3] = b.y; + } +} + +__device__ __forceinline__ void store4(void* p, int dtype, size_t i, const float (&v)[4]) { + if (dtype == F32) { + *reinterpret_cast(static_cast(p) + i) = make_float4(v[0], v[1], v[2], v[3]); + } else if (dtype == BF16) { + __nv_bfloat162 a = __floats2bfloat162_rn(v[0], v[1]), b = __floats2bfloat162_rn(v[2], v[3]); + uint2 u; + u.x = *reinterpret_cast(&a); + u.y = *reinterpret_cast(&b); + *reinterpret_cast(static_cast<__nv_bfloat16*>(p) + i) = u; + } else { + half2 a = __floats2half2_rn(v[0], v[1]), b = __floats2half2_rn(v[2], v[3]); + uint2 u; + u.x = *reinterpret_cast(&a); + u.y = *reinterpret_cast(&b); + *reinterpret_cast(static_cast(p) + i) = u; + } +} + +// The finished outputs of one row's 128 columns from their fp32 sums (4 a lane): H / sqrt(128), * svh, + bias. +__device__ __forceinline__ void finish(float (&v)[4], int lane, const half* svh, const half* bias, int col) { + fwht128(v, lane); +#pragma unroll + for (int j = 0; j < 4; ++j) { + v[j] = v[j] * HAD_SCALE * __half2float(__ldg(svh + col + j)); + if (bias) v[j] += __half2float(__ldg(bias + col + j)); + } +} + +// A lane's words of one k step: at 1 and 2 bits the warp loads the step and each lane takes its words by shuffle. +template +__host__ __device__ constexpr bool step_shuffled() { + return K2 == 2 || K2 == 4; +} + +template +__host__ __device__ constexpr int step_regs() { + return step_shuffled() ? tile_words() / 4 : 8 * lane_words(); +} + +template +__device__ __forceinline__ void load_step(const uint32_t* step, int lane, uint32_t (&raw)[step_regs()]) { + constexpr int TW = tile_words(), LW = lane_words(); + if constexpr (step_shuffled()) { +#pragma unroll + for (int c = 0; c < step_regs(); ++c) raw[c] = __ldg(step + c * 32 + lane); + } else { + int word, offset; + lane_start(lane, word, offset); +#pragma unroll + for (int j = 0; j < 8; ++j) +#pragma unroll + for (int q = 0; q < LW; ++q) raw[j * LW + q] = __ldg(step + j * TW + (word + q) % TW); + } +} + +// Tile j's lane words from a loaded step; prev: the lane's first window starts in the word before its own. +template +__device__ __forceinline__ void step_lane_words(const uint32_t (&raw)[step_regs()], int j, int lane, bool prev, + uint32_t (&w)[lane_words()]) { + constexpr int TW = tile_words(), LW = lane_words(); + if constexpr (step_shuffled()) { + static_assert(LW == 2, "a lane's windows span two words at 1 and 2 bits"); + constexpr int LPW = 8 / K2; // lanes whose windows end in the same word + const uint32_t r = raw[j * TW / 32]; + const int base = (j * TW) % 32; + const uint32_t own = __shfl_sync(0xffffffffu, r, base + lane / LPW); + const uint32_t before = __shfl_sync(0xffffffffu, r, base + (lane / LPW + TW - 1) % TW); + w[0] = prev ? before : own; + w[1] = prev ? own : 0u; // a window inside one word never reads w[1] + } else { +#pragma unroll + for (int q = 0; q < LW; ++q) w[q] = raw[j * LW + q]; + } +} + +__global__ void __launch_bounds__(128) rot_in_kernel(const void* __restrict__ x, int x_dtype, + const half* __restrict__ suh, half* __restrict__ xh, int K) { + const int blk = blockIdx.x * 4 + (threadIdx.x >> 5), row = blockIdx.y, lane = threadIdx.x & 31; + if (blk * 128 >= K) return; + const int k = blk * 128 + 4 * lane; + float v[4], s[4]; + load4(x, x_dtype, (size_t)row * K + k, v); + load4(suh, F16, k, s); +#pragma unroll + for (int j = 0; j < 4; ++j) v[j] *= s[j]; + fwht128(v, lane); +#pragma unroll + for (int j = 0; j < 4; ++j) v[j] *= HAD_SCALE; + store4(xh, F16, (size_t)row * K + k, v); +} + +template +__global__ void __launch_bounds__(WK * 32) linear_kernel( + const half* __restrict__ xh, const uint32_t* __restrict__ T, long long stride_k, long long stride_nb, + const half* __restrict__ svh, const half* __restrict__ bias, void* __restrict__ y, int y_dtype, + float* __restrict__ Z, int* __restrict__ counters, int M, int K, int N, int SK, const int* __restrict__ skip) { + if (skip != nullptr && *skip != 0) return; // a device-side skip (every program: no counter moves) + constexpr int TW = tile_words(); + constexpr int LW = lane_words(); + extern __shared__ __align__(16) float red[]; // WK * RH * 128 floats + __shared__ int last; + const int RH = min(M, 8); // rows of red a warp + + const int nb = blockIdx.x, split = blockIdx.y, NB = gridDim.x; + const int warp = threadIdx.x >> 5, lane = threadIdx.x & 31; + const int g = lane >> 2, t = lane & 3; + const int per_warp = (K >> 4) / SK / WK; + const int kt0 = split * (per_warp * WK) + warp * per_warp; + const uint32_t* tiles = T + nb * stride_nb; + const int col0 = nb * 128; + bool prev = false; + if constexpr (step_shuffled()) { + int word, offset; + lane_start(lane, word, offset); + prev = word != lane / (8 / K2); + } + + for (int m0 = 0, pass = 0; m0 < M; m0 += 16, ++pass) { + const int R = min(16, M - m0); + + float acc[8][2][4]; +#pragma unroll + for (int i = 0; i < 8; ++i) +#pragma unroll + for (int h = 0; h < 2; ++h) +#pragma unroll + for (int c = 0; c < 4; ++c) acc[i][h][c] = 0.f; + + // the walk's rows for the mma, clamped so rows past the pass read inside the buffer (their outputs are dropped) + const int r0 = m0 + (g < R ? g : R - 1), r1 = m0 + (g + 8 < R ? g + 8 : R - 1); + const half* x0 = xh + (size_t)r0 * K; + const half* x1 = xh + (size_t)r1 * K; + const uint32_t* tile = tiles + (size_t)kt0 * stride_k; + // up to 6 bits the next k step's words are loaded while this one is decoded; 7 and 8 bits load per tile + constexpr bool PF = K2 <= 12; + constexpr int SR = step_regs(); + uint32_t cur[PF ? SR : 1], nxt[PF ? SR : 1]; + if constexpr (PF) load_step(tile, lane, cur); +#pragma unroll 1 + for (int i = 0; i < per_warp; ++i) { + const int kt = kt0 + i; + uint32_t a[4]; + a[0] = __ldg(reinterpret_cast(x0 + kt * 16 + 2 * t)); + a[1] = __ldg(reinterpret_cast(x1 + kt * 16 + 2 * t)); + a[2] = __ldg(reinterpret_cast(x0 + kt * 16 + 2 * t + 8)); + a[3] = __ldg(reinterpret_cast(x1 + kt * 16 + 2 * t + 8)); + if constexpr (PF) { + if (i + 1 < per_warp) load_step(tile + (size_t)(i + 1) * stride_k, lane, nxt); + } +#pragma unroll + for (int j = 0; j < 8; ++j) { + uint32_t w[LW]; + if constexpr (PF) step_lane_words(cur, j, lane, prev, w); + else ldg_lane_words(tile + (size_t)i * stride_k + j * TW, lane, w); + uint32_t b0[2], b1[2]; + decode_lane(w, lane, b0, b1); + mma16816(acc[j][0], a, b0); + mma16816(acc[j][1], a, b1); + } + if constexpr (PF) { +#pragma unroll + for (int q = 0; q < SR; ++q) cur[q] = nxt[q]; + } + } + + // the warps' sums, added in warp order, rows 0-7 of the pass and then rows 8-15 + for (int rlo = 0; rlo < R; rlo += 8) { + const int rn = min(R - rlo, 8); + __syncthreads(); // red is reused by every half and pass + if (g < RH) { +#pragma unroll + for (int i = 0; i < 8; ++i) +#pragma unroll + for (int h = 0; h < 2; ++h) { + const int col = i * 16 + h * 8 + 2 * t; + *reinterpret_cast(red + (warp * RH + g) * 128 + col) = + rlo ? make_float2(acc[i][h][2], acc[i][h][3]) : make_float2(acc[i][h][0], acc[i][h][1]); + } + } + __syncthreads(); + + if (SK == 1) { + for (int r = warp; r < rn; r += WK) { + float v[4]; + const float4 u = *reinterpret_cast(red + r * 128 + 4 * lane); + v[0] = u.x; v[1] = u.y; v[2] = u.z; v[3] = u.w; +#pragma unroll + for (int w = 1; w < WK; ++w) { + const float4 q = *reinterpret_cast(red + (w * RH + r) * 128 + 4 * lane); + v[0] += q.x; v[1] += q.y; v[2] += q.z; v[3] += q.w; + } + finish(v, lane, svh, bias, col0 + 4 * lane); + store4(y, y_dtype, (size_t)(m0 + rlo + r) * N + col0 + 4 * lane, v); + } + } else { + for (int idx = threadIdx.x; idx < rn * 32; idx += WK * 32) { + const int r = idx >> 5, c = 4 * (idx & 31); + float4 s = *reinterpret_cast(red + r * 128 + c); +#pragma unroll + for (int w = 1; w < WK; ++w) { + const float4 q = *reinterpret_cast(red + (w * RH + r) * 128 + c); + s.x += q.x; s.y += q.y; s.z += q.z; s.w += q.w; + } + *reinterpret_cast(Z + ((size_t)split * M + m0 + rlo + r) * N + col0 + c) = s; + } + } + } + if (SK > 1) { + __threadfence(); + __syncthreads(); + if (threadIdx.x == 0) last = atomicAdd(counters + pass * NB + nb, 1) == SK - 1; + __syncthreads(); + if (last) { + __threadfence(); + for (int r = warp; r < R; r += WK) { + const size_t at = ((size_t)m0 + r) * N + col0 + 4 * lane; + float4 s = __ldcg(reinterpret_cast(Z + at)); + for (int q = 1; q < SK; ++q) { + const float4 u = __ldcg(reinterpret_cast(Z + (size_t)q * M * N + at)); + s.x += u.x; s.y += u.y; s.z += u.z; s.w += u.w; + } + float v[4] = {s.x, s.y, s.z, s.w}; + finish(v, lane, svh, bias, col0 + 4 * lane); + store4(y, y_dtype, (size_t)(m0 + r) * N + col0 + 4 * lane, v); + } + if (threadIdx.x == 0) counters[pass * NB + nb] = 0; // every program of the block has arrived + } + } + __syncthreads(); // red is reused in the next pass + } +} + +// W_q [K, N] fp16 from the trellis words (tile (kt, nt) at kt * stride_k + (nt / 8) * stride_nb): one warp per tile. +template +__global__ void __launch_bounds__(32) unpack_kernel(const uint32_t* __restrict__ T, half* __restrict__ W, int N, + int64_t stride_k, int64_t stride_nb) { + const int kt = blockIdx.y, nt = blockIdx.x, lane = threadIdx.x; + uint32_t w[lane_words()]; + ldg_lane_words(T + kt * stride_k + (nt >> 3) * stride_nb + (nt & 7) * tile_words(), lane, w); + uint32_t b[2][2]; + decode_lane(w, lane, b[0], b[1]); +#pragma unroll + for (int j = 0; j < 8; ++j) { + const uint32_t v = b[j >> 2][(j >> 1) & 1]; + const unsigned short h = (j & 1) ? (unsigned short)(v >> 16) : (unsigned short)(v & 0xffffu); + W[(size_t)(kt * 16 + value_row(lane, j)) * N + nt * 16 + value_col(lane, j)] = __ushort_as_half(h); + } +} + +} // namespace tf_exl3_linear + +// Launched at mul1 (TF_EXL3_WIDTHS(X, 2) + K2 3, 5, 7): linear_kernel at WK 2 / 4 / 8, unpack_kernel. +template __global__ void tf_exl3_linear::linear_kernel<2, 2, 2>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<2, 2, 4>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<2, 2, 8>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::unpack_kernel<2, 2>(const uint32_t*, half*, int, int64_t, int64_t); +template __global__ void tf_exl3_linear::linear_kernel<3, 2, 2>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<3, 2, 4>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<3, 2, 8>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::unpack_kernel<3, 2>(const uint32_t*, half*, int, int64_t, int64_t); +template __global__ void tf_exl3_linear::linear_kernel<4, 2, 2>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<4, 2, 4>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<4, 2, 8>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::unpack_kernel<4, 2>(const uint32_t*, half*, int, int64_t, int64_t); +template __global__ void tf_exl3_linear::linear_kernel<5, 2, 2>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<5, 2, 4>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<5, 2, 8>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::unpack_kernel<5, 2>(const uint32_t*, half*, int, int64_t, int64_t); +template __global__ void tf_exl3_linear::linear_kernel<6, 2, 2>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<6, 2, 4>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<6, 2, 8>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::unpack_kernel<6, 2>(const uint32_t*, half*, int, int64_t, int64_t); +template __global__ void tf_exl3_linear::linear_kernel<7, 2, 2>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<7, 2, 4>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<7, 2, 8>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::unpack_kernel<7, 2>(const uint32_t*, half*, int, int64_t, int64_t); +template __global__ void tf_exl3_linear::linear_kernel<8, 2, 2>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<8, 2, 4>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<8, 2, 8>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::unpack_kernel<8, 2>(const uint32_t*, half*, int, int64_t, int64_t); +template __global__ void tf_exl3_linear::linear_kernel<10, 2, 2>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<10, 2, 4>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<10, 2, 8>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::unpack_kernel<10, 2>(const uint32_t*, half*, int, int64_t, int64_t); +template __global__ void tf_exl3_linear::linear_kernel<12, 2, 2>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<12, 2, 4>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<12, 2, 8>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::unpack_kernel<12, 2>(const uint32_t*, half*, int, int64_t, int64_t); +template __global__ void tf_exl3_linear::linear_kernel<14, 2, 2>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<14, 2, 4>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<14, 2, 8>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::unpack_kernel<14, 2>(const uint32_t*, half*, int, int64_t, int64_t); +template __global__ void tf_exl3_linear::linear_kernel<16, 2, 2>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<16, 2, 4>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::linear_kernel<16, 2, 8>(const half*, const uint32_t*, long long, long long, \ + const half*, const half*, void*, int, float*, int*, int, int, int, int, const int*); +template __global__ void tf_exl3_linear::unpack_kernel<16, 2>(const uint32_t*, half*, int, int64_t, int64_t); diff --git a/zig/kernels/cuda/deepseek_v41/mhc_cuda.cu b/zig/kernels/cuda/deepseek_v41/mhc_cuda.cu new file mode 100644 index 0000000000..92414ed276 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/mhc_cuda.cu @@ -0,0 +1,539 @@ +// Copy of src/tensorfold/families/deepseek_v41/cuda/mhc_cuda.cu (blob caa24ba4bb72): sass_check.sh +#define DSV41_MHC_NO_TORCH + + + +// TF_DSV41_MHC_CUDA=1: one mHC boundary (post + site + finish + next RMSNorm) as ONE launch for decode rows (R <= 16). + +// Triton's arithmetic operation for operation (same bits); the last CTA's finish reads every partial, so order is moot. + +#include +#include + +namespace dsv41_mhc { + +constexpr int D = 5120; +constexpr int NB = 40; // column blocks: part of the arithmetic +constexpr int CB = D / NB; // 128 columns a block: one FMA chain +constexpr int G8 = CB / 8; // 16-byte groups a block row +constexpr int NMIX = 24; +constexpr int MAXR = 16; +constexpr int NT = 128; // threads a CTA +constexpr int PB = CB + 8; // bf16 pitch of a staged row (272 B: conflict-free 16-byte reads) +constexpr int PF = CB + 4; // fp32 pitch (fp32 fn) +constexpr int NE = 4 * NB; // partial entries a row, (stream, block) order +constexpr int PW = 32; // floats an entry +constexpr int NQ = 7; // float4 groups of an entry the finish reads: [0, 28) +constexpr int STAGE4 = 640; // float4 a finish stage (10 KiB; 2 stages) +constexpr int ROWB = 128; // dynamic shared memory a row (the finish's row sums; nsys reads R from it) +static_assert(D % (NB * 8) == 0 && CB == 128, "the chains are 128 columns"); + +struct Args { + const uint16_t* x; long long xs; // streams [R, 4 D] bf16, row stride xs + uint16_t* xout; // new streams [R, 4 D] (POST) + const void* g; long long gr; int world; int gbf16; // gathered partials [world, R, D], rank stride gr + const float* post; const float* comb; const float* pre; // [R, 4], [R, 16] (comb[i, j] at 4 i + j), [R, 4] + const void* fn; const float* base; const float* scale; // [24, 4 D] bf16 / fp32, [24], [3] + float* part; uint16_t* c; // [R, 4, NB, 32] fp32, the collapsed row [R, D] bf16 + uint16_t* tap; long long ts; // [R, D] row stride ts (or null) + const void* nw; int nwbf16; // norm weight [D] bf16 / fp32 + uint16_t* out; // the next sublayer's input [R, D] bf16 + float* opre; float* opost; float* ocomb; // the next site's coefficients (MIX) + int* cnt; // [0] the ticket, [1] the tail's items, [2] exits (self-cleaning) + int R; + float eps, hc_eps, post_alpha; int iters; + long long spin; // TAIL: SM cycles a CTA waits for the last arrival (0: it leaves) + int defer; // TAIL: the coefficient items run in coef_kernel (a side stream) +}; + +__device__ __forceinline__ uint4 ld_stream(const void* p) { + uint4 v; + asm volatile("ld.global.nc.L1::no_allocate.v4.u32 {%0,%1,%2,%3}, [%4];" + : "=r"(v.x), "=r"(v.y), "=r"(v.z), "=r"(v.w) : "l"(p)); + return v; +} +__device__ __forceinline__ uint4 ld_plain(const void* p) { return *reinterpret_cast(p); } +__device__ __forceinline__ void widen8(const uint4 v, float (&f)[8]) { // 8 bf16, column ascending -> fp32 + f[0] = __uint_as_float(v.x << 16); f[1] = __uint_as_float(v.x & 0xffff0000u); + f[2] = __uint_as_float(v.y << 16); f[3] = __uint_as_float(v.y & 0xffff0000u); + f[4] = __uint_as_float(v.z << 16); f[5] = __uint_as_float(v.z & 0xffff0000u); + f[6] = __uint_as_float(v.w << 16); f[7] = __uint_as_float(v.w & 0xffff0000u); +} +__device__ __forceinline__ uint32_t bfbits(float f) { // cvt.rn.bf16.f32: Triton's .to(tl.bfloat16) + unsigned short h; + asm("cvt.rn.bf16.f32 %0, %1;" : "=h"(h) : "f"(f)); + return h; +} +__device__ __forceinline__ float bfr(float f) { return __uint_as_float(bfbits(f) << 16); } +__device__ __forceinline__ uint4 pack8(const float (&f)[8]) { // values already bf16: exact + return make_uint4(bfbits(f[0]) | (bfbits(f[1]) << 16), bfbits(f[2]) | (bfbits(f[3]) << 16), + bfbits(f[4]) | (bfbits(f[5]) << 16), bfbits(f[6]) | (bfbits(f[7]) << 16)); +} +__device__ __forceinline__ void load8f(const void* p, bool bf, float (&f)[8]) { // 8 bf16 or 8 fp32, read-only + if (bf) { + widen8(ld_stream(p), f); + } else { + const float4 a = __ldg(reinterpret_cast(p)), b = __ldg(reinterpret_cast(p) + 1); + f[0] = a.x; f[1] = a.y; f[2] = a.z; f[3] = a.w; f[4] = b.x; f[5] = b.y; f[6] = b.z; f[7] = b.w; + } +} +__device__ __forceinline__ float ex2a(float x) { // tl.exp's ex2.approx.f32 (not .ftz) + float y; + asm("ex2.approx.f32 %0, %1;" : "=f"(y) : "f"(x)); + return y; +} +__device__ __forceinline__ float divf(float a, float b) { // Triton's fp32 '/': div.full.f32 + float y; + asm("div.full.f32 %0, %1, %2;" : "=f"(y) : "f"(a), "f"(b)); + return y; +} +__device__ __forceinline__ void cp16(void* s, const void* g) { + const uint32_t a = static_cast(__cvta_generic_to_shared(s)); + asm volatile("cp.async.cg.shared.global [%0], [%1], 16;" :: "r"(a), "l"(g) : "memory"); +} +__device__ __forceinline__ void cp_commit() { asm volatile("cp.async.commit_group;" ::: "memory"); } +template __device__ __forceinline__ void cp_wait() { + asm volatile("cp.async.wait_group %0;" :: "n"(N) : "memory"); +} + +// X'_i of 8 columns: the post expression, one rounding an operation +__device__ __forceinline__ void post1(const Args& a, int r, int i, const float (&br)[8], const float (&x)[4][8], + float (&y)[8]) { + const float p = __ldg(a.post + r * 4 + i); + const float c0 = __ldg(a.comb + r * 16 + i), c1 = __ldg(a.comb + r * 16 + 4 + i); + const float c2 = __ldg(a.comb + r * 16 + 8 + i), c3 = __ldg(a.comb + r * 16 + 12 + i); +#pragma unroll + for (int k = 0; k < 8; ++k) { + const float m = __fadd_rn(__fadd_rn(__fadd_rn(__fmul_rn(c0, x[0][k]), __fmul_rn(c1, x[1][k])), + __fmul_rn(c2, x[2][k])), __fmul_rn(c3, x[3][k])); + y[k] = bfr(__fadd_rn(__fmul_rn(p, br[k]), m)); + } +} + +template struct Smem { + static constexpr int MAIN = 2 * MAXR * PB * 2 + (FN32 ? NMIX * PF * 4 : NMIX * PB * 2); + static constexpr int BYTES = MAIN > 2 * STAGE4 * 16 ? MAIN : 2 * STAGE4 * 16; +}; + +// phase B: NS chains a thread at once (chain c = t + s NT -> row c / K, kind c % K; kind 25 = the collapsed row) +template +__device__ __forceinline__ void chains(const Args& a, const uint16_t* xs, const uint16_t* cs, const void* fns, int K, + int nch, int b, int jp, int t) { + const uint16_t* src[NS]; + const void* wr[NS]; + int row[NS], kind[NS]; + float acc[NS]; +#pragma unroll + for (int s = 0; s < NS; ++s) { + const int cc = min(t + s * NT, nch - 1); + row[s] = cc / K; + kind[s] = MIX ? cc % K : 25; + src[s] = (kind[s] == 25 ? cs : xs) + row[s] * PB; + wr[s] = kind[s] < NMIX ? (FN32 ? static_cast(static_cast(fns) + kind[s] * PF) + : static_cast(static_cast(fns) + kind[s] * PB)) + : static_cast(src[s]); + acc[s] = 0.0f; + } +#pragma unroll + for (int k8 = 0; k8 < G8; ++k8) { +#pragma unroll + for (int s = 0; s < NS; ++s) { + float v[8], w[8]; + widen8(*reinterpret_cast(src[s] + k8 * 8), v); + if (FN32 && kind[s] < NMIX) { + const float4 w0 = reinterpret_cast(wr[s])[2 * k8]; + const float4 w1 = reinterpret_cast(wr[s])[2 * k8 + 1]; + w[0] = w0.x; w[1] = w0.y; w[2] = w0.z; w[3] = w0.w; w[4] = w1.x; w[5] = w1.y; w[6] = w1.z; w[7] = w1.w; + } else { + widen8(reinterpret_cast(wr[s])[k8], w); + } +#pragma unroll + for (int i = 0; i < 8; ++i) acc[s] = __fmaf_rn(v[i], w[i], acc[s]); + } + } +#pragma unroll + for (int s = 0; s < NS; ++s) + if (t + s * NT < nch) a.part[((row[s] * 4 + jp) * NB + b) * PW + kind[s]] = acc[s]; +} + +// row r's coefficients on 16 lanes (comb (i, j) at lane bits 3..2, 1..0): _finish_k's ops and butterfly order +__device__ __forceinline__ void coefs_of(const Args& a, const float* mx, int r, bool store, int e) { + const float rinv = __fdiv_rn(1.0f, __fsqrt_rn(__fadd_rn(__fdiv_rn(mx[24], 4.0f * D), a.eps))); + const float LOG2E = __int_as_float(0x3FB8AA3B), NLOG2E = __int_as_float(0xBFB8AA3B); + if (e < 4) { + const float pl = __fadd_rn(__fmul_rn(__fmul_rn(mx[e], rinv), __ldg(a.scale + 0)), __ldg(a.base + e)); + const float ql = __fadd_rn(__fmul_rn(__fmul_rn(mx[4 + e], rinv), __ldg(a.scale + 1)), __ldg(a.base + 4 + e)); + const float pre = __fadd_rn(a.hc_eps, divf(1.0f, __fadd_rn(ex2a(__fmul_rn(pl, NLOG2E)), 1.0f))); + const float post = __fmul_rn(a.post_alpha, divf(1.0f, __fadd_rn(ex2a(__fmul_rn(ql, NLOG2E)), 1.0f))); + if (store) { a.opre[r * 4 + e] = pre; a.opost[r * 4 + e] = post; } + } + const float cl = __fadd_rn(__fmul_rn(__fmul_rn(mx[8 + e], rinv), __ldg(a.scale + 2)), __ldg(a.base + 8 + e)); + float mxv = fmaxf(cl, __shfl_xor_sync(0xffffffffu, cl, 2)); + mxv = fmaxf(mxv, __shfl_xor_sync(0xffffffffu, mxv, 1)); + const float ce = ex2a(__fmul_rn(__fsub_rn(cl, mxv), LOG2E)); + float s = __fadd_rn(ce, __shfl_xor_sync(0xffffffffu, ce, 2)); + s = __fadd_rn(s, __shfl_xor_sync(0xffffffffu, s, 1)); + float cm = __fadd_rn(a.hc_eps, divf(ce, s)); + s = __fadd_rn(cm, __shfl_xor_sync(0xffffffffu, cm, 8)); + s = __fadd_rn(s, __shfl_xor_sync(0xffffffffu, s, 4)); + cm = divf(cm, __fadd_rn(a.hc_eps, s)); + for (int it = 1; it < a.iters; ++it) { + s = __fadd_rn(cm, __shfl_xor_sync(0xffffffffu, cm, 2)); + s = __fadd_rn(s, __shfl_xor_sync(0xffffffffu, s, 1)); + cm = divf(cm, __fadd_rn(a.hc_eps, s)); + s = __fadd_rn(cm, __shfl_xor_sync(0xffffffffu, cm, 8)); + s = __fadd_rn(s, __shfl_xor_sync(0xffffffffu, s, 4)); + cm = divf(cm, __fadd_rn(a.hc_eps, s)); + } + if (store) a.ocomb[r * 16 + e] = cm; +} +__device__ __forceinline__ void coefs(const Args& a, const float* rowv, int r, bool store, int e) { + coefs_of(a, rowv + r * (ROWB / 4), r, store, e); +} + +__host__ __device__ constexpr int chunk_entries(int R) { // partial entries a finish stage holds a row + return R <= 1 ? 80 : R <= 2 ? 40 : R <= 4 ? 20 : R <= 9 ? 10 : 5; +} + +// the last CTA: row sums (F1), then the coefficients (F3) beside the normed input (F2) +template +__device__ void finish(const Args& a, float* rowv, float4* st, int t) { + const int R = a.R; + if (MIX) { // F1: 7 float4 sums a row, a thread each; partials staged through 2 cp.async stages + const int ch = chunk_entries(R), nck = NE / ch, per = R * ch * NQ; + const int ri = t / NQ, qi = t % NQ; + float4 acc = make_float4(0.f, 0.f, 0.f, 0.f); + float ssc = 0.0f; + auto issue = [&](int c) { + float4* dst = st + (c & 1) * STAGE4; + for (int i = t; i < per; i += NT) { + const int r = i / (ch * NQ), rem = i % (ch * NQ), e = rem / NQ, q = rem % NQ; + cp16(dst + i, a.part + (static_cast(r) * NE + c * ch + e) * PW + q * 4); + } + cp_commit(); + }; + issue(0); + if (nck > 1) issue(1); + for (int c = 0; c < nck; ++c) { + if (c + 1 < nck) cp_wait<1>(); else cp_wait<0>(); + __syncthreads(); + if (t < R * NQ) { + const float4* src = st + (c & 1) * STAGE4 + ri * ch * NQ + qi; +#pragma unroll 5 + for (int e = 0; e < ch; ++e) { + const float4 v = src[e * NQ]; + acc.x = __fadd_rn(acc.x, v.x); + if (qi < 6) { + acc.y = __fadd_rn(acc.y, v.y); acc.z = __fadd_rn(acc.z, v.z); acc.w = __fadd_rn(acc.w, v.w); + } else if (c * ch + e < NB) { + ssc = __fadd_rn(ssc, v.y); // [25]: stream 0's blocks only + } + } + } + __syncthreads(); + if (c + 2 < nck) issue(c + 2); + } + if (t < R * NQ) { + float* rv = rowv + ri * (ROWB / 4); + if (qi < 6) { + rv[4 * qi] = acc.x; rv[4 * qi + 1] = acc.y; rv[4 * qi + 2] = acc.z; rv[4 * qi + 3] = acc.w; + } else { + rv[24] = acc.x; + rv[25] = __fdiv_rn(1.0f, __fsqrt_rn(__fadd_rn(__fdiv_rn(ssc, 1.0f * D), a.eps))); + } + } + } else if (t < R) { // final: the collapsed row's square sum alone + const float* p = a.part + static_cast(t) * NE * PW + 25; + float v[NB]; +#pragma unroll + for (int e = 0; e < NB; ++e) v[e] = __ldcg(p + e * PW); + float ssc = 0.0f; +#pragma unroll + for (int e = 0; e < NB; ++e) ssc = __fadd_rn(ssc, v[e]); + rowv[t * (ROWB / 4) + 25] = __fdiv_rn(1.0f, __fsqrt_rn(__fadd_rn(__fdiv_rn(ssc, 1.0f * D), a.eps))); + } + __syncthreads(); + const int warp = t >> 5, lane = t & 31; + const int f3w = MIX ? min(4, (R + 1) / 2) : 0; // warps with coefficient rows (2 rows a warp a pass) + if (warp < f3w) { +#pragma unroll 1 + for (int p = 0; p < 2 && warp * 2 + 8 * p < R; ++p) { + const int r = warp * 2 + (lane >> 4) + 8 * p; + coefs(a, rowv, min(r, R - 1), r < R, lane & 15); + } + } + const bool split = f3w < 4; // F2 beside F3 on the other warps, else after it + if (split && warp < f3w) return; + const int wid = split ? t - f3w * 32 : t, nwk = split ? NT - f3w * 32 : NT; + const bool nwbf = a.nwbf16 != 0; +#pragma unroll 4 + for (int i = wid; i < R * (D / 8); i += nwk) { + const int r = i / (D / 8), d0 = (i % (D / 8)) * 8; + const float rn = rowv[r * (ROWB / 4) + 25]; + float c[8], w[8], o[8]; + widen8(__ldcg(reinterpret_cast(a.c + static_cast(r) * D + d0)), c); + load8f(static_cast(a.nw) + d0 * (nwbf ? 2 : 4), nwbf, w); +#pragma unroll + for (int k = 0; k < 8; ++k) o[k] = __fmul_rn(__fmul_rn(c[k], rn), w[k]); + *reinterpret_cast(a.out + static_cast(r) * D + d0) = pack8(o); + } +} + +#include "mhc_tail.cuh" + +// DEFER: a row's coefficient item on its own CTA, on a side stream that joins before the next boundary +__global__ void __launch_bounds__(NT) coef_kernel(const Args a) { + __shared__ __align__(16) float4 st[NE * NQ]; + __shared__ float mx[32]; + coef_item(a, blockIdx.x, st, mx, threadIdx.x); +} + +// POST: post from gathered partials; COLL 1 / 2: c = stream 0 / pre-mix; MIX: next site; ALL4: a CTA writes 4 streams +template +__global__ void __launch_bounds__(NT, 4) boundary_kernel(const Args a) { + extern __shared__ float4 dyn[]; // R x ROWB: the finish's row sums + __shared__ __align__(16) unsigned char sm[Smem::BYTES]; + __shared__ int last; + const int b = blockIdx.x, j = ALL4 ? 0 : blockIdx.y, t = threadIdx.x, R = a.R; + const bool lead = j == 0; + uint16_t* xs = reinterpret_cast(sm); // stream j's new values: the chains' input + uint16_t* cs = xs + MAXR * PB; // the collapsed row (lead) + void* fns = cs + MAXR * PB; // this CTA's fn columns + constexpr int FV = FN32 ? 6 : 3; // 16-byte fn vectors a thread + uint4 fr[FV]; + if (MIX) { // in flight through phase A +#pragma unroll + for (int i = 0; i < FV; ++i) { + const int v = t + i * NT, m = v / (FN32 ? 32 : 16), q = v % (FN32 ? 32 : 16); + fr[i] = ld_stream(static_cast(a.fn) + + (static_cast(m) * 4 * D + j * D + b * CB) * (FN32 ? 4 : 2) + q * 16); + } + } + // ---- phase A: the post, the collapsed row, the taps; 8 columns a thread a step, every row ---- + for (int w = t; w < R * G8; w += NT) { + const int r = w / G8, q = w % G8, col = b * CB + q * 8; + const uint16_t* xr = a.x + static_cast(r) * a.xs + col; + float x[4][8], vj[8]; + if (POST || lead) { +#pragma unroll + for (int s = 0; s < 4; ++s) widen8(ld_plain(xr + s * D), x[s]); + } + if (POST) { + float br[8], gv[8]; + const char* gp = static_cast(a.g); + const int gsz = a.gbf16 ? 2 : 4; + load8f(gp + (static_cast(r) * D + col) * gsz, a.gbf16, br); + for (int rk = 1; rk < a.world; ++rk) { + load8f(gp + (rk * a.gr + static_cast(r) * D + col) * gsz, a.gbf16, gv); +#pragma unroll + for (int k = 0; k < 8; ++k) br[k] = __fadd_rn(br[k], gv[k]); + } +#pragma unroll + for (int k = 0; k < 8; ++k) br[k] = bfr(br[k]); + uint16_t* xo = a.xout + static_cast(r) * 4 * D + col; + if (lead) { // every stream's X' (the collapse, the taps) + float y[4][8]; +#pragma unroll + for (int i = 0; i < 4; ++i) post1(a, r, i, br, x, y[i]); +#pragma unroll + for (int i = 0; i < 4; ++i) + if (ALL4 || i == 0) *reinterpret_cast(xo + i * D) = pack8(y[i]); +#pragma unroll + for (int s = 0; s < 4; ++s) +#pragma unroll + for (int k = 0; k < 8; ++k) x[s][k] = y[s][k]; +#pragma unroll + for (int k = 0; k < 8; ++k) vj[k] = y[0][k]; + } else { + post1(a, r, j, br, x, vj); + *reinterpret_cast(xo + j * D) = pack8(vj); + } + } else if (lead) { +#pragma unroll + for (int k = 0; k < 8; ++k) vj[k] = x[0][k]; + } else { + widen8(ld_plain(xr + j * D), vj); + } + if (MIX) *reinterpret_cast(xs + r * PB + q * 8) = pack8(vj); + if (lead) { + float cv[8]; + if (COLL == 2) { + const float q0 = __ldg(a.pre + r * 4), q1 = __ldg(a.pre + r * 4 + 1); + const float q2 = __ldg(a.pre + r * 4 + 2), q3 = __ldg(a.pre + r * 4 + 3); +#pragma unroll + for (int k = 0; k < 8; ++k) + cv[k] = bfr(__fadd_rn(__fadd_rn(__fadd_rn(__fmul_rn(q0, x[0][k]), __fmul_rn(q1, x[1][k])), + __fmul_rn(q2, x[2][k])), __fmul_rn(q3, x[3][k]))); + } else { +#pragma unroll + for (int k = 0; k < 8; ++k) cv[k] = x[0][k]; + } + const uint4 cp = pack8(cv); + *reinterpret_cast(a.c + static_cast(r) * D + col) = cp; + *reinterpret_cast(cs + r * PB + q * 8) = cp; + if (POST && a.tap != nullptr) { + float tp[8]; +#pragma unroll + for (int k = 0; k < 8; ++k) + tp[k] = bfr(__fmul_rn(__fadd_rn(__fadd_rn(__fadd_rn(x[0][k], x[1][k]), x[2][k]), x[3][k]), 0.25f)); + *reinterpret_cast(a.tap + static_cast(r) * a.ts + col) = pack8(tp); + } + } + } + if (MIX) { +#pragma unroll + for (int i = 0; i < FV; ++i) { + const int v = t + i * NT, m = v / (FN32 ? 32 : 16), q = v % (FN32 ? 32 : 16); + reinterpret_cast(fns)[m * (FN32 ? PF / 4 : PB / 8) + q] = fr[i]; + } + } + __syncthreads(); + // ---- phase B: the FMA chains ---- + const int K = (MIX ? 25 : 0) + (lead ? 1 : 0); + const int nch = R * K; + switch ((nch + NT - 1) / NT) { + case 1: chains<1, FN32, MIX>(a, xs, cs, fns, K, nch, b, j, t); break; + case 2: chains<2, FN32, MIX>(a, xs, cs, fns, K, nch, b, j, t); break; + case 3: chains<3, FN32, MIX>(a, xs, cs, fns, K, nch, b, j, t); break; + case 4: chains<4, FN32, MIX>(a, xs, cs, fns, K, nch, b, j, t); break; + default: break; + } + // ---- the ticket: the last CTA runs the finish ---- + __threadfence(); + __syncthreads(); + if (TAIL) { + tail(a, reinterpret_cast(dyn), reinterpret_cast(sm), t); + return; + } + if (t == 0) last = atomicAdd(a.cnt, 1) == static_cast(gridDim.x * gridDim.y) - 1; + __syncthreads(); + if (!last) return; + __threadfence(); + if (t == 0) atomicExch(a.cnt, 0); // self-cleaning: the next launch / replay starts at 0 + finish(a, reinterpret_cast(dyn), reinterpret_cast(sm), t); +} + +template +cudaError_t launch_one(const Args& a, cudaStream_t stream, bool tail = false) { + const dim3 grid(NB, ALL4 ? 1 : 4); + if (tail) boundary_kernel<<>>(a); + else boundary_kernel<<>>(a); + return cudaGetLastError(); +} + +// mode: 0 boundary (POST, pre-mix collapse, MIX), 1 site from stream 0, 2 site with a carried pre-mix, 3 final +inline cudaError_t coef_launch(const Args& a, cudaStream_t stream) { + if (a.R < 1 || a.R > MAXR) return cudaErrorInvalidValue; + coef_kernel<<>>(a); + return cudaGetLastError(); +} + +// tail: the parallel tail instead of the last CTA's finish (the same bits) +inline cudaError_t dispatch(const Args& a, int mode, bool fn32, cudaStream_t stream, bool tail = false) { + if (a.R < 1 || a.R > MAXR || a.world < 1 || a.world > 8) return cudaErrorInvalidValue; + switch (mode * 2 + (fn32 ? 1 : 0)) { + case 0: return launch_one(a, stream, tail); + case 1: return launch_one(a, stream, tail); + case 2: return launch_one(a, stream, tail); + case 3: return launch_one(a, stream, tail); + case 4: return launch_one(a, stream, tail); + case 5: return launch_one(a, stream, tail); + case 6: case 7: return launch_one(a, stream, tail); + default: return cudaErrorInvalidValue; + } +} + +} // namespace dsv41_mhc + +#ifndef DSV41_MHC_NO_TORCH // the compile test builds the kernels alone (nvcc, no torch headers) +#include +#include +#include + +namespace { +template T* ptr(const at::Tensor& t) { return t.numel() ? static_cast(t.data_ptr()) : nullptr; } +} + +void dsv41_mhc_run_cuda(const at::Tensor& x, at::Tensor& xout, const at::Tensor& g, const at::Tensor& post, + const at::Tensor& comb, const at::Tensor& pre, const at::Tensor& fn, const at::Tensor& base, + const at::Tensor& scale, at::Tensor& part, at::Tensor& c, at::Tensor& tap, + const at::Tensor& nw, at::Tensor& out, at::Tensor& opre, at::Tensor& opost, + at::Tensor& ocomb, at::Tensor& cnt, int64_t R, int64_t mode, double eps, double hc_eps, + double post_alpha, int64_t iters, int64_t spin, int64_t defer) { + using namespace dsv41_mhc; + Args a{}; + a.x = ptr(x); a.xs = x.stride(0); + a.xout = ptr(xout); + a.g = g.numel() ? g.data_ptr() : nullptr; a.gr = g.numel() ? g.stride(0) : 0; + a.world = g.numel() ? static_cast(g.size(0)) : 1; + a.gbf16 = g.scalar_type() == at::kBFloat16 ? 1 : 0; + a.post = ptr(post); a.comb = ptr(comb); a.pre = ptr(pre); + a.fn = fn.numel() ? fn.data_ptr() : nullptr; a.base = ptr(base); a.scale = ptr(scale); + a.part = ptr(part); a.c = ptr(c); + a.tap = ptr(tap); a.ts = tap.numel() ? tap.stride(0) : 0; + a.nw = nw.data_ptr(); a.nwbf16 = nw.scalar_type() == at::kBFloat16 ? 1 : 0; + a.out = ptr(out); + a.opre = ptr(opre); a.opost = ptr(opost); a.ocomb = ptr(ocomb); + a.cnt = ptr(cnt); + a.R = static_cast(R); + a.eps = static_cast(eps); a.hc_eps = static_cast(hc_eps); + a.post_alpha = static_cast(post_alpha); a.iters = static_cast(iters); + a.spin = spin; // < 0: the last CTA's finish; >= 0: the parallel tail + a.defer = spin >= 0 && defer ? 1 : 0; + const bool fn32 = fn.numel() && fn.scalar_type() == at::kFloat; + C10_CUDA_CHECK(dispatch(a, static_cast(mode), fn32, at::cuda::getCurrentCUDAStream(), spin >= 0)); +} + +void dsv41_mhc_coef_cuda(const at::Tensor& part, const at::Tensor& base, const at::Tensor& scale, at::Tensor& opre, + at::Tensor& opost, at::Tensor& ocomb, int64_t R, double eps, double hc_eps, double post_alpha, + int64_t iters) { + using namespace dsv41_mhc; + Args a{}; + a.part = ptr(part); a.base = ptr(base); a.scale = ptr(scale); + a.opre = ptr(opre); a.opost = ptr(opost); a.ocomb = ptr(ocomb); + a.R = static_cast(R); + a.eps = static_cast(eps); a.hc_eps = static_cast(hc_eps); + a.post_alpha = static_cast(post_alpha); a.iters = static_cast(iters); + C10_CUDA_CHECK(coef_launch(a, at::cuda::getCurrentCUDAStream())); +} + +std::vector dsv41_mhc_info_cuda(int64_t device) { // what the GPU offers for cross-CTA reductions + int cl = 0, sms = 0, optin = 0, maxcl = 0; + cudaDeviceGetAttribute(&cl, cudaDevAttrClusterLaunch, static_cast(device)); + cudaDeviceGetAttribute(&sms, cudaDevAttrMultiProcessorCount, static_cast(device)); + cudaDeviceGetAttribute(&optin, cudaDevAttrMaxSharedMemoryPerBlockOptin, static_cast(device)); + if (cl) { + cudaLaunchConfig_t cfg = {}; + cfg.gridDim = dim3(dsv41_mhc::NB, 4); + cfg.blockDim = dim3(dsv41_mhc::NT); + cfg.dynamicSmemBytes = dsv41_mhc::MAXR * dsv41_mhc::ROWB; + cudaFuncSetAttribute(dsv41_mhc::boundary_kernel, + cudaFuncAttributeNonPortableClusterSizeAllowed, 1); + if (cudaOccupancyMaxPotentialClusterSize(&maxcl, dsv41_mhc::boundary_kernel, + &cfg) != cudaSuccess) { + maxcl = -1; + cudaGetLastError(); + } + } + int blocks = 0; + cudaOccupancyMaxActiveBlocksPerMultiprocessor(&blocks, dsv41_mhc::boundary_kernel, + dsv41_mhc::NT, dsv41_mhc::MAXR * dsv41_mhc::ROWB); + return {cl, maxcl, sms, optin, blocks}; +} +#endif + +// The instantiations dispatch() launches: modes 0-2 at bf16 / fp32 fn, mode 3 at bf16; tail off and on. +template __global__ void dsv41_mhc::boundary_kernel(const dsv41_mhc::Args); +template __global__ void dsv41_mhc::boundary_kernel(const dsv41_mhc::Args); +template __global__ void dsv41_mhc::boundary_kernel(const dsv41_mhc::Args); +template __global__ void dsv41_mhc::boundary_kernel(const dsv41_mhc::Args); +template __global__ void dsv41_mhc::boundary_kernel(const dsv41_mhc::Args); +template __global__ void dsv41_mhc::boundary_kernel(const dsv41_mhc::Args); +template __global__ void dsv41_mhc::boundary_kernel(const dsv41_mhc::Args); +template __global__ void dsv41_mhc::boundary_kernel(const dsv41_mhc::Args); +template __global__ void dsv41_mhc::boundary_kernel(const dsv41_mhc::Args); +template __global__ void dsv41_mhc::boundary_kernel(const dsv41_mhc::Args); +template __global__ void dsv41_mhc::boundary_kernel(const dsv41_mhc::Args); +template __global__ void dsv41_mhc::boundary_kernel(const dsv41_mhc::Args); +template __global__ void dsv41_mhc::boundary_kernel(const dsv41_mhc::Args); +template __global__ void dsv41_mhc::boundary_kernel(const dsv41_mhc::Args); diff --git a/zig/kernels/cuda/deepseek_v41/mhc_pf.cu b/zig/kernels/cuda/deepseek_v41/mhc_pf.cu new file mode 100644 index 0000000000..a11c0ce34e --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/mhc_pf.cu @@ -0,0 +1,336 @@ +// Copy of src/tensorfold/families/deepseek_v41/cuda/mhc_pf.cu (blob 8615dbba9caf): sass_check.sh +#define DSV41_MHC_PF_NO_TORCH + + + +// TF_DSV41_MHC_PF: an mHC site of a prefill segment (R >= 256 rows) as one CUDA pass over the 4 streams. + +// mhc_cuda.cu's decode arithmetic, operation for operation; rows never mix, no atomics, one writer an output. + +#include +#include + +namespace dsv41_mhc_pf { + +constexpr int D = 5120; +constexpr int NB = 40; // column blocks: part of the arithmetic +constexpr int CB = D / NB; // 128 columns a block: one FMA chain +constexpr int G8 = CB / 8; // 16-byte groups a block row +constexpr int NMIX = 24; +constexpr int BM = 16; // rows a CTA +constexpr int NT = 128; // threads a CTA +constexpr int IPT = BM * G8 / NT; // phase A items a thread (2) +constexpr int PB = CB + 8; // bf16 pitch of a staged row (272 B: conflict-free 16-byte reads across 8 rows) +constexpr int PF = CB + 4; // fp32 pitch (fp32 fn) +constexpr int PW = 32; // floats a PART entry +constexpr int NS = 13; // chains a thread at most +constexpr int MAXW = 8; // ranks +static_assert(D % (NB * 8) == 0 && CB == 128 && IPT * NT == BM * G8, "the chains are 128 columns"); + +template struct Lay { + static constexpr int XS = 4 * BM * PB * 2; // new streams bf16 [stream][row][PB] + static constexpr int CS = BM * PB * 2; // collapsed row bf16 [row][PB] + static constexpr int FNB = FN32 ? 4 * NMIX * PF * 4 : 4 * NMIX * PB * 2; // fn [stream][mix][pitch] + static constexpr int BYTES = XS + CS + FNB; +}; + +struct Args { + const uint16_t* x; long long xs; // streams [R, 4 D] bf16, row stride xs + uint16_t* xout; // new streams [R, 4 D] (POST; may be x) + const void* g; long long gr; int world; int gbf16; // gathered partials [world, R, D], rank stride gr + const float* post; const float* comb; const float* pre; // [R, 4], [R, 16] (comb[i, j] at 4 i + j), [R, 4] + const void* fn; // [24, 4 D] bf16 / fp32 + float* part; uint16_t* c; // [R, 4, NB, 32] fp32, the collapsed row [R, D] bf16 + uint16_t* tap; long long ts; // [R, D] row stride ts (or null) + int R; +}; + +__device__ __forceinline__ uint4 ld_stream(const void* p) { + uint4 v; + asm volatile("ld.global.nc.L1::no_allocate.v4.u32 {%0,%1,%2,%3}, [%4];" + : "=r"(v.x), "=r"(v.y), "=r"(v.z), "=r"(v.w) : "l"(p)); + return v; +} +__device__ __forceinline__ uint4 ld_plain(const void* p) { return *reinterpret_cast(p); } +__device__ __forceinline__ void widen8(const uint4 v, float (&f)[8]) { // 8 bf16, column ascending -> fp32 + f[0] = __uint_as_float(v.x << 16); f[1] = __uint_as_float(v.x & 0xffff0000u); + f[2] = __uint_as_float(v.y << 16); f[3] = __uint_as_float(v.y & 0xffff0000u); + f[4] = __uint_as_float(v.z << 16); f[5] = __uint_as_float(v.z & 0xffff0000u); + f[6] = __uint_as_float(v.w << 16); f[7] = __uint_as_float(v.w & 0xffff0000u); +} +__device__ __forceinline__ uint32_t bfbits(float f) { // cvt.rn.bf16.f32: Triton's .to(tl.bfloat16) + unsigned short h; + asm("cvt.rn.bf16.f32 %0, %1;" : "=h"(h) : "f"(f)); + return h; +} +__device__ __forceinline__ float bfr(float f) { return __uint_as_float(bfbits(f) << 16); } +__device__ __forceinline__ uint4 pack8(const float (&f)[8]) { // values already bf16: exact + return make_uint4(bfbits(f[0]) | (bfbits(f[1]) << 16), bfbits(f[2]) | (bfbits(f[3]) << 16), + bfbits(f[4]) | (bfbits(f[5]) << 16), bfbits(f[6]) | (bfbits(f[7]) << 16)); +} +__device__ __forceinline__ void load8f(const void* p, bool bf, float (&f)[8]) { // 8 bf16 or 8 fp32, read-only + if (bf) { + widen8(ld_stream(p), f); + } else { + const float4 a = __ldg(reinterpret_cast(p)), b = __ldg(reinterpret_cast(p) + 1); + f[0] = a.x; f[1] = a.y; f[2] = a.z; f[3] = a.w; f[4] = b.x; f[5] = b.y; f[6] = b.z; f[7] = b.w; + } +} +__device__ __forceinline__ void cp16(void* s, const void* g) { + const uint32_t a = static_cast(__cvta_generic_to_shared(s)); + asm volatile("cp.async.cg.shared.global [%0], [%1], 16;" :: "r"(a), "l"(g) : "memory"); +} +__device__ __forceinline__ void cp_commit() { asm volatile("cp.async.commit_group;" ::: "memory"); } +__device__ __forceinline__ void cp_wait_all() { asm volatile("cp.async.wait_group 0;" ::: "memory"); } + +// X'_i of 8 columns of row r: mhc_cuda.cu post1's expression, one rounding an operation +__device__ __forceinline__ void post1(const Args& a, int r, int i, const float (&br)[8], const float (&x)[4][8], + float (&y)[8]) { + const float p = __ldg(a.post + r * 4 + i); + const float c0 = __ldg(a.comb + r * 16 + i), c1 = __ldg(a.comb + r * 16 + 4 + i); + const float c2 = __ldg(a.comb + r * 16 + 8 + i), c3 = __ldg(a.comb + r * 16 + 12 + i); +#pragma unroll + for (int k = 0; k < 8; ++k) { + const float m = __fadd_rn(__fadd_rn(__fadd_rn(__fmul_rn(c0, x[0][k]), __fmul_rn(c1, x[1][k])), + __fmul_rn(c2, x[2][k])), __fmul_rn(c3, x[3][k])); + y[k] = bfr(__fadd_rn(__fmul_rn(p, br[k]), m)); + } +} + +// one k8 step of the fn row (stream j, mix m): 8 weights fp32 +template +__device__ __forceinline__ void fn8(const unsigned char* fns, int j, int m, int k8, float (&w)[8]) { + if (FN32) { + const float4* p = reinterpret_cast(fns + ((j * NMIX + m) * PF + k8 * 8) * 4); + const float4 a = p[0], b = p[1]; + w[0] = a.x; w[1] = a.y; w[2] = a.z; w[3] = a.w; w[4] = b.x; w[5] = b.y; w[6] = b.z; w[7] = b.w; + } else { + widen8(*reinterpret_cast(fns + ((j * NMIX + m) * PB + k8 * 8) * 2), w); + } +} + +// POST: the post from the gathered partials; COLL 1 = c is stream 0, 2 = the pre-mix; TAP: the DSpark taps (POST) +template +__global__ void __launch_bounds__(NT, 2) site_kernel(const Args a) { + extern __shared__ __align__(16) unsigned char sm[]; + uint16_t* xs = reinterpret_cast(sm); // [4][BM][PB] + uint16_t* cs = reinterpret_cast(sm + Lay::XS); // [BM][PB] + unsigned char* fns = sm + Lay::XS + Lay::CS; // [4][24][PB or PF] + const int b = blockIdx.x, r0 = blockIdx.y * BM, t = threadIdx.x, R = a.R; + + // fn's slice of block b into shared memory, in flight through phase A + { + constexpr int QR = FN32 ? CB / 4 : CB / 8; // 16-byte pieces a (stream, mix) row + const char* f = static_cast(a.fn); + for (int v = t; v < 4 * NMIX * QR; v += NT) { + const int jm = v / QR, q = v % QR, j = jm / NMIX, m = jm % NMIX; + const long long src = static_cast(m) * 4 * D + j * D + b * CB; + if (FN32) cp16(fns + (jm * PF + q * 4) * 4, f + (src + q * 4) * 4); + else cp16(fns + (jm * PB + q * 8) * 2, f + (src + q * 8) * 2); + } + cp_commit(); + } + + // ---- phase A: every load of the thread's items first, then the post / collapse / taps and the stores ---- + uint4 xv[IPT][4]; + float br[IPT][8]; +#pragma unroll + for (int n = 0; n < IPT; ++n) { + const int i = t + n * NT, r = r0 + i / G8, col = b * CB + (i % G8) * 8; + const uint16_t* xr = a.x + static_cast(r < R ? r : 0) * a.xs + col; +#pragma unroll + for (int s = 0; s < 4; ++s) xv[n][s] = r < R ? ld_plain(xr + s * D) : make_uint4(0u, 0u, 0u, 0u); + } + if (POST) { + const char* gp = static_cast(a.g); + const int gsz = a.gbf16 ? 2 : 4; +#pragma unroll + for (int n = 0; n < IPT; ++n) { + const int i = t + n * NT, r = r0 + i / G8, col = b * CB + (i % G8) * 8; + const long long off = static_cast(r < R ? r : 0) * D + col; + load8f(gp + off * gsz, a.gbf16, br[n]); + for (int rk = 1; rk < a.world; ++rk) { + float gv[8]; + load8f(gp + (rk * a.gr + off) * gsz, a.gbf16, gv); +#pragma unroll + for (int k = 0; k < 8; ++k) br[n][k] = __fadd_rn(br[n][k], gv[k]); + } +#pragma unroll + for (int k = 0; k < 8; ++k) br[n][k] = bfr(br[n][k]); + } + } +#pragma unroll + for (int n = 0; n < IPT; ++n) { + const int i = t + n * NT, rl = i / G8, q = i % G8, r = r0 + rl, col = b * CB + q * 8; + const bool ok = r < R; + float x[4][8]; +#pragma unroll + for (int s = 0; s < 4; ++s) widen8(xv[n][s], x[s]); + if (POST && ok) { + float y[4][8]; +#pragma unroll + for (int s = 0; s < 4; ++s) post1(a, r, s, br[n], x, y[s]); + uint16_t* xo = a.xout + static_cast(r) * 4 * D + col; +#pragma unroll + for (int s = 0; s < 4; ++s) *reinterpret_cast(xo + s * D) = pack8(y[s]); +#pragma unroll + for (int s = 0; s < 4; ++s) +#pragma unroll + for (int k = 0; k < 8; ++k) x[s][k] = y[s][k]; + } + float cv[8]; + if (COLL == 2 && ok) { + const float q0 = __ldg(a.pre + r * 4), q1 = __ldg(a.pre + r * 4 + 1); + const float q2 = __ldg(a.pre + r * 4 + 2), q3 = __ldg(a.pre + r * 4 + 3); +#pragma unroll + for (int k = 0; k < 8; ++k) + cv[k] = bfr(__fadd_rn(__fadd_rn(__fadd_rn(__fmul_rn(q0, x[0][k]), __fmul_rn(q1, x[1][k])), + __fmul_rn(q2, x[2][k])), __fmul_rn(q3, x[3][k]))); + } else { +#pragma unroll + for (int k = 0; k < 8; ++k) cv[k] = x[0][k]; + } + const uint4 cp = pack8(cv); + if (ok) { + *reinterpret_cast(a.c + static_cast(r) * D + col) = cp; + if (POST && TAP) { + float tp[8]; +#pragma unroll + for (int k = 0; k < 8; ++k) + tp[k] = bfr(__fmul_rn(__fadd_rn(__fadd_rn(__fadd_rn(x[0][k], x[1][k]), x[2][k]), x[3][k]), 0.25f)); + *reinterpret_cast(a.tap + static_cast(r) * a.ts + col) = pack8(tp); + } + } +#pragma unroll + for (int s = 0; s < 4; ++s) *reinterpret_cast(xs + (s * BM + rl) * PB + q * 8) = pack8(x[s]); + *reinterpret_cast(cs + rl * PB + q * 8) = cp; + } + cp_wait_all(); + __syncthreads(); + + // ---- phase B: chains of row rl, stream j: h 0 mixes 0-12; h 1 mixes 13-23 + square sums (24; 25 at stream 0) + const int rl = t % BM, j = (t / BM) % 4, h = t / (4 * BM), r = r0 + rl; + const bool coll = h == 1 && j == 0; + const uint16_t* vrow = xs + (j * BM + rl) * PB; + const uint16_t* crow = cs + rl * PB; + float acc[NS]; +#pragma unroll + for (int s = 0; s < NS; ++s) acc[s] = 0.0f; + if (h == 0) { +#pragma unroll 1 + for (int k8 = 0; k8 < G8; ++k8) { + float v[8]; + widen8(*reinterpret_cast(vrow + k8 * 8), v); +#pragma unroll + for (int s = 0; s < 13; ++s) { + float w[8]; + fn8(fns, j, s, k8, w); +#pragma unroll + for (int k = 0; k < 8; ++k) acc[s] = __fmaf_rn(v[k], w[k], acc[s]); + } + } + } else { +#pragma unroll 1 + for (int k8 = 0; k8 < G8; ++k8) { + float v[8]; + widen8(*reinterpret_cast(vrow + k8 * 8), v); +#pragma unroll + for (int s = 0; s < 11; ++s) { + float w[8]; + fn8(fns, j, 13 + s, k8, w); +#pragma unroll + for (int k = 0; k < 8; ++k) acc[s] = __fmaf_rn(v[k], w[k], acc[s]); + } +#pragma unroll + for (int k = 0; k < 8; ++k) acc[11] = __fmaf_rn(v[k], v[k], acc[11]); + if (coll) { + float cvv[8]; + widen8(*reinterpret_cast(crow + k8 * 8), cvv); +#pragma unroll + for (int k = 0; k < 8; ++k) acc[12] = __fmaf_rn(cvv[k], cvv[k], acc[12]); + } + } + } + if (r < R) { + float* pr = a.part + ((static_cast(r) * 4 + j) * NB + b) * PW; + if (h == 0) { +#pragma unroll + for (int s = 0; s < 13; ++s) pr[s] = acc[s]; + } else { +#pragma unroll + for (int s = 0; s < 12; ++s) pr[13 + s] = acc[s]; // mixes 13-23, [24] the square sum + if (coll) pr[25] = acc[12]; + } + } +} + +template +cudaError_t launch_one(const Args& a, cudaStream_t stream) { + static bool attr = false; // > 48 KiB of dynamic shared memory needs the opt-in, once + if (!attr) { + const cudaError_t e = cudaFuncSetAttribute(site_kernel, + cudaFuncAttributeMaxDynamicSharedMemorySize, Lay::BYTES); + if (e != cudaSuccess) return e; + attr = true; + } + const dim3 grid(NB, (a.R + BM - 1) / BM); + site_kernel<<::BYTES, stream>>>(a); + return cudaGetLastError(); +} + +// mode: 0 boundary (POST, pre-mix collapse), 1 site from stream 0, 2 site with a carried pre-mix +inline cudaError_t dispatch(const Args& a, int mode, bool fn32, bool tap, cudaStream_t stream) { + if (a.R < 1 || a.world < 1 || a.world > MAXW || (tap && mode != 0)) return cudaErrorInvalidValue; + switch (mode * 4 + (fn32 ? 2 : 0) + (tap ? 1 : 0)) { + case 0: return launch_one(a, stream); + case 1: return launch_one(a, stream); + case 2: return launch_one(a, stream); + case 3: return launch_one(a, stream); + case 4: return launch_one(a, stream); + case 6: return launch_one(a, stream); + case 8: return launch_one(a, stream); + case 10: return launch_one(a, stream); + default: return cudaErrorInvalidValue; + } +} + +} // namespace dsv41_mhc_pf + +#ifndef DSV41_MHC_PF_NO_TORCH // the compile test builds the kernels alone (nvcc, no torch headers) +#include +#include +#include + +namespace { +template T* ptr(const at::Tensor& t) { return t.numel() ? static_cast(t.data_ptr()) : nullptr; } +} + +void dsv41_mhc_pf_run_cuda(const at::Tensor& x, at::Tensor& xout, const at::Tensor& g, const at::Tensor& post, + const at::Tensor& comb, const at::Tensor& pre, const at::Tensor& fn, at::Tensor& part, + at::Tensor& c, at::Tensor& tap, int64_t R, int64_t mode) { + using namespace dsv41_mhc_pf; + Args a{}; + a.x = ptr(x); a.xs = x.stride(0); + a.xout = ptr(xout); + a.g = g.numel() ? g.data_ptr() : nullptr; a.gr = g.numel() ? g.stride(0) : 0; + a.world = g.numel() ? static_cast(g.size(0)) : 1; + a.gbf16 = g.scalar_type() == at::kBFloat16 ? 1 : 0; + a.post = ptr(post); a.comb = ptr(comb); a.pre = ptr(pre); + a.fn = fn.data_ptr(); + a.part = ptr(part); a.c = ptr(c); + a.tap = ptr(tap); a.ts = tap.numel() ? tap.stride(0) : 0; + a.R = static_cast(R); + const bool fn32 = fn.scalar_type() == at::kFloat; + C10_CUDA_CHECK(dispatch(a, static_cast(mode), fn32, tap.numel() > 0, at::cuda::getCurrentCUDAStream())); +} +#endif + +// The instantiations dispatch() launches: (mode, fn32, tap) as its switch lists them. +template __global__ void dsv41_mhc_pf::site_kernel(const dsv41_mhc_pf::Args); +template __global__ void dsv41_mhc_pf::site_kernel(const dsv41_mhc_pf::Args); +template __global__ void dsv41_mhc_pf::site_kernel(const dsv41_mhc_pf::Args); +template __global__ void dsv41_mhc_pf::site_kernel(const dsv41_mhc_pf::Args); +template __global__ void dsv41_mhc_pf::site_kernel(const dsv41_mhc_pf::Args); +template __global__ void dsv41_mhc_pf::site_kernel(const dsv41_mhc_pf::Args); +template __global__ void dsv41_mhc_pf::site_kernel(const dsv41_mhc_pf::Args); +template __global__ void dsv41_mhc_pf::site_kernel(const dsv41_mhc_pf::Args); diff --git a/zig/kernels/cuda/deepseek_v41/mhc_tail.cuh b/zig/kernels/cuda/deepseek_v41/mhc_tail.cuh new file mode 100644 index 0000000000..e3f236de73 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/mhc_tail.cuh @@ -0,0 +1,94 @@ +// mhc_cuda.cu's parallel tail (TF_DSV41_MHC_TAIL=1): coef/norm items and tail(), inside namespace dsv41_mhc +#pragma once +// ---- parallel tail (TF_DSV41_MHC_TAIL=1): live CTAs claim finish()'s items from a counter, same chains, same bits +constexpr int F2C = NT * 8; // columns a normed-input item +constexpr int F2N = D / F2C; // items a row +static_assert(D % F2C == 0 && NE * NQ * 16 <= 2 * STAGE4 * 16, "tail items"); + +__device__ __forceinline__ int ld_acquire(const int* p) { + int v; + asm volatile("ld.acquire.gpu.b32 %0, [%1];" : "=r"(v) : "l"(p) : "memory"); + return v; +} +__device__ __forceinline__ float rn_of(float ssc, float eps) { + return __fdiv_rn(1.0f, __fsqrt_rn(__fadd_rn(__fdiv_rn(ssc, 1.0f * D), eps))); +} + +// row r's 25 sums over its 160 entries and the square sum over stream 0's 40 (finish()'s chains), then coefs() +__device__ void coef_item(const Args& a, int r, float4* st, float* mx, int t) { + for (int i = t; i < NE * NQ; i += NT) + cp16(st + i, a.part + (static_cast(r) * NE + i / NQ) * PW + (i % NQ) * 4); + cp_commit(); + cp_wait<0>(); + __syncthreads(); + if (t < 26) { + const float* sf = reinterpret_cast(st); + const int n = t == 25 ? NB : NE; + float acc = 0.0f; +#pragma unroll 8 + for (int e = 0; e < n; ++e) acc = __fadd_rn(acc, sf[e * (NQ * 4) + t]); + mx[t] = t == 25 ? rn_of(acc, a.eps) : acc; + } + __syncthreads(); + if (t < 32) coefs_of(a, mx, r, t < 16, t & 15); +} + +// normed input of row r, columns [F2C q, F2C q + F2C): bf16((c * rn) * w) +__device__ void norm_item(const Args& a, int r, int q, float* buf, int t) { + const int d0 = q * F2C + t * 8; + const bool nwbf = a.nwbf16 != 0; + float c[8], w[8], o[8]; + widen8(__ldcg(reinterpret_cast(a.c + static_cast(r) * D + d0)), c); + load8f(static_cast(a.nw) + d0 * (nwbf ? 2 : 4), nwbf, w); + if (t < NB) buf[t] = __ldcg(a.part + (static_cast(r) * NE + t) * PW + 25); + __syncthreads(); + if (t == 0) { + float ssc = 0.0f; +#pragma unroll 8 + for (int e = 0; e < NB; ++e) ssc = __fadd_rn(ssc, buf[e]); + buf[NB] = rn_of(ssc, a.eps); + } + __syncthreads(); + const float rn = buf[NB]; +#pragma unroll + for (int k = 0; k < 8; ++k) o[k] = __fmul_rn(__fmul_rn(c[k], rn), w[k]); + *reinterpret_cast(a.out + static_cast(r) * D + d0) = pack8(o); +} + +template +__device__ void tail(const Args& a, float* rowv, float4* st, int t) { + __shared__ int live, item; + __shared__ float nbuf[NB + 1]; // a normed-input item's 40 square sums + rn + const int total = static_cast(gridDim.x * gridDim.y), R = a.R; + if (t == 0) { + int n = atomicAdd(a.cnt, 1) + 1; + if (n < total && a.spin > 0) { + const long long t0 = clock64(); + while ((n = ld_acquire(a.cnt)) < total && clock64() - t0 < a.spin) __nanosleep(32); + } + live = n >= total; + } + __syncthreads(); + if (live) { + __threadfence(); + const int nc = MIX && !a.defer ? R : 0, nitems = nc + R * F2N; + for (;;) { + if (t == 0) item = atomicAdd(a.cnt + 1, 1); + __syncthreads(); + const int it = item; + if (it >= nitems) break; + if (it < nc) { + coef_item(a, it, st, rowv, t); + } else { + const int f = it - nc; + norm_item(a, f / F2N, f % F2N, nbuf, t); + } + __syncthreads(); + } + } + if (t == 0 && atomicAdd(a.cnt + 2, 1) == total - 1) { // the last to leave: every counter back at 0 + atomicExch(a.cnt, 0); + atomicExch(a.cnt + 1, 0); + atomicExch(a.cnt + 2, 0); + } +} diff --git a/zig/kernels/cuda/deepseek_v41/nvcc-docker.sh b/zig/kernels/cuda/deepseek_v41/nvcc-docker.sh new file mode 100755 index 0000000000..e71d12832e --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/nvcc-docker.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# nvcc from the NGC PyTorch image for hosts without CUDA (`zig build -Dnvcc=`); paths mount 1:1. +set -euo pipefail +IMAGE="${TF_ZIG_IMAGE:-nvcr.io/nvidia/pytorch:26.07-py3}" +# the container's memory and memory+swap cap: the Docker daemon runs it outside the caller's own limits +MEM="${TF_ZIG_DOCKER_MEMORY:-8g}" +# the mount of a directory: its repository's root (a source's headers sit beside it), else the directory (a cache) +root() { git -C "$1" rev-parse --show-toplevel 2>/dev/null || printf '%s\n' "$1"; } +roots=("$PWD" "$(root "$PWD")") +for a in "$@"; do + [ "${a:0:1}" = / ] || continue + d="$(dirname "$a")" + mkdir -p "$d" + roots+=("$(root "$d")") +done +mapfile -t roots < <(printf '%s\n' "${roots[@]}" | sort -u) +mounts=() +for r in "${roots[@]}"; do mounts+=(-v "$r:$r"); done +docker run --rm -i --network none --memory "$MEM" --memory-swap "$MEM" -u "$(id -u):$(id -g)" "${mounts[@]}" -w "$PWD" \ + --entrypoint /usr/local/cuda/bin/nvcc "$IMAGE" "$@" +# depfilter FILE CWD ROOT...: keep the -MF file's dependencies under a mounted root (image headers drop out) +depfilter() { + local path=$1 cwd=$2 text target d full keep=() out=() reals k deps=() + shift 2 + for r in "$@"; do keep+=("$(realpath -m -- "$r")/"); done + text=$(sed -e ':a' -e '/\\$/{N;s/\\\n/ /;ba' -e '}' "$path") + target=${text%%:*} + read -r -d "" -a deps <<< "${text#*:}" || true + [ "${#deps[@]}" -gt 0 ] && for d in "${deps[@]}"; do + if [ "${d:0:1}" = / ]; then full=$d; else full=$cwd/$d; fi + reals=$(realpath -m -- "$full") + for k in "${keep[@]}"; do [[ "$reals" == "$k"* ]] && { out+=("$d"); break; }; done + done + { printf '%s: ' "$target"; local first=1; for d in "${out[@]}"; do [ $first = 1 ] && printf '%s' "$d" || printf ' \\\n %s' "$d"; first=0; done; printf '\n'; } > "$path" +} +dep="" +prev="" +for a in "$@"; do + [ "$prev" = "-MF" ] && dep="$a" + prev="$a" +done +if [ -n "$dep" ] && [ -f "$dep" ]; then + depfilter "$dep" "$PWD" "${roots[@]}" +fi diff --git a/zig/kernels/cuda/deepseek_v41/pfdense.cuh b/zig/kernels/cuda/deepseek_v41/pfdense.cuh new file mode 100644 index 0000000000..ee6f38f868 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/pfdense.cuh @@ -0,0 +1,488 @@ +// Copy of src/tensorfold/families/deepseek_v41/cuda/pfdense.cuh (blob 7757ae8b9b9f): sass_check.sh + + + +// pfdense: prefill's dense EXL3 GEMM with the trellis decoded inside it, bit-exact to unpack + Triton's _gemm epilogue + +#pragma once + +#include +#include +#include + +#include "decode.cuh" +#include "pfdense_args.h" + +namespace dsv41_pfd { + +using namespace tf_exl3; + +// -- configurations: all give the same bits (one fp32 mma.m16n8k16 chain per output, k16 slices ascending from +0) --- +template +struct Cfg { + static constexpr int BM = BM_, WM = WM_, WN = WN_, KS = KS_, NST = NST_; + static constexpr int NW = WM * WN, THREADS = NW * 32; + static constexpr int MT = BM / (WM * 16); // m16 tiles a warp + static constexpr int NT = 8 / WN; // n16 tiles (one trellis tile each) a warp + static constexpr int CPR = 2 * KS, LDA = 16 * KS; // 16-byte chunks / halves of a stage's xh row + static constexpr int EPR = BM / WM; // epilogue rows a round (one warp row's rows) + static constexpr int GPW = MT >= WN ? MT / WN : 1; // 16-row groups a warp in its round + static constexpr int NSPLIT = WN >= MT ? WN / MT : 1; // column parts of a group + static constexpr int EN8 = 16 / NSPLIT; // n8 tiles of a warp's epilogue part + static_assert(BM % (WM * 16) == 0 && MT >= 1 && 8 % WN == 0, "warp grid"); + static_assert(MT % WN == 0 || WN % MT == 0, "epilogue units"); + static_assert(KS == 2 || KS == 4 || KS == 8, "k steps a stage"); + static_assert((KS * 8) % NW == 0, "a stage's tiles split evenly over the warps"); + static_assert(NST >= 2, "stages"); +}; + +template +struct Lay { + static constexpr int TW = 4 * K2; // words of a tile + static constexpr int A_BYTES = C::BM * C::LDA * 2; + static constexpr int W_BYTES = C::KS * 8 * TW * 4; // 128 K2 bytes a k step + static constexpr int STAGE = A_BYTES + W_BYTES; + static constexpr int RING = C::NST * STAGE; + static constexpr int BBUF = C::KS * 8 * 32 * 16; // decoded B fragments of a stage: 512 B a tile + static constexpr int MAIN = RING + 2 * BBUF; + static constexpr int EPI = C::EPR * 128 * 2 * 2; // top and rest, bf16 [EPR][128] each + static constexpr int SMEM = MAIN > EPI ? MAIN : EPI; + static constexpr int TPW = C::KS * 8 / C::NW; // tiles a warp decodes a stage + static constexpr int MINB = 2 * (SMEM + 1024) <= 102400 ? 2 : 1; + static_assert(A_BYTES % 16 == 0 && STAGE % 16 == 0, "16-byte stages"); + static_assert(!LANES || (TPW % 2 == 0 && C::KS % 2 == 0 && K2 % 2 == 0 && K2 <= 12), + "lanes: tile pairs of 2-step groups, K2 4-12"); +}; + +// the configuration list: pfdense_args.h (DSV41_PFD_CFGS) + +// -- small device helpers ----------------------------------------------------------------------------------------- +__device__ __forceinline__ void cp_async16(void* smem, const void* gmem, bool ok) { + const unsigned s = (unsigned)__cvta_generic_to_shared(smem); + asm volatile("cp.async.cg.shared.global [%0], [%1], 16, %2;\n" ::"r"(s), "l"(gmem), "r"(ok ? 16 : 0)); +} +__device__ __forceinline__ void cp_commit() { asm volatile("cp.async.commit_group;\n" ::); } +template +__device__ __forceinline__ void cp_wait() { asm volatile("cp.async.wait_group %0;\n" ::"n"(N)); } +__device__ __forceinline__ void bar_sync() { asm volatile("bar.sync 0;\n" ::: "memory"); } +__device__ __forceinline__ void ldsm4(uint32_t (&a)[4], const void* p) { + const unsigned s = (unsigned)__cvta_generic_to_shared(p); + asm volatile("ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0,%1,%2,%3}, [%4];\n" + : "=r"(a[0]), "=r"(a[1]), "=r"(a[2]), "=r"(a[3]) + : "r"(s)); +} +// mma.m16n8k16, bf16 inputs, fp32 accumulators (the epilogue's Hadamard chain): d += a @ b +__device__ __forceinline__ void mma16816_bf16(float (&d)[4], const uint32_t (&a)[4], const uint32_t (&b)[2]) { + asm volatile("mma.sync.aligned.m16n8k16.row.col.f32.bf16.bf16.f32 {%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, " + "{%0,%1,%2,%3};\n" + : "+f"(d[0]), "+f"(d[1]), "+f"(d[2]), "+f"(d[3]) + : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b[0]), "r"(b[1])); +} + +// chunk c of row r of a stage's xh rows (CPR chunks a row): an ldmatrix phase's 8 rows in 8 bank groups +template +__device__ __forceinline__ int swz(int r, int c) { + if constexpr (CPR == 4) return c ^ ((r >> 1) & 3); + else return c ^ (r & 7); +} +// the epilogue's [rows][128] bf16 planes: 16 chunks a row, the low 3 bits XORed with the row +__device__ __forceinline__ int esw(int r, int c) { return c ^ (r & 7); } + +// -- decode: stage tiles into B fragments; strips / stored via decode.cuh's decode_lane (upstream unpack_kernel's) -- +template +__device__ __forceinline__ uint4 frag_strips(const uint32_t* tile, int lane) { + uint32_t w[lane_words()]; + load_lane_words(tile, lane, w); + uint32_t b0[2], b1[2]; + decode_lane(w, lane, b0, b1); + return make_uint4(b0[0], b0[1], b1[0], b1[1]); +} + +// lanes (dense3.cu): lane L's words at ((w / 4) * 32 + L) * 4 + w % 4; tiles j, j + 1 by 64-bit funnel shifts +template +__device__ __forceinline__ void frag_lanes_pair(const uint32_t* grp, int gs, int j, int lane, uint4& f0, uint4& f1) { + constexpr int n = 4 * K2, GW = 2 * K2; + const int p = (gs * 8 + j) * n, wi = p >> 5, bo = p & 31; + const int c0 = wi >> 2, off = wi & 3; + const uint4 u = *reinterpret_cast(grp + (c0 * 32 + lane) * 4); + const uint4 v = c0 + 1 < GW / 4 ? *reinterpret_cast(grp + ((c0 + 1) * 32 + lane) * 4) + : make_uint4(0u, 0u, 0u, 0u); + const uint32_t x0 = off == 0 ? u.x : off == 1 ? u.y : off == 2 ? u.z : u.w; + const uint32_t x1 = off == 0 ? u.y : off == 1 ? u.z : off == 2 ? u.w : v.x; + const uint32_t x2 = off == 0 ? u.z : off == 1 ? u.w : off == 2 ? v.x : v.y; + const uint32_t x3 = off == 0 ? u.w : off == 1 ? v.x : off == 2 ? v.y : v.z; + const uint64_t hi = ((uint64_t)x0 << 32) | x1, lo = ((uint64_t)x2 << 32) | x3; + const uint64_t sh = bo ? ((hi << bo) | (lo >> (64 - bo))) : hi; + const uint64_t sl = lo << bo; + const uint64_t o0 = sh >> (64 - n); + const uint64_t o1 = ((sh << n) | (sl >> (64 - n))) >> (64 - n); + const uint32_t tails = (uint32_t)(o0 & 0xffffu) | ((uint32_t)(o1 & 0xffffu) << 16); + const uint32_t pre = __shfl_sync(0xffffffffu, tails, (lane + 31) & 31); + uint32_t b[2][4]; +#pragma unroll + for (int h = 0; h < 2; ++h) { + const uint64_t o = h ? o1 : o0; + const uint64_t pp = h ? (pre >> 16) : (pre & 0xffffu); + uint32_t s[8]; +#pragma unroll + for (int q = 0; q < 8; ++q) { + const int sft = (7 - q) * K2 / 2; // the window of value 8 lane + q: bits [sft, sft + 16) + uint64_t w = o >> sft; + if (n - sft < 16) w |= pp << (n - sft); + s[q] = (uint32_t)(w & 0xffffu); + } + b[h][0] = decode2(s[0], s[1]); + b[h][1] = decode2(s[2], s[3]); + b[h][2] = decode2(s[4], s[5]); + b[h][3] = decode2(s[6], s[7]); + } + f0 = make_uint4(b[0][0], b[0][1], b[0][2], b[0][3]); + f1 = make_uint4(b[1][0], b[1][1], b[1][2], b[1][3]); +} + +// warp `warp` decodes tiles warp * TPW .. + TPW - 1 (tile kk * 8 + j of the stage) of the raw stage W into D +template +__device__ __forceinline__ void decode_stage(const uint32_t* W, uint4* D, int warp, int lane) { + using L = Lay; + if constexpr (!LANES) { +#pragma unroll + for (int i = 0; i < L::TPW; ++i) { + const int ti = warp * L::TPW + i; + D[ti * 32 + lane] = frag_strips(W + ti * L::TW, lane); + } + } else { +#pragma unroll + for (int i = 0; i < L::TPW; i += 2) { + const int ti = warp * L::TPW + i, kk = ti >> 3, j = ti & 7; + uint4 f0, f1; + frag_lanes_pair(W + (kk >> 1) * (64 * K2), kk & 1, j, lane, f0, f1); + D[ti * 32 + lane] = f0; + D[(ti + 1) * 32 + lane] = f1; + } + } +} + +// H's B fragment, k16 step kb, n8 tile nb (H[k][n] = (-1)^popc(k & n), bf16 +-1); P: this lane's pair at kb = nb = 0 +__device__ __forceinline__ uint32_t had_base(int lane) { + const int g = lane >> 2, t = lane & 3; + const uint32_t lo = (__popc((2 * t) & g) & 1) ? 0xBF80u : 0x3F80u; + const uint32_t hi = (__popc((2 * t + 1) & g) & 1) ? 0xBF80u : 0x3F80u; + return lo | (hi << 16); +} +__device__ __forceinline__ void had_frag(uint32_t P, int kb, int nb, uint32_t (&b)[2]) { + const uint32_t s = (__popc(kb & (nb >> 1)) & 1) ? 0x80008000u : 0u; + b[0] = P ^ s; + b[1] = b[0] ^ ((nb & 1) ? 0x80008000u : 0u); +} + +// the finished pair of outputs (columns col, col + 1 of one row): Triton's y * SCALE * svh (+ bias as one fma) +__device__ __forceinline__ void store2(const Args& a, long long row, int col, float y0, float y1) { + const half2 sv = *reinterpret_cast(static_cast(a.svh) + col); + const float s0 = __low2float(sv), s1 = __high2float(sv); + float v0 = __fmul_rn(y0, HAD_SCALE), v1 = __fmul_rn(y1, HAD_SCALE); + if (a.bias != nullptr) { + const half2 bb = *reinterpret_cast(static_cast(a.bias) + col); + v0 = __fmaf_rn(v0, s0, __low2float(bb)); + v1 = __fmaf_rn(v1, s1, __high2float(bb)); + } else { + v0 = __fmul_rn(v0, s0); + v1 = __fmul_rn(v1, s1); + } + const long long at = row * a.o_stride + col; + if (a.out_type == OUT_F32) { + *reinterpret_cast(static_cast(a.out) + at) = make_float2(v0, v1); + } else if (a.out_type == OUT_BF16) { + *reinterpret_cast<__nv_bfloat162*>(static_cast<__nv_bfloat16*>(a.out) + at) = __floats2bfloat162_rn(v0, v1); + } else { + *reinterpret_cast(static_cast(a.out) + at) = __floats2half2_rn(v0, v1); + } +} + +// -- the kernel ----------------------------------------------------------------------------------------------------- +template +__global__ void __launch_bounds__(C::THREADS, (Lay::MINB)) pfd_kernel(const Args a) { + using L = Lay; + constexpr int BM = C::BM, KS = C::KS, NST = C::NST, CPR = C::CPR, LDA = C::LDA, MT = C::MT, NT = C::NT; + constexpr int TW = L::TW; + extern __shared__ __align__(128) unsigned char smem[]; + const int warp = threadIdx.x >> 5, lane = threadIdx.x & 31; + const int wm = warp / C::WN, wn = warp % C::WN; + const int g = lane >> 2, t = lane & 3; + const int M = a.M, K = a.K, NB = a.N >> 7, S = K / (16 * KS); + + // Triton _gemm's raster: GROUP row tiles a column sweep + const int pid = blockIdx.x, nm = (M + BM - 1) / BM; + const int per = a.group * NB, first = (pid / per) * a.group; + const int rows = min(nm - first, a.group); + const int pm = first + (pid % per) % rows, pn = (pid % per) / rows; + const int m0 = pm * BM; + const uint32_t* Tb = a.T + (size_t)pn * a.stride_nb; + const half* xh = static_cast(a.xh); + unsigned char* bdec = smem + L::RING; + + // stage s's xh rows into ring slot s % NST (rows >= M: zero-filled, never stored) + auto load_a = [&](int s) { + half* A = reinterpret_cast(smem + (s % NST) * L::STAGE); + const int k0 = s * KS * 16; + for (int i = threadIdx.x; i < BM * CPR; i += C::THREADS) { + const int r = i / CPR, c = i % CPR; + const bool ok = m0 + r < M; + cp_async16(A + r * LDA + swz(r, c) * 8, xh + (size_t)(ok ? m0 + r : 0) * K + k0 + c * 8, ok); + } + }; + // stage s's words (KS k steps x 8 tiles, as stored) into ring slot `slot` + auto load_w = [&](int s, int slot) { + uint32_t* W = reinterpret_cast(smem + slot * L::STAGE + L::A_BYTES); + for (int i = threadIdx.x; i < KS * 8 * K2; i += C::THREADS) { + const int kk = i / (8 * K2), q = i % (8 * K2); + cp_async16(W + kk * 8 * TW + q * 4, Tb + (size_t)(s * KS + kk) * a.stride_k + q * 4, true); + } + }; + auto words_of = [&](int slot) { + return reinterpret_cast(smem + slot * L::STAGE + L::A_BYTES); + }; + auto bbuf = [&](int b) { return reinterpret_cast(bdec + b * L::BBUF); }; + + float acc[MT][2 * NT][4]; +#pragma unroll + for (int i = 0; i < MT; ++i) +#pragma unroll + for (int j = 0; j < 2 * NT; ++j) +#pragma unroll + for (int c = 0; c < 4; ++c) acc[i][j][c] = 0.f; + + // prologue: W(0) in slot NST - 1 (free until iteration 0 refills it), then groups G_s = {A(s), W(s + 1)} + load_w(0, NST - 1); + cp_commit(); +#pragma unroll + for (int s = 0; s < NST - 1; ++s) { + if (s < S) load_a(s); + if (s + 1 < S) load_w(s + 1, s); + cp_commit(); + } + cp_wait(); + bar_sync(); + decode_stage(words_of(NST - 1), bbuf(0), warp, lane); + +#pragma unroll 1 + for (int s = 0; s < S; ++s) { + cp_wait(); // G_s landed: A(s), W(s + 1) + bar_sync(); // ... for every thread; B(s) decoded; iteration s - 1 done + { + const int f = s + NST - 1; // refill the slot iteration s - 1 used + if (f < S) load_a(f); + if (f + 1 < S) load_w(f + 1, f % NST); + cp_commit(); + } + if (s + 1 < S) decode_stage(words_of(s % NST), bbuf((s + 1) & 1), warp, lane); + const half* A = reinterpret_cast(smem + (s % NST) * L::STAGE); + const uint4* D = bbuf(s & 1); +#pragma unroll + for (int kk = 0; kk < KS; ++kk) { + uint32_t af[MT][4]; +#pragma unroll + for (int mt = 0; mt < MT; ++mt) { + const int r = (wm * MT + mt) * 16 + (lane & 15); + ldsm4(af[mt], A + r * LDA + swz(r, 2 * kk + (lane >> 4)) * 8); + } +#pragma unroll + for (int nt = 0; nt < NT; ++nt) { + const uint4 bb = D[(kk * 8 + wn * NT + nt) * 32 + lane]; + const uint32_t b0[2] = {bb.x, bb.y}, b1[2] = {bb.z, bb.w}; +#pragma unroll + for (int mt = 0; mt < MT; ++mt) { + mma16816(acc[mt][2 * nt], af[mt], b0); + mma16816(acc[mt][2 * nt + 1], af[mt], b1); + } + } + } + } + cp_wait<0>(); + bar_sync(); // the ring and the B buffers become the epilogue's planes + + // epilogue, round wm: this warp row's rows. Earlier rounds: wait them out (2 barriers each) + __nv_bfloat16* Et = reinterpret_cast<__nv_bfloat16*>(smem); + __nv_bfloat16* Er = Et + C::EPR * 128; + for (int r = 0; r < wm; ++r) { + bar_sync(); + bar_sync(); + } +#pragma unroll + for (int mt = 0; mt < MT; ++mt) +#pragma unroll + for (int j = 0; j < 2 * NT; ++j) { + const int col = 16 * (wn * NT + (j >> 1)) + 8 * (j & 1) + 2 * t; +#pragma unroll + for (int hh = 0; hh < 2; ++hh) { + const float c0 = acc[mt][j][2 * hh], c1 = acc[mt][j][2 * hh + 1]; + const __nv_bfloat162 top = __floats2bfloat162_rn(c0, c1); + const __nv_bfloat162 rest = __floats2bfloat162_rn(__fsub_rn(c0, __low2float(top)), + __fsub_rn(c1, __high2float(top))); + const int row = mt * 16 + g + 8 * hh; + const int at = row * 128 + esw(row, col >> 3) * 8 + (col & 7); + *reinterpret_cast<__nv_bfloat162*>(Et + at) = top; + *reinterpret_cast<__nv_bfloat162*>(Er + at) = rest; + } + } + bar_sync(); + { + const uint32_t P = had_base(lane); + const int part = wn % C::NSPLIT; +#pragma unroll + for (int gi = 0; gi < C::GPW; ++gi) { + const int grp = (wn / C::NSPLIT) * C::GPW + gi; + float y[C::EN8][4]; +#pragma unroll + for (int e = 0; e < C::EN8; ++e) +#pragma unroll + for (int c = 0; c < 4; ++c) y[e][c] = 0.f; +#pragma unroll + for (int q = 0; q < 16; ++q) { // top's k16 steps 0-7, then rest's 0-7: one chain + const int kb = q & 7; + const int row = grp * 16 + (lane & 15); + uint32_t af[4]; + ldsm4(af, (q < 8 ? Et : Er) + row * 128 + esw(row, 2 * kb + (lane >> 4)) * 8); +#pragma unroll + for (int e = 0; e < C::EN8; ++e) { + uint32_t hb[2]; + had_frag(P, kb, part * C::EN8 + e, hb); + mma16816_bf16(y[e], af, hb); + } + } +#pragma unroll + for (int e = 0; e < C::EN8; ++e) { + const int col = pn * 128 + 8 * (part * C::EN8 + e) + 2 * t; +#pragma unroll + for (int hh = 0; hh < 2; ++hh) { + const int row = m0 + wm * C::EPR + grp * 16 + g + 8 * hh; + if (row < M) store2(a, row, col, y[e][2 * hh], y[e][2 * hh + 1]); + } + } + } + } + bar_sync(); + for (int r = wm + 1; r < C::WM; ++r) { + bar_sync(); + bar_sync(); + } +} + +// Tests: W_q [K, N] fp16 read back through the kernel's decode; one CTA a (k step pair, 128-column block), 4 warps +template +__global__ void __launch_bounds__(128) pfd_dequant_kernel(const uint32_t* __restrict__ T, long long stride_k, + long long stride_nb, half* __restrict__ out, int N) { + using C = Cfg<64, 2, 2, 2, 2>; + using L = Lay; + __shared__ __align__(16) uint32_t W[2 * 8 * 4 * K2]; + __shared__ __align__(16) uint4 D[2 * 8 * 32]; + const int ks = blockIdx.x, pn = blockIdx.y, warp = threadIdx.x >> 5, lane = threadIdx.x & 31; + for (int i = threadIdx.x; i < 2 * 8 * K2; i += 128) { + const int kk = i / (8 * K2), q = i % (8 * K2); + const uint4 v = *reinterpret_cast(T + (size_t)pn * stride_nb + (size_t)(2 * ks + kk) * stride_k + + q * 4); + *reinterpret_cast(W + kk * 8 * L::TW + q * 4) = v; + } + __syncthreads(); + decode_stage(W, D, warp, lane); + __syncthreads(); + const int g = lane >> 2, t = lane & 3; + for (int ti = warp; ti < 16; ti += 4) { + const uint4 f = D[ti * 32 + lane]; + const uint32_t r[4] = {f.x, f.y, f.z, f.w}; + const int kk = ti >> 3, j = ti & 7; +#pragma unroll + for (int q = 0; q < 4; ++q) { // b0[0], b0[1], b1[0], b1[1] + const int k = (2 * ks + kk) * 16 + 2 * t + 8 * (q & 1), n = pn * 128 + j * 16 + g + 8 * (q >> 1); + out[(size_t)k * N + n] = __ushort_as_half((unsigned short)(r[q] & 0xffffu)); + out[(size_t)(k + 1) * N + n] = __ushort_as_half((unsigned short)(r[q] >> 16)); + } + } +} + +// -- host side (CUDA runtime only; the bindings are pfdense.cpp) --------------------------------------------------- +template +inline void launch(const Args& a, cudaStream_t stream) { + using L = Lay; + auto kern = pfd_kernel; + static bool ready = false; + if (!ready) { + cudaFuncSetAttribute(kern, cudaFuncAttributeMaxDynamicSharedMemorySize, L::SMEM); + ready = true; + } + const int nm = (a.M + C::BM - 1) / C::BM; + kern<<<(unsigned)(nm * (a.N >> 7)), C::THREADS, L::SMEM, stream>>>(a); +} + +template +inline int occupancy() { + using L = Lay; + auto kern = pfd_kernel; + cudaFuncSetAttribute(kern, cudaFuncAttributeMaxDynamicSharedMemorySize, L::SMEM); + int n = 0; + cudaOccupancyMaxActiveBlocksPerMultiprocessor(&n, kern, C::THREADS, L::SMEM); + return n; +} + +// [BM, WM, WN, KS, NST, threads, smem, blocks an SM] of configuration `cfg` at (K2, LANES); -1s for an unknown id +template +inline void describe(int cfg, long long (&o)[8]) { + for (auto& v : o) v = -1; +#define DSV41_PFD_DESC(ID, BM, WM, WN, KS, NST) \ + if (cfg == ID) { \ + using C = Cfg; \ + o[0] = BM; o[1] = WM; o[2] = WN; o[3] = KS; o[4] = NST; o[5] = C::THREADS; \ + o[6] = Lay::SMEM; o[7] = occupancy(); \ + } + DSV41_PFD_CFGS(DSV41_PFD_DESC) +#undef DSV41_PFD_DESC +} + +template +inline bool run(const Args& a, int cfg, cudaStream_t stream) { +#define DSV41_PFD_RUN(ID, BM, WM, WN, KS, NST) \ + if (cfg == ID) { \ + launch>(a, stream); \ + return true; \ + } + DSV41_PFD_CFGS(DSV41_PFD_RUN) +#undef DSV41_PFD_RUN + return false; +} + +template +inline void dequant(const uint32_t* T, long long stride_k, long long stride_nb, void* out, int K, int N, + cudaStream_t stream) { + pfd_dequant_kernel<<>>( + T, stride_k, stride_nb, static_cast(out), N); +} + +} // namespace dsv41_pfd + +// every kernel of a width, explicitly (the sm_121 compile test builds the .cu files alone) +#define DSV41_PFD_INST(ID, BM, WM, WN, KS, NST) \ + template __global__ void dsv41_pfd::pfd_kernel>( \ + const dsv41_pfd::Args); + +// the entry points of width K2 (pfdense_args.h): strips / stored always, lanes when LANES_OK +#define DSV41_PFD_ENTRY(K2_, RUN_LANES, DESC_LANES, DQ_LANES) \ + namespace dsv41_pfd { \ + bool run_k##K2_(const Args& a, bool lanes, int cfg, cudaStream_t s) { \ + if (lanes) return RUN_LANES; \ + return run(a, cfg, s); \ + } \ + void describe_k##K2_(bool lanes, int cfg, long long (&o)[8]) { \ + if (lanes) { DESC_LANES; } else describe(cfg, o); \ + } \ + bool dequant_k##K2_(bool lanes, const uint32_t* T, long long sk, long long snb, void* out, int K, int N, \ + cudaStream_t s) { \ + if (lanes) return DQ_LANES; \ + dequant(T, sk, snb, out, K, N, s); \ + return true; \ + } \ + } +#define DSV41_PFD_WIDTH(K2_) \ + DSV41_PFD_ENTRY(K2_, run(a, cfg, s), describe(cfg, o), \ + (dequant(T, sk, snb, out, K, N, s), true)) +#define DSV41_PFD_WIDTH_STRIPS(K2_) \ + DSV41_PFD_ENTRY(K2_, false, for (auto& v : o) v = -1, false) +#define DSV41_PFD_COMMA , diff --git a/zig/kernels/cuda/deepseek_v41/pfdense_args.h b/zig/kernels/cuda/deepseek_v41/pfdense_args.h new file mode 100644 index 0000000000..7da47f2572 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/pfdense_args.h @@ -0,0 +1,61 @@ +// Copy of src/tensorfold/families/deepseek_v41/cuda/pfdense_args.h (blob f4e1d1bed4e5): sass_check.sh + + + +// pfdense (TF_DSV41_PF_DENSE=fused) launch arguments and config list, shared by kernels and host bindings +#pragma once + +#include +#include + +namespace dsv41_pfd { + +enum OutType : int { OUT_F16 = 0, OUT_BF16 = 1, OUT_F32 = 2 }; + +struct Args { + const void* xh; // [M, K] fp16, the rotated input (upstream rot_in), contiguous + const uint32_t* T; // trellis words: tile (kt, nt) at kt * stride_k + (nt / 8) * stride_nb + (nt % 8) * 4 K2 + long long stride_k, stride_nb; + const void* svh; // fp16 [N] + const void* bias; // fp16 [N] or null + void* out; // [M, N] view of OutType, unit column stride, row stride o_stride (elements) + long long o_stride; + int M, K, N, group, out_type; +}; + +// X(id, BM, WM, WN, KS, NST); warps = WM * WN, warp tile (BM / WM) x (128 / WN); 6 = narrow layers' row tiles +#define DSV41_PFD_CFGS(X) \ + X(0, 128, 2, 4, 2, 3) \ + X(1, 128, 2, 4, 2, 4) \ + X(2, 128, 4, 2, 2, 3) \ + X(3, 64, 2, 2, 2, 4) \ + X(4, 64, 1, 4, 2, 4) \ + X(5, 128, 2, 4, 2, 2) \ + X(6, 32, 1, 4, 2, 4) \ + X(7, 256, 4, 2, 2, 3) +#define DSV41_PFD_NCFG 8 + +// (BM, WM, WN, KS, NST) of configuration id, or all zeros +inline void cfg_of(int id, int (&o)[5]) { + for (auto& v : o) v = 0; +#define DSV41_PFD_CFG_OF(ID, BM, WM, WN, KS, NST) \ + if (id == ID) { o[0] = BM; o[1] = WM; o[2] = WN; o[3] = KS; o[4] = NST; } + DSV41_PFD_CFGS(DSV41_PFD_CFG_OF) +#undef DSV41_PFD_CFG_OF +} + +// the per-width entry points (pfdense_k.cu); lanes is built for K2 8, 10, 12 +bool run_k8(const Args&, bool lanes, int cfg, cudaStream_t); +bool run_k10(const Args&, bool lanes, int cfg, cudaStream_t); +bool run_k12(const Args&, bool lanes, int cfg, cudaStream_t); +bool run_k16(const Args&, bool lanes, int cfg, cudaStream_t); +void describe_k8(bool lanes, int cfg, long long (&o)[8]); +void describe_k10(bool lanes, int cfg, long long (&o)[8]); +void describe_k12(bool lanes, int cfg, long long (&o)[8]); +void describe_k16(bool lanes, int cfg, long long (&o)[8]); +bool dequant_k8(bool lanes, const uint32_t*, long long, long long, void*, int, int, cudaStream_t); +bool dequant_k10(bool lanes, const uint32_t*, long long, long long, void*, int, int, cudaStream_t); +bool dequant_k12(bool lanes, const uint32_t*, long long, long long, void*, int, int, cudaStream_t); +bool dequant_k16(bool lanes, const uint32_t*, long long, long long, void*, int, int, cudaStream_t); + +} // namespace dsv41_pfd diff --git a/zig/kernels/cuda/deepseek_v41/pfdense_k10.cu b/zig/kernels/cuda/deepseek_v41/pfdense_k10.cu new file mode 100644 index 0000000000..f343cde298 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/pfdense_k10.cu @@ -0,0 +1,17 @@ +// Copy of src/tensorfold/families/deepseek_v41/cuda/pfdense_k10.cu (blob e45d146864d6): sass_check.sh + + + +// pfdense (TF_DSV41_PF_DENSE=fused) at K2 = 10 (5 bits): strips and lanes, every config of pfdense_args.h +#include "pfdense.cuh" + +#define DSV41_PFD_K2 10 +#define DSV41_PFD_LN false +DSV41_PFD_CFGS(DSV41_PFD_INST) +template __global__ void dsv41_pfd::pfd_dequant_kernel<10, false>(const uint32_t*, long long, long long, half*, int); +#undef DSV41_PFD_LN +#define DSV41_PFD_LN true +DSV41_PFD_CFGS(DSV41_PFD_INST) +template __global__ void dsv41_pfd::pfd_dequant_kernel<10, true>(const uint32_t*, long long, long long, half*, int); + +DSV41_PFD_WIDTH(10) diff --git a/zig/kernels/cuda/deepseek_v41/pfdense_k12.cu b/zig/kernels/cuda/deepseek_v41/pfdense_k12.cu new file mode 100644 index 0000000000..f429844f73 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/pfdense_k12.cu @@ -0,0 +1,17 @@ +// Copy of src/tensorfold/families/deepseek_v41/cuda/pfdense_k12.cu (blob 5905afbd762d): sass_check.sh + + + +// pfdense (TF_DSV41_PF_DENSE=fused) at K2 = 12 (6 bits): strips and lanes, every config of pfdense_args.h +#include "pfdense.cuh" + +#define DSV41_PFD_K2 12 +#define DSV41_PFD_LN false +DSV41_PFD_CFGS(DSV41_PFD_INST) +template __global__ void dsv41_pfd::pfd_dequant_kernel<12, false>(const uint32_t*, long long, long long, half*, int); +#undef DSV41_PFD_LN +#define DSV41_PFD_LN true +DSV41_PFD_CFGS(DSV41_PFD_INST) +template __global__ void dsv41_pfd::pfd_dequant_kernel<12, true>(const uint32_t*, long long, long long, half*, int); + +DSV41_PFD_WIDTH(12) diff --git a/zig/kernels/cuda/deepseek_v41/pfdense_k16.cu b/zig/kernels/cuda/deepseek_v41/pfdense_k16.cu new file mode 100644 index 0000000000..da8c9c588a --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/pfdense_k16.cu @@ -0,0 +1,14 @@ +// Copy of src/tensorfold/families/deepseek_v41/cuda/pfdense_k16.cu (blob b474eefeeb26): sass_check.sh + + + +// pfdense (TF_DSV41_PF_DENSE=fused) at K2 = 16 (8 bits): strips only (the indexer's wk), every config +#include "pfdense.cuh" + +#define DSV41_PFD_K2 16 +#define DSV41_PFD_LN false +DSV41_PFD_CFGS(DSV41_PFD_INST) +template __global__ void dsv41_pfd::pfd_dequant_kernel<16, false>(const uint32_t*, long long, long long, half*, int); + + +DSV41_PFD_WIDTH_STRIPS(16) diff --git a/zig/kernels/cuda/deepseek_v41/pfdense_k8.cu b/zig/kernels/cuda/deepseek_v41/pfdense_k8.cu new file mode 100644 index 0000000000..1c8bbdbe82 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/pfdense_k8.cu @@ -0,0 +1,17 @@ +// Copy of src/tensorfold/families/deepseek_v41/cuda/pfdense_k8.cu (blob f99f10d92688): sass_check.sh + + + +// pfdense (TF_DSV41_PF_DENSE=fused) at K2 = 8 (4 bits): strips and lanes, every config of pfdense_args.h +#include "pfdense.cuh" + +#define DSV41_PFD_K2 8 +#define DSV41_PFD_LN false +DSV41_PFD_CFGS(DSV41_PFD_INST) +template __global__ void dsv41_pfd::pfd_dequant_kernel<8, false>(const uint32_t*, long long, long long, half*, int); +#undef DSV41_PFD_LN +#define DSV41_PFD_LN true +DSV41_PFD_CFGS(DSV41_PFD_INST) +template __global__ void dsv41_pfd::pfd_dequant_kernel<8, true>(const uint32_t*, long long, long long, half*, int); + +DSV41_PFD_WIDTH(8) diff --git a/zig/kernels/cuda/deepseek_v41/pointwise.cu b/zig/kernels/cuda/deepseek_v41/pointwise.cu new file mode 100644 index 0000000000..eefb387097 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/pointwise.cu @@ -0,0 +1,40 @@ +// Torch pointwise ops of DeepSeek-V4.1's fast prefill MoE, bit-exact: fp32 opmath, NaN-propagating clamps. + +// swiglu: act = silu(min(g, limit)) * clamp(u, -limit, limit); add: out += b; add_bf16: y = bf16_rn(a + b). + +// Built with --fmad=false: torch runs each op as its own kernel with fp32 rounding, so nothing may contract. + +#include +#include + +namespace dsv41_pointwise { + +// torch's clamp_max / clamp: a NaN stays NaN +__device__ __forceinline__ float clamp_max(float v, float hi) { return isnan(v) ? v : fminf(v, hi); } +__device__ __forceinline__ float clamp(float v, float lo, float hi) { return isnan(v) ? v : fminf(fmaxf(v, lo), hi); } +// torch's silu (ActivationSiluKernel.cu): x / (1 + exp(-x)) in fp32 +__device__ __forceinline__ float silu(float x) { return x / (1.0f + expf(-x)); } + +// g [R, ldg], u [R, ldu], act [R, lda]; n columns a row +__global__ void __launch_bounds__(256) swiglu_kernel(const float* __restrict__ g, long long ldg, + const float* __restrict__ u, long long ldu, + float* __restrict__ act, long long lda, int n, float limit) { + const int r = blockIdx.y; + for (int c = blockIdx.x * blockDim.x + threadIdx.x; c < n; c += gridDim.x * blockDim.x) { + const float a = silu(clamp_max(g[r * ldg + c], limit)); + act[r * lda + c] = a * clamp(u[r * ldu + c], -limit, limit); + } +} + +__global__ void __launch_bounds__(256) add_kernel(float* __restrict__ out, const float* __restrict__ b, long long n) { + for (long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x; i < n; i += (long long)gridDim.x * blockDim.x) + out[i] = out[i] + b[i]; +} + +__global__ void __launch_bounds__(256) add_bf16_kernel(const float* __restrict__ a, const float* __restrict__ b, + __nv_bfloat16* __restrict__ y, long long n) { + for (long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x; i < n; i += (long long)gridDim.x * blockDim.x) + y[i] = __float2bfloat16_rn(a[i] + b[i]); +} + +} // namespace dsv41_pointwise diff --git a/zig/kernels/cuda/deepseek_v41/prefill_glue.cu b/zig/kernels/cuda/deepseek_v41/prefill_glue.cu new file mode 100644 index 0000000000..4ad758d8cd --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/prefill_glue.cu @@ -0,0 +1,175 @@ +// Prefill segment glue: the torch ops between a segment's kernels, element-exact (integers and casts only). + +// top_positions, cand_keys, counts, gm_picks, width_mask, f64_bf16_f32, widen_cat, ring_copy: see each kernel. + +// Built with --fmad=false (nothing here multiplies and adds). + +#include +#include +#include + +namespace dsv41_pfglue { + +constexpr long long KEY_NONE = (long long)(1ull << 63); // -(2 ** 63) +constexpr int NT = 1024, CMAX = 2048; + +__device__ __forceinline__ unsigned long long ukey(long long k) { // signed order as unsigned + return static_cast(k) ^ 0x8000000000000000ull; +} + +// One CTA a row: 8-pass radix select of the c-th largest key (c = min(count, n)), bitonic sort ascending, -1 padded. +__global__ void __launch_bounds__(NT) top_positions_kernel(const long long* __restrict__ keys, long long ld, int n, + int count, int* __restrict__ out) { + __shared__ int hist[256]; + __shared__ unsigned long long s_prefix, s_mask; + __shared__ int s_need, s_n, s_eq; + __shared__ int pos_sh[CMAX]; + const int tid = threadIdx.x; + const long long* row = keys + (long long)blockIdx.x * ld; + const int c = n < count ? n : count; + if (tid == 0) { s_prefix = 0; s_mask = 0; s_need = c; } + __syncthreads(); + for (int shift = 56; shift >= 0 && c > 0; shift -= 8) { + if (tid < 256) hist[tid] = 0; + __syncthreads(); + const unsigned long long prefix = s_prefix, mask = s_mask; + for (int i = tid; i < n; i += NT) { + const unsigned long long u = ukey(row[i]); + if ((u & mask) == prefix) atomicAdd(&hist[(u >> shift) & 255], 1); + } + __syncthreads(); + if (tid == 0) { + int need = s_need, d = 255; + for (; d > 0 && hist[d] < need; --d) need -= hist[d]; + s_need = need; // how many keys equal to the c-th are taken + s_prefix = prefix | ((unsigned long long)d << shift); + s_mask = mask | (255ull << shift); + } + __syncthreads(); + } + const unsigned long long kth = s_prefix; + if (tid == 0) { s_n = 0; s_eq = 0; } + __syncthreads(); + // winners: every key above the c-th, and `need` keys equal to it (equal keys are one value: any of them) + for (int i = tid; i < n && c > 0; i += NT) { + const long long k = row[i]; + const unsigned long long u = ukey(k); + bool take = u > kth; + if (!take && u == kth) take = atomicAdd(&s_eq, 1) < s_need; + if (take) { + long long p; + if (k == KEY_NONE) p = 0x7FFFFFFF; // -1 after the sort + else { + p = 0x7FFFFFFFll - (k & 0xFFFFFFFFll); + if (p < 0) p = 0x7FFFFFFF; + } + pos_sh[atomicAdd(&s_n, 1)] = (int)p; + } + } + __syncthreads(); + int m = 1; + while (m < c) m <<= 1; + for (int i = c + tid; i < m; i += NT) pos_sh[i] = 0x7FFFFFFF; + __syncthreads(); + for (int size = 2; size <= m; size <<= 1) { + for (int stride = size >> 1; stride > 0; stride >>= 1) { + for (int i = tid; i < m; i += NT) { + const int j = i ^ stride; + if (j > i) { + const bool asc = (i & size) == 0; + const int a = pos_sh[i], b = pos_sh[j]; + if (asc ? a > b : a < b) { pos_sh[i] = b; pos_sh[j] = a; } + } + } + __syncthreads(); + } + } + int* o = out + (long long)blockIdx.x * count; + for (int i = tid; i < count; i += NT) { + const int p = i < c ? pos_sh[i] : 0x7FFFFFFF; + o[i] = p == 0x7FFFFFFF ? -1 : p; + } +} + +// blocks [R, nb] (row stride ld) -> out [R, nb * bs] +__global__ void __launch_bounds__(256) cand_keys_kernel(const int* __restrict__ blocks, long long ld, int R, int nb, + int bs, int* __restrict__ out) { + const long long total = (long long)R * nb * bs; + for (long long e = (long long)blockIdx.x * blockDim.x + threadIdx.x; e < total; e += (long long)gridDim.x * blockDim.x) { + const long long r = e / ((long long)nb * bs), q = e - r * nb * bs, b = q / bs, t = q - b * bs; + const int blk = blocks[r * ld + b]; + out[e] = blk >= 0 ? (int)((long long)blk * bs + t) : -1; + } +} + +// one warp a row: sel [R, K] int32, pos int64 [R] +__global__ void __launch_bounds__(256) counts_kernel(const int* __restrict__ sel, int K, const long long* __restrict__ pos, + int ratio, int R, int* __restrict__ out) { + const int r = blockIdx.x * 8 + (threadIdx.x >> 5), lane = threadIdx.x & 31; + if (r >= R) return; + // positions in [0, 2^31 - 1) and ratio > 0: C's truncating division equals floor division + const long long nvis = (long long)((int)(pos[r] + 1) / ratio); + int c = 0; + for (int j = lane; j < K; j += 32) { + const int s = sel[(long long)r * K + j]; + c += (s >= 0 && (long long)s < nvis) ? 1 : 0; + } + for (int o = 16; o > 0; o >>= 1) c += __shfl_xor_sync(0xffffffffu, c, o); + if (lane == 0) out[r] = c; +} + +// pick / wts [n, ld] -> pk int32 [n, topk], w6 fp32 [n, topk] (the kit's fp16 weights) +__global__ void __launch_bounds__(256) gm_picks_kernel(const int* __restrict__ pick, const float* __restrict__ wts, int ld, + int topk, int n, int* __restrict__ pk, float* __restrict__ w6) { + const long long total = (long long)n * topk; + for (long long e = (long long)blockIdx.x * blockDim.x + threadIdx.x; e < total; e += (long long)gridDim.x * blockDim.x) { + const long long i = e / topk, j = e - i * topk; + pk[e] = pick[i * ld + j]; + w6[e] = __half2float(__float2half(wts[i * ld + j])); + } +} + +__global__ void __launch_bounds__(256) width_mask_kernel(const int* __restrict__ pick, long long P, + const int* __restrict__ k2tab, int E, int k2, + int* __restrict__ out) { + for (long long e = (long long)blockIdx.x * blockDim.x + threadIdx.x; e < P; e += (long long)gridDim.x * blockDim.x) { + const int v = pick[e]; + const int w = (v >= 0 && v < E) ? k2tab[v] : 0; + out[e] = w == k2 ? v : E; + } +} + +__global__ void __launch_bounds__(256) f64_bf16_f32_kernel(const double* __restrict__ x, float* __restrict__ y, long long n) { + for (long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x; i < n; i += (long long)gridDim.x * blockDim.x) + y[i] = __bfloat162float(__float2bfloat16(__double2float_rn(x[i]))); +} + +// a [n, hd] (and b [n, hd]) bf16 -> out [n, hd * (b ? 2 : 1)] fp32 +__global__ void __launch_bounds__(256) widen_cat_kernel(const uint16_t* __restrict__ a, const uint16_t* __restrict__ b, + int n, int hd, float* __restrict__ out) { + const int w = b ? 2 * hd : hd; + const long long total = (long long)n * w; + for (long long e = (long long)blockIdx.x * blockDim.x + threadIdx.x; e < total; e += (long long)gridDim.x * blockDim.x) { + const long long i = e / w, j = e - i * w; + const uint16_t v = j < hd ? a[i * hd + j] : b[i * hd + (j - hd)]; + out[e] = __uint_as_float(static_cast(v) << 16); + } +} + +// rows p in [lo, hi) of a src_ring ring into a dst_ring ring, row_bytes each (4-byte units when all 4-byte aligned) +__global__ void __launch_bounds__(256) ring_copy_kernel(const unsigned char* __restrict__ src, int src_ring, + unsigned char* __restrict__ dst, int dst_ring, long long row_bytes, + long long lo, long long hi) { + const bool words = (row_bytes % 4 == 0) && (((uintptr_t)src | (uintptr_t)dst) % 4 == 0); + const long long unit = words ? 4 : 1, per = row_bytes / unit, total = (hi - lo) * per; + for (long long e = (long long)blockIdx.x * blockDim.x + threadIdx.x; e < total; e += (long long)gridDim.x * blockDim.x) { + const long long p = lo + e / per, k = e - (e / per) * per; + const long long s = p % src_ring, d = p % dst_ring; + if (words) + reinterpret_cast(dst + d * row_bytes)[k] = reinterpret_cast(src + s * row_bytes)[k]; + else + dst[d * row_bytes + k] = src[s * row_bytes + k]; + } +} + +} // namespace dsv41_pfglue diff --git a/zig/kernels/cuda/deepseek_v41/proto/attn_fastmerge.cuh b/zig/kernels/cuda/deepseek_v41/proto/attn_fastmerge.cuh new file mode 100644 index 0000000000..7409ec1763 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/proto/attn_fastmerge.cuh @@ -0,0 +1,499 @@ +// attn_cuda.cu with a latency-hiding merge (all chunk loads issued before the chains); same bits as attn_cuda.cu. +#ifndef DSV41_ATTN_NO_TORCH +#define DSV41_ATTN_NO_TORCH +#endif +// Copy of src/tensorfold/families/deepseek_v41/cuda/csa2/attn_cuda.cu (blob 0f3234101360): sass_check.sh + + + +// CSA2 decode attention core in ONE launch: CTA (chunk of 5, 16-head tile, row), 8 warps, ops = Triton's sm_121 PTX +#include +#include +#include +#include + +namespace dsv41_attn_fm { + +constexpr int D = 512, CH = 128, NCOMP = 4, NCH = 5, BMQ = 16, WINDOW = 128; // tiles of 32 keys +constexpr int NOPE = 448, VB = 576, SB = 8, NT = 256; +constexpr int SM_Q = 0; // q A fragments [32 steps][32 lanes] uint4 16384 +constexpr int SM_KV = 16384; // 128 rows x 576 B, 8-byte pieces swizzled by key & 7 73728 +constexpr int SM_SC = SM_KV + CH * VB; // 128 x 8 scale bytes 1024 +constexpr int SM_P = SM_SC + CH * SB; // P A fragments [4 tiles][2 steps][32 lanes] uint4 4096 +constexpr int SM_TMAX = SM_P + 4096; // [4 tiles][4 n8][16 rows] fp32 1024 +constexpr int SM_WSUM = SM_TMAX + 1024; // same shape: the warps' row sums 1024 +constexpr int SM_ROW = SM_WSUM + 1024; // [4 tiles][16 rows] nm, alpha, active 768 +constexpr int SM_PHYS = SM_ROW + 768; // 128 physical rows (int32, -1: masked) 512 +constexpr int SM_MISC = SM_PHYS + 512; // final m, l [16] each + the ticket flag 256 +constexpr int SMEM = SM_MISC + 256; // 98,816 B (sm_121: <= 101,376 a block) +static_assert(SMEM <= 101376, "shared memory"); +constexpr unsigned FULL = 0xffffffffu; + +struct Args { + const uint16_t* q; // [R, H, 512] bf16 (RoPE'd) + const uint8_t* cv; const uint8_t* csc; long long cvs, css; // compressed rows (values, scales), row strides + const int* tok; long long ts; const int* cnt; // selection [R, ts] int32 (-1 pad), counts [R] + const uint8_t* sv; const uint8_t* ssc; long long svs, sss; // SWA rings + const int* lo; const int* hi; // [R] window starts; window ends (HAS_HI) or null + const void* pos; // int32 [1] (POS + r) or int64 [R] (ROWS) + const long long* sl; // ROWS: the rows' slots [R] + const int* pt; long long pts; int psh; // page table of the compressed rows (psh 0: contiguous) + const float* sink; const float* cs; long long csst; // [H] fp32, RoPE table fp32 [max_pos, 64] + float* po; float* pm; float* pl; // partials (attn.Scratch layout) + uint16_t* out; // [R, H, 512] bf16 + int* ticket; // [R, H / 16] (self-cleaning) + int R, H, ring, has_comp; +}; + +__device__ __forceinline__ float ex2(float x) { + float y; + asm("ex2.approx.f32 %0, %1;" : "=f"(y) : "f"(x)); + return y; +} +__device__ __forceinline__ float div_full(float a, float b) { + float y; + asm("div.full.f32 %0, %1, %2;" : "=f"(y) : "f"(a), "f"(b)); + return y; +} +__device__ __forceinline__ float texp(float x) { return ex2(__fmul_rn(x, __int_as_float(0x3FB8AA3B))); } +__device__ __forceinline__ float pow2b(uint32_t s) { return __int_as_float(static_cast(s) << 23); } + +__device__ __forceinline__ void mma(float (&d)[4], const uint4& a, uint32_t b0, uint32_t b1) { + asm volatile("mma.sync.aligned.m16n8k16.row.col.f32.bf16.bf16.f32 {%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, " + "{%0,%1,%2,%3};" + : "+f"(d[0]), "+f"(d[1]), "+f"(d[2]), "+f"(d[3]) + : "r"(a.x), "r"(a.y), "r"(a.z), "r"(a.w), "r"(b0), "r"(b1)); +} + +// two e4m3 bytes (low byte = the lower k) with their own scales -> bf16x2 (exact: e4m3 x 2^e is a bf16) +__device__ __forceinline__ uint32_t deq2(uint32_t two, float s0, float s1) { + uint32_t h2; + asm("cvt.rn.f16x2.e4m3x2 %0, %1;" : "=r"(h2) : "h"(static_cast(two))); + const __half2 h = *reinterpret_cast(&h2); + const __nv_bfloat162 b = __floats2bfloat162_rn(__fmul_rn(__low2float(h), s0), __fmul_rn(__high2float(h), s1)); + return *reinterpret_cast(&b); +} + +// a byte offset x of row `key` in the staged rows: 8-byte pieces swizzled within groups of 8 (bank-conflict free) +__device__ __forceinline__ int swz(int key, int x) { + const int ch = x >> 3; + return key * VB + ((((ch & ~7) | ((ch & 7) ^ (key & 7)))) << 3) + (x & 7); +} + +// Triton's dot-operand k order: step s (16 k) of a 32-k block takes k = klo(s), klo + 1 and khi(s), khi + 1 +template __device__ __forceinline__ int klo(int s, int t) { + return KW == 4 ? 32 * (s >> 1) + 4 * t + 2 * (s & 1) : 16 * s + 2 * t; +} +template __device__ __forceinline__ int khi(int s, int t) { return klo(s, t) + (KW == 4 ? 16 : 8); } +// where element k (< 32) of a row sits in a 2-step A fragment: step, quad lane, high half (+16 / +8) +template __device__ __forceinline__ void frag_of(int k, int& step, int& tq, int& hi) { + if (KW == 4) { step = (k >> 1) & 1; hi = (k >> 4) & 1; tq = (k & 15) >> 2; } + else { step = k >> 4; hi = (k >> 3) & 1; tq = (k & 7) >> 1; } +} + +// the bf16 pair (k, k + 1) of staged row `key` (S's B operand: k along the row) +__device__ __forceinline__ uint32_t pair_k(const uint8_t* sm, int key, int k) { + if (k < NOPE) { + const uint32_t two = *reinterpret_cast(sm + SM_KV + swz(key, k)); + const float s = pow2b(sm[SM_SC + key * SB + (k >> 6)]); + return deq2(two, s, s); + } + return *reinterpret_cast(sm + SM_KV + swz(key, NOPE + 2 * (k - NOPE))); +} + +// value (key, n) of the staged rows as a bf16 bit pattern / its e4m3 byte (PV's B operand: k along the keys) +__device__ __forceinline__ uint32_t pair_n(const uint8_t* sm, int k0, int k1, int n) { + if (n < NOPE) { + const uint32_t two = sm[SM_KV + swz(k0, n)] | (static_cast(sm[SM_KV + swz(k1, n)]) << 8); + return deq2(two, pow2b(sm[SM_SC + k0 * SB + (n >> 6)]), pow2b(sm[SM_SC + k1 * SB + (n >> 6)])); + } + const int x = NOPE + 2 * (n - NOPE); + return *reinterpret_cast(sm + SM_KV + swz(k0, x)) | + (static_cast(*reinterpret_cast(sm + SM_KV + swz(k1, x))) << 16); +} + +__device__ __forceinline__ void cp8(uint32_t dst, const void* src, bool ok) { + asm volatile("cp.async.ca.shared.global [%0], [%1], 8, %2;" :: "r"(dst), "l"(src), "r"(ok ? 8 : 0)); +} + +template __device__ __forceinline__ int row_pos(const Args& a, int r) { + return ROWS ? static_cast(static_cast(a.pos)[r]) : static_cast(a.pos)[0] + r; +} + +// how many chunks of row r have keys (the merge's ticket target; the others are Triton's empty partial) +__device__ __forceinline__ bool chunk_works(const Args& a, int r, int c) { + return c == NCOMP || (a.has_comp && c * CH < a.cnt[r]); +} + +template +__device__ void merge(const Args& a, uint8_t* sm, int r, int hb) { + const int tid = threadIdx.x, H = a.H, R = a.R; + float* ca = reinterpret_cast(sm + SM_TMAX); // [5][16] a, [5][16] b, [16] af, [16] den + float* cb = ca + NCH * BMQ; + float* fa = cb + NCH * BMQ; + float* fd = fa + BMQ; + if (tid < BMQ) { // a head's scalar chain (the merge, unrolled) + const int h = hb * BMQ + tid; + float m = -INFINITY, l = 0.f; + float pmv[NCH], plv[NCH]; // every chunk's (m, l) in flight at once +#pragma unroll + for (int c = 0; c < NCH; ++c) { + const bool w = chunk_works(a, r, c); + const long long base = (static_cast(c) * R + r) * H + h; + pmv[c] = w ? __ldcg(a.pm + base) : -INFINITY; + plv[c] = w ? __ldcg(a.pl + base) : 0.f; + } +#pragma unroll + for (int c = 0; c < NCH; ++c) { + const float cm = pmv[c], cl = plv[c]; + const bool act = cl > 0.f; + float aa, bb; + if (c == 0) { + const float nm = act ? fmaxf(cm, -INFINITY) : -INFINITY; + bb = act ? texp(__fsub_rn(cm, nm)) : 0.f; + aa = 0.f; + l = __fmaf_rn(bb, cl, 0.f); + m = nm; + } else { + const float nm = act ? fmaxf(m, cm) : m; + aa = act ? (m == -INFINITY ? 0.f : texp(__fsub_rn(m, nm))) : 1.f; + bb = act ? texp(__fsub_rn(cm, nm)) : 0.f; + l = __fmaf_rn(l, aa, __fmul_rn(bb, cl)); + m = nm; + } + ca[c * BMQ + tid] = aa; + cb[c * BMQ + tid] = bb; + } + const float sink = a.sink[h]; + const float top = fmaxf(m, sink); + const float af = m == -INFINITY ? 0.f : texp(__fsub_rn(m, top)); + const float e = sink == -INFINITY ? 0.f : texp(__fsub_rn(sink, top)); + fa[tid] = af; + fd[tid] = __fmaf_rn(l, af, e); + } + __syncthreads(); + const int at = row_pos(a, r); + const float* csr = a.cs + static_cast(at) * a.csst; + constexpr int UB = BMQ * (D / 4) / NT; // 8 units a thread, every load issued first + bool cw[NCH]; +#pragma unroll + for (int c = 0; c < NCH; ++c) cw[c] = chunk_works(a, r, c); + float4 pv[UB][NCH]; +#pragma unroll + for (int ub = 0; ub < UB; ++ub) { + const int u = tid + ub * NT, hl = u / (D / 4), d = (u % (D / 4)) * 4, h = hb * BMQ + hl; +#pragma unroll + for (int c = 0; c < NCH; ++c) + pv[ub][c] = cw[c] ? __ldcg(reinterpret_cast(a.po + ((static_cast(c) * R + r) * H + h) * D + d)) + : make_float4(0.f, 0.f, 0.f, 0.f); + } +#pragma unroll + for (int ub = 0; ub < UB; ++ub) { // float4 units: head hl, columns d .. d + 3 + const int u = tid + ub * NT, hl = u / (D / 4), d = (u % (D / 4)) * 4, h = hb * BMQ + hl; + float o[4]; +#pragma unroll + for (int c = 0; c < NCH; ++c) { + const float4 v = pv[ub][c]; + const float co[4] = {v.x, v.y, v.z, v.w}; + const float aa = ca[c * BMQ + hl], bb = cb[c * BMQ + hl]; + for (int i = 0; i < 4; ++i) + o[i] = c == 0 ? __fmaf_rn(bb, co[i], 0.f) : __fmaf_rn(o[i], aa, __fmul_rn(co[i], bb)); + } + float x[4]; + for (int i = 0; i < 4; ++i) x[i] = div_full(__fmul_rn(o[i], fa[hl]), fd[hl]); + uint16_t ob[4]; + for (int i = 0; i < 4; i += 2) { // pair (d + i, d + i + 1): GPT-J, inverse + const int p = (d + i) >> 1; + const float c = p >= 224 ? csr[p - 224] : 1.f, s = p >= 224 ? csr[32 + p - 224] : 0.f; + const float ne = __fmaf_rn(x[i], c, __fmul_rn(x[i + 1], s)); + const float no = __fmaf_rn(x[i + 1], c, -__fmul_rn(x[i], s)); + const __nv_bfloat16 b0 = __float2bfloat16_rn(ne), b1 = __float2bfloat16_rn(no); + ob[i] = *reinterpret_cast(&b0); + ob[i + 1] = *reinterpret_cast(&b1); + } + uint2 w; + w.x = ob[0] | (static_cast(ob[1]) << 16); + w.y = ob[2] | (static_cast(ob[3]) << 16); + *reinterpret_cast(a.out + (static_cast(r) * H + h) * D + d) = w; + } +} + +template +__global__ void __launch_bounds__(NT, 1) attn_kernel(Args a) { + extern __shared__ __align__(16) uint8_t sm[]; + const int c = blockIdx.x, hb = blockIdx.y, r = blockIdx.z; + const int tid = threadIdx.x, lane = tid & 31, warp = tid >> 5, g = lane >> 2, t = lane & 3; + const int H = a.H, R = a.R; + if (!chunk_works(a, r, c)) return; + int* phys = reinterpret_cast(sm + SM_PHYS); + const long long sl = ROWS ? a.sl[r] : 0; + if (tid < CH) { // the chunk's 128 keys -> physical rows (-1: masked) + int row = -1; + if (c < NCOMP) { + const int idx = c * CH + tid; + if (idx < a.cnt[r]) { + const int tk = a.tok[static_cast(r) * a.ts + idx]; + if (tk >= 0) { + row = tk; + if (a.psh > 0) { + const int* ptr = a.pt + (ROWS ? sl * a.pts : 0); + row = (ptr[tk >> a.psh] << a.psh) + (tk & ((1 << a.psh) - 1)); + } + } + } + } else { + const int hi = HAS_HI ? a.hi[r] : row_pos(a, r); + const int lo = max(a.lo[r], hi - (WINDOW - 1)); + const int p = hi - (WINDOW - 1) + tid; + if (p >= lo && p >= 0) row = (p % a.ring) + (ROWS ? static_cast(sl) * a.ring : 0); + } + phys[tid] = row; + } + { // q -> A fragments (Triton's k order) + const uint16_t* qb = a.q + (static_cast(r) * H + hb * BMQ) * D; + for (int i = tid; i < 32 * 32; i += NT) { + const int s = i >> 5, ln = i & 31, gg = ln >> 2, tt = ln & 3; + const int k0 = klo(s, tt), k1 = khi(s, tt); + uint4 f; + f.x = *reinterpret_cast(qb + gg * D + k0); + f.y = *reinterpret_cast(qb + (gg + 8) * D + k0); + f.z = *reinterpret_cast(qb + gg * D + k1); + f.w = *reinterpret_cast(qb + (gg + 8) * D + k1); + reinterpret_cast(sm + SM_Q)[i] = f; + } + } + __syncthreads(); + { // the rows: 72 value pieces + 1 scale piece a key + const bool comp = c < NCOMP; + const uint8_t* V = comp ? a.cv : a.sv; + const uint8_t* S = comp ? a.csc : a.ssc; + const long long vs = comp ? a.cvs : a.svs, ss = comp ? a.css : a.sss; + const uint32_t base = static_cast(__cvta_generic_to_shared(sm)); + for (int i = tid; i < CH * 73; i += NT) { + const int key = i / 73, pc = i % 73, row = phys[key]; + const bool ok = row >= 0; + const long long rr = ok ? row : 0; + if (pc < 72) cp8(base + SM_KV + swz(key, pc * 8), V + rr * vs + pc * 8, ok); + else cp8(base + SM_SC + key * SB, S + rr * ss, ok); + } + asm volatile("cp.async.commit_group;\ncp.async.wait_group 0;" ::: "memory"); + } + __syncthreads(); + const float NINF = -INFINITY; + const int n8 = warp & 3, ta = warp >> 2; // this warp: S of tiles ta and ta + 2, block n8 + float sv[2][4] = {{0.f, 0.f, 0.f, 0.f}, {0.f, 0.f, 0.f, 0.f}}; + { + const int kA = 32 * ta + 8 * n8 + g, kB = kA + 64; + const uint4* qf = reinterpret_cast(sm + SM_Q); +#pragma unroll 4 + for (int s = 0; s < 32; ++s) { + const uint4 af = qf[s * 32 + lane]; + const int k0 = klo(s, t), k1 = khi(s, t); + mma(sv[0], af, pair_k(sm, kA, k0), pair_k(sm, kA, k1)); + mma(sv[1], af, pair_k(sm, kB, k0), pair_k(sm, kB, k1)); + } + } + float* tmax = reinterpret_cast(sm + SM_TMAX); + float* wsum = reinterpret_cast(sm + SM_WSUM); + float* rowv = reinterpret_cast(sm + SM_ROW); // [tile][row]: nm, alpha, active at 0 / 64 / 128 + bool val[2][2]; + for (int j = 0; j < 2; ++j) { + const int tile = ta + 2 * j, kc = 32 * tile + 8 * n8 + 2 * t; + val[j][0] = phys[kc] >= 0; + val[j][1] = phys[kc + 1] >= 0; + for (int e = 0; e < 4; ++e) + sv[j][e] = val[j][e & 1] ? __fmul_rn(sv[j][e], __int_as_float(0x3D3504F3)) : NINF; + float m0 = fmaxf(sv[j][0], sv[j][1]), m1 = fmaxf(sv[j][2], sv[j][3]); + m0 = fmaxf(m0, __shfl_xor_sync(FULL, m0, 2)); + m0 = fmaxf(m0, __shfl_xor_sync(FULL, m0, 1)); + m1 = fmaxf(m1, __shfl_xor_sync(FULL, m1, 2)); + m1 = fmaxf(m1, __shfl_xor_sync(FULL, m1, 1)); + if (t == 0) { + tmax[(tile * 4 + n8) * 16 + g] = m0; + tmax[(tile * 4 + n8) * 16 + g + 8] = m1; + } + } + __syncthreads(); + if (tid < BMQ) { // the running max over the tiles, a row a thread + float m = NINF; + for (int tile = 0; tile < 4; ++tile) { + const float* tm4 = tmax + tile * 64 + tid; + const float tm = fmaxf(fmaxf(tm4[0], tm4[16]), fmaxf(tm4[32], tm4[48])); + const bool act = tm != NINF; + const float nm = act ? fmaxf(m, tm) : m; + const float al = act ? (m == NINF ? 0.f : texp(__fsub_rn(m, nm))) : 1.f; + rowv[tile * 16 + tid] = nm; + rowv[64 + tile * 16 + tid] = al; + rowv[128 + tile * 16 + tid] = act ? 1.f : 0.f; + m = nm; + } + reinterpret_cast(sm + SM_MISC)[tid] = m; + } + __syncthreads(); + uint32_t* pf = reinterpret_cast(sm + SM_P); // [tile][step][lane][4] + for (int j = 0; j < 2; ++j) { // p, the thread / quad sums, bf16(p) -> fragments + const int tile = ta + 2 * j; + float p[4]; + for (int e = 0; e < 4; ++e) { + const int row = g + (e >> 1) * 8; + const bool act = rowv[128 + tile * 16 + row] != 0.f; + p[e] = (val[j][e & 1] && act) ? texp(__fsub_rn(sv[j][e], rowv[tile * 16 + row])) : 0.f; + } + float a0 = __fadd_rn(p[0], p[1]), a1 = __fadd_rn(p[2], p[3]); + a0 = __fadd_rn(a0, __shfl_xor_sync(FULL, a0, 2)); + a0 = __fadd_rn(a0, __shfl_xor_sync(FULL, a0, 1)); + a1 = __fadd_rn(a1, __shfl_xor_sync(FULL, a1, 2)); + a1 = __fadd_rn(a1, __shfl_xor_sync(FULL, a1, 1)); + if (t == 0) { + wsum[(tile * 4 + n8) * 16 + g] = a0; + wsum[(tile * 4 + n8) * 16 + g + 8] = a1; + } + int step, tq, hi; + frag_of(8 * n8 + 2 * t, step, tq, hi); + const __nv_bfloat162 lo2 = __floats2bfloat162_rn(p[0], p[1]), hi2 = __floats2bfloat162_rn(p[2], p[3]); + uint32_t* f = pf + ((tile * 2 + step) * 32 + g * 4 + tq) * 4; + f[2 * hi] = *reinterpret_cast(&lo2); // reg 0 / 2: row g; 1 / 3: row g + 8 + f[2 * hi + 1] = *reinterpret_cast(&hi2); + } + __syncthreads(); + if (tid < BMQ) { // l: fma(l, alpha, (W0 + W2) + (W1 + W3)) + float l = 0.f; + for (int tile = 0; tile < 4; ++tile) { + const float* w4 = wsum + tile * 64 + tid; + const float sum = __fadd_rn(__fadd_rn(w4[0], w4[32]), __fadd_rn(w4[16], w4[48])); + l = __fmaf_rn(l, rowv[64 + tile * 16 + tid], sum); + } + reinterpret_cast(sm + SM_MISC)[16 + tid] = l; + } + float acc[8][4]; + for (int j = 0; j < 8; ++j) + for (int e = 0; e < 4; ++e) acc[j][e] = 0.f; + for (int tile = 0; tile < 4; ++tile) { // o = mma(P, kv, rn(o * alpha)), 2 k steps a tile + const float al0 = rowv[64 + tile * 16 + g], al1 = rowv[64 + tile * 16 + g + 8]; + for (int j = 0; j < 8; ++j) { + acc[j][0] = __fmul_rn(acc[j][0], al0); + acc[j][1] = __fmul_rn(acc[j][1], al0); + acc[j][2] = __fmul_rn(acc[j][2], al1); + acc[j][3] = __fmul_rn(acc[j][3], al1); + } + for (int step = 0; step < 2; ++step) { + const uint4 af = reinterpret_cast(pf)[(tile * 2 + step) * 32 + lane]; + const int k0 = 32 * tile + klo(step, t), k1 = 32 * tile + khi(step, t); +#pragma unroll + for (int j = 0; j < 8; ++j) { + const int n = 64 * j + 8 * warp + g; + mma(acc[j], af, pair_n(sm, k0, k0 + 1, n), pair_n(sm, k1, k1 + 1, n)); + } + } + } + const long long pbase = (static_cast(c) * R + r) * H + hb * BMQ; + for (int j = 0; j < 8; ++j) { + const int n = 64 * j + 8 * warp + 2 * t; + *reinterpret_cast(a.po + (pbase + g) * D + n) = make_float2(acc[j][0], acc[j][1]); + *reinterpret_cast(a.po + (pbase + g + 8) * D + n) = make_float2(acc[j][2], acc[j][3]); + } + if (tid < BMQ) { + a.pm[pbase + tid] = reinterpret_cast(sm + SM_MISC)[tid]; + a.pl[pbase + tid] = reinterpret_cast(sm + SM_MISC)[16 + tid]; + } + int* flag = reinterpret_cast(sm + SM_MISC) + 32; + __threadfence(); + __syncthreads(); + if (tid == 0) { + int need = 0; + for (int cc = 0; cc < NCH; ++cc) need += chunk_works(a, r, cc) ? 1 : 0; + *flag = atomicAdd(a.ticket + r * (H / BMQ) + hb, 1) == need - 1; + } + __syncthreads(); + if (!*flag) return; + __threadfence(); + if (tid == 0) atomicExch(a.ticket + r * (H / BMQ) + hb, 0); // self-cleaning: the next launch / replay + merge(a, sm, r, hb); +} + +template +cudaError_t launch_one(const Args& a, cudaStream_t stream) { + static bool init = false; + if (!init) { + const cudaError_t e = cudaFuncSetAttribute(attn_kernel, + cudaFuncAttributeMaxDynamicSharedMemorySize, SMEM); + if (e != cudaSuccess) return e; + init = true; + } + attn_kernel<<>>(a); + return cudaGetLastError(); +} + +inline cudaError_t dispatch(const Args& a, int kw, bool rows, bool has_hi, cudaStream_t stream) { + if (a.R < 1 || a.H % BMQ || a.ring < WINDOW || (a.ring & (a.ring - 1))) return cudaErrorInvalidValue; + switch ((kw == 4 ? 4 : 0) + (rows ? 2 : 0) + (has_hi ? 1 : 0)) { + case 0: return launch_one<2, false, false>(a, stream); + case 1: return launch_one<2, false, true>(a, stream); + case 2: return launch_one<2, true, false>(a, stream); + case 3: return launch_one<2, true, true>(a, stream); + case 4: return launch_one<4, false, false>(a, stream); + case 5: return launch_one<4, false, true>(a, stream); + case 6: return launch_one<4, true, false>(a, stream); + default: return launch_one<4, true, true>(a, stream); + } +} + +} // namespace dsv41_attn_fm + +#ifndef DSV41_ATTN_NO_TORCH // the compile test builds the kernels alone (nvcc, no torch headers) +#include +#include +#include + +namespace { +template T* tp(const at::Tensor& t) { return t.numel() ? static_cast(t.data_ptr()) : nullptr; } +} + +void dsv41_attn_run_cuda(const at::Tensor& q, const at::Tensor& cv, const at::Tensor& csc, const at::Tensor& tok, + const at::Tensor& cnt, const at::Tensor& sv, const at::Tensor& ssc, const at::Tensor& lo, + const at::Tensor& hi, const at::Tensor& pos, const at::Tensor& sl, const at::Tensor& pt, + const at::Tensor& sink, const at::Tensor& cs, at::Tensor& po, at::Tensor& pm, + at::Tensor& pl, at::Tensor& out, at::Tensor& ticket, int64_t ring, int64_t psh, + int64_t pts, int64_t kw) { + using namespace dsv41_attn; + Args a{}; + a.q = tp(q); + a.has_comp = cv.numel() ? 1 : 0; + a.cv = tp(cv); a.csc = tp(csc); + a.cvs = cv.numel() ? cv.stride(0) : 0; a.css = csc.numel() ? csc.stride(0) : 0; + a.tok = tp(tok); a.ts = tok.numel() ? tok.stride(0) : 0; a.cnt = tp(cnt); + a.sv = tp(sv); a.ssc = tp(ssc); a.svs = sv.stride(0); a.sss = ssc.stride(0); + a.lo = tp(lo); a.hi = tp(hi); + a.pos = pos.data_ptr(); + a.sl = tp(sl); + a.pt = tp(pt); a.pts = pts; a.psh = static_cast(psh); + a.sink = tp(sink); a.cs = tp(cs); a.csst = cs.stride(0); + a.po = tp(po); a.pm = tp(pm); a.pl = tp(pl); + a.out = tp(out); a.ticket = tp(ticket); + a.R = static_cast(q.size(0)); a.H = static_cast(q.size(1)); a.ring = static_cast(ring); + C10_CUDA_CHECK(dispatch(a, static_cast(kw), sl.numel() > 0, hi.numel() > 0, + at::cuda::getCurrentCUDAStream())); +} + +std::vector dsv41_attn_info_cuda(int64_t device) { // the device limits of the two launches + int sms = 0, optin = 0, cl = 0, blocks = 0; + cudaDeviceGetAttribute(&sms, cudaDevAttrMultiProcessorCount, static_cast(device)); + cudaDeviceGetAttribute(&optin, cudaDevAttrMaxSharedMemoryPerBlockOptin, static_cast(device)); + cudaDeviceGetAttribute(&cl, cudaDevAttrClusterLaunch, static_cast(device)); + cudaFuncSetAttribute(dsv41_attn_fm::attn_kernel<4, true, false>, cudaFuncAttributeMaxDynamicSharedMemorySize, + dsv41_attn_fm::SMEM); + cudaOccupancyMaxActiveBlocksPerMultiprocessor(&blocks, dsv41_attn_fm::attn_kernel<4, true, false>, dsv41_attn_fm::NT, + dsv41_attn_fm::SMEM); + return {sms, optin, cl, blocks, dsv41_attn_fm::SMEM}; +} +#endif + +// The instantiations dispatch() launches: attn_kernel. +template __global__ void dsv41_attn_fm::attn_kernel<2, false, false>(dsv41_attn_fm::Args); +template __global__ void dsv41_attn_fm::attn_kernel<2, false, true>(dsv41_attn_fm::Args); +template __global__ void dsv41_attn_fm::attn_kernel<2, true, false>(dsv41_attn_fm::Args); +template __global__ void dsv41_attn_fm::attn_kernel<2, true, true>(dsv41_attn_fm::Args); +template __global__ void dsv41_attn_fm::attn_kernel<4, false, false>(dsv41_attn_fm::Args); +template __global__ void dsv41_attn_fm::attn_kernel<4, false, true>(dsv41_attn_fm::Args); +template __global__ void dsv41_attn_fm::attn_kernel<4, true, false>(dsv41_attn_fm::Args); +template __global__ void dsv41_attn_fm::attn_kernel<4, true, true>(dsv41_attn_fm::Args); diff --git a/zig/kernels/cuda/deepseek_v41/proto/attn_phases.cuh b/zig/kernels/cuda/deepseek_v41/proto/attn_phases.cuh new file mode 100644 index 0000000000..c205a8a4d6 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/proto/attn_phases.cuh @@ -0,0 +1,489 @@ +// Timing-only copy of attn_cuda.cu with globaltimer stamps STAMP(0..7) at phase boundaries per CTA. +#ifndef DSV41_ATTN_NO_TORCH +#define DSV41_ATTN_NO_TORCH +#endif +// Copy of src/tensorfold/families/deepseek_v41/cuda/csa2/attn_cuda.cu (blob 0f3234101360): sass_check.sh + + + +// CSA2 decode attention core in ONE launch: CTA (chunk of 5, 16-head tile, row), 8 warps, ops = Triton's sm_121 PTX +#include +#include +#include +#include + +namespace dsv41_attn_ph { +__device__ unsigned long long g_stamp[256][10]; +__device__ __forceinline__ unsigned long long gtime() { unsigned long long t; asm volatile("mov.u64 %0, %%globaltimer;" : "=l"(t)); return t; } +#define STAMP(k) do { __syncthreads(); if (threadIdx.x == 0) g_stamp[(blockIdx.z * gridDim.y + blockIdx.y) * gridDim.x + blockIdx.x][k] = gtime(); } while (0) + +constexpr int D = 512, CH = 128, NCOMP = 4, NCH = 5, BMQ = 16, WINDOW = 128; // tiles of 32 keys +constexpr int NOPE = 448, VB = 576, SB = 8, NT = 256; +constexpr int SM_Q = 0; // q A fragments [32 steps][32 lanes] uint4 16384 +constexpr int SM_KV = 16384; // 128 rows x 576 B, 8-byte pieces swizzled by key & 7 73728 +constexpr int SM_SC = SM_KV + CH * VB; // 128 x 8 scale bytes 1024 +constexpr int SM_P = SM_SC + CH * SB; // P A fragments [4 tiles][2 steps][32 lanes] uint4 4096 +constexpr int SM_TMAX = SM_P + 4096; // [4 tiles][4 n8][16 rows] fp32 1024 +constexpr int SM_WSUM = SM_TMAX + 1024; // same shape: the warps' row sums 1024 +constexpr int SM_ROW = SM_WSUM + 1024; // [4 tiles][16 rows] nm, alpha, active 768 +constexpr int SM_PHYS = SM_ROW + 768; // 128 physical rows (int32, -1: masked) 512 +constexpr int SM_MISC = SM_PHYS + 512; // final m, l [16] each + the ticket flag 256 +constexpr int SMEM = SM_MISC + 256; // 98,816 B (sm_121: <= 101,376 a block) +static_assert(SMEM <= 101376, "shared memory"); +constexpr unsigned FULL = 0xffffffffu; + +struct Args { + const uint16_t* q; // [R, H, 512] bf16 (RoPE'd) + const uint8_t* cv; const uint8_t* csc; long long cvs, css; // compressed rows (values, scales), row strides + const int* tok; long long ts; const int* cnt; // selection [R, ts] int32 (-1 pad), counts [R] + const uint8_t* sv; const uint8_t* ssc; long long svs, sss; // SWA rings + const int* lo; const int* hi; // [R] window starts; window ends (HAS_HI) or null + const void* pos; // int32 [1] (POS + r) or int64 [R] (ROWS) + const long long* sl; // ROWS: the rows' slots [R] + const int* pt; long long pts; int psh; // page table of the compressed rows (psh 0: contiguous) + const float* sink; const float* cs; long long csst; // [H] fp32, RoPE table fp32 [max_pos, 64] + float* po; float* pm; float* pl; // partials (attn.Scratch layout) + uint16_t* out; // [R, H, 512] bf16 + int* ticket; // [R, H / 16] (self-cleaning) + int R, H, ring, has_comp; +}; + +__device__ __forceinline__ float ex2(float x) { + float y; + asm("ex2.approx.f32 %0, %1;" : "=f"(y) : "f"(x)); + return y; +} +__device__ __forceinline__ float div_full(float a, float b) { + float y; + asm("div.full.f32 %0, %1, %2;" : "=f"(y) : "f"(a), "f"(b)); + return y; +} +__device__ __forceinline__ float texp(float x) { return ex2(__fmul_rn(x, __int_as_float(0x3FB8AA3B))); } +__device__ __forceinline__ float pow2b(uint32_t s) { return __int_as_float(static_cast(s) << 23); } + +__device__ __forceinline__ void mma(float (&d)[4], const uint4& a, uint32_t b0, uint32_t b1) { + asm volatile("mma.sync.aligned.m16n8k16.row.col.f32.bf16.bf16.f32 {%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, " + "{%0,%1,%2,%3};" + : "+f"(d[0]), "+f"(d[1]), "+f"(d[2]), "+f"(d[3]) + : "r"(a.x), "r"(a.y), "r"(a.z), "r"(a.w), "r"(b0), "r"(b1)); +} + +// two e4m3 bytes (low byte = the lower k) with their own scales -> bf16x2 (exact: e4m3 x 2^e is a bf16) +__device__ __forceinline__ uint32_t deq2(uint32_t two, float s0, float s1) { + uint32_t h2; + asm("cvt.rn.f16x2.e4m3x2 %0, %1;" : "=r"(h2) : "h"(static_cast(two))); + const __half2 h = *reinterpret_cast(&h2); + const __nv_bfloat162 b = __floats2bfloat162_rn(__fmul_rn(__low2float(h), s0), __fmul_rn(__high2float(h), s1)); + return *reinterpret_cast(&b); +} + +// a byte offset x of row `key` in the staged rows: 8-byte pieces swizzled within groups of 8 (bank-conflict free) +__device__ __forceinline__ int swz(int key, int x) { + const int ch = x >> 3; + return key * VB + ((((ch & ~7) | ((ch & 7) ^ (key & 7)))) << 3) + (x & 7); +} + +// Triton's dot-operand k order: step s (16 k) of a 32-k block takes k = klo(s), klo + 1 and khi(s), khi + 1 +template __device__ __forceinline__ int klo(int s, int t) { + return KW == 4 ? 32 * (s >> 1) + 4 * t + 2 * (s & 1) : 16 * s + 2 * t; +} +template __device__ __forceinline__ int khi(int s, int t) { return klo(s, t) + (KW == 4 ? 16 : 8); } +// where element k (< 32) of a row sits in a 2-step A fragment: step, quad lane, high half (+16 / +8) +template __device__ __forceinline__ void frag_of(int k, int& step, int& tq, int& hi) { + if (KW == 4) { step = (k >> 1) & 1; hi = (k >> 4) & 1; tq = (k & 15) >> 2; } + else { step = k >> 4; hi = (k >> 3) & 1; tq = (k & 7) >> 1; } +} + +// the bf16 pair (k, k + 1) of staged row `key` (S's B operand: k along the row) +__device__ __forceinline__ uint32_t pair_k(const uint8_t* sm, int key, int k) { + if (k < NOPE) { + const uint32_t two = *reinterpret_cast(sm + SM_KV + swz(key, k)); + const float s = pow2b(sm[SM_SC + key * SB + (k >> 6)]); + return deq2(two, s, s); + } + return *reinterpret_cast(sm + SM_KV + swz(key, NOPE + 2 * (k - NOPE))); +} + +// value (key, n) of the staged rows as a bf16 bit pattern / its e4m3 byte (PV's B operand: k along the keys) +__device__ __forceinline__ uint32_t pair_n(const uint8_t* sm, int k0, int k1, int n) { + if (n < NOPE) { + const uint32_t two = sm[SM_KV + swz(k0, n)] | (static_cast(sm[SM_KV + swz(k1, n)]) << 8); + return deq2(two, pow2b(sm[SM_SC + k0 * SB + (n >> 6)]), pow2b(sm[SM_SC + k1 * SB + (n >> 6)])); + } + const int x = NOPE + 2 * (n - NOPE); + return *reinterpret_cast(sm + SM_KV + swz(k0, x)) | + (static_cast(*reinterpret_cast(sm + SM_KV + swz(k1, x))) << 16); +} + +__device__ __forceinline__ void cp8(uint32_t dst, const void* src, bool ok) { + asm volatile("cp.async.ca.shared.global [%0], [%1], 8, %2;" :: "r"(dst), "l"(src), "r"(ok ? 8 : 0)); +} + +template __device__ __forceinline__ int row_pos(const Args& a, int r) { + return ROWS ? static_cast(static_cast(a.pos)[r]) : static_cast(a.pos)[0] + r; +} + +// how many chunks of row r have keys (the merge's ticket target; the others are Triton's empty partial) +__device__ __forceinline__ bool chunk_works(const Args& a, int r, int c) { + return c == NCOMP || (a.has_comp && c * CH < a.cnt[r]); +} + +template +__device__ void merge(const Args& a, uint8_t* sm, int r, int hb) { + const int tid = threadIdx.x, H = a.H, R = a.R; + float* ca = reinterpret_cast(sm + SM_TMAX); // [5][16] a, [5][16] b, [16] af, [16] den + float* cb = ca + NCH * BMQ; + float* fa = cb + NCH * BMQ; + float* fd = fa + BMQ; + if (tid < BMQ) { // a head's scalar chain (the merge, unrolled) + const int h = hb * BMQ + tid; + float m = -INFINITY, l = 0.f; + for (int c = 0; c < NCH; ++c) { + const bool w = chunk_works(a, r, c); + const long long base = (static_cast(c) * R + r) * H + h; + const float cm = w ? __ldcg(a.pm + base) : -INFINITY, cl = w ? __ldcg(a.pl + base) : 0.f; + const bool act = cl > 0.f; + float aa, bb; + if (c == 0) { + const float nm = act ? fmaxf(cm, -INFINITY) : -INFINITY; + bb = act ? texp(__fsub_rn(cm, nm)) : 0.f; + aa = 0.f; + l = __fmaf_rn(bb, cl, 0.f); + m = nm; + } else { + const float nm = act ? fmaxf(m, cm) : m; + aa = act ? (m == -INFINITY ? 0.f : texp(__fsub_rn(m, nm))) : 1.f; + bb = act ? texp(__fsub_rn(cm, nm)) : 0.f; + l = __fmaf_rn(l, aa, __fmul_rn(bb, cl)); + m = nm; + } + ca[c * BMQ + tid] = aa; + cb[c * BMQ + tid] = bb; + } + const float sink = a.sink[h]; + const float top = fmaxf(m, sink); + const float af = m == -INFINITY ? 0.f : texp(__fsub_rn(m, top)); + const float e = sink == -INFINITY ? 0.f : texp(__fsub_rn(sink, top)); + fa[tid] = af; + fd[tid] = __fmaf_rn(l, af, e); + } + __syncthreads(); + const int at = row_pos(a, r); + const float* csr = a.cs + static_cast(at) * a.csst; + for (int u = tid; u < BMQ * (D / 4); u += NT) { // float4 units: head hl, columns d .. d + 3 + const int hl = u / (D / 4), d = (u % (D / 4)) * 4, h = hb * BMQ + hl; + float o[4]; + for (int c = 0; c < NCH; ++c) { + float4 v = make_float4(0.f, 0.f, 0.f, 0.f); + if (chunk_works(a, r, c)) + v = __ldcg(reinterpret_cast(a.po + ((static_cast(c) * R + r) * H + h) * D + + d)); + const float co[4] = {v.x, v.y, v.z, v.w}; + const float aa = ca[c * BMQ + hl], bb = cb[c * BMQ + hl]; + for (int i = 0; i < 4; ++i) + o[i] = c == 0 ? __fmaf_rn(bb, co[i], 0.f) : __fmaf_rn(o[i], aa, __fmul_rn(co[i], bb)); + } + float x[4]; + for (int i = 0; i < 4; ++i) x[i] = div_full(__fmul_rn(o[i], fa[hl]), fd[hl]); + uint16_t ob[4]; + for (int i = 0; i < 4; i += 2) { // pair (d + i, d + i + 1): GPT-J, inverse + const int p = (d + i) >> 1; + const float c = p >= 224 ? csr[p - 224] : 1.f, s = p >= 224 ? csr[32 + p - 224] : 0.f; + const float ne = __fmaf_rn(x[i], c, __fmul_rn(x[i + 1], s)); + const float no = __fmaf_rn(x[i + 1], c, -__fmul_rn(x[i], s)); + const __nv_bfloat16 b0 = __float2bfloat16_rn(ne), b1 = __float2bfloat16_rn(no); + ob[i] = *reinterpret_cast(&b0); + ob[i + 1] = *reinterpret_cast(&b1); + } + uint2 w; + w.x = ob[0] | (static_cast(ob[1]) << 16); + w.y = ob[2] | (static_cast(ob[3]) << 16); + *reinterpret_cast(a.out + (static_cast(r) * H + h) * D + d) = w; + } +} + +template +__global__ void __launch_bounds__(NT, 1) attn_kernel(Args a) { + extern __shared__ __align__(16) uint8_t sm[]; + const int c = blockIdx.x, hb = blockIdx.y, r = blockIdx.z; + const int tid = threadIdx.x, lane = tid & 31, warp = tid >> 5, g = lane >> 2, t = lane & 3; + const int H = a.H, R = a.R; + if (!chunk_works(a, r, c)) return; + STAMP(0); + int* phys = reinterpret_cast(sm + SM_PHYS); + const long long sl = ROWS ? a.sl[r] : 0; + if (tid < CH) { // the chunk's 128 keys -> physical rows (-1: masked) + int row = -1; + if (c < NCOMP) { + const int idx = c * CH + tid; + if (idx < a.cnt[r]) { + const int tk = a.tok[static_cast(r) * a.ts + idx]; + if (tk >= 0) { + row = tk; + if (a.psh > 0) { + const int* ptr = a.pt + (ROWS ? sl * a.pts : 0); + row = (ptr[tk >> a.psh] << a.psh) + (tk & ((1 << a.psh) - 1)); + } + } + } + } else { + const int hi = HAS_HI ? a.hi[r] : row_pos(a, r); + const int lo = max(a.lo[r], hi - (WINDOW - 1)); + const int p = hi - (WINDOW - 1) + tid; + if (p >= lo && p >= 0) row = (p % a.ring) + (ROWS ? static_cast(sl) * a.ring : 0); + } + phys[tid] = row; + } + { // q -> A fragments (Triton's k order) + const uint16_t* qb = a.q + (static_cast(r) * H + hb * BMQ) * D; + for (int i = tid; i < 32 * 32; i += NT) { + const int s = i >> 5, ln = i & 31, gg = ln >> 2, tt = ln & 3; + const int k0 = klo(s, tt), k1 = khi(s, tt); + uint4 f; + f.x = *reinterpret_cast(qb + gg * D + k0); + f.y = *reinterpret_cast(qb + (gg + 8) * D + k0); + f.z = *reinterpret_cast(qb + gg * D + k1); + f.w = *reinterpret_cast(qb + (gg + 8) * D + k1); + reinterpret_cast(sm + SM_Q)[i] = f; + } + } + STAMP(1); + { // the rows: 72 value pieces + 1 scale piece a key + const bool comp = c < NCOMP; + const uint8_t* V = comp ? a.cv : a.sv; + const uint8_t* S = comp ? a.csc : a.ssc; + const long long vs = comp ? a.cvs : a.svs, ss = comp ? a.css : a.sss; + const uint32_t base = static_cast(__cvta_generic_to_shared(sm)); + for (int i = tid; i < CH * 73; i += NT) { + const int key = i / 73, pc = i % 73, row = phys[key]; + const bool ok = row >= 0; + const long long rr = ok ? row : 0; + if (pc < 72) cp8(base + SM_KV + swz(key, pc * 8), V + rr * vs + pc * 8, ok); + else cp8(base + SM_SC + key * SB, S + rr * ss, ok); + } + asm volatile("cp.async.commit_group;\ncp.async.wait_group 0;" ::: "memory"); + } + STAMP(2); + const float NINF = -INFINITY; + const int n8 = warp & 3, ta = warp >> 2; // this warp: S of tiles ta and ta + 2, block n8 + float sv[2][4] = {{0.f, 0.f, 0.f, 0.f}, {0.f, 0.f, 0.f, 0.f}}; + { + const int kA = 32 * ta + 8 * n8 + g, kB = kA + 64; + const uint4* qf = reinterpret_cast(sm + SM_Q); +#pragma unroll 4 + for (int s = 0; s < 32; ++s) { + const uint4 af = qf[s * 32 + lane]; + const int k0 = klo(s, t), k1 = khi(s, t); + mma(sv[0], af, pair_k(sm, kA, k0), pair_k(sm, kA, k1)); + mma(sv[1], af, pair_k(sm, kB, k0), pair_k(sm, kB, k1)); + } + } + STAMP(3); + float* tmax = reinterpret_cast(sm + SM_TMAX); + float* wsum = reinterpret_cast(sm + SM_WSUM); + float* rowv = reinterpret_cast(sm + SM_ROW); // [tile][row]: nm, alpha, active at 0 / 64 / 128 + bool val[2][2]; + for (int j = 0; j < 2; ++j) { + const int tile = ta + 2 * j, kc = 32 * tile + 8 * n8 + 2 * t; + val[j][0] = phys[kc] >= 0; + val[j][1] = phys[kc + 1] >= 0; + for (int e = 0; e < 4; ++e) + sv[j][e] = val[j][e & 1] ? __fmul_rn(sv[j][e], __int_as_float(0x3D3504F3)) : NINF; + float m0 = fmaxf(sv[j][0], sv[j][1]), m1 = fmaxf(sv[j][2], sv[j][3]); + m0 = fmaxf(m0, __shfl_xor_sync(FULL, m0, 2)); + m0 = fmaxf(m0, __shfl_xor_sync(FULL, m0, 1)); + m1 = fmaxf(m1, __shfl_xor_sync(FULL, m1, 2)); + m1 = fmaxf(m1, __shfl_xor_sync(FULL, m1, 1)); + if (t == 0) { + tmax[(tile * 4 + n8) * 16 + g] = m0; + tmax[(tile * 4 + n8) * 16 + g + 8] = m1; + } + } + __syncthreads(); + if (tid < BMQ) { // the running max over the tiles, a row a thread + float m = NINF; + for (int tile = 0; tile < 4; ++tile) { + const float* tm4 = tmax + tile * 64 + tid; + const float tm = fmaxf(fmaxf(tm4[0], tm4[16]), fmaxf(tm4[32], tm4[48])); + const bool act = tm != NINF; + const float nm = act ? fmaxf(m, tm) : m; + const float al = act ? (m == NINF ? 0.f : texp(__fsub_rn(m, nm))) : 1.f; + rowv[tile * 16 + tid] = nm; + rowv[64 + tile * 16 + tid] = al; + rowv[128 + tile * 16 + tid] = act ? 1.f : 0.f; + m = nm; + } + reinterpret_cast(sm + SM_MISC)[tid] = m; + } + __syncthreads(); + uint32_t* pf = reinterpret_cast(sm + SM_P); // [tile][step][lane][4] + for (int j = 0; j < 2; ++j) { // p, the thread / quad sums, bf16(p) -> fragments + const int tile = ta + 2 * j; + float p[4]; + for (int e = 0; e < 4; ++e) { + const int row = g + (e >> 1) * 8; + const bool act = rowv[128 + tile * 16 + row] != 0.f; + p[e] = (val[j][e & 1] && act) ? texp(__fsub_rn(sv[j][e], rowv[tile * 16 + row])) : 0.f; + } + float a0 = __fadd_rn(p[0], p[1]), a1 = __fadd_rn(p[2], p[3]); + a0 = __fadd_rn(a0, __shfl_xor_sync(FULL, a0, 2)); + a0 = __fadd_rn(a0, __shfl_xor_sync(FULL, a0, 1)); + a1 = __fadd_rn(a1, __shfl_xor_sync(FULL, a1, 2)); + a1 = __fadd_rn(a1, __shfl_xor_sync(FULL, a1, 1)); + if (t == 0) { + wsum[(tile * 4 + n8) * 16 + g] = a0; + wsum[(tile * 4 + n8) * 16 + g + 8] = a1; + } + int step, tq, hi; + frag_of(8 * n8 + 2 * t, step, tq, hi); + const __nv_bfloat162 lo2 = __floats2bfloat162_rn(p[0], p[1]), hi2 = __floats2bfloat162_rn(p[2], p[3]); + uint32_t* f = pf + ((tile * 2 + step) * 32 + g * 4 + tq) * 4; + f[2 * hi] = *reinterpret_cast(&lo2); // reg 0 / 2: row g; 1 / 3: row g + 8 + f[2 * hi + 1] = *reinterpret_cast(&hi2); + } + __syncthreads(); + if (tid < BMQ) { // l: fma(l, alpha, (W0 + W2) + (W1 + W3)) + float l = 0.f; + for (int tile = 0; tile < 4; ++tile) { + const float* w4 = wsum + tile * 64 + tid; + const float sum = __fadd_rn(__fadd_rn(w4[0], w4[32]), __fadd_rn(w4[16], w4[48])); + l = __fmaf_rn(l, rowv[64 + tile * 16 + tid], sum); + } + reinterpret_cast(sm + SM_MISC)[16 + tid] = l; + } + STAMP(4); + float acc[8][4]; + for (int j = 0; j < 8; ++j) + for (int e = 0; e < 4; ++e) acc[j][e] = 0.f; + for (int tile = 0; tile < 4; ++tile) { // o = mma(P, kv, rn(o * alpha)), 2 k steps a tile + const float al0 = rowv[64 + tile * 16 + g], al1 = rowv[64 + tile * 16 + g + 8]; + for (int j = 0; j < 8; ++j) { + acc[j][0] = __fmul_rn(acc[j][0], al0); + acc[j][1] = __fmul_rn(acc[j][1], al0); + acc[j][2] = __fmul_rn(acc[j][2], al1); + acc[j][3] = __fmul_rn(acc[j][3], al1); + } + for (int step = 0; step < 2; ++step) { + const uint4 af = reinterpret_cast(pf)[(tile * 2 + step) * 32 + lane]; + const int k0 = 32 * tile + klo(step, t), k1 = 32 * tile + khi(step, t); +#pragma unroll + for (int j = 0; j < 8; ++j) { + const int n = 64 * j + 8 * warp + g; + mma(acc[j], af, pair_n(sm, k0, k0 + 1, n), pair_n(sm, k1, k1 + 1, n)); + } + } + } + STAMP(5); + const long long pbase = (static_cast(c) * R + r) * H + hb * BMQ; + for (int j = 0; j < 8; ++j) { + const int n = 64 * j + 8 * warp + 2 * t; + *reinterpret_cast(a.po + (pbase + g) * D + n) = make_float2(acc[j][0], acc[j][1]); + *reinterpret_cast(a.po + (pbase + g + 8) * D + n) = make_float2(acc[j][2], acc[j][3]); + } + if (tid < BMQ) { + a.pm[pbase + tid] = reinterpret_cast(sm + SM_MISC)[tid]; + a.pl[pbase + tid] = reinterpret_cast(sm + SM_MISC)[16 + tid]; + } + int* flag = reinterpret_cast(sm + SM_MISC) + 32; + __threadfence(); + __syncthreads(); + if (tid == 0) { + int need = 0; + for (int cc = 0; cc < NCH; ++cc) need += chunk_works(a, r, cc) ? 1 : 0; + *flag = atomicAdd(a.ticket + r * (H / BMQ) + hb, 1) == need - 1; + } + __syncthreads(); + STAMP(6); + if (!*flag) return; + __threadfence(); + if (tid == 0) atomicExch(a.ticket + r * (H / BMQ) + hb, 0); // self-cleaning: the next launch / replay + merge(a, sm, r, hb); + STAMP(7); +} + +template +cudaError_t launch_one(const Args& a, cudaStream_t stream) { + static bool init = false; + if (!init) { + const cudaError_t e = cudaFuncSetAttribute(attn_kernel, + cudaFuncAttributeMaxDynamicSharedMemorySize, SMEM); + if (e != cudaSuccess) return e; + init = true; + } + attn_kernel<<>>(a); + return cudaGetLastError(); +} + +inline cudaError_t dispatch(const Args& a, int kw, bool rows, bool has_hi, cudaStream_t stream) { + if (a.R < 1 || a.H % BMQ || a.ring < WINDOW || (a.ring & (a.ring - 1))) return cudaErrorInvalidValue; + switch ((kw == 4 ? 4 : 0) + (rows ? 2 : 0) + (has_hi ? 1 : 0)) { + case 0: return launch_one<2, false, false>(a, stream); + case 1: return launch_one<2, false, true>(a, stream); + case 2: return launch_one<2, true, false>(a, stream); + case 3: return launch_one<2, true, true>(a, stream); + case 4: return launch_one<4, false, false>(a, stream); + case 5: return launch_one<4, false, true>(a, stream); + case 6: return launch_one<4, true, false>(a, stream); + default: return launch_one<4, true, true>(a, stream); + } +} + +} // namespace dsv41_attn_ph + +#ifndef DSV41_ATTN_NO_TORCH // the compile test builds the kernels alone (nvcc, no torch headers) +#include +#include +#include + +namespace { +template T* tp(const at::Tensor& t) { return t.numel() ? static_cast(t.data_ptr()) : nullptr; } +} + +void dsv41_attn_run_cuda(const at::Tensor& q, const at::Tensor& cv, const at::Tensor& csc, const at::Tensor& tok, + const at::Tensor& cnt, const at::Tensor& sv, const at::Tensor& ssc, const at::Tensor& lo, + const at::Tensor& hi, const at::Tensor& pos, const at::Tensor& sl, const at::Tensor& pt, + const at::Tensor& sink, const at::Tensor& cs, at::Tensor& po, at::Tensor& pm, + at::Tensor& pl, at::Tensor& out, at::Tensor& ticket, int64_t ring, int64_t psh, + int64_t pts, int64_t kw) { + using namespace dsv41_attn; + Args a{}; + a.q = tp(q); + a.has_comp = cv.numel() ? 1 : 0; + a.cv = tp(cv); a.csc = tp(csc); + a.cvs = cv.numel() ? cv.stride(0) : 0; a.css = csc.numel() ? csc.stride(0) : 0; + a.tok = tp(tok); a.ts = tok.numel() ? tok.stride(0) : 0; a.cnt = tp(cnt); + a.sv = tp(sv); a.ssc = tp(ssc); a.svs = sv.stride(0); a.sss = ssc.stride(0); + a.lo = tp(lo); a.hi = tp(hi); + a.pos = pos.data_ptr(); + a.sl = tp(sl); + a.pt = tp(pt); a.pts = pts; a.psh = static_cast(psh); + a.sink = tp(sink); a.cs = tp(cs); a.csst = cs.stride(0); + a.po = tp(po); a.pm = tp(pm); a.pl = tp(pl); + a.out = tp(out); a.ticket = tp(ticket); + a.R = static_cast(q.size(0)); a.H = static_cast(q.size(1)); a.ring = static_cast(ring); + C10_CUDA_CHECK(dispatch(a, static_cast(kw), sl.numel() > 0, hi.numel() > 0, + at::cuda::getCurrentCUDAStream())); +} + +std::vector dsv41_attn_info_cuda(int64_t device) { // the device limits of the two launches + int sms = 0, optin = 0, cl = 0, blocks = 0; + cudaDeviceGetAttribute(&sms, cudaDevAttrMultiProcessorCount, static_cast(device)); + cudaDeviceGetAttribute(&optin, cudaDevAttrMaxSharedMemoryPerBlockOptin, static_cast(device)); + cudaDeviceGetAttribute(&cl, cudaDevAttrClusterLaunch, static_cast(device)); + cudaFuncSetAttribute(dsv41_attn_ph::attn_kernel<4, true, false>, cudaFuncAttributeMaxDynamicSharedMemorySize, + dsv41_attn_ph::SMEM); + cudaOccupancyMaxActiveBlocksPerMultiprocessor(&blocks, dsv41_attn_ph::attn_kernel<4, true, false>, dsv41_attn_ph::NT, + dsv41_attn_ph::SMEM); + return {sms, optin, cl, blocks, dsv41_attn_ph::SMEM}; +} +#endif + +// The instantiations dispatch() launches: attn_kernel. +template __global__ void dsv41_attn_ph::attn_kernel<2, false, false>(dsv41_attn_ph::Args); +template __global__ void dsv41_attn_ph::attn_kernel<2, false, true>(dsv41_attn_ph::Args); +template __global__ void dsv41_attn_ph::attn_kernel<2, true, false>(dsv41_attn_ph::Args); +template __global__ void dsv41_attn_ph::attn_kernel<2, true, true>(dsv41_attn_ph::Args); +template __global__ void dsv41_attn_ph::attn_kernel<4, false, false>(dsv41_attn_ph::Args); +template __global__ void dsv41_attn_ph::attn_kernel<4, false, true>(dsv41_attn_ph::Args); +template __global__ void dsv41_attn_ph::attn_kernel<4, true, false>(dsv41_attn_ph::Args); +template __global__ void dsv41_attn_ph::attn_kernel<4, true, true>(dsv41_attn_ph::Args); diff --git a/zig/kernels/cuda/deepseek_v41/proto/gb10emu.cuh b/zig/kernels/cuda/deepseek_v41/proto/gb10emu.cuh new file mode 100644 index 0000000000..59c05b20bd --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/proto/gb10emu.cuh @@ -0,0 +1,132 @@ +// Emulates a 48-SM, bandwidth-limited GB10-class part on a bigger sm_120 GPU: blocker CTAs park or hog the other SMs. + +// Call gb10::eager() before any CUDA call: with lazy loading a first launch waits on the parked blockers (deadlock). +#pragma once +#include +#include +#include +#include +#include + +#define CK(x) do { cudaError_t e_ = (x); if (e_ != cudaSuccess) { \ + fprintf(stderr, "%s:%d %s: %s\n", __FILE__, __LINE__, #x, cudaGetErrorString(e_)); exit(1); } } while (0) + +namespace gb10 { + +constexpr int FREE = 48; // the GB10's SMs +constexpr int BLOCK_SMEM = 99 * 1024 - 64; // one blocker an SM (sm_120: 99 KB a CTA with the static flag) + +__device__ __forceinline__ unsigned smid() { unsigned s; asm volatile("mov.u32 %0, %%smid;" : "=r"(s)); return s; } + +// hogs: the first `hogs` blocked SMs stream `buf` (16-byte loads, 512 threads); the others spin +__global__ void __launch_bounds__(512) blocker(volatile int* release, int* ready, const int4* __restrict__ buf, + size_t n16, int hogs, unsigned long long* sink) { + extern __shared__ int4 pad[]; + const unsigned s = smid(); + if (s < FREE) return; + if (threadIdx.x == 0) atomicAdd(ready, 1); + const bool hog = (int)(s - FREE) < hogs; + int4 acc = make_int4(0, 0, 0, 0); + size_t i = ((size_t)(s - FREE) * 512 + threadIdx.x) * 8; + const size_t stride = (size_t)hogs * 512 * 8; + __shared__ int stop; + for (;;) { // one thread polls the host flag, every 64 sweeps + if (threadIdx.x == 0) stop = *release; + __syncthreads(); + if (stop) break; + __syncthreads(); + for (int k = 0; k < 64; ++k) { + if (hog) { +#pragma unroll + for (int u = 0; u < 8; ++u) { + const int4 v = __ldcs(buf + ((i + u) & (n16 - 1))); // n16: a power of two + acc.x ^= v.x; acc.y ^= v.y; acc.z ^= v.z; acc.w ^= v.w; + } + i = (i + stride) & (n16 - 1); + } else { + __nanosleep(2000); + } + } + } + if (acc.x == 0x7fffffff) sink[0] = (unsigned long long)(acc.y ^ acc.z ^ acc.w ^ pad[0].x); +} + +// a plain stream read of n16 16-byte words on the kernel's SMs: the bandwidth the kernel under test sees +__global__ void __launch_bounds__(512) stream_read(const int4* __restrict__ p, size_t n16, unsigned long long* sink) { + int4 acc = make_int4(0, 0, 0, 0); + for (size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x; i < n16; i += (size_t)gridDim.x * blockDim.x) { + const int4 v = __ldcs(p + i); + acc.x ^= v.x; acc.y ^= v.y; acc.z ^= v.z; acc.w ^= v.w; + } + if (acc.x == 0x7fffffff) sink[1] = (unsigned long long)(acc.y ^ acc.z ^ acc.w); +} + +inline void eager() { setenv("CUDA_MODULE_LOADING", "EAGER", 1); } + +enum class Mode { Full, Sms, Gb10 }; + +// begin() parks the blockers, end() releases them; wrap a timed region: emu.begin(); time(kernel); emu.end(); +struct Emu { + Mode mode = Mode::Full; + int sms = 0, hogs = 0; + cudaStream_t side{}; + int* release_h = nullptr; int* release_d = nullptr; int* ready = nullptr; + int4* buf = nullptr; size_t n16 = 0; unsigned long long* sink = nullptr; + + void init(Mode m, size_t hog_bytes = (size_t)4 << 30) { + mode = m; + cudaDeviceProp p{}; CK(cudaGetDeviceProperties(&p, 0)); sms = p.multiProcessorCount; + CK(cudaStreamCreateWithFlags(&side, cudaStreamNonBlocking)); + CK(cudaHostAlloc(&release_h, sizeof(int), cudaHostAllocMapped)); + CK(cudaHostGetDevicePointer(&release_d, release_h, 0)); + CK(cudaMalloc(&ready, sizeof(int))); + CK(cudaMalloc(&sink, 16)); + if (m == Mode::Gb10) { + n16 = hog_bytes / 16; CK(cudaMalloc(&buf, n16 * 16)); CK(cudaMemset(buf, 1, n16 * 16)); + } + CK(cudaFuncSetAttribute(blocker, cudaFuncAttributeMaxDynamicSharedMemorySize, BLOCK_SMEM)); + } + int grid_sms() const { return mode == Mode::Full ? sms : FREE; } // what the kernel under test may size for + void begin() { + if (mode == Mode::Full) return; + *release_h = 0; CK(cudaMemset(ready, 0, sizeof(int))); CK(cudaDeviceSynchronize()); + blocker<<>>(release_h ? release_d : nullptr, ready, buf, n16, + mode == Mode::Gb10 ? hogs : 0, sink); + CK(cudaGetLastError()); + int r = 0; + for (int spin = 0; r < sms - FREE && spin < 200000; ++spin) CK(cudaMemcpy(&r, ready, 4, cudaMemcpyDeviceToHost)); + if (r < sms - FREE) { fprintf(stderr, "gb10emu: only %d of %d blockers resident\n", r, sms - FREE); exit(1); } + } + void end() { + if (mode == Mode::Full) return; + *release_h = 1; __sync_synchronize(); CK(cudaDeviceSynchronize()); + } + // GB/s of a plain 16-byte stream read of `bytes` on the kernel's SMs (inside begin / end) + double stream_gbps(const void* p, size_t bytes, cudaStream_t st) { + cudaEvent_t a, b; CK(cudaEventCreate(&a)); CK(cudaEventCreate(&b)); + const int grid = grid_sms() * 4; + stream_read<<>>((const int4*)p, bytes / 16, sink); + CK(cudaEventRecord(a, st)); + stream_read<<>>((const int4*)p, bytes / 16, sink); + CK(cudaEventRecord(b, st)); CK(cudaEventSynchronize(b)); + float ms = 0; CK(cudaEventElapsedTime(&ms, a, b)); + CK(cudaEventDestroy(a)); CK(cudaEventDestroy(b)); + return bytes / (ms * 1e6); + } + // Gb10 mode: the hog count that leaves the kernel's SMs closest to `target` GB/s of stream read + void calibrate(const void* p, size_t bytes, cudaStream_t st, double target) { + if (mode != Mode::Gb10) return; + int best = 0; double bestd = 1e30; + for (int h = 0; h <= sms - FREE; h += 4) { + hogs = h; begin(); const double g = stream_gbps(p, bytes, st); end(); + printf(" calib hogs %3d -> stream %.1f GB/s\n", h, g); + if (fabs(g - target) < bestd) { bestd = fabs(g - target); best = h; } + if (g < target * 0.9) break; + } + hogs = best; + begin(); const double g = stream_gbps(p, bytes, st); end(); + printf("gb10emu: %d hog SMs, the 48 SMs stream %.1f GB/s (target %.0f)\n", hogs, g, target); + } +}; + +} // namespace gb10 diff --git a/zig/kernels/cuda/deepseek_v41/proto/glue_bench.cu b/zig/kernels/cuda/deepseek_v41/proto/glue_bench.cu new file mode 100644 index 0000000000..28d142de74 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/proto/glue_bench.cu @@ -0,0 +1,186 @@ +// Latency-bound decode kernels (CSA2 attention core, mHC boundary) at 1-16 rows on 48 emulated SMs, cold L2 a launch. +#include "gb10emu.cuh" +#include "../attn_cuda.cu" +#include "../mhc_cuda.cu" +#include "attn_phases.cuh" +#include "attn_fastmerge.cuh" +#include +#include +#include +#include + +namespace { + +__global__ void fill_bytes(uint8_t* p, size_t n, uint32_t seed, uint8_t mask) { + for (size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x; i < n; i += (size_t)gridDim.x * blockDim.x) { + uint32_t x = (uint32_t)i * 0x9E3779B1u ^ seed; + x ^= x >> 16; x *= 0x7feb352du; x ^= x >> 15; + p[i] = (uint8_t)(x & mask); + } +} +__global__ void fill_f32(float* p, size_t n, uint32_t seed, float lo, float hi) { + for (size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x; i < n; i += (size_t)gridDim.x * blockDim.x) { + uint32_t x = (uint32_t)i * 0x9E3779B1u ^ seed; + x ^= x >> 16; x *= 0x7feb352du; x ^= x >> 15; + p[i] = lo + (hi - lo) * (x & 0xffffff) / 16777216.f; + } +} +__global__ void flush(int4* p, size_t n) { + for (size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x; i < n; i += (size_t)gridDim.x * blockDim.x) + p[i] = make_int4((int)i, 0, 0, 0); +} + +template T* dalloc(size_t n) { T* p; CK(cudaMalloc(&p, n * sizeof(T) + 256)); CK(cudaMemset(p, 0, n * sizeof(T) + 256)); return p; } +uint8_t* rbytes(size_t n, uint32_t seed, uint8_t mask) { auto* p = dalloc(n); fill_bytes<<<512, 256>>>(p, n, seed, mask); return p; } +float* rf32(size_t n, uint32_t seed, float lo, float hi) { auto* p = dalloc(n); fill_f32<<<512, 256>>>(p, n, seed, lo, hi); return p; } + +// times fn() `reps` times, each after a cold-L2 write; returns the median in us +template +double time_us(gb10::Emu& emu, cudaStream_t st, int reps, int4* fl, size_t fln, F fn) { + cudaEvent_t a, b; CK(cudaEventCreate(&a)); CK(cudaEventCreate(&b)); + std::vector v; + fn(); // warm (first-launch attributes) + CK(cudaStreamSynchronize(st)); + emu.begin(); + for (int i = 0; i < reps; ++i) { + flush<<>>(fl, fln); + CK(cudaEventRecord(a, st)); + fn(); + CK(cudaEventRecord(b, st)); + CK(cudaEventSynchronize(b)); + float ms; CK(cudaEventElapsedTime(&ms, a, b)); + v.push_back(ms * 1e3); + } + emu.end(); + CK(cudaGetLastError()); + std::sort(v.begin(), v.end()); + return v[v.size() / 2]; +} + +} // namespace + +int main(int argc, char** argv) { // glue_bench [--mode full|sms] [--reps 40] + gb10::eager(); + std::string mode = "sms"; int reps = 40; + for (int i = 1; i + 1 < argc; i += 2) { + const std::string k = argv[i]; + if (k == "--mode") mode = argv[i + 1]; else if (k == "--reps") reps = atoi(argv[i + 1]); + } + gb10::Emu emu; emu.init(mode == "full" ? gb10::Mode::Full : gb10::Mode::Sms); + cudaStream_t st; CK(cudaStreamCreateWithFlags(&st, cudaStreamNonBlocking)); + const size_t fln = (256u << 20) / 16; int4* fl = dalloc(fln); + constexpr int RMAX = 16, H = 32, NCOMP_ROWS = 16384, RING = 128, POS = 32768; + using namespace dsv41_attn; + // ---- attention: 512 selected compressed rows + the 128-row window a row, 32 heads (one rank) ---- + uint8_t* cv = rbytes((size_t)NCOMP_ROWS * VB, 1, 0x3b); + uint8_t* csc = dalloc((size_t)NCOMP_ROWS * SB); CK(cudaMemset(csc, 127, (size_t)NCOMP_ROWS * SB)); + uint8_t* sv = rbytes((size_t)RMAX * RING * VB, 2, 0x3b); + uint8_t* ssc = dalloc((size_t)RMAX * RING * SB); CK(cudaMemset(ssc, 127, (size_t)RMAX * RING * SB)); + std::vector tok((size_t)RMAX * 512), cnt(RMAX, 512), lo(RMAX, 0); + for (size_t i = 0; i < tok.size(); ++i) tok[i] = (int)((i * 2654435761u) % NCOMP_ROWS); + int *dtok = dalloc(tok.size()), *dcnt = dalloc(RMAX), *dlo = dalloc(RMAX), *dpos = dalloc(1); + CK(cudaMemcpy(dtok, tok.data(), tok.size() * 4, cudaMemcpyHostToDevice)); + CK(cudaMemcpy(dcnt, cnt.data(), RMAX * 4, cudaMemcpyHostToDevice)); + CK(cudaMemcpy(dlo, lo.data(), RMAX * 4, cudaMemcpyHostToDevice)); + const int pos = POS; CK(cudaMemcpy(dpos, &pos, 4, cudaMemcpyHostToDevice)); + uint8_t* q = rbytes((size_t)RMAX * H * D * 2, 3, 0x3b); + float* cs = rf32((size_t)(POS + RMAX + 8) * 64, 4, -1.f, 1.f); + float* sink = rf32(H, 5, -1.f, 1.f); + float* po = dalloc((size_t)NCH * RMAX * H * D); + float *pm = dalloc((size_t)NCH * RMAX * H), *pl = dalloc((size_t)NCH * RMAX * H); + uint16_t* out = dalloc((size_t)RMAX * H * D); + int* ticket = dalloc(RMAX * H / BMQ); + // ---- mHC boundary (mode 0: post of 2 ranks' partials, collapse, 24 mixes, finish, norm) ---- + constexpr int DM = dsv41_mhc::D, NBM = dsv41_mhc::NB; + uint8_t* x = rbytes((size_t)RMAX * 4 * DM * 2, 6, 0x3b); + uint16_t* xout = dalloc((size_t)RMAX * 4 * DM); + uint8_t* gp = rbytes((size_t)2 * RMAX * DM * 2, 7, 0x3b); + float *post = rf32(RMAX * 4, 8, 0.1f, 1.f), *comb = rf32(RMAX * 16, 9, 0.f, 0.5f), *pre = rf32(RMAX * 4, 10, 0.f, 1.f); + uint8_t* fn = rbytes((size_t)24 * 4 * DM * 2, 11, 0x3b); + float *base = rf32(24, 12, -0.1f, 0.1f), *scale = rf32(3, 13, 0.5f, 1.f); + float* part = dalloc((size_t)RMAX * 4 * NBM * 32); + uint16_t *cc = dalloc((size_t)RMAX * DM), *mout = dalloc((size_t)RMAX * DM); + uint8_t* nw = rbytes((size_t)DM * 2, 14, 0x3b); + float *opre = dalloc(RMAX * 4), *opost = dalloc(RMAX * 4), *ocomb = dalloc(RMAX * 16); + int* mcnt = dalloc(1); + CK(cudaDeviceSynchronize()); + + // a CTA's time against its key count: chunk 0 holds `kc` selected keys (chunks 1-3 exit at once), the window kc + if (getenv("ATTN_KEYS")) { + for (int kc : {128, 64, 32, 16}) { + std::vector c1(RMAX, kc), l1(RMAX); + for (int r = 0; r < RMAX; ++r) l1[r] = POS + r - (kc - 1); + CK(cudaMemcpy(dcnt, c1.data(), RMAX * 4, cudaMemcpyHostToDevice)); + CK(cudaMemcpy(dlo, l1.data(), RMAX * 4, cudaMemcpyHostToDevice)); + Args a{}; + a.q = reinterpret_cast(q); a.cv = cv; a.csc = csc; a.cvs = VB; a.css = SB; + a.tok = dtok; a.ts = 512; a.cnt = dcnt; a.sv = sv; a.ssc = ssc; a.svs = VB; a.sss = SB; + a.lo = dlo; a.pos = dpos; a.sink = sink; a.cs = cs; a.csst = 64; a.po = po; a.pm = pm; a.pl = pl; + a.out = out; a.ticket = ticket; a.R = 1; a.H = H; a.ring = RING; a.has_comp = 1; + const double t = time_us(emu, st, reps, fl, fln, [&] { CK(dispatch(a, 4, false, false, st)); }); + printf("KEYS %3d a chunk (2 chunks x 2 head tiles working): %.1f us\n", kc, t); + } + CK(cudaMemcpy(dcnt, cnt.data(), RMAX * 4, cudaMemcpyHostToDevice)); + CK(cudaMemcpy(dlo, lo.data(), RMAX * 4, cudaMemcpyHostToDevice)); + } + if (getenv("ATTN_PHASES")) { // per-phase times of the R = 1 launch's CTAs + dsv41_attn_ph::Args a{}; + a.q = reinterpret_cast(q); a.cv = cv; a.csc = csc; a.cvs = VB; a.css = SB; + a.tok = dtok; a.ts = 512; a.cnt = dcnt; a.sv = sv; a.ssc = ssc; a.svs = VB; a.sss = SB; + a.lo = dlo; a.pos = dpos; a.sink = sink; a.cs = cs; a.csst = 64; a.po = po; a.pm = pm; a.pl = pl; + a.out = out; a.ticket = ticket; a.R = 1; a.H = H; a.ring = RING; a.has_comp = 1; + const double t = time_us(emu, st, reps, fl, fln, [&] { CK(dsv41_attn_ph::dispatch(a, 4, false, false, st)); }); + unsigned long long hs[256][10]; + CK(cudaMemcpyFromSymbol(hs, dsv41_attn_ph::g_stamp, sizeof(hs))); + unsigned long long t0 = ~0ull; + for (int i = 0; i < 10; ++i) t0 = std::min(t0, hs[i][0]); + printf("PHASES launch %.1f us; per CTA (us from the first CTA's start): start rows+q keys S softmax PV ticket merge\n", t); + for (int i = 0; i < 10; ++i) { + printf("PHASES cta %d:", i); + for (int k = 0; k < 8; ++k) printf(" %6.2f", hs[i][k] ? (hs[i][k] - t0) / 1e3 : -1.0); + printf("\n"); + } + } + { // fast merge: same bits, time at 1-16 rows + std::vector ref((size_t)RMAX * H * D), got((size_t)RMAX * H * D); + for (int R : {1, 4, 16}) { + Args a{}; + a.q = reinterpret_cast(q); a.cv = cv; a.csc = csc; a.cvs = VB; a.css = SB; + a.tok = dtok; a.ts = 512; a.cnt = dcnt; a.sv = sv; a.ssc = ssc; a.svs = VB; a.sss = SB; + a.lo = dlo; a.pos = dpos; a.sink = sink; a.cs = cs; a.csst = 64; a.po = po; a.pm = pm; a.pl = pl; + a.out = out; a.ticket = ticket; a.R = R; a.H = H; a.ring = RING; a.has_comp = 1; + CK(cudaMemset(out, 0, (size_t)RMAX * H * D * 2)); + const double t0 = time_us(emu, st, reps, fl, fln, [&] { CK(dispatch(a, 4, false, false, st)); }); + CK(cudaMemcpy(ref.data(), out, ref.size() * 2, cudaMemcpyDeviceToHost)); + dsv41_attn_fm::Args b{}; + static_assert(sizeof(b) == sizeof(a), "same Args"); + memcpy(&b, &a, sizeof(a)); + CK(cudaMemset(out, 0, (size_t)RMAX * H * D * 2)); + const double t1 = time_us(emu, st, reps, fl, fln, [&] { CK(dsv41_attn_fm::dispatch(b, 4, false, false, st)); }); + CK(cudaMemcpy(got.data(), out, got.size() * 2, cudaMemcpyDeviceToHost)); + size_t diff = 0, nz = 0; + for (size_t i = 0; i < (size_t)R * H * D; ++i) { diff += ref[i] != got[i]; nz += ref[i] != 0; } + printf("FASTMERGE R %2d: attn_cuda %.1f us, fast merge %.1f us, %zu of %zu outputs differ (%zu nonzero)\n", + R, t0, t1, diff, (size_t)R * H * D, nz); + } + } + printf("mode %s: per launch, median of %d cold launches (us)\n rows | attn_kernel | mhc boundary\n", mode.c_str(), reps); + for (int R : {1, 2, 4, 8, 16}) { + Args a{}; + a.q = reinterpret_cast(q); a.cv = cv; a.csc = csc; a.cvs = VB; a.css = SB; + a.tok = dtok; a.ts = 512; a.cnt = dcnt; a.sv = sv; a.ssc = ssc; a.svs = VB; a.sss = SB; + a.lo = dlo; a.hi = nullptr; a.pos = dpos; a.sl = nullptr; a.pt = nullptr; a.pts = 0; a.psh = 0; + a.sink = sink; a.cs = cs; a.csst = 64; a.po = po; a.pm = pm; a.pl = pl; a.out = out; a.ticket = ticket; + a.R = R; a.H = H; a.ring = RING; a.has_comp = 1; + const double ta = time_us(emu, st, reps, fl, fln, [&] { CK(dispatch(a, 4, false, false, st)); }); + dsv41_mhc::Args m{}; + m.x = reinterpret_cast(x); m.xs = 4 * DM; m.xout = xout; m.g = gp; m.gr = (long long)R * DM; + m.world = 2; m.gbf16 = 1; m.post = post; m.comb = comb; m.pre = pre; m.fn = fn; m.base = base; m.scale = scale; + m.part = part; m.c = cc; m.tap = nullptr; m.ts = 0; m.nw = nw; m.nwbf16 = 1; m.out = mout; + m.opre = opre; m.opost = opost; m.ocomb = ocomb; m.cnt = mcnt; m.R = R; + m.eps = 1e-20f; m.hc_eps = 1e-6f; m.post_alpha = 2.f; m.iters = 20; + const double tm = time_us(emu, st, reps, fl, fln, [&] { CK(dsv41_mhc::dispatch(m, 0, false, st)); }); + printf("RESULT %4d | %9.1f | %9.1f\n", R, ta, tm); + } + return 0; +} diff --git a/zig/kernels/cuda/deepseek_v41/proto/mma_rate.cu b/zig/kernels/cuda/deepseek_v41/proto/mma_rate.cu new file mode 100644 index 0000000000..69977cd09e --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/proto/mma_rate.cu @@ -0,0 +1,80 @@ +// mma.sync issue rates (FLOP/SM/clock) on sm_120/sm_121, 8 accumulator chains a warp; usage: mma_rate [warps_per_sm] +#include "gb10emu.cuh" +#include + +namespace { + +constexpr int ITERS = 4096, CH = 8; + +template +__global__ void __launch_bounds__(512) mma_loop(float* out, uint32_t seed) { + uint32_t a[4], b[2]; +#pragma unroll + for (int i = 0; i < 4; ++i) a[i] = 0x3c003c00u ^ (seed * (threadIdx.x + i)); + b[0] = a[0] ^ 1u; b[1] = a[1] ^ 3u; + float d[CH][4] = {}; + uint32_t h[CH][2] = {}; + for (int it = 0; it < ITERS; ++it) { +#pragma unroll + for (int c = 0; c < CH; ++c) { + if constexpr (V == 0) + asm volatile("mma.sync.aligned.m16n8k16.row.col.f32.f16.f16.f32 {%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, {%0,%1,%2,%3};\n" + : "+f"(d[c][0]), "+f"(d[c][1]), "+f"(d[c][2]), "+f"(d[c][3]) + : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b[0]), "r"(b[1])); + else if constexpr (V == 1) + asm volatile("mma.sync.aligned.m16n8k16.row.col.f32.bf16.bf16.f32 {%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, {%0,%1,%2,%3};\n" + : "+f"(d[c][0]), "+f"(d[c][1]), "+f"(d[c][2]), "+f"(d[c][3]) + : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b[0]), "r"(b[1])); + else if constexpr (V == 2) + asm volatile("mma.sync.aligned.m16n8k16.row.col.f16.f16.f16.f16 {%0,%1}, {%2,%3,%4,%5}, {%6,%7}, {%0,%1};\n" + : "+r"(h[c][0]), "+r"(h[c][1]) + : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b[0]), "r"(b[1])); + else if constexpr (V == 3) + asm volatile("mma.sync.aligned.m16n8k32.row.col.kind::f8f6f4.f32.e4m3.e4m3.f32 {%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, {%0,%1,%2,%3};\n" + : "+f"(d[c][0]), "+f"(d[c][1]), "+f"(d[c][2]), "+f"(d[c][3]) + : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b[0]), "r"(b[1])); + else + asm volatile("mma.sync.aligned.m16n8k32.row.col.f32.e4m3.e4m3.f32 {%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, {%0,%1,%2,%3};\n" + : "+f"(d[c][0]), "+f"(d[c][1]), "+f"(d[c][2]), "+f"(d[c][3]) + : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b[0]), "r"(b[1])); + } + } + float s = 0; +#pragma unroll + for (int c = 0; c < CH; ++c) s += d[c][0] + d[c][3] + __half2float(__ushort_as_half((unsigned short)h[c][0])); + if (s == 1234.5f) out[threadIdx.x] = s; +} + +template +void run(const char* name, gb10::Emu& emu, int warps, double flop_per_mma, float* out) { + cudaStream_t st; CK(cudaStreamCreateWithFlags(&st, cudaStreamNonBlocking)); + cudaEvent_t a, b; CK(cudaEventCreate(&a)); CK(cudaEventCreate(&b)); + const int grid = emu.grid_sms(); + mma_loop<<>>(out, 7); // warm + emu.begin(); + CK(cudaEventRecord(a, st)); + mma_loop<<>>(out, 7); + CK(cudaEventRecord(b, st)); CK(cudaEventSynchronize(b)); + emu.end(); + float ms; CK(cudaEventElapsedTime(&ms, a, b)); + int clk; CK(cudaDeviceGetAttribute(&clk, cudaDevAttrClockRate, 0)); + const double flop = (double)grid * warps * ITERS * CH * flop_per_mma; + const double per = flop / (ms * 1e-3) / grid / (clk * 1e3); + printf("RATE %-28s %2d warps/SM: %8.1f TFLOP/s on %d SMs, %6.0f FLOP/SM/clk (at %d MHz) -> GB10 48 SMs @2.5 GHz %.1f TFLOP/s\n", + name, warps, flop / (ms * 1e9), grid, per, clk / 1000, per * 48 * 2.5e9 / 1e12); +} + +} // namespace + +int main(int argc, char** argv) { + gb10::eager(); + const int warps = argc > 1 ? atoi(argv[1]) : 8; + gb10::Emu emu; emu.init(gb10::Mode::Sms); + float* out; CK(cudaMalloc(&out, 4096)); + run<0>("f16 x f16 -> f32 k16", emu, warps, 2.0 * 16 * 8 * 16, out); + run<1>("bf16 x bf16 -> f32 k16", emu, warps, 2.0 * 16 * 8 * 16, out); + run<2>("f16 x f16 -> f16 k16", emu, warps, 2.0 * 16 * 8 * 16, out); + run<3>("e4m3 kind::f8f6f4 -> f32 k32", emu, warps, 2.0 * 16 * 8 * 32, out); + run<4>("e4m3 -> f32 k32", emu, warps, 2.0 * 16 * 8 * 32, out); + return 0; +} diff --git a/zig/kernels/cuda/deepseek_v41/proto/router_glue_check.cu b/zig/kernels/cuda/deepseek_v41/proto/router_glue_check.cu new file mode 100644 index 0000000000..facf2620f7 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/proto/router_glue_check.cu @@ -0,0 +1,162 @@ +// Oracle and timing for router_glue.cu against the pinned upstream code; usage: router_glue_check [--reps N]. + +// Keep fmad on: the oracle reproduces upstream's FMA tree bit for bit (SASS-audited on sm_120/sm_121). +#include "../router_glue.cu" +#include "../exl3_experts.cu" +#include +#include +#include +#include +#include + +#define CK(call) do { const cudaError_t err = (call); if (err != cudaSuccess) { \ + std::fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, #call, cudaGetErrorString(err)); \ + std::exit(2); } } while (0) + +template struct Device { + T* p; + size_t n; + explicit Device(size_t count) : n(count) { CK(cudaMalloc(&p, n * sizeof(T))); } + ~Device() { cudaFree(p); } + void set(const std::vector& v) { CK(cudaMemcpy(p, v.data(), n * sizeof(T), cudaMemcpyHostToDevice)); } + void sentinel() { CK(cudaMemset(p, 0xa5, n * sizeof(T))); } + std::vector get() const { + std::vector v(n); CK(cudaMemcpy(v.data(), p, n * sizeof(T), cudaMemcpyDeviceToHost)); return v; + } +}; + +template void same(const Device& ref, const Device& got, const char* what, int R, int mode, int edges) { + const auto a = ref.get(), b = got.get(); + if (std::memcmp(a.data(), b.data(), a.size() * sizeof(T)) == 0) return; + for (size_t i = 0; i < a.size(); ++i) + if (std::memcmp(a.data() + i, b.data() + i, sizeof(T)) != 0) { + std::fprintf(stderr, "FAIL %s R=%d mode=%d edges=%d element=%zu\n", what, R, mode, edges, i); + std::exit(1); + } +} + +uint32_t mix(uint32_t v) { v ^= v >> 16; v *= 0x7feb352du; v ^= v >> 15; v *= 0x846ca68bu; return v ^ (v >> 16); } + +template float time_us(int reps, F launch) { + cudaEvent_t a, b; CK(cudaEventCreate(&a)); CK(cudaEventCreate(&b)); + launch(); CK(cudaDeviceSynchronize()); + CK(cudaEventRecord(a)); + for (int q = 0; q < reps; ++q) launch(); + CK(cudaEventRecord(b)); CK(cudaEventSynchronize(b)); + float ms; CK(cudaEventElapsedTime(&ms, a, b)); + CK(cudaEventDestroy(a)); CK(cudaEventDestroy(b)); + return ms * 1000.0f / reps; +} + +template float time_graph_us(int reps, F launch) { + cudaStream_t stream; CK(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking)); + cudaGraph_t graph; cudaGraphExec_t exec; + CK(cudaStreamBeginCapture(stream, cudaStreamCaptureModeThreadLocal)); + launch(stream); + CK(cudaStreamEndCapture(stream, &graph)); + CK(cudaGraphInstantiate(&exec, graph, nullptr, nullptr, 0)); + const float us = time_us(reps, [&] { CK(cudaGraphLaunch(exec, nullptr)); }); + CK(cudaGraphExecDestroy(exec)); CK(cudaGraphDestroy(graph)); CK(cudaStreamDestroy(stream)); + return us; +} + +void check_case(int R, int mode, bool edges, int reps) { + constexpr int K = 5120; + const int slots = mode == 4 ? 1 : mode == 6 ? 4 : (mode == 5 || mode == 7) ? 7 : 9; + const int E = mode == 4 ? 1 : mode == 5 ? 512 : mode == 6 ? 17 : mode == 7 ? 385 : 257; + const int maxm = mode == 2 ? 3 : R; + const int stride = K + 8, P = R * slots; + const size_t out_n = (size_t)P * K + 32; + Device x((size_t)R * stride), suh0((size_t)E * K), suh1((size_t)E * K); + Device pick(P), ids0(E + 32), ids1(E + 32), ids2(E + 32), count0(1), count1(1), count2(1); + Device mem0((size_t)E * maxm + 32), mem1((size_t)E * maxm + 32), mem2((size_t)E * maxm + 32); + Device out00(out_n), out01(out_n), out10(out_n), out11(out_n); + std::vector xv(x.n), s0(suh0.n), s1(suh1.n); + // bf16/fp16 edge bits (signed zeros, subnormals, maxima, infinities, NaNs) plus finite values of both signs + const uint16_t bf_edge[] = {0, 0x8000, 1, 0x8001, 0x007f, 0x0080, 0x3f80, 0xbf80, + 0x7f7f, 0xff7f, 0x7f80, 0xff80, 0x7fc1, 0xffc2}; + const uint16_t hf_edge[] = {0, 0x8000, 1, 0x8001, 0x03ff, 0x0400, 0x3c00, 0xbc00, + 0x7bff, 0xfbff, 0x7c00, 0xfc00, 0x7e01, 0xfe02}; + for (size_t i = 0; i < xv.size(); ++i) { + const auto h = mix((uint32_t)i + 77); + xv[i] = edges ? bf_edge[h % 14] : (uint16_t)((h & 0x8000u) | (0x3e00u + h % 512)); + } + for (size_t i = 0; i < s0.size(); ++i) { + const auto h = mix((uint32_t)i + 41), j = mix((uint32_t)i + 43); + s0[i] = edges ? hf_edge[h % 14] : (uint16_t)((h & 0x8000u) | (0x3400u + h % 2048)); + s1[i] = edges ? hf_edge[j % 14] : (uint16_t)((j & 0x8000u) | (0x3400u + j % 2048)); + } + std::vector pv(P); + for (int i = 0; i < P; ++i) { + const int slot = i % slots; + if (mode == 0 || mode == 5 || mode == 6 || mode == 7) + pv[i] = slot == slots - 1 ? E - 1 : (i * 13 + slot * 7) % (E - 1); + else if (mode == 1) pv[i] = i % 5 == 0 ? E : i % 7 == 0 ? -1 : (i * 11) % E; + else if (mode == 2) pv[i] = i % 3; // duplicates and truncation to maxm=3 + else if (mode == 3) pv[i] = i % 2 ? E : -1; // no valid experts + else pv[i] = 0; + } + x.set(xv); suh0.set(s0); suh1.set(s1); pick.set(pv); + ids0.sentinel(); ids1.sentinel(); ids2.sentinel(); mem0.sentinel(); mem1.sentinel(); mem2.sentinel(); + out00.sentinel(); out01.sentinel(); out10.sentinel(); out11.sentinel(); + const auto reference_group = [&](cudaStream_t stream = nullptr) { + tf_exl3_experts::group_kernel<<<1, 1024, P * sizeof(int), stream>>>(pick.p, ids0.p, count0.p, mem0.p, R, slots, E, maxm); + CK(cudaGetLastError()); + }; + const auto reference = [&](cudaStream_t stream = nullptr) { + reference_group(stream); + tf_exl3_experts::rot_in_kernel<__nv_bfloat16><<>>( + reinterpret_cast(x.p), stride, pick.p, + reinterpret_cast(suh0.p), reinterpret_cast(suh1.p), + reinterpret_cast(out00.p), reinterpret_cast(out01.p), K, slots, E); + CK(cudaGetLastError()); + }; + const auto group = [&](cudaStream_t stream = nullptr) { + router_group<<<1, 1024, 0, stream>>>(pick.p, ids2.p, count2.p, mem2.p, R, slots, E, maxm); + CK(cudaGetLastError()); + }; + const auto fused = [&](cudaStream_t stream = nullptr) { + router_group_rot<<<1 + (P * (K / 128) * 2 + 31) / 32, 1024, 0, stream>>>(x.p, stride, pick.p, + reinterpret_cast(suh0.p), reinterpret_cast(suh1.p), + reinterpret_cast(out10.p), reinterpret_cast(out11.p), + ids1.p, count1.p, mem1.p, R, K, slots, E, maxm); + CK(cudaGetLastError()); + }; + reference(); group(); fused(); CK(cudaDeviceSynchronize()); + same(count0, count1, "fused count", R, mode, edges); same(count0, count2, "group count", R, mode, edges); + same(ids0, ids1, "fused ids", R, mode, edges); same(ids0, ids2, "group ids", R, mode, edges); + same(mem0, mem1, "fused members", R, mode, edges); same(mem0, mem2, "group members", R, mode, edges); + same(out00, out10, "gate rotation", R, mode, edges); same(out01, out11, "up rotation", R, mode, edges); + // Repeated launches require no ticket reset; check both integer and fp bits. + for (int repeat = 0; repeat < 3; ++repeat) fused(); + CK(cudaDeviceSynchronize()); + same(mem0, mem1, "replayed members", R, mode, edges); + same(out00, out10, "replayed gate", R, mode, edges); + if ((mode == 0 || mode == 6 || mode == 7) && !edges) { + const float old_group = time_us(reps, reference_group), new_group = time_us(reps, group); + const float old_pair = time_us(reps, reference), new_pair = time_us(reps, fused); + std::printf("PASS R=%d slots=%d E=%d group_us %.3f -> %.3f pair_us %.3f -> %.3f (warm eager)\n", + R, slots, E, old_group, new_group, old_pair, new_pair); + const float graph_old = time_graph_us(reps, reference), graph_new = time_graph_us(reps, fused); + same(count0, count1, "graphed count", R, mode, edges); + same(ids0, ids1, "graphed ids", R, mode, edges); + same(mem0, mem1, "graphed members", R, mode, edges); + same(out00, out10, "graphed gate", R, mode, edges); + same(out01, out11, "graphed up", R, mode, edges); + std::printf("PASS R=%d slots=%d E=%d graph_pair_us %.3f -> %.3f nodes 2 -> 1 (warm graph replay)\n", + R, slots, E, graph_old, graph_new); + } else std::printf("PASS R=%d slots=%d E=%d maxm=%d mode=%d edges=%d\n", R, slots, E, maxm, mode, edges); +} + +int main(int argc, char** argv) { + int reps = 100; + if (argc == 3 && std::strcmp(argv[1], "--reps") == 0) reps = std::max(1, std::atoi(argv[2])); + else if (argc != 1) { std::fprintf(stderr, "usage: %s [--reps N]\n", argv[0]); return 2; } + int count = 0; CK(cudaGetDeviceCount(&count)); + if (count == 0) { std::fprintf(stderr, "No CUDA GPU: oracle not run\n"); return 2; } + for (int R : {17, 20, 24, 32, 48, 64}) + for (int mode = 0; mode < 8; ++mode) + for (bool edges : {false, true}) check_case(R, mode, edges, reps); + std::printf("96/96 cases bit-exact, replay and sentinel guards included\n"); + return 0; +} diff --git a/zig/kernels/cuda/deepseek_v41/proto/x3gm_ablate.cuh b/zig/kernels/cuda/deepseek_v41/proto/x3gm_ablate.cuh new file mode 100644 index 0000000000..d3a111ae23 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/proto/x3gm_ablate.cuh @@ -0,0 +1,180 @@ +// Timing ablations of x3gm.cu's gm_kernel: ABL 0 = the kernel, 1-7 each drop one part; ABL > 0 outputs are junk. +#pragma once +namespace dsv41_x3gm { +template +__global__ void __launch_bounds__(Cfg::THREADS, + Cfg::MINB) gm_ablate(const Args a) { + using C = Cfg; + constexpr int THREADS = C::THREADS, BM = C::BM, LDA = C::LDA, LDE = C::LDE, ER = C::ER, CPR = C::CPR, + TW = C::TW, W = C::W; + extern __shared__ __align__(16) unsigned char smem[]; + float* ep = reinterpret_cast(smem); // [MATS][ER][LDE] after the K loop (aliases the ring) + __shared__ int rows_sh[BM]; + __shared__ int item_sh; + const int warp = threadIdx.x >> 5, lane = threadIdx.x & 31; + const int mat = warp / C::WPM, slice = warp % C::WPM; // columns slice 16 MTL .. + 16 MTL of the item + const int xmat = XM == 1 ? 0 : mat; + const int g = lane >> 2, t = lane & 3; + const int K = a.K, N = a.N, KT = K >> 4, NTILES = N >> 4, S = KT / KS, NBLK = N >> 7; + const size_t se = (size_t)KT * NTILES * TW; // words of one expert's matrix + const int total = a.npass[0] * NBLK; + const int lr = (lane & 7) + ((lane >> 4) << 3), lc = (lane >> 3) & 1; + const LaneMap map(lane); + + int next = blockIdx.x; + for (;;) { + __syncthreads(); // the last item is done with rows_sh and shared memory + if (threadIdx.x == 0) item_sh = a.ticket ? atomicAdd(a.ticket, 1) : next; + next += gridDim.x; + __syncthreads(); + const int item = item_sh; + if (item >= total) break; + const int tt = item / NBLK, nb = item % NBLK; + const int e = a.pe[tt], off = a.poff[tt], cnt = a.pcnt[tt]; + const uint32_t* te0 = a.tp0 ? reinterpret_cast(a.tp0[e]) : a.t0 + (size_t)e * se; + const uint32_t* te1 = a.tp1 ? reinterpret_cast(a.tp1[e]) : a.t1 + (size_t)e * se; + for (int i = threadIdx.x; i < BM; i += THREADS) rows_sh[i] = i < cnt ? a.order[off + i] : -1; + __syncthreads(); + const int cnt16 = (cnt + 15) & ~15; // rows past it are never read (warp-uniform skip) + + // stage s: member rows (zero past the count), then the item's words of k tiles KS s .. KS s + KS - 1, block nb + auto load_stage = [&](int s) { + unsigned char* st = smem + (size_t)(s % NSA) * C::STAGE; + half* ra = reinterpret_cast(st); + const int k0 = s * KS * 16; + for (int i = threadIdx.x; i < XM * BM * CPR && !(ABL == 4 && s >= NSA); i += THREADS) { + const int m = i / (BM * CPR), r = (i / CPR) % BM, c = i % CPR; + if (r < cnt16) { + const int row = rows_sh[r]; + const half* src = (m ? a.x1 : a.x0) + (size_t)(row < 0 ? 0 : row) * K + k0 + c * 8; + cp_async16(ra + ((size_t)m * BM + r) * LDA + swz(r, c) * 8, src, row >= 0); + } + } + uint32_t* rw = reinterpret_cast(st + C::A_BYTES); + constexpr int WCH = MATS * KS * NB * K2; // 16-byte chunks: K2 a tile + if (ABL == 3 && s >= NSA) return; + for (int i = threadIdx.x; i < WCH; i += THREADS) { + const int q = i % K2, n = (i / K2) % NB, kk = (i / (K2 * NB)) % KS, m = i / (K2 * NB * KS); + const uint32_t* src = (m ? te1 : te0) + ((size_t)(s * KS + kk) * NTILES + nb * NB + n) * TW + q * 4; + cp_async16(rw + ((m * KS + kk) * NB + n) * TW + q * 4, src, true); + } + }; + + float acc[MTL][NG][4]; +#pragma unroll + for (int l = 0; l < MTL; ++l) +#pragma unroll + for (int n = 0; n < NG; ++n) +#pragma unroll + for (int c = 0; c < 4; ++c) acc[l][n][c] = 0.f; + +#pragma unroll + for (int s = 0; s < NSA - 1; ++s) { + if (s < S) load_stage(s); + cp_commit(); + } + cp_wait(); + __syncthreads(); + for (int s = 0; s < S; ++s) { + if (s + NSA - 1 < S) load_stage(s + NSA - 1); + cp_commit(); + const unsigned char* st = smem + (size_t)(s % NSA) * C::STAGE; + const half* A = reinterpret_cast(st) + (size_t)xmat * BM * LDA; + const uint32_t* Wd = reinterpret_cast(st + C::A_BYTES) + (size_t)mat * KS * NB * TW + + slice * MTL * TW; +#pragma unroll + for (int kk = 0; kk < KS; ++kk) { + uint32_t af[MTL][4]; +#pragma unroll + for (int l = 0; l < MTL; ++l) { + if constexpr (ABL == 1) { + const uint32_t* tw = Wd + (kk * NB + l) * TW; + af[l][0] = tw[lane % TW] & 0x3bff3bffu; af[l][1] = af[l][0] ^ 0x1u; + af[l][2] = af[l][0] ^ 0x2u; af[l][3] = af[l][0] ^ 0x4u; + } else { + decode_a(Wd + (kk * NB + l) * TW, map, af[l]); + } + } + const int chunk = swz(lr, kk * 2 + lc); +#pragma unroll + for (int np = 0; np < NG / 2; ++np) { + if (16 * np < cnt) { // warp-uniform + uint32_t b[4]; + ldsm4(b, A + (16 * np + lr) * LDA + chunk * 8); + const uint32_t b01[2] = {b[0], b[1]}, b23[2] = {b[2], b[3]}; +#pragma unroll + for (int l = 0; l < MTL; ++l) { + if constexpr (ABL == 2) { + acc[l][2 * np][0] += __uint_as_float((af[l][0] ^ b01[0]) & 0x3f7fffffu); + acc[l][2 * np + 1][0] += __uint_as_float((af[l][1] ^ b23[1]) & 0x3f7fffffu); + } else { + mma16816(acc[l][2 * np], af[l], b01); + mma16816(acc[l][2 * np + 1], af[l], b23); + } + } + } + } + } + cp_wait(); + __syncthreads(); + } + + // epilogue: ER members a round, accumulators -> ep[mat][member][column], then one warp a member row + if constexpr (ABL == 5) { // no epilogue: one store keeps the chains live + if (acc[0][0][0] == 1234.5f) a.y[threadIdx.x] = acc[MTL - 1][NG - 1][3]; + continue; + } +#pragma unroll + for (int rd = 0; rd < C::ROUNDS; ++rd) { + if (rd) __syncthreads(); + if (ER * rd < cnt) { +#pragma unroll + for (int n = 0; n < NGR; ++n) { + const int ng = rd * NGR + n; + float* E = ep + ((size_t)mat * ER + 8 * n + 2 * t) * LDE + slice * 16 * MTL + g; +#pragma unroll + for (int l = 0; l < MTL; ++l) { + E[l * 16] = acc[l][ng][0]; + E[LDE + l * 16] = acc[l][ng][1]; + E[l * 16 + 8] = acc[l][ng][2]; + E[LDE + l * 16 + 8] = acc[l][ng][3]; + } + } + } + __syncthreads(); + for (int r = warp; r < ER && ER * rd + r < cnt; r += W) { + const int row = rows_sh[ER * rd + r]; + const int c0 = 4 * lane; + const size_t col = (size_t)e * N + nb * 128 + c0; + float v[4]; + *reinterpret_cast(v) = *reinterpret_cast(ep + (size_t)r * LDE + c0); + if constexpr (ABL != 7) tf_exl3::fwht128(v, lane); + if constexpr (MATS == 2) { + float u[4]; + *reinterpret_cast(u) = *reinterpret_cast(ep + ((size_t)ER + r) * LDE + c0); + if constexpr (ABL != 7) tf_exl3::fwht128(u, lane); +#pragma unroll + for (int j = 0; j < 4; ++j) { // upstream gateup_epilogue, ACT_F32 + const float gg = fminf(v[j] * HAD * (ABL == 6 ? 1.f : __half2float(a.sv0[col + j])), a.limit); + const float uu = fminf(fmaxf(u[j] * HAD * (ABL == 6 ? 1.f : __half2float(a.sv1[col + j])), -a.limit), a.limit); + const float act = gg / (1.f + expf(-gg)) * uu; + v[j] = act * (ABL == 6 ? 1.f : __half2float(a.sd[col + j])); + } + if constexpr (ABL != 7) tf_exl3::fwht128(v, lane); + half2* o = reinterpret_cast(a.xd + (size_t)row * N + nb * 128 + c0); + o[0] = __halves2half2(__float2half_rn(v[0] * HAD), __float2half_rn(v[1] * HAD)); + o[1] = __halves2half2(__float2half_rn(v[2] * HAD), __float2half_rn(v[3] * HAD)); + } else { + float4 o; // upstream down_epilogue + o.x = v[0] * HAD * (ABL == 6 ? 1.f : __half2float(a.sv0[col + 0])); + o.y = v[1] * HAD * (ABL == 6 ? 1.f : __half2float(a.sv0[col + 1])); + o.z = v[2] * HAD * (ABL == 6 ? 1.f : __half2float(a.sv0[col + 2])); + o.w = v[3] * HAD * (ABL == 6 ? 1.f : __half2float(a.sv0[col + 3])); + *reinterpret_cast(a.y + (size_t)row * N + nb * 128 + c0) = o; + } + } + } + } +} + +} // namespace dsv41_x3gm diff --git a/zig/kernels/cuda/deepseek_v41/proto/x3gm_bench.cu b/zig/kernels/cuda/deepseek_v41/proto/x3gm_bench.cu new file mode 100644 index 0000000000..00e6325fe5 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/proto/x3gm_bench.cu @@ -0,0 +1,218 @@ +// Times x3gm rotation, gate/up and down at one DeepSeek-V4.1 layer's per-rank shapes under gb10emu.cuh modes. +#include "gb10emu.cuh" +#include "../x3gm.cu" +#include "x3gm_ablate.cuh" +#include +#include +#include +#include +#include +#include + +using namespace dsv41_x3gm; + +namespace { + +constexpr int E = 384, S = 6, D = 5120, I = 1152; + +__global__ void fill_words(uint32_t* p, size_t n, uint32_t seed) { + for (size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x; i < n; i += (size_t)gridDim.x * blockDim.x) { + uint32_t x = (uint32_t)i * 0x9E3779B1u ^ seed; + x ^= x >> 16; x *= 0x7feb352du; x ^= x >> 15; x *= 0x846ca68bu; x ^= x >> 16; + p[i] = x; + } +} +__global__ void fill_half(half* p, size_t n, uint32_t seed, float lo, float hi) { + for (size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x; i < n; i += (size_t)gridDim.x * blockDim.x) { + uint32_t x = (uint32_t)i * 0x9E3779B1u ^ seed; + x ^= x >> 16; x *= 0x7feb352du; x ^= x >> 15; + p[i] = __float2half(lo + (hi - lo) * (x & 0xffffff) / 16777216.f); + } +} + +using Kern = void (*)(const Args); +struct Launch { Kern k; int threads; size_t smem; int bm; }; + +template +Launch make() { + using C = Cfg; + Kern k = gm_kernel; + CK(cudaFuncSetAttribute(k, cudaFuncAttributeMaxDynamicSharedMemorySize, (int)C::SMEM)); + CK(cudaFuncSetAttribute(k, cudaFuncAttributePreferredSharedMemoryCarveout, 100)); + return {k, C::THREADS, C::SMEM, C::BM}; +} +template +Launch make_abl() { + using C = Cfg; + Kern k = gm_ablate; + CK(cudaFuncSetAttribute(k, cudaFuncAttributeMaxDynamicSharedMemorySize, (int)C::SMEM)); + CK(cudaFuncSetAttribute(k, cudaFuncAttributePreferredSharedMemoryCarveout, 100)); + return {k, C::THREADS, C::SMEM, C::BM}; +} +// ablations (x3gm_ablate.cuh) at the default configurations, one rotated input, 2 and 3 bits +#define ABL_K(A, K2) if (abl == A && k2 == K2) return down ? make_abl() : make_abl(); +#define ABL_A(A) ABL_K(A, 4) ABL_K(A, 6) +Launch abl_launch(int abl, int k2, bool down) { + ABL_A(1) ABL_A(2) ABL_A(3) ABL_A(4) ABL_A(5) ABL_A(6) ABL_A(7) + fprintf(stderr, "no ablation %d at k2 %d\n", abl, k2); exit(2); +} + +// default instances (2,048 rows: gate/up 0 with one rotated input, else 1; down 0) + alternatives, K2 4-10 = 2-5 bits +#define GU_CASE(XM, K2, CFG, ID) if (xm == XM && k2 == K2 && id == ID) return make<2, XM, K2, CFG>(); +#define DN_CASE(K2, CFG, ID) if (k2 == K2 && id == ID) return make<1, 1, K2, CFG>(); +#define GU_W(K2) GU_CASE(1, K2, GM_GU0, 0) GU_CASE(1, K2, GM_GU1, 1) GU_CASE(1, K2, GM_GU5, 5) GU_CASE(1, K2, GM_GU2, 2) GU_CASE(1, K2, GM_GU3, 3) \ + GU_CASE(2, K2, GM_GU0, 0) GU_CASE(2, K2, GM_GU1, 1) GU_CASE(2, K2, GM_GU5, 5) +#define DN_W(K2) DN_CASE(K2, GM_DN0, 0) DN_CASE(K2, GM_DN1, 1) DN_CASE(K2, GM_DN2, 2) DN_CASE(K2, GM_DN3, 3) +Launch gu_launch(int xm, int k2, int id) { + GU_W(4) GU_W(6) GU_W(8) GU_W(10) + fprintf(stderr, "no gate/up instance xm %d k2 %d cfg %d\n", xm, k2, id); exit(2); +} +Launch dn_launch(int k2, int id) { + DN_W(4) DN_W(6) DN_W(8) DN_W(10) + fprintf(stderr, "no down instance k2 %d cfg %d\n", k2, id); exit(2); +} + +struct Plan { std::vector pick, order, pe, poff, pcnt; int npass = 0; }; + +// routing: each row picks 6 distinct experts; zipf > 0 skews the experts' popularity (rank^-zipf) +Plan route(int rows, double zipf, int bm, uint32_t seed) { + Plan p; std::mt19937 rng(seed); + std::vector w(E); + for (int e = 0; e < E; ++e) w[e] = zipf > 0 ? std::pow(e + 1.0, -zipf) : 1.0; + std::shuffle(w.begin(), w.end(), rng); + std::discrete_distribution pickd(w.begin(), w.end()); + p.pick.resize((size_t)rows * S); + for (int r = 0; r < rows; ++r) { + int got = 0; + while (got < S) { + const int e = pickd(rng); + bool dup = false; + for (int j = 0; j < got; ++j) dup |= p.pick[(size_t)r * S + j] == e; + if (!dup) p.pick[(size_t)r * S + got++] = e; + } + } + const int P = rows * S; + p.order.resize(P); std::iota(p.order.begin(), p.order.end(), 0); + std::stable_sort(p.order.begin(), p.order.end(), [&](int a, int b) { return p.pick[a] < p.pick[b]; }); + std::vector cnt(E, 0); + for (int x : p.pick) ++cnt[x]; + int off = 0; + for (int e = 0; e < E; ++e) { + for (int j = 0; j < cnt[e]; j += bm) { + p.pe.push_back(e); p.poff.push_back(off + j); p.pcnt.push_back(std::min(bm, cnt[e] - j)); + } + off += cnt[e]; + } + p.npass = (int)p.pe.size(); + return p; +} + +template T* dev(const std::vector& v) { + T* d; CK(cudaMalloc(&d, v.size() * sizeof(T) + 16)); + CK(cudaMemcpy(d, v.data(), v.size() * sizeof(T), cudaMemcpyHostToDevice)); + return d; +} + +double median(std::vector v) { std::sort(v.begin(), v.end()); return v[v.size() / 2]; } + +} // namespace + +int main(int argc, char** argv) { // flags: --k2 --rows --mode --gu --dn --xm --zipf --reps --target --abl + int k2 = 4, rows = 2048, gu = 0, dn = 0, xm = 1, reps = 5, abl = 0; + double zipf = 0, target = 240; + std::string mode = "full"; + for (int i = 1; i + 1 < argc; i += 2) { + const std::string a = argv[i]; + if (a == "--k2") k2 = atoi(argv[i + 1]); + else if (a == "--rows") rows = atoi(argv[i + 1]); + else if (a == "--gu") gu = atoi(argv[i + 1]); + else if (a == "--dn") dn = atoi(argv[i + 1]); + else if (a == "--xm") xm = atoi(argv[i + 1]); + else if (a == "--abl") abl = atoi(argv[i + 1]); + else if (a == "--reps") reps = atoi(argv[i + 1]); + else if (a == "--zipf") zipf = atof(argv[i + 1]); + else if (a == "--target") target = atof(argv[i + 1]); + else if (a == "--mode") mode = argv[i + 1]; + else { fprintf(stderr, "unknown %s\n", a.c_str()); return 2; } + } + gb10::eager(); + gb10::Emu emu; + emu.init(mode == "gb10" ? gb10::Mode::Gb10 : mode == "sms" ? gb10::Mode::Sms : gb10::Mode::Full); + cudaStream_t st; CK(cudaStreamCreateWithFlags(&st, cudaStreamNonBlocking)); + + const Launch G = abl ? abl_launch(abl, k2, false) : gu_launch(xm, k2, gu); + const Launch Dn = abl ? abl_launch(abl, k2, true) : dn_launch(k2, dn); + const Plan pl = route(rows, zipf, G.bm, 1234); + const Plan pd = G.bm == Dn.bm ? pl : route(rows, zipf, Dn.bm, 1234); + const int P = rows * S; + const size_t mw = (size_t)E * (D / 16) * (I / 16) * 4 * k2; // words of one stacked matrix + uint32_t *tg, *tu, *td; + CK(cudaMalloc(&tg, mw * 4)); CK(cudaMalloc(&tu, mw * 4)); CK(cudaMalloc(&td, mw * 4)); + fill_words<<<1024, 256>>>(tg, mw, 1); fill_words<<<1024, 256>>>(tu, mw, 2); fill_words<<<1024, 256>>>(td, mw, 3); + half *x, *suh_g, *suh_u, *svg, *svu, *sud, *svd, *xg, *xu, *xd; float* y; + CK(cudaMalloc(&x, (size_t)rows * D * 2)); + CK(cudaMalloc(&suh_g, (size_t)E * D * 2)); CK(cudaMalloc(&suh_u, (size_t)E * D * 2)); + CK(cudaMalloc(&svg, (size_t)E * I * 2)); CK(cudaMalloc(&svu, (size_t)E * I * 2)); + CK(cudaMalloc(&sud, (size_t)E * I * 2)); CK(cudaMalloc(&svd, (size_t)E * D * 2)); + CK(cudaMalloc(&xg, (size_t)P * D * 2)); CK(cudaMalloc(&xu, (size_t)P * D * 2)); CK(cudaMalloc(&xd, (size_t)P * I * 2)); + CK(cudaMalloc(&y, (size_t)P * D * 4)); + fill_half<<<1024, 256>>>(x, (size_t)rows * D, 4, -1.f, 1.f); + for (half* p : {suh_g, suh_u}) fill_half<<<1024, 256>>>(p, (size_t)E * D, 5, -1.f, 1.f); + for (half* p : {svg, svu, sud}) fill_half<<<1024, 256>>>(p, (size_t)E * I, 6, 0.5f, 1.5f); + fill_half<<<1024, 256>>>(svd, (size_t)E * D, 7, 0.5f, 1.5f); + int *pick = dev(pl.pick), *order = dev(pl.order), *pe = dev(pl.pe), *poff = dev(pl.poff), *pcnt = dev(pl.pcnt); + int *orderd = dev(pd.order), *ped = dev(pd.pe), *poffd = dev(pd.poff), *pcntd = dev(pd.pcnt); + int *np = dev(std::vector{pl.npass}), *npd = dev(std::vector{pd.npass}); + int* ticket; CK(cudaMalloc(&ticket, 8)); + CK(cudaDeviceSynchronize()); + + int per_g = 0, per_d = 0; + CK(cudaOccupancyMaxActiveBlocksPerMultiprocessor(&per_g, G.k, G.threads, G.smem)); + CK(cudaOccupancyMaxActiveBlocksPerMultiprocessor(&per_d, Dn.k, Dn.threads, Dn.smem)); + emu.calibrate(tg, mw * 4, st, target); + const int gg = emu.grid_sms() * per_g, gd = emu.grid_sms() * per_d; + + Args ag{}; ag.x0 = xg; ag.x1 = xm == 2 ? xu : nullptr; ag.t0 = tg; ag.t1 = tu; ag.order = order; ag.pe = pe; + ag.poff = poff; ag.pcnt = pcnt; ag.npass = np; ag.ticket = ticket; ag.sv0 = svg; ag.sv1 = svu; ag.sd = sud; + ag.xd = xd; ag.K = D; ag.N = I; ag.limit = 10.f; + Args ad{}; ad.x0 = xd; ad.t0 = td; ad.order = orderd; ad.pe = ped; ad.poff = poffd; ad.pcnt = pcntd; ad.npass = npd; + ad.ticket = ticket + 1; ad.sv0 = svd; ad.y = y; ad.K = I; ad.N = D; + const int rot_items = P * (D / 128); + + cudaEvent_t ev[4]; + for (auto& e : ev) CK(cudaEventCreate(&e)); + std::vector t_rot, t_gu, t_dn; + for (int r = 0; r < reps + 1; ++r) { + CK(cudaMemsetAsync(ticket, 0, 8, st)); + emu.begin(); + const bool dbg = getenv("X3GM_DEBUG") != nullptr; + auto step = [&](const char* w) { if (dbg) { CK(cudaStreamSynchronize(st)); fprintf(stderr, " %s done\n", w); } }; + step("begin"); + CK(cudaEventRecord(ev[0], st)); + if (xm == 2) rot_kernel<<<(rot_items + 7) / 8, 256, 0, st>>>(x, D, pick, suh_g, suh_u, xg, xu, D, S, rot_items); + else rot_kernel<<<(rot_items + 7) / 8, 256, 0, st>>>(x, D, pick, suh_g, nullptr, xg, nullptr, D, S, rot_items); + step("rot"); + CK(cudaEventRecord(ev[1], st)); + G.k<<>>(ag); + step("gateup"); + CK(cudaEventRecord(ev[2], st)); + Dn.k<<>>(ad); + CK(cudaEventRecord(ev[3], st)); + CK(cudaGetLastError()); + CK(cudaEventSynchronize(ev[3])); + emu.end(); + float a, b, c; + CK(cudaEventElapsedTime(&a, ev[0], ev[1])); CK(cudaEventElapsedTime(&b, ev[1], ev[2])); CK(cudaEventElapsedTime(&c, ev[2], ev[3])); + if (r) { t_rot.push_back(a); t_gu.push_back(b); t_dn.push_back(c); } + } + const double bits = k2 / 2.0, wbytes = 3.0 * E * D * I * bits / 8; + const double flop = 2.0 * P * 3.0 * D * I; + const double tr = median(t_rot), tg_ = median(t_gu), td_ = median(t_dn), tot = tr + tg_ + td_; + cudaDeviceProp prop{}; CK(cudaGetDeviceProperties(&prop, 0)); + int clk = 0; CK(cudaDeviceGetAttribute(&clk, cudaDevAttrClockRate, 0)); + printf("RESULT abl=%d mode=%s k2=%d rows=%d xm=%d gu=%d dn=%d zipf=%.2f passes=%d/%d grid=%d/%d (per_sm %d/%d) sms=%d clk=%dMHz hogs=%d\n", + abl, mode.c_str(), k2, rows, xm, gu, dn, zipf, pl.npass, pd.npass, gg, gd, per_g, per_d, emu.grid_sms(), clk / 1000, emu.hogs); + printf("RESULT ms rot %.3f gateup %.3f down %.3f total %.3f | weights %.3f GB -> %.1f GB/s | %.1f TFLOP/s\n", + tr, tg_, td_, tot, wbytes / 1e9, wbytes / (tot * 1e6), flop / (tot * 1e9)); + return 0; +} diff --git a/zig/kernels/cuda/deepseek_v41/ptxas.sh b/zig/kernels/cuda/deepseek_v41/ptxas.sh new file mode 100755 index 0000000000..a90929d20d --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/ptxas.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# Compiles DeepSeek-V4.1 kernel copies to fatbins in the NGC PyTorch image with -Xptxas -v; reports spills / stack. +set -euo pipefail +# usage: OUT=

SM=121,120 bash ptxas.sh name[:flags] ... (flags comma separated as in cuda.zig, e.g. "x3ld:-O3") +HERE="$(cd "$(dirname "$0")" && pwd)" +OUT="${OUT:?set OUT}" +SM="${SM:-121}" +IMAGE="${TF_ZIG_IMAGE:-nvcr.io/nvidia/pytorch:26.07-py3}" +mkdir -p "$OUT" +TORCH="-D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_BFLOAT16_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ --expt-relaxed-constexpr -std=c++20" +cmds="" +for spec in "$@"; do + name="${spec%%:*}"; flags="${spec#*:}"; [ "$flags" = "$spec" ] && flags="-O3" + gen="" + for sm in ${SM//,/ }; do gen="$gen -gencode=arch=compute_${sm},code=sm_${sm}"; done + cmds="$cmds nvcc -fatbin $TORCH ${flags//,/ } $gen -Xptxas -v -o /out/$name.fatbin /k/$name.cu > /out/$name.ptxas 2>&1 && echo built $name || { echo FAILED $name; tail -20 /out/$name.ptxas; } ;" +done +docker run --rm --network none --memory "${TF_ZIG_DOCKER_MEMORY:-12g}" --memory-swap "${TF_ZIG_DOCKER_MEMORY:-12g}" -u "$(id -u):$(id -g)" -v "$HERE:/k:ro" -v "$OUT:/out" --entrypoint bash "$IMAGE" -c "$cmds" +for spec in "$@"; do + name="${spec%%:*}" + f="$OUT/$name.ptxas" + [ -f "$f" ] || continue + fns=$(grep -c "Compiling entry function" "$f" || true) + spill=$(grep -E "bytes spill (stores|loads)" "$f" | grep -vc " 0 bytes spill stores, 0 bytes spill loads" || true) + stack=$(grep -E "bytes stack frame" "$f" | grep -vc " 0 bytes stack frame" || true) + echo "$name: $fns entry functions; with spills: $spill; with a stack frame: $stack" +done diff --git a/zig/kernels/cuda/deepseek_v41/router_gemv.cu b/zig/kernels/cuda/deepseek_v41/router_gemv.cu new file mode 100644 index 0000000000..60db87747a --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/router_gemv.cu @@ -0,0 +1,521 @@ +// Copy of src/tensorfold/families/deepseek_v41/cuda/router_gemv.cu (blob 5841ced53bf5): sass_check.sh +#define DSV41_RG_NO_TORCH + + + +// DeepSeek-V4.1 MoE router, one GEMV + top-K launch: each logit is one fp32 FMA chain + xor butterfly, any tiling +#include +#include +#include + +namespace dsv41_rg { + +constexpr int NV = 20; // 16-byte vectors a lane a gate row: D = 32 x 8 x NV = 5,120 +constexpr int D = 32 * 8 * NV; +constexpr int JC = 2; // vectors a lane a chunk +constexpr int NC = NV / JC; // chunks of x a row tile +constexpr int CK = 32 * 8 * JC; // k a chunk (512) +constexpr int EPL = 16; // experts a lane in the selection: E <= 512 +constexpr int KMAX = 8; // top-k at most +static_assert(NV % JC == 0, "whole chunks"); + +__device__ __forceinline__ uint4 ldg_stream(const uint16_t* p) { + uint4 v; + asm volatile("ld.global.nc.L1::no_allocate.v4.u32 {%0,%1,%2,%3}, [%4];" + : "=r"(v.x), "=r"(v.y), "=r"(v.z), "=r"(v.w) + : "l"(p)); + return v; +} + +// 8 bf16 (k ascending: the low half of each word first) -> fp32, exactly +__device__ __forceinline__ void widen8(const uint4 v, float (&f)[8]) { + f[0] = __uint_as_float(v.x << 16); f[1] = __uint_as_float(v.x & 0xffff0000u); + f[2] = __uint_as_float(v.y << 16); f[3] = __uint_as_float(v.y & 0xffff0000u); + f[4] = __uint_as_float(v.z << 16); f[5] = __uint_as_float(v.z & 0xffff0000u); + f[6] = __uint_as_float(v.w << 16); f[7] = __uint_as_float(v.w & 0xffff0000u); +} + +// the reference router's safe softplus, operation for operation (no contraction: the intrinsics are never fused) +__device__ __forceinline__ float softplus(float z) { + const float e = expf(fminf(z, 20.0f)); + if (z > 20.0f) return z; + if (z < -10.0f) return __fsub_rn(e, __fmul_rn(0.5f, __fmul_rn(e, e))); + return logf(__fadd_rn(1.0f, e)); +} + +// TF_DSV41_RG_PRUNE: the top-p prune folded into the selection, its operations in order on the row's K weights +struct Prune { + float min_w, topp, scale; + int min_k, orig, drop, dup, on; +}; + +__device__ __forceinline__ void prune_row(const Prune& P, int K, float (&w)[KMAX], int (&p)[KMAX]) { + float total = 0.0f, f[KMAX], excl[KMAX]; + int rank[KMAX]; + bool keep[KMAX]; +#pragma unroll + for (int i = 0; i < KMAX; ++i) if (i < K) total = __fadd_rn(total, w[i]); +#pragma unroll + for (int i = 0; i < KMAX; ++i) if (i < K) f[i] = __fdiv_rn(w[i], total); +#pragma unroll + for (int i = 0; i < KMAX; ++i) { + rank[i] = 0; +#pragma unroll + for (int j = 0; j < KMAX; ++j) + if (i < K && j < K) rank[i] += (f[j] > f[i]) || (f[j] == f[i] && j < i); + } + float acc = 0.0f; +#pragma unroll + for (int r = 0; r < KMAX; ++r) +#pragma unroll + for (int i = 0; i < KMAX; ++i) + if (r < K && i < K && rank[i] == r) { excl[i] = acc; acc = __fadd_rn(acc, f[i]); } + int nk = 0, top = 0; + float ks = 0.0f; +#pragma unroll + for (int i = 0; i < KMAX; ++i) { + if (i >= K) break; + keep[i] = ((excl[i] < P.topp) && (f[i] >= P.min_w)) || (rank[i] < P.min_k); + nk += keep[i]; + if (rank[i] == 0) top = p[i]; + ks = __fadd_rn(ks, keep[i] ? w[i] : 0.0f); + } + if (nk >= K) return; +#pragma unroll + for (int i = 0; i < KMAX; ++i) { + if (i >= K) break; + w[i] = keep[i] ? (P.orig ? w[i] : __fmul_rn(P.scale, __fdiv_rn(w[i], ks))) : 0.0f; + if (!keep[i]) p[i] = P.dup ? top : P.drop; + } +} + +// Prune pass of one selected row (lane 0); noinline so its arrays never share a live range with select_row's +__device__ __noinline__ void prune_pass(const Prune& P, int K, int kit, int* pick, float* wts, int* psh) { + float wv[KMAX]; + int ip[KMAX]; +#pragma unroll + for (int t = 0; t < KMAX; ++t) { + wv[t] = t < K ? wts[t] : 0.0f; + ip[t] = t < K ? pick[t] : 0; + } + prune_row(P, K, wv, ip); +#pragma unroll + for (int t = 0; t < KMAX; ++t) { + if (t >= K) break; + pick[t] = ip[t]; + wts[t] = kit ? __half2float(__float2half_rn(wv[t])) : wv[t]; + psh[t] = ip[t]; + } +} + +// One row's selection by one warp (lane l holds experts l + 32 i). +__device__ __forceinline__ void select_row(const float* lg, const float* __restrict__ bias, int E, int K, int slots, + float scale, int kit, int* pick, float* wts, int* psh, int lane, + const Prune& P) { + float s[EPL], ch[EPL]; +#pragma unroll + for (int i = 0; i < EPL; ++i) { + const int e = lane + 32 * i; + if (e < E) { + s[i] = __fsqrt_rn(softplus(__ldcg(lg + e))); + ch[i] = __fadd_rn(s[i], bias[e]); + } else { + s[i] = 0.0f; + ch[i] = -INFINITY; + } + } + float sp[KMAX]; + int ip[KMAX]; + float total = 0.0f; +#pragma unroll + for (int t = 0; t < KMAX; ++t) { + if (t >= K) break; + float bv = -INFINITY; + int bi = 0x7fffffff; +#pragma unroll + for (int i = 0; i < EPL; ++i) // ids ascend with i: strict > keeps the lower id on a tie + if (ch[i] > bv) { bv = ch[i]; bi = lane + 32 * i; } +#pragma unroll + for (int o = 16; o > 0; o >>= 1) { // (score desc, id asc): a total order, every lane ends equal + const float ov = __shfl_xor_sync(0xffffffffu, bv, o); + const int oi = __shfl_xor_sync(0xffffffffu, bi, o); + if (ov > bv || (ov == bv && oi < bi)) { bv = ov; bi = oi; } + } + float mine = 0.0f; +#pragma unroll + for (int i = 0; i < EPL; ++i) + if (lane + 32 * i == bi) { mine = s[i]; ch[i] = -INFINITY; } + sp[t] = __shfl_sync(0xffffffffu, mine, bi & 31); + ip[t] = bi; + total = __fadd_rn(total, sp[t]); + } + if (lane == 0) { +#pragma unroll + for (int t = 0; t < KMAX; ++t) { + if (t >= K) break; + float w = __fmul_rn(scale, __fdiv_rn(sp[t], total)); + if (kit && !P.on) w = __half2float(__float2half_rn(w)); // pick.kit_weights (with prune: after it) + pick[t] = ip[t]; + wts[t] = w; + psh[t] = ip[t]; + } + if (slots > K) { pick[K] = E; wts[K] = 1.0f; psh[K] = E; } + } +} + +struct Args { + const uint16_t* x; // [R, x_stride] bf16, rows 16-byte aligned + long long xs; + const uint16_t* w; // [E, D] bf16 + const float* bias; // [E] + int* pick; // [R, slots] + float* wts; // [R, slots] + float* lg; // [R, E] fp32 (scratch, or the caller's logits) + int* cnt; // [>= grid.y] int32, 0 between launches + int R, E, K, slots, select; + float scale; + int kit; // the routed weights rounded to fp16 (pick.kit_weights, folded) + int* gid; // group != 0: upstream's group kernel folded (one row tile only): distinct ids (< GE) + int* gcnt; // in id order, their count, members [., maxm] row * 32 + slot in row order, -1 after + int* gmem; + int GE, maxm, group; + Prune prune; // the top-p prune rule folded into the selection (on = 0: off) +}; + +constexpr int GE_MAX = 512; // table entries the folded grouping takes +constexpr int PICKS_MAX = 16 * (KMAX + 1); +constexpr int TAIL_INTS = PICKS_MAX; // the tail's picks (both kernels) +constexpr int GROUP_INTS = TAIL_INTS + 2 * GE_MAX + PICKS_MAX + 8; // + the grouping's (the narrow kernel) + +// Upstream's group_kernel (exl3/experts.cu) on the R <= 16 rows of one row tile, by the selecting CTA; integers only +template +__device__ __forceinline__ void group_rows(const Args& a, const int* psh, int* sh, int tid) { + constexpr int T = NW * 32; + int* gc = sh; // the main loop's shared memory, reused + int* gpl = gc + GE_MAX; + int* cpl = gpl + GE_MAX; + int* wtot = cpl + PICKS_MAX; + const int n = a.R * a.slots, GE = a.GE, lane = tid & 31, warp = tid >> 5; + for (int e = tid; e < GE; e += T) gc[e] = 0; + __syncthreads(); + for (int i = tid; i < n; i += T) { + const int e = psh[i]; + if (e >= 0 && e < GE) atomicAdd(gc + e, 1); + } + __syncthreads(); + const int per = (GE + T - 1) / T, lo = min(GE, tid * per), hi = min(GE, lo + per); + int used = 0; + for (int e = lo; e < hi; ++e) used += gc[e] > 0; + int inc = used; // inclusive scan in the warp, then over warps +#pragma unroll + for (int o = 1; o < 32; o <<= 1) { + const int v = __shfl_up_sync(0xffffffffu, inc, o); + if (lane >= o) inc += v; + } + if (lane == 31) wtot[warp] = inc; + __syncthreads(); + int base = 0, total = 0; +#pragma unroll + for (int w = 0; w < NW; ++w) { + base += w < warp ? wtot[w] : 0; + total += wtot[w]; + } + int place = base + inc - used; + for (int e = lo; e < hi; ++e) + if (gc[e] > 0) { + a.gid[place] = e; + gpl[e] = place; + cpl[place] = gc[e]; + ++place; + } + if (tid == 0) a.gcnt[0] = total; + __syncthreads(); + for (int i = tid; i < n; i += T) { // rank of pick i among its expert's picks: row order + const int e = psh[i]; + if (e < 0 || e >= GE) continue; + int rank = 0; + for (int q = 0; q < i; ++q) rank += psh[q] == e; + if (rank < a.maxm) a.gmem[(size_t)gpl[e] * a.maxm + rank] = (i / a.slots) * 32 + i % a.slots; + } + for (int q = tid; q < total * a.maxm; q += T) + if (q % a.maxm >= cpl[q / a.maxm]) a.gmem[q] = -1; +} + +// Fused tail: the row tile's last CTA selects its rows, groups them (GROUP), resets the counter; sh: TAIL_INTS ints +template +__device__ __forceinline__ void tail(const Args& a, int r0, int nr, int tid, int* sh) { + __shared__ int last_sh; + int* psh = sh; + const int lane = tid & 31, warp = tid >> 5; + __threadfence(); // this CTA's logits visible device-wide before its arrival + __syncthreads(); // (also: every thread is past the main loop's shared memory) + if (tid == 0) last_sh = atomicAdd(a.cnt + blockIdx.y, 1) == (int)gridDim.x - 1; + __syncthreads(); + if (!last_sh) return; + __threadfence(); + for (int r = warp; r < nr; r += NW) + select_row(a.lg + (size_t)(r0 + r) * a.E, a.bias, a.E, a.K, a.slots, a.scale, a.kit, + a.pick + (size_t)(r0 + r) * a.slots, a.wts + (size_t)(r0 + r) * a.slots, psh + r * a.slots, lane, + a.prune); + if (a.prune.on && lane == 0) // the rule, then the kit rounding (each lane 0: its own rows) + for (int r = warp; r < nr; r += NW) + prune_pass(a.prune, a.K, a.kit, a.pick + (size_t)(r0 + r) * a.slots, a.wts + (size_t)(r0 + r) * a.slots, + psh + r * a.slots); + if (GROUP && a.group) { + __syncthreads(); + group_rows(a, psh, sh + TAIL_INTS, tid); + } + if (tid == 0) a.cnt[blockIdx.y] = 0; // self-cleaning +} + +// RT rows a tile (1 .. 16), NW warps a CTA, EW experts a warp. +template +__global__ void __launch_bounds__(NW * 32) gemv_kernel(const Args a) { + constexpr int PD = 8 / EW; // vectors in flight a lane, per expert + constexpr int XU = RT * CK / 8; // uint4 of x a chunk + constexpr int XT = (XU + NW * 32 - 1) / (NW * 32); + static_assert(PD < NV, "ring shorter than a row"); + __shared__ __align__(16) uint16_t xsh[2][RT][CK]; + + const int tid = threadIdx.x, lane = tid & 31, warp = tid >> 5; + const int r0 = blockIdx.y * RT; + const int nr = min(RT, a.R - r0); + const int e0 = (blockIdx.x * NW + warp) * EW; + + auto wload = [&](int j, uint4 (&dst)[EW]) { +#pragma unroll + for (int e = 0; e < EW; ++e) { + if (e0 + e < a.E) dst[e] = ldg_stream(a.w + (size_t)(e0 + e) * D + (size_t)(j * 32 + lane) * 8); + else dst[e] = make_uint4(0u, 0u, 0u, 0u); + } + }; + auto xload = [&](int c, uint4 (&xr)[XT]) { +#pragma unroll + for (int q = 0; q < XT; ++q) { + const int u = tid + NW * 32 * q, row = u / (CK / 8), col = (u % (CK / 8)) * 8; + xr[q] = make_uint4(0u, 0u, 0u, 0u); + if (u < XU && row < nr) + xr[q] = __ldg(reinterpret_cast(a.x + (size_t)(r0 + row) * a.xs + c * CK + col)); + } + }; + auto xstore = [&](int buf, const uint4 (&xr)[XT]) { +#pragma unroll + for (int q = 0; q < XT; ++q) { + const int u = tid + NW * 32 * q, row = u / (CK / 8), col = (u % (CK / 8)) * 8; + if (u < XU) *reinterpret_cast(&xsh[buf][row][col]) = xr[q]; + } + }; + + float acc[EW][RT]; +#pragma unroll + for (int e = 0; e < EW; ++e) +#pragma unroll + for (int r = 0; r < RT; ++r) acc[e][r] = 0.0f; + uint4 ring[PD][EW]; +#pragma unroll + for (int j = 0; j < PD; ++j) wload(j, ring[j]); + uint4 xr[XT]; + xload(0, xr); + xstore(0, xr); + __syncthreads(); + +#pragma unroll + for (int j = 0; j < NV; ++j) { + const int c = j / JC, jj = j % JC; + if (jj == 0 && c + 1 < NC) xload(c + 1, xr); // next chunk's x: L2, consumed at the chunk's end + float wf[EW][8]; +#pragma unroll + for (int e = 0; e < EW; ++e) widen8(ring[j % PD][e], wf[e]); + if (j + PD < NV) wload(j + PD, ring[j % PD]); // the slot refilled PD steps ahead +#pragma unroll + for (int r = 0; r < RT; ++r) { + float xf[8]; + widen8(*reinterpret_cast(&xsh[c & 1][r][(jj * 32 + lane) * 8]), xf); +#pragma unroll + for (int e = 0; e < EW; ++e) +#pragma unroll + for (int i = 0; i < 8; ++i) acc[e][r] = __fmaf_rn(xf[i], wf[e][i], acc[e][r]); + } + if (jj == JC - 1) { + if (c + 1 < NC) xstore((c + 1) & 1, xr); // the buffer chunk c - 1 read (all past its barrier) + __syncthreads(); + } + } + +#pragma unroll + for (int e = 0; e < EW; ++e) +#pragma unroll + for (int r = 0; r < RT; ++r) { + float v = acc[e][r]; +#pragma unroll + for (int o = 16; o > 0; o >>= 1) v = __fadd_rn(v, __shfl_xor_sync(0xffffffffu, v, o)); + if (lane == r && r < nr && e0 + e < a.E) a.lg[(size_t)(r0 + r) * a.E + e0 + e] = v; + } + static_assert(sizeof(xsh) >= TAIL_INTS * sizeof(int), "the tail's picks fit the staged x"); + if (a.select) tail(a, r0, nr, tid, reinterpret_cast(&xsh[0][0][0])); +} + +#include "router_narrow.cuh" + +template +cudaError_t launch_narrow(const Args& a, cudaStream_t stream) { + static const bool carved = [] { // the most shared memory a SM (8 / NW CTAs resident at once); a + cudaFuncSetAttribute(narrow_kernel, // preference only: an error here is cleared, never fatal + cudaFuncAttributePreferredSharedMemoryCarveout, (int)cudaSharedmemCarveoutMaxShared); + (void)cudaGetLastError(); + return true; + }(); + (void)carved; + const dim3 grid((unsigned)((a.E + NW - 1) / NW), (unsigned)((a.R + RT - 1) / RT)); + narrow_kernel<<>>(a); + return cudaGetLastError(); +} + +template +cudaError_t by_rt_narrow(const Args& a, int rt, cudaStream_t stream) { + switch (rt) { + case 1: return launch_narrow<1, NW>(a, stream); + case 2: return launch_narrow<2, NW>(a, stream); + case 4: return launch_narrow<4, NW>(a, stream); + case 8: return launch_narrow<8, NW>(a, stream); + case 16: return launch_narrow<16, NW>(a, stream); + default: return cudaErrorInvalidValue; + } +} + +template +cudaError_t launch(const Args& a, cudaStream_t stream) { + const dim3 grid((unsigned)((a.E + NW * EW - 1) / (NW * EW)), (unsigned)((a.R + RT - 1) / RT)); + gemv_kernel<<>>(a); + return cudaGetLastError(); +} + +template +cudaError_t by_rt(const Args& a, int rt, cudaStream_t stream) { + switch (rt) { + case 1: return launch<1, NW, EW>(a, stream); + case 2: return launch<2, NW, EW>(a, stream); + case 4: return launch<4, NW, EW>(a, stream); + case 8: return launch<8, NW, EW>(a, stream); + case 16: return launch<16, NW, EW>(a, stream); + default: return cudaErrorInvalidValue; + } +} + +// rt rows a tile (1, 2, 4, 8, 16); (nw, ew) in {4, 8} x {1, 2}, or ew == 0: narrow, nw in {1, 2, 4, 8} +inline cudaError_t dispatch(const Args& a, int rt, int nw, int ew, cudaStream_t stream) { + if (ew == 0) { + switch (nw) { + case 1: return by_rt_narrow<1>(a, rt, stream); + case 2: return by_rt_narrow<2>(a, rt, stream); + case 4: return by_rt_narrow<4>(a, rt, stream); + case 8: return by_rt_narrow<8>(a, rt, stream); + default: return cudaErrorInvalidValue; + } + } + if (nw == 8 && ew == 1) return by_rt<8, 1>(a, rt, stream); + if (nw == 4 && ew == 1) return by_rt<4, 1>(a, rt, stream); + if (nw == 8 && ew == 2) return by_rt<8, 2>(a, rt, stream); + if (nw == 4 && ew == 2) return by_rt<4, 2>(a, rt, stream); + return cudaErrorInvalidValue; +} + +} // namespace dsv41_rg + +#ifndef DSV41_RG_NO_TORCH // the compile test builds the kernels alone (nvcc, no torch headers) +#include +#include +#include + +void dsv41_rg_route_cuda(const at::Tensor& X, const at::Tensor& W, const at::Tensor& B, at::Tensor& pick, + at::Tensor& wts, at::Tensor& LG, at::Tensor& cnt, int64_t K, int64_t slots, double scale, + int64_t rt, int64_t nw, int64_t ew, bool select, bool kit, at::Tensor& gid, at::Tensor& gcnt, + at::Tensor& gmem, int64_t GE, bool group, const std::vector& prune) { + using namespace dsv41_rg; + TORCH_CHECK(prune.empty() || prune.size() == 7, "router_gemv: prune = [min_w, topp, scale, min_k, orig, drop, dup]"); + const int64_t R = X.size(0), E = W.size(0); + TORCH_CHECK(X.size(1) == D && W.size(1) == D, "router_gemv: D must be ", D); + TORCH_CHECK(X.stride(1) == 1 && X.stride(0) % 8 == 0 && reinterpret_cast(X.data_ptr()) % 16 == 0, + "router_gemv: x rows must be unit-stride and 16-byte aligned"); + TORCH_CHECK(E >= 1 && E <= 32 * EPL && K >= 1 && K <= KMAX && K <= E && slots >= K && slots <= K + 1, + "router_gemv: E <= ", 32 * EPL, ", 1 <= K <= ", KMAX, ", slots K or K + 1"); + TORCH_CHECK(LG.numel() >= R * E, "router_gemv: logits buffer too small"); + TORCH_CHECK(!select || cnt.numel() >= (R + rt - 1) / rt, "router_gemv: counters too few for ", R, " rows"); + const bool wide = (nw == 4 || nw == 8) && (ew == 1 || ew == 2); + const bool narrow = ew == 0 && (nw == 1 || nw == 2 || nw == 4 || nw == 8); + TORCH_CHECK((rt == 1 || rt == 2 || rt == 4 || rt == 8 || rt == 16) && (wide || narrow), + "router_gemv: no instance (rt, nw, ew) = (", rt, ", ", nw, ", ", ew, ")"); + TORCH_CHECK(!group || (select && narrow && R <= rt && GE >= 1 && GE <= GE_MAX && gid.numel() >= std::min(R * slots, GE) + && gcnt.numel() >= 1 && gmem.dim() == 2 && gmem.size(0) >= std::min(R * slots, GE) + && gmem.size(1) >= 1), + "router_gemv: the folded grouping takes one row tile, GE <= ", GE_MAX, " and ids / members sized"); + if (R == 0) return; + Args a; + a.x = reinterpret_cast(X.data_ptr()); + a.xs = X.stride(0); + a.w = reinterpret_cast(W.data_ptr()); + a.bias = B.data_ptr(); + a.pick = pick.data_ptr(); + a.wts = wts.data_ptr(); + a.lg = LG.data_ptr(); + a.cnt = cnt.data_ptr(); + a.R = (int)R; a.E = (int)E; a.K = (int)K; a.slots = (int)slots; a.select = select ? 1 : 0; + a.scale = (float)scale; + a.kit = kit ? 1 : 0; + a.group = group ? 1 : 0; + a.gid = group ? gid.data_ptr() : nullptr; + a.gcnt = group ? gcnt.data_ptr() : nullptr; + a.gmem = group ? gmem.data_ptr() : nullptr; + a.GE = (int)GE; + a.maxm = group ? (int)gmem.size(1) : 0; // upstream's Scratch.window: members [maxu, R] + a.prune = Prune{}; + if (!prune.empty()) { // fp32 thresholds: the rounding of Triton's float arguments + a.prune.min_w = (float)prune[0]; a.prune.topp = (float)prune[1]; a.prune.scale = (float)prune[2]; + a.prune.min_k = (int)prune[3]; a.prune.orig = (int)prune[4]; a.prune.drop = (int)prune[5]; + a.prune.dup = (int)prune[6]; a.prune.on = 1; + } + C10_CUDA_CHECK(dispatch(a, (int)rt, (int)nw, (int)ew, at::cuda::getCurrentCUDAStream())); +} +#endif + +// The instantiations dispatch() launches: gemv_kernel and narrow_kernel. +template __global__ void dsv41_rg::gemv_kernel<1, 8, 1>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<1, 4, 1>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<1, 8, 2>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<1, 4, 2>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<1, 1>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<1, 2>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<1, 4>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<1, 8>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<2, 8, 1>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<2, 4, 1>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<2, 8, 2>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<2, 4, 2>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<2, 1>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<2, 2>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<2, 4>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<2, 8>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<4, 8, 1>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<4, 4, 1>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<4, 8, 2>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<4, 4, 2>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<4, 1>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<4, 2>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<4, 4>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<4, 8>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<8, 8, 1>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<8, 4, 1>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<8, 8, 2>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<8, 4, 2>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<8, 1>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<8, 2>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<8, 4>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<8, 8>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<16, 8, 1>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<16, 4, 1>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<16, 8, 2>(const dsv41_rg::Args); +template __global__ void dsv41_rg::gemv_kernel<16, 4, 2>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<16, 1>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<16, 2>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<16, 4>(const dsv41_rg::Args); +template __global__ void dsv41_rg::narrow_kernel<16, 8>(const dsv41_rg::Args); diff --git a/zig/kernels/cuda/deepseek_v41/router_glue.cu b/zig/kernels/cuda/deepseek_v41/router_glue.cu new file mode 100644 index 0000000000..5e39b67d95 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/router_glue.cu @@ -0,0 +1,115 @@ +// Decode router glue, arithmetic of exl3/experts.cu (blob 8ce66ca83bce); -O3, fmad on, ftz off; R <= 64, K == 5120. +#include +#include +#include +#include + +namespace dsv41_router_glue { +constexpr int THREADS = 1024; +constexpr int WARPS = THREADS / 32; +constexpr int MAX_E = 512; +constexpr int MAX_PICKS = 64 * 9; +constexpr float HAD_SCALE = 0.08838834764831845f; + +// Verbatim arithmetic from tf_exl3_experts::fwht128 at the source above. +__device__ __forceinline__ void fwht128(float (&v)[4], int lane) { + float a = v[0] + v[1], b = v[0] - v[1], c = v[2] + v[3], d = v[2] - v[3]; + v[0] = a + c; v[1] = b + d; v[2] = a - c; v[3] = b - d; +#pragma unroll + for (int m = 1; m < 32; m <<= 1) { +#pragma unroll + for (int j = 0; j < 4; ++j) { + float o = __shfl_xor_sync(0xffffffffu, v[j], m); + v[j] = (lane & m) ? o - v[j] : v[j] + o; + } + } +} + +// Integer-equal to upstream group_kernel (histogram, id scan, earlier equal picks; duplicates past maxm truncated). +__device__ __forceinline__ void group_rows(const int* pick, int* uids, int* ucount, + int* members, int R, int slots, int E, int maxm) { + __shared__ int picks[MAX_PICKS], count[MAX_E], place[MAX_E], warp_tot[WARPS]; + const int tid = threadIdx.x, lane = tid & 31, warp = tid >> 5; + const int n = R * slots; + if (tid < n) picks[tid] = pick[tid]; + if (tid < E) count[tid] = 0; + __syncthreads(); + if (tid < n) { + const int e = picks[tid]; + if (e >= 0 && e < E) atomicAdd(count + e, 1); + } + __syncthreads(); + const int used = tid < E && count[tid] > 0; + int inc = used; +#pragma unroll + for (int o = 1; o < 32; o <<= 1) { + const int v = __shfl_up_sync(0xffffffffu, inc, o); + if (lane >= o) inc += v; + } + if (lane == 31) warp_tot[warp] = inc; + __syncthreads(); + if (warp == 0) { + const int v = warp_tot[lane]; + int s = v; +#pragma unroll + for (int o = 1; o < 32; o <<= 1) { + const int x = __shfl_up_sync(0xffffffffu, s, o); + if (lane >= o) s += x; + } + warp_tot[lane] = s - v; + if (lane == 31) ucount[0] = s; + } + __syncthreads(); + if (used) { + const int p = warp_tot[warp] + inc - used; + uids[p] = tid; + place[tid] = p; + } + __syncthreads(); + if (tid < n) { + const int e = picks[tid]; + if (e >= 0 && e < E) { + int rank = 0; + for (int q = 0; q < tid; ++q) rank += picks[q] == e; + if (rank < maxm) members[(size_t)place[e] * maxm + rank] = (tid / slots) * 32 + tid % slots; + } + } + if (used) + for (int j = count[tid]; j < maxm; ++j) members[(size_t)place[tid] * maxm + j] = -1; +} +} // namespace dsv41_router_glue + +// One CTA, block 1024, no dynamic shared memory. Same argument order as group. +extern "C" __global__ void __launch_bounds__(1024) router_group( + const int* pick, int* uids, int* ucount, int* members, int R, int slots, int E, int maxm) { + dsv41_router_glue::group_rows(pick, uids, ucount, members, R, slots, E, maxm); +} + +// grid.x = 1 + ceil(R slots (K / 128) 2 / 32), block 1024: CTA 0 groups, other warps rotate one (pick, block, mat). +extern "C" __global__ void __launch_bounds__(1024) router_group_rot( + const uint16_t* x, int x_stride, const int* pick, const half* suh0, const half* suh1, + half* out0, half* out1, int* uids, int* ucount, int* members, + int R, int K, int slots, int E, int maxm) { + using namespace dsv41_router_glue; + if (blockIdx.x == 0) { + group_rows(pick, uids, ucount, members, R, slots, E, maxm); + return; + } + const int lane = threadIdx.x & 31, warp = threadIdx.x >> 5; + const int tile = (blockIdx.x - 1) * WARPS + warp; + const int P = R * slots, blocks = K / 128; + if (tile >= P * blocks * 2) return; // uniform within each warp + const int p = tile % P, blk = (tile / P) % blocks, mat = tile / (P * blocks); + const int row = p / slots, e = pick[p]; + if (e < 0 || e >= E) return; // invalid picks leave outputs untouched + const half* suh = (mat ? suh1 : suh0) + (size_t)e * K + blk * 128 + 4 * lane; + const __nv_bfloat16* xr = reinterpret_cast(x) + + (size_t)row * x_stride + blk * 128 + 4 * lane; + float v[4]; +#pragma unroll + for (int j = 0; j < 4; ++j) v[j] = __bfloat162float(xr[j]) * __half2float(suh[j]); + fwht128(v, lane); + half* o = (mat ? out1 : out0) + (size_t)p * K + blk * 128 + 4 * lane; +#pragma unroll + for (int j = 0; j < 4; ++j) o[j] = __float2half_rn(v[j] * HAD_SCALE); +} diff --git a/zig/kernels/cuda/deepseek_v41/router_narrow.cuh b/zig/kernels/cuda/deepseek_v41/router_narrow.cuh new file mode 100644 index 0000000000..2b38f41edb --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/router_narrow.cuh @@ -0,0 +1,96 @@ +// router_gemv.cu's narrow_kernel and its cp.async helpers, inside namespace dsv41_rg +#pragma once +// x through L1 (ld.global.nc), volatile so it stays in the step loop (a plain __ldg was hoisted, spilling registers) +__device__ __forceinline__ uint4 ldg_keep(const uint16_t* p) { + uint4 v; + asm volatile("ld.global.nc.v4.u32 {%0,%1,%2,%3}, [%4];" : "=r"(v.x), "=r"(v.y), "=r"(v.z), "=r"(v.w) : "l"(p)); + return v; +} + +__device__ __forceinline__ void cp_async16(void* dst, const void* src) { + const unsigned d = (unsigned)__cvta_generic_to_shared(dst); + asm volatile("cp.async.cg.shared.global [%0], [%1], 16;" ::"r"(d), "l"(src) : "memory"); +} +__device__ __forceinline__ void cp_commit() { asm volatile("cp.async.commit_group;" ::: "memory"); } +template +__device__ __forceinline__ void cp_wait() { asm volatile("cp.async.wait_group %0;" ::"n"(N) : "memory"); } +// n a constant once the step loop is unrolled: the switch folds to one wait_group +__device__ __forceinline__ void cp_wait_n(int n) { + switch (n) { +#define DSV41_RG_W(k) case k: cp_wait(); break; + DSV41_RG_W(0) DSV41_RG_W(1) DSV41_RG_W(2) DSV41_RG_W(3) DSV41_RG_W(4) DSV41_RG_W(5) DSV41_RG_W(6) DSV41_RG_W(7) + DSV41_RG_W(8) DSV41_RG_W(9) DSV41_RG_W(10) DSV41_RG_W(11) DSV41_RG_W(12) DSV41_RG_W(13) DSV41_RG_W(14) + DSV41_RG_W(15) DSV41_RG_W(16) DSV41_RG_W(17) DSV41_RG_W(18) DSV41_RG_W(19) +#undef DSV41_RG_W + default: cp_wait<0>(); break; + } +} + +// Narrow kernel (TF_DSV41_RG_NARROW = NW, rows <= 16): gemv_kernel's chains and bits; gate via cp.async, x via L1 +template +__global__ void __launch_bounds__(NW * 32) narrow_kernel(const Args a) { + constexpr int PD = NW <= 4 && RT <= 4 ? NV : NV / 2; // gate vectors in flight a lane + constexpr int XP = RT <= 4 ? 2 : 0; // steps of x in flight (0: loaded at use, L1) + constexpr int TS = (GROUP_INTS * 4 + NW * 512 - 1) / (NW * 512); // slots the tail needs + __shared__ __align__(16) uint4 wsh[NW][PD > TS ? PD : TS][32]; + const int tid = threadIdx.x, lane = tid & 31, warp = tid >> 5; + const int r0 = blockIdx.y * RT; + const int nr = min(RT, a.R - r0); + const int e = blockIdx.x * NW + warp; + const bool live = e < a.E; + const uint16_t* wrow = a.w + (size_t)(live ? e : 0) * D + (size_t)lane * 8; + + auto wload = [&](int j) { // a dead warp's slots are never read: committed empty + if (live) cp_async16(&wsh[warp][j % PD][lane], wrow + (size_t)j * 256); + cp_commit(); + }; + auto xload = [&](int j, uint4 (&dst)[RT]) { +#pragma unroll + for (int r = 0; r < RT; ++r) + dst[r] = (live && r < nr) + ? ldg_keep(a.x + (size_t)(r0 + r) * a.xs + (size_t)(j * 32 + lane) * 8) + : make_uint4(0u, 0u, 0u, 0u); + }; + +#pragma unroll + for (int j = 0; j < PD; ++j) wload(j); + uint4 xr[XP > 0 ? XP : 1][RT]; +#pragma unroll + for (int j = 0; j < XP; ++j) xload(j, xr[j]); + float acc[RT]; +#pragma unroll + for (int r = 0; r < RT; ++r) acc[r] = 0.0f; + +#pragma unroll + for (int j = 0; j < NV; ++j) { + // groups committed before step j: min(NV, PD + j); group j done = at most that - j - 1 pending + cp_wait_n(min(NV - j - 1, PD - 1)); + float wf[8]; + widen8(wsh[warp][j % PD][lane], wf); + uint4 xc[RT]; + if constexpr (XP == 0) { + xload(j, xc); + } else { +#pragma unroll + for (int r = 0; r < RT; ++r) xc[r] = xr[j % XP][r]; + if (j + XP < NV) xload(j + XP, xr[j % XP]); + } +#pragma unroll + for (int r = 0; r < RT; ++r) { + float xf[8]; + widen8(xc[r], xf); +#pragma unroll + for (int i = 0; i < 8; ++i) acc[r] = __fmaf_rn(xf[i], wf[i], acc[r]); + } + if (j + PD < NV) wload(j + PD); // the slot just read (its value is in wf: used above) + } + +#pragma unroll + for (int r = 0; r < RT; ++r) { + float v = acc[r]; +#pragma unroll + for (int o = 16; o > 0; o >>= 1) v = __fadd_rn(v, __shfl_xor_sync(0xffffffffu, v, o)); + if (lane == r && r < nr && live) a.lg[(size_t)(r0 + r) * a.E + e] = v; + } + if (a.select) tail(a, r0, nr, tid, reinterpret_cast(&wsh[0][0][0])); +} diff --git a/zig/kernels/cuda/deepseek_v41/sampling.cu b/zig/kernels/cuda/deepseek_v41/sampling.cu new file mode 100644 index 0000000000..e8cc52392f --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/sampling.cu @@ -0,0 +1,152 @@ +// DeepSeek-V4.1 keyed sampling, device part; --fmad=false: torch rounds v / t, the subtraction and exp separately. + +// stats: per-row (m, s) in float64, sum order not torch's (the keep-count guard absorbs it); pack: [vals, cols + id0]. + +#include + +namespace dsv41_sampling { + +constexpr int NT = 1024; + +__device__ __forceinline__ double warp_max(double v) { + for (int o = 16; o > 0; o >>= 1) { + const double u = __shfl_xor_sync(0xFFFFFFFFu, v, o); + v = u > v ? u : v; + } + return v; +} + +__device__ __forceinline__ double warp_sum(double v) { + for (int o = 16; o > 0; o >>= 1) v += __shfl_xor_sync(0xFFFFFFFFu, v, o); + return v; +} + +// One CTA a row: rows [rows, C] fp32 (row stride ld), out float64 [rows, 2]. +__global__ void __launch_bounds__(NT) stats_kernel(const float* __restrict__ lg, long long ld, int C, double t, + double* __restrict__ out) { + __shared__ double red[NT / 32]; + __shared__ double s_m; + const int tid = threadIdx.x, lane = tid & 31, warp = tid >> 5; + const float* row = lg + (long long)blockIdx.x * ld; + double m = -__longlong_as_double(0x7FF0000000000000LL); // -inf + for (int c = tid; c < C; c += NT) { + const double v = (double)row[c]; + m = v > m ? v : m; + } + m = warp_max(m); + if (lane == 0) red[warp] = m; + __syncthreads(); + if (warp == 0) { + double v = red[lane]; + v = warp_max(v); + if (lane == 0) s_m = v; + } + __syncthreads(); + const double mx = s_m; + const double mt = mx / t; // (m / t)[:, None] + double s = 0.0; + for (int c = tid; c < C; c += NT) s += exp((double)row[c] / t - mt); + s = warp_sum(s); + __syncthreads(); // red is reused + if (lane == 0) red[warp] = s; + __syncthreads(); + if (warp == 0) { + double v = red[lane]; + v = warp_sum(v); + if (lane == 0) { + out[2 * (long long)blockIdx.x] = mx; + out[2 * (long long)blockIdx.x + 1] = v; + } + } +} + +// One CTA a row: vals fp32 [rows, k], cols int64 [rows, k] -> out fp32 [rows, 2k]. +__global__ void __launch_bounds__(256) pack_kernel(const float* __restrict__ vals, const long long* __restrict__ cols, + int k, int id0, float* __restrict__ out) { + const long long r = blockIdx.x; + for (int j = threadIdx.x; j < k; j += blockDim.x) { + out[r * 2 * k + j] = vals[r * k + j]; + out[r * 2 * k + k + j] = __int_as_float((int)cols[r * k + j] + id0); + } +} + +} // namespace dsv41_sampling + +// Split stats: same (m, s) as stats_kernel, sum in fixed chunk order; max, sum per chunk, fin adds chunks in order. +namespace dsv41_sampling_split { + +constexpr int NT = 256; + +__device__ __forceinline__ float block_max(float v, float* red) { + for (int o = 16; o > 0; o >>= 1) { + const float u = __shfl_xor_sync(0xFFFFFFFFu, v, o); + v = u > v ? u : v; + } + const int lane = threadIdx.x & 31, warp = threadIdx.x >> 5; + if (lane == 0) red[warp] = v; + __syncthreads(); + v = lane < NT / 32 ? red[lane] : -__int_as_float(0x7F800000); + for (int o = 16; o > 0; o >>= 1) { + const float u = __shfl_xor_sync(0xFFFFFFFFu, v, o); + v = u > v ? u : v; + } + return v; // valid in warp 0 +} + +// the row's max from its chunk maxima, every thread +__device__ __forceinline__ float row_max(const float* pm, int chunks) { + float m = -__int_as_float(0x7F800000); + for (int j = 0; j < chunks; ++j) m = pm[j] > m ? pm[j] : m; + return m; +} + +} // namespace dsv41_sampling_split + +extern "C" __global__ void __launch_bounds__(dsv41_sampling_split::NT) +dsv41_sampling_stats_max(const float* __restrict__ lg, long long ld, int C, int chunk, float* __restrict__ pmax) { + using namespace dsv41_sampling_split; + __shared__ float red[NT / 32]; + const float* row = lg + (long long)blockIdx.y * ld; + const int c0 = blockIdx.x * chunk, c1 = min(C, c0 + chunk); + float m = -__int_as_float(0x7F800000); + for (int c = c0 + threadIdx.x; c < c1; c += NT) { + const float v = row[c]; + m = v > m ? v : m; + } + m = block_max(m, red); + if (threadIdx.x == 0) pmax[(long long)blockIdx.y * gridDim.x + blockIdx.x] = m; +} + +extern "C" __global__ void __launch_bounds__(dsv41_sampling_split::NT) +dsv41_sampling_stats_sum(const float* __restrict__ lg, long long ld, int C, int chunk, double t, + const float* __restrict__ pmax, double* __restrict__ psum) { + using namespace dsv41_sampling_split; + __shared__ double red[NT / 32]; + const int chunks = gridDim.x; + const float* row = lg + (long long)blockIdx.y * ld; + const double mt = (double)row_max(pmax + (long long)blockIdx.y * chunks, chunks) / t; + const int c0 = blockIdx.x * chunk, c1 = min(C, c0 + chunk); + double s = 0.0; + for (int c = c0 + threadIdx.x; c < c1; c += NT) s += exp((double)row[c] / t - mt); + for (int o = 16; o > 0; o >>= 1) s += __shfl_xor_sync(0xFFFFFFFFu, s, o); + const int lane = threadIdx.x & 31, warp = threadIdx.x >> 5; + if (lane == 0) red[warp] = s; + __syncthreads(); + if (threadIdx.x == 0) { + double v = 0.0; + for (int w = 0; w < NT / 32; ++w) v += red[w]; + psum[(long long)blockIdx.y * chunks + blockIdx.x] = v; + } +} + +extern "C" __global__ void __launch_bounds__(32) +dsv41_sampling_stats_fin(const float* __restrict__ pmax, const double* __restrict__ psum, int chunks, + double* __restrict__ out) { + using namespace dsv41_sampling_split; + if (threadIdx.x != 0) return; + const long long i = blockIdx.x; + double s = 0.0; + for (int j = 0; j < chunks; ++j) s += psum[i * chunks + j]; + out[2 * i] = (double)row_max(pmax + i * chunks, chunks); + out[2 * i + 1] = s; +} diff --git a/zig/kernels/cuda/deepseek_v41/topk_b.cu b/zig/kernels/cuda/deepseek_v41/topk_b.cu new file mode 100644 index 0000000000..8f2d30d836 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/topk_b.cu @@ -0,0 +1,183 @@ + + +// Row-bounded decode top-k (TF_DSV41_INDEX_BOUND): topk_kernel with each row cut at its visible end, same selection. + +// Bound B: mode 0 min(nk, max(nvis, k)), mode 2 min(nk, cdiv(nvis, bs)), mode 3 nk; past-B entries rank below all kept. + +// Every barrier and cluster sync is kept (a CTA with no entries still takes part); other lines are topk_kernel's. + +#include "topk_cuda.cu" + +namespace dsv41_topk { + +__global__ void __launch_bounds__(NT, 1) topk_b_kernel(Args a) { + cg::cluster_group cl = cg::this_cluster(); + const Job J = blockIdx.z == 0 ? a.job[0] : a.job[1]; // (no dynamic indexing: no local memory) + const int CL = static_cast(cl.num_blocks()), rank = static_cast(cl.block_rank()); + const int r = blockIdx.y, tid = threadIdx.x, lane = tid & 31; + extern __shared__ uint32_t img[]; // SL = 512 x ept images (0: missing) + __shared__ int hist[2][256]; + __shared__ int tot[256]; + __shared__ int ws[2 * NW]; + __shared__ int ex[2]; // (above, equal) of this CTA, read by the cluster + __shared__ int dec[4]; + const int q = a.pos64 ? static_cast(static_cast(a.pos)[r]) + : static_cast(a.pos)[0] + r; + const int nvis = (q + 1) / a.ratio; + // the row's bound (above): entries [B, nk) are never read + const int B = J.mode == 0 ? min(J.nk, max(nvis, J.k)) + : J.mode == 2 ? min(J.nk, max(0, (nvis + a.bs - 1) / a.bs)) + : J.nk; + const int SL = NT * J.ept, lo = rank * SL, n = max(0, min(SL, B - lo)); + const int nr = (n + NT - 1) / NT * NT; // the warp-collective loops' end (<= SL) + if (rank == 0 && tid == 0 && J.cnt) J.cnt[r] = 0; // before the first cluster barrier (release) + const float* srow = J.s + static_cast(r) * J.ss; + const int* crow = J.cand ? J.cand + static_cast(r) * J.cs : nullptr; + for (int i = tid; i < nr; i += NT) { // images, coalesced + const int e = lo + i; + uint32_t u = 0; + if (i < n) { + if (J.mode == 2) { // block e: max over its visible members, newest +inf + float m = 0.f; // tl.max (maxnumf) over the members, masked -inf + for (int j = 0; j < a.bs; ++j) { + const int p = e * a.bs + j; + const float v = p < nvis ? srow[p] : -INFINITY; + m = j ? fmaxf(m, v) : v; + } + if (e == (nvis - 1) / a.bs) m = INFINITY; + if (e * a.bs < nvis) u = image(m); + } else if (J.mode == 3) { + if (crow[e / a.bs] >= 0) u = image(srow[e]); + } else { + u = image(srow[e]); + } + } + img[i] = u; + } + __syncthreads(); + uint32_t prefix = 0, pmask = 0; + int above = 0, quota_mode = 0; // 0: every match selected; 1: equal images capped + for (int p = 0; p < 4; ++p) { + int* h = hist[p & 1]; + if (tid < 256) h[tid] = 0; + __syncthreads(); + const int shift = 24 - 8 * p; + for (int i0 = 0; i0 < nr; i0 += NT) { + const uint32_t v = img[i0 + tid]; + const bool m = v != 0 && (v & pmask) == prefix; + const unsigned act = __ballot_sync(FULL, m); + if (m) { + const int d = (v >> shift) & 255; + const unsigned peers = __match_any_sync(act, d); + if (lane == __ffs(peers) - 1) atomicAdd(&h[d], __popc(peers)); + } + } + cl.sync(); + if (tid < 256) { + int v[MAXCL]; +#pragma unroll + for (int x = 0; x < MAXCL; ++x) v[x] = x < CL ? cl.map_shared_rank(h, x)[tid] : 0; + int sum = 0; +#pragma unroll + for (int x = 0; x < MAXCL; ++x) sum += v[x]; + tot[tid] = sum; + } + __syncthreads(); + if (tid < 32) { // suffix sums over 256 digits, 8 a lane; the digit + int loc[8], run = 0; + for (int x = 7; x >= 0; --x) { run += tot[lane * 8 + x]; loc[x] = run; } + int suf = run; // inclusive suffix over lanes >= this one + for (int o = 1; o < 32; o <<= 1) { + const int y = __shfl_down_sync(FULL, suf, o); + if (lane + o < 32) suf += y; + } + const int after = suf - run; // lanes above this one + int best = -1, bge = 0; + for (int x = 7; x >= 0; --x) + if (best < 0 && above + after + loc[x] >= J.k) { best = lane * 8 + x; bge = after + loc[x]; } + const unsigned has = __ballot_sync(FULL, best >= 0); + const int src = has ? 31 - __clz(has) : 0; + const int dg = __shfl_sync(FULL, best, src), ge = __shfl_sync(FULL, bge, src); + const int all = __shfl_sync(FULL, suf, 0); + if (lane == 0) { dec[0] = dg; dec[1] = ge; dec[2] = all; } + } + __syncthreads(); + const int dg = dec[0], ge = dec[1], all = dec[2]; + if (p == 0 && all <= J.k) break; // every valid entry (prefix / mask stay 0) + const int hd = tot[dg]; + above += ge - hd; + prefix |= static_cast(dg) << shift; + pmask |= 255u << shift; + if (above + hd == J.k) break; // the prefix's entries complete the top K + if (p == 3) quota_mode = 1; // ties at the K-th image: the first ones win + } + // classify: 2 above (selected), 1 equal (selected up to the quota, in scan order), 0 out + const int e0 = tid * J.ept; + const int xe = max(0, min(J.ept, n - e0)); // this thread's entries below the bound + int gt = 0, eq = 0; + for (int x = 0; x < xe; ++x) { + const uint32_t v = img[e0 + x]; + if (v == 0) continue; + const uint32_t mv = v & pmask; + gt += mv > prefix; + eq += mv == prefix; + } + int tgt, teq; + const int pre_eq = block_scan(eq, ws, &teq); + block_scan(gt, ws, &tgt); + if (tid == 0) { ex[0] = tgt; ex[1] = teq; } + cl.sync(); + int base = 0, eq_before = 0, total = 0, quota = teq; + { + const int need = J.k - above; // equal images to take (quota mode) + for (int x = 0; x < CL; ++x) { + const int* rx = cl.map_shared_rank(ex, x); + const int xg = rx[0], xe2 = rx[1]; + const int xq = quota_mode ? max(0, min(xe2, need - eq_before)) : xe2; + if (x < rank) base += xg + xq; + if (x == rank) quota = xq; + eq_before += xe2; + total += xg + xq; + } + } + cl.sync(); // no DSMEM access after this: CTAs may exit + int sel = 0; + { + int er = pre_eq; + for (int x = 0; x < xe; ++x) { + const uint32_t v = img[e0 + x]; + if (v == 0) continue; + const uint32_t mv = v & pmask; + if (mv > prefix) ++sel; + else if (mv == prefix) { sel += er < quota; ++er; } + } + } + int tsel; + const int off = base + block_scan(sel, ws, &tsel); + int* orow = J.out + static_cast(r) * J.os; + int vis = 0; + { + int er = pre_eq, o = off; + for (int x = 0; x < xe; ++x) { + const uint32_t v = img[e0 + x]; + if (v == 0) continue; + const uint32_t mv = v & pmask; + bool s = mv > prefix; + if (!s && mv == prefix) { s = er < quota; ++er; } + if (!s) continue; + const int e = lo + e0 + x; + const int pp = J.mode == 3 ? crow[e / a.bs] * a.bs + e % a.bs : e; + orow[o++] = pp; + vis += pp < nvis; + } + } + if (J.cnt) { + int tv; + block_scan(vis, ws, &tv); + if (tid == 0 && tv) atomicAdd(J.cnt + r, tv); + } + if (rank == 0) + for (int i = total + tid; i < J.k; i += NT) orow[i] = -1; +} + +} // namespace dsv41_topk diff --git a/zig/kernels/cuda/deepseek_v41/topk_cuda.cu b/zig/kernels/cuda/deepseek_v41/topk_cuda.cu new file mode 100644 index 0000000000..f633f78e22 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/topk_cuda.cu @@ -0,0 +1,296 @@ +// Copy of src/tensorfold/families/deepseek_v41/cuda/csa2/topk_cuda.cu (blob 839ee8a85f0e): sass_check.sh +#define DSV41_ATTN_NO_TORCH + + + +// Indexer decode top-k (TF_DSV41_ATTN_CUDA=1): top K of unique keys (score image, then lower position), exact. + +// Radix select of the K-th 32-bit image over a cluster of <= 8 CTAs a row (DSMEM histograms), then scan-order pick. + +// Image: s == 0 -> 0, NaN -> 0x7FC00000, negative -> bits ^ 0x7FFFFFFF, then ^ 0x80000000 (0 marks missing). + +#include +#include +#include + +namespace dsv41_topk { +namespace cg = cooperative_groups; + +constexpr int NT = 512, NW = NT / 32, MAXCL = 8, MAXEPT = 31; +constexpr unsigned FULL = 0xffffffffu; + +struct Job { + const float* s; long long ss; // scores [R, n] (unit stride) + const int* cand; long long cs; // mode 3: ascending candidate blocks [R, cols] (-1 last) + int* out; long long os; // [R, k] + int* cnt; // [R] visible counts or null + int nk, k, mode, ept; // entries, K, 0 dense / 2 blocks / 3 candidate positions, entries a thread +}; + +struct Args { + Job job[2]; + const void* pos; int pos64; // int32 [1] (POS + r) or int64 [R] + int ratio, bs; +}; + +__device__ __forceinline__ uint32_t image(float s) { + int b = __float_as_int(s); + if (s == 0.f) b = 0; + if (s != s) b = 0x7FC00000; + if (b < 0) b ^= 0x7FFFFFFF; + return static_cast(b) ^ 0x80000000u; +} + +// exclusive scan of one int a thread over the CTA (512 threads); returns the prefix, *total the sum +__device__ int block_scan(int v, int* ws, int* total) { + const int lane = threadIdx.x & 31, w = threadIdx.x >> 5; + int x = v; + for (int o = 1; o < 32; o <<= 1) { + const int y = __shfl_up_sync(FULL, x, o); + if (lane >= o) x += y; + } + if (lane == 31) ws[w] = x; + __syncthreads(); + if (w == 0) { + int z = lane < NW ? ws[lane] : 0; + for (int o = 1; o < 32; o <<= 1) { + const int y = __shfl_up_sync(FULL, z, o); + if (lane >= o) z += y; + } + if (lane < NW) ws[NW + lane] = z; + } + __syncthreads(); + const int pre = (w ? ws[NW + w - 1] : 0) + x - v; + *total = ws[2 * NW - 1]; + __syncthreads(); + return pre; +} + +__global__ void __launch_bounds__(NT, 1) topk_kernel(Args a) { + cg::cluster_group cl = cg::this_cluster(); + const Job J = blockIdx.z == 0 ? a.job[0] : a.job[1]; // (no dynamic indexing: no local memory) + const int CL = static_cast(cl.num_blocks()), rank = static_cast(cl.block_rank()); + const int r = blockIdx.y, tid = threadIdx.x, lane = tid & 31; + extern __shared__ uint32_t img[]; // SL = 512 x ept images (0: missing) + __shared__ int hist[2][256]; + __shared__ int tot[256]; + __shared__ int ws[2 * NW]; + __shared__ int ex[2]; // (above, equal) of this CTA, read by the cluster + __shared__ int dec[4]; + const int SL = NT * J.ept, lo = rank * SL, n = max(0, min(SL, J.nk - lo)); + const int q = a.pos64 ? static_cast(static_cast(a.pos)[r]) + : static_cast(a.pos)[0] + r; + const int nvis = (q + 1) / a.ratio; + if (rank == 0 && tid == 0 && J.cnt) J.cnt[r] = 0; // before the first cluster barrier (release) + const float* srow = J.s + static_cast(r) * J.ss; + const int* crow = J.cand ? J.cand + static_cast(r) * J.cs : nullptr; + for (int i = tid; i < SL; i += NT) { // images, coalesced + const int e = lo + i; + uint32_t u = 0; + if (i < n) { + if (J.mode == 2) { // block e: max over its visible members, newest +inf + float m = 0.f; // tl.max (maxnumf) over the members, masked -inf + for (int j = 0; j < a.bs; ++j) { + const int p = e * a.bs + j; + const float v = p < nvis ? srow[p] : -INFINITY; + m = j ? fmaxf(m, v) : v; + } + if (e == (nvis - 1) / a.bs) m = INFINITY; + if (e * a.bs < nvis) u = image(m); + } else if (J.mode == 3) { + if (crow[e / a.bs] >= 0) u = image(srow[e]); + } else { + u = image(srow[e]); + } + } + img[i] = u; + } + __syncthreads(); + uint32_t prefix = 0, pmask = 0; + int above = 0, quota_mode = 0; // 0: every match selected; 1: equal images capped + for (int p = 0; p < 4; ++p) { + int* h = hist[p & 1]; + if (tid < 256) h[tid] = 0; + __syncthreads(); + const int shift = 24 - 8 * p; + for (int i0 = 0; i0 < SL; i0 += NT) { + const uint32_t v = img[i0 + tid]; + const bool m = v != 0 && (v & pmask) == prefix; + const unsigned act = __ballot_sync(FULL, m); + if (m) { + const int d = (v >> shift) & 255; + const unsigned peers = __match_any_sync(act, d); + if (lane == __ffs(peers) - 1) atomicAdd(&h[d], __popc(peers)); + } + } + cl.sync(); + if (tid < 256) { + int v[MAXCL]; +#pragma unroll + for (int x = 0; x < MAXCL; ++x) v[x] = x < CL ? cl.map_shared_rank(h, x)[tid] : 0; + int sum = 0; +#pragma unroll + for (int x = 0; x < MAXCL; ++x) sum += v[x]; + tot[tid] = sum; + } + __syncthreads(); + if (tid < 32) { // suffix sums over 256 digits, 8 a lane; the digit + int loc[8], run = 0; + for (int x = 7; x >= 0; --x) { run += tot[lane * 8 + x]; loc[x] = run; } + int suf = run; // inclusive suffix over lanes >= this one + for (int o = 1; o < 32; o <<= 1) { + const int y = __shfl_down_sync(FULL, suf, o); + if (lane + o < 32) suf += y; + } + const int after = suf - run; // lanes above this one + int best = -1, bge = 0; + for (int x = 7; x >= 0; --x) + if (best < 0 && above + after + loc[x] >= J.k) { best = lane * 8 + x; bge = after + loc[x]; } + const unsigned has = __ballot_sync(FULL, best >= 0); + const int src = has ? 31 - __clz(has) : 0; + const int dg = __shfl_sync(FULL, best, src), ge = __shfl_sync(FULL, bge, src); + const int all = __shfl_sync(FULL, suf, 0); + if (lane == 0) { dec[0] = dg; dec[1] = ge; dec[2] = all; } + } + __syncthreads(); + const int dg = dec[0], ge = dec[1], all = dec[2]; + if (p == 0 && all <= J.k) break; // every valid entry (prefix / mask stay 0) + const int hd = tot[dg]; + above += ge - hd; + prefix |= static_cast(dg) << shift; + pmask |= 255u << shift; + if (above + hd == J.k) break; // the prefix's entries complete the top K + if (p == 3) quota_mode = 1; // ties at the K-th image: the first ones win + } + // classify: 2 above (selected), 1 equal (selected up to the quota, in scan order), 0 out + const int e0 = tid * J.ept; + int gt = 0, eq = 0; + for (int x = 0; x < J.ept; ++x) { + const uint32_t v = img[e0 + x]; + if (v == 0) continue; + const uint32_t mv = v & pmask; + gt += mv > prefix; + eq += mv == prefix; + } + int tgt, teq; + const int pre_eq = block_scan(eq, ws, &teq); + block_scan(gt, ws, &tgt); + if (tid == 0) { ex[0] = tgt; ex[1] = teq; } + cl.sync(); + int base = 0, eq_before = 0, total = 0, quota = teq; + { + const int need = J.k - above; // equal images to take (quota mode) + for (int x = 0; x < CL; ++x) { + const int* rx = cl.map_shared_rank(ex, x); + const int xg = rx[0], xe = rx[1]; + const int xq = quota_mode ? max(0, min(xe, need - eq_before)) : xe; + if (x < rank) base += xg + xq; + if (x == rank) quota = xq; + eq_before += xe; + total += xg + xq; + } + } + cl.sync(); // no DSMEM access after this: CTAs may exit + int sel = 0; + { + int er = pre_eq; + for (int x = 0; x < J.ept; ++x) { + const uint32_t v = img[e0 + x]; + if (v == 0) continue; + const uint32_t mv = v & pmask; + if (mv > prefix) ++sel; + else if (mv == prefix) { sel += er < quota; ++er; } + } + } + int tsel; + const int off = base + block_scan(sel, ws, &tsel); + int* orow = J.out + static_cast(r) * J.os; + int vis = 0; + { + int er = pre_eq, o = off; + for (int x = 0; x < J.ept; ++x) { + const uint32_t v = img[e0 + x]; + if (v == 0) continue; + const uint32_t mv = v & pmask; + bool s = mv > prefix; + if (!s && mv == prefix) { s = er < quota; ++er; } + if (!s) continue; + const int e = lo + e0 + x; + const int pp = J.mode == 3 ? crow[e / a.bs] * a.bs + e % a.bs : e; + orow[o++] = pp; + vis += pp < nvis; + } + } + if (J.cnt) { + int tv; + block_scan(vis, ws, &tv); + if (tid == 0 && tv) atomicAdd(J.cnt + r, tv); + } + if (rank == 0) + for (int i = total + tid; i < J.k; i += NT) orow[i] = -1; +} + +// Shared memory a CTA for ``ept`` entries a thread (the images; the rest is static). +inline size_t smem(int ept) { return static_cast(NT) * ept * 4; } + +inline cudaError_t launch(const Args& a, int jobs, int R, int CL, int ept, cudaStream_t stream) { + if (R < 1 || jobs < 1 || jobs > 2 || CL < 1 || CL > MAXCL || ept < 1 || ept > MAXEPT) + return cudaErrorInvalidValue; + static bool init = false; + if (!init) { + const cudaError_t e = cudaFuncSetAttribute(topk_kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, + static_cast(smem(MAXEPT))); + if (e != cudaSuccess) return e; + init = true; + } + cudaLaunchConfig_t cfg = {}; + cfg.gridDim = dim3(CL, R, jobs); + cfg.blockDim = dim3(NT); + cfg.dynamicSmemBytes = smem(ept); + cfg.stream = stream; + cudaLaunchAttribute at[1]; + at[0].id = cudaLaunchAttributeClusterDimension; + at[0].val.clusterDim.x = CL; + at[0].val.clusterDim.y = 1; + at[0].val.clusterDim.z = 1; + cfg.attrs = at; + cfg.numAttrs = 1; + return cudaLaunchKernelEx(&cfg, topk_kernel, a); +} + +} // namespace dsv41_topk + +#ifndef DSV41_ATTN_NO_TORCH +#include +#include +#include + +namespace { +dsv41_topk::Job job_of(const at::Tensor& s, const at::Tensor& cand, at::Tensor& out, at::Tensor& cnt, int64_t nk, + int64_t k, int64_t mode, int64_t ept) { + dsv41_topk::Job j{}; + j.s = static_cast(s.data_ptr()); j.ss = s.stride(0); + j.cand = cand.numel() ? static_cast(cand.data_ptr()) : nullptr; + j.cs = cand.numel() ? cand.stride(0) : 0; + j.out = static_cast(out.data_ptr()); j.os = out.stride(0); + j.cnt = cnt.numel() ? static_cast(cnt.data_ptr()) : nullptr; + j.nk = static_cast(nk); j.k = static_cast(k); j.mode = static_cast(mode); + j.ept = static_cast(ept); + return j; +} +} + +void dsv41_topk_run_cuda(const at::Tensor& s0, const at::Tensor& c0, at::Tensor& o0, at::Tensor& n0, int64_t nk0, + int64_t k0, int64_t m0, int64_t e0, const at::Tensor& s1, const at::Tensor& c1, + at::Tensor& o1, at::Tensor& n1, int64_t nk1, int64_t k1, int64_t m1, int64_t e1, + int64_t jobs, const at::Tensor& pos, int64_t ratio, int64_t bs, int64_t R, int64_t CL) { + dsv41_topk::Args a{}; + a.job[0] = job_of(s0, c0, o0, n0, nk0, k0, m0, e0); + a.job[1] = jobs > 1 ? job_of(s1, c1, o1, n1, nk1, k1, m1, e1) : a.job[0]; + a.pos = pos.data_ptr(); a.pos64 = pos.scalar_type() == at::kLong ? 1 : 0; + a.ratio = static_cast(ratio); a.bs = static_cast(bs); + const int ept = static_cast(std::max(e0, jobs > 1 ? e1 : e0)); + C10_CUDA_CHECK(dsv41_topk::launch(a, static_cast(jobs), static_cast(R), static_cast(CL), ept, + at::cuda::getCurrentCUDAStream())); +} +#endif diff --git a/zig/kernels/cuda/deepseek_v41/topk_keys.cu b/zig/kernels/cuda/deepseek_v41/topk_keys.cu new file mode 100644 index 0000000000..1bf475ce08 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/topk_keys.cu @@ -0,0 +1,94 @@ +// Row top-k of bf16-valued fp32 logits on the device: value descending, ties by lower column (torch.topk of keys). + +// keys = (mono(bits(x + 0.0)) & -65536) | (65535 - col): unique in a row (C <= 65,536), so any exact select matches. + +// One CTA a row: 4-pass 8-bit radix select of the k-th key, gather k winners, bitonic sort; cols int64, vals fp32. + +#include + +namespace dsv41_topk_keys { + +constexpr int NT = 1024, KMAX = 1024, COLS = 1 << 16; + +__device__ __forceinline__ uint32_t ukey(float x, int col) { + int b = __float_as_int(x + 0.0f); // -0.0 + 0.0 = +0.0 + const int mono = b >= 0 ? b : (b ^ 0x7FFFFFFF); + const int key = (mono & -COLS) | (COLS - 1 - col); + return static_cast(key) ^ 0x80000000u; // signed order as unsigned +} + +__global__ void __launch_bounds__(NT) topk_kernel(const float* __restrict__ lg, long long ld, int C, int k, + float* __restrict__ vals, long long* __restrict__ cols) { + __shared__ int hist[256]; + __shared__ uint32_t s_prefix, s_mask; + __shared__ int s_need, s_n; + __shared__ uint32_t key_sh[KMAX]; + __shared__ int col_sh[KMAX]; + const int tid = threadIdx.x; + const float* row = lg + (long long)blockIdx.x * ld; + + // radix select: the k-th largest key, 8 bits a pass from the top + if (tid == 0) { s_prefix = 0; s_mask = 0; s_need = k; } + __syncthreads(); + for (int shift = 24; shift >= 0; shift -= 8) { + if (tid < 256) hist[tid] = 0; + __syncthreads(); + const uint32_t prefix = s_prefix, mask = s_mask; + for (int c = tid; c < C; c += NT) { + const uint32_t u = ukey(row[c], c); + if ((u & mask) == prefix) atomicAdd(&hist[(u >> shift) & 255], 1); + } + __syncthreads(); + if (tid == 0) { // the digit holding the need-th largest, from the top + int need = s_need, d = 255; + for (; d > 0 && hist[d] < need; --d) need -= hist[d]; + s_need = need; + s_prefix = prefix | ((uint32_t)d << shift); + s_mask = mask | (255u << shift); + } + __syncthreads(); + } + const uint32_t kth = s_prefix; // the k-th largest key itself (keys are unique) + + // the winners (> kth, and kth): exactly k, in any order + if (tid == 0) s_n = 0; + __syncthreads(); + for (int c = tid; c < C; c += NT) { + const uint32_t u = ukey(row[c], c); + if (u >= kth) { + const int at = atomicAdd(&s_n, 1); + key_sh[at] = u; + col_sh[at] = c; + } + } + __syncthreads(); + int n = 1; + while (n < k) n <<= 1; + for (int i = k + tid; i < n; i += NT) { key_sh[i] = 0; col_sh[i] = -1; } // pads sort last (keys >= 1) + __syncthreads(); + + // bitonic sort, descending + for (int size = 2; size <= n; size <<= 1) { + for (int stride = size >> 1; stride > 0; stride >>= 1) { + for (int i = tid; i < n; i += NT) { + const int j = i ^ stride; + if (j > i) { + const bool desc = (i & size) == 0; + const uint32_t a = key_sh[i], b = key_sh[j]; + if (desc ? a < b : a > b) { + key_sh[i] = b; key_sh[j] = a; + const int t = col_sh[i]; col_sh[i] = col_sh[j]; col_sh[j] = t; + } + } + } + __syncthreads(); + } + } + for (int i = tid; i < k; i += NT) { + const int c = col_sh[i]; + cols[(long long)blockIdx.x * k + i] = c; + vals[(long long)blockIdx.x * k + i] = row[c]; + } +} + +} // namespace dsv41_topk_keys diff --git a/zig/kernels/cuda/deepseek_v41/x3gm.cu b/zig/kernels/cuda/deepseek_v41/x3gm.cu new file mode 100644 index 0000000000..3798f4e298 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/x3gm.cu @@ -0,0 +1,477 @@ +// Copy of src/tensorfold/families/deepseek_v41/cuda/x3gm.cu (blob 7aed2b027afc): sass_check.sh + + + +// x3gm: routed EXL3 prefill experts (gate/up, down); each output is one mma chain over ascending k from +0, any tiling + +#include +#include + +#include "decode.cuh" +#include "experts_grouped.cuh" + +namespace dsv41_x3gm { + +using tf_exl3x::cb_pair; +using tf_exl3x::Fmt; +using tf_exl3x::LaneMap; +using tf_exl3x::mma16816; + +constexpr float HAD = 0.08838834764831845f; // 1 / sqrt(128) +constexpr int NB = 8; // column tiles an item: 128 columns, one Hadamard block +constexpr int MUL1 = 2; + +__device__ __forceinline__ void cp_async16(void* smem, const void* gmem, bool ok) { + const unsigned s = (unsigned)__cvta_generic_to_shared(smem); + asm volatile("cp.async.cg.shared.global [%0], [%1], 16, %2;\n" ::"r"(s), "l"(gmem), "r"(ok ? 16 : 0)); +} +__device__ __forceinline__ void cp_commit() { asm volatile("cp.async.commit_group;\n" ::); } +template +__device__ __forceinline__ void cp_wait() { asm volatile("cp.async.wait_group %0;\n" ::"n"(N)); } +__device__ __forceinline__ void ldsm4(uint32_t (&a)[4], const void* p) { + const unsigned s = (unsigned)__cvta_generic_to_shared(p); + asm volatile("ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0,%1,%2,%3}, [%4];\n" + : "=r"(a[0]), "=r"(a[1]), "=r"(a[2]), "=r"(a[3]) + : "r"(s)); +} + +// the 16-byte chunk c of member row r in a stage (CPR chunks a row): 8 rows of an ldmatrix phase in 8 bank groups +template +__device__ __forceinline__ int swz(int r, int c) { + static_assert(CPR == 8 || CPR == 4, "rows of 64 or 32 halves"); + return CPR == 8 ? (c ^ (r & 7)) : (c ^ ((r >> 1) & 3)); +} + +// A 16x16 W_q tile from shared words as the A fragment of W^T: upstream decode_tile, registers reordered +template +__device__ __forceinline__ void decode_a(const uint32_t* tile, const LaneMap& m, uint32_t (&a)[4]) { + constexpr int GV = Fmt::GV, NG = Fmt::NG; + uint32_t st[8]; +#pragma unroll + for (int g = 0; g < NG; ++g) { + const uint32_t whi = tile[m.hi[g]], wlo = tile[m.lo[g]]; + const uint64_t mm = ((((uint64_t)wlo) << 32) | whi) >> m.sh[g]; +#pragma unroll + for (int j = 0; j < GV; ++j) st[g * GV + j] = (uint32_t)(mm >> Fmt::off(j)) & 0xffffu; + } + a[0] = cb_pair(st[0], st[1]); // b0[0]: k 2t, 2t+1 of column g -> A row g, k 2t.. + a[1] = cb_pair(st[4], st[5]); // b1[0]: k 2t, 2t+1 of column g + 8 -> A row g + 8, k 2t.. + a[2] = cb_pair(st[2], st[3]); // b0[1]: k 2t+8, 2t+9 of column g -> A row g, k 2t+8.. + a[3] = cb_pair(st[6], st[7]); // b1[1] -> A row g + 8, k 2t+8.. +} + +template +struct Cfg { + static constexpr int BM = NG * 8, WPM = NB / MTL, W = MATS * WPM, THREADS = W * 32; + static constexpr int CPR = KS * 2, LDA = KS * 16, TW = 4 * K2; + static constexpr int LDE = 132, ER = NGR * 8, ROUNDS = NG / NGR; + static constexpr size_t A_BYTES = (size_t)XM * BM * LDA * sizeof(half); + static constexpr size_t W_BYTES = (size_t)MATS * KS * NB * TW * sizeof(uint32_t); + static constexpr size_t STAGE = A_BYTES + W_BYTES; + static constexpr size_t RING = (size_t)NSA * STAGE; + static constexpr size_t EPI = (size_t)MATS * ER * LDE * sizeof(float); + static constexpr size_t SMEM = RING > EPI ? RING : EPI; + // CTAs an SM the registers are capped for: 2 when two rings fit, <= 8 warps, <= 64 accumulators a lane; else 1 + static constexpr int MINB = 2 * (SMEM + 1536) <= 102400 && THREADS <= 256 && MTL * NG <= 16 ? 2 : 1; + static_assert(NG % 2 == 0 && NG % NGR == 0 && NGR % 2 == 0 && NB % MTL == 0, "tiling"); + static_assert(A_BYTES % 16 == 0 && (TW * 4) % 16 == 0, "16-byte stages"); + static_assert(XM == 1 || XM == MATS, "one shared input or one a matrix"); +}; + +struct Args { + const half* x0; // [P, K] rows of matrix 0 (gate / down input) + const half* x1; // [P, K] rows of matrix 1 (up) when XM == 2 + const uint32_t* t0; // [E, K/16, N/16, 4 K2] trellis words, stacked (when tp0 is null) + const uint32_t* t1; + const int64_t* tp0; // [E] expert e's words [K/16, N/16, 4 K2] (a ragged stack), or null + const int64_t* tp1; + const int* k2e; // [E] each expert's K2 (gm2_kernel; the launch's width table) + const int* order; // pair indices by expert (stable: rows ascending) + const int* pe; // pass -> expert + const int* poff; // pass -> first index into order + const int* pcnt; // pass -> members (<= BM) + const int* npass; // [1] passes in all + int* ticket; // [1] zeroed, or null (static stride) + const half* sv0; // gate/up: svh_g [E, N]; down: svh_d [E, N] + const half* sv1; // svh_u [E, N] + const half* sd; // suh_d [E, N] + half* xd; // gate/up out [P, N] + float* y; // down out [P, N] + int K, N; + float limit; +}; + +template +__global__ void __launch_bounds__(Cfg::THREADS, + Cfg::MINB) gm_kernel(const Args a) { + using C = Cfg; + constexpr int THREADS = C::THREADS, BM = C::BM, LDA = C::LDA, LDE = C::LDE, ER = C::ER, CPR = C::CPR, + TW = C::TW, W = C::W; + extern __shared__ __align__(16) unsigned char smem[]; + float* ep = reinterpret_cast(smem); // [MATS][ER][LDE] after the K loop (aliases the ring) + __shared__ int rows_sh[BM]; + __shared__ int item_sh; + const int warp = threadIdx.x >> 5, lane = threadIdx.x & 31; + const int mat = warp / C::WPM, slice = warp % C::WPM; // columns slice 16 MTL .. + 16 MTL of the item + const int xmat = XM == 1 ? 0 : mat; + const int g = lane >> 2, t = lane & 3; + const int K = a.K, N = a.N, KT = K >> 4, NTILES = N >> 4, S = KT / KS, NBLK = N >> 7; + const size_t se = (size_t)KT * NTILES * TW; // words of one expert's matrix + const int total = a.npass[0] * NBLK; + const int lr = (lane & 7) + ((lane >> 4) << 3), lc = (lane >> 3) & 1; + const LaneMap map(lane); + + int next = blockIdx.x; + for (;;) { + __syncthreads(); // the last item is done with rows_sh and shared memory + if (threadIdx.x == 0) item_sh = a.ticket ? atomicAdd(a.ticket, 1) : next; + next += gridDim.x; + __syncthreads(); + const int item = item_sh; + if (item >= total) break; + const int tt = item / NBLK, nb = item % NBLK; + const int e = a.pe[tt], off = a.poff[tt], cnt = a.pcnt[tt]; + const uint32_t* te0 = a.tp0 ? reinterpret_cast(a.tp0[e]) : a.t0 + (size_t)e * se; + const uint32_t* te1 = a.tp1 ? reinterpret_cast(a.tp1[e]) : a.t1 + (size_t)e * se; + for (int i = threadIdx.x; i < BM; i += THREADS) rows_sh[i] = i < cnt ? a.order[off + i] : -1; + __syncthreads(); + const int cnt16 = (cnt + 15) & ~15; // rows past it are never read (warp-uniform skip) + + // stage s: member rows' k [16 KS s, + 16 KS) (zero past the count), then the item's k tile words, block nb + auto load_stage = [&](int s) { + unsigned char* st = smem + (size_t)(s % NSA) * C::STAGE; + half* ra = reinterpret_cast(st); + const int k0 = s * KS * 16; + for (int i = threadIdx.x; i < XM * BM * CPR; i += THREADS) { + const int m = i / (BM * CPR), r = (i / CPR) % BM, c = i % CPR; + if (r < cnt16) { + const int row = rows_sh[r]; + const half* src = (m ? a.x1 : a.x0) + (size_t)(row < 0 ? 0 : row) * K + k0 + c * 8; + cp_async16(ra + ((size_t)m * BM + r) * LDA + swz(r, c) * 8, src, row >= 0); + } + } + uint32_t* rw = reinterpret_cast(st + C::A_BYTES); + constexpr int WCH = MATS * KS * NB * K2; // 16-byte chunks: K2 a tile + for (int i = threadIdx.x; i < WCH; i += THREADS) { + const int q = i % K2, n = (i / K2) % NB, kk = (i / (K2 * NB)) % KS, m = i / (K2 * NB * KS); + const uint32_t* src = (m ? te1 : te0) + ((size_t)(s * KS + kk) * NTILES + nb * NB + n) * TW + q * 4; + cp_async16(rw + ((m * KS + kk) * NB + n) * TW + q * 4, src, true); + } + }; + + float acc[MTL][NG][4]; +#pragma unroll + for (int l = 0; l < MTL; ++l) +#pragma unroll + for (int n = 0; n < NG; ++n) +#pragma unroll + for (int c = 0; c < 4; ++c) acc[l][n][c] = 0.f; + +#pragma unroll + for (int s = 0; s < NSA - 1; ++s) { + if (s < S) load_stage(s); + cp_commit(); + } + cp_wait(); + __syncthreads(); + for (int s = 0; s < S; ++s) { + if (s + NSA - 1 < S) load_stage(s + NSA - 1); + cp_commit(); + const unsigned char* st = smem + (size_t)(s % NSA) * C::STAGE; + const half* A = reinterpret_cast(st) + (size_t)xmat * BM * LDA; + const uint32_t* Wd = reinterpret_cast(st + C::A_BYTES) + (size_t)mat * KS * NB * TW + + slice * MTL * TW; +#pragma unroll + for (int kk = 0; kk < KS; ++kk) { + uint32_t af[MTL][4]; +#pragma unroll + for (int l = 0; l < MTL; ++l) decode_a(Wd + (kk * NB + l) * TW, map, af[l]); + const int chunk = swz(lr, kk * 2 + lc); +#pragma unroll + for (int np = 0; np < NG / 2; ++np) { + if (16 * np < cnt) { // warp-uniform + uint32_t b[4]; + ldsm4(b, A + (16 * np + lr) * LDA + chunk * 8); + const uint32_t b01[2] = {b[0], b[1]}, b23[2] = {b[2], b[3]}; +#pragma unroll + for (int l = 0; l < MTL; ++l) { + mma16816(acc[l][2 * np], af[l], b01); + mma16816(acc[l][2 * np + 1], af[l], b23); + } + } + } + } + cp_wait(); + __syncthreads(); + } + + // epilogue: ER members a round, accumulators -> ep[mat][member][column], then one warp a member row +#pragma unroll + for (int rd = 0; rd < C::ROUNDS; ++rd) { + if (rd) __syncthreads(); + if (ER * rd < cnt) { +#pragma unroll + for (int n = 0; n < NGR; ++n) { + const int ng = rd * NGR + n; + float* E = ep + ((size_t)mat * ER + 8 * n + 2 * t) * LDE + slice * 16 * MTL + g; +#pragma unroll + for (int l = 0; l < MTL; ++l) { + E[l * 16] = acc[l][ng][0]; + E[LDE + l * 16] = acc[l][ng][1]; + E[l * 16 + 8] = acc[l][ng][2]; + E[LDE + l * 16 + 8] = acc[l][ng][3]; + } + } + } + __syncthreads(); + for (int r = warp; r < ER && ER * rd + r < cnt; r += W) { + const int row = rows_sh[ER * rd + r]; + const int c0 = 4 * lane; + const size_t col = (size_t)e * N + nb * 128 + c0; + float v[4]; + *reinterpret_cast(v) = *reinterpret_cast(ep + (size_t)r * LDE + c0); + tf_exl3::fwht128(v, lane); + if constexpr (MATS == 2) { + float u[4]; + *reinterpret_cast(u) = *reinterpret_cast(ep + ((size_t)ER + r) * LDE + c0); + tf_exl3::fwht128(u, lane); +#pragma unroll + for (int j = 0; j < 4; ++j) { // upstream gateup_epilogue, ACT_F32 + const float gg = fminf(v[j] * HAD * __half2float(a.sv0[col + j]), a.limit); + const float uu = fminf(fmaxf(u[j] * HAD * __half2float(a.sv1[col + j]), -a.limit), a.limit); + const float act = gg / (1.f + expf(-gg)) * uu; + v[j] = act * __half2float(a.sd[col + j]); + } + tf_exl3::fwht128(v, lane); + half2* o = reinterpret_cast(a.xd + (size_t)row * N + nb * 128 + c0); + o[0] = __halves2half2(__float2half_rn(v[0] * HAD), __float2half_rn(v[1] * HAD)); + o[1] = __halves2half2(__float2half_rn(v[2] * HAD), __float2half_rn(v[3] * HAD)); + } else { + float4 o; // upstream down_epilogue + o.x = v[0] * HAD * __half2float(a.sv0[col + 0]); + o.y = v[1] * HAD * __half2float(a.sv0[col + 1]); + o.z = v[2] * HAD * __half2float(a.sv0[col + 2]); + o.w = v[3] * HAD * __half2float(a.sv0[col + 3]); + *reinterpret_cast(a.y + (size_t)row * N + nb * 128 + c0) = o; + } + } + } + } +} + +#include "x3gm2.cuh" + +// Rotated routed inputs: out_m[p] = fp16((x[p / slots] * suh_m[e]) H / sqrt 128), upstream rot_in's arithmetic +template __device__ __forceinline__ float to_f(TIN v); +template <> __device__ __forceinline__ float to_f<__nv_bfloat16>(__nv_bfloat16 v) { return __bfloat162float(v); } +template <> __device__ __forceinline__ float to_f(half v) { return __half2float(v); } + +template +__global__ void __launch_bounds__(256) rot_kernel(const TIN* __restrict__ x, int x_stride, const int* __restrict__ pick, + const half* __restrict__ suh0, const half* __restrict__ suh1, + half* __restrict__ out0, half* __restrict__ out1, int K, int slots, + int items) { + const int item = blockIdx.x * 8 + (threadIdx.x >> 5); + if (item >= items) return; // warp-uniform + const int KB = K >> 7, p = item / KB, blk = item % KB, lane = threadIdx.x & 31; + const int e = pick[p]; + const TIN* xr = x + (size_t)(p / slots) * x_stride + blk * 128 + 4 * lane; + float xv[4]; +#pragma unroll + for (int j = 0; j < 4; ++j) xv[j] = to_f(xr[j]); +#pragma unroll + for (int m = 0; m < MATS; ++m) { + const half* suh = (m ? suh1 : suh0) + (size_t)e * K + blk * 128 + 4 * lane; + float v[4]; +#pragma unroll + for (int j = 0; j < 4; ++j) v[j] = xv[j] * __half2float(suh[j]); + tf_exl3::fwht128(v, lane); + half* o = (m ? out1 : out0) + (size_t)p * K + blk * 128 + 4 * lane; +#pragma unroll + for (int j = 0; j < 4; ++j) o[j] = __float2half_rn(v[j] * HAD); + } +} + +// Tests: W_q [K, N] fp16 of one trellis through decode_a (words staged in shared memory as the gm kernels stage them). +template +__global__ void __launch_bounds__(32) dequant_kernel(const uint32_t* __restrict__ T, half* __restrict__ out, int N) { + constexpr int TW = 4 * K2; + __shared__ __align__(16) uint32_t tile[TW]; + const int kt = blockIdx.x, nt = blockIdx.y, lane = threadIdx.x, NTILES = N >> 4; + for (int i = lane; i < TW; i += 32) tile[i] = T[((size_t)kt * NTILES + nt) * TW + i]; + __syncwarp(); + const LaneMap map(lane); + uint32_t af[4]; + decode_a(tile, map, af); + const int g = lane >> 2, t = lane & 3; +#pragma unroll + for (int q = 0; q < 4; ++q) { // A row (q & 1 ? g + 8 : g) = W column; k 2t (+8) + const half2 h = *reinterpret_cast(&af[q]); + const int col = nt * 16 + g + 8 * (q & 1), row = kt * 16 + 2 * t + 8 * (q >> 1); + out[(size_t)row * N + col] = __low2half(h); + out[(size_t)(row + 1) * N + col] = __high2half(h); + } +} + + +// Tile table (MTL, NG, KS, NSA, NGR), mirrored by the host tile lists (a test parses these lines); 2.. = 4K tiles +#define GM_GU0 2, 8, 4, 3, 4 +#define GM_GU1 2, 8, 2, 4, 4 +#define GM_GU2 1, 16, 2, 4, 4 +#define GM_GU3 1, 16, 4, 3, 4 +#define GM_GU4 2, 16, 2, 3, 4 +#define GM_GU5 2, 8, 2, 6, 4 +#define GM_DN0 2, 8, 4, 4, 8 +#define GM_DN1 1, 8, 4, 4, 8 +#define GM_DN2 1, 16, 4, 4, 8 +#define GM_DN3 1, 16, 2, 6, 8 +#define GM_DN4 2, 16, 4, 3, 16 +#define GM_NGU 6 +#define GM_NDN 5 + + +} // namespace dsv41_x3gm + +#ifndef DSV41_X3GM_NO_INSTANCES +// Instances the bindings launch: gate/up (XM 1 shared input, 2 own) GM_GU0-5, down GM_DN0-4, K2 3-8 and 10 +#define DSV41_X3GM_GU(XM, K2, CFG) template __global__ void dsv41_x3gm::gm_kernel<2, XM, K2, CFG>(const dsv41_x3gm::Args); +#define DSV41_X3GM_DN(K2, CFG) template __global__ void dsv41_x3gm::gm_kernel<1, 1, K2, CFG>(const dsv41_x3gm::Args); +// v2 (gm2_kernel: every width in one launch): gate/up GM_GU0 / GM_GU1 at either input mode, down GM_DN0 +template __global__ void dsv41_x3gm::gm2_kernel<2, 1, GM_GU0>(const dsv41_x3gm::Args); +template __global__ void dsv41_x3gm::gm2_kernel<2, 1, GM_GU1>(const dsv41_x3gm::Args); +template __global__ void dsv41_x3gm::gm2_kernel<2, 2, GM_GU0>(const dsv41_x3gm::Args); +template __global__ void dsv41_x3gm::gm2_kernel<2, 2, GM_GU1>(const dsv41_x3gm::Args); +template __global__ void dsv41_x3gm::gm2_kernel<1, 1, GM_DN0>(const dsv41_x3gm::Args); +DSV41_X3GM_GU(1, 3, GM_GU0) +DSV41_X3GM_GU(1, 3, GM_GU1) +DSV41_X3GM_GU(1, 3, GM_GU2) +DSV41_X3GM_GU(1, 3, GM_GU3) +DSV41_X3GM_GU(1, 3, GM_GU4) +DSV41_X3GM_GU(1, 3, GM_GU5) +DSV41_X3GM_GU(2, 3, GM_GU0) +DSV41_X3GM_GU(2, 3, GM_GU1) +DSV41_X3GM_GU(2, 3, GM_GU2) +DSV41_X3GM_GU(2, 3, GM_GU3) +DSV41_X3GM_GU(2, 3, GM_GU4) +DSV41_X3GM_GU(2, 3, GM_GU5) +DSV41_X3GM_DN(3, GM_DN0) +DSV41_X3GM_DN(3, GM_DN1) +DSV41_X3GM_DN(3, GM_DN2) +DSV41_X3GM_DN(3, GM_DN3) +DSV41_X3GM_DN(3, GM_DN4) +DSV41_X3GM_GU(1, 4, GM_GU0) +DSV41_X3GM_GU(1, 4, GM_GU1) +DSV41_X3GM_GU(1, 4, GM_GU2) +DSV41_X3GM_GU(1, 4, GM_GU3) +DSV41_X3GM_GU(1, 4, GM_GU4) +DSV41_X3GM_GU(1, 4, GM_GU5) +DSV41_X3GM_GU(2, 4, GM_GU0) +DSV41_X3GM_GU(2, 4, GM_GU1) +DSV41_X3GM_GU(2, 4, GM_GU2) +DSV41_X3GM_GU(2, 4, GM_GU3) +DSV41_X3GM_GU(2, 4, GM_GU4) +DSV41_X3GM_GU(2, 4, GM_GU5) +DSV41_X3GM_DN(4, GM_DN0) +DSV41_X3GM_DN(4, GM_DN1) +DSV41_X3GM_DN(4, GM_DN2) +DSV41_X3GM_DN(4, GM_DN3) +DSV41_X3GM_DN(4, GM_DN4) +DSV41_X3GM_GU(1, 5, GM_GU0) +DSV41_X3GM_GU(1, 5, GM_GU1) +DSV41_X3GM_GU(1, 5, GM_GU2) +DSV41_X3GM_GU(1, 5, GM_GU3) +DSV41_X3GM_GU(1, 5, GM_GU4) +DSV41_X3GM_GU(1, 5, GM_GU5) +DSV41_X3GM_GU(2, 5, GM_GU0) +DSV41_X3GM_GU(2, 5, GM_GU1) +DSV41_X3GM_GU(2, 5, GM_GU2) +DSV41_X3GM_GU(2, 5, GM_GU3) +DSV41_X3GM_GU(2, 5, GM_GU4) +DSV41_X3GM_GU(2, 5, GM_GU5) +DSV41_X3GM_DN(5, GM_DN0) +DSV41_X3GM_DN(5, GM_DN1) +DSV41_X3GM_DN(5, GM_DN2) +DSV41_X3GM_DN(5, GM_DN3) +DSV41_X3GM_DN(5, GM_DN4) +DSV41_X3GM_GU(1, 6, GM_GU0) +DSV41_X3GM_GU(1, 6, GM_GU1) +DSV41_X3GM_GU(1, 6, GM_GU2) +DSV41_X3GM_GU(1, 6, GM_GU3) +DSV41_X3GM_GU(1, 6, GM_GU4) +DSV41_X3GM_GU(1, 6, GM_GU5) +DSV41_X3GM_GU(2, 6, GM_GU0) +DSV41_X3GM_GU(2, 6, GM_GU1) +DSV41_X3GM_GU(2, 6, GM_GU2) +DSV41_X3GM_GU(2, 6, GM_GU3) +DSV41_X3GM_GU(2, 6, GM_GU4) +DSV41_X3GM_GU(2, 6, GM_GU5) +DSV41_X3GM_DN(6, GM_DN0) +DSV41_X3GM_DN(6, GM_DN1) +DSV41_X3GM_DN(6, GM_DN2) +DSV41_X3GM_DN(6, GM_DN3) +DSV41_X3GM_DN(6, GM_DN4) +DSV41_X3GM_GU(1, 7, GM_GU0) +DSV41_X3GM_GU(1, 7, GM_GU1) +DSV41_X3GM_GU(1, 7, GM_GU2) +DSV41_X3GM_GU(1, 7, GM_GU3) +DSV41_X3GM_GU(1, 7, GM_GU4) +DSV41_X3GM_GU(1, 7, GM_GU5) +DSV41_X3GM_GU(2, 7, GM_GU0) +DSV41_X3GM_GU(2, 7, GM_GU1) +DSV41_X3GM_GU(2, 7, GM_GU2) +DSV41_X3GM_GU(2, 7, GM_GU3) +DSV41_X3GM_GU(2, 7, GM_GU4) +DSV41_X3GM_GU(2, 7, GM_GU5) +DSV41_X3GM_DN(7, GM_DN0) +DSV41_X3GM_DN(7, GM_DN1) +DSV41_X3GM_DN(7, GM_DN2) +DSV41_X3GM_DN(7, GM_DN3) +DSV41_X3GM_DN(7, GM_DN4) +DSV41_X3GM_GU(1, 8, GM_GU0) +DSV41_X3GM_GU(1, 8, GM_GU1) +DSV41_X3GM_GU(1, 8, GM_GU2) +DSV41_X3GM_GU(1, 8, GM_GU3) +DSV41_X3GM_GU(1, 8, GM_GU4) +DSV41_X3GM_GU(1, 8, GM_GU5) +DSV41_X3GM_GU(2, 8, GM_GU0) +DSV41_X3GM_GU(2, 8, GM_GU1) +DSV41_X3GM_GU(2, 8, GM_GU2) +DSV41_X3GM_GU(2, 8, GM_GU3) +DSV41_X3GM_GU(2, 8, GM_GU4) +DSV41_X3GM_GU(2, 8, GM_GU5) +DSV41_X3GM_DN(8, GM_DN0) +DSV41_X3GM_DN(8, GM_DN1) +DSV41_X3GM_DN(8, GM_DN2) +DSV41_X3GM_DN(8, GM_DN3) +DSV41_X3GM_DN(8, GM_DN4) +DSV41_X3GM_GU(1, 10, GM_GU0) +DSV41_X3GM_GU(1, 10, GM_GU1) +DSV41_X3GM_GU(1, 10, GM_GU2) +DSV41_X3GM_GU(1, 10, GM_GU3) +DSV41_X3GM_GU(1, 10, GM_GU4) +DSV41_X3GM_GU(1, 10, GM_GU5) +DSV41_X3GM_GU(2, 10, GM_GU0) +DSV41_X3GM_GU(2, 10, GM_GU1) +DSV41_X3GM_GU(2, 10, GM_GU2) +DSV41_X3GM_GU(2, 10, GM_GU3) +DSV41_X3GM_GU(2, 10, GM_GU4) +DSV41_X3GM_GU(2, 10, GM_GU5) +DSV41_X3GM_DN(10, GM_DN0) +DSV41_X3GM_DN(10, GM_DN1) +DSV41_X3GM_DN(10, GM_DN2) +DSV41_X3GM_DN(10, GM_DN3) +DSV41_X3GM_DN(10, GM_DN4) +#define DSV41_X3GM_ROT(T, M) template __global__ void dsv41_x3gm::rot_kernel(const T*, int, const int*, const half*, \ + const half*, half*, half*, int, int, int); +DSV41_X3GM_ROT(__nv_bfloat16, 1) +DSV41_X3GM_ROT(__nv_bfloat16, 2) +DSV41_X3GM_ROT(half, 1) +DSV41_X3GM_ROT(half, 2) +template __global__ void dsv41_x3gm::dequant_kernel<3>(const uint32_t*, half*, int); +template __global__ void dsv41_x3gm::dequant_kernel<4>(const uint32_t*, half*, int); +template __global__ void dsv41_x3gm::dequant_kernel<5>(const uint32_t*, half*, int); +template __global__ void dsv41_x3gm::dequant_kernel<6>(const uint32_t*, half*, int); +template __global__ void dsv41_x3gm::dequant_kernel<7>(const uint32_t*, half*, int); +template __global__ void dsv41_x3gm::dequant_kernel<8>(const uint32_t*, half*, int); +template __global__ void dsv41_x3gm::dequant_kernel<10>(const uint32_t*, half*, int); +#endif // DSV41_X3GM_NO_INSTANCES diff --git a/zig/kernels/cuda/deepseek_v41/x3gm2.cuh b/zig/kernels/cuda/deepseek_v41/x3gm2.cuh new file mode 100644 index 0000000000..526092524d --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/x3gm2.cuh @@ -0,0 +1,256 @@ +// x3gm.cu's v2 kernel (TF_DSV41_GM_V2): gm2_item, kloop2 and gm2_kernel, inside namespace dsv41_x3gm +#pragma once +// x3gm v2 (TF_DSV41_GM_V2): gm_kernel's items, chains and bits with less issue work (member groups, width per item) + +__device__ __forceinline__ void ldsm2(uint32_t (&a)[2], const void* p) { + const unsigned s = (unsigned)__cvta_generic_to_shared(p); + asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0,%1}, [%2];\n" : "=r"(a[0]), "=r"(a[1]) : "r"(s)); +} + +// The K loop of one item with N8 active n8 member groups (acc[l][0 .. N8 - 1]; the rest stay 0.0, never stored) +template +__device__ __forceinline__ void kloop2(float (&acc)[MTL][NG][4], const unsigned char* smem, int S, int xmat, int mat, + int slice, int lr, int lc, const LaneMap& map, LoadFn&& load_stage) { + using C = Cfg; + constexpr int BM = C::BM, LDA = C::LDA, CPR = C::CPR, TW = C::TW, PAIRS = N8 / 2; +#pragma unroll + for (int s = 0; s < NSA - 1; ++s) { + if (s < S) load_stage(s); + cp_commit(); + } + cp_wait(); + __syncthreads(); + for (int s = 0; s < S; ++s) { + if (s + NSA - 1 < S) load_stage(s + NSA - 1); + cp_commit(); + const unsigned char* st = smem + (size_t)(s % NSA) * C::STAGE; + const half* A = reinterpret_cast(st) + (size_t)xmat * BM * LDA; + const uint32_t* Wd = reinterpret_cast(st + C::A_BYTES) + (size_t)mat * KS * NB * TW + + slice * MTL * TW; +#pragma unroll + for (int kk = 0; kk < KS; ++kk) { + uint32_t af[MTL][4]; +#pragma unroll + for (int l = 0; l < MTL; ++l) decode_a(Wd + (kk * NB + l) * TW, map, af[l]); + const int chunk = swz(lr, kk * 2 + lc); +#pragma unroll + for (int np = 0; np < PAIRS; ++np) { + uint32_t b[4]; + ldsm4(b, A + (16 * np + lr) * LDA + chunk * 8); + const uint32_t b01[2] = {b[0], b[1]}, b23[2] = {b[2], b[3]}; +#pragma unroll + for (int l = 0; l < MTL; ++l) { + mma16816(acc[l][2 * np], af[l], b01); + mma16816(acc[l][2 * np + 1], af[l], b23); + } + } + if constexpr ((N8 & 1) != 0) { // members 16 PAIRS .. + 7 (x2: lanes 0-15's addresses) + uint32_t b[2]; + ldsm2(b, A + (16 * PAIRS + lr) * LDA + chunk * 8); +#pragma unroll + for (int l = 0; l < MTL; ++l) mma16816(acc[l][2 * PAIRS], af[l], b); + } + } + cp_wait(); + __syncthreads(); + } +} + +// One item (pass x 128-column block) at width K2, rows_sh filled by the caller; otherwise gm_kernel's body +template +__device__ __forceinline__ void gm2_item(const Args& a, unsigned char* smem, const int* rows_sh, int e, int cnt, + int nb) { + using C = Cfg; + constexpr int THREADS = C::THREADS, BM = C::BM, LDA = C::LDA, LDE = C::LDE, ER = C::ER, CPR = C::CPR, + TW = C::TW, W = C::W; + constexpr int ACH = XM * BM * CPR, WCH = MATS * KS * NB * K2; // 16-byte chunks a stage: member rows, words + constexpr int APT = (ACH + THREADS - 1) / THREADS, WPT = (WCH + THREADS - 1) / THREADS; + float* ep = reinterpret_cast(smem); // [MATS][ER][LDE] after the K loop (aliases the ring) + const int warp = threadIdx.x >> 5, lane = threadIdx.x & 31; + const int mat = warp / C::WPM, slice = warp % C::WPM; + const int xmat = XM == 1 ? 0 : mat; + const int g = lane >> 2, t = lane & 3; + const int K = a.K, N = a.N, KT = K >> 4, NTILES = N >> 4, S = KT / KS; + const size_t se = (size_t)KT * NTILES * TW; + const int lr = (lane & 7) + ((lane >> 4) << 3), lc = (lane >> 3) & 1; + const LaneMap map(lane); + const uint32_t* te0 = a.tp0 ? reinterpret_cast(a.tp0[e]) : a.t0 + (size_t)e * se; + const uint32_t* te1 = a.tp1 ? reinterpret_cast(a.tp1[e]) : a.t1 + (size_t)e * se; + const int cnt16 = (cnt + 15) & ~15; // rows past it are never read + + // this thread's chunks: row chunk element offset at k 0 (-1: none, -2: zero fill), word chunk offset at stage 0 + int a_src[APT], w_src[WPT]; +#pragma unroll + for (int j = 0; j < APT; ++j) { + const int i = threadIdx.x + j * THREADS, r = (i / CPR) % BM, c = i % CPR; + int v = -1; + if (i < ACH && r < cnt16) v = rows_sh[r] < 0 ? -2 : rows_sh[r] * K + c * 8; + a_src[j] = v; + } +#pragma unroll + for (int j = 0; j < WPT; ++j) { + const int i = threadIdx.x + j * THREADS, q = i % K2, n = (i / K2) % NB, kk = (i / (K2 * NB)) % KS; + w_src[j] = (kk * NTILES + nb * NB + n) * TW + q * 4; + } + auto load_stage = [&](int s) { + unsigned char* st = smem + (size_t)(s % NSA) * C::STAGE; + half* ra = reinterpret_cast(st); + uint32_t* rw = reinterpret_cast(st + C::A_BYTES); + const int k0 = s * KS * 16; +#pragma unroll + for (int j = 0; j < APT; ++j) { + const int i = threadIdx.x + j * THREADS, v = a_src[j]; + if (v != -1) { + const int m = i / (BM * CPR), r = (i / CPR) % BM, c = i % CPR; + cp_async16(ra + ((size_t)m * BM + r) * LDA + swz(r, c) * 8, (m ? a.x1 : a.x0) + (v < 0 ? 0 : v + k0), + v >= 0); + } + } + const size_t ws = (size_t)s * KS * NTILES * TW; +#pragma unroll + for (int j = 0; j < WPT; ++j) { + const int i = threadIdx.x + j * THREADS; + if (WCH % THREADS == 0 || i < WCH) { + const int q = i % K2, n = (i / K2) % NB, kk = (i / (K2 * NB)) % KS, m = i / (K2 * NB * KS); + cp_async16(rw + ((m * KS + kk) * NB + n) * TW + q * 4, (m ? te1 : te0) + ws + w_src[j], true); + } + } + }; + + float acc[MTL][NG][4]; +#pragma unroll + for (int l = 0; l < MTL; ++l) +#pragma unroll + for (int n = 0; n < NG; ++n) +#pragma unroll + for (int c = 0; c < 4; ++c) acc[l][n][c] = 0.f; + + static_assert(NG <= 16 && NG % 2 == 0, "n8 groups"); + const int n8 = (cnt + 7) >> 3; // >= 1 (a pass has members) +#define GM2_K(G) kloop2(acc, smem, S, xmat, mat, slice, lr, lc, map, load_stage) + switch (n8 <= 8 ? n8 : (n8 + 1) & ~1) { // 1..8, then even counts (NG 16) + case 1: GM2_K(1); break; + case 2: GM2_K(2); break; + case 3: GM2_K(3); break; + case 4: GM2_K(4); break; + case 5: if constexpr (NG >= 6) GM2_K(5); break; + case 6: if constexpr (NG >= 6) GM2_K(6); break; + case 7: if constexpr (NG >= 8) GM2_K(7); break; + case 8: if constexpr (NG >= 8) GM2_K(8); break; + case 10: if constexpr (NG >= 10) GM2_K(10); break; + case 12: if constexpr (NG >= 12) GM2_K(12); break; + case 14: if constexpr (NG >= 14) GM2_K(14); break; + default: if constexpr (NG >= 16) GM2_K(16); break; + } +#undef GM2_K + + // epilogue: gm_kernel's +#pragma unroll + for (int rd = 0; rd < C::ROUNDS; ++rd) { + if (rd) __syncthreads(); + if (ER * rd < cnt) { +#pragma unroll + for (int n = 0; n < NGR; ++n) { + const int ng = rd * NGR + n; + float* E = ep + ((size_t)mat * ER + 8 * n + 2 * t) * LDE + slice * 16 * MTL + g; +#pragma unroll + for (int l = 0; l < MTL; ++l) { + E[l * 16] = acc[l][ng][0]; + E[LDE + l * 16] = acc[l][ng][1]; + E[l * 16 + 8] = acc[l][ng][2]; + E[LDE + l * 16 + 8] = acc[l][ng][3]; + } + } + } + __syncthreads(); + for (int r = warp; r < ER && ER * rd + r < cnt; r += W) { + const int row = rows_sh[ER * rd + r]; + const int c0 = 4 * lane; + const size_t col = (size_t)e * N + nb * 128 + c0; + float v[4]; + *reinterpret_cast(v) = *reinterpret_cast(ep + (size_t)r * LDE + c0); + tf_exl3::fwht128(v, lane); + if constexpr (MATS == 2) { + float u[4]; + *reinterpret_cast(u) = *reinterpret_cast(ep + ((size_t)ER + r) * LDE + c0); + tf_exl3::fwht128(u, lane); +#pragma unroll + for (int j = 0; j < 4; ++j) { // upstream gateup_epilogue, ACT_F32 + const float gg = fminf(v[j] * HAD * __half2float(a.sv0[col + j]), a.limit); + const float uu = fminf(fmaxf(u[j] * HAD * __half2float(a.sv1[col + j]), -a.limit), a.limit); + const float act = gg / (1.f + expf(-gg)) * uu; + v[j] = act * __half2float(a.sd[col + j]); + } + tf_exl3::fwht128(v, lane); + half2* o = reinterpret_cast(a.xd + (size_t)row * N + nb * 128 + c0); + o[0] = __halves2half2(__float2half_rn(v[0] * HAD), __float2half_rn(v[1] * HAD)); + o[1] = __halves2half2(__float2half_rn(v[2] * HAD), __float2half_rn(v[3] * HAD)); + } else { + float4 o; // upstream down_epilogue + o.x = v[0] * HAD * __half2float(a.sv0[col + 0]); + o.y = v[1] * HAD * __half2float(a.sv0[col + 1]); + o.z = v[2] * HAD * __half2float(a.sv0[col + 2]); + o.w = v[3] * HAD * __half2float(a.sv0[col + 3]); + *reinterpret_cast(a.y + (size_t)row * N + nb * 128 + c0) = o; + } + } + } +} + +// The widths a v2 launch dispatches and, per width, the ring depth (cut where it would cost a second CTA an SM) +#define GM2_WIDTHS(X) X(3) X(4) X(5) X(6) X(7) X(8) X(10) +constexpr int GM2_NWIDTHS = 7; +template +struct Ring2 { + static constexpr bool fits2(int n) { + return 2 * (Cfg::STAGE * (size_t)n + 1536) <= 102400; + } + static constexpr int cut(int n) { return n <= 2 || fits2(n) ? n : cut(n - 1); } + static constexpr int value = Cfg::MINB == 2 ? cut(NSA) : NSA; +}; +template +struct Smem2 { +#define GM2_SM(K) Cfg::value, NGR>::SMEM, + static constexpr size_t list[GM2_NWIDTHS] = {GM2_WIDTHS(GM2_SM)}; +#undef GM2_SM + static constexpr size_t max_from(int i) { + return i == GM2_NWIDTHS ? 0 : (list[i] > max_from(i + 1) ? list[i] : max_from(i + 1)); + } + static constexpr size_t SMEM = max_from(0); + static constexpr int THREADS = Cfg::THREADS; + static constexpr int MINB = 2 * (SMEM + 1536) <= 102400 && THREADS <= 256 && MTL * NG <= 16 ? 2 : 1; +}; + +// One v2 launch over the passes of every width (uniform stack: one width; ragged: each expert its own, a.k2e) +template +__global__ void __launch_bounds__(Smem2::THREADS, + Smem2::MINB) gm2_kernel(const Args a) { + constexpr int BM = NG * 8, THREADS = Smem2::THREADS; + extern __shared__ __align__(16) unsigned char smem[]; + __shared__ int rows_sh[BM]; + __shared__ int item_sh; + const int NBLK = a.N >> 7, total = a.npass[0] * NBLK; + int next = blockIdx.x; + for (;;) { + __syncthreads(); // the last item is done with rows_sh and shared memory + if (threadIdx.x == 0) item_sh = a.ticket ? atomicAdd(a.ticket, 1) : next; + next += gridDim.x; + __syncthreads(); + const int item = item_sh; + if (item >= total) break; + const int tt = item / NBLK, nb = item % NBLK; + const int e = a.pe[tt], off = a.poff[tt], cnt = a.pcnt[tt]; + for (int i = threadIdx.x; i < BM; i += THREADS) rows_sh[i] = i < cnt ? a.order[off + i] : -1; + __syncthreads(); + switch (a.k2e[e]) { // block-uniform +#define GM2_CASE(K) \ + case K: \ + gm2_item::value, NGR>(a, smem, rows_sh, e, \ + cnt, nb); \ + break; + GM2_WIDTHS(GM2_CASE) +#undef GM2_CASE + default: break; // never: the host checks every width is in K2S + } + } +} diff --git a/zig/kernels/cuda/deepseek_v41/x3gm3.cu b/zig/kernels/cuda/deepseek_v41/x3gm3.cu new file mode 100644 index 0000000000..f9c2a53dc2 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/x3gm3.cu @@ -0,0 +1,360 @@ +// x3gm v3: gm2_kernel's items, chains and epilogue (x3gm.cu) with claim-ahead items, LEAN decode and MTL 1 warps. + +#define DSV41_X3GM_NO_INSTANCES +#include "x3gm.cu" + +namespace dsv41_x3gm { // each element: one mma chain over ascending k from +0.0, gm_kernel's epilogue: v2's bits + +// The 16-bit state at bit `OFF` of a 32-bit run. +template +__device__ __forceinline__ uint32_t window(uint32_t mm) { + if constexpr (OFF == 0) return mm & 0xffffu; + else if constexpr (OFF == 8) return __byte_perm(mm, 0u, 0x4421); + else if constexpr (OFF == 16) return mm >> 16; + else return (mm >> OFF) & 0xffffu; +} + +template +__device__ __forceinline__ void windows32(uint32_t mm, uint32_t (&st)[8]) { + if constexpr (J < Fmt::GV) { + st[G * Fmt::GV + J] = window::off(J)>(mm); + windows32(mm, st); + } +} + +// decode_a's A fragment (same states), read from 32 bits when a run's highest window ends within them +template +__device__ __forceinline__ void decode_a3(const uint32_t* tile, const LaneMap& m, uint32_t (&a)[4]) { + constexpr int GV = Fmt::GV, NG = Fmt::NG, SPAN = Fmt::off(0) + 16; + uint32_t st[8]; +#pragma unroll + for (int g = 0; g < NG; ++g) { + const uint32_t whi = tile[m.hi[g]], wlo = tile[m.lo[g]]; + if constexpr (SPAN <= 32) { + const uint32_t mm = __funnelshift_r(whi, wlo, m.sh[g]); // low 32 bits of (wlo:whi) >> sh, sh 0..31 + if (g == 0) windows32(mm, st); + else if (g == 1) windows32(mm, st); + } else { + const uint64_t mm = ((((uint64_t)wlo) << 32) | whi) >> m.sh[g]; +#pragma unroll + for (int j = 0; j < GV; ++j) st[g * GV + j] = (uint32_t)(mm >> Fmt::off(j)) & 0xffffu; + } + } + a[0] = cb_pair(st[0], st[1]); + a[1] = cb_pair(st[4], st[5]); + a[2] = cb_pair(st[2], st[3]); + a[3] = cb_pair(st[6], st[7]); +} + +template +__device__ __forceinline__ void decode3(const uint32_t* tile, const LaneMap& m, uint32_t (&a)[4]) { + if constexpr (LEAN) decode_a3(tile, m, a); + else decode_a(tile, m, a); +} + +// kloop2 with LEAN's decode and `side(s)` after the barrier that opens stage s (stages 0 .. S - 1) +template +__device__ __forceinline__ void kloop3(float (&acc)[MTL][NG][4], const unsigned char* smem, int S, int xmat, int mat, + int slice, int lr, int lc, const LaneMap& map, LoadFn&& load_stage, + SideFn&& side) { + using C = Cfg; + constexpr int BM = C::BM, LDA = C::LDA, CPR = C::CPR, TW = C::TW, PAIRS = N8 / 2; +#pragma unroll + for (int s = 0; s < NSA - 1; ++s) { + if (s < S) load_stage(s); + cp_commit(); + } + cp_wait(); + __syncthreads(); + for (int s = 0; s < S; ++s) { + side(s); + if (s + NSA - 1 < S) load_stage(s + NSA - 1); + cp_commit(); + const unsigned char* st = smem + (size_t)(s % NSA) * C::STAGE; + const half* A = reinterpret_cast(st) + (size_t)xmat * BM * LDA; + const uint32_t* Wd = reinterpret_cast(st + C::A_BYTES) + (size_t)mat * KS * NB * TW + + slice * MTL * TW; +#pragma unroll + for (int kk = 0; kk < KS; ++kk) { + uint32_t af[MTL][4]; +#pragma unroll + for (int l = 0; l < MTL; ++l) decode3(Wd + (kk * NB + l) * TW, map, af[l]); + const int chunk = swz(lr, kk * 2 + lc); +#pragma unroll + for (int np = 0; np < PAIRS; ++np) { + uint32_t b[4]; + ldsm4(b, A + (16 * np + lr) * LDA + chunk * 8); + const uint32_t b01[2] = {b[0], b[1]}, b23[2] = {b[2], b[3]}; +#pragma unroll + for (int l = 0; l < MTL; ++l) { + mma16816(acc[l][2 * np], af[l], b01); + mma16816(acc[l][2 * np + 1], af[l], b23); + } + } + if constexpr ((N8 & 1) != 0) { + uint32_t b[2]; + ldsm2(b, A + (16 * PAIRS + lr) * LDA + chunk * 8); +#pragma unroll + for (int l = 0; l < MTL; ++l) mma16816(acc[l][2 * PAIRS], af[l], b); + } + } + cp_wait(); + __syncthreads(); + } +} + +// gm2_item at LEAN's decode, `side` run inside its k loop +template +__device__ __forceinline__ void gm3_item(const Args& a, unsigned char* smem, const int* rows_sh, int e, int cnt, + int nb, SideFn&& side) { + using C = Cfg; + constexpr int THREADS = C::THREADS, BM = C::BM, LDA = C::LDA, LDE = C::LDE, ER = C::ER, CPR = C::CPR, + TW = C::TW, W = C::W; + constexpr int ACH = XM * BM * CPR, WCH = MATS * KS * NB * K2; + constexpr int APT = (ACH + THREADS - 1) / THREADS, WPT = (WCH + THREADS - 1) / THREADS; + float* ep = reinterpret_cast(smem); + const int warp = threadIdx.x >> 5, lane = threadIdx.x & 31; + const int mat = warp / C::WPM, slice = warp % C::WPM; + const int xmat = XM == 1 ? 0 : mat; + const int g = lane >> 2, t = lane & 3; + const int K = a.K, N = a.N, KT = K >> 4, NTILES = N >> 4, S = KT / KS; + const size_t se = (size_t)KT * NTILES * TW; + const int lr = (lane & 7) + ((lane >> 4) << 3), lc = (lane >> 3) & 1; + const LaneMap map(lane); + const uint32_t* te0 = a.tp0 ? reinterpret_cast(a.tp0[e]) : a.t0 + (size_t)e * se; + const uint32_t* te1 = a.tp1 ? reinterpret_cast(a.tp1[e]) : a.t1 + (size_t)e * se; + const int cnt16 = (cnt + 15) & ~15; + + int a_src[APT], w_src[WPT]; +#pragma unroll + for (int j = 0; j < APT; ++j) { + const int i = threadIdx.x + j * THREADS, r = (i / CPR) % BM, c = i % CPR; + int v = -1; + if (i < ACH && r < cnt16) v = rows_sh[r] < 0 ? -2 : rows_sh[r] * K + c * 8; + a_src[j] = v; + } +#pragma unroll + for (int j = 0; j < WPT; ++j) { + const int i = threadIdx.x + j * THREADS, q = i % K2, n = (i / K2) % NB, kk = (i / (K2 * NB)) % KS; + w_src[j] = (kk * NTILES + nb * NB + n) * TW + q * 4; + } + auto load_stage = [&](int s) { + unsigned char* st = smem + (size_t)(s % NSA) * C::STAGE; + half* ra = reinterpret_cast(st); + uint32_t* rw = reinterpret_cast(st + C::A_BYTES); + const int k0 = s * KS * 16; +#pragma unroll + for (int j = 0; j < APT; ++j) { + const int i = threadIdx.x + j * THREADS, v = a_src[j]; + if (v != -1) { + const int m = i / (BM * CPR), r = (i / CPR) % BM, c = i % CPR; + cp_async16(ra + ((size_t)m * BM + r) * LDA + swz(r, c) * 8, (m ? a.x1 : a.x0) + (v < 0 ? 0 : v + k0), + v >= 0); + } + } + const size_t ws = (size_t)s * KS * NTILES * TW; +#pragma unroll + for (int j = 0; j < WPT; ++j) { + const int i = threadIdx.x + j * THREADS; + if (WCH % THREADS == 0 || i < WCH) { + const int q = i % K2, n = (i / K2) % NB, kk = (i / (K2 * NB)) % KS, m = i / (K2 * NB * KS); + cp_async16(rw + ((m * KS + kk) * NB + n) * TW + q * 4, (m ? te1 : te0) + ws + w_src[j], true); + } + } + }; + + float acc[MTL][NG][4]; +#pragma unroll + for (int l = 0; l < MTL; ++l) +#pragma unroll + for (int n = 0; n < NG; ++n) +#pragma unroll + for (int c = 0; c < 4; ++c) acc[l][n][c] = 0.f; + + const int n8 = (cnt + 7) >> 3; +#define GM3_K(G) \ + kloop3(acc, smem, S, xmat, mat, slice, lr, lc, map, load_stage, side) + switch (n8 <= 8 ? n8 : (n8 + 1) & ~1) { + case 1: GM3_K(1); break; + case 2: GM3_K(2); break; + case 3: GM3_K(3); break; + case 4: GM3_K(4); break; + case 5: if constexpr (NG >= 6) GM3_K(5); break; + case 6: if constexpr (NG >= 6) GM3_K(6); break; + case 7: if constexpr (NG >= 8) GM3_K(7); break; + case 8: if constexpr (NG >= 8) GM3_K(8); break; + case 10: if constexpr (NG >= 10) GM3_K(10); break; + case 12: if constexpr (NG >= 12) GM3_K(12); break; + case 14: if constexpr (NG >= 14) GM3_K(14); break; + default: if constexpr (NG >= 16) GM3_K(16); break; + } +#undef GM3_K + + // epilogue: gm_kernel's +#pragma unroll + for (int rd = 0; rd < C::ROUNDS; ++rd) { + if (rd) __syncthreads(); + if (ER * rd < cnt) { +#pragma unroll + for (int n = 0; n < NGR; ++n) { + const int ng = rd * NGR + n; + float* E = ep + ((size_t)mat * ER + 8 * n + 2 * t) * LDE + slice * 16 * MTL + g; +#pragma unroll + for (int l = 0; l < MTL; ++l) { + E[l * 16] = acc[l][ng][0]; + E[LDE + l * 16] = acc[l][ng][1]; + E[l * 16 + 8] = acc[l][ng][2]; + E[LDE + l * 16 + 8] = acc[l][ng][3]; + } + } + } + __syncthreads(); + for (int r = warp; r < ER && ER * rd + r < cnt; r += W) { + const int row = rows_sh[ER * rd + r]; + const int c0 = 4 * lane; + const size_t col = (size_t)e * N + nb * 128 + c0; + float v[4]; + *reinterpret_cast(v) = *reinterpret_cast(ep + (size_t)r * LDE + c0); + tf_exl3::fwht128(v, lane); + if constexpr (MATS == 2) { + float u[4]; + *reinterpret_cast(u) = *reinterpret_cast(ep + ((size_t)ER + r) * LDE + c0); + tf_exl3::fwht128(u, lane); +#pragma unroll + for (int j = 0; j < 4; ++j) { + const float gg = fminf(v[j] * HAD * __half2float(a.sv0[col + j]), a.limit); + const float uu = fminf(fmaxf(u[j] * HAD * __half2float(a.sv1[col + j]), -a.limit), a.limit); + const float act = gg / (1.f + expf(-gg)) * uu; + v[j] = act * __half2float(a.sd[col + j]); + } + tf_exl3::fwht128(v, lane); + half2* o = reinterpret_cast(a.xd + (size_t)row * N + nb * 128 + c0); + o[0] = __halves2half2(__float2half_rn(v[0] * HAD), __float2half_rn(v[1] * HAD)); + o[1] = __halves2half2(__float2half_rn(v[2] * HAD), __float2half_rn(v[3] * HAD)); + } else { + float4 o; + o.x = v[0] * HAD * __half2float(a.sv0[col + 0]); + o.y = v[1] * HAD * __half2float(a.sv0[col + 1]); + o.z = v[2] * HAD * __half2float(a.sv0[col + 2]); + o.w = v[3] * HAD * __half2float(a.sv0[col + 3]); + *reinterpret_cast(a.y + (size_t)row * N + nb * 128 + c0) = o; + } + } + } +} + +__device__ __forceinline__ int ld_relaxed(const int* p) { + int v; + asm volatile("ld.global.relaxed.gpu.b32 %0, [%1];\n" : "=r"(v) : "l"(p)); + return v; +} + +// One v3 launch over the passes of every width (gm2_kernel's), the next item claimed and read during this one +template +__global__ void __launch_bounds__(Smem2::THREADS, + Smem2::MINB) gm3_kernel(const Args a) { + constexpr int BM = NG * 8, THREADS = Smem2::THREADS, AHEAD = 5; + constexpr int RPT = (BM + THREADS - 1) / THREADS; // member rows a thread reads ahead + extern __shared__ __align__(16) unsigned char smem[]; + __shared__ int rows_sh[2][BM]; + __shared__ int meta_sh[2][4]; // item, expert, first index, members + const int NBLK = a.N >> 7, total = a.npass[0] * NBLK; + int next = blockIdx.x; + // the first item, claimed and read as gm2_kernel does + if (threadIdx.x == 0) { + const int item = a.ticket ? atomicAdd(a.ticket, 1) : next; + meta_sh[0][0] = item; + if (item < total) { + const int tt = item / NBLK; + meta_sh[0][1] = a.pe[tt]; + meta_sh[0][2] = a.poff[tt]; + meta_sh[0][3] = a.pcnt[tt]; + } + } + next += gridDim.x; + __syncthreads(); + if (meta_sh[0][0] < total) + for (int i = threadIdx.x; i < BM; i += THREADS) rows_sh[0][i] = i < meta_sh[0][3] ? a.order[meta_sh[0][2] + i] : -1; + int cur = 0; + for (;;) { + __syncthreads(); // this item's rows and meta are written + const int item = meta_sh[cur][0]; + if (item >= total) break; + const int nb = item % NBLK, e = meta_sh[cur][1], cnt = meta_sh[cur][3]; + const int nx = cur ^ 1; + // the next item, one dependent step a stage: claim; pass entry; publish; member rows; publish + int claim = 0, pe_v = 0, off_v = 0, cnt_v = 0, rows_v[RPT]; + auto side = [&](int s) { + if (s == 0) { + if (threadIdx.x == 0) claim = a.ticket ? atomicAdd(a.ticket, 1) : next; + next += gridDim.x; + } else if (s == 1) { + if (threadIdx.x == 0 && claim < total) { + const int tt = claim / NBLK; + pe_v = ld_relaxed(a.pe + tt); + off_v = ld_relaxed(a.poff + tt); + cnt_v = ld_relaxed(a.pcnt + tt); + } + } else if (s == 2) { + if (threadIdx.x == 0) { + meta_sh[nx][0] = claim; + meta_sh[nx][1] = pe_v; + meta_sh[nx][2] = off_v; + meta_sh[nx][3] = cnt_v; + } + } else if (s == 3) { + const bool live = meta_sh[nx][0] < total; + const int o = meta_sh[nx][2], c = meta_sh[nx][3]; +#pragma unroll + for (int j = 0; j < RPT; ++j) { + const int i = threadIdx.x + j * THREADS; + rows_v[j] = live && i < BM && i < c ? ld_relaxed(a.order + o + i) : -1; + } + } else if (s == 4) { +#pragma unroll + for (int j = 0; j < RPT; ++j) { + const int i = threadIdx.x + j * THREADS; + if (i < BM) rows_sh[nx][i] = rows_v[j]; + } + } + }; + switch (a.k2e[e]) { // block-uniform +#define GM3_CASE(K) \ + case K: \ + gm3_item::value, NGR, LEAN>( \ + a, smem, rows_sh[cur], e, cnt, nb, side); \ + break; + GM2_WIDTHS(GM3_CASE) +#undef GM3_CASE + default: break; + } + // a k loop shorter than AHEAD stages: the rest of the steps now, a barrier between each + const int S = (a.K >> 4) / KS; + for (int s = S; s < AHEAD; ++s) { + __syncthreads(); + side(s); + } + cur = nx; + } +} + +} // namespace dsv41_x3gm + +// v3's tiles x LEAN: gate/up at v2's GU0/GU1 and 1-column-tile warps, down at DN0 and 8-warp tiles; a Zig test picks +#define DSV41_X3GM3(MATS, XM, MTL, NG, KS, NSA, NGR, LEAN) \ + template __global__ void dsv41_x3gm::gm3_kernel(const dsv41_x3gm::Args); +DSV41_X3GM3(2, 2, 2, 8, 2, 4, 4, 0) +DSV41_X3GM3(2, 2, 2, 8, 2, 4, 4, 1) +DSV41_X3GM3(2, 2, 1, 8, 2, 4, 4, 1) +DSV41_X3GM3(2, 2, 1, 8, 4, 3, 4, 1) +DSV41_X3GM3(2, 1, 2, 8, 4, 3, 4, 1) +DSV41_X3GM3(2, 1, 1, 8, 4, 3, 4, 1) +DSV41_X3GM3(1, 1, 2, 8, 4, 4, 8, 0) +DSV41_X3GM3(1, 1, 2, 8, 4, 4, 8, 1) +DSV41_X3GM3(1, 1, 1, 8, 4, 4, 8, 0) +DSV41_X3GM3(1, 1, 1, 8, 4, 4, 8, 1) +DSV41_X3GM3(1, 1, 1, 8, 2, 6, 8, 1) + +// v2's gate/up at the 128-member tile GU2 (TF_DSV41_GM_GU2), never built by x3gm.cu's bindings; tiles move data only +template __global__ void dsv41_x3gm::gm2_kernel<2, 2, GM_GU2>(const dsv41_x3gm::Args); diff --git a/zig/kernels/cuda/deepseek_v41/x3gm_plan.cu b/zig/kernels/cuda/deepseek_v41/x3gm_plan.cu new file mode 100644 index 0000000000..ee7355a061 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/x3gm_plan.cu @@ -0,0 +1,104 @@ +// x3gm's pass tables on the device in one launch: same integers as the torch ops of x3gm.plan. + +// order = stable argsort(pick); cnt/off/npe/pend per expert; pass t -> expert e, poff, pcnt; npass = pend[E - 1]. + +// One CTA (E < 512): histogram, scan, stable chunked scatter (per-warp counts + match_any); integers only, so exact. + +#include + +namespace dsv41_x3gm_plan { + +constexpr int NT = 512, NW = NT / 32, EMAX = 512; // E <= 511: the routers take <= 512 experts (GE_MAX) + +__global__ void __launch_bounds__(NT) plan_kernel(const int* __restrict__ pick, int P, int E, int bm, + int* __restrict__ order, int* __restrict__ pe, + int* __restrict__ poff, int* __restrict__ pcnt, + int* __restrict__ npass) { + __shared__ int cnt[EMAX + 1]; // counts of values 0..E, then (scatter) the moving bases + __shared__ int pend[EMAX]; // inclusive scan of passes an expert + __shared__ int wc[NW][EMAX + 1]; // a chunk's per-warp counts of each value (scatter) + __shared__ int scan_tmp[NT]; + const int tid = threadIdx.x, lane = tid & 31, warp = tid >> 5, V = E + 1; + + for (int v = tid; v < V; v += NT) cnt[v] = 0; + __syncthreads(); + for (int i = tid; i < P; i += NT) atomicAdd(&cnt[pick[i]], 1); // counts: order-free + __syncthreads(); + + // exclusive scans: bases of values 0..E and pend over the first E; V <= NT * 2, two elements a thread + auto block_scan = [&](int x) { // inclusive scan of one value a thread + scan_tmp[tid] = x; + __syncthreads(); + for (int o = 1; o < NT; o <<= 1) { + const int y = tid >= o ? scan_tmp[tid - o] : 0; + __syncthreads(); + scan_tmp[tid] += y; + __syncthreads(); + } + const int r = scan_tmp[tid]; + __syncthreads(); + return r; + }; + int c0 = 2 * tid < V ? cnt[2 * tid] : 0, c1 = 2 * tid + 1 < V ? cnt[2 * tid + 1] : 0; + const int b_end = block_scan(c0 + c1); // inclusive over pairs of values + const int n0 = 2 * tid < E ? (c0 + bm - 1) / bm : 0, n1 = 2 * tid + 1 < E ? (c1 + bm - 1) / bm : 0; + const int p_end = block_scan(n0 + n1); + __syncthreads(); + if (2 * tid < V) cnt[2 * tid] = b_end - c0 - c1; // base of value 2 tid + if (2 * tid + 1 < V) cnt[2 * tid + 1] = b_end - c1; + if (2 * tid < E) pend[2 * tid] = p_end - n1; + if (2 * tid + 1 < E) pend[2 * tid + 1] = p_end; + __syncthreads(); + + // pass tables (counts recomputed from the bases: base[v + 1] - base[v], base[E + 1] = P) + const int tmax = P / bm + E + 1; + const int npe_last = E > 0 ? pend[E - 1] : 0; + for (int t = tid; t < tmax; t += NT) { + int lo = 0, hi = E; // searchsorted(pend, t, right): first pend > t + while (lo < hi) { + const int mid = (lo + hi) >> 1; + if (pend[mid] <= t) lo = mid + 1; else hi = mid; + } + const int e = lo < E - 1 ? lo : E - 1; + const int ce = (e + 1 < V ? cnt[e + 1] : P) - cnt[e]; + const int npe_e = (ce + bm - 1) / bm; + const long long j = (long long)t - (pend[e] - npe_e); + long long o = (long long)cnt[e] + j * bm; + const long long cap = P - 1 > 0 ? P - 1 : 0; + if (o > cap) o = cap; + long long c = (long long)ce - j * bm; + c = c < 0 ? 0 : (c > bm ? bm : c); + pe[t] = e; + poff[t] = (int)o; + pcnt[t] = (int)c; + } + if (tid == 0) npass[0] = npe_last; + __syncthreads(); + + // stable scatter, NT pairs a chunk in index order + for (int base = 0; base < P; base += NT) { + for (int i = tid; i < NW * V; i += NT) (&wc[0][0])[(i / V) * (EMAX + 1) + i % V] = 0; + __syncthreads(); + const int i = base + tid; + const bool live = i < P; + const int v = live ? pick[i] : -1; + const unsigned same = __match_any_sync(0xffffffffu, v); + const int before = __popc(same & ((1u << lane) - 1u)); // equal values in earlier lanes of the warp + if (live && before == 0) wc[warp][v] = __popc(same); // the warp's count of v, by its first lane + __syncthreads(); + if (live) { + int slot = cnt[v] + before; + for (int w = 0; w < warp; ++w) slot += wc[w][v]; + order[slot] = i; + } + __syncthreads(); + for (int u = tid; u < V; u += NT) { // the bases move past this chunk's pairs + int s = 0; + for (int w = 0; w < NW; ++w) s += wc[w][u]; + cnt[u] += s; + } + __syncthreads(); + } +} + +} // namespace dsv41_x3gm_plan diff --git a/zig/kernels/cuda/deepseek_v41/x3ld.cu b/zig/kernels/cuda/deepseek_v41/x3ld.cu new file mode 100644 index 0000000000..15bdfb4fcc --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/x3ld.cu @@ -0,0 +1,249 @@ +// Copy of src/tensorfold/families/deepseek_v41/cuda/x3ld.cu (blob 5cd25f2cdf3c): sass_check.sh + + + +// DeepSeek-V4.1-Flash routed experts (TF_DSV41_EXPERT_LOADS=1): grouped EXL3 expert GEMV with a faster load path. + +// Same bits as grouped_kernel at any row count; data movement changes (prologue, ring, PDL). + +// PROBE 3 (timing only, wrong results): no decode and no mma, the load path alone. + +#include "experts_grouped.cuh" + +namespace dsv41_x3ld { + +using tf_exl3x::decode_tile; +using tf_exl3x::Fmt; +using tf_exl3x::LaneMap; +using tf_exl3x::mma16816; + +constexpr int W = 4; // warps a CTA: upstream's default_config, which fixes the K ranges + +__device__ __forceinline__ uint4 ldg_v4(const uint32_t* p) { + uint4 v; + asm volatile("ld.global.nc.L1::no_allocate.v4.u32 {%0,%1,%2,%3}, [%4];" + : "=r"(v.x), "=r"(v.y), "=r"(v.z), "=r"(v.w) + : "l"(p)); + return v; +} + +// load_pair's value (ok ? *(const uint32_t*)x : 0), the load pinned where it stands +__device__ __forceinline__ uint32_t ldg_pair(const half* x, bool ok) { + uint32_t v = 0u; + if (ok) asm volatile("ld.global.nc.u32 %0, [%1];" : "=r"(v) : "l"(x)); + return v; +} + +__device__ __forceinline__ void pdl_wait() { +#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900) + asm volatile("griddepcontrol.wait;" ::: "memory"); +#endif +} + +template +struct Step { + static constexpr int TW = Fmt::TW; // words a tile + static constexpr int LW = Fmt::LW; // words a lane a tile (load_words) + static constexpr int WORDS = NT * TW; // a warp's k step, contiguous + static constexpr int NV = (WORDS + 127) / 128; // 16-byte loads a lane a step +}; + +// One warp's k tiles [kt0, kt0 + nkt) into acc (warp_tiles' arithmetic, loads PD ahead); all CTA threads call it. +template +__device__ __forceinline__ void ld_tiles(const uint32_t* __restrict__ T, int NTILES, int kt0, int nkt, int nt0, + const half* __restrict__ X, int K, const int* rows_sh, uint32_t* stage, + int lane, float (&acc)[NT][2][4]) { + using S = Step; + const int g = lane >> 2, t = lane & 3; + const size_t kstride = (size_t)NTILES * S::TW; + const uint32_t* tp = T + ((size_t)kt0 * NTILES + nt0) * S::TW; + + uint4 ring[PD][S::NV]; + auto issue_w = [&](int d, int s) { + const uint32_t* src = tp + (size_t)s * kstride; +#pragma unroll + for (int v = 0; v < S::NV; ++v) { + const int off = 128 * v + 4 * lane; + if (off < S::WORDS) ring[d][v] = ldg_v4(src + off); + } + }; +#pragma unroll + for (int d = 0; d < PD; ++d) issue_w(d, d); // PD <= nkt (host) + + pdl_wait(); // X (and Z) belong to the kernels before + __syncthreads(); // rows_sh + const int r0 = rows_sh[g], r1 = rows_sh[g + 8]; + const half* x0 = X + (size_t)(r0 < 0 ? 0 : r0) * K + 2 * t; + const half* x1 = X + (size_t)(r1 < 0 ? 0 : r1) * K + 2 * t; + uint32_t a_ring[PD][4]; + auto issue_a = [&](int d, int s) { + const int k = (kt0 + s) * 16; + a_ring[d][0] = ldg_pair(x0 + k, r0 >= 0); + a_ring[d][1] = ldg_pair(x1 + k, r1 >= 0); + a_ring[d][2] = ldg_pair(x0 + k + 8, r0 >= 0); + a_ring[d][3] = ldg_pair(x1 + k + 8, r1 >= 0); + }; +#pragma unroll + for (int d = 0; d < PD; ++d) issue_a(d, d); + + const LaneMap map(lane); + for (int kb = 0; kb < nkt; kb += PD) { // nkt % PD == 0 (host) +#pragma unroll + for (int d = 0; d < PD; ++d) { + const int s = kb + d; + const bool more = s + PD < nkt; + __syncwarp(); // every lane has read step s - 1 back +#pragma unroll + for (int v = 0; v < S::NV; ++v) { + const int off = 128 * v + 4 * lane; + if (off < S::WORDS) *reinterpret_cast(stage + off) = ring[d][v]; + } + if (more) issue_w(d, s + PD); // the slot's registers are free again + __syncwarp(); + uint32_t w[NT][S::LW]; +#pragma unroll + for (int i = 0; i < NT; ++i) +#pragma unroll + for (int l = 0; l < S::LW; ++l) + w[i][l] = (S::TW % 32 == 0 || l * 32 + lane < S::TW) ? stage[i * S::TW + l * 32 + lane] : 0u; + uint32_t a[4]; +#pragma unroll + for (int j = 0; j < 4; ++j) a[j] = a_ring[d][j]; + if (more) issue_a(d, s + PD); +#pragma unroll + for (int i = 0; i < NT; ++i) { + if constexpr (PROBE == 3) { + uint32_t x = a[0] ^ a[1] ^ a[2] ^ a[3]; +#pragma unroll + for (int l = 0; l < S::LW; ++l) x ^= w[i][l]; + acc[i][0][0] = __uint_as_float(__float_as_uint(acc[i][0][0]) ^ x); + } else { + uint32_t b0[2], b1[2]; + decode_tile(w[i], map, lane, b0, b1); + mma16816(acc[i][0], a, b0); + mma16816(acc[i][1], a, b1); + } + } + } + } + __syncwarp(); // the staging area becomes red[warp] again +} + +// Program (u, n block, (mat * SK + split) * MT + member tile): grouped_kernel's work item. +template +__global__ void __launch_bounds__(W * 32, NT == 8 ? 3 : 4) ld_kernel( + const half* __restrict__ X0, const half* __restrict__ X1, const int64_t* __restrict__ TP0, + const int64_t* __restrict__ TP1, const int* __restrict__ K2_0, const int* __restrict__ K2_1, + const int* __restrict__ uids, const int* __restrict__ ucount, const int* __restrict__ members, + float* __restrict__ Z, int K, int N, int P, int SK, int maxm, int slots) { + static_assert(PD >= 1 && PD <= 4, "PD 1-4"); + const int u = blockIdx.x; + const int MT = (maxm + 15) / 16; + const int mtile = blockIdx.z % MT; + const int split = (blockIdx.z / MT) % SK; + const int mat = blockIdx.z / MT / SK; + const int warp = threadIdx.x >> 5, lane = threadIdx.x & 31; + + // round trip 1: none of these depends on another (uids / members are sized by the grid: always in bounds) + const int cnt = __ldcg(ucount); + const int e = __ldcg(uids + u); + const int m = mtile * 16 + threadIdx.x; + const int code = threadIdx.x < 16 && m < maxm ? __ldcg(members + u * maxm + m) : -1; + const int first = __ldcg(members + u * maxm + mtile * 16); + if (u >= cnt || first < 0) return; // grouped_kernel's exits (CTA-uniform) + + __shared__ int rows_sh[16]; + __shared__ __align__(16) float red[W][16][NT * 16]; + if (threadIdx.x < 16) rows_sh[threadIdx.x] = code >= 0 ? (code >> 5) * slots + (code & 31) : -1; + + // round trip 2: the expert's trellis and width + const uint32_t* T = reinterpret_cast(mat ? TP1[e] : TP0[e]); + const int k2 = mat ? K2_1[e] : K2_0[e]; + const half* X = mat ? X1 : X0; + const int KT = K >> 4, NTILES = N >> 4; + const int per_split = KT / SK, per_warp = per_split / W; + const int kt0 = split * per_split + warp * per_warp; + const int nt0 = blockIdx.y * NT; + uint32_t* stage = reinterpret_cast(&red[warp][0][0]); + + float acc[NT][2][4]; +#pragma unroll + for (int i = 0; i < NT; ++i) +#pragma unroll + for (int h = 0; h < 2; ++h) +#pragma unroll + for (int c = 0; c < 4; ++c) acc[i][h][c] = 0.f; + + switch (k2) { +#define DSV41_X3LD_CASE(K2_) \ + case K2_: \ + if constexpr (K2_ >= LO && K2_ <= HI) \ + ld_tiles(T, NTILES, kt0, per_warp, nt0, X, K, rows_sh, stage, lane, acc); \ + else \ + __trap(); \ + break; + DSV41_X3LD_CASE(2) + DSV41_X3LD_CASE(3) + DSV41_X3LD_CASE(4) + DSV41_X3LD_CASE(5) + DSV41_X3LD_CASE(6) + DSV41_X3LD_CASE(7) + DSV41_X3LD_CASE(8) + DSV41_X3LD_CASE(9) + DSV41_X3LD_CASE(10) + DSV41_X3LD_CASE(11) + DSV41_X3LD_CASE(12) + DSV41_X3LD_CASE(13) + DSV41_X3LD_CASE(14) + DSV41_X3LD_CASE(15) + DSV41_X3LD_CASE(16) +#undef DSV41_X3LD_CASE + default: + __trap(); + } + + // grouped_kernel's reduction, verbatim: the warps' partial sums through shared memory, added in warp order + const int g = lane >> 2, t = lane & 3; +#pragma unroll + for (int i = 0; i < NT; ++i) +#pragma unroll + for (int h = 0; h < 2; ++h) { + const int col = i * 16 + h * 8 + 2 * t; + red[warp][g][col] = acc[i][h][0]; + red[warp][g][col + 1] = acc[i][h][1]; + red[warp][g + 8][col] = acc[i][h][2]; + red[warp][g + 8][col + 1] = acc[i][h][3]; + } + __syncthreads(); + for (int idx = threadIdx.x; idx < 16 * NT * 16; idx += W * 32) { + const int row = idx / (NT * 16), col = idx % (NT * 16); + const int r = rows_sh[row]; + if (r < 0) continue; + float s = red[0][row][col]; +#pragma unroll + for (int w = 1; w < W; ++w) s += red[w][row][col]; + Z[(((size_t)mat * SK + split) * P + r) * N + nt0 * 16 + col] = s; + } +} + +} // namespace dsv41_x3ld + +// The instantiations dsv41_x3ld_grouped_cuda dispatches: mul1, (nt, pd) x probe x the (lo, hi) ranges. +#define DSV41_X3LD_INST(NT, PD, LO, HI, PROBE) template __global__ void dsv41_x3ld::ld_kernel<2, NT, PD, LO, HI, PROBE>( \ + const half*, const half*, const int64_t*, const int64_t*, const int*, const int*, const int*, const int*, \ + const int*, float*, int, int, int, int, int, int); +DSV41_X3LD_INST(8, 1, 8, 8, 0) +DSV41_X3LD_INST(8, 1, 2, 10, 0) +DSV41_X3LD_INST(8, 1, 2, 12, 0) +DSV41_X3LD_INST(8, 2, 8, 8, 0) +DSV41_X3LD_INST(8, 2, 2, 10, 0) +DSV41_X3LD_INST(8, 2, 2, 12, 0) +DSV41_X3LD_INST(4, 2, 8, 8, 0) +DSV41_X3LD_INST(4, 2, 2, 10, 0) +DSV41_X3LD_INST(4, 2, 2, 12, 0) +DSV41_X3LD_INST(8, 1, 8, 8, 3) +DSV41_X3LD_INST(8, 1, 2, 10, 3) +DSV41_X3LD_INST(8, 1, 2, 12, 3) +DSV41_X3LD_INST(8, 2, 8, 8, 3) +DSV41_X3LD_INST(8, 2, 2, 10, 3) +DSV41_X3LD_INST(8, 2, 2, 12, 3) diff --git a/zig/kernels/cuda/deepseek_v41/x3ld_epi.cu b/zig/kernels/cuda/deepseek_v41/x3ld_epi.cu new file mode 100644 index 0000000000..3c5da62317 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/x3ld_epi.cu @@ -0,0 +1,276 @@ +// x3ld with gateup_epilogue / down_combine fused in its tail (TF_DSV41_X3LD_EPI=1, default off): 2 launches, same bits. + +// Preconditions (host binding checks): NT 8, gate/up mats 2, down mats 1, SK 1; down skips rows with no live slot. + +#include "x3ld.cu" +#include "exl3_experts.cu" + +namespace dsv41_x3ld_epi { + +using dsv41_x3ld::ld_tiles; +using dsv41_x3ld::W; +using tf_exl3_experts::bf16r; +using tf_exl3_experts::fwht128; +using tf_exl3_experts::HAD_SCALE; +using tf_exl3_experts::store_out; + +// The epilogue operands (one struct by value; the unused ones 0). +struct Epi { + const int* pick; // int32 [rows, slots] + const half* sv0; // gate/up: svh_g [E, N]; down: svh_d [E, N] + const half* sv1; // gate/up: svh_u [E, N] + const half* sd; // gate/up: suh_d [E, N] + half* xd; // gate/up: Xd fp16 [P, N] + float* y; // down: y fp32 [P, N] + const float* wts; // down: [rows, slots] + void* out; // down: [rows, N] fp32 or bf16 + int* ticket; // int32, zero between launches: the last arriver resets its word (threadFenceReduction) + int E; + float limit; + int act_mode; +}; + +// gateup_epilogue_kernel's body for program (p, blk) on one warp (Z read through L2: other CTAs wrote it). +__device__ __forceinline__ void gateup_block(const float* __restrict__ Z, int e, const half* __restrict__ svh_g, + const half* __restrict__ svh_u, const half* __restrict__ suh_d, + half* __restrict__ xd, int p, int blk, int lane, int P, int N, int SK, + float limit, int act_mode) { + const int n = blk * 128 + 4 * lane; + float gv[4], uv[4]; +#pragma unroll + for (int j = 0; j < 4; ++j) { + float sg = 0.f, su = 0.f; + for (int s = 0; s < SK; ++s) { + sg += __ldcg(Z + ((size_t)(0 * SK + s) * P + p) * N + n + j); + su += __ldcg(Z + ((size_t)(1 * SK + s) * P + p) * N + n + j); + } + gv[j] = sg; + uv[j] = su; + } + fwht128(gv, lane); + fwht128(uv, lane); + float v[4]; +#pragma unroll + for (int j = 0; j < 4; ++j) { + float act; + if (act_mode == 0) { + float gg = fminf(bf16r(gv[j] * HAD_SCALE * __half2float(svh_g[(size_t)e * N + n + j])), limit); + float uu = fminf(fmaxf(bf16r(uv[j] * HAD_SCALE * __half2float(svh_u[(size_t)e * N + n + j])), -limit), + limit); + act = bf16r(bf16r(gg / (1.f + expf(-gg))) * uu); + } else { + float gg = fminf(gv[j] * HAD_SCALE * __half2float(svh_g[(size_t)e * N + n + j]), limit); + float uu = fminf(fmaxf(uv[j] * HAD_SCALE * __half2float(svh_u[(size_t)e * N + n + j]), -limit), limit); + act = gg / (1.f + expf(-gg)) * uu; + } + v[j] = act * __half2float(suh_d[(size_t)e * N + n + j]); + } + fwht128(v, lane); + half* o = xd + (size_t)p * N + n; +#pragma unroll + for (int j = 0; j < 4; ++j) o[j] = __float2half_rn(v[j] * HAD_SCALE); +} + +// MODE 0: gate/up + gateup_epilogue; 1: down + down_combine (OutT the combine's output). +template +__device__ __forceinline__ void epi_body(const half* __restrict__ X0, const half* __restrict__ X1, + const int64_t* __restrict__ TP0, const int64_t* __restrict__ TP1, + const int* __restrict__ K2_0, const int* __restrict__ K2_1, + const int* __restrict__ uids, const int* __restrict__ ucount, + const int* __restrict__ members, float* __restrict__ Z, int K, int N, int P, + int SK, int maxm, int slots, const Epi& a) { + static_assert(NT == 8, "a CTA's columns are one 128-column Hadamard block"); + // ---- ld_kernel, verbatim but for the Z store in MODE 1 ---- + const int u = blockIdx.x; + const int MT = (maxm + 15) / 16; + const int mtile = blockIdx.z % MT; + const int split = (blockIdx.z / MT) % SK; + const int mat = blockIdx.z / MT / SK; + const int warp = threadIdx.x >> 5, lane = threadIdx.x & 31; + + const int cnt = __ldcg(ucount); + const int e = __ldcg(uids + u); + const int m = mtile * 16 + threadIdx.x; + const int code = threadIdx.x < 16 && m < maxm ? __ldcg(members + u * maxm + m) : -1; + const int first = __ldcg(members + u * maxm + mtile * 16); + if (u >= cnt || first < 0) return; + + __shared__ int rows_sh[16]; + __shared__ int last_sh[16]; + __shared__ __align__(16) float red[W][16][NT * 16]; + if (threadIdx.x < 16) rows_sh[threadIdx.x] = code >= 0 ? (code >> 5) * slots + (code & 31) : -1; + + const uint32_t* T = reinterpret_cast(mat ? TP1[e] : TP0[e]); + const int k2 = mat ? K2_1[e] : K2_0[e]; + const half* X = mat ? X1 : X0; + const int KT = K >> 4, NTILES = N >> 4; + const int per_split = KT / SK, per_warp = per_split / W; + const int kt0 = split * per_split + warp * per_warp; + const int nt0 = blockIdx.y * NT; + uint32_t* stage = reinterpret_cast(&red[warp][0][0]); + + float acc[NT][2][4]; +#pragma unroll + for (int i = 0; i < NT; ++i) +#pragma unroll + for (int h = 0; h < 2; ++h) +#pragma unroll + for (int c = 0; c < 4; ++c) acc[i][h][c] = 0.f; + + switch (k2) { +#define DSV41_X3LD_EPI_CASE(K2_) \ + case K2_: \ + if constexpr (K2_ >= LO && K2_ <= HI) \ + ld_tiles(T, NTILES, kt0, per_warp, nt0, X, K, rows_sh, stage, lane, acc); \ + else \ + __trap(); \ + break; + DSV41_X3LD_EPI_CASE(2) + DSV41_X3LD_EPI_CASE(3) + DSV41_X3LD_EPI_CASE(4) + DSV41_X3LD_EPI_CASE(5) + DSV41_X3LD_EPI_CASE(6) + DSV41_X3LD_EPI_CASE(7) + DSV41_X3LD_EPI_CASE(8) + DSV41_X3LD_EPI_CASE(9) + DSV41_X3LD_EPI_CASE(10) + DSV41_X3LD_EPI_CASE(11) + DSV41_X3LD_EPI_CASE(12) + DSV41_X3LD_EPI_CASE(13) + DSV41_X3LD_EPI_CASE(14) + DSV41_X3LD_EPI_CASE(15) + DSV41_X3LD_EPI_CASE(16) +#undef DSV41_X3LD_EPI_CASE + default: + __trap(); + } + + const int g = lane >> 2, t = lane & 3; +#pragma unroll + for (int i = 0; i < NT; ++i) +#pragma unroll + for (int h = 0; h < 2; ++h) { + const int col = i * 16 + h * 8 + 2 * t; + red[warp][g][col] = acc[i][h][0]; + red[warp][g][col + 1] = acc[i][h][1]; + red[warp][g + 8][col] = acc[i][h][2]; + red[warp][g + 8][col + 1] = acc[i][h][3]; + } + __syncthreads(); + + if constexpr (MODE == 0) { + // ---- Z as ld_kernel stores it ---- + for (int idx = threadIdx.x; idx < 16 * NT * 16; idx += W * 32) { + const int row = idx / (NT * 16), col = idx % (NT * 16); + const int r = rows_sh[row]; + if (r < 0) continue; + float s = red[0][row][col]; +#pragma unroll + for (int w = 1; w < W; ++w) s += red[w][row][col]; + Z[(((size_t)mat * SK + split) * P + r) * N + nt0 * 16 + col] = s; + } + // ---- the last of the mats x SK CTAs of (u, tile, block): gateup_epilogue for the tile's rows ---- + __threadfence(); + __syncthreads(); + if (threadIdx.x == 0) { + int* tk = a.ticket + ((size_t)u * MT + mtile) * gridDim.y + blockIdx.y; + const int arrivals = gridDim.z / MT; // mats x SK + const int last = atomicAdd(tk, 1) == arrivals - 1; + if (last) *tk = 0; // every arrival is in: back to zero + last_sh[0] = last; + } + __syncthreads(); + if (!last_sh[0]) return; + __threadfence(); + for (int row = warp; row < 16; row += W) { + const int p = rows_sh[row]; + if (p < 0) continue; + const int ep = a.pick[p]; + if (ep < 0 || ep >= a.E) continue; // gateup_epilogue_kernel's exit + gateup_block(Z, ep, a.sv0, a.sv1, a.sd, a.xd, p, blockIdx.y, lane, P, N, SK, a.limit, a.act_mode); + } + } else { + // ---- down_combine's per-slot part from the complete Z in shared memory (SK 1) ---- + const int n = blockIdx.y * 128 + 4 * lane; + for (int row = warp; row < 16; row += W) { + const int p = rows_sh[row]; + if (p < 0) continue; + const int ep = a.pick[p]; + if (ep < 0 || ep >= a.E) continue; // never a member (down_combine reads its y) + float v[4]; +#pragma unroll + for (int j = 0; j < 4; ++j) { + float z = red[0][row][4 * lane + j]; // ld_kernel's Z value +#pragma unroll + for (int w = 1; w < W; ++w) z += red[w][row][4 * lane + j]; + float s = 0.f; // down_combine: the splits from 0.f (SK 1) + s += z; + v[j] = s; + } + fwht128(v, lane); +#pragma unroll + for (int j = 0; j < 4; ++j) + a.y[(size_t)p * N + n + j] = v[j] * HAD_SCALE * __half2float(a.sv0[(size_t)ep * N + n + j]); + } + // ---- a ticket per (row, block): the arrival of the row's last live slot combines it ---- + __threadfence(); + __syncthreads(); + if (threadIdx.x < 16) { + int last = 0; + const int p = rows_sh[threadIdx.x]; + if (p >= 0 && a.pick[p] >= 0 && a.pick[p] < a.E) { + const int r = p / slots; + int live = 0; + for (int q = 0; q < slots; ++q) { + const int eq = a.pick[r * slots + q]; + live += eq >= 0 && eq < a.E; + } + int* tk = a.ticket + (size_t)r * gridDim.y + blockIdx.y; + last = atomicAdd(tk, 1) == live - 1; + if (last) *tk = 0; + } + last_sh[threadIdx.x] = last; + } + __syncthreads(); + bool fenced = false; + OutT* out = reinterpret_cast(a.out); + const int d = blockIdx.y * 128 + threadIdx.x; // a column a thread + for (int i = 0; i < 16; ++i) { + if (!last_sh[i]) continue; // CTA-uniform + if (!fenced) { + __threadfence(); + fenced = true; + } + const int r = rows_sh[i] / slots; + float acc1 = 0.f; + for (int q = 0; q < slots; ++q) + acc1 = fmaf(a.wts[r * slots + q], __ldcg(a.y + ((size_t)r * slots + q) * N + d), acc1); + store_out(out + (size_t)r * N + d, acc1); + } + } +} + +} // namespace dsv41_x3ld_epi + +// Instances the binding names (extern "C"): NT 8, PD 1 / 2, x3ld's (lo, hi) ranges; gate/up, down fp32 / bf16 out. +#define DSV41_X3LD_EPI_ARGS \ + const half *X0, const half *X1, const int64_t *TP0, const int64_t *TP1, const int *K2_0, const int *K2_1, \ + const int *uids, const int *ucount, const int *members, float *Z, int K, int N, int P, int SK, int maxm, \ + int slots, const dsv41_x3ld_epi::Epi a +#define DSV41_X3LD_EPI_PASS X0, X1, TP0, TP1, K2_0, K2_1, uids, ucount, members, Z, K, N, P, SK, maxm, slots, a +#define DSV41_X3LD_EPI_INST(PD, LO, HI) \ + extern "C" __global__ void __launch_bounds__(128, 3) dsv41_x3ld_epi_gu_##PD##_##LO##_##HI(DSV41_X3LD_EPI_ARGS) { \ + dsv41_x3ld_epi::epi_body<2, 8, PD, LO, HI, 0, float>(DSV41_X3LD_EPI_PASS); \ + } \ + extern "C" __global__ void __launch_bounds__(128, 3) dsv41_x3ld_epi_dn_##PD##_##LO##_##HI(DSV41_X3LD_EPI_ARGS) { \ + dsv41_x3ld_epi::epi_body<2, 8, PD, LO, HI, 1, float>(DSV41_X3LD_EPI_PASS); \ + } \ + extern "C" __global__ void __launch_bounds__(128, 3) dsv41_x3ld_epi_dnb_##PD##_##LO##_##HI(DSV41_X3LD_EPI_ARGS) { \ + dsv41_x3ld_epi::epi_body<2, 8, PD, LO, HI, 1, __nv_bfloat16>(DSV41_X3LD_EPI_PASS); \ + } +DSV41_X3LD_EPI_INST(1, 8, 8) +DSV41_X3LD_EPI_INST(1, 2, 10) +DSV41_X3LD_EPI_INST(1, 2, 12) +DSV41_X3LD_EPI_INST(2, 8, 8) +DSV41_X3LD_EPI_INST(2, 2, 10) +DSV41_X3LD_EPI_INST(2, 2, 12) diff --git a/zig/kernels/cuda/deepseek_v41/x3pf.cu b/zig/kernels/cuda/deepseek_v41/x3pf.cu new file mode 100644 index 0000000000..56fa065305 --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/x3pf.cu @@ -0,0 +1,197 @@ +// Copy of src/tensorfold/families/deepseek_v41/cuda/x3pf.cu (blob 3617e7173522): sass_check.sh + + + +// Prefill routed experts: upstream's grouped EXL3 expert GEMM, each decoded trellis tile reused for MTL member tiles. + +#include "experts_grouped.cuh" + +namespace dsv41_x3pf { // only work placement differs from grouped_kernel: Z is upstream's, bit for bit + +using tf_exl3x::decode_tile; +using tf_exl3x::Fmt; +using tf_exl3x::LaneMap; +using tf_exl3x::load_pair; +using tf_exl3x::load_words; +using tf_exl3x::mma16816; + +constexpr int W = 4; // warps a CTA: upstream's default_config, which fixes the K ranges + +template +__device__ __forceinline__ void pf_tiles(const uint32_t* __restrict__ T, int NTILES, int kt0, int nkt, int nt0, + const half* const (&x0)[MTL], const half* const (&x1)[MTL], + const bool (&ok0)[MTL], const bool (&ok1)[MTL], int live, int lane, + float (&acc)[MTL][NT][2][4]) { + constexpr int TW = Fmt::TW, LW = Fmt::LW; + const LaneMap map(lane); + const size_t kstride = (size_t)NTILES * TW; + const uint32_t* tp = T + ((size_t)kt0 * NTILES + nt0) * TW + lane; + + uint32_t nx[NT][LW]; // the next k tile's words (one in flight) + if (nkt > 0) +#pragma unroll + for (int i = 0; i < NT; ++i) load_words(nx[i], tp + i * TW, lane); + + for (int it = 0; it < nkt; ++it) { + uint32_t w[NT][LW]; +#pragma unroll + for (int i = 0; i < NT; ++i) +#pragma unroll + for (int l = 0; l < LW; ++l) w[i][l] = nx[i][l]; + if (it + 1 < nkt) +#pragma unroll + for (int i = 0; i < NT; ++i) load_words(nx[i], tp + (size_t)(it + 1) * kstride + i * TW, lane); + const int k = (kt0 + it) * 16; + uint32_t a[MTL][4]; +#pragma unroll + for (int mt = 0; mt < MTL; ++mt) { + const bool on = mt < live; + a[mt][0] = load_pair(x0[mt] + k, on && ok0[mt]); + a[mt][1] = load_pair(x1[mt] + k, on && ok1[mt]); + a[mt][2] = load_pair(x0[mt] + k + 8, on && ok0[mt]); + a[mt][3] = load_pair(x1[mt] + k + 8, on && ok1[mt]); + } +#pragma unroll + for (int i = 0; i < NT; ++i) { + uint32_t b0[2], b1[2]; + decode_tile(w[i], map, lane, b0, b1); +#pragma unroll + for (int mt = 0; mt < MTL; ++mt) { + if (mt < live) { // CTA-uniform + mma16816(acc[mt][i][0], a[mt], b0); + mma16816(acc[mt][i][1], a[mt], b1); + } + } + } + } +} + +// Program (u, n block, (mat * SK + split) * MG + member group): MTL of grouped_kernel's member tiles. +template +__global__ void __launch_bounds__(W * 32) pf_kernel( + const half* __restrict__ X0, const half* __restrict__ X1, const int64_t* __restrict__ TP0, + const int64_t* __restrict__ TP1, const int* __restrict__ K2_0, const int* __restrict__ K2_1, + const int* __restrict__ uids, const int* __restrict__ ucount, const int* __restrict__ members, + float* __restrict__ Z, int K, int N, int P, int SK, int maxm, int slots) { + const int u = blockIdx.x; + if (u >= ucount[0]) return; + const int MG = (maxm + 16 * MTL - 1) / (16 * MTL); + const int mg = blockIdx.z % MG; + const int split = (blockIdx.z / MG) % SK; + const int mat = blockIdx.z / MG / SK; + const half* X = mat ? X1 : X0; + const int e = uids[u]; + const uint32_t* T = reinterpret_cast(mat ? TP1[e] : TP0[e]); + const int k2 = mat ? K2_1[e] : K2_0[e]; + const int warp = threadIdx.x >> 5, lane = threadIdx.x & 31; + const int g = lane >> 2, t = lane & 3; + const int KT = K >> 4, NTILES = N >> 4; + + __shared__ int rows_sh[MTL * 16]; + for (int i = threadIdx.x; i < MTL * 16; i += W * 32) { + const int m = mg * MTL * 16 + i; + const int code = m < maxm ? members[u * maxm + m] : -1; + rows_sh[i] = code >= 0 ? (code >> 5) * slots + (code & 31) : -1; + } + __syncthreads(); + if (rows_sh[0] < 0) return; // members come first: an empty group + int live = 0; // member tiles with members (a prefix) +#pragma unroll + for (int mt = 0; mt < MTL; ++mt) + if (rows_sh[mt * 16] >= 0) live = mt + 1; + + const half* x0[MTL]; + const half* x1[MTL]; + bool ok0[MTL], ok1[MTL]; +#pragma unroll + for (int mt = 0; mt < MTL; ++mt) { + const int r0 = rows_sh[mt * 16 + g], r1 = rows_sh[mt * 16 + g + 8]; + ok0[mt] = r0 >= 0; + ok1[mt] = r1 >= 0; + x0[mt] = X + (size_t)(r0 < 0 ? 0 : r0) * K + 2 * t; + x1[mt] = X + (size_t)(r1 < 0 ? 0 : r1) * K + 2 * t; + } + + const int per_split = KT / SK, per_warp = per_split / W; + const int kt0 = split * per_split + warp * per_warp; + const int nt0 = blockIdx.y * NT; + + float acc[MTL][NT][2][4]; +#pragma unroll + for (int mt = 0; mt < MTL; ++mt) +#pragma unroll + for (int i = 0; i < NT; ++i) +#pragma unroll + for (int h = 0; h < 2; ++h) +#pragma unroll + for (int c = 0; c < 4; ++c) acc[mt][i][h][c] = 0.f; + + switch (k2) { +#define DSV41_X3PF_CASE(K2_) \ + case K2_: \ + if constexpr (K2_ >= LO && K2_ <= HI) \ + pf_tiles(T, NTILES, kt0, per_warp, nt0, x0, x1, ok0, ok1, live, lane, acc); \ + else \ + __trap(); \ + break; + DSV41_X3PF_CASE(2) + DSV41_X3PF_CASE(3) + DSV41_X3PF_CASE(4) + DSV41_X3PF_CASE(5) + DSV41_X3PF_CASE(6) + DSV41_X3PF_CASE(7) + DSV41_X3PF_CASE(8) + DSV41_X3PF_CASE(9) + DSV41_X3PF_CASE(10) + DSV41_X3PF_CASE(11) + DSV41_X3PF_CASE(12) + DSV41_X3PF_CASE(13) + DSV41_X3PF_CASE(14) + DSV41_X3PF_CASE(15) + DSV41_X3PF_CASE(16) +#undef DSV41_X3PF_CASE + default: + __trap(); + } + + // grouped_kernel's reduction, one member tile at a time: the warps' partials added in warp order + __shared__ float red[W][16][NT * 16]; +#pragma unroll + for (int mt = 0; mt < MTL; ++mt) { + if (mt >= live) break; // CTA-uniform +#pragma unroll + for (int i = 0; i < NT; ++i) +#pragma unroll + for (int h = 0; h < 2; ++h) { + const int col = i * 16 + h * 8 + 2 * t; + red[warp][g][col] = acc[mt][i][h][0]; + red[warp][g][col + 1] = acc[mt][i][h][1]; + red[warp][g + 8][col] = acc[mt][i][h][2]; + red[warp][g + 8][col + 1] = acc[mt][i][h][3]; + } + __syncthreads(); + for (int idx = threadIdx.x; idx < 16 * NT * 16; idx += W * 32) { + const int row = idx / (NT * 16), col = idx % (NT * 16); + const int r = rows_sh[mt * 16 + row]; + if (r < 0) continue; + float s = red[0][row][col]; +#pragma unroll + for (int w = 1; w < W; ++w) s += red[w][row][col]; + Z[(((size_t)mat * SK + split) * P + r) * N + nt0 * 16 + col] = s; + } + __syncthreads(); + } +} + +} // namespace dsv41_x3pf + +// The instantiations dsv41_x3pf_grouped_cuda dispatches: mul1, (nt, mtl) x the (lo, hi) ranges. +#define DSV41_X3PF_INST(NT, MTL, LO, HI) template __global__ void dsv41_x3pf::pf_kernel<2, NT, MTL, LO, HI>( \ + const half*, const half*, const int64_t*, const int64_t*, const int*, const int*, const int*, const int*, \ + const int*, float*, int, int, int, int, int, int); +DSV41_X3PF_INST(4, 4, 8, 8) +DSV41_X3PF_INST(4, 4, 2, 10) +DSV41_X3PF_INST(4, 4, 2, 12) +DSV41_X3PF_INST(8, 2, 8, 8) +DSV41_X3PF_INST(8, 2, 2, 10) +DSV41_X3PF_INST(8, 2, 2, 12) diff --git a/zig/kernels/cuda/deepseek_v41/x3seg.cu b/zig/kernels/cuda/deepseek_v41/x3seg.cu new file mode 100644 index 0000000000..ca6648299e --- /dev/null +++ b/zig/kernels/cuda/deepseek_v41/x3seg.cu @@ -0,0 +1,483 @@ +// Copy of src/tensorfold/families/deepseek_v41/cuda/x3seg.cu (blob 97123dbc731d): sass_check.sh +#define DSV41_X3SEG_NO_TORCH + + +// Segment-table EXL3 linears: several 1-128-row projections in ONE launch, each program upstream's linear_kernel's. + +#include +#include +#ifndef DSV41_X3SEG_NO_TORCH // the kernels alone (nvcc compile test, no torch) +#include +#include +#include +#endif +#include +#include + +#include "decode.cuh" + +using namespace tf_exl3; + +namespace dsv41_x3seg { // program p = (nb, split) of the last segment with p0 <= p: the unfused launch's bits + +constexpr int MAXSEG = 8; +enum DType : int { F16 = 0, BF16 = 1, F32 = 2 }; + +// -- upstream linear.cu's helpers, verbatim ------------------------------------------------------------------------- +__device__ __forceinline__ void load4(const void* p, int dtype, size_t i, float (&v)[4]) { + if (dtype == F32) { + const float4 u = *reinterpret_cast(static_cast(p) + i); + v[0] = u.x; v[1] = u.y; v[2] = u.z; v[3] = u.w; + } else if (dtype == BF16) { + const uint2 u = *reinterpret_cast(static_cast(p) + i); + const float2 a = __bfloat1622float2(*reinterpret_cast(&u.x)); + const float2 b = __bfloat1622float2(*reinterpret_cast(&u.y)); + v[0] = a.x; v[1] = a.y; v[2] = b.x; v[3] = b.y; + } else { + const uint2 u = *reinterpret_cast(static_cast(p) + i); + const float2 a = __half22float2(*reinterpret_cast(&u.x)); + const float2 b = __half22float2(*reinterpret_cast(&u.y)); + v[0] = a.x; v[1] = a.y; v[2] = b.x; v[3] = b.y; + } +} + +__device__ __forceinline__ void store4(void* p, int dtype, size_t i, const float (&v)[4]) { + if (dtype == F32) { + *reinterpret_cast(static_cast(p) + i) = make_float4(v[0], v[1], v[2], v[3]); + } else if (dtype == BF16) { + __nv_bfloat162 a = __floats2bfloat162_rn(v[0], v[1]), b = __floats2bfloat162_rn(v[2], v[3]); + uint2 u; + u.x = *reinterpret_cast(&a); + u.y = *reinterpret_cast(&b); + *reinterpret_cast(static_cast<__nv_bfloat16*>(p) + i) = u; + } else { + half2 a = __floats2half2_rn(v[0], v[1]), b = __floats2half2_rn(v[2], v[3]); + uint2 u; + u.x = *reinterpret_cast(&a); + u.y = *reinterpret_cast(&b); + *reinterpret_cast(static_cast(p) + i) = u; + } +} + +__device__ __forceinline__ void finish(float (&v)[4], int lane, const half* svh, const half* bias, int col) { + fwht128(v, lane); +#pragma unroll + for (int j = 0; j < 4; ++j) { + v[j] = v[j] * HAD_SCALE * __half2float(__ldg(svh + col + j)); + if (bias) v[j] += __half2float(__ldg(bias + col + j)); + } +} + +template +__host__ __device__ constexpr bool step_shuffled() { + return K2 == 2 || K2 == 4; +} + +template +__host__ __device__ constexpr int step_regs() { + return step_shuffled() ? tile_words() / 4 : 8 * lane_words(); +} + +template +__device__ __forceinline__ void load_step(const uint32_t* step, int lane, uint32_t (&raw)[step_regs()]) { + constexpr int TW = tile_words(), LW = lane_words(); + if constexpr (step_shuffled()) { +#pragma unroll + for (int c = 0; c < step_regs(); ++c) raw[c] = __ldg(step + c * 32 + lane); + } else { + int word, offset; + lane_start(lane, word, offset); +#pragma unroll + for (int j = 0; j < 8; ++j) +#pragma unroll + for (int q = 0; q < LW; ++q) raw[j * LW + q] = __ldg(step + j * TW + (word + q) % TW); + } +} + +template +__device__ __forceinline__ void step_lane_words(const uint32_t (&raw)[step_regs()], int j, int lane, bool prev, + uint32_t (&w)[lane_words()]) { + constexpr int TW = tile_words(), LW = lane_words(); + if constexpr (step_shuffled()) { + static_assert(LW == 2, "a lane's windows span two words at 1 and 2 bits"); + constexpr int LPW = 8 / K2; + const uint32_t r = raw[j * TW / 32]; + const int base = (j * TW) % 32; + const uint32_t own = __shfl_sync(0xffffffffu, r, base + lane / LPW); + const uint32_t before = __shfl_sync(0xffffffffu, r, base + (lane / LPW + TW - 1) % TW); + w[0] = prev ? before : own; + w[1] = prev ? own : 0u; + } else { +#pragma unroll + for (int q = 0; q < LW; ++q) w[q] = raw[j * LW + q]; + } +} + +__device__ __forceinline__ void pdl_wait() { +#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900) + asm volatile("griddepcontrol.wait;" ::: "memory"); +#endif +} + +__device__ __forceinline__ void pdl_trigger() { +#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900) + asm volatile("griddepcontrol.launch_dependents;" ::: "memory"); +#endif +} + +// -- the segment tables (kernel parameters: nothing to copy, graph-capture safe) ------------------------------------- +struct RotSeg { + const void* x; // [M, K] rows at x + row * ld (fp16, bf16 or fp32) + const half* suh; // [K] + half* xh; // [M, K] fp16, contiguous + long long ld; + int K, x_dtype; + const void* nw; // TF_DSV41_RMS_FOLD: x is bf16 un-normed; this segment's input is rmsnorm's row + int nw_dtype; // bf16(fp32((x * rn) * w)), rn as Triton's rmsnorm row computes it (null: off) + float inv_k, eps; +}; +struct RotTable { + RotSeg s[MAXSEG]; + int n; +}; + +struct LinSeg { + const half* xh; // [M, K] fp16 (the segment's rot_in output) + const uint32_t* T; // trellis words, upstream's "strips" layout + long long stride_k, stride_nb; + const half* svh; // [N] + void* y; // output element (row, col) at y + row * ld + col + float* Z; // [SK, M, N] fp32 when SK > 1 + int* counters; // [8 * N / 128], left zero + int K, N, SK, wk, p0, ld; +}; +struct LinTable { + LinSeg s[MAXSEG]; + int n; +}; + +// Triton's rmsnorm rn for row `xr` (bf16, K) by 128 threads, in its sm_120 / sm_121 PTX's float64 order +__device__ __forceinline__ double row_rn(const __nv_bfloat16* xr, int K, float inv_k, float eps) { + __shared__ double ws[4]; + const int t = threadIdx.x; + double A[4] = {0.0, 0.0, 0.0, 0.0}; + for (int c = 0; c < K; c += 512) { +#pragma unroll + for (int i = 0; i < 4; ++i) { + const int e = c + t + 128 * i; + const double v = e < K ? static_cast(__bfloat162float(xr[e])) : 0.0; + A[i] = __dadd_rn(A[i], __dmul_rn(v, v)); + } + } + double s = __dadd_rn(__dadd_rn(__dadd_rn(A[0], A[1]), A[2]), A[3]); +#pragma unroll + for (int o = 16; o > 0; o >>= 1) s = __dadd_rn(s, __shfl_xor_sync(0xffffffffu, s, o)); + if ((t & 31) == 0) ws[t >> 5] = s; + __syncthreads(); + const double w0 = __dadd_rn(ws[0], ws[2]), w1 = __dadd_rn(ws[1], ws[3]); + const double ss = __dadd_rn(w0, w1); + const double a = __dadd_rn(__dmul_rn(ss, static_cast(inv_k)), static_cast(eps)); + double rn; + asm("rsqrt.approx.f64 %0, %1;" : "=d"(rn) : "d"(a)); + return rn; +} + +__global__ void __launch_bounds__(128) seg_rot_in_kernel(const RotTable tab) { // upstream rot_in, z = segment + pdl_wait(); // x belongs to the kernels before (every CTA waits) + pdl_trigger(); + RotSeg s = tab.s[0]; +#pragma unroll + for (int i = 1; i < MAXSEG; ++i) + if (i == (int)blockIdx.z) s = tab.s[i]; + const int blk = blockIdx.x * 4 + (threadIdx.x >> 5), row = blockIdx.y, lane = threadIdx.x & 31; + double rn = 0.0; + if (s.nw != nullptr) // the whole CTA (uniform: its segment and row) + rn = row_rn(static_cast(s.x) + (size_t)row * s.ld, s.K, s.inv_k, s.eps); + if (blk * 128 >= s.K) return; + const int k = blk * 128 + 4 * lane; + float v[4], sc[4]; + load4(s.x, s.x_dtype, (size_t)row * s.ld + k, v); + if (s.nw != nullptr) { // _rms_row's y = (x * rn) * w -> fp32 -> bf16 + float w[4]; + load4(s.nw, s.nw_dtype, k, w); +#pragma unroll + for (int j = 0; j < 4; ++j) { + const double y = __dmul_rn(__dmul_rn(rn, static_cast(v[j])), static_cast(w[j])); + v[j] = __bfloat162float(__float2bfloat16_rn(__double2float_rn(y))); + } + } + load4(s.suh, F16, k, sc); +#pragma unroll + for (int j = 0; j < 4; ++j) v[j] *= sc[j]; + fwht128(v, lane); +#pragma unroll + for (int j = 0; j < 4; ++j) v[j] *= HAD_SCALE; + store4(s.xh, F16, (size_t)row * s.K + k, v); +} + +// upstream's linear_kernel at its segment's (nb, split, NB); MINB bounds registers only, never values +template +__global__ void __launch_bounds__(WK * 32, MINB) seg_linear_kernel(const LinTable tab, int y_dtype, int M) { + constexpr int TW = tile_words(); + constexpr int LW = lane_words(); + extern __shared__ __align__(16) float red[]; // WK * RH * 128 floats + __shared__ int last; + const int RH = min(M, 8); + + LinSeg s = tab.s[0]; +#pragma unroll + for (int i = 1; i < MAXSEG; ++i) + if (i < tab.n && (int)blockIdx.x >= tab.s[i].p0) s = tab.s[i]; + const half* __restrict__ xh = s.xh; + const half* __restrict__ svh = s.svh; + void* __restrict__ y = s.y; + float* __restrict__ Z = s.Z; + int* __restrict__ counters = s.counters; + const int K = s.K, N = s.N, SK = s.SK, wk = s.wk, ld = s.ld; + + const int NB = N >> 7, q = (int)blockIdx.x - s.p0, nb = q % NB, split = q / NB; + const int warp = threadIdx.x >> 5, lane = threadIdx.x & 31; + const int g = lane >> 2, t = lane & 3; + const bool on = warp < wk; // WK 4 segment, 8-warp launch: warps 4-7 idle, same sums + const int per_warp = (K >> 4) / SK / wk; + const int kt0 = split * (per_warp * wk) + warp * per_warp; + const int steps = on ? per_warp : 0; + const uint32_t* tiles = s.T + nb * s.stride_nb; + const int col0 = nb * 128; + bool prev = false; + if constexpr (step_shuffled()) { + int word, offset; + lane_start(lane, word, offset); + prev = word != lane / (8 / K2); + } + + for (int m0 = 0, pass = 0; m0 < M; m0 += 16, ++pass) { + const int R = min(16, M - m0); + + float acc[8][2][4]; +#pragma unroll + for (int i = 0; i < 8; ++i) +#pragma unroll + for (int h = 0; h < 2; ++h) +#pragma unroll + for (int c = 0; c < 4; ++c) acc[i][h][c] = 0.f; + + const int r0 = m0 + (g < R ? g : R - 1), r1 = m0 + (g + 8 < R ? g + 8 : R - 1); + const half* x0 = xh + (size_t)r0 * K; + const half* x1 = xh + (size_t)r1 * K; + const uint32_t* tile = tiles + (size_t)kt0 * s.stride_k; + constexpr bool PF = K2 <= 12; + constexpr int SR = step_regs(); + uint32_t cur[PF ? SR : 1], nxt[PF ? SR : 1]; + if constexpr (PF) { + if (steps > 0) load_step(tile, lane, cur); + } + if (pass == 0) pdl_wait(); // weights above, xh / Z / counters below +#pragma unroll 1 + for (int i = 0; i < steps; ++i) { + const int kt = kt0 + i; + uint32_t a[4]; + a[0] = __ldg(reinterpret_cast(x0 + kt * 16 + 2 * t)); + a[1] = __ldg(reinterpret_cast(x1 + kt * 16 + 2 * t)); + a[2] = __ldg(reinterpret_cast(x0 + kt * 16 + 2 * t + 8)); + a[3] = __ldg(reinterpret_cast(x1 + kt * 16 + 2 * t + 8)); + if constexpr (PF) { + if (i + 1 < steps) load_step(tile + (size_t)(i + 1) * s.stride_k, lane, nxt); + } +#pragma unroll + for (int j = 0; j < 8; ++j) { + uint32_t w[LW]; + if constexpr (PF) step_lane_words(cur, j, lane, prev, w); + else ldg_lane_words(tile + (size_t)i * s.stride_k + j * TW, lane, w); + uint32_t b0[2], b1[2]; + decode_lane(w, lane, b0, b1); + mma16816(acc[j][0], a, b0); + mma16816(acc[j][1], a, b1); + } + if constexpr (PF) { +#pragma unroll + for (int qq = 0; qq < SR; ++qq) cur[qq] = nxt[qq]; + } + } + + if (m0 + 16 >= M) pdl_trigger(); // the last pass's weights are read + + // the warps' sums, added in warp order, rows 0-7 of the pass and then rows 8-15 + for (int rlo = 0; rlo < R; rlo += 8) { + const int rn = min(R - rlo, 8); + __syncthreads(); + if (on && g < RH) { +#pragma unroll + for (int i = 0; i < 8; ++i) +#pragma unroll + for (int h = 0; h < 2; ++h) { + const int col = i * 16 + h * 8 + 2 * t; + *reinterpret_cast(red + (warp * RH + g) * 128 + col) = + rlo ? make_float2(acc[i][h][2], acc[i][h][3]) : make_float2(acc[i][h][0], acc[i][h][1]); + } + } + __syncthreads(); + + if (SK == 1) { + for (int r = warp; r < rn; r += WK) { + float v[4]; + const float4 u = *reinterpret_cast(red + r * 128 + 4 * lane); + v[0] = u.x; v[1] = u.y; v[2] = u.z; v[3] = u.w; + for (int w = 1; w < wk; ++w) { + const float4 qv = *reinterpret_cast(red + (w * RH + r) * 128 + 4 * lane); + v[0] += qv.x; v[1] += qv.y; v[2] += qv.z; v[3] += qv.w; + } + finish(v, lane, svh, nullptr, col0 + 4 * lane); + store4(y, y_dtype, (size_t)(m0 + rlo + r) * ld + col0 + 4 * lane, v); + } + } else { + for (int idx = threadIdx.x; idx < rn * 32; idx += WK * 32) { + const int r = idx >> 5, c = 4 * (idx & 31); + float4 sm = *reinterpret_cast(red + r * 128 + c); + for (int w = 1; w < wk; ++w) { + const float4 qv = *reinterpret_cast(red + (w * RH + r) * 128 + c); + sm.x += qv.x; sm.y += qv.y; sm.z += qv.z; sm.w += qv.w; + } + *reinterpret_cast(Z + ((size_t)split * M + m0 + rlo + r) * N + col0 + c) = sm; + } + } + } + if (SK > 1) { + __threadfence(); + __syncthreads(); + if (threadIdx.x == 0) last = atomicAdd(counters + pass * NB + nb, 1) == SK - 1; + __syncthreads(); + if (last) { + __threadfence(); + for (int r = warp; r < R; r += WK) { + const size_t at = ((size_t)m0 + r) * N + col0 + 4 * lane; + float4 sm = __ldcg(reinterpret_cast(Z + at)); + for (int qq = 1; qq < SK; ++qq) { + const float4 u = __ldcg(reinterpret_cast(Z + (size_t)qq * M * N + at)); + sm.x += u.x; sm.y += u.y; sm.z += u.z; sm.w += u.w; + } + float v[4] = {sm.x, sm.y, sm.z, sm.w}; + finish(v, lane, svh, nullptr, col0 + 4 * lane); + store4(y, y_dtype, (size_t)(m0 + r) * ld + col0 + 4 * lane, v); + } + if (threadIdx.x == 0) counters[pass * NB + nb] = 0; + } + } + __syncthreads(); + } +} + +#ifndef DSV41_X3SEG_NO_TORCH +int dtype_of(const at::Tensor& t) { + return t.scalar_type() == at::kFloat ? F32 : t.scalar_type() == at::kBFloat16 ? BF16 : F16; +} +#endif + +} // namespace dsv41_x3seg + +#ifndef DSV41_X3SEG_NO_TORCH + +using namespace dsv41_x3seg; + +namespace { +// a launch with the PDL attribute when pdl (cudaLaunchKernelEx: graph-capturable, the attribute becomes the edge) +template +void launch_ex(K kernel, dim3 grid, dim3 block, int smem, bool pdl, A... args) { + cudaLaunchConfig_t cfg = {}; + cfg.gridDim = grid; + cfg.blockDim = block; + cfg.dynamicSmemBytes = (size_t)smem; + cfg.stream = at::cuda::getCurrentCUDAStream(); + cudaLaunchAttribute attr[1]; + attr[0].id = cudaLaunchAttributeProgrammaticStreamSerialization; + attr[0].val.programmaticStreamSerializationAllowed = 1; + cfg.attrs = attr; + cfg.numAttrs = pdl ? 1 : 0; + C10_CUDA_CHECK(cudaLaunchKernelEx(&cfg, kernel, args...)); +} +} // namespace + +void dsv41_x3seg_rot_in_cuda(const std::vector& xs, const std::vector& suh, at::Tensor& xh, + const std::vector& xh_off, bool pdl, const std::vector& nw, + double eps) { + RotTable tab{}; + tab.n = (int)xs.size(); + const int M = (int)xs[0].size(0); + int kmax = 0; + for (int i = 0; i < tab.n; ++i) { + const int K = (int)xs[i].size(1); + tab.s[i] = RotSeg{xs[i].data_ptr(), reinterpret_cast(suh[i].data_ptr()), + reinterpret_cast(xh.data_ptr()) + xh_off[i], (long long)xs[i].stride(0), K, + dtype_of(xs[i]), nullptr, 0, 0.f, 0.f}; + if (i < (int)nw.size() && nw[i].numel()) { // fp32 1 / K and eps: Triton's float arguments + TORCH_CHECK(xs[i].scalar_type() == at::kBFloat16 && nw[i].numel() == K && nw[i].is_contiguous(), + "x3seg rot_in: the folded norm takes bf16 rows and a [K] weight"); + tab.s[i].nw = nw[i].data_ptr(); + tab.s[i].nw_dtype = dtype_of(nw[i]); + tab.s[i].inv_k = static_cast(1.0 / K); + tab.s[i].eps = static_cast(eps); + } + kmax = std::max(kmax, K); + } + dim3 grid((unsigned)((kmax / 128 + 3) / 4), (unsigned)M, (unsigned)tab.n); + launch_ex(seg_rot_in_kernel, grid, dim3(128), 0, pdl, tab); +} + +// meta: 11 ints a segment (K, N, SK, wk, stride_k, stride_nb, xh_off, z_off, c_off, col, p0); ys[i] its output +void dsv41_x3seg_linear_cuda(const at::Tensor& xh, const std::vector& T, + const std::vector& svh, const std::vector& ys, + const c10::optional& Z, at::Tensor& counters, + const std::vector& meta, int64_t programs, int64_t K2, int64_t WK, + int64_t minb, bool pdl) { + LinTable tab{}; + tab.n = (int)T.size(); + const int M = (int)ys[0].size(0); + const int y_dtype = dtype_of(ys[0]); + float* zptr = Z ? Z->data_ptr() : nullptr; + for (int i = 0; i < tab.n; ++i) { + const int64_t* m = meta.data() + 11 * i; + const size_t esz = ys[i].element_size(); + tab.s[i] = LinSeg{reinterpret_cast(xh.data_ptr()) + m[6], + reinterpret_cast(T[i].data_ptr()), (long long)m[4], (long long)m[5], + reinterpret_cast(svh[i].data_ptr()), + static_cast(ys[i].data_ptr()) + (size_t)m[9] * esz, + m[2] > 1 ? zptr + m[7] : nullptr, counters.data_ptr() + m[8], + (int)m[0], (int)m[1], (int)m[2], (int)m[3], (int)m[10], (int)ys[i].stride(0)}; + } + const int smem = (int)(WK * std::min(M, 8) * 128 * sizeof(float)); +#define TF_SEG_LAUNCH(K2_, WK_, MINB_) \ + if (K2 == K2_ && WK == WK_ && (WK_ == 8 || minb == MINB_)) { \ + auto kernel = seg_linear_kernel; \ + if (smem > 48 * 1024) cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, smem); \ + launch_ex(kernel, dim3((unsigned)programs), dim3((unsigned)(WK_ * 32)), smem, pdl, tab, y_dtype, M); \ + return; \ + } +#define TF_SEG_WIDTH(K2_) TF_SEG_LAUNCH(K2_, 4, 3) TF_SEG_LAUNCH(K2_, 4, 2) TF_SEG_LAUNCH(K2_, 8, 1) + TF_SEG_WIDTH(4) TF_SEG_WIDTH(6) TF_SEG_WIDTH(8) TF_SEG_WIDTH(10) TF_SEG_WIDTH(12) TF_SEG_WIDTH(16) +#undef TF_SEG_WIDTH +#undef TF_SEG_LAUNCH + TORCH_CHECK(false, "x3seg: unsupported EXL3 width / warps / blocks: K2=", K2, " WK=", WK, " MINB=", minb); +} +#endif // DSV41_X3SEG_NO_TORCH + +// The instantiations dsv41_x3seg_linear_cuda launches (TF_SEG_WIDTH), mul1; not when dense3.cu includes this file +#ifndef DSV41_DENSE3_NO_TORCH +template __global__ void dsv41_x3seg::seg_linear_kernel<4, 2, 4, 3>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_x3seg::seg_linear_kernel<4, 2, 4, 2>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_x3seg::seg_linear_kernel<4, 2, 8, 1>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_x3seg::seg_linear_kernel<6, 2, 4, 3>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_x3seg::seg_linear_kernel<6, 2, 4, 2>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_x3seg::seg_linear_kernel<6, 2, 8, 1>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_x3seg::seg_linear_kernel<8, 2, 4, 3>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_x3seg::seg_linear_kernel<8, 2, 4, 2>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_x3seg::seg_linear_kernel<8, 2, 8, 1>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_x3seg::seg_linear_kernel<10, 2, 4, 3>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_x3seg::seg_linear_kernel<10, 2, 4, 2>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_x3seg::seg_linear_kernel<10, 2, 8, 1>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_x3seg::seg_linear_kernel<12, 2, 4, 3>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_x3seg::seg_linear_kernel<12, 2, 4, 2>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_x3seg::seg_linear_kernel<12, 2, 8, 1>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_x3seg::seg_linear_kernel<16, 2, 4, 3>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_x3seg::seg_linear_kernel<16, 2, 4, 2>(const dsv41_x3seg::LinTable, int, int); +template __global__ void dsv41_x3seg::seg_linear_kernel<16, 2, 8, 1>(const dsv41_x3seg::LinTable, int, int); +#endif diff --git a/zig/src/cuda/abi.zig b/zig/src/cuda/abi.zig index b784479964..10747bd88e 100644 --- a/zig/src/cuda/abi.zig +++ b/zig/src/cuda/abi.zig @@ -39,6 +39,8 @@ pub const DeviceAttribute = enum(c_int) { compute_capability_minor = 76, max_shared_memory_per_multiprocessor = 81, max_shared_memory_per_block_optin = 97, + virtual_memory_management_supported = 102, + gpu_direct_rdma_with_cuda_vmm_supported = 110, }; pub const FunctionAttribute = enum(c_int) { @@ -149,6 +151,12 @@ pub const ExecUpdateResultInfo = extern struct { error_from_node: GraphNode, }; +/// CU_EVENT_RECORD_EXTERNAL: in a capture, an event record node outside work can wait on; else a plain record. +pub const event_record_external: c_uint = 0x1; + +/// CU_MEMORYTYPE_DEVICE +pub const memory_type_device: c_uint = 2; + pub const jit_info_log_buffer: c_int = 3; pub const jit_info_log_buffer_size: c_int = 4; pub const jit_error_log_buffer: c_int = 5; @@ -169,6 +177,7 @@ pub const Api = struct { cuDeviceGetAttribute: *const fn (*c_int, DeviceAttribute, Device) callconv(.c) R, cuDeviceTotalMem_v2: *const fn (*usize, Device) callconv(.c) R, cuDevicePrimaryCtxRetain: *const fn (*Context, Device) callconv(.c) R, + cuDevicePrimaryCtxSetFlags_v2: *const fn (Device, c_uint) callconv(.c) R, cuDevicePrimaryCtxRelease_v2: *const fn (Device) callconv(.c) R, cuCtxSetCurrent: *const fn (Context) callconv(.c) R, cuCtxGetCurrent: *const fn (*Context) callconv(.c) R, @@ -194,6 +203,8 @@ pub const Api = struct { cuMemsetD8Async: *const fn (DevicePtr, u8, usize, Stream) callconv(.c) R, cuMemsetD32Async: *const fn (DevicePtr, c_uint, usize, Stream) callconv(.c) R, cuStreamCreate: *const fn (*Stream, c_uint) callconv(.c) R, + cuStreamCreateWithPriority: *const fn (*Stream, c_uint, c_int) callconv(.c) R, + cuCtxGetStreamPriorityRange: *const fn (*c_int, *c_int) callconv(.c) R, cuStreamDestroy_v2: *const fn (Stream) callconv(.c) R, cuStreamSynchronize: *const fn (Stream) callconv(.c) R, cuStreamWaitEvent: *const fn (Stream, Event, c_uint) callconv(.c) R, @@ -201,6 +212,7 @@ pub const Api = struct { cuEventCreate: *const fn (*Event, c_uint) callconv(.c) R, cuEventDestroy_v2: *const fn (Event) callconv(.c) R, cuEventRecord: *const fn (Event, Stream) callconv(.c) R, + cuEventRecordWithFlags: *const fn (Event, Stream, c_uint) callconv(.c) R, cuEventSynchronize: *const fn (Event) callconv(.c) R, cuEventQuery: *const fn (Event) callconv(.c) R, cuEventElapsedTime: *const fn (*f32, Event, Event) callconv(.c) R, @@ -242,3 +254,30 @@ comptime { std.debug.assert(@sizeOf(KernelNodeParams) == 72 and @offsetOf(KernelNodeParams, "params") == 40); std.debug.assert(@sizeOf(ExecUpdateResultInfo) == 24); } + +test "CUDA device attribute matches the RDMA VMM capability ABI" { + // CUDA's CUdevice_attribute assigns 110 to RDMA VMM support; 126 is the memory sync domain count. + try @import("std").testing.expectEqual(@as(c_int, 110), @backingInt( + DeviceAttribute.gpu_direct_rdma_with_cuda_vmm_supported, + )); +} + +test "Memcpy2D has CUDA_MEMCPY2D's layout: each side's seven fields, then the copy's width and rows" { + const t = @import("std").testing; + const want = [_]struct { []const u8, usize }{ + .{ "src_x", 0 }, .{ "src_y", 8 }, .{ "src_type", 16 }, .{ "src_host", 24 }, .{ "src", 32 }, .{ "src_array", 40 }, + .{ "src_pitch", 48 }, .{ "dst_x", 56 }, .{ "dst_y", 64 }, .{ "dst_type", 72 }, .{ "dst_host", 80 }, .{ "dst", 88 }, + .{ "dst_array", 96 }, .{ "dst_pitch", 104 }, .{ "width", 112 }, .{ "height", 120 }, + }; + const names = @typeInfo(Memcpy2D).@"struct".field_names; + try t.expectEqual(want.len, names.len); + inline for (names, 0..) |name, i| { + try t.expectEqualStrings(want[i][0], name); + try t.expectEqual(want[i][1], @offsetOf(Memcpy2D, name)); + } + try t.expectEqual(@as(usize, 128), @sizeOf(Memcpy2D)); + // a default copy is device to device, so a caller sets only addresses, pitches and the extent + const c: Memcpy2D = .{ .src = 0, .src_pitch = 0, .dst = 0, .dst_pitch = 0, .width = 0, .height = 0 }; + try t.expectEqual(@as(c_int, memory_type_device), c.src_type); + try t.expectEqual(@as(c_int, memory_type_device), c.dst_type); +} diff --git a/zig/src/cuda/aot.zig b/zig/src/cuda/aot.zig index c957cc9601..780614f3c4 100644 --- a/zig/src/cuda/aot.zig +++ b/zig/src/cuda/aot.zig @@ -45,6 +45,20 @@ const KernelJson = struct { }; const SetJson = struct { kernels: []KernelJson }; +/// A variant's description in aot.json, and a whole set's: what `Set.find` matches launches against. +pub const Spec = KernelJson; +pub const SpecSet = SetJson; + +/// aot.json parsed without loading any cubin (host-side checks of variant selection). +pub fn parseSpecs(gpa: std.mem.Allocator, text: []const u8) !std.json.Parsed(SpecSet) { + return std.json.parseFromSlice(SpecSet, gpa, text, .{ .ignore_unknown_fields = true, .allocate = .alloc_always }); +} + +/// Whether variant `k` can serve these arguments and constexprs: `Set.find` takes an exact variant first, else this. +pub fn specMatches(k: Spec, args: []const Arg, consts: []const Const) bool { + return matches(k, args, consts, .general); +} + /// One argument of a launch, by the kernel's parameter name. pub const Arg = struct { name: []const u8, @@ -81,6 +95,8 @@ pub const Set = struct { parsed: std.json.Parsed(SetJson), variants: []Variant, gpa: std.mem.Allocator, + /// every distinct lookup key and its outcome, when the caller attaches a Trace (the set destroys it) + trace: ?*Trace = null, /// Loads every cubin listed in `dir`/aot.json into its own module. pub fn load(gpa: std.mem.Allocator, io: std.Io, d: *const Driver, device: abi.Device, dir: []const u8) !Set { @@ -88,7 +104,7 @@ pub const Set = struct { defer gpa.free(path); const text = try std.Io.Dir.cwd().readFileAlloc(io, path, gpa, .limited(1 << 24)); defer gpa.free(text); - const parsed = try std.json.parseFromSlice(SetJson, gpa, text, .{ .ignore_unknown_fields = true, .allocate = .alloc_always }); + const parsed = try parseSpecs(gpa, text); errdefer parsed.deinit(); const variants = try gpa.alloc(Variant, parsed.value.kernels.len); var n: usize = 0; @@ -112,6 +128,7 @@ pub const Set = struct { } pub fn deinit(self: *Set) void { + if (self.trace) |t| t.destroy(); for (self.variants) |*v| v.kernel.unload(); self.gpa.free(self.variants); self.parsed.deinit(); @@ -122,13 +139,14 @@ pub const Set = struct { pub fn find(self: *const Set, function: []const u8, args: []const Arg, consts: []const Const) !*const Variant { for (self.variants) |*v| { if (!std.mem.eql(u8, v.spec.@"fn", function)) continue; - if (matches(v.spec, args, consts, .exact)) return v; + if (matches(v.spec, args, consts, .exact)) return self.hit(function, args, consts, v); } // a variant that assumes less of an argument than the launch gives computes the same for it, so it may serve for (self.variants) |*v| { if (!std.mem.eql(u8, v.spec.@"fn", function)) continue; - if (matches(v.spec, args, consts, .general)) return v; + if (matches(v.spec, args, consts, .general)) return self.hit(function, args, consts, v); } + if (self.trace) |t| t.note(function, args, consts, null); std.log.err("no captured Triton variant of {s} for this launch:", .{function}); for (args) |a| switch (a.value) { .ptr => |p| std.log.err(" {s}: {s} at {x} (16-aligned {})", .{ a.name, p.ty, p.addr, p.addr % 16 == 0 }), @@ -136,9 +154,19 @@ pub const Set = struct { .f32 => |x| std.log.err(" {s}: fp32 {d}", .{ a.name, x }), .u64 => |x| std.log.err(" {s}: u64 {d}", .{ a.name, x }), }; + // the constexprs pick the variant as much as the arguments do: without them the gap cannot be named + for (consts) |c| { + if (c.int) |x| std.log.err(" constexpr {s} = {d}", .{ c.name, x }) else if (c.f32) |x| std.log.err(" constexpr {s} = {d}", .{ c.name, x }); + } return error.MissingTritonVariant; } + /// A found variant, noted in the trace when one is attached. + fn hit(self: *const Set, function: []const u8, args: []const Arg, consts: []const Const, v: *const Variant) *const Variant { + if (self.trace) |t| t.note(function, args, consts, v.spec.hash); + return v; + } + /// Whether the set holds any variant of `function`. pub fn has(self: *const Set, function: []const u8) bool { for (self.variants) |v| if (std.mem.eql(u8, v.spec.@"fn", function)) return true; @@ -173,6 +201,89 @@ pub const Set = struct { } }; +/// One JSON line per distinct `find` key (as `matches` reads it) and its hit hash or null, rewritten to `path`. +pub const Trace = struct { + gpa: std.mem.Allocator, + io: std.Io, + path: []const u8, + mutex: std.Io.Mutex = .init, + seen: std.AutoHashMapUnmanaged(u64, void) = .empty, + text: std.ArrayList(u8) = .empty, + + pub fn create(gpa: std.mem.Allocator, io: std.Io, path: []const u8) !*Trace { + const t = try gpa.create(Trace); + errdefer gpa.destroy(t); + t.* = .{ .gpa = gpa, .io = io, .path = try gpa.dupe(u8, path) }; + return t; + } + + pub fn destroy(t: *Trace) void { + t.seen.deinit(t.gpa); + t.text.deinit(t.gpa); + t.gpa.free(t.path); + t.gpa.destroy(t); + } + + /// The key's hash: what `matches` tells apart, nothing else (equal keys pick the same variant). + pub fn key(function: []const u8, args: []const Arg, consts: []const Const) u64 { + var h = std.hash.Wyhash.init(0); + h.update(function); + for (args) |a| { + h.update(a.name); + h.update(class(a)); + if (a.value == .ptr) h.update(a.value.ptr.ty); + } + for (consts) |c| { + h.update(c.name); + if (c.int) |x| h.update(std.mem.asBytes(&x)); + if (c.f32) |x| h.update(std.mem.asBytes(&@as(u32, @bitCast(x)))); + h.update("|"); + } + return h.final(); + } + + fn class(a: Arg) []const u8 { + return switch (a.value) { + .i32 => |x| if (x == 1) "1" else if (@mod(x, 16) == 0) "div16" else "int", + .ptr => |x| if (x.addr % 16 == 0) "ptr/16" else "ptr", + .f32 => "fp32", + .u64 => |x| if (x % 16 == 0) "u64/16" else "u64", + }; + } + + pub fn note(t: *Trace, function: []const u8, args: []const Arg, consts: []const Const, hash: ?[]const u8) void { + const k = key(function, args, consts); + t.mutex.lockUncancelable(t.io); + defer t.mutex.unlock(t.io); + const got = t.seen.getOrPut(t.gpa, k) catch return; + if (got.found_existing) return; + t.line(function, args, consts, hash) catch return; + std.Io.Dir.cwd().writeFile(t.io, .{ .sub_path = t.path, .data = t.text.items }) catch {}; + } + + fn line(t: *Trace, function: []const u8, args: []const Arg, consts: []const Const, hash: ?[]const u8) !void { + var w: std.Io.Writer.Allocating = .fromArrayList(t.gpa, &t.text); + defer t.text = w.toArrayList(); + const o = &w.writer; + try o.print("{{\"fn\":\"{s}\",\"hit\":", .{function}); + if (hash) |x| try o.print("\"{s}\"", .{x}) else try o.writeAll("null"); + try o.writeAll(",\"args\":{"); + for (args, 0..) |a, i| { + const ty = switch (a.value) { + .ptr => |x| x.ty, + else => "", + }; + try o.print("{s}\"{s}\":\"{s}{s}{s}\"", .{ if (i > 0) "," else "", a.name, ty, if (ty.len > 0) " " else "", class(a) }); + } + try o.writeAll("},\"consts\":{"); + for (consts, 0..) |c, i| { + try o.print("{s}\"{s}\":", .{ if (i > 0) "," else "", c.name }); + if (c.int) |x| try o.print("{d}", .{x}) else if (c.f32) |x| try o.print("{e}", .{x}) else try o.writeAll("null"); + } + try o.writeAll("}}\n"); + } +}; + fn lookup(args: []const Arg, name: []const u8) ?Arg { for (args) |a| if (std.mem.eql(u8, a.name, name)) return a; return null; @@ -237,6 +348,70 @@ fn matches(k: KernelJson, args: []const Arg, consts: []const Const, fit: Fit) bo return runtime == k.params.len; } +// -- tests: variant selection over an aot.json fixture, and the trace's one line per distinct key ------------------- + +const fixture = + \\{"kernels": [ + \\ {"fn": "add", "hash": "a16", "name": "add_k", "num_warps": 4, "extra": "ignored", + \\ "params": [{"name": "x", "type": "fp16", "div16": true, "nospec": false}, + \\ {"name": "n", "type": "i32", "div16": true, "nospec": false}], + \\ "consts": {"BLOCK": {"int": 128}}}, + \\ {"fn": "add", "hash": "a1", "name": "add_k", "num_warps": 4, + \\ "params": [{"name": "x", "type": "fp16", "div16": false, "nospec": false}, + \\ {"name": "n", "type": "i32", "div16": false, "nospec": false}], + \\ "consts": {"BLOCK": {"int": 128}}}, + \\ {"fn": "add", "hash": "one", "name": "add_k", "num_warps": 4, + \\ "params": [{"name": "x", "type": "fp16", "div16": true, "nospec": false}], + \\ "consts": {"BLOCK": {"int": 128}, "n": {"int": 1}}} + \\]} +; + +fn picked(set: SpecSet, args: []const Arg, consts: []const Const) ?[]const u8 { + for (set.kernels) |k| if (specMatches(k, args, consts)) return k.hash; + return null; +} + +test "aot specs: Triton's specialization picks the variant; a launch no variant covers picks none" { + const t = std.testing; + const p = try parseSpecs(t.allocator, fixture); + defer p.deinit(); + try t.expectEqual(@as(usize, 3), p.value.kernels.len); + try t.expectEqual(@as(u32, 1), p.value.kernels[0].num_ctas); // a field aot.json leaves out takes its default + const block = [_]Const{ci("BLOCK", 128)}; + try t.expectEqualStrings("a16", picked(p.value, &.{ ptr("x", "fp16", 0x1000), int("n", 64) }, &block).?); + try t.expectEqualStrings("a1", picked(p.value, &.{ ptr("x", "fp16", 0x1004), int("n", 63) }, &block).?); + // n == 1 was folded into a constexpr when Triton compiled it: only the variant without an n parameter takes it + try t.expectEqualStrings("one", picked(p.value, &.{ ptr("x", "fp16", 0x1000), int("n", 1) }, &block).?); + // an unaligned pointer with a 16-divisible count: the variant that assumes neither serves it + try t.expectEqualStrings("a1", picked(p.value, &.{ ptr("x", "fp16", 0x1004), int("n", 64) }, &block).?); + // another dtype, another constexpr: no variant was captured + try t.expectEqual(@as(?[]const u8, null), picked(p.value, &.{ ptr("x", "bf16", 0x1000), int("n", 64) }, &block)); + try t.expectEqual(@as(?[]const u8, null), picked(p.value, &.{ ptr("x", "fp16", 0x1000), int("n", 64) }, &.{ci("BLOCK", 256)})); +} + +test "aot trace: one line per distinct key, a launch that differs only in what matches ignores adds none" { + const t = std.testing; + var tmp = t.tmpDir(.{}); + defer tmp.cleanup(); + const dir = try tmp.dir.realPathFileAlloc(t.io, ".", t.allocator); + defer t.allocator.free(dir); + const path = try std.fs.path.join(t.allocator, &.{ dir, "trace.jsonl" }); + defer t.allocator.free(path); + const tr = try Trace.create(t.allocator, t.io, path); + defer tr.destroy(); + const block = [_]Const{ci("BLOCK", 128)}; + tr.note("add", &.{ ptr("x", "fp16", 0x1000), int("n", 64) }, &block, "a16"); + tr.note("add", &.{ ptr("x", "fp16", 0x2000), int("n", 32) }, &block, "a16"); // the same key: both 16-aligned + tr.note("add", &.{ ptr("x", "fp16", 0x1004), int("n", 64) }, &block, null); + const got = try std.Io.Dir.cwd().readFileAlloc(t.io, path, t.allocator, .limited(1 << 16)); + defer t.allocator.free(got); + try t.expectEqualStrings( + \\{"fn":"add","hit":"a16","args":{"x":"fp16 ptr/16","n":"div16"},"consts":{"BLOCK":128}} + \\{"fn":"add","hit":null,"args":{"x":"fp16 ptr","n":"div16"},"consts":{"BLOCK":128}} + \\ + , got); +} + /// A set parsed from aot.json text whose variants carry no loaded cubin, so find can be tested without a GPU. fn testSet(gpa: std.mem.Allocator, text: []const u8) !Set { const parsed = try std.json.parseFromSlice(SetJson, gpa, text, .{ diff --git a/zig/src/cuda/arena.zig b/zig/src/cuda/arena.zig new file mode 100644 index 0000000000..f33ab51f85 --- /dev/null +++ b/zig/src/cuda/arena.zig @@ -0,0 +1,480 @@ +//! A device sub-allocator under DeviceBuffer.alloc: buffers carved from a few large chunks, not one cuMemAlloc each. + +const std = @import("std"); +const abi = @import("abi.zig"); +const Driver = @import("driver.zig").Driver; +const memory = @import("memory.zig"); +const quiet = @import("builtin").is_test; // a test's stderr is the build runner's channel + +pub const Error = error{ DriverUnavailable, MissingSymbol, Unsupported, CudaFailed, OutOfMemory }; + +/// Where chunks come from: `chunk` maps `len` bytes (a multiple of `granularity`), `release` gives a chunk back. +pub const Backend = struct { + ctx: *anyopaque, + granularity: usize, + chunk: *const fn (ctx: *anyopaque, len: usize) ?u64, + release: *const fn (ctx: *anyopaque, base: u64, len: usize) void, + name: []const u8, +}; + +pub const Options = struct { + small_max: usize = 4 << 20, // requests up to this many bytes share chunks + small_chunk: usize = 64 << 20, // a shared chunk's bytes, rounded up to the granularity + alignment: usize = 512, // every range starts at a multiple of this +}; + +pub const Stats = struct { small: u64 = 0, large: u64 = 0, reused: u64 = 0, fallback: u64 = 0, chunk_bytes: u64 = 0 }; + +/// Small requests bump 512-byte aligned through shared chunks and are reused by size; large ones get their own chunk. +pub const Arena = struct { + gpa: std.mem.Allocator, + be: Backend, + o: Options, + mutex: std.atomic.Mutex = .unlocked, + small: std.ArrayList([2]u64) = .empty, // shared chunks (base, len) + cursor: u64 = 0, // the current shared chunk's next free byte + limit: u64 = 0, + free_small: std.AutoHashMapUnmanaged(u64, std.ArrayList(u64)) = .empty, // freed small ranges by rounded size + live: std.AutoHashMapUnmanaged(u64, u64) = .empty, // live small ranges: address to rounded size + large: std.AutoHashMapUnmanaged(u64, u64) = .empty, // large chunks: address to mapped size + dead: [2]u32 = .{ 0, 0 }, // removals from `live` and `large` since each was last rehashed + stats: Stats = .{}, + + pub fn init(gpa: std.mem.Allocator, be: Backend, o: Options) Arena { + return .{ .gpa = gpa, .be = be, .o = o }; + } + + pub fn deinit(a: *Arena) void { + var it = a.large.iterator(); + while (it.next()) |e| a.be.release(a.be.ctx, e.key_ptr.*, e.value_ptr.*); + for (a.small.items) |c| a.be.release(a.be.ctx, c[0], c[1]); + var f = a.free_small.valueIterator(); + while (f.next()) |l| l.deinit(a.gpa); + a.free_small.deinit(a.gpa); + a.live.deinit(a.gpa); + a.large.deinit(a.gpa); + a.small.deinit(a.gpa); + a.* = undefined; + } + + fn lock(a: *Arena) void { + while (!a.mutex.tryLock()) std.Thread.yield() catch {}; + } + + fn roundUp(x: usize, m: usize) usize { + return std.mem.alignForward(usize, x, m); + } + + /// An address for `len` bytes, or null (the caller then allocates as before). + pub fn alloc(a: *Arena, len: usize) ?u64 { + if (len == 0) return null; + a.lock(); + defer a.mutex.unlock(); + return a.allocLocked(len) catch null; + } + + fn allocLocked(a: *Arena, len: usize) !?u64 { + if (len > a.o.small_max) { + const n = roundUp(len, a.be.granularity); + const p = a.be.chunk(a.be.ctx, n) orelse { + a.fellBack(n); + return null; + }; + errdefer a.be.release(a.be.ctx, p, n); + try a.large.put(a.gpa, p, n); + a.stats.large += 1; + a.stats.chunk_bytes += n; + return p; + } + const n: u64 = roundUp(len, a.o.alignment); + try a.live.ensureUnusedCapacity(a.gpa, 1); // before a range is taken: a failure leaves free list and cursor alone + if (a.free_small.getPtr(n)) |l| if (l.pop()) |p| { + a.live.putAssumeCapacityNoClobber(p, n); + a.stats.reused += 1; + return p; + }; + if (a.limit - a.cursor < n) { + const cl = roundUp(@max(a.o.small_chunk, n), a.be.granularity); + const base = a.be.chunk(a.be.ctx, cl) orelse { + a.fellBack(cl); + return null; + }; + errdefer a.be.release(a.be.ctx, base, cl); + try a.small.append(a.gpa, .{ base, cl }); + a.cursor = base; + a.limit = base + cl; + a.stats.chunk_bytes += cl; + } + const p = a.cursor; + a.live.putAssumeCapacityNoClobber(p, n); + a.cursor += n; + a.stats.small += 1; + return p; + } + + /// A chunk the backend did not give: the request goes to cuMemAlloc (the first logged: a dead arena shows). + fn fellBack(a: *Arena, len: usize) void { + if (!quiet and a.stats.fallback == 0) std.log.scoped(.cuda).warn("device arena ({s}): a {d} MiB chunk failed; requests go to cuMemAlloc", .{ a.be.name, len >> 20 }); + a.stats.fallback += 1; + } + + /// True when `p` was the arena's (and is now free); false: not ours, the caller frees it as before. + pub fn free(a: *Arena, p: u64) bool { + a.lock(); + defer a.mutex.unlock(); + if (a.large.fetchRemove(p)) |e| { + removed(&a.large, &a.dead[1]); + a.be.release(a.be.ctx, p, e.value); + return true; + } + const e = a.live.fetchRemove(p) orelse return false; + removed(&a.live, &a.dead[0]); + const g = a.free_small.getOrPut(a.gpa, e.value) catch return true; // leaked range, still ours + if (!g.found_existing) g.value_ptr.* = .empty; + g.value_ptr.append(a.gpa, p) catch {}; + return true; + } + + /// std's tombstones never clear and keys fill 80 %: rehash at a tenth, so a tenth of the map always stays free. + fn removed(map: *std.AutoHashMapUnmanaged(u64, u64), dead: *u32) void { + dead.* += 1; + if (@as(u64, dead.*) * 10 < map.capacity()) return; + map.rehash(std.hash_map.AutoContext(u64){}); + dead.* = 0; + } + + // -- memory.zig's hook ---------------------------------------------------------------------------------------- + + fn hookAlloc(ctx: *anyopaque, len: usize) ?u64 { + const a: *Arena = @ptrCast(@alignCast(ctx)); + return a.alloc(len); + } + + fn hookFree(ctx: *anyopaque, p: u64) bool { + const a: *Arena = @ptrCast(@alignCast(ctx)); + return a.free(p); + } + + /// Every later DeviceBuffer.alloc comes from the arena, which must outlive its buffers; only addresses change. + pub fn install(a: *Arena) void { + memory.setHook(.{ .ctx = a, .alloc = hookAlloc, .free = hookFree }); + } +}; + +// -- backends --------------------------------------------------------------------------------------------------------- + +/// One cuMemAlloc a chunk (the driver's own 2 MiB-aligned allocations). +pub const Plain = struct { + d: *const Driver, + + pub fn backend(p: *Plain) Backend { + return .{ .ctx = p, .granularity = 2 << 20, .chunk = chunk, .release = release, .name = "plain" }; + } + + fn chunk(ctx: *anyopaque, len: usize) ?u64 { + const p: *Plain = @ptrCast(@alignCast(ctx)); + var q: abi.DevicePtr = 0; + if (p.d.api.cuMemAlloc_v2(&q, len) != abi.success) return null; + return q; + } + + fn release(ctx: *anyopaque, base: u64, _: usize) void { + const p: *Plain = @ptrCast(@alignCast(ctx)); + _ = p.d.api.cuMemFree_v2(base); + } +}; + +/// CUDA's virtual memory management: one reserved virtual range, chunks of the recommended granularity mapped in order. +pub const Vmm = struct { + lib: std.DynLib, + device: abi.Device, + prop: Prop, + granularity: usize, + va: u64 = 0, + va_len: usize = 0, + next: u64 = 0, + api: Api, + /// the first chunk that failed: the driver call and its CUresult (null: none failed) + failed: ?struct { call: []const u8, code: c_int } = null, + + const Api = struct { + reserve: *const fn (*u64, usize, usize, u64, c_ulonglong) callconv(.c) abi.Result, + addressFree: *const fn (u64, usize) callconv(.c) abi.Result, + create: *const fn (*u64, usize, *const Prop, c_ulonglong) callconv(.c) abi.Result, + release: *const fn (u64) callconv(.c) abi.Result, + map: *const fn (u64, usize, usize, u64, c_ulonglong) callconv(.c) abi.Result, + unmap: *const fn (u64, usize) callconv(.c) abi.Result, + setAccess: *const fn (u64, usize, *const AccessDesc, usize) callconv(.c) abi.Result, + granularity: *const fn (*usize, *const Prop, c_int) callconv(.c) abi.Result, + }; + + pub const Prop = extern struct { // CUmemAllocationProp + type: c_int = 1, // CU_MEM_ALLOCATION_TYPE_PINNED + handle_types: c_int = 0, + loc_type: c_int = 1, // CU_MEM_LOCATION_TYPE_DEVICE + loc_id: c_int = 0, + win32: ?*anyopaque = null, + compression: u8 = 0, + rdma: u8 = 0, + usage: u16 = 0, + reserved: [4]u8 = .{ 0, 0, 0, 0 }, + }; + const AccessDesc = extern struct { loc_type: c_int = 1, loc_id: c_int, flags: c_int = 3 }; // CUmemAccessDesc, read-write + + comptime { + std.debug.assert(@sizeOf(Prop) == 32 and @offsetOf(Prop, "compression") == 24 and @sizeOf(AccessDesc) == 12); + } + + /// `va_bytes` of virtual addresses reserved (cheap: no memory behind them until a chunk maps). + pub fn open(d: *const Driver, device: abi.Device, va_bytes: usize) Error!Vmm { + var vmm_ok: c_int = 0; + if (d.api.cuDeviceGetAttribute(&vmm_ok, .virtual_memory_management_supported, device) != abi.success or vmm_ok == 0) return error.Unsupported; // VIRTUAL_MEMORY_MANAGEMENT_SUPPORTED + var lib = std.DynLib.open("libcuda.so.1") catch return error.DriverUnavailable; + errdefer lib.close(); + var api: Api = undefined; + const names = .{ .{ "reserve", "cuMemAddressReserve" }, .{ "addressFree", "cuMemAddressFree" }, .{ "create", "cuMemCreate" }, .{ "release", "cuMemRelease" }, .{ "map", "cuMemMap" }, .{ "unmap", "cuMemUnmap" }, .{ "setAccess", "cuMemSetAccess" }, .{ "granularity", "cuMemGetAllocationGranularity" } }; + inline for (names) |n| @field(api, n[0]) = lib.lookup(@FieldType(Api, n[0]), n[1]) orelse return error.MissingSymbol; + var prop: Prop = .{ .loc_id = device }; + var rdma: c_int = 0; // GPU_DIRECT_RDMA_WITH_CUDA_VMM_SUPPORTED: keep the memory registrable as cuMemAlloc's is + if (d.api.cuDeviceGetAttribute(&rdma, .gpu_direct_rdma_with_cuda_vmm_supported, device) == abi.success and rdma != 0) prop.rdma = 1; + var g: usize = 0; + if (api.granularity(&g, &prop, 1) != abi.success or g == 0) return error.Unsupported; // RECOMMENDED + g = @max(g, 2 << 20); + const len = std.mem.alignForward(usize, va_bytes, g); + var va: u64 = 0; + if (api.reserve(&va, len, g, 0, 0) != abi.success) return error.CudaFailed; + return .{ .lib = lib, .device = device, .prop = prop, .granularity = g, .va = va, .va_len = len, .next = va, .api = api }; + } + + pub fn close(v: *Vmm) void { + _ = v.api.addressFree(v.va, v.va_len); + v.lib.close(); + } + + pub fn backend(v: *Vmm) Backend { + return .{ .ctx = v, .granularity = v.granularity, .chunk = chunk, .release = release, .name = "vmm" }; + } + + fn chunk(ctx: *anyopaque, len: usize) ?u64 { + const v: *Vmm = @ptrCast(@alignCast(ctx)); + if (v.va + v.va_len - v.next < len) return v.fail("the reserved range", 0); + var h: u64 = 0; + const rc = v.api.create(&h, len, &v.prop, 0); + if (rc != abi.success) return v.fail("cuMemCreate", rc); + defer _ = v.api.release(h); // the mapping holds the memory until it is unmapped + const at = v.next; + const rm = v.api.map(at, len, 0, h, 0); + if (rm != abi.success) return v.fail("cuMemMap", rm); + const desc: AccessDesc = .{ .loc_id = v.device }; + const ra = v.api.setAccess(at, len, &desc, 1); + if (ra != abi.success) { + _ = v.api.unmap(at, len); + return v.fail("cuMemSetAccess", ra); + } + v.next += len; + return at; + } + + fn fail(v: *Vmm, call: []const u8, code: c_int) ?u64 { + if (v.failed == null) v.failed = .{ .call = call, .code = code }; + return null; + } + + fn release(ctx: *anyopaque, base: u64, len: usize) void { + const v: *Vmm = @ptrCast(@alignCast(ctx)); + _ = v.api.unmap(base, len); // the virtual range is not reused: the reservation is sized for the process + } +}; + +// -- tests (a fake backend: addresses only) --------------------------------------------------------------------------- + +const Fake = struct { + next: u64 = 1 << 40, + chunks: u32 = 0, + released: u32 = 0, + fail: bool = false, + + fn backend(f: *Fake, g: usize) Backend { + return .{ .ctx = f, .granularity = g, .chunk = chunk, .release = release, .name = "fake" }; + } + fn chunk(ctx: *anyopaque, len: usize) ?u64 { + const f: *Fake = @ptrCast(@alignCast(ctx)); + if (f.fail) return null; + const p = f.next; + f.next += len; + f.chunks += 1; + return p; + } + fn release(ctx: *anyopaque, _: u64, _: usize) void { + const f: *Fake = @ptrCast(@alignCast(ctx)); + f.released += 1; + } +}; + +test "arena: small requests pack 512-aligned into shared chunks, are reused by size, large ones get their own" { + const t = std.testing; + var f: Fake = .{}; + var a = Arena.init(t.allocator, f.backend(2 << 20), .{ .small_max = 1 << 20, .small_chunk = 4 << 20 }); + defer a.deinit(); + const p1 = a.alloc(20480).?; // a norm weight + const p2 = a.alloc(983040).?; // an mHC fn16 + const p3 = a.alloc(100).?; + try t.expectEqual(p1 + 20480, p2); + try t.expectEqual(p2 + 983040, p3); + for ([_]u64{ p1, p2, p3 }) |p| try t.expectEqual(@as(u64, 0), p % 512); + try t.expectEqual(@as(u32, 1), f.chunks); + // a freed range comes back for the same rounded size + try t.expect(a.free(p3)); + try t.expectEqual(p3, a.alloc(17).?); + try t.expectEqual(@as(u64, 1), a.stats.reused); + // three more 1 MiB ranges fit the 4 MiB chunk, the fourth opens a new shared chunk + for (0..3) |_| _ = a.alloc(1 << 20).?; + try t.expectEqual(@as(u32, 1), f.chunks); + _ = a.alloc(1 << 20).?; + try t.expectEqual(@as(u32, 2), f.chunks); + // large: its own chunk, rounded to the granularity, released when freed + const big = a.alloc(5 << 20).?; + try t.expectEqual(@as(u32, 3), f.chunks); + try t.expectEqual(@as(u64, 6 << 20), a.large.get(big).?); + try t.expect(a.free(big)); + try t.expectEqual(@as(u32, 1), f.released); + // not ours + try t.expect(!a.free(12345)); + // a backend that cannot map: null, the caller allocates as before + f.fail = true; + try t.expectEqual(@as(?u64, null), a.alloc(64 << 20)); + try t.expectEqual(@as(u64, 1), a.stats.fallback); +} + +test "arena: under DeviceBuffer.alloc through memory.setHook" { + const t = std.testing; + var f: Fake = .{}; + var a = Arena.init(t.allocator, f.backend(2 << 20), .{}); + defer a.deinit(); + a.install(); + defer memory.setHook(null); + const h = memory.currentHook().?; + const p = h.alloc(h.ctx, 4096).?; + try t.expect(h.free(h.ctx, p)); + try t.expect(!h.free(h.ctx, 7)); +} + +/// Slots of `m` that hold neither a key nor a tombstone. +fn freeSlots(m: *const std.AutoHashMapUnmanaged(u64, u64)) usize { + const meta = m.metadata orelse return 0; + var n: usize = 0; + for (meta[0..m.capacity()]) |x| n += @intFromBool(x.isFree()); + return n; +} + +test "arena: a long run of allocations and frees leaves both address maps free slots" { + const t = std.testing; + var f: Fake = .{}; + var a = Arena.init(t.allocator, f.backend(2 << 20), .{ .small_max = 1 << 20, .small_chunk = 4 << 20 }); + defer a.deinit(); + var held: std.ArrayList(u64) = .empty; + defer held.deinit(t.allocator); + var rng = std.Random.DefaultPrng.init(3); + for (0..20000) |_| { + // a serving process's buffers come and go (prompt workspaces, grown buffers): small and large alike + if (held.items.len > 0 and rng.random().boolean()) { + const k = rng.random().uintLessThan(usize, held.items.len); + try t.expect(a.free(held.swapRemove(k))); + } else { + const big = rng.random().uintLessThan(u32, 4) == 0; + try held.append(t.allocator, a.alloc(if (big) 3 << 20 else 512 * (1 + rng.random().uintLessThan(u64, 64))).?); + } + for ([_]*const std.AutoHashMapUnmanaged(u64, u64){ &a.live, &a.large }) |m| { + if (m.capacity() == 0) continue; + try t.expect(freeSlots(m) * 10 >= m.capacity()); + } + } +} + +test "arena: a backend that maps nothing sends every request to cuMemAlloc and counts each fallback" { + const t = std.testing; + var f: Fake = .{ .fail = true }; + var a = Arena.init(t.allocator, f.backend(2 << 20), .{ .small_max = 1 << 20, .small_chunk = 4 << 20 }); + defer a.deinit(); + try t.expectEqual(@as(?u64, null), a.alloc(8 << 20)); // a large request's own chunk + try t.expectEqual(@as(?u64, null), a.alloc(4096)); // a small one's shared chunk + try t.expectEqual(@as(u64, 2), a.stats.fallback); + try t.expectEqual(@as(u64, 0), a.stats.chunk_bytes); + try t.expect(!a.free(0x1000)); // nothing was the arena's: the caller frees as before + f.fail = false; // the backend recovers: the next request maps again + try t.expect(a.alloc(4096) != null); + try t.expectEqual(@as(u64, 2), a.stats.fallback); +} + +test "arena: VMM keeps the first failed driver call and its code, later failures leave it" { + const t = std.testing; + const Api = struct { + var create_rc: abi.Result = 2; // CUDA_ERROR_OUT_OF_MEMORY + var unmaps: u32 = 0; + fn create(h: *u64, _: usize, _: *const Vmm.Prop, _: c_ulonglong) callconv(.c) abi.Result { + h.* = 7; + return create_rc; + } + fn ok1(_: u64) callconv(.c) abi.Result { + return abi.success; + } + fn map(_: u64, _: usize, _: usize, _: u64, _: c_ulonglong) callconv(.c) abi.Result { + return abi.success; + } + fn unmap(_: u64, _: usize) callconv(.c) abi.Result { + unmaps += 1; + return abi.success; + } + fn setAccess(_: u64, _: usize, _: *const Vmm.AccessDesc, _: usize) callconv(.c) abi.Result { + return 1; // CUDA_ERROR_INVALID_VALUE + } + }; + var v: Vmm = .{ .lib = undefined, .device = 0, .prop = .{}, .granularity = 2 << 20, .va = 1 << 40, .va_len = 4 << 20, .next = 1 << 40, .api = undefined }; + v.api.create = Api.create; + v.api.release = Api.ok1; + v.api.map = Api.map; + v.api.unmap = Api.unmap; + v.api.setAccess = Api.setAccess; + const be = v.backend(); + try t.expectEqual(@as(?u64, null), be.chunk(be.ctx, 2 << 20)); + try t.expectEqualStrings("cuMemCreate", v.failed.?.call); + try t.expectEqual(@as(c_int, 2), v.failed.?.code); + Api.create_rc = abi.success; // now access fails: the mapping is undone, the first failure stays + try t.expectEqual(@as(?u64, null), be.chunk(be.ctx, 2 << 20)); + try t.expectEqual(@as(u32, 1), Api.unmaps); + try t.expectEqualStrings("cuMemCreate", v.failed.?.call); + try t.expectEqual(@as(u64, 1 << 40), v.next); // nothing was mapped + v.failed = null; // past the reserved range: no driver call at all + try t.expectEqual(@as(?u64, null), be.chunk(be.ctx, 8 << 20)); + try t.expectEqualStrings("the reserved range", v.failed.?.call); +} + +test "arena: a reuse whose address-map insertion fails keeps its range free, and the retry gets it" { + const t = std.testing; + var f: Fake = .{}; + var fa = t.FailingAllocator.init(t.allocator, .{}); + { + var a = Arena.init(fa.allocator(), f.backend(2 << 20), .{ .small_max = 1 << 20, .small_chunk = 4 << 20 }); + defer a.deinit(); // testing.allocator underneath reports any leaked host bytes + const p = a.alloc(4096).?; + try t.expect(a.free(p)); + // other sizes take back the room the free gave: inserting the reused range must grow the map + while (a.live.available > 0) _ = a.alloc(512).?; + const live = a.live.count(); + const cursor = a.cursor; + const chunks = f.chunks; + fa.fail_index = fa.alloc_index; // the next allocation fails, and every one after it until healed + try t.expectEqual(@as(?u64, null), a.alloc(4096)); + try t.expectEqual(live, a.live.count()); + try t.expectEqual(cursor, a.cursor); + try t.expectEqual(chunks, f.chunks); + try t.expectEqualSlices(u64, &.{p}, a.free_small.get(4096).?.items); + try t.expectEqual(@as(u64, 0), a.stats.reused); + fa.fail_index = std.math.maxInt(usize); + try t.expectEqual(p, a.alloc(4096).?); + try t.expectEqual(@as(u64, 1), a.stats.reused); + try t.expectEqual(@as(usize, 0), a.free_small.get(4096).?.items.len); + try t.expect(a.free(p)); + } + try t.expectEqual(f.chunks, f.released); // every chunk went back to the backend +} diff --git a/zig/src/cuda/context.zig b/zig/src/cuda/context.zig index 76c75f59db..60436f3d8e 100644 --- a/zig/src/cuda/context.zig +++ b/zig/src/cuda/context.zig @@ -10,6 +10,16 @@ pub const Context = struct { device: abi.Device, handle: abi.Context, + /// How the host waits for the device (CU_CTX_SCHED_*); host scheduling only, no launch or value changes. + pub const Schedule = enum(c_uint) { auto = 0, spin = 1, yield = 2, blocking = 4 }; + + /// The device's primary context's schedule, before any `init` of it (the driver ignores it on an active context). + pub fn setSchedule(d: *const Driver, ordinal: c_int, s: Schedule) Error!void { + var dev: abi.Device = 0; + try d.check(d.api.cuDeviceGet(&dev, ordinal), "cuDeviceGet"); + try d.check(d.api.cuDevicePrimaryCtxSetFlags_v2(dev, @backingInt(s)), "cuDevicePrimaryCtxSetFlags"); + } + pub fn init(d: *const Driver, ordinal: c_int) Error!Context { var dev: abi.Device = 0; try d.check(d.api.cuDeviceGet(&dev, ordinal), "cuDeviceGet"); diff --git a/zig/src/cuda/driver.zig b/zig/src/cuda/driver.zig index 7c7fb7f068..38a9b930d7 100644 --- a/zig/src/cuda/driver.zig +++ b/zig/src/cuda/driver.zig @@ -9,6 +9,10 @@ pub const Error = error{ CopySourceLimit, StageBusy, StageUnprepared, UnownedCap pub const Driver = struct { lib: platform.DynLib, api: abi.Api, + /// cuFuncGetName (CUDA 12.3+; optional: an older driver opens without it): a failed launch names its kernel + func_name: ?FuncGetName = null, + + pub const FuncGetName = *const fn (*?[*:0]const u8, abi.Function) callconv(.c) abi.Result; pub fn open() Error!Driver { return openPath(platform.cuda_driver); @@ -26,7 +30,7 @@ pub const Driver = struct { return error.MissingSymbol; }; } - const d: Driver = .{ .lib = lib, .api = api }; + const d: Driver = .{ .lib = lib, .api = api, .func_name = lib.lookup(FuncGetName, "cuFuncGetName") }; try d.check(api.cuInit(0), "cuInit"); return d; } @@ -43,18 +47,33 @@ pub const Driver = struct { pub fn check(self: *const Driver, res: abi.Result, what: []const u8) Error!void { if (res == abi.success) return; if (res == abi.error_not_ready) return error.NotReady; - std.log.err("{s}: {s} ({d}) {s}", .{ what, self.errorName(res), res, self.errorText(res) }); + std.log.err("{s}: {s} ({d}) {s}{s}", .{ what, self.errorName(res), res, self.errorText(res), shutdownNote(res) }); + return errorOf(res); + } + + /// The error a failed call returns (`check` without its log line, for callers that log more). + pub fn errorOf(res: abi.Result) Error { return switch (res) { 2 => error.OutOfDeviceMemory, abi.error_not_found => error.NotFound, + abi.error_not_ready => error.NotReady, else => error.CudaFailed, }; } + /// The driver's name for the code, else knownName's: during the exit's teardown cuGetErrorName fails too. pub fn errorName(self: *const Driver, res: abi.Result) []const u8 { var s: ?[*:0]const u8 = null; - if (self.api.cuGetErrorName(res, &s) != abi.success) return "CUDA_ERROR_UNKNOWN_CODE"; - return if (s) |p| std.mem.span(p) else "CUDA_ERROR_UNKNOWN_CODE"; + if (self.api.cuGetErrorName(res, &s) == abi.success) if (s) |p| return std.mem.span(p); + return knownName(res) orelse "CUDA_ERROR_UNKNOWN_CODE"; + } + + /// A kernel's symbol name (cuFuncGetName), or "?" when the driver cannot say. + pub fn functionName(self: *const Driver, f: abi.Function) []const u8 { + const get = self.func_name orelse return "?"; + var s: ?[*:0]const u8 = null; + if (get(&s, f) != abi.success) return "?"; + return if (s) |p| std.mem.span(p) else "?"; } pub fn errorText(self: *const Driver, res: abi.Result) []const u8 { @@ -96,3 +115,43 @@ pub const Driver = struct { return std.mem.sliceTo(buf, 0); } }; + +/// CUresult names (cuda.h) for when cuGetErrorName cannot answer. +pub fn knownName(res: abi.Result) ?[]const u8 { + return switch (res) { + 1 => "CUDA_ERROR_INVALID_VALUE", + 2 => "CUDA_ERROR_OUT_OF_MEMORY", + 3 => "CUDA_ERROR_NOT_INITIALIZED", + 4 => "CUDA_ERROR_DEINITIALIZED", + 101 => "CUDA_ERROR_INVALID_DEVICE", + 200 => "CUDA_ERROR_INVALID_IMAGE", + 201 => "CUDA_ERROR_INVALID_CONTEXT", + 209 => "CUDA_ERROR_NO_BINARY_FOR_GPU", + 400 => "CUDA_ERROR_INVALID_HANDLE", + 500 => "CUDA_ERROR_NOT_FOUND", + 600 => "CUDA_ERROR_NOT_READY", + 700 => "CUDA_ERROR_ILLEGAL_ADDRESS", + 701 => "CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES", + 702 => "CUDA_ERROR_LAUNCH_TIMEOUT", + 709 => "CUDA_ERROR_CONTEXT_IS_DESTROYED", + 710 => "CUDA_ERROR_ASSERT", + 714 => "CUDA_ERROR_HARDWARE_STACK_ERROR", + 715 => "CUDA_ERROR_ILLEGAL_INSTRUCTION", + 716 => "CUDA_ERROR_MISALIGNED_ADDRESS", + 719 => "CUDA_ERROR_LAUNCH_FAILED", + 999 => "CUDA_ERROR_UNKNOWN", + else => null, + }; +} + +/// CUDA_ERROR_DEINITIALIZED: the process is exiting (a stop signal, then exit) while a thread still calls the driver. +pub fn shutdownNote(res: abi.Result) []const u8 { + return if (res == 4) " (the CUDA driver is shutting down: the process is exiting)" else ""; +} + +test "CUresult names without the driver: DEINITIALIZED is 4, unknown codes stay unknown" { + try std.testing.expectEqualStrings("CUDA_ERROR_DEINITIALIZED", knownName(4).?); + try std.testing.expectEqualStrings("CUDA_ERROR_ILLEGAL_ADDRESS", knownName(700).?); + try std.testing.expect(knownName(12345) == null); + try std.testing.expect(shutdownNote(4).len > 0 and shutdownNote(1).len == 0); +} diff --git a/zig/src/cuda/graph_cache.zig b/zig/src/cuda/graph_cache.zig new file mode 100644 index 0000000000..9113579996 --- /dev/null +++ b/zig/src/cuda/graph_cache.zig @@ -0,0 +1,563 @@ +//! Keyed CUDA graphs for any family: one captured executable per key, launched again when the key comes back. + +const std = @import("std"); +const graph = @import("graph.zig"); +const Driver = @import("driver.zig").Driver; +const Stream = @import("stream.zig").Stream; +const registry = @import("graph_registry.zig"); +const quiet = @import("builtin").is_test; // a test's stderr is the build runner's channel + +/// One step's launches on `stream`; it must not synchronize, allocate device memory or read the device on the host. +pub const Body = struct { + ctx: *anyopaque, + run: *const fn (ctx: *anyopaque, stream: Stream) anyerror!void, +}; + +/// Every rank's verdict AND-ed on a miss, never a hit; an off cache never votes, so ranks match `Settings.digest`. +pub const Agree = struct { + ctx: *anyopaque, + all: *const fn (ctx: *anyopaque, ok: bool) anyerror!bool, +}; + +/// Capture a body into an executable graph, launch it, free it: a fake engine runs the policy in host tests. +pub const Engine = struct { + ctx: *anyopaque, + capture: *const fn (ctx: *anyopaque, stream: Stream, body: Body) anyerror!*anyopaque, + launch: *const fn (ctx: *anyopaque, exec: *anyopaque, stream: Stream) anyerror!void, + free: *const fn (ctx: *anyopaque, exec: *anyopaque) void, +}; + +pub const Settings = struct { + on: bool = true, // off: every step runs eagerly + max: u32 = 48, // graphs held at most + min_keep: u32 = 8, // under the memory floor the cache evicts down to this many, then stops capturing + hold: bool = false, // under the floor a miss runs eagerly with no eviction: held graphs cost next to no memory + + /// These settings under the operator's TF_GRAPH_CACHE value: `0` or `off` runs every step eagerly; else unchanged. + pub fn operator(s: Settings, value: ?[]const u8) Settings { + var out = s; + const v = value orelse return out; + if (std.mem.eql(u8, v, "0") or std.ascii.eqlIgnoreCase(v, "off")) out.on = false; + return out; + } + + /// What a cache runs with: the operator's value, min_keep under max (0 with hold), and every off cache one value. + pub fn effective(s: Settings, value: ?[]const u8) Settings { + var out = s.operator(value); + if (!out.on or out.max == 0) return .{ .on = false, .max = 0, .min_keep = 0, .hold = false }; + out.min_keep = if (out.hold) 0 else @min(out.min_keep, out.max); + return out; + } + + /// `effective` under this process's TF_GRAPH_CACHE. + pub fn fromEnv(s: Settings) Settings { + return s.effective(if (std.c.getenv("TF_GRAPH_CACHE")) |v| std.mem.span(v) else null); + } + + /// The effective settings as bytes for ranks to compare at start (a family's bootstrap agreement), never per step. + pub fn digest(s: Settings) [10]u8 { + const e = s.effective(null); + var out: [10]u8 = undefined; + out[0] = @intFromBool(e.on); + out[1] = @intFromBool(e.hold); + std.mem.writeInt(u32, out[2..6], e.max, .little); + std.mem.writeInt(u32, out[6..10], e.min_keep, .little); + return out; + } +}; + +pub const Outcome = enum { replayed, captured, eager }; + +/// Eager steps under the floor between two "still under" lines. +pub const report_every = 256; + +/// One cache's name and counts as other threads read them (/health); `snapshots` copies the caches in use. +pub const Snapshot = registry.Snapshot; +pub const snapshots = registry.snapshots; + +pub fn Cache(comptime Key: type) type { + return struct { + const Self = @This(); + const Entry = struct { exec: *anyopaque, used: u64 }; + + gpa: std.mem.Allocator, + engine: Engine, + settings: Settings, + agree: ?Agree = null, + room: ?*const fn () bool = null, // false when this rank is below its memory floor; null: always room + meter: ?*const fn () ?f64 = null, // MemAvailable now in GiB, to log a capture's cost; null: not measured + name: []const u8 = "graphs", // the cache's name in its log lines and snapshot + entries: std.AutoArrayHashMapUnmanaged(Key, Entry) = .empty, + cap: u32, + tick: u64 = 0, + fingerprint: ?u64 = null, + failed: bool = false, // a capture failed on some rank: eager from here on + under: bool = false, // the last miss met no room on some rank + under_eager: u64 = 0, // eager steps since `under` began + slot: ?usize = null, // this cache's registry slot, claimed at its first step; null: every slot taken + claimed: bool = false, + stats: struct { replayed: u64 = 0, captured: u64 = 0, eager: u64 = 0, evicted: u64 = 0, dropped: u64 = 0, floor_hits: u64 = 0, capture_mib: f64 = 0 } = .{}, + + /// The cache with `settings.fromEnv()`: every rank sharing an `agree` must hold an equal `Settings.digest`. + pub fn init(gpa: std.mem.Allocator, engine: Engine, settings: Settings) Self { + const s = settings.fromEnv(); + if (!quiet and settings.on and settings.max > 0 and !s.on) std.log.info("graph cache: off (TF_GRAPH_CACHE), every step eager", .{}); + return .{ .gpa = gpa, .engine = engine, .settings = s, .cap = s.max }; + } + + pub fn deinit(c: *Self) void { + if (c.slot) |i| registry.release(i); + c.dropAll(); + c.entries.deinit(c.gpa); + } + + pub fn count(c: *const Self) usize { + return c.entries.count(); + } + + pub fn has(c: *const Self, key: Key) bool { + return c.entries.contains(key); + } + + /// Every graph freed (a fingerprint change, a shutdown). + pub fn dropAll(c: *Self) void { + for (c.entries.values()) |e| c.engine.free(c.engine.ctx, e.exec); + c.stats.dropped += c.entries.count(); + c.entries.clearRetainingCapacity(); + } + + fn agreed(c: *Self, ok: bool) !bool { + const a = c.agree orelse return ok; + return a.all(a.ctx, ok); + } + + fn evictOldest(c: *Self) void { + var at: ?usize = null; + for (c.entries.values(), 0..) |e, i| { + if (at == null or e.used < c.entries.values()[at.?].used) at = i; + } + const i = at orelse return; + c.engine.free(c.engine.ctx, c.entries.values()[i].exec); + c.entries.swapRemoveAt(i); + c.stats.evicted += 1; + } + + fn publish(c: *Self) void { + if (!c.claimed) { + c.claimed = true; + c.slot = registry.claim(c.name); + } + const i = c.slot orelse return; + registry.publish(i, .{ .held = @intCast(c.entries.count()), .captured = c.stats.captured, .replayed = c.stats.replayed, .eager = c.stats.eager, .floor_hits = c.stats.floor_hits, .under = c.under }); + } + + fn avail(c: *const Self) ?f64 { + return if (c.meter) |m| m() else null; + } + + /// A miss without room on some rank: the floor entered (logged), or a "still under" line every `report_every`. + fn underFloor(c: *Self, room_here: bool) void { + c.stats.floor_hits += 1; + c.under_eager += 1; + if (!c.under) { + c.under = true; + c.under_eager = 1; + if (!quiet) std.log.warn("graph cache {s}: under the memory floor on {s} rank (MemAvailable {?d:.2} GiB here): new keys eager, {d} graphs held", .{ c.name, if (room_here) "another" else "this", c.avail(), c.entries.count() }); + } else if (c.under_eager % report_every == 0) { + if (!quiet) std.log.warn("graph cache {s}: still under the memory floor: {d} eager steps since it began (MemAvailable {?d:.2} GiB here, {d} graphs held)", .{ c.name, c.under_eager, c.avail(), c.entries.count() }); + } + } + + fn eager(c: *Self, stream: Stream, body: Body) !Outcome { + try body.run(body.ctx, stream); + c.stats.eager += 1; + return .eager; + } + + /// One step: the key's graph, captured first on a miss; a new `fingerprint` (same step on all ranks) drops all. + pub fn run(c: *Self, key: Key, stream: Stream, fingerprint: u64, body: Body) !Outcome { + defer c.publish(); + if (!c.settings.on or c.failed or c.settings.max == 0) return c.eager(stream, body); + if (c.fingerprint != null and c.fingerprint.? != fingerprint) c.dropAll(); + c.fingerprint = fingerprint; + c.tick += 1; + if (c.entries.getPtr(key)) |e| { + e.used = c.tick; + try c.engine.launch(c.engine.ctx, e.exec, stream); + c.stats.replayed += 1; + return .replayed; + } + // a miss: room on every rank (a host map that cannot grow is none), else shed a quarter and lower the cap + const reserved = if (c.entries.ensureUnusedCapacity(c.gpa, 1)) |_| true else |_| false; + const room_here = reserved and (if (c.room) |f| f() else true); + if (!try c.agreed(room_here)) { + c.underFloor(room_here); + if (c.settings.hold) return c.eager(stream, body); + if (c.entries.count() > c.settings.min_keep) { + const drop = @max(1, c.entries.count() / 4); + for (0..drop) |_| if (c.entries.count() > c.settings.min_keep) c.evictOldest(); + } + const was = c.cap; + c.cap = @intCast(@max(c.entries.count(), c.settings.min_keep)); // min_keep <= max (init) + if (!quiet and c.cap != was) std.log.warn("graph cache: under the memory floor on {s} rank: cap {d} -> {d} ({d} held, {d} evicted, {d} eager so far)", .{ if (room_here) "another" else "this", was, c.cap, c.entries.count(), c.stats.evicted, c.stats.eager + 1 }); + return c.eager(stream, body); + } + if (c.under) { + if (!quiet) std.log.info("graph cache {s}: room again on every rank (MemAvailable {?d:.2} GiB here) after {d} eager steps under the floor, {d} graphs held", .{ c.name, c.avail(), c.under_eager, c.entries.count() }); + c.under = false; + } + if (c.cap < c.settings.max) { + if (!quiet) std.log.info("graph cache: room again on every rank: cap {d} -> {d}", .{ c.cap, c.settings.max }); + c.cap = c.settings.max; + } + while (c.entries.count() >= c.cap) c.evictOldest(); + const before = c.avail(); + const exec = c.engine.capture(c.engine.ctx, stream, body) catch |err| blk: { + // the only trace of why graphs went off (the step then runs eagerly, which may fail the same way) + if (!quiet) std.log.warn("graph capture failed: {s} (key {any}, {d} graphs held)", .{ @errorName(err), key, c.entries.count() }); + break :blk null; + }; + var owned = exec; // freed here until the map holds it, so a failed agreement cannot leak it + defer if (owned) |x| c.engine.free(c.engine.ctx, x); + if (!try c.agreed(exec != null)) { + c.failed = true; + c.dropAll(); + return c.eager(stream, body); + } + c.entries.putAssumeCapacityNoClobber(key, .{ .exec = exec.?, .used = c.tick }); + owned = null; + try c.engine.launch(c.engine.ctx, exec.?, stream); + c.stats.captured += 1; + // what a graph really costs this rank (the room rule prices it) + if (before) |b| if (c.avail()) |a| { + c.stats.capture_mib = (b - a) * 1024; + if (!quiet) std.log.info("graph cache {s}: captured, {d} held: MemAvailable {d:.2} -> {d:.2} GiB ({d:.0} MiB)", .{ c.name, c.entries.count(), b, a, c.stats.capture_mib }); + }; + return .captured; + } + }; +} + +/// The driver's engine: thread-local stream capture (other threads' CUDA calls stay out), instantiate, upload, launch. +pub const CudaEngine = struct { + d: *const Driver, + + pub fn engine(e: *CudaEngine) Engine { + return .{ .ctx = e, .capture = capture, .launch = launch, .free = free }; + } + + const Held = struct { exec: graph.Exec }; + + fn capture(ctx: *anyopaque, stream: Stream, body: Body) anyerror!*anyopaque { + const e: *CudaEngine = @ptrCast(@alignCast(ctx)); + try graph.beginCapture(stream, .thread_local); + body.run(body.ctx, stream) catch |err| { + // end the capture so the stream is usable again; the partial graph is dropped + if (graph.endCapture(stream)) |g| { + var gg = g; + gg.deinit(); + } else |_| {} + return err; + }; + var g = try graph.endCapture(stream); + defer g.deinit(); + const h = try std.heap.c_allocator.create(Held); + errdefer std.heap.c_allocator.destroy(h); + h.exec = try g.instantiate(); + errdefer h.exec.deinit(); + try h.exec.upload(stream); + _ = e; + return h; + } + + fn launch(_: *anyopaque, exec: *anyopaque, stream: Stream) anyerror!void { + const h: *Held = @ptrCast(@alignCast(exec)); + try h.exec.launchOn(stream); + } + + fn free(_: *anyopaque, exec: *anyopaque) void { + const h: *Held = @ptrCast(@alignCast(exec)); + h.exec.deinit(); + std.heap.c_allocator.destroy(h); + } +}; + +/// FNV-1a over the addresses a body captures (buffer bases, sizes): the fingerprint `run` compares, on one rank only. +pub fn fingerprintOf(words: []const u64) u64 { + var h: u64 = 0xcbf29ce484222325; + for (words) |w| { + var x = w; + for (0..8) |_| { + h = (h ^ (x & 0xff)) *% 0x100000001b3; + x >>= 8; + } + } + return h; +} + +// -- host tests: the policy with a fake engine -------------------------------------------------------------------- + +const Fake = struct { + captures: u32 = 0, + launches: u32 = 0, + live: u32 = 0, + fail_capture: bool = false, + slots: [64]u8 = undefined, + + fn engine(f: *Fake) Engine { + return .{ .ctx = f, .capture = cap, .launch = lau, .free = fre }; + } + fn cap(ctx: *anyopaque, stream: Stream, body: Body) anyerror!*anyopaque { + const f: *Fake = @ptrCast(@alignCast(ctx)); + if (f.fail_capture) return error.CaptureFailed; + _ = stream; + _ = body; // a capture records the body; nothing runs + f.captures += 1; + f.live += 1; + return &f.slots[f.captures % f.slots.len]; + } + fn lau(ctx: *anyopaque, _: *anyopaque, _: Stream) anyerror!void { + const f: *Fake = @ptrCast(@alignCast(ctx)); + f.launches += 1; + } + fn fre(ctx: *anyopaque, _: *anyopaque) void { + const f: *Fake = @ptrCast(@alignCast(ctx)); + f.live -= 1; + } +}; + +const Ran = struct { + n: u32 = 0, + fn body(r: *Ran) Body { + return .{ .ctx = r, .run = go }; + } + fn go(ctx: *anyopaque, _: Stream) anyerror!void { + const r: *Ran = @ptrCast(@alignCast(ctx)); + r.n += 1; + } +}; + +const no_stream: Stream = .{ .d = undefined, .handle = null }; + +test "graph cache: capture on first use, replay after, LRU at the cap" { + var f: Fake = .{}; + var r: Ran = .{}; + var c = Cache(u32).init(std.testing.allocator, f.engine(), .{ .max = 2 }); + defer c.deinit(); + try std.testing.expectEqual(Outcome.captured, try c.run(1, no_stream, 7, r.body())); + try std.testing.expectEqual(Outcome.replayed, try c.run(1, no_stream, 7, r.body())); + try std.testing.expectEqual(Outcome.captured, try c.run(2, no_stream, 7, r.body())); + _ = try c.run(1, no_stream, 7, r.body()); // 1 is now the most recent + try std.testing.expectEqual(Outcome.captured, try c.run(3, no_stream, 7, r.body())); // evicts 2 + try std.testing.expect(c.has(1) and c.has(3) and !c.has(2)); + try std.testing.expectEqual(@as(u32, 0), r.n); // the body never ran eagerly + try std.testing.expectEqual(@as(u32, 2), f.live); + try std.testing.expectEqual(@as(u32, 5), f.launches); +} + +test "graph cache: a new fingerprint drops every graph" { + var f: Fake = .{}; + var r: Ran = .{}; + var c = Cache(u32).init(std.testing.allocator, f.engine(), .{}); + defer c.deinit(); + _ = try c.run(1, no_stream, 7, r.body()); + _ = try c.run(2, no_stream, 7, r.body()); + try std.testing.expectEqual(Outcome.captured, try c.run(1, no_stream, 8, r.body())); + try std.testing.expectEqual(@as(usize, 1), c.count()); + try std.testing.expectEqual(@as(u32, 1), f.live); +} + +const Peer = struct { + theirs: []const bool, // the other rank's verdicts, in call order + at: usize = 0, + fn agree(p: *Peer) Agree { + return .{ .ctx = p, .all = all }; + } + fn all(ctx: *anyopaque, ok: bool) anyerror!bool { + const p: *Peer = @ptrCast(@alignCast(ctx)); + const t = p.theirs[p.at]; + p.at += 1; + return ok and t; + } +}; + +test "graph cache: a capture failing on another rank turns graphs off here too" { + var f: Fake = .{}; + var r: Ran = .{}; + // per miss: room, then the capture's verdict; the peer's capture fails on the second key + var peer: Peer = .{ .theirs = &.{ true, true, true, false } }; + var c = Cache(u32).init(std.testing.allocator, f.engine(), .{}); + c.agree = peer.agree(); + defer c.deinit(); + try std.testing.expectEqual(Outcome.captured, try c.run(1, no_stream, 7, r.body())); + try std.testing.expectEqual(Outcome.eager, try c.run(2, no_stream, 7, r.body())); + try std.testing.expect(c.failed); + try std.testing.expectEqual(@as(u32, 0), f.live); // this rank's good graphs are freed as well + try std.testing.expectEqual(Outcome.eager, try c.run(1, no_stream, 7, r.body())); + try std.testing.expectEqual(@as(u32, 2), r.n); +} + +var room_flag = true; +fn roomFn() bool { + return room_flag; +} + +test "graph cache: below the memory floor the least recent quarter goes, the cap follows, the step runs eagerly" { + var f: Fake = .{}; + var r: Ran = .{}; + var c = Cache(u32).init(std.testing.allocator, f.engine(), .{ .max = 48, .min_keep = 2 }); + c.room = roomFn; + defer c.deinit(); + room_flag = true; + for (0..8) |k| _ = try c.run(@intCast(k), no_stream, 7, r.body()); + room_flag = false; + try std.testing.expectEqual(Outcome.eager, try c.run(100, no_stream, 7, r.body())); + try std.testing.expectEqual(@as(usize, 6), c.count()); + try std.testing.expectEqual(@as(u32, 6), c.cap); + try std.testing.expect(!c.has(0) and !c.has(1) and c.has(7)); + room_flag = true; + // room again: the cap is back to max, the miss captures without evicting + try std.testing.expectEqual(Outcome.captured, try c.run(100, no_stream, 7, r.body())); + try std.testing.expectEqual(@as(usize, 7), c.count()); + try std.testing.expectEqual(@as(u32, 48), c.cap); + try std.testing.expect(c.has(7) and c.has(2)); + try std.testing.expectEqual(@as(u64, 1), c.stats.floor_hits); +} + +test "graph cache: a dip under the floor does not leave later keys churning once memory is back" { + var f: Fake = .{}; + var r: Ran = .{}; + var c = Cache(u32).init(std.testing.allocator, f.engine(), .{ .max = 16, .min_keep = 2 }); + c.room = roomFn; + defer c.deinit(); + room_flag = true; + for (0..12) |k| _ = try c.run(@intCast(k), no_stream, 7, r.body()); + room_flag = false; // a long prompt's workspace held + _ = try c.run(200, no_stream, 7, r.body()); + room_flag = true; // freed: the decode after it meets 12 new keys (a new context bucket), then replays them + const before = c.stats.evicted; + for (0..12) |k| _ = try c.run(@intCast(300 + k), no_stream, 7, r.body()); + for (0..12) |k| try std.testing.expectEqual(Outcome.replayed, try c.run(@intCast(300 + k), no_stream, 7, r.body())); + try std.testing.expectEqual(@as(u32, 16), c.cap); + // only what the cap of 16 forces: 9 left after the dip + 12 new = 21 -> 5 evictions, none during the replays + try std.testing.expectEqual(before + 5, c.stats.evicted); +} + +test "graph cache: hold - below the floor a miss runs eagerly, nothing is evicted, the held keys replay" { + var f: Fake = .{}; + var r: Ran = .{}; + var c = Cache(u32).init(std.testing.allocator, f.engine(), .{ .max = 48, .min_keep = 2, .hold = true }); + c.room = roomFn; + defer c.deinit(); + room_flag = true; + for (0..8) |k| _ = try c.run(@intCast(k), no_stream, 7, r.body()); + room_flag = false; + try std.testing.expectEqual(Outcome.eager, try c.run(100, no_stream, 7, r.body())); + try std.testing.expectEqual(Outcome.eager, try c.run(101, no_stream, 7, r.body())); + try std.testing.expectEqual(@as(usize, 8), c.count()); + try std.testing.expectEqual(@as(u32, 48), c.cap); + for (0..8) |k| try std.testing.expectEqual(Outcome.replayed, try c.run(@intCast(k), no_stream, 7, r.body())); + try std.testing.expectEqual(@as(u64, 0), c.stats.evicted); + try std.testing.expectEqual(@as(u64, 2), c.stats.floor_hits); + room_flag = true; + try std.testing.expectEqual(Outcome.captured, try c.run(100, no_stream, 7, r.body())); + try std.testing.expectEqual(@as(usize, 9), c.count()); +} + +test "graph cache: the floor's enter / under / leave state and its counts, as /health reads them" { + var f: Fake = .{}; + var r: Ran = .{}; + var c = Cache(u32).init(std.testing.allocator, f.engine(), .{ .max = 48, .hold = true }); + c.room = roomFn; + c.name = "test floor"; + defer c.deinit(); + room_flag = true; + for (0..4) |k| _ = try c.run(@intCast(k), no_stream, 7, r.body()); + try std.testing.expect(!c.under); + room_flag = false; + for (0..report_every + 3) |k| _ = try c.run(@intCast(1000 + k), no_stream, 7, r.body()); + try std.testing.expect(c.under); + try std.testing.expectEqual(@as(u64, report_every + 3), c.under_eager); + // the held keys replay under the floor and leave the state alone + try std.testing.expectEqual(Outcome.replayed, try c.run(2, no_stream, 7, r.body())); + try std.testing.expect(c.under); + var out: [8]Snapshot = undefined; + const mine = for (out[0..snapshots(&out)]) |x| { + if (std.mem.eql(u8, x.name(), "test floor")) break x; + } else return error.NotPublished; + try std.testing.expectEqual(registry.Counts{ .held = 4, .captured = 4, .replayed = 1, .eager = report_every + 3, .floor_hits = report_every + 3, .under = true }, mine.counts); + // room again: the next miss captures and the floor is left; the counts keep their totals + room_flag = true; + try std.testing.expectEqual(Outcome.captured, try c.run(2000, no_stream, 7, r.body())); + try std.testing.expect(!c.under); + try std.testing.expectEqual(@as(u64, report_every + 3), c.stats.floor_hits); + // a second dip starts its own count + room_flag = false; + _ = try c.run(3000, no_stream, 7, r.body()); + try std.testing.expectEqual(@as(u64, 1), c.under_eager); + room_flag = true; +} + +var meter_gib: f64 = 100; +var meter_reads: u32 = 0; +fn meterFn() ?f64 { + meter_reads += 1; + defer meter_gib -= 0.25; // each read sees a quarter GiB less: a capture's before and after differ by that + return meter_gib; +} + +test "graph cache: the meter prices a capture, replays never read it, a freed cache leaves the snapshots" { + var f: Fake = .{}; + var r: Ran = .{}; + var out: [8]Snapshot = undefined; + const others = snapshots(&out); + { + var c = Cache(u32).init(std.testing.allocator, f.engine(), .{ .max = 4 }); + defer c.deinit(); + c.meter = meterFn; + c.name = "metered"; + meter_reads = 0; + try std.testing.expectEqual(Outcome.captured, try c.run(1, no_stream, 7, r.body())); + try std.testing.expectEqual(@as(f64, 256), c.stats.capture_mib); + try std.testing.expectEqual(@as(u32, 2), meter_reads); + try std.testing.expectEqual(Outcome.replayed, try c.run(1, no_stream, 7, r.body())); + try std.testing.expectEqual(@as(u32, 2), meter_reads); + try std.testing.expectEqual(others + 1, snapshots(&out)); + const mine = for (out[0 .. others + 1]) |x| { + if (std.mem.eql(u8, x.name(), "metered")) break x; + } else return error.NotPublished; + try std.testing.expectEqual(@as(u64, 1), mine.counts.replayed); + } + try std.testing.expectEqual(others, snapshots(&out)); +} + +test "graph cache: off runs every step eagerly" { + var f: Fake = .{}; + var r: Ran = .{}; + var c = Cache(u32).init(std.testing.allocator, f.engine(), .{ .on = false }); + defer c.deinit(); + try std.testing.expectEqual(Outcome.eager, try c.run(1, no_stream, 7, r.body())); + try std.testing.expectEqual(@as(u32, 0), f.captures); +} + +test "graph cache: TF_GRAPH_CACHE=0 or off turns a family's cache off without a rebuild; anything else leaves it" { + const on: Settings = .{}; + for ([_][]const u8{ "0", "off", "OFF" }) |v| try std.testing.expect(!on.operator(v).on); + for ([_]?[]const u8{ null, "1", "on", "" }) |v| try std.testing.expect(on.operator(v).on); + try std.testing.expect(!(Settings{ .on = false }).operator("1").on); // the operator only turns a cache off + var f: Fake = .{}; + var r: Ran = .{}; + var c = Cache(u32).init(std.testing.allocator, f.engine(), on.operator("off")); + defer c.deinit(); + for (0..3) |_| try std.testing.expectEqual(Outcome.eager, try c.run(1, no_stream, 7, r.body())); + try std.testing.expectEqual(@as(u32, 0), f.captures); +} + +test "graph cache: fingerprint" { + try std.testing.expect(fingerprintOf(&.{ 1, 2 }) != fingerprintOf(&.{ 2, 1 })); +} + +test { + _ = @import("graph_cache_regression_test.zig"); + _ = @import("graph_cache_ranks_test.zig"); +} diff --git a/zig/src/cuda/graph_cache_ranks_test.zig b/zig/src/cuda/graph_cache_ranks_test.zig new file mode 100644 index 0000000000..18b8a87e9d --- /dev/null +++ b/zig/src/cuda/graph_cache_ranks_test.zig @@ -0,0 +1,136 @@ +//! Two in-process ranks with fake engines: equal effective settings vote on misses only; a mismatch is a stall. + +const std = @import("std"); +const cache = @import("graph_cache.zig"); +const Stream = @import("stream.zig").Stream; +const no_stream: Stream = .{ .d = undefined, .handle = null }; +const equal = std.testing.expectEqual; +const Settings = cache.Settings; + +const Engine = struct { + captures: usize = 0, + live: usize = 0, + handles: [8]u8 = undefined, + + fn engine(e: *Engine) cache.Engine { + return .{ .ctx = e, .capture = capture, .launch = launch, .free = free }; + } + fn capture(p: *anyopaque, _: Stream, _: cache.Body) !*anyopaque { + const e: *Engine = @ptrCast(@alignCast(p)); + e.captures += 1; + e.live += 1; + return &e.handles[e.captures % e.handles.len]; + } + fn launch(_: *anyopaque, _: *anyopaque, _: Stream) !void {} + fn free(p: *anyopaque, _: *anyopaque) void { + const e: *Engine = @ptrCast(@alignCast(p)); + e.live -= 1; + } + fn body(e: *Engine) cache.Body { + return .{ .ctx = e, .run = run }; + } + fn run(_: *anyopaque, _: Stream) !void {} +}; + +/// The two ranks' votes in call order; a rank waits at most `wait_ns` for the other's vote of the same round. +const Board = struct { + votes: [2][16]bool = undefined, + cast: [2]std.atomic.Value(u32) = .{ .init(0), .init(0) }, + wait_ns: u64, + + fn vote(b: *Board, rank: u1, ok: bool) !bool { + const r = b.cast[rank].load(.monotonic); + if (r == b.votes[rank].len) return error.TooManyVotes; + b.votes[rank][r] = ok; + b.cast[rank].store(r + 1, .release); + const other = 1 - rank; + const deadline = now() + b.wait_ns; + while (b.cast[other].load(.acquire) <= r) { + if (now() > deadline) return error.PeerNeverVoted; + std.Thread.yield() catch {}; + } + return ok and b.votes[other][r]; + } + + fn now() u64 { + return @intCast(std.Io.Clock.awake.now(std.testing.io).toNanoseconds()); + } +}; + +const Rank = struct { + board: *Board, + id: u1, + engine: Engine = .{}, + outcomes: [8]cache.Outcome = undefined, + err: ?anyerror = null, + + fn all(ctx: *anyopaque, ok: bool) anyerror!bool { + const r: *Rank = @ptrCast(@alignCast(ctx)); + return r.board.vote(r.id, ok); + } + + /// One rank's steps over `keys` with `settings` exactly as its process resolved them. + fn steps(r: *Rank, settings: Settings, ks: []const u32) void { + var c = cache.Cache(u32).init(std.testing.allocator, r.engine.engine(), settings); + defer c.deinit(); + c.agree = .{ .ctx = r, .all = all }; + for (ks, 0..) |k, i| r.outcomes[i] = c.run(k, no_stream, 7, r.engine.body()) catch |e| { + r.err = e; + return; + }; + } +}; + +const keys = [_]u32{ 1, 2, 1, 2, 1 }; + +test "graph cache ranks: equal enabled settings capture together and vote on each miss, never on a hit" { + var board: Board = .{ .wait_ns = 5 * std.time.ns_per_s }; + var ranks = [2]Rank{ .{ .board = &board, .id = 0 }, .{ .board = &board, .id = 1 } }; + const s = (Settings{ .max = 4 }).effective(null); + const t = try std.Thread.spawn(.{}, Rank.steps, .{ &ranks[1], s, &keys }); + ranks[0].steps(s, &keys); + t.join(); + const want = [_]cache.Outcome{ .captured, .captured, .replayed, .replayed, .replayed }; + for (&ranks) |*r| { + try equal(@as(?anyerror, null), r.err); + try std.testing.expectEqualSlices(cache.Outcome, &want, r.outcomes[0..keys.len]); + try equal(@as(u32, 4), board.cast[r.id].load(.acquire)); // two misses: room and capture each, hits none + try equal(@as(usize, 0), r.engine.live); + } +} + +test "graph cache ranks: TF_GRAPH_CACHE 0, off and OFF are one effective setting, and an off cache never votes" { + const d: Settings = .{}; + const off = d.effective("0").digest(); + try std.testing.expectEqualSlices(u8, &off, &d.effective("off").digest()); + try std.testing.expectEqualSlices(u8, &off, &d.effective("OFF").digest()); + try std.testing.expectEqualSlices(u8, &off, &(Settings{ .max = 0, .hold = true }).digest()); + try std.testing.expect(!std.mem.eql(u8, &off, &d.effective(null).digest())); + const held: Settings = .{ .hold = true, .min_keep = 2 }; + try std.testing.expectEqualSlices(u8, &held.digest(), &(Settings{ .hold = true, .min_keep = 30 }).digest()); + try std.testing.expectEqualSlices(u8, &d.effective("1").digest(), &d.effective(null).digest()); + var board: Board = .{ .wait_ns = 0 }; + var ranks = [2]Rank{ .{ .board = &board, .id = 0 }, .{ .board = &board, .id = 1 } }; + ranks[0].steps(d.effective("0"), &keys); + ranks[1].steps(d.effective("OFF"), &keys); + for (&ranks) |*r| { + try equal(@as(?anyerror, null), r.err); + for (r.outcomes[0..keys.len]) |o| try equal(cache.Outcome.eager, o); + try equal(@as(u32, 0), board.cast[r.id].load(.acquire)); + try equal(@as(usize, 0), r.engine.captures); + } +} + +test "graph cache ranks: one rank switched off leaves the other waiting at its first miss; digests show it first" { + const d: Settings = .{}; + try std.testing.expect(!std.mem.eql(u8, &d.effective(null).digest(), &d.effective("off").digest())); + var board: Board = .{ .wait_ns = 50 * std.time.ns_per_ms }; + var ranks = [2]Rank{ .{ .board = &board, .id = 0 }, .{ .board = &board, .id = 1 } }; + ranks[1].steps(d.effective("off"), &keys); + try equal(@as(?anyerror, null), ranks[1].err); + try equal(@as(u32, 0), board.cast[1].load(.acquire)); // the off rank ran every step eagerly without a vote + ranks[0].steps(d.effective(null), &keys); + try equal(@as(?anyerror, error.PeerNeverVoted), ranks[0].err); // in production this wait has no deadline + try equal(@as(u32, 1), board.cast[0].load(.acquire)); + try equal(@as(usize, 0), ranks[0].engine.live); +} diff --git a/zig/src/cuda/graph_cache_regression_test.zig b/zig/src/cuda/graph_cache_regression_test.zig new file mode 100644 index 0000000000..7f2da1063a --- /dev/null +++ b/zig/src/cuda/graph_cache_regression_test.zig @@ -0,0 +1,181 @@ +//! Failure ownership, rank verdict counts and bounded-cap regressions for the graph cache. + +const std = @import("std"); +const cache = @import("graph_cache.zig"); +const Stream = @import("stream.zig").Stream; +const no_stream: Stream = .{ .d = undefined, .handle = null }; +const expect = std.testing.expect; +const equal = std.testing.expectEqual; + +const Engine = struct { + captures: usize = 0, + live: usize = 0, + launches: usize = 0, + bodies: usize = 0, + handles: [32]u8 = undefined, + + fn engine(e: *Engine) cache.Engine { + return .{ .ctx = e, .capture = capture, .launch = launch, .free = free }; + } + fn capture(p: *anyopaque, _: Stream, _: cache.Body) !*anyopaque { + const e: *Engine = @ptrCast(@alignCast(p)); + e.captures += 1; + e.live += 1; + return &e.handles[e.captures % e.handles.len]; + } + fn launch(p: *anyopaque, _: *anyopaque, _: Stream) !void { + const e: *Engine = @ptrCast(@alignCast(p)); + e.launches += 1; + } + fn free(p: *anyopaque, _: *anyopaque) void { + const e: *Engine = @ptrCast(@alignCast(p)); + std.debug.assert(e.live > 0); + e.live -= 1; + } + fn body(e: *Engine) cache.Body { + return .{ .ctx = e, .run = run }; + } + fn run(p: *anyopaque, _: Stream) !void { + const e: *Engine = @ptrCast(@alignCast(p)); + e.bodies += 1; + } +}; + +const Peer = struct { + verdicts: []const bool, + at: usize = 0, + throw_at: ?usize = null, + fn agree(p: *Peer) cache.Agree { + return .{ .ctx = p, .all = all }; + } + fn all(ctx: *anyopaque, ok: bool) !bool { + const p: *Peer = @ptrCast(@alignCast(ctx)); + const at = p.at; + p.at += 1; + if (p.throw_at == at) return error.CollectiveFailed; + if (at >= p.verdicts.len) return error.UnexpectedCollective; + return ok and p.verdicts[at]; + } +}; + +test "graph cache regression: capture agreement throw frees the not-yet-inserted executable" { + var e: Engine = .{}; + // per miss: room, then the capture's verdict + var p: Peer = .{ .verdicts = &.{ true, true }, .throw_at = 1 }; + var c = cache.Cache(u32).init(std.testing.allocator, e.engine(), .{}); + defer c.deinit(); + c.agree = p.agree(); + try std.testing.expectError(error.CollectiveFailed, c.run(1, no_stream, 7, e.body())); + try equal(@as(usize, 1), e.captures); + try equal(@as(usize, 0), e.live); + try equal(@as(usize, 0), c.count()); + try equal(@as(usize, 2), p.at); +} + +test "graph cache regression: local map OOM declines before any capture and can recover" { + var e: Engine = .{}; + var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{ .fail_index = 0 }); + var c = cache.Cache(u32).init(failing.allocator(), e.engine(), .{}); + defer c.deinit(); + try equal(cache.Outcome.eager, try c.run(1, no_stream, 7, e.body())); + try expect(failing.has_induced_failure); + try equal(@as(usize, 0), e.captures); + try equal(@as(usize, 0), e.live); + try expect(!c.failed); + failing.fail_index = std.math.maxInt(usize); + try equal(cache.Outcome.captured, try c.run(1, no_stream, 7, e.body())); + try equal(@as(usize, 1), e.live); +} + +test "graph cache regression: peer map OOM makes a locally prepared rank eager before capture" { + var e: Engine = .{}; + var p: Peer = .{ .verdicts = &.{false} }; + var c = cache.Cache(u32).init(std.testing.allocator, e.engine(), .{}); + defer c.deinit(); + c.agree = p.agree(); + try equal(cache.Outcome.eager, try c.run(1, no_stream, 7, e.body())); + try equal(@as(usize, 1), p.at); + try equal(@as(usize, 0), e.captures); + try equal(@as(usize, 1), e.bodies); +} + +test "graph cache regression: local OOM still contributes the room verdict" { + var e: Engine = .{}; + var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{ .fail_index = 0 }); + var p: Peer = .{ .verdicts = &.{true} }; + var c = cache.Cache(u32).init(failing.allocator(), e.engine(), .{}); + defer c.deinit(); + c.agree = p.agree(); + try equal(cache.Outcome.eager, try c.run(1, no_stream, 7, e.body())); + try equal(@as(usize, 1), p.at); + try equal(@as(usize, 0), e.captures); +} + +test "graph cache regression: hits make no collective; a fingerprint change shared by every rank recaptures" { + var e: Engine = .{}; + var p: Peer = .{ .verdicts = &.{ true, true, true, true, true, true } }; + var c = cache.Cache(u32).init(std.testing.allocator, e.engine(), .{}); + defer c.deinit(); + c.agree = p.agree(); + _ = try c.run(1, no_stream, 7, e.body()); + _ = try c.run(2, no_stream, 7, e.body()); + try equal(@as(usize, 4), p.at); + for (0..4) |_| try equal(cache.Outcome.replayed, try c.run(1, no_stream, 7, e.body())); + try equal(@as(usize, 4), p.at); + // a new layout generation on every rank: both graphs go, the next miss agrees twice as any other + try equal(cache.Outcome.captured, try c.run(1, no_stream, 8, e.body())); + try equal(@as(usize, 6), p.at); + try equal(@as(u64, 2), c.stats.dropped); + try equal(@as(usize, 1), c.count()); + try equal(@as(usize, 1), e.live); +} + +var room = true; +fn available() bool { + return room; +} + +test "graph cache regression: zero maximum remains eager across pressure recovery, with no collective" { + var e: Engine = .{}; + var p: Peer = .{ .verdicts = &.{} }; + var c = cache.Cache(u32).init(std.testing.allocator, e.engine(), .{ .max = 0 }); + defer c.deinit(); + c.room = available; + c.agree = p.agree(); + for ([_]bool{ false, true, false, true }) |ok| { + room = ok; + try equal(cache.Outcome.eager, try c.run(1, no_stream, 7, e.body())); + try equal(@as(u32, 0), c.cap); + try equal(@as(usize, 0), c.count()); + } + try equal(@as(usize, 0), e.captures); + try equal(@as(usize, 0), p.at); +} + +test "graph cache regression: default floor cannot enlarge a two-entry maximum" { + var e: Engine = .{}; + var c = cache.Cache(u32).init(std.testing.allocator, e.engine(), .{ .max = 2 }); + defer c.deinit(); + c.room = available; + try equal(@as(u32, 2), c.settings.min_keep); + room = false; + _ = try c.run(1, no_stream, 7, e.body()); + room = true; + for (0..8) |key| { + _ = try c.run(@intCast(key), no_stream, 7, e.body()); + try expect(c.count() <= 2 and c.cap <= 2 and e.live <= 2); + } +} + +test "graph cache regression: zero keep floor recovers after reducing the cap to zero" { + var e: Engine = .{}; + var c = cache.Cache(u32).init(std.testing.allocator, e.engine(), .{ .max = 2, .min_keep = 0 }); + defer c.deinit(); + c.room = available; + room = false; + try equal(cache.Outcome.eager, try c.run(1, no_stream, 7, e.body())); + try equal(@as(u32, 0), c.cap); + room = true; + try equal(cache.Outcome.captured, try c.run(1, no_stream, 7, e.body())); + try equal(@as(u32, 2), c.cap); +} diff --git a/zig/src/cuda/graph_registry.zig b/zig/src/cuda/graph_registry.zig new file mode 100644 index 0000000000..1922b2fd5b --- /dev/null +++ b/zig/src/cuda/graph_registry.zig @@ -0,0 +1,141 @@ +//! The graph caches' counts as other threads read them (/health): fixed slots under one lock, each name copied in. + +const std = @import("std"); + +/// A name's bytes kept per slot; a longer name is cut to this many. +pub const name_max = 32; + +pub const Counts = struct { held: u32 = 0, captured: u64 = 0, replayed: u64 = 0, eager: u64 = 0, floor_hits: u64 = 0, under: bool = false }; + +/// One cache's name and counts at one instant; it owns its name, so it outlives the cache it was taken from. +pub const Snapshot = struct { + name_buf: [name_max]u8 = undefined, + name_len: u8 = 0, + counts: Counts = .{}, + + pub fn name(s: *const Snapshot) []const u8 { + return s.name_buf[0..s.name_len]; + } +}; + +const Slot = struct { taken: bool = false, snap: Snapshot = .{} }; + +var slots: [8]Slot = @splat(.{}); +var mutex: std.atomic.Mutex = .unlocked; + +/// Test seams: `contended` runs when a lock attempt finds it held, `reading` between a slot's name and counts copies. +var contended: ?*const fn () void = null; +var reading: ?*const fn () void = null; + +fn lock() void { + if (mutex.tryLock()) return; + if (contended) |f| f(); + while (!mutex.tryLock()) std.Thread.yield() catch {}; +} + +/// A free slot holding a copy of `name_text`, or null when all are taken (that cache is then not reported). +pub fn claim(name_text: []const u8) ?usize { + lock(); + defer mutex.unlock(); + for (&slots, 0..) |*s, i| if (!s.taken) { + const n = @min(name_text.len, name_max); + s.* = .{ .taken = true }; + @memcpy(s.snap.name_buf[0..n], name_text[0..n]); + s.snap.name_len = @intCast(n); + return i; + }; + return null; +} + +pub fn publish(i: usize, c: Counts) void { + lock(); + defer mutex.unlock(); + slots[i].snap.counts = c; +} + +pub fn release(i: usize) void { + lock(); + defer mutex.unlock(); + slots[i] = .{}; +} + +/// The slots in use, at most `out.len`; returns how many it filled. Each copy is one cache's name and counts. +pub fn snapshots(out: []Snapshot) usize { + lock(); + defer mutex.unlock(); + var n: usize = 0; + for (&slots) |*s| { + if (n == out.len) break; + if (!s.taken) continue; + out[n].name_buf = s.snap.name_buf; + out[n].name_len = s.snap.name_len; + if (reading) |f| f(); + out[n].counts = s.snap.counts; + n += 1; + } + return n; +} + +// -- test: a reader overlapping a release and a reuse of its slot, ordered by the seams (no timing) --------------- + +const Overlap = struct { + var slot: usize = 0; + var reused: usize = 0; + var thread: ?std.Thread = null; + var blocked: std.atomic.Value(bool) = .init(false); + var done: std.atomic.Value(bool) = .init(false); + + /// Another thread frees the reader's slot and claims it for a new cache, as a deinit and a new cache's first step. + fn other() void { + release(slot); + reused = claim("second").?; + publish(reused, .{ .captured = 2 }); + done.store(true, .release); + } + + /// Mid-copy: start the other thread and wait until it is blocked on the lock or (without one) has finished. + fn mid() void { + if (thread != null) return; + thread = std.Thread.spawn(.{}, other, .{}) catch unreachable; + while (!blocked.load(.acquire) and !done.load(.acquire)) std.Thread.yield() catch {}; + } + + fn onContended() void { + blocked.store(true, .release); + } +}; + +test "a snapshot overlapping its slot's release and reuse copies one cache's name and counts, never a mix" { + const t = std.testing; + Overlap.slot = claim("first").?; + publish(Overlap.slot, .{ .captured = 1 }); + reading = Overlap.mid; + contended = Overlap.onContended; + var out: [8]Snapshot = undefined; + const n = snapshots(&out); + reading = null; + Overlap.thread.?.join(); + contended = null; + defer release(Overlap.reused); // a failed check leaves no slot taken for later tests + try t.expectEqual(@as(usize, 1), n); + try t.expectEqualStrings("first", out[0].name()); + try t.expectEqual(@as(u64, 1), out[0].counts.captured); + try t.expect(Overlap.blocked.load(.acquire)); // the other thread met the lock while the copy was in progress + // after the release and reuse: the new cache, with its own name and counts + try t.expectEqual(@as(usize, 1), snapshots(&out)); + try t.expectEqualStrings("second", out[0].name()); + try t.expectEqual(@as(u64, 2), out[0].counts.captured); + release(Overlap.reused); + try t.expectEqual(@as(usize, 0), snapshots(&out)); +} + +test "a long name is cut to name_max and the copy outlives the caller's buffer" { + var buf: [40]u8 = @splat('n'); + const i = claim(&buf).?; + defer release(i); + buf = @splat('x'); + var out: [8]Snapshot = undefined; + try std.testing.expectEqual(@as(usize, 1), snapshots(&out)); + const want: [name_max]u8 = @splat('n'); + try std.testing.expectEqualStrings(&want, out[0].name()); +} diff --git a/zig/src/cuda/launch.zig b/zig/src/cuda/launch.zig index 56bba5272a..47cbf720b6 100644 --- a/zig/src/cuda/launch.zig +++ b/zig/src/cuda/launch.zig @@ -4,7 +4,8 @@ const std = @import("std"); const abi = @import("abi.zig"); const Function = @import("module.zig").Function; const Stream = @import("stream.zig").Stream; -const Error = @import("driver.zig").Error; +const driver = @import("driver.zig"); +const Error = driver.Error; pub const Dim3 = abi.Dim3; @@ -88,7 +89,7 @@ pub fn launch(f: Function, cfg: Config, stream: Stream, args: *Args) Error!void const g = cfg.grid; const b = cfg.block; if (cfg.plain()) { - return d.check(d.api.cuLaunchKernel(f.handle, g.x, g.y, g.z, b.x, b.y, b.z, cfg.shared, stream.handle, args.pointers(), null), "cuLaunchKernel"); + return launched(f, cfg, d.api.cuLaunchKernel(f.handle, g.x, g.y, g.z, b.x, b.y, b.z, cfg.shared, stream.handle, args.pointers(), null), "cuLaunchKernel"); } var attrs: [4]abi.LaunchAttribute = undefined; const n = fillAttributes(cfg, &attrs); @@ -104,7 +105,37 @@ pub fn launch(f: Function, cfg: Config, stream: Stream, args: *Args) Error!void .attrs = &attrs, .num_attrs = @intCast(n), }; - try d.check(d.api.cuLaunchKernelEx(&lc, f.handle, args.pointers(), null), "cuLaunchKernelEx"); + try launched(f, cfg, d.api.cuLaunchKernelEx(&lc, f.handle, args.pointers(), null), "cuLaunchKernelEx"); +} + +/// A launch's result; a failure names the kernel and its configuration with the code. +fn launched(f: Function, cfg: Config, res: abi.Result, what: []const u8) Error!void { + if (res == abi.success) return; + if (res == abi.error_not_ready) return error.NotReady; // as Driver.check: not a failure to log + const d = f.d; + var buf: [160]u8 = undefined; + std.log.err("{s}: {s} ({d}) {s}; {s}{s}", .{ what, d.errorName(res), res, d.errorText(res), describe(&buf, d.functionName(f.handle), cfg), driver.shutdownNote(res) }); + return driver.Driver.errorOf(res); +} + +/// "kernel , grid XxYxZ, block XxYxZ, shared N B" (+ cluster / PDL / cooperative when set). +pub fn describe(buf: []u8, name: []const u8, cfg: Config) []const u8 { + const g = cfg.grid; + const b = cfg.block; + var w = std.Io.Writer.fixed(buf); + w.print("kernel {s}, grid {d}x{d}x{d}, block {d}x{d}x{d}, shared {d} B", .{ name, g.x, g.y, g.z, b.x, b.y, b.z, cfg.shared }) catch return w.buffered(); + if (cfg.cluster) |c| w.print(", cluster {d}x{d}x{d}", .{ c.x, c.y, c.z }) catch return w.buffered(); + if (cfg.pdl) w.writeAll(", pdl") catch return w.buffered(); + if (cfg.cooperative) w.writeAll(", cooperative") catch return w.buffered(); + return w.buffered(); +} + +test "a failed launch's description: the kernel, its grid, block and shared bytes, bounded" { + var buf: [160]u8 = undefined; + const cfg: Config = .{ .grid = .{ .x = 70000, .y = 2, .z = 1 }, .block = .{ .x = 128, .y = 1, .z = 1 }, .shared = 49152, .pdl = true }; + try std.testing.expectEqualStrings("kernel _stream_pf, grid 70000x2x1, block 128x1x1, shared 49152 B, pdl", describe(&buf, "_stream_pf", cfg)); + var small: [24]u8 = undefined; + try std.testing.expect(describe(&small, "a_very_long_kernel_symbol_name", cfg).len <= small.len); } fn attribute(id: abi.LaunchAttributeId) abi.LaunchAttribute { diff --git a/zig/src/cuda/memory.zig b/zig/src/cuda/memory.zig index 28c5b6244f..0f8d4aae58 100644 --- a/zig/src/cuda/memory.zig +++ b/zig/src/cuda/memory.zig @@ -5,13 +5,14 @@ const abi = @import("abi.zig"); const Driver = @import("driver.zig").Driver; const Error = @import("driver.zig").Error; -/// Bytes this process holds in DeviceBuffers and HostBuffers, and device/pinned peaks since the last reset. -pub const Usage = struct { device: u64, host: u64, peak: u64, host_peak: u64 = 0 }; +/// Bytes held in DeviceBuffers and HostBuffers, device/pinned peaks since the last reset, and the driver allocations. +pub const Usage = struct { device: u64, host: u64, peak: u64, host_peak: u64 = 0, allocs: u64 = 0 }; var device_bytes: std.atomic.Value(u64) = .init(0); var host_bytes: std.atomic.Value(u64) = .init(0); var host_peak_bytes: std.atomic.Value(u64) = .init(0); var peak_bytes: std.atomic.Value(u64) = .init(0); +var driver_allocs: std.atomic.Value(u64) = .init(0); // live DeviceBuffers from cuMemAlloc itself, not from a hook var counts_mutex: std.atomic.Mutex = .unlocked; var allocation_limit: ?u64 = null; var reserved_bytes: u64 = 0; @@ -75,7 +76,7 @@ fn sample(reset_peak: bool, hook: anytype, pre_reset: bool) Usage { peak_bytes.store(now, .monotonic); host_peak_bytes.store(host, .monotonic); } - return .{ .device = now, .host = host, .peak = if (pre_reset) peak else @max(now, peak_bytes.load(.monotonic)), .host_peak = if (pre_reset) host_peak else @max(host, host_peak_bytes.load(.monotonic)) }; + return .{ .device = now, .host = host, .peak = if (pre_reset) peak else @max(now, peak_bytes.load(.monotonic)), .host_peak = if (pre_reset) host_peak else @max(host, host_peak_bytes.load(.monotonic)), .allocs = driver_allocs.load(.monotonic) }; } fn recordAllocation(counter: *std.atomic.Value(u64), n: usize) void { @@ -100,18 +101,42 @@ fn lockCounts() void { while (!counts_mutex.tryLock()) std.Thread.yield() catch {}; } +/// A sub-allocator under DeviceBuffer (arena.zig): `alloc` gives an address or null for cuMemAlloc, `free` owns it. +pub const Hook = struct { + ctx: *anyopaque, + alloc: *const fn (ctx: *anyopaque, len: usize) ?abi.DevicePtr, + free: *const fn (ctx: *anyopaque, p: abi.DevicePtr) bool, // true when the address was the hook's +}; + +var alloc_hook: ?Hook = null; + +/// Installs, or with null removes, the sub-allocator: once at boot, before the buffers it should hold. +pub fn setHook(h: ?Hook) void { + alloc_hook = h; +} + +pub fn currentHook() ?Hook { + return alloc_hook; +} + pub const DeviceBuffer = struct { d: *const Driver, ptr: abi.DevicePtr, len: usize, borrowed: bool = false, // a span of memory another value owns (a carveout's): free leaves it alone - /// `len` bytes, 256-byte aligned by the driver; zero bytes allocate nothing and hold address 0. + /// `len` bytes, 256-byte aligned (512 from an installed arena); zero bytes allocate nothing and hold address 0. pub fn alloc(d: *const Driver, len: usize) Error!DeviceBuffer { try reserveAllocation(len); errdefer finishAllocation(null, len); var p: abi.DevicePtr = 0; - if (len > 0) try d.check(d.api.cuMemAlloc_v2(&p, len), "cuMemAlloc"); + if (len > 0) { + if (alloc_hook) |h| p = h.alloc(h.ctx, len) orelse 0; + if (p == 0) { + try d.check(d.api.cuMemAlloc_v2(&p, len), "cuMemAlloc"); + _ = driver_allocs.fetchAdd(1, .monotonic); + } + } finishAllocation(&device_bytes, len); return .{ .d = d, .ptr = p, .len = len }; } @@ -129,7 +154,13 @@ pub const DeviceBuffer = struct { pub fn free(self: *DeviceBuffer) void { if (!self.borrowed) { - if (self.ptr != 0) _ = self.d.api.cuMemFree_v2(self.ptr); + if (self.ptr != 0) { + const mine = if (alloc_hook) |h| h.free(h.ctx, self.ptr) else false; + if (!mine) { + _ = self.d.api.cuMemFree_v2(self.ptr); + _ = driver_allocs.fetchSub(1, .monotonic); + } + } freed(&device_bytes, self.len); } self.* = undefined; @@ -266,6 +297,70 @@ test "freeing a borrowed span leaves the driver and the counts alone" { try std.testing.expectEqual(before.device, usage(false).device); } +test "a buffer the hook owns is freed by the hook, never by the driver" { + const Owner = struct { + var frees: u32 = 0; + fn alloc(_: *anyopaque, _: usize) ?abi.DevicePtr { + return null; + } + fn free(_: *anyopaque, p: abi.DevicePtr) bool { + frees += 1; + return p == 0x2000; + } + }; + var ctx: u8 = 0; + setHook(.{ .ctx = &ctx, .alloc = Owner.alloc, .free = Owner.free }); + defer setHook(null); + held(&device_bytes, 4096); + var b: DeviceBuffer = .{ .d = undefined, .ptr = 0x2000, .len = 4096 }; + b.free(); + var c: DeviceBuffer = .{ .d = undefined, .ptr = 0x3000, .len = 64, .borrowed = true }; + c.free(); + try std.testing.expectEqual(@as(u32, 1), Owner.frees); +} + +test "the driver allocation count follows cuMemAlloc and cuMemFree, not a hook's or a borrowed span's" { + const Fake = struct { + var next: abi.DevicePtr = 0x10_0000; + fn alloc(p: *abi.DevicePtr, len: usize) callconv(.c) abi.Result { + p.* = next; + next += len; + return abi.success; + } + fn free(_: abi.DevicePtr) callconv(.c) abi.Result { + return abi.success; + } + fn hookAlloc(_: *anyopaque, len: usize) ?abi.DevicePtr { + return if (len == 4096) 0x9000 else null; // the hook serves one size, the rest go to the driver + } + fn hookFree(_: *anyopaque, p: abi.DevicePtr) bool { + return p == 0x9000; + } + }; + var d: Driver = .{ .lib = undefined, .api = undefined }; + d.api.cuMemAlloc_v2 = Fake.alloc; + d.api.cuMemFree_v2 = Fake.free; + const before = usage(false).allocs; + var a = try DeviceBuffer.alloc(&d, 1000); + var b = try DeviceBuffer.alloc(&d, 2000); + try std.testing.expectEqual(before + 2, usage(false).allocs); + var ctx: u8 = 0; + setHook(.{ .ctx = &ctx, .alloc = Fake.hookAlloc, .free = Fake.hookFree }); + var h = try DeviceBuffer.alloc(&d, 4096); + var c = try DeviceBuffer.alloc(&d, 64); + try std.testing.expectEqual(@as(abi.DevicePtr, 0x9000), h.ptr); + try std.testing.expectEqual(before + 3, usage(false).allocs); + h.free(); + c.free(); + setHook(null); + var s: DeviceBuffer = .{ .d = &d, .ptr = a.ptr + 256, .len = 256, .borrowed = true }; + s.free(); + try std.testing.expectEqual(before + 2, usage(false).allocs); + a.free(); + b.free(); + try std.testing.expectEqual(before, usage(false).allocs); +} + test "peak reset preserves an allocation between its read and store" { const before = usage(true); held(&device_bytes, 100); diff --git a/zig/src/cuda/nccl.zig b/zig/src/cuda/nccl.zig index 420b2d460a..66fc50c3f1 100644 --- a/zig/src/cuda/nccl.zig +++ b/zig/src/cuda/nccl.zig @@ -39,6 +39,8 @@ pub const Config = extern struct { pub const Error = error{ LibraryUnavailable, MissingSymbol, NcclFailed }; +pub const in_progress: Result = 7; // ncclInProgress: a non-blocking communicator's call is still running + const R = Result; const D = abi.DevicePtr; diff --git a/zig/src/cuda/pack.zig b/zig/src/cuda/pack.zig new file mode 100644 index 0000000000..a69557f665 --- /dev/null +++ b/zig/src/cuda/pack.zig @@ -0,0 +1,300 @@ +//! A load's permanent buffers bumped back to back in one growing mapping (a memory.Hook), not 2 MiB-rounded each. + +const std = @import("std"); +const memory = @import("memory.zig"); +const arena = @import("arena.zig"); + +pub const Pack = struct { + gpa: std.mem.Allocator, + be: arena.Backend, + alignment: usize = 512, + prev: ?memory.Hook = null, + open: bool = false, + mutex: std.atomic.Mutex = .unlocked, + /// mapped segments (base, len): one unless the backend hands out a range not following the last + segments: std.ArrayList([2]u64) = .empty, + cursor: u64 = 0, + limit: u64 = 0, + stats: struct { buffers: u64 = 0, bytes: u64 = 0, mapped: u64 = 0, freed: u64 = 0, outside: u64 = 0, fell_back: u64 = 0 } = .{}, + /// a backend whose chunks do not follow each other (cuMemAlloc): segments of `seg_min`, larger buffers their own + fallback: ?arena.Backend = null, + contiguous: bool = true, + seg_min: usize = 256 << 20, + + pub fn init(gpa: std.mem.Allocator, be: arena.Backend) Pack { + return .{ .gpa = gpa, .be = be }; + } + + /// Forgets the segments; their memory stays mapped (the backend's owner releases it). + pub fn deinit(p: *Pack) void { + p.segments.deinit(p.gpa); + } + + /// The first backend's chunk failed: pack on the fallback from here (none: the buffer goes to cuMemAlloc). + fn fallBack(p: *Pack) bool { + const f = p.fallback orelse return false; + p.be = f; + p.fallback = null; + p.contiguous = false; + p.cursor = 0; + p.limit = 0; + p.stats.fell_back += 1; + return true; + } + + /// Under DeviceBuffer.alloc / free, over whatever hook was there (the pack must outlive every buffer it hands out). + pub fn install(p: *Pack) void { + p.prev = memory.currentHook(); + memory.setHook(.{ .ctx = p, .alloc = hookAlloc, .free = hookFree }); + } + + pub fn begin(p: *Pack) void { + p.open = true; + } + + pub fn end(p: *Pack) void { + p.open = false; + } + + fn lock(p: *Pack) void { + while (!p.mutex.tryLock()) std.Thread.yield() catch {}; + } + + /// An address for `len` bytes while the phase is open; else the previous hook's (null: cuMemAlloc). + pub fn alloc(p: *Pack, len: usize) ?u64 { + if (len == 0) return null; + p.lock(); + defer p.mutex.unlock(); + if (!p.open) { + p.stats.outside += 1; + const h = p.prev orelse return null; + return h.alloc(h.ctx, len); + } + if (!p.contiguous) return p.segmented(len); + const at = std.mem.alignForward(u64, p.cursor, p.alignment); + if (at + len > p.limit) { + const grow = std.mem.alignForward(usize, @intCast(at + len - p.limit), p.be.granularity); + if (p.limit != 0) if (p.be.chunk(p.be.ctx, grow)) |base| { + p.stats.mapped += grow; + if (base == p.limit) { + p.segments.items[p.segments.items.len - 1][1] += grow; + p.limit += grow; + return p.place(len); + } + // a range not following the last (another user of the backend mapped in between): give it back + p.be.release(p.be.ctx, base, grow); + p.stats.mapped -= grow; + }; + // the first segment, or a new one: the buffer's own pages to start it + const n = std.mem.alignForward(usize, len, p.be.granularity); + const base = p.be.chunk(p.be.ctx, n) orelse return if (p.fallBack()) p.segmented(len) else null; + p.segments.append(p.gpa, .{ base, n }) catch return null; + p.stats.mapped += n; + p.cursor = base; + p.limit = base + n; + } + return p.place(len); + } + + /// Without contiguous growth: a `seg_min`+ buffer its own chunk, the rest bumped in segments. + fn segmented(p: *Pack, len: usize) ?u64 { + if (len >= p.seg_min) { + const n = std.mem.alignForward(usize, len, p.be.granularity); + const base = p.be.chunk(p.be.ctx, n) orelse return null; + p.segments.append(p.gpa, .{ base, n }) catch return null; + p.stats.mapped += n; + p.stats.buffers += 1; + p.stats.bytes += len; + return base; + } + if (std.mem.alignForward(u64, p.cursor, p.alignment) + len > p.limit) { + const base = p.be.chunk(p.be.ctx, p.seg_min) orelse return null; + p.segments.append(p.gpa, .{ base, p.seg_min }) catch return null; + p.stats.mapped += p.seg_min; + p.cursor = base; + p.limit = base + p.seg_min; + } + return p.place(len); + } + + fn place(p: *Pack, len: usize) u64 { + const at = std.mem.alignForward(u64, p.cursor, p.alignment); + p.cursor = at + len; + p.stats.buffers += 1; + p.stats.bytes += len; + return at; + } + + fn owns(p: *const Pack, x: u64) bool { + for (p.segments.items) |s| if (x >= s[0] and x < s[0] + s[1]) return true; + return false; + } + + /// A packed address stays mapped until close (true: ours); any other goes to the previous hook. + pub fn free(p: *Pack, x: u64) bool { + p.lock(); + defer p.mutex.unlock(); + if (p.owns(x)) { + p.stats.freed += 1; + return true; + } + const h = p.prev orelse return false; + return h.free(h.ctx, x); + } + + /// Mapped bytes past the packed ones: the segments' tails and alignment (what packing still costs). + pub fn slack(p: *const Pack) u64 { + return p.stats.mapped - p.stats.bytes; + } + + fn hookAlloc(ctx: *anyopaque, len: usize) ?u64 { + const p: *Pack = @ptrCast(@alignCast(ctx)); + return p.alloc(len); + } + + fn hookFree(ctx: *anyopaque, x: u64) bool { + const p: *Pack = @ptrCast(@alignCast(ctx)); + return p.free(x); + } +}; + +// -- tests: a fake backend handing out contiguous ranges, and a previous hook that counts ---------------------------- + +const Fake = struct { + next: u64 = 1 << 40, + chunks: u32 = 0, + gap: bool = false, + + fn backend(f: *Fake, g: usize) arena.Backend { + return .{ .ctx = f, .granularity = g, .chunk = chunk, .release = release, .name = "fake" }; + } + fn chunk(ctx: *anyopaque, len: usize) ?u64 { + const f: *Fake = @ptrCast(@alignCast(ctx)); + if (f.gap) f.next += 4096; // another mapping in between + const at = f.next; + f.next += len; + f.chunks += 1; + return at; + } + fn release(_: *anyopaque, _: u64, _: usize) void {} +}; + +/// A backend whose every chunk fails (a VMM that maps nothing would otherwise pack 0 buffers). +const Broken = struct { + calls: u32 = 0, + fn backend(b: *Broken) arena.Backend { + return .{ .ctx = b, .granularity = 2 << 20, .chunk = chunk, .release = Fake.release, .name = "broken" }; + } + fn chunk(ctx: *anyopaque, _: usize) ?u64 { + const b: *Broken = @ptrCast(@alignCast(ctx)); + b.calls += 1; + return null; + } +}; + +const Prev = struct { + allocs: u32 = 0, + frees: u32 = 0, + fn hook(p: *Prev) memory.Hook { + return .{ .ctx = p, .alloc = a, .free = f }; + } + fn a(ctx: *anyopaque, _: usize) ?u64 { + const p: *Prev = @ptrCast(@alignCast(ctx)); + p.allocs += 1; + return 0x7000_0000; + } + fn f(ctx: *anyopaque, _: u64) bool { + const p: *Prev = @ptrCast(@alignCast(ctx)); + p.frees += 1; + return true; + } +}; + +test "pack: permanent buffers back to back, 512-aligned, under a granularity page of slack in all" { + const t = std.testing; + var fake: Fake = .{}; + var prev: Prev = .{}; + var p = Pack.init(t.allocator, fake.backend(2 << 20)); + defer p.deinit(); + p.prev = prev.hook(); + p.begin(); + // the weights' mix: many tensors of odd sizes, a few large ones + var sizes: [600]usize = undefined; + var prng = std.Random.DefaultPrng.init(3); + for (&sizes, 0..) |*s, i| s.* = if (i % 50 == 0) 300 << 20 else prng.random().intRangeAtMost(usize, 1000, 9 << 20); + var total: u64 = 0; + var last: u64 = 0; + for (sizes) |s| { + const x = p.alloc(s).?; + try t.expectEqual(@as(u64, 0), x % 512); + try t.expect(x >= last); // in order, never overlapping the one before + last = x + s; + total += s; + } + try t.expectEqual(@as(usize, 1), p.segments.items.len); + try t.expect(p.slack() < (2 << 20) + 600 * 512); + // one cuMemAlloc each would round each to 2 MiB: far more than the pack's slack + var rounded: u64 = 0; + for (sizes) |s| rounded += std.mem.alignForward(u64, s, 2 << 20); + try t.expect(rounded - total > 100 * p.slack()); + // a packed buffer's free is ours and a no-op; after the phase the previous hook serves and frees + const kept = p.stats.mapped; + try t.expect(p.free(last - 1)); + p.end(); + try t.expectEqual(@as(?u64, 0x7000_0000), p.alloc(4096)); + try t.expect(p.free(0x7000_0000)); + try t.expectEqual(@as(u32, 1), prev.allocs); + try t.expectEqual(@as(u32, 1), prev.frees); + try t.expectEqual(kept, p.stats.mapped); +} + +test "pack: a range that does not follow the last starts a new segment" { + const t = std.testing; + var fake: Fake = .{}; + var p = Pack.init(t.allocator, fake.backend(2 << 20)); + defer p.deinit(); + p.begin(); + _ = p.alloc(3 << 20).?; + fake.gap = true; + const x = p.alloc(3 << 20).?; + try t.expectEqual(@as(usize, 2), p.segments.items.len); + try t.expect(p.owns(x) and !p.owns(1)); +} + +test "pack: when the first backend's chunks fail it packs on the fallback; a boot never packs 0 buffers" { + const t = std.testing; + var broken: Broken = .{}; + var plain: Fake = .{ .gap = true }; // cuMemAlloc's chunks: never following each other + var p = Pack.init(t.allocator, broken.backend()); + defer p.deinit(); + p.fallback = plain.backend(2 << 20); + p.seg_min = 64 << 20; + p.begin(); + var sizes: [800]usize = undefined; + var prng = std.Random.DefaultPrng.init(5); + // the weights' small tensors (scales, norms, tables) between a few large ones + for (&sizes, 0..) |*s, i| s.* = if (i % 40 == 0) 300 << 20 else prng.random().intRangeAtMost(usize, 1000, 2 << 20); + var total: u64 = 0; + var spans: std.ArrayList([2]u64) = .empty; + defer spans.deinit(t.allocator); + for (sizes) |s| { + const x = p.alloc(s) orelse return error.NotPacked; + try t.expectEqual(@as(u64, 0), x % 512); + try spans.append(t.allocator, .{ x, x + s }); + total += s; + } + try t.expectEqual(@as(u32, 1), broken.calls); // tried once, then the fallback for good + try t.expectEqual(@as(u64, 1), p.stats.fell_back); + try t.expectEqual(@as(u64, sizes.len), p.stats.buffers); + // no two buffers overlap + std.mem.sort([2]u64, spans.items, {}, struct { + fn lt(_: void, a: [2]u64, b: [2]u64) bool { + return a[0] < b[0]; + } + }.lt); + for (spans.items[1..], spans.items[0 .. spans.items.len - 1]) |b, a| try t.expect(b[0] >= a[1]); + // slack: the segments' tails and the large buffers' own rounding, a small part of 2 MiB-rounding each buffer + var rounded: u64 = 0; + for (sizes) |s| rounded += std.mem.alignForward(u64, s, 2 << 20); + try t.expect(p.slack() * 10 < rounded - total); +} diff --git a/zig/src/cuda/root.zig b/zig/src/cuda/root.zig index bd3ffa6845..e18995e182 100644 --- a/zig/src/cuda/root.zig +++ b/zig/src/cuda/root.zig @@ -54,6 +54,9 @@ pub const tp_group = @import("tp_group.zig"); pub const tp_watch = @import("tp_watch.zig"); pub const ck = @import("ck.zig"); pub const modelopt = @import("modelopt.zig"); +pub const graph_cache = @import("graph_cache.zig"); +pub const arena = @import("arena.zig"); +pub const pack = @import("pack.zig"); pub const tree_attention = @import("tree_attention.zig"); pub const block_attention = @import("block_attention.zig"); pub const window_attention = @import("window_attention.zig"); @@ -100,6 +103,10 @@ test { _ = @import("modelopt.zig"); _ = @import("ck.zig"); _ = @import("tp_watch.zig"); + _ = graph_cache; + _ = @import("graph_registry.zig"); + _ = arena; + _ = pack; _ = @import("shared_test.zig"); _ = @import("tree_attention.zig"); _ = @import("block_attention.zig"); diff --git a/zig/src/cuda/stream.zig b/zig/src/cuda/stream.zig index cca3cbc768..1733965193 100644 --- a/zig/src/cuda/stream.zig +++ b/zig/src/cuda/stream.zig @@ -15,6 +15,22 @@ pub const Stream = struct { return .{ .d = d, .handle = s }; } + /// A non-blocking stream at `priority`, clamped to the context's range (lower numbers run first). + pub const Priority = enum { default, highest }; + + pub fn initPriority(d: *const Driver, priority: Priority) Error!Stream { + var least: c_int = 0; + var greatest: c_int = 0; + try d.check(d.api.cuCtxGetStreamPriorityRange(&least, &greatest), "cuCtxGetStreamPriorityRange"); + var s: abi.Stream = null; + const p: c_int = switch (priority) { + .default => 0, + .highest => greatest, + }; + try d.check(d.api.cuStreamCreateWithPriority(&s, abi.stream_non_blocking, p), "cuStreamCreateWithPriority"); + return .{ .d = d, .handle = s }; + } + /// A stream its caller owns, such as a host framework's (null: the legacy default stream); never deinit it. pub fn borrowed(d: *const Driver, handle: abi.Stream) Stream { return .{ .d = d, .handle = handle }; @@ -60,6 +76,11 @@ pub const Event = struct { try self.d.check(self.d.api.cuEventRecord(self.handle, stream.handle), "cuEventRecord"); } + /// A record a capture keeps as an event record node: outside waits after a launch wait for that point of it. + pub fn recordExternal(self: Event, stream: Stream) Error!void { + try self.d.check(self.d.api.cuEventRecordWithFlags(self.handle, stream.handle, abi.event_record_external), "cuEventRecordWithFlags"); + } + pub fn synchronize(self: Event) Error!void { try self.d.check(self.d.api.cuEventSynchronize(self.handle), "cuEventSynchronize"); } diff --git a/zig/src/fabric/tp/README.md b/zig/src/fabric/tp/README.md new file mode 100644 index 0000000000..7ad00ef7d5 --- /dev/null +++ b/zig/src/fabric/tp/README.md @@ -0,0 +1,72 @@ +# Tensor parallelism across processes + +`zig/src/fabric/tp` runs tensor parallelism with one GPU per process, either on one host or on two hosts over RoCE. +`TF_COMM_BACKEND` picks the transport for small exchanges: `nccl` (the default), `mailbox` (two GPUs on one host) +or `roce` (two hosts). NCCL carries everything larger, so it must work on its own as well. + +## Building `tf-tp-test` + +The `mailbox` and `roce` backends need the mailbox kernel (`mailbox.cu`) built into the binary: + +```sh +zig build tf-tp-test -Doptimize=ReleaseSafe -Dtp-nvcc=/usr/local/cuda/bin/nvcc +# or embed a fatbin built elsewhere: +zig build tf-tp-test -Doptimize=ReleaseSafe -Dtp-fatbin=/abs/path/tp_mailbox.fatbin +``` + +A build without `-Dtp-nvcc` or `-Dtp-fatbin` still runs `nccl` and `roce-host`, which needs no GPU. +In that build the `mailbox` and `roce` suites fail at load with `NoKernel` (an engine falls back to NCCL instead). +The `INFO` line `tf-tp-test` prints at start shows the embedded kernel's size; `0 B` means none was built in. + +## Choosing the RDMA devices + +| Variable | Used by | Needed when | +| --- | --- | --- | +| `TF_TP_ROCE_HCA` | the RoCE wire | the host has more than one active RoCE port that is not cabled to the peer | +| `NCCL_IB_HCA` | NCCL | the same as above; NCCL otherwise picks its own devices | +| `NCCL_SOCKET_IFNAME` | NCCL bootstrap | the host has several IP interfaces and NCCL picks one the peer cannot reach | +| `TF_TP_MASTER` | the TCP bootstrap | always on two hosts: rank 0's address as the other rank reaches it | + +`TF_TP_ROCE_HCA` is `name[:gid],name[:gid]`: up to two devices, and optionally each one's RoCE v2 GID index. +Without it the rank picks the devices itself, at most `TF_TP_ROCE_HCAS` (default 2). +A host cabled to more than one neighbour should always set it explicitly. +The two hosts may list their devices in a different order and use different names. +Each device pairs with the peer device on the same IPv4 /24. + +`NCCL_IB_HCA` should name the same devices without the `:gid` suffix. +In NCCL, `:N` after a device name selects port `N`, not a GID index. + +`run_hosts.sh` sets `NCCL_IB_HCA` from `TF_TP_ROCE_HCA` when only that is set. +When neither is set, it first runs `tf-tp-test hca` on both ranks: the RoCE pick, made with the peer over the bootstrap. +The same rule picks the wire's devices, and each rank prints its own as `NCCL_IB_HCA=name,...`. +`hca` needs no GPU, and it exits 1 when no port shares a /24 with the peer, so no host falls back to its first ports. +The runner logs each rank's pick and stops with that reason when `hca` fails. +Both hosts must use the same mode: if one names its ports and the other names none, only the latter runs `hca`. +It finds no peer and stops after `TF_TP_PLAN_TIMEOUT` (default 300 s) with that reason. + +Two DGX Spark nodes, with the second ConnectX-7 port of each cabled to the other (both PCIe functions of that +port), and the first port cabled to a third node: + +```sh +# on both hosts; rank 0's link address is 192.0.2.1 +export TF_TP_ROCE_HCA=rocep1s0f1,roceP2p1s0f1 +export NCCL_IB_HCA=rocep1s0f1,roceP2p1s0f1 +export NCCL_SOCKET_IFNAME=enp1s0f1np1 +zig/src/fabric/tp/run_hosts.sh 0 192.0.2.1 ./zig-out/bin/tf-tp-test out-rank0 # host A +zig/src/fabric/tp/run_hosts.sh 1 192.0.2.1 ./zig-out/bin/tf-tp-test out-rank1 # host B +``` + +`ibv_devices` lists the RDMA devices; `ibv_devinfo -d NAME` shows a port's state and link layer. +`/sys/class/infiniband/NAME/ports/1/gids/N` and `gid_attrs/ndevs/N` show each GID and its netdev. +`ulimit -l` must allow the RoCE region to be pinned (`unlimited` is simplest). + +## RoCE limits and the failed-transport check + +No RDMA write is larger than 1 MiB, so on `TF_COMM_BACKEND=roce` a `TF_TP_MAILBOX_MAX_KB` above 1024 fails at boot. +`TF_TP_ROCE_CHUNKED_KB` (default 0, off) sends variable-length gathers past the slot over RoCE in slot-sized ops. +It needs a kernel built from this tree's `mailbox.cu`. +At most 2 MiB is posted and not yet completed at any time. +A rank whose transport fails moves its queue pairs to the error state and keeps its region until the process exits. + +`run_transport.sh MASTER_ADDR PEER_SSH OUTDIR` checks that failure path on two hosts with `tf-tp-test failfast V +transport` and stops at the first kernel log line or HCA error counter that grows; run it with no arguments for help. diff --git a/zig/src/fabric/tp/bootstrap.zig b/zig/src/fabric/tp/bootstrap.zig new file mode 100644 index 0000000000..e1a6911efb --- /dev/null +++ b/zig/src/fabric/tp/bootstrap.zig @@ -0,0 +1,311 @@ +//! Rank discovery over TCP (a star on rank 0): small host blobs all-gathered, settings agreed, sockets kept for fate. +const std = @import("std"); +const log = @import("log.zig"); +const sock = @import("sock.zig"); +const Config = @import("config.zig").Config; + +pub const max_ranks = 64; +/// Largest blob a rank contributes to one `allGather`. +pub const max_blob = 64 * 1024; + +const magic: u32 = 0x31425054; // "TPB1" +const version: u32 = 2; + +pub const Error = error{ Protocol, Mismatch, TooLarge } || sock.Error; + +/// `role`: 0 the control connection (bootstrap, then the fate channel), 1 the plan link. +const Hello = extern struct { magic: u32, version: u32, world: u32, rank: u32, role: u32 }; + +pub const Bootstrap = struct { + rank: u32, + world: u32, + /// Rank 0: the socket of rank i at index i (index 0 unused); other ranks: rank 0's socket at index 0. + fds: [max_ranks]sock.Fd = @splat(-1), + /// The plan link's sockets, laid out as `fds`. + plan_fds: [max_ranks]sock.Fd = @splat(-1), + /// Each call's limit for the peers' bytes (the connect timeout). + timeout_ns: u64, + + fn deadline(b: *const Bootstrap) u64 { + return sock.nowNs() + b.timeout_ns; + } + + /// Rank 0 listens on `link_port` and accepts every other rank; the others connect to `master` (connect timeout). + pub fn open(cfg: Config) Error!Bootstrap { + if (cfg.world > max_ranks or cfg.rank >= cfg.world) return error.Protocol; + var b: Bootstrap = .{ .rank = cfg.rank, .world = cfg.world, .timeout_ns = cfg.connect_timeout_ns }; + errdefer b.close(); + if (cfg.world == 1) return b; + if (cfg.rank == 0) { + const l = try sock.listen(cfg.link_port); + defer sock.close(l); + try b.acceptAll(l); + } else { + const until = b.deadline(); + for ([_]u32{ 0, 1 }) |role| { + const fd = try sock.connect(cfg.master, cfg.link_port, until); + if (role == 0) b.fds[0] = fd else b.plan_fds[0] = fd; + const h: Hello = .{ .magic = magic, .version = version, .world = cfg.world, .rank = cfg.rank, .role = role }; + try sock.sendAll(fd, std.mem.asBytes(&h)); + } + } + return b; + } + + /// Rank 0 over an already listening socket (tests: an ephemeral port). + pub fn acceptOn(l: sock.Fd, world: u32, timeout_ns: u64) Error!Bootstrap { + var b: Bootstrap = .{ .rank = 0, .world = world, .timeout_ns = timeout_ns }; + errdefer b.close(); + try b.acceptAll(l); + return b; + } + + fn acceptAll(b: *Bootstrap, l: sock.Fd) Error!void { + // every other rank's control and plan connections, in any order + var joined: u32 = 0; + const until = b.deadline(); + while (joined < 2 * (b.world - 1)) { + const fd = try sock.accept(l, until); + var h: Hello = undefined; + sock.recvExact(fd, std.mem.asBytes(&h), until) catch { + sock.close(fd); + continue; + }; + if (h.magic != magic or h.version != version) { + log.warn("tp bootstrap: a connection that is not a rank (magic {x}); ignored", .{h.magic}); + sock.close(fd); + continue; + } + const table = if (h.role == 0) &b.fds else &b.plan_fds; + if (h.world != b.world or h.rank == 0 or h.rank >= b.world or h.role > 1 or table[h.rank] >= 0) { + log.warn("tp bootstrap: rank {d} of {d} joined a world of {d}", .{ h.rank, h.world, b.world }); + sock.close(fd); + return error.Mismatch; + } + table[h.rank] = fd; + joined += 1; + } + } + + /// Takes the control sockets (the fate channel keeps them); `close` then leaves them open. + pub fn release(b: *Bootstrap) [max_ranks]sock.Fd { + const out = b.fds; + b.fds = @splat(-1); + return out; + } + + /// Takes the plan link's sockets. + pub fn releasePlan(b: *Bootstrap) [max_ranks]sock.Fd { + const out = b.plan_fds; + b.plan_fds = @splat(-1); + return out; + } + + pub fn close(b: *Bootstrap) void { + for ([_]*[max_ranks]sock.Fd{ &b.fds, &b.plan_fds }) |table| for (table) |*fd| if (fd.* >= 0) { + sock.close(fd.*); + fd.* = -1; + }; + } + + /// Every rank's `mine` in rank order into `out`, returned as slices of `out`; every rank calls it equally often. + pub fn allGather(b: *Bootstrap, mine: []const u8, out: []u8, blobs: [][]const u8) Error![]const []const u8 { + if (mine.len > max_blob) return error.TooLarge; + std.debug.assert(blobs.len >= b.world); + if (b.world == 1) { + if (out.len < mine.len) return error.TooLarge; + @memcpy(out[0..mine.len], mine); + blobs[0] = out[0..mine.len]; + return blobs[0..1]; + } + if (b.rank != 0) { + try sendFrame(b.fds[0], mine); + return b.readAll(b.fds[0], out, blobs); + } + // rank 0: everyone's blob, then the whole table to everyone + var used: usize = 0; + for (0..b.world) |i| { + const len: u32 = if (i == 0) @intCast(mine.len) else blk: { + var l: u32 = undefined; + try sock.recvExact(b.fds[i], std.mem.asBytes(&l), b.deadline()); + break :blk l; + }; + if (len > max_blob or used + len > out.len) return error.TooLarge; + if (i == 0) @memcpy(out[used..][0..len], mine) else try sock.recvExact(b.fds[i], out[used..][0..len], b.deadline()); + blobs[i] = out[used..][0..len]; + used += len; + } + for (1..b.world) |i| { + for (blobs[0..b.world]) |blob| try sendFrame(b.fds[i], blob); + } + return blobs[0..b.world]; + } + + fn readAll(b: *Bootstrap, fd: sock.Fd, out: []u8, blobs: [][]const u8) Error![]const []const u8 { + var used: usize = 0; + for (0..b.world) |i| { + var l: u32 = undefined; + try sock.recvExact(fd, std.mem.asBytes(&l), b.deadline()); + if (l > max_blob or used + l > out.len) return error.TooLarge; + try sock.recvExact(fd, out[used..][0..l], b.deadline()); + blobs[i] = out[used..][0..l]; + used += l; + } + return blobs[0..b.world]; + } + + /// Rank 0's `bytes` on every rank (into `out` on the others; `bytes` is ignored there). + pub fn broadcast(b: *Bootstrap, bytes: []const u8, out: []u8) Error!void { + var blobs: [max_ranks][]const u8 = undefined; + var scratch: [4096]u8 = undefined; + if (out.len * b.world > scratch.len) return error.TooLarge; + const all = try b.allGather(if (b.rank == 0) bytes else &.{}, &scratch, &blobs); + if (all[0].len != out.len) return error.Protocol; + @memcpy(out, all[0]); + } + + /// Every rank's `bytes` must be equal (settings that change bytes on the wire); `what` names them in the log. + pub fn agree(b: *Bootstrap, bytes: []const u8, what: []const u8) Error!void { + var blobs: [max_ranks][]const u8 = undefined; + var scratch: [8192]u8 = undefined; + const all = try b.allGather(bytes, &scratch, &blobs); + for (all, 0..) |blob, i| if (!std.mem.eql(u8, blob, all[0])) { + log.warn("tp bootstrap: rank {d}'s {s} differs from rank 0's; both ranks refuse to start", .{ i, what }); + return error.Mismatch; + }; + } +}; + +fn sendFrame(fd: sock.Fd, bytes: []const u8) sock.Error!void { + const l: u32 = @intCast(bytes.len); + try sock.sendAll(fd, std.mem.asBytes(&l)); + try sock.sendAll(fd, bytes); +} + +/// An ephemeral listening port for tests. +pub fn testListener() !struct { fd: sock.Fd, port: u16 } { + const l = try sock.listen(0); + var addr: std.posix.sockaddr.in = undefined; + var len: std.posix.socklen_t = @sizeOf(std.posix.sockaddr.in); + _ = std.c.getsockname(l, @ptrCast(&addr), &len); + return .{ .fd = l, .port = std.mem.bigToNative(u16, addr.port) }; +} + +fn testRank(cfg: Config, out: *[3][32]u8, err: *?anyerror) void { + var b = Bootstrap.open(cfg) catch |e| { + err.* = e; + return; + }; + defer b.close(); + var id: [32]u8 = undefined; + b.broadcast(&@as([32]u8, @splat(0xAB)), &id) catch |e| { + err.* = e; + return; + }; + var blobs: [max_ranks][]const u8 = undefined; + var buf: [256]u8 = undefined; + const mine: [3]u8 = @splat(@intCast(cfg.rank)); + const all = b.allGather(mine[0 .. cfg.rank + 1], &buf, &blobs) catch |e| { + err.* = e; + return; + }; + for (all, 0..) |blob, i| @memcpy(out[i][0..blob.len], blob); + b.agree("same", "settings") catch |e| { + err.* = e; + }; +} + +test "three ranks gather in rank order and agree" { + const l = try testListener(); + defer sock.close(l.fd); + var outs: [3][3][32]u8 = undefined; + var errs: [3]?anyerror = @splat(null); + var threads: [2]std.Thread = undefined; + for (0..2) |i| threads[i] = try std.Thread.spawn(.{}, testRank, .{ Config{ .world = 3, .rank = @intCast(i + 1), .link_port = l.port, .connect_timeout_ns = 5 * std.time.ns_per_s }, &outs[i + 1], &errs[i + 1] }); + var b = try Bootstrap.acceptOn(l.fd, 3, 5 * std.time.ns_per_s); + defer b.close(); + var id: [32]u8 = undefined; + try b.broadcast(&@as([32]u8, @splat(0xAB)), &id); + var blobs: [max_ranks][]const u8 = undefined; + var buf: [256]u8 = undefined; + const all = try b.allGather(&.{0}, &buf, &blobs); + try b.agree("same", "settings"); + for (threads) |t| t.join(); + for (errs) |e| try std.testing.expect(e == null); + try std.testing.expectEqual(@as(usize, 3), all.len); + try std.testing.expectEqualSlices(u8, &.{ 2, 2, 2 }, all[2]); + try std.testing.expectEqualSlices(u8, &.{ 1, 1 }, outs[2][1][0..2]); +} + +test "settings that differ stop every rank" { + const l = try testListener(); + defer sock.close(l.fd); + const T = struct { + fn go(port: u16, err: *?anyerror) void { + var b = Bootstrap.open(.{ .world = 2, .rank = 1, .link_port = port, .connect_timeout_ns = 5 * std.time.ns_per_s }) catch |e| { + err.* = e; + return; + }; + defer b.close(); + b.agree("knobs-b", "settings") catch |e| { + err.* = e; + }; + } + }; + var err: ?anyerror = null; + const t = try std.Thread.spawn(.{}, T.go, .{ l.port, &err }); + var b = try Bootstrap.acceptOn(l.fd, 2, 5 * std.time.ns_per_s); + defer b.close(); + try std.testing.expectError(error.Mismatch, b.agree("knobs-a", "settings")); + t.join(); + try std.testing.expectEqual(@as(?anyerror, error.Mismatch), err); +} + +/// Rank 1 agrees on `theirs.digest()` against rank 0's `ours.digest()`; returns both ranks' results. +fn agreeDigests(ours: Config, theirs: Config) !struct { ?anyerror, ?anyerror } { + const l = try testListener(); + defer sock.close(l.fd); + const T = struct { + fn go(port: u16, cfg: Config, err: *?anyerror) void { + var c = cfg; + c.world = 2; + c.rank = 1; + c.link_port = port; + c.connect_timeout_ns = 5 * std.time.ns_per_s; + var b = Bootstrap.open(c) catch |e| { + err.* = e; + return; + }; + defer b.close(); + b.agree(&c.digest(), "settings") catch |e| { + err.* = e; + }; + } + }; + var err1: ?anyerror = null; + const t = try std.Thread.spawn(.{}, T.go, .{ l.port, theirs, &err1 }); + var b = try Bootstrap.acceptOn(l.fd, 2, 5 * std.time.ns_per_s); + defer b.close(); + var err0: ?anyerror = null; + b.agree(&ours.digest(), "settings") catch |e| { + err0 = e; + }; + t.join(); + return .{ err0, err1 }; +} + +test "a graph cache switched off on one rank stops both at start; 0 and OFF on the two ranks start" { + const on: Config = .{ .graph_cache = .{} }; + const off: Config = .{ .graph_cache = .{}, .graph_cache_env = "off" }; + const r = try agreeDigests(on, off); + try std.testing.expectEqual(@as(?anyerror, error.Mismatch), r[0]); + try std.testing.expectEqual(@as(?anyerror, error.Mismatch), r[1]); + const none = try agreeDigests(.{}, .{ .graph_cache_env = "off" }); // a family without the cache is not refused + try std.testing.expectEqual(@as(?anyerror, null), none[0]); + try std.testing.expectEqual(@as(?anyerror, null), none[1]); + const same = try agreeDigests(.{ .graph_cache = .{}, .graph_cache_env = "0" }, .{ .graph_cache = .{}, .graph_cache_env = "OFF" }); + try std.testing.expectEqual(@as(?anyerror, null), same[0]); + try std.testing.expectEqual(@as(?anyerror, null), same[1]); + const family = try agreeDigests(.{ .graph_cache = .{ .max = 8 } }, .{ .graph_cache = .{ .max = 16 } }); + try std.testing.expectEqual(@as(?anyerror, error.Mismatch), family[0]); +} diff --git a/zig/src/fabric/tp/collective.zig b/zig/src/fabric/tp/collective.zig new file mode 100644 index 0000000000..d2334dc74a --- /dev/null +++ b/zig/src/fabric/tp/collective.zig @@ -0,0 +1,136 @@ +//! The tensor-parallel communicator every backend implements: stream-ordered, graph-capturable, rank-order sums. +const std = @import("std"); +const cuda = @import("cuda"); + +pub const DevicePtr = cuda.abi.DevicePtr; +pub const Stream = cuda.abi.Stream; + +pub const Error = error{ Aborted, Unsupported, Invalid, PeerFailed, Timeout, BackendFailed }; + +/// Element types the collectives move (`reduce` takes the float ones and the 32/64-bit integers). +pub const DType = enum(u8) { + u8, + i32, + i64, + f16, + bf16, + f32, + f64, + + pub fn size(t: DType) usize { + return switch (t) { + .u8 => 1, + .f16, .bf16 => 2, + .i32, .f32 => 4, + .i64, .f64 => 8, + }; + } +}; + +pub const Op = enum(u8) { sum, max, min }; + +/// Which implementation is behind a `Collective` (logs, bench labels). +pub const Kind = enum { nccl, mailbox, hybrid, roce, host }; + +pub const Collective = struct { + ptr: *anyopaque, + vtable: *const VTable, + + pub const VTable = struct { + kind: Kind, + rank: *const fn (ptr: *anyopaque) u32, + world: *const fn (ptr: *anyopaque) u32, + /// recv[count] <- the element-wise reduction of every rank's send[count], added in rank order. + all_reduce: *const fn (ptr: *anyopaque, send: DevicePtr, recv: DevicePtr, count: usize, t: DType, op: Op, stream: Stream) Error!void, + /// recv[world * count] <- every rank's send[count], rank 0's first (NCCL's layout). + all_gather: *const fn (ptr: *anyopaque, send: DevicePtr, recv: DevicePtr, count: usize, t: DType, stream: Stream) Error!void, + /// Two ranks trade: this rank's send[count] lands in `peer`'s recv[count] and the peer's in ours (one group). + exchange: *const fn (ptr: *anyopaque, send: DevicePtr, recv: DevicePtr, count: usize, t: DType, peer: u32, stream: Stream) Error!void, + send: *const fn (ptr: *anyopaque, buf: DevicePtr, count: usize, t: DType, peer: u32, stream: Stream) Error!void, + recv: *const fn (ptr: *anyopaque, buf: DevicePtr, count: usize, t: DType, peer: u32, stream: Stream) Error!void, + /// Host: returns once every rank has called it and this rank's earlier collectives have finished. + barrier: *const fn (ptr: *anyopaque) Error!void, + /// Host: a failure the transport recorded since the last check (a peer that timed out, an NCCL async error). + check: *const fn (ptr: *anyopaque) Error!void, + /// Any thread: end pending and later work at once; safe while another thread is inside a call. + abort: *const fn (ptr: *anyopaque) void, + /// recv[world][stride]: rank r's first lens[r] bytes (device int32 [world]) land at recv + r * stride. + all_gather_v: ?*const fn (ptr: *anyopaque, send: DevicePtr, recv: DevicePtr, stride: usize, lens: DevicePtr, t: DType, stream: Stream) Error!void = null, + /// Whether `all_gather_v` moves only the lengths for shards of `bytes` here (rank-local). Null: never. + var_fits: ?*const fn (ptr: *anyopaque, bytes: usize) bool = null, + /// Host: whether every rank's transport takes device lengths, agreed once per communicator. Null: false. + var_agreed: ?*const fn (ptr: *anyopaque) Error!bool = null, + }; + + pub fn kind(c: Collective) Kind { + return c.vtable.kind; + } + + pub fn rank(c: Collective) u32 { + return c.vtable.rank(c.ptr); + } + + pub fn world(c: Collective) u32 { + return c.vtable.world(c.ptr); + } + + pub fn allReduce(c: Collective, src: DevicePtr, dst: DevicePtr, count: usize, t: DType, op: Op, stream: Stream) Error!void { + return c.vtable.all_reduce(c.ptr, src, dst, count, t, op, stream); + } + + pub fn allGather(c: Collective, src: DevicePtr, dst: DevicePtr, count: usize, t: DType, stream: Stream) Error!void { + return c.vtable.all_gather(c.ptr, src, dst, count, t, stream); + } + + /// `allGather` of `stride` elements a rank where only rank r's first lens[r] bytes matter (`VTable.all_gather_v`). + pub fn allGatherV(c: Collective, src: DevicePtr, dst: DevicePtr, stride: usize, lens: DevicePtr, t: DType, stream: Stream) Error!void { + if (c.vtable.all_gather_v) |f| return f(c.ptr, src, dst, stride, lens, t, stream); + return c.vtable.all_gather(c.ptr, src, dst, stride, t, stream); + } + + /// Whether `allGatherV` of `bytes`-byte strides moves only the lengths here (callers agree first: `varAgreed`). + pub fn varFits(c: Collective, bytes: usize) bool { + const f = c.vtable.var_fits orelse return false; + return f(c.ptr, bytes); + } + + /// Every rank's transport takes device lengths: one agreement a communicator, called alike outside any graph. + pub fn varAgreed(c: Collective) Error!bool { + const f = c.vtable.var_agreed orelse return false; + return f(c.ptr); + } + + pub fn exchange(c: Collective, src: DevicePtr, dst: DevicePtr, count: usize, t: DType, peer: u32, stream: Stream) Error!void { + return c.vtable.exchange(c.ptr, src, dst, count, t, peer, stream); + } + + pub fn send(c: Collective, buf: DevicePtr, count: usize, t: DType, peer: u32, stream: Stream) Error!void { + return c.vtable.send(c.ptr, buf, count, t, peer, stream); + } + + pub fn recv(c: Collective, buf: DevicePtr, count: usize, t: DType, peer: u32, stream: Stream) Error!void { + return c.vtable.recv(c.ptr, buf, count, t, peer, stream); + } + + pub fn barrier(c: Collective) Error!void { + return c.vtable.barrier(c.ptr); + } + + pub fn check(c: Collective) Error!void { + return c.vtable.check(c.ptr); + } + + pub fn abort(c: Collective) void { + c.vtable.abort(c.ptr); + } +}; + +/// Refuses a peer that is this rank or out of range. +pub fn checkPeer(rank: u32, world: u32, peer: u32) Error!void { + if (peer == rank or peer >= world) return error.Invalid; +} + +test "dtype sizes" { + try std.testing.expectEqual(@as(usize, 2), DType.bf16.size()); + try std.testing.expectEqual(@as(usize, 8), DType.i64.size()); +} diff --git a/zig/src/fabric/tp/config.zig b/zig/src/fabric/tp/config.zig new file mode 100644 index 0000000000..6f96091c1c --- /dev/null +++ b/zig/src/fabric/tp/config.zig @@ -0,0 +1,288 @@ +//! A rank's TP settings from the environment: rank, master, port, backend, fail-fast knobs. +const std = @import("std"); +const log = @import("log.zig"); +const graph_cache = @import("cuda").graph_cache; + +pub const Backend = enum { + /// NCCL for everything (the default). + nccl, + /// Exchanges of at most `mailbox_max_bytes` a rank through the one-shot mailbox kernel, NCCL above that. + mailbox, + /// The same split between two hosts: the small exchanges over RoCE (doorbell + RDMA proxy), NCCL above. + roce, +}; + +/// What `roce` does when its setup or load-time probe fails on any rank (TF_TP_ROCE_FALLBACK). +pub const Fallback = enum { nccl, @"error" }; + +pub const Error = error{BadSetting}; + +/// RoCE's per-write cap (roce_guard.max_write): on the roce backend a slot past it fails boot, never clamped. +pub const max_rdma_write: usize = @import("roce_guard.zig").max_write; + +/// TF_TP_PLAN_PIN: the CPU the plan threads pin to at their first plan (auto: the highest cpu_capacity). +pub const PlanPin = union(enum) { auto, cpu: u32 }; + +pub const Config = struct { + world: u32 = 2, + rank: u32 = 0, + /// Rank 0's address as the other ranks reach it. + master: []const u8 = "localhost", + /// The rendezvous port (TF_TP_PORT); the link listens on `link_port`. + port: u16 = 29500, + /// Rank 0 listens here: TF_TP_PLAN_PORT, else `port` + 7. + link_port: u16 = 29507, + /// How long rank 0 waits for the others and they keep retrying the connect (TF_TP_PLAN_TIMEOUT). + connect_timeout_ns: u64 = 300 * std.time.ns_per_s, + /// CUDA device ordinal this rank uses. + device: u32 = 0, + backend: Backend = .nccl, + mailbox_max_bytes: usize = 256 * 1024, + /// TF_TP_ROCE_CHUNKED_KB: `allGatherV` strides up to this go RoCE in ops of at most a slot (0: off). + roce_chunked_bytes: usize = 0, + /// A mailbox wait for the peer gives up after this long and poisons the transport (fail-stop). + mailbox_timeout_ns: u64 = 120 * std.time.ns_per_s, + failfast: bool = true, + /// After a failure: time for the HTTP threads to answer before exit 70. + grace_ns: u64 = std.time.ns_per_s, + /// Rank 1's memory report period on the fate channel (also how fast it notices rank 0 hanging up). + report_ns: u64 = std.time.ns_per_s / 2, + nccl_lib: []const u8 = "libnccl.so.2", + /// `name[:gid],...` RDMA devices for `roce` (null: active RoCE v2 ports on an IPv4 /24 the peer has, roce_pick). + roce_hca: ?[]const u8 = null, + /// Stripe over at most this many HCAs (auto detection only). + roce_hcas: u8 = 2, + roce_tc: u8 = 0, + /// TF_TP_ROCE_FAST as the RoCE kernel's `opts` word: bit 0 on, bits 8-15 pollers, bits 16-31 stagger ns (local). + roce_fast: u32 = 0, + /// TF_TP_ROCE_BLOCKS: the largest RoCE grid, a power of two 1..16; launch shape only, the bytes are the same. + roce_blocks: u32 = 16, + /// Pin the proxy thread (it busy-spins one core between exchanges). + roce_cpu: ?u32 = null, + /// `shm` runs the RoCE protocol on one host without RDMA (tests); `verbs` is the real wire. + roce_wire: enum { verbs, shm } = .verbs, + roce_fallback: Fallback = .nccl, + /// TF_TP_PLAN_LINK=rdma: the follower polls the plan socket this long before blocking (TF_TP_PLAN_SPIN_US). + plan_spin_ns: u64 = 0, + plan_pin: ?PlanPin = null, + /// The keyed graph cache settings a family builds with, set before `Session.open` (null: the family has none). + graph_cache: ?graph_cache.Settings = null, + /// TF_GRAPH_CACHE as read (`Cache.init` applies the same value); only its effect on `graph_cache` is compared. + graph_cache_env: ?[]const u8 = null, + + /// Reads every setting from `get` (the environment in `fromEnv`); unset names keep the defaults. + pub fn parse(get: *const fn ([]const u8) ?[]const u8) Error!Config { + var c: Config = .{}; + c.world = try int(u32, get, "TF_TP_WORLD", c.world, 1, 64); + c.rank = try int(u32, get, "TF_TP_RANK", c.rank, 0, c.world - 1); + if (get("TF_TP_MASTER")) |m| c.master = m; + c.port = try int(u16, get, "TF_TP_PORT", c.port, 1, 65535 - 8); + c.link_port = try int(u16, get, "TF_TP_PLAN_PORT", c.port + 7, 1, 65535); + c.connect_timeout_ns = try seconds(get, "TF_TP_PLAN_TIMEOUT", 300, 0.1, 86400); + c.device = try int(u32, get, "TF_TP_DEVICE", c.device, 0, 63); + const backend = "TF_COMM_BACKEND"; + if (get(backend)) |b| c.backend = std.meta.stringToEnum(Backend, std.mem.trim(u8, b, " ")) orelse { + log.warn("{s}={s}: expected nccl, mailbox or roce", .{ backend, b }); + return error.BadSetting; + }; + c.mailbox_max_bytes = 1024 * try int(usize, get, "TF_TP_MAILBOX_MAX_KB", 256, 4, 16 * 1024); + if (c.backend == .roce and c.mailbox_max_bytes > max_rdma_write) { + log.warn("TF_TP_MAILBOX_MAX_KB={d}: a RoCE write is capped at {d} KiB; refusing to start", .{ c.mailbox_max_bytes / 1024, max_rdma_write / 1024 }); + return error.BadSetting; + } + c.roce_chunked_bytes = 1024 * try int(usize, get, "TF_TP_ROCE_CHUNKED_KB", 0, 0, 64 * 1024); + c.mailbox_timeout_ns = try seconds(get, "TF_TP_MAILBOX_TIMEOUT_S", 120, 0.001, 86400); + if (get("TF_TP_FAILFAST")) |v| c.failfast = !(std.mem.eql(u8, v, "0") or std.mem.eql(u8, v, "off")); + c.grace_ns = try seconds(get, "TF_TP_FAILFAST_GRACE_S", 1.0, 0, 60); + c.report_ns = try seconds(get, "TF_TP_PEER_MEM_S", 0.5, 0.01, 60); + if (get("TF_NCCL_LIB")) |p| c.nccl_lib = p; + c.roce_hca = get("TF_TP_ROCE_HCA"); + c.roce_hcas = try int(u8, get, "TF_TP_ROCE_HCAS", 2, 1, 2); + c.roce_tc = try int(u8, get, "TF_TP_ROCE_TC", try int(u8, get, "NCCL_IB_TC", 0, 0, 255), 0, 255); + if (try int(u8, get, "TF_TP_ROCE_FAST", 0, 0, 1) == 1) { + const pollers = try int(u32, get, "TF_TP_ROCE_POLLERS", 1, 1, 8); + const stagger = try int(u32, get, "TF_TP_ROCE_STAGGER_NS", 150, 0, 65535); + c.roce_fast = 1 | (pollers << 8) | (stagger << 16); + } + c.roce_blocks = try int(u32, get, "TF_TP_ROCE_BLOCKS", 16, 1, 16); + if (c.roce_blocks & (c.roce_blocks - 1) != 0) { + log.warn("TF_TP_ROCE_BLOCKS={d}: expected a power of two", .{c.roce_blocks}); + return error.BadSetting; + } + if (get("TF_TP_ROCE_CPU")) |_| c.roce_cpu = try int(u32, get, "TF_TP_ROCE_CPU", 0, 0, 4095); + if (get("TF_TP_ROCE_WIRE")) |w| c.roce_wire = std.meta.stringToEnum(@TypeOf(c.roce_wire), w) orelse { + log.warn("TF_TP_ROCE_WIRE={s}: expected verbs or shm", .{w}); + return error.BadSetting; + }; + const fallback = "TF_TP_ROCE_FALLBACK"; + if (get(fallback)) |f| c.roce_fallback = std.meta.stringToEnum(Fallback, f) orelse { + log.warn("{s}={s}: expected nccl or error", .{ fallback, f }); + return error.BadSetting; + }; + if (get("TF_TP_PLAN_LINK")) |l| { + const mode = std.mem.trim(u8, l, " "); + if (std.mem.eql(u8, mode, "rdma")) { + c.plan_spin_ns = 1000 * try int(u64, get, "TF_TP_PLAN_SPIN_US", 5000, 0, 10_000_000); + } else if (!std.mem.eql(u8, mode, "tcp") and !std.mem.eql(u8, mode, "nccl")) { + log.warn("TF_TP_PLAN_LINK={s}: expected nccl, tcp or rdma", .{l}); + return error.BadSetting; + } + } + if (get("TF_TP_PLAN_PIN")) |raw| { + const v = std.mem.trim(u8, raw, " "); + if (std.mem.eql(u8, v, "auto")) { + c.plan_pin = .auto; + } else if (!std.mem.eql(u8, v, "-1") and !std.mem.eql(u8, v, "off")) { + c.plan_pin = .{ .cpu = try int(u32, get, "TF_TP_PLAN_PIN", 0, 0, 4095) }; + } + } + c.graph_cache_env = get("TF_GRAPH_CACHE"); + return c; + } + + pub fn fromEnv() Error!Config { + return parse(&envGet); + } + + /// The settings both ranks must share (everything but rank, device and timeouts), as bytes to compare. + pub fn digest(c: Config) [32]u8 { + var h = std.crypto.hash.sha2.Sha256.init(.{}); + h.update(std.mem.asBytes(&c.world)); + h.update(std.mem.asBytes(&c.backend)); + h.update(std.mem.asBytes(&c.mailbox_max_bytes)); + h.update(std.mem.asBytes(&c.roce_chunked_bytes)); + h.update(std.mem.asBytes(&c.failfast)); + h.update(std.mem.asBytes(&c.roce_wire)); + h.update(std.mem.asBytes(&c.roce_fallback)); + if (c.graph_cache) |g| h.update(&g.effective(c.graph_cache_env).digest()); + var out: [32]u8 = undefined; + h.final(&out); + return out; + } +}; + +fn envGet(name: []const u8) ?[]const u8 { + var buf: [64]u8 = undefined; + if (name.len >= buf.len) return null; + @memcpy(buf[0..name.len], name); + buf[name.len] = 0; + const v = std.c.getenv(buf[0..name.len :0]) orelse return null; + const s = std.mem.span(v); + return if (s.len == 0) null else s; +} + +fn int(comptime T: type, get: *const fn ([]const u8) ?[]const u8, name: []const u8, default: T, lo: T, hi: T) Error!T { + const raw = get(name) orelse return default; + const v = std.fmt.parseInt(T, std.mem.trim(u8, raw, " "), 10) catch { + log.warn("{s}={s}: expected an integer", .{ name, raw }); + return error.BadSetting; + }; + if (v < lo or v > hi) { + log.warn("{s}={d}: expected {d}..{d}", .{ name, v, lo, hi }); + return error.BadSetting; + } + return v; +} + +fn seconds(get: *const fn ([]const u8) ?[]const u8, name: []const u8, default: f64, lo: f64, hi: f64) Error!u64 { + const v = if (get(name)) |raw| std.fmt.parseFloat(f64, std.mem.trim(u8, raw, " ")) catch { + log.warn("{s}={s}: expected seconds", .{ name, raw }); + return error.BadSetting; + } else default; + if (!(v >= lo and v <= hi)) { + log.warn("{s}={d}: expected {d}..{d} s", .{ name, v, lo, hi }); + return error.BadSetting; + } + return @intFromFloat(v * std.time.ns_per_s); +} + +const Fake = struct { + var pairs: []const [2][]const u8 = &.{}; + fn get(name: []const u8) ?[]const u8 { + for (pairs) |p| if (std.mem.eql(u8, p[0], name)) return p[1]; + return null; + } +}; + +test "defaults: the plan link 7 ports past the rendezvous, NCCL, fail-fast on" { + Fake.pairs = &.{ .{ "TF_TP_RANK", "1" }, .{ "TF_TP_PORT", "29600" } }; + const c = try Config.parse(&Fake.get); + try std.testing.expectEqual(@as(u32, 1), c.rank); + try std.testing.expectEqual(@as(u16, 29607), c.link_port); + try std.testing.expectEqual(Backend.nccl, c.backend); + try std.testing.expectEqual(std.time.ns_per_s, c.grace_ns); + try std.testing.expectEqual(std.time.ns_per_s / 2, c.report_ns); +} + +test "bad settings are refused and the digest ignores the rank" { + Fake.pairs = &.{.{ "TF_TP_RANK", "2" }}; + try std.testing.expectError(error.BadSetting, Config.parse(&Fake.get)); + Fake.pairs = &.{.{ "TF_COMM_BACKEND", "rdma" }}; + try std.testing.expectError(error.BadSetting, Config.parse(&Fake.get)); + Fake.pairs = &.{ .{ "TF_COMM_BACKEND", "roce" }, .{ "TF_TP_ROCE_HCA", "rocep1s0f1,roceP2p1s0f1" }, .{ "NCCL_IB_TC", "106" } }; + const r = try Config.parse(&Fake.get); + try std.testing.expectEqual(Backend.roce, r.backend); + try std.testing.expectEqual(@as(u8, 106), r.roce_tc); + try std.testing.expectEqualStrings("rocep1s0f1,roceP2p1s0f1", r.roce_hca.?); + Fake.pairs = &.{ .{ "TF_COMM_BACKEND", "mailbox" }, .{ "TF_TP_MAILBOX_MAX_KB", "64" }, .{ "TF_TP_FAILFAST_GRACE_S", "0.25" } }; + const a = try Config.parse(&Fake.get); + try std.testing.expectEqual(@as(usize, 64 * 1024), a.mailbox_max_bytes); + try std.testing.expectEqual(std.time.ns_per_s / 4, a.grace_ns); + var b = a; + b.rank = 1; + try std.testing.expectEqualSlices(u8, &a.digest(), &b.digest()); + b.mailbox_max_bytes = 1024; + try std.testing.expect(!std.mem.eql(u8, &a.digest(), &b.digest())); +} + +test "the plan link's spin and pin, the RoCE grid cap" { + Fake.pairs = &.{ .{ "TF_COMM_BACKEND", "roce" }, .{ "TF_TP_MAILBOX_MAX_KB", "1024" }, .{ "TF_TP_ROCE_FALLBACK", "error" }, .{ "TF_TP_PLAN_LINK", "rdma" }, .{ "TF_TP_PLAN_PIN", "auto" } }; + const p = try Config.parse(&Fake.get); + try std.testing.expectEqual(Backend.roce, p.backend); + try std.testing.expectEqual(@as(usize, 1024 * 1024), p.mailbox_max_bytes); + try std.testing.expectEqual(Fallback.@"error", p.roce_fallback); + try std.testing.expectEqual(@as(u64, 5000 * 1000), p.plan_spin_ns); + try std.testing.expectEqual(PlanPin.auto, p.plan_pin.?); + Fake.pairs = &.{ .{ "TF_TP_PLAN_LINK", "tcp" }, .{ "TF_TP_PLAN_PIN", "7" } }; // tcp blocks at once; an explicit CPU + const o = try Config.parse(&Fake.get); + try std.testing.expectEqual(@as(u64, 0), o.plan_spin_ns); + try std.testing.expectEqual(PlanPin{ .cpu = 7 }, o.plan_pin.?); + Fake.pairs = &.{ .{ "TF_TP_PLAN_LINK", "rdma" }, .{ "TF_TP_PLAN_SPIN_US", "200" }, .{ "TF_TP_PLAN_PIN", "-1" } }; + const s = try Config.parse(&Fake.get); + try std.testing.expectEqual(@as(u64, 200 * 1000), s.plan_spin_ns); + try std.testing.expect(s.plan_pin == null); + Fake.pairs = &.{.{ "TF_TP_PLAN_LINK", "udp" }}; + try std.testing.expectError(error.BadSetting, Config.parse(&Fake.get)); + Fake.pairs = &.{}; // the RoCE grid cap: 16 unset, powers of two only + try std.testing.expectEqual(@as(u32, 16), (try Config.parse(&Fake.get)).roce_blocks); + Fake.pairs = &.{.{ "TF_TP_ROCE_BLOCKS", "4" }}; + try std.testing.expectEqual(@as(u32, 4), (try Config.parse(&Fake.get)).roce_blocks); + Fake.pairs = &.{.{ "TF_TP_ROCE_BLOCKS", "12" }}; + try std.testing.expectError(error.BadSetting, Config.parse(&Fake.get)); + Fake.pairs = &.{}; +} + +test "the digest holds the graph cache's effective settings: 0, off and OFF agree, on and off differ" { + Fake.pairs = &.{.{ "TF_GRAPH_CACHE", "0" }}; + var zero = try Config.parse(&Fake.get); + Fake.pairs = &.{.{ "TF_GRAPH_CACHE", "OFF" }}; + var upper = try Config.parse(&Fake.get); + Fake.pairs = &.{.{ "TF_GRAPH_CACHE", "1" }}; + var one = try Config.parse(&Fake.get); + Fake.pairs = &.{}; + var unset = try Config.parse(&Fake.get); + try std.testing.expectEqualSlices(u8, &zero.digest(), &unset.digest()); // no cache installed: not compared + for ([_]*Config{ &zero, &upper, &one, &unset }) |c| c.graph_cache = .{}; + var max0 = unset; + max0.graph_cache = .{ .max = 0 }; + try std.testing.expectEqualSlices(u8, &zero.digest(), &upper.digest()); + try std.testing.expectEqualSlices(u8, &zero.digest(), &max0.digest()); + try std.testing.expectEqualSlices(u8, &one.digest(), &unset.digest()); + try std.testing.expect(!std.mem.eql(u8, &zero.digest(), &unset.digest())); + var family = unset; + family.graph_cache = .{ .max = 8, .hold = true }; + try std.testing.expect(!std.mem.eql(u8, &family.digest(), &unset.digest())); + var family_off = upper; + family_off.graph_cache = family.graph_cache; + try std.testing.expectEqualSlices(u8, &zero.digest(), &family_off.digest()); +} diff --git a/zig/src/fabric/tp/fate.zig b/zig/src/fabric/tp/fate.zig new file mode 100644 index 0000000000..5084c4648b --- /dev/null +++ b/zig/src/fabric/tp/fate.zig @@ -0,0 +1,261 @@ +//! Fail fast on every rank: a failure or a vanished rank ends all ranks with exit 70 about a second later. +const std = @import("std"); +const log = @import("log.zig"); +const sock = @import("sock.zig"); +const Config = @import("config.zig").Config; +const posix = std.posix; +/// Linux POLLRDHUP: the peer shut its side down (std has no name for it). +const pollrdhup: i16 = 0x2000; + +pub const exit_code: u8 = 70; +const magic: u32 = 0x46463431; +const kind_mem: u32 = 1; +const kind_fail: u32 = 2; +/// rank 0's clean stop: the hang-up that follows is no failure (sent before rank 0 closes its sockets) +const kind_stop: u32 = 3; +const Frame = extern struct { magic: u32, kind: u32, value: i64 }; + +pub const max_ranks = 64; +pub const max_hooks = 8; + +/// A callback with its context (an abort of a collective, rank 0's request failing); `reason` lives until exit. +pub const Hook = struct { ctx: *anyopaque, call: *const fn (ctx: *anyopaque, reason: []const u8) void }; + +pub const Fate = struct { + rank: u32, + world: u32, + fds: [max_ranks]sock.Fd, + grace_ns: u64, + report_ns: u64, + /// This rank's usable memory in bytes for the MEM report (default: MemAvailable). + usable: *const fn () u64 = memAvailable, + /// The process exit; tests replace it (it may return there). + exit_fn: *const fn (u8) void = exitNow, + hooks: [max_hooks]Hook = undefined, + n_hooks: std.atomic.Value(u32) = .init(0), + dead: std.atomic.Value(bool) = .init(false), + closing: std.atomic.Value(bool) = .init(false), + /// Set once the exit was asked for (tests read it; the process is gone in serving). + exited: std.atomic.Value(bool) = .init(false), + reason_buf: [512]u8 = undefined, + reason_len: usize = 0, + send_lock: std.atomic.Value(bool) = .init(false), + /// Rank 0: each rank's last report (bytes) and its monotonic time. + peer_mem: [max_ranks]std.atomic.Value(u64) = @splat(.init(0)), + peer_mem_at: [max_ranks]std.atomic.Value(u64) = @splat(.init(0)), + wake: [2]sock.Fd = .{ -1, -1 }, + thread: ?std.Thread = null, + + /// The channel over the bootstrap's sockets (`Bootstrap.release`); `start` runs its thread. + pub fn init(cfg: Config, fds: [max_ranks]sock.Fd) Fate { + return .{ .rank = cfg.rank, .world = cfg.world, .fds = fds, .grace_ns = cfg.grace_ns, .report_ns = cfg.report_ns }; + } + + pub fn start(f: *Fate) !void { + var p: [2]c_int = undefined; + if (std.c.pipe2(&p, .{ .CLOEXEC = true }) != 0) return error.SocketFailed; + f.wake = p; + f.thread = if (f.rank == 0) try std.Thread.spawn(.{}, watch0, .{f}) else try std.Thread.spawn(.{}, watchK, .{f}); + } + + /// Runs `hook` when the ranks go down; collectives register their abort here. + pub fn onFail(f: *Fate, hook: Hook) void { + const i = f.n_hooks.load(.acquire); + std.debug.assert(i < max_hooks); + f.hooks[i] = hook; + f.n_hooks.store(i + 1, .release); + } + + /// This rank failed (`where`: what it was doing): every rank goes down. Returns only in tests. + pub fn fatal(f: *Fate, where: []const u8, err: anyerror) void { + var buf: [256]u8 = undefined; + const msg = std.fmt.bufPrint(&buf, "rank {d} failed in {s}: {t}", .{ f.rank, where, err }) catch "rank failed"; + f.die(msg, true); + } + + /// This rank failed while booting: every rank goes down, and the thread is joined before the caller frees this. + pub fn failBoot(f: *Fate, where: []const u8, err: anyerror) void { + f.fatal(where, err); + f.close(); + } + + /// A normal stop is coming (rank 0 before it tells the others; the others when told): later hang-ups are fine. + pub fn expectStop(f: *Fate) void { + f.closing.store(true, .release); + } + + /// Rank 0's clean stop of every rank: a STOP frame to each ahead of its close, so followers end quietly. + pub fn announceStop(f: *Fate) void { + if (f.closing.swap(true, .acq_rel)) return; + if (f.rank != 0) return; + const h: Frame = .{ .magic = magic, .kind = kind_stop, .value = 0 }; + f.lock(); + defer f.unlock(); + for (f.fds[1..f.world]) |fd| if (fd >= 0) { + _ = sock.trySend(fd, std.mem.asBytes(&h)); + }; + } + + /// A normal stop: the thread ends and the sockets close. + pub fn close(f: *Fate) void { + f.closing.store(true, .release); + if (f.wake[1] >= 0) _ = std.c.write(f.wake[1], "x", 1); + if (f.thread) |t| t.join(); + f.thread = null; + for (&f.fds) |*fd| if (fd.* >= 0) { + sock.close(fd.*); + fd.* = -1; + }; + for (f.wake) |fd| if (fd >= 0) sock.close(fd); + f.wake = .{ -1, -1 }; + } + + /// Rank 0: rank `r`'s last reported usable bytes and the age of the report, or null before its first. + pub fn peerMem(f: *const Fate, r: u32) ?struct { bytes: u64, age_ns: u64 } { + const at = f.peer_mem_at[r].load(.acquire); + if (at == 0) return null; + return .{ .bytes = f.peer_mem[r].load(.acquire), .age_ns = sock.nowNs() -| at }; + } + + pub fn reason(f: *const Fate) []const u8 { + return f.reason_buf[0..f.reason_len]; + } + + fn lock(f: *Fate) void { + while (f.send_lock.swap(true, .acquire)) std.atomic.spinLoopHint(); + } + + fn unlock(f: *Fate) void { + f.send_lock.store(false, .release); + } + + fn die(f: *Fate, why: []const u8, own: bool) void { + if (f.dead.swap(true, .acq_rel)) { + // the first caller exits the process; a second (the fate thread or a round thread) waits for it + while (!f.exited.load(.acquire)) sock.sleepNs(10 * std.time.ns_per_ms); + return; + } + const n = @min(why.len, f.reason_buf.len); + @memcpy(f.reason_buf[0..n], why[0..n]); + f.reason_len = n; + const r = f.reason(); + log.warn("[tensorfold] tp fail-fast: rank {d}: {s}; exiting ({d}) in {d} ms so every rank restarts", .{ f.rank, r, exit_code, f.grace_ns / std.time.ns_per_ms }); + // aborts first: kernels waiting on the dead peer end now; a hook that blocks cannot hold the exit + if (std.Thread.spawn(.{}, runHooks, .{ f, r })) |t| t.detach() else |_| runHooks(f, r); + if (!f.closing.load(.acquire)) { + const head: Frame = .{ .magic = magic, .kind = kind_fail, .value = @intCast(r.len) }; + f.lock(); + for (f.fds[0..f.world]) |fd| if (fd >= 0) { + if (own or f.rank == 0) { + _ = sock.trySend(fd, std.mem.asBytes(&head)); + _ = sock.trySend(fd, r); + } + sock.hangup(fd); + }; + f.unlock(); + } + sock.sleepNs(f.grace_ns); + f.exited.store(true, .release); + f.exit_fn(exit_code); + } + + fn runHooks(f: *Fate, r: []const u8) void { + const n = f.n_hooks.load(.acquire); + for (f.hooks[0..n]) |h| h.call(h.ctx, r); + } + + /// Reads one frame from `fd`: a MEM report, a FAIL (the ranks go down), or the end (the peer is gone). + fn readFrame(f: *Fate, peer: u32, fd: sock.Fd) bool { + var h: Frame = undefined; + const deadline = sock.nowNs() + std.time.ns_per_s; + sock.recvExact(fd, std.mem.asBytes(&h), deadline) catch { + if (f.closing.load(.acquire)) return false; + var buf: [128]u8 = undefined; + f.die(std.fmt.bufPrint(&buf, "rank {d} closed the fate channel (it failed, exited or was killed)", .{peer}) catch "peer gone", false); + return false; + }; + if (h.magic != magic) { + f.die("a frame that is not the fate channel's", false); + return false; + } + if (h.kind == kind_stop) { + // rank 0 stops every rank: its hang-up is next, and no failure + f.closing.store(true, .release); + return false; + } + if (h.kind == kind_mem) { + f.peer_mem[peer].store(@intCast(@max(h.value, 0)), .release); + f.peer_mem_at[peer].store(sock.nowNs(), .release); + return true; + } + var text: [512]u8 = undefined; + const len: usize = @intCast(std.math.clamp(h.value, 0, text.len)); + sock.recvExact(fd, text[0..len], deadline) catch {}; + f.die(if (len > 0) text[0..len] else "a peer failed", false); + return false; + } + + fn watch0(f: *Fate) void { + var p: [max_ranks + 1]std.c.pollfd = undefined; + var who: [max_ranks + 1]u32 = undefined; + var n: usize = 0; + for (f.fds[1..f.world], 1..) |fd, r| if (fd >= 0) { + p[n] = .{ .fd = fd, .events = posix.POLL.IN | pollrdhup, .revents = 0 }; + who[n] = @intCast(r); + n += 1; + }; + p[n] = .{ .fd = f.wake[0], .events = posix.POLL.IN, .revents = 0 }; + while (!f.dead.load(.acquire) and !f.closing.load(.acquire)) { + if (std.c.poll(&p, @intCast(n + 1), -1) <= 0) continue; + for (p[0..n], who[0..n]) |*q, r| if (q.revents != 0) { + if (!f.readFrame(r, q.fd)) return; + }; + } + } + + fn watchK(f: *Fate) void { + const fd = f.fds[0]; + var p = [_]std.c.pollfd{ + .{ .fd = fd, .events = posix.POLL.IN | pollrdhup, .revents = 0 }, + .{ .fd = f.wake[0], .events = posix.POLL.IN, .revents = 0 }, + }; + const ms: c_int = @intCast(@max(f.report_ns / std.time.ns_per_ms, 1)); + f.report(fd); + while (!f.dead.load(.acquire) and !f.closing.load(.acquire)) { + const ready = std.c.poll(&p, 2, ms); + if (ready == 0) { + f.report(fd); + continue; + } + if (ready > 0 and p[0].revents != 0 and !f.readFrame(0, fd)) return; + } + } + + /// One MEM frame, never blocking (a full buffer drops it: the next comes one period later). + fn report(f: *Fate, fd: sock.Fd) void { + const h: Frame = .{ .magic = magic, .kind = kind_mem, .value = @intCast(f.usable()) }; + f.lock(); + defer f.unlock(); + _ = sock.trySend(fd, std.mem.asBytes(&h)); + } +}; + +fn exitNow(code: u8) void { + std.c._exit(code); +} + +/// MemAvailable from /proc/meminfo in bytes (0 when unreadable). +pub fn memAvailable() u64 { + const fd = std.c.open("/proc/meminfo", .{ .ACCMODE = .RDONLY }); + if (fd < 0) return 0; + defer _ = std.c.close(fd); + var buf: [4096]u8 = undefined; + const n = std.c.read(fd, &buf, buf.len); + if (n <= 0) return 0; + const text = buf[0..@intCast(n)]; + const key = "MemAvailable:"; + const at = std.mem.indexOf(u8, text, key) orelse return 0; + var it = std.mem.tokenizeAny(u8, text[at + key.len ..], " \n"); + const kb = std.fmt.parseInt(u64, it.next() orelse return 0, 10) catch return 0; + return kb * 1024; +} diff --git a/zig/src/fabric/tp/fate_test.zig b/zig/src/fabric/tp/fate_test.zig new file mode 100644 index 0000000000..728f4bfdf3 --- /dev/null +++ b/zig/src/fabric/tp/fate_test.zig @@ -0,0 +1,241 @@ +//! The fate channel between two ranks over real TCP: a failure or a vanished rank ends both, a stop ends neither. +const std = @import("std"); +const sock = @import("sock.zig"); +const fate = @import("fate.zig"); +const bootstrap = @import("bootstrap.zig"); +const Config = @import("config.zig").Config; +const Fate = fate.Fate; + +var exits: std.atomic.Value(u32) = .init(0); +var hooks: std.atomic.Value(u32) = .init(0); + +fn recordExit(code: u8) void { + std.debug.assert(code == fate.exit_code); + _ = exits.fetchAdd(1, .acq_rel); +} + +fn hook(_: *anyopaque, reason: []const u8) void { + std.debug.assert(reason.len > 0); + _ = hooks.fetchAdd(1, .acq_rel); +} + +const grace = 50 * std.time.ns_per_ms; + +/// Two connected ranks' fate channels (rank 0 first), started, with recorded exits and hooks. +fn pair(fates: *[2]Fate) !void { + return pairAt(.{ &fates[0], &fates[1] }); +} + +fn pairAt(fates: [2]*Fate) !void { + const l = try bootstrap.testListener(); + defer sock.close(l.fd); + const deadline = sock.nowNs() + 5 * std.time.ns_per_s; + const a = try sock.connect("localhost", l.port, deadline); + const b = try sock.accept(l.fd, deadline); + var fds0: [fate.max_ranks]sock.Fd = @splat(-1); + var fds1: [fate.max_ranks]sock.Fd = @splat(-1); + fds0[1] = b; + fds1[0] = a; + for (fates, [2][fate.max_ranks]sock.Fd{ fds0, fds1 }, 0..) |f, fds, r| { + f.* = Fate.init(.{ .world = 2, .rank = @intCast(r), .grace_ns = grace, .report_ns = 20 * std.time.ns_per_ms }, fds); + f.exit_fn = recordExit; + f.usable = fakeUsable; + f.onFail(.{ .ctx = f, .call = hook }); + } + for (fates) |f| try f.start(); +} + +fn fakeUsable() u64 { + return 123 << 20; +} + +fn waitExits(want: u32) !u64 { + const t0 = sock.nowNs(); + while (exits.load(.acquire) < want) { + if (sock.nowNs() - t0 > 5 * std.time.ns_per_s) return error.Timeout; + sock.sleepNs(std.time.ns_per_ms); + } + return sock.nowNs() - t0; +} + +fn reset() void { + exits.store(0, .release); + hooks.store(0, .release); +} + +test "rank 1's failure ends both ranks with its reason" { + reset(); + var fates: [2]Fate = undefined; + try pair(&fates); + defer for (&fates) |*f| f.close(); + sock.sleepNs(60 * std.time.ns_per_ms); + const mem = fates[0].peerMem(1) orelse return error.NoReport; + try std.testing.expectEqual(@as(u64, 123 << 20), mem.bytes); + fates[1].fatal("prefill", error.OutOfMemory); + const took = try waitExits(2); + try std.testing.expect(took < grace + 500 * std.time.ns_per_ms); + try std.testing.expectEqual(@as(u32, 2), hooks.load(.acquire)); + try std.testing.expect(std.mem.indexOf(u8, fates[0].reason(), "rank 1 failed in prefill: OutOfMemory") != null); +} + +test "rank 0's failure ends rank 1" { + reset(); + var fates: [2]Fate = undefined; + try pair(&fates); + defer for (&fates) |*f| f.close(); + fates[0].fatal("window", error.Unexpected); + _ = try waitExits(2); + try std.testing.expect(std.mem.indexOf(u8, fates[1].reason(), "rank 0 failed in window") != null); +} + +test "a rank that vanishes without a word ends the other" { + reset(); + var fates: [2]Fate = undefined; + try pair(&fates); + defer fates[0].close(); + // rank 1 killed: its socket closes with no FAIL frame (its own fate thread ends quietly first) + fates[1].expectStop(); + fates[1].close(); + _ = try waitExits(1); + try std.testing.expect(std.mem.indexOf(u8, fates[0].reason(), "rank 1 closed the fate channel") != null); +} + +test "a normal stop ends nobody" { + reset(); + var fates: [2]Fate = undefined; + try pair(&fates); + fates[0].expectStop(); + fates[1].expectStop(); + fates[1].close(); + fates[0].close(); + sock.sleepNs(2 * grace); + try std.testing.expectEqual(@as(u32, 0), exits.load(.acquire)); +} + +test "a rank failing at boot ends both with its reason, and its channel is down before its owner frees it" { + // a follower's boot failed after its session came up; its Fate is freed while the fate thread could still read it + reset(); + // rank 1's Fate where a Model keeps it: on the heap, its pages unmapped when freed (a stale read faults) + const pa = std.heap.page_allocator; + var f0: Fate = undefined; + const f1 = try pa.create(Fate); + try pairAt(.{ &f0, f1 }); + defer f0.close(); + sock.sleepNs(60 * std.time.ns_per_ms); + f1.failBoot("boot", error.RopeTooShort); + try std.testing.expect(f1.thread == null); + pa.destroy(f1); + _ = try waitExits(2); + // several report periods with rank 1's Fate gone: its thread would have reported from freed memory + sock.sleepNs(5 * 20 * std.time.ns_per_ms); + try std.testing.expect(std.mem.indexOf(u8, f0.reason(), "rank 1 failed in boot: RopeTooShort") != null); +} + +test "rank 0's clean stop ends every rank with no fail-fast exit, whatever order the ranks close in" { + // rank 0 ended its run and closed; the follower, still tearing down, must not see the hang-up as a failure + reset(); + var fates: [2]Fate = undefined; + try pair(&fates); + sock.sleepNs(60 * std.time.ns_per_ms); + fates[0].announceStop(); + fates[0].close(); // rank 0 gone before rank 1 even started its own stop + sock.sleepNs(2 * grace + 100 * std.time.ns_per_ms); + fates[1].close(); + try std.testing.expectEqual(@as(u32, 0), exits.load(.acquire)); + try std.testing.expectEqual(@as(u32, 0), hooks.load(.acquire)); +} + +// -- a server's drain on two ranks: the plan link and the fate channel in the order a family's stop uses them -- + +const planlink = @import("planlink.zig"); + +/// Plan words a family's rank 0 sends: a round's operation, and a stop word that ends a follower's loop. +const op_round: i64 = 1; +const op_stop: i64 = 4; + +fn linkPair(links: *[2]planlink.PlanLink) !void { + const l = try bootstrap.testListener(); + defer sock.close(l.fd); + const deadline = sock.nowNs() + 5 * std.time.ns_per_s; + const a = try sock.connect("localhost", l.port, deadline); + const b = try sock.accept(l.fd, deadline); + var f0: [planlink.max_ranks]sock.Fd = @splat(-1); + var f1: [planlink.max_ranks]sock.Fd = @splat(-1); + f0[1] = b; + f1[0] = a; + links[0] = planlink.PlanLink.init(0, 2, f0); + links[1] = planlink.PlanLink.init(1, 2, f1); +} + +/// Rank 1's loop: each round as it comes, until the stop word, or until the link ends. +const Follower = struct { + link: *planlink.PlanLink, + rounds: u32 = 0, + ended: enum { running, stopped, peer_closed, failed } = .running, + + fn run(f: *Follower) void { + var buf: [16]i64 = undefined; + while (true) { + const msg = f.link.recv(&buf) catch |e| { + f.ended = if (e == error.PeerClosed) .peer_closed else .failed; + return; + }; + if (msg.len == 0) continue; + if (msg[0] == op_stop) { + f.ended = .stopped; + return; + } + if (msg[0] == op_round) f.rounds += 1; + } + } +}; + +test "a drain on rank 0, then its clean stop at a round boundary: rank 1 ends its loop there and nobody exits 70" { + reset(); + var fates: [2]Fate = undefined; + try pair(&fates); + var links: [2]planlink.PlanLink = undefined; + try linkPair(&links); + var follower: Follower = .{ .link = &links[1] }; + const t = try std.Thread.spawn(.{}, Follower.run, .{&follower}); + // serving, then draining (rank 0 refuses new work, rounds go on; the fate channel is untouched), past a grace + var sent: u32 = 0; + for (0..30) |_| { + try links[0].send(&.{ op_round, sent }); + sent += 1; + sock.sleepNs(5 * std.time.ns_per_ms); + } + try std.testing.expect(!fates[0].closing.load(.acquire)); + // the stop's order: the stop word first, so the follower ends its loop before the fate STOP and the hang-up + try links[0].send(&.{op_stop}); + fates[0].announceStop(); + links[0].close(); + fates[0].close(); + t.join(); + // rank 1's loop returned: its model closes and the process exits 0 + fates[1].close(); + links[1].close(); + sock.sleepNs(2 * grace + 100 * std.time.ns_per_ms); + try std.testing.expectEqual(.stopped, follower.ended); + try std.testing.expectEqual(sent, follower.rounds); + try std.testing.expectEqual(@as(u32, 0), exits.load(.acquire)); + try std.testing.expectEqual(@as(u32, 0), hooks.load(.acquire)); +} + +test "a drain on rank 0 does not disarm fail-fast: rank 1 killed mid-drain still ends rank 0 (exit 70)" { + reset(); + var fates: [2]Fate = undefined; + try pair(&fates); + defer fates[0].close(); + var links: [2]planlink.PlanLink = undefined; + try linkPair(&links); + defer links[0].close(); + for (0..5) |i| try links[0].send(&.{ op_round, @intCast(i) }); + // rank 0 is draining (no stop announced); rank 1 is killed: its sockets close with no word + links[1].close(); + fates[1].expectStop(); + fates[1].close(); + _ = try waitExits(1); + try std.testing.expect(std.mem.indexOf(u8, fates[0].reason(), "rank 1 closed the fate channel") != null); + try std.testing.expectEqual(@as(u32, 1), exits.load(.acquire)); +} diff --git a/zig/src/fabric/tp/gpu_bench.zig b/zig/src/fabric/tp/gpu_bench.zig new file mode 100644 index 0000000000..99831e6d77 --- /dev/null +++ b/zig/src/fabric/tp/gpu_bench.zig @@ -0,0 +1,82 @@ +//! Latency and bandwidth of each backend's collectives, eager and graphed; RESULT lines are JSON, busbw as nccl-tests. +const std = @import("std"); +const cuda = @import("cuda"); +const tp = @import("tp"); +const Ctx = @import("gpu_checks.zig").Ctx; +const Collective = tp.Collective; + +pub const Kind = enum { all_reduce, all_gather }; + +fn issue(c: *Ctx, comm: Collective, kind: Kind, bytes: usize) !void { + const count = bytes / 2; + switch (kind) { + .all_reduce => try comm.allReduce(c.in.ptr, c.out.ptr, count, .bf16, .sum, c.stream.handle), + .all_gather => try comm.allGather(c.in.ptr, c.out.ptr, count, .bf16, c.stream.handle), + } +} + +/// GPU microseconds a call, `iters` calls issued back to back on the stream. +fn eager(c: *Ctx, comm: Collective, kind: Kind, bytes: usize, iters: u32) !f64 { + var e0 = try cuda.Event.init(c.d, true); + defer e0.deinit(); + var e1 = try cuda.Event.init(c.d, true); + defer e1.deinit(); + for (0..10) |_| try issue(c, comm, kind, bytes); + try c.stream.synchronize(); + try comm.barrier(); + try e0.record(c.stream); + for (0..iters) |_| try issue(c, comm, kind, bytes); + try e1.record(c.stream); + try e1.synchronize(); + return @as(f64, try cuda.Event.elapsedMs(e0, e1)) * 1000.0 / @as(f64, @floatFromInt(iters)); +} + +/// GPU microseconds a call inside one graph of `per_graph` calls, replayed `replays` times. +fn graphed(c: *Ctx, comm: Collective, kind: Kind, bytes: usize, per_graph: u32, replays: u32) !f64 { + try cuda.graph.beginCapture(c.stream, .thread_local); + const err: ?tp.collective.Error = blk: { + for (0..per_graph) |_| issue(c, comm, kind, bytes) catch |e| break :blk e; + break :blk null; + }; + var g = try cuda.graph.endCapture(c.stream); + defer g.deinit(); + if (err) |e| return e; + var exec = try g.instantiate(); + defer exec.deinit(); + try exec.upload(c.stream); + try exec.launchOn(c.stream); + try c.stream.synchronize(); + try comm.barrier(); + var e0 = try cuda.Event.init(c.d, true); + defer e0.deinit(); + var e1 = try cuda.Event.init(c.d, true); + defer e1.deinit(); + try e0.record(c.stream); + for (0..replays) |_| try exec.launchOn(c.stream); + try e1.record(c.stream); + try e1.synchronize(); + try comm.check(); + return @as(f64, try cuda.Event.elapsedMs(e0, e1)) * 1000.0 / @as(f64, @floatFromInt(per_graph * replays)); +} + +/// Every power of two from `lo` to `hi` bytes a rank, both kinds, eager and graphed. +pub fn sweep(c: *Ctx, comm: Collective, label: []const u8, lo: usize, hi: usize) !void { + for ([_]Kind{ .all_reduce, .all_gather }) |kind| { + var bytes = lo; + while (bytes <= hi) : (bytes *= 2) { + const per_graph: u32 = if (bytes >= 1 << 20) 20 else 100; + const e = try eager(c, comm, kind, bytes, if (bytes >= 1 << 20) 50 else 200); + const g = try graphed(c, comm, kind, bytes, per_graph, 5); + const moved: f64 = @floatFromInt(if (kind == .all_gather) 2 * bytes else bytes); + const factor: f64 = if (kind == .all_gather) 0.5 else 1.0; + const busbw = moved / (g * 1e-6) * factor / 1e9; + if (c.rank == 0) std.debug.print("RESULT {{\"backend\":\"{s}\",\"op\":\"{t}\",\"bytes\":{d},\"eager_us\":{d:.2},\"graph_us\":{d:.2},\"busbw_GBps\":{d:.2}}}\n", .{ label, kind, bytes, e, g, busbw }); + } + } +} + +/// One decode window's exchanges: `n` all-gathers of one row's bf16 partials (`row_bytes` a rank) in one graph. +pub fn decodeWindow(c: *Ctx, comm: Collective, label: []const u8, n: u32, row_bytes: usize) !void { + const per = try graphed(c, comm, .all_gather, row_bytes, n, 20); + if (c.rank == 0) std.debug.print("RESULT {{\"backend\":\"{s}\",\"op\":\"decode_window\",\"exchanges\":{d},\"bytes\":{d},\"per_exchange_us\":{d:.2},\"window_us\":{d:.1}}}\n", .{ label, n, row_bytes, per, per * @as(f64, @floatFromInt(n)) }); +} diff --git a/zig/src/fabric/tp/gpu_checks.zig b/zig/src/fabric/tp/gpu_checks.zig new file mode 100644 index 0000000000..5dc4ed6d4c --- /dev/null +++ b/zig/src/fabric/tp/gpu_checks.zig @@ -0,0 +1,188 @@ +//! Every collective on real GPUs against the host reference, eager and replayed from a graph. +const std = @import("std"); +const cuda = @import("cuda"); +const tp = @import("tp"); +const Collective = tp.Collective; +const DType = tp.DType; +const Op = tp.Op; +const reference = tp.reference; + +pub const Ctx = struct { + d: *const cuda.Driver, + gpa: std.mem.Allocator, + stream: cuda.Stream, + rank: u32, + /// Device buffers sized for the largest case: input, output (world x), a spare. + in: cuda.DeviceBuffer, + out: cuda.DeviceBuffer, + /// Host scratch: this rank's input, the peer's, the expected and the downloaded output. + mine: []u8, + peer: []u8, + want: []u8, + got: []u8, + failures: u32 = 0, + passes: u32 = 0, + + pub fn init(d: *const cuda.Driver, gpa: std.mem.Allocator, rank: u32, max_bytes: usize) !Ctx { + return .{ + .d = d, + .gpa = gpa, + .stream = try cuda.Stream.init(d, true), + .rank = rank, + .in = try cuda.DeviceBuffer.alloc(d, 2 * max_bytes + 256), + .out = try cuda.DeviceBuffer.alloc(d, 2 * max_bytes + 256), + .mine = try gpa.alloc(u8, max_bytes), + .peer = try gpa.alloc(u8, max_bytes), + .want = try gpa.alloc(u8, 2 * max_bytes), + .got = try gpa.alloc(u8, 2 * max_bytes), + }; + } + + pub fn deinit(c: *Ctx) void { + c.stream.deinit(); + c.in.free(); + c.out.free(); + for ([_][]u8{ c.mine, c.peer, c.want, c.got }) |b| c.gpa.free(b); + } + + fn verdict(c: *Ctx, ok: bool, comptime fmt: []const u8, args: anytype) void { + if (ok) c.passes += 1 else c.failures += 1; + std.debug.print("{s} " ++ fmt ++ "\n", .{if (ok) "PASS" else "FAIL"} ++ args); + } + + /// Both ranks' inputs for case `seed`: this rank's into `mine`, the peer's into `peer` (n bytes each). + fn inputs(c: *Ctx, n: usize, t: DType, seed: u64) void { + reference.fill(c.mine[0..n], t, c.rank, seed); + reference.fill(c.peer[0..n], t, 1 - c.rank, seed); + } + + fn rankInput(c: *Ctx, r: u32, n: usize) []const u8 { + return if (r == c.rank) c.mine[0..n] else c.peer[0..n]; + } + + fn compare(c: *Ctx, comm: Collective, label: []const u8, what: []const u8, t: DType, n: usize, out_off: usize, want: []const u8) !void { + try c.stream.synchronize(); + comm.check() catch |e| { + c.verdict(false, "{s} {s} {t} {d} B: transport error {t}", .{ label, what, t, n, e }); + return; + }; + try c.out.download(out_off, c.got[0..want.len]); + const ok = std.mem.eql(u8, c.got[0..want.len], want); + var first: usize = 0; + if (!ok) first = std.mem.indexOfDiff(u8, c.got[0..want.len], want) orelse 0; + c.verdict(ok, "{s} {s} {t} {d} B a rank{s}", .{ label, what, t, n, if (ok) "" else " (first difference at a byte shown below)" }); + if (!ok) std.debug.print(" byte {d}: got {x} want {x}\n", .{ first, c.got[first], want[first] }); + } + + /// all-reduce (out of place, in place, misaligned by `skew` bytes), all-gather (out of place, in place), exchange. + pub fn collectives(c: *Ctx, comm: Collective, label: []const u8, n_bytes: usize, seed: u64) !void { + const reduce_types = [_]DType{ .f32, .bf16, .f16, .i32 }; + for (reduce_types, 0..) |t, ti| { + const n = n_bytes - n_bytes % t.size(); + const count = n / t.size(); + c.inputs(n, t, seed + ti); + reference.reduce(c.want[0..n], &.{ c.rankInput(0, n), c.rankInput(1, n) }, t, .sum); + try c.in.uploadAsync(0, c.mine[0..n], c.stream.handle); + try comm.allReduce(c.in.ptr, c.out.ptr, count, t, .sum, c.stream.handle); + try c.compare(comm, label, "all-reduce sum", t, n, 0, c.want[0..n]); + // in place, at an offset of one element (not 16-byte aligned for 2-byte types) + const skew = t.size(); + try c.out.uploadAsync(skew, c.mine[0..n], c.stream.handle); + try comm.allReduce(c.out.ptr + skew, c.out.ptr + skew, count, t, .sum, c.stream.handle); + try c.compare(comm, label, "all-reduce sum in place, skewed", t, n, skew, c.want[0..n]); + } + for ([_]Op{ .max, .min }) |op| { + const n = n_bytes - n_bytes % 4; + c.inputs(n, .f32, seed + 11); + reference.reduce(c.want[0..n], &.{ c.rankInput(0, n), c.rankInput(1, n) }, .f32, op); + try c.in.uploadAsync(0, c.mine[0..n], c.stream.handle); + try comm.allReduce(c.in.ptr, c.out.ptr, n / 4, .f32, op, c.stream.handle); + try c.compare(comm, label, if (op == .max) "all-reduce max" else "all-reduce min", .f32, n, 0, c.want[0..n]); + } + for ([_]DType{ .u8, .bf16, .f32 }, 0..) |t, ti| { + const n = n_bytes - n_bytes % t.size(); + c.inputs(n, t, seed + 20 + ti); + @memcpy(c.want[0..n], c.rankInput(0, n)); + @memcpy(c.want[n..][0..n], c.rankInput(1, n)); + try c.in.uploadAsync(0, c.mine[0..n], c.stream.handle); + try comm.allGather(c.in.ptr, c.out.ptr, n / t.size(), t, c.stream.handle); + try c.compare(comm, label, "all-gather", t, n, 0, c.want[0 .. 2 * n]); + try c.out.fill8(0, c.stream.handle); + try c.out.uploadAsync(c.rank * n, c.mine[0..n], c.stream.handle); + try comm.allGather(c.out.ptr + c.rank * n, c.out.ptr, n / t.size(), t, c.stream.handle); + try c.compare(comm, label, "all-gather in place", t, n, 0, c.want[0 .. 2 * n]); + try c.in.uploadAsync(0, c.mine[0..n], c.stream.handle); + try comm.exchange(c.in.ptr, c.out.ptr, n / t.size(), t, 1 - c.rank, c.stream.handle); + try c.compare(comm, label, "exchange", t, n, 0, c.peer[0..n]); + } + } + + /// send then recv one way, then the other way (NCCL behind every backend). + pub fn pointToPoint(c: *Ctx, comm: Collective, label: []const u8, n_bytes: usize, seed: u64) !void { + const n = n_bytes - n_bytes % 4; + c.inputs(n, .f32, seed); + try c.in.uploadAsync(0, c.mine[0..n], c.stream.handle); + for (0..2) |dir| { + const sender: u32 = @intCast(dir); + if (c.rank == sender) { + try comm.send(c.in.ptr, n / 4, .f32, 1 - c.rank, c.stream.handle); + try c.stream.synchronize(); + } else { + try comm.recv(c.out.ptr, n / 4, .f32, sender, c.stream.handle); + try c.compare(comm, label, "send/recv", .f32, n, 0, c.peer[0..n]); + } + } + } + + /// A captured chain replayed with fresh inputs, eager calls between replays (the mailbox epoch spans both). + pub fn graphs(c: *Ctx, comm: Collective, label: []const u8, n_bytes: usize, replays: u32) !void { + const n = n_bytes - n_bytes % 16; + const half = n / 2; // bf16 elements + try cuda.graph.beginCapture(c.stream, .thread_local); + // x1 = sum(x0) [bf16], x2 = gather(x1), x3 = sum(x2) [f32 view], x4 = exchange(x3) + const x0 = c.in.ptr; + const x1 = c.out.ptr; + const x2 = c.in.ptr + n; + const x3 = c.out.ptr + n; + const x4 = c.out.ptr + 3 * n; + const ops: ?tp.collective.Error = blk: { + comm.allReduce(x0, x1, half, .bf16, .sum, c.stream.handle) catch |e| break :blk e; + comm.allGather(x1, x2, half, .bf16, c.stream.handle) catch |e| break :blk e; + comm.allReduce(x2, x3, n / 2, .f32, .sum, c.stream.handle) catch |e| break :blk e; + comm.exchange(x3, x4, n / 2, .f32, 1 - c.rank, c.stream.handle) catch |e| break :blk e; + break :blk null; + }; + var g = try cuda.graph.endCapture(c.stream); + defer g.deinit(); + if (ops) |e| return e; + var exec = try g.instantiate(); + defer exec.deinit(); + const nodes = try g.nodeCount(); + const want1 = try c.gpa.alloc(u8, 2 * n); + defer c.gpa.free(want1); + const want3 = try c.gpa.alloc(u8, 2 * n); + defer c.gpa.free(want3); + for (0..replays) |k| { + c.inputs(n, .bf16, 1000 + k); + try c.in.uploadAsync(0, c.mine[0..n], c.stream.handle); + reference.reduce(want1[0..n], &.{ c.rankInput(0, n), c.rankInput(1, n) }, .bf16, .sum); + @memcpy(want1[n..][0..n], want1[0..n]); // the gather of equal sums: twice the same + reference.reduce(want3[0 .. 2 * n], &.{ want1[0 .. 2 * n], want1[0 .. 2 * n] }, .f32, .sum); + try exec.launchOn(c.stream); + try c.compare(comm, label, "graph replay: sum, gather, sum, exchange", .bf16, n, n, want3[0 .. 2 * n]); + try c.compare(comm, label, "graph replay: exchange of the sum", .f32, 2 * n, 3 * n, want3[0 .. 2 * n]); + // an eager call between replays + try c.collectivesQuick(comm, label, k); + } + std.debug.print(" ({s}: {d} graph nodes, {d} replays)\n", .{ label, nodes, replays }); + } + + fn collectivesQuick(c: *Ctx, comm: Collective, label: []const u8, k: usize) !void { + const n = 4096; + c.inputs(n, .bf16, 5000 + k); + reference.reduce(c.want[0..n], &.{ c.rankInput(0, n), c.rankInput(1, n) }, .bf16, .sum); + try c.in.uploadAsync(5 * n, c.mine[0..n], c.stream.handle); + try comm.allReduce(c.in.ptr + 5 * n, c.out.ptr + 5 * n, n / 2, .bf16, .sum, c.stream.handle); + try c.compare(comm, label, "eager between replays", .bf16, n, 5 * n, c.want[0..n]); + } +}; diff --git a/zig/src/fabric/tp/gpu_cli.zig b/zig/src/fabric/tp/gpu_cli.zig new file mode 100644 index 0000000000..dc576fc03b --- /dev/null +++ b/zig/src/fabric/tp/gpu_cli.zig @@ -0,0 +1,66 @@ +//! Validate the runner's command before opening a driver, bootstrap or fate channel. +const std = @import("std"); + +pub const usage = "usage: tf-tp-test suite | failfast RANK fatal|kill|transport [N [HOLD_S]] | roce-host | hca | kernel\n"; +pub const Mode = enum { fatal, kill, transport }; +pub const Failure = struct { victim: u32, mode: Mode, n: u32 = 50, hold: ?u32 = null }; +pub const Command = union(enum) { suite, failfast: Failure, @"roce-host", hca, kernel }; + +pub fn parse(args: []const []const u8) error{InvalidArguments}!Command { + if (args.len == 0) return error.InvalidArguments; + const tag = std.meta.stringToEnum(std.meta.Tag(Command), args[0]) orelse return error.InvalidArguments; + switch (tag) { + .suite, .@"roce-host", .hca, .kernel => { + if (args.len != 1) return error.InvalidArguments; + return switch (tag) { + .suite => .suite, + .@"roce-host" => .@"roce-host", + .hca => .hca, + .kernel => .kernel, + else => unreachable, + }; + }, + .failfast => { + if (args.len < 3 or args.len > 5) return error.InvalidArguments; + const mode = std.meta.stringToEnum(Mode, args[2]) orelse return error.InvalidArguments; + if (mode != .transport and args.len != 3) return error.InvalidArguments; + return .{ .failfast = .{ + .victim = try number(args[1]), + .mode = mode, + .n = if (args.len > 3) try number(args[3]) else 50, + .hold = if (args.len > 4) try number(args[4]) else null, + } }; + }, + } +} + +fn number(s: []const u8) error{InvalidArguments}!u32 { + return std.fmt.parseInt(u32, s, 10) catch error.InvalidArguments; +} + +// Also executed against the Linux entry point with an invalid environment, before any CUDA work. +pub const invalid = [_][]const []const u8{ + &.{}, &.{"UNKNOWN"}, &.{ "suite", "extra" }, &.{ "kernel", "extra" }, + &.{ "hca", "extra" }, &.{ "roce-host", "extra" }, &.{"failfast"}, &.{ "failfast", "0" }, + &.{ "failfast", "0", "typo" }, &.{ "failfast", "bad", "fatal" }, &.{ "failfast", "-1", "kill" }, &.{ "failfast", "4294967296", "fatal" }, + &.{ "failfast", "0", "fatal", "50" }, &.{ "failfast", "0", "kill", "50" }, &.{ "failfast", "0", "transport", "bad" }, &.{ "failfast", "0", "transport", "50", "bad" }, + &.{ "failfast", "0", "transport", "50", "0", "extra" }, +}; + +test "invalid commands are refused before resources open" { + for (invalid) |args| try std.testing.expectError(error.InvalidArguments, parse(args)); +} + +test "documented commands and failfast defaults" { + inline for (.{ "suite", "roce-host", "hca", "kernel" }) |name| { + try std.testing.expectEqualStrings(name, @tagName(try parse(&.{name}))); + } + inline for (.{ Mode.fatal, Mode.kill, Mode.transport }) |mode| { + const f = (try parse(&.{ "failfast", "2", @tagName(mode) })).failfast; + try std.testing.expectEqualDeep(Failure{ .victim = 2, .mode = mode }, f); + } + const explicit = (try parse(&.{ "failfast", "63", "transport", "0", "0" })).failfast; + try std.testing.expectEqualDeep(Failure{ .victim = 63, .mode = .transport, .n = 0, .hold = 0 }, explicit); + const n = (try parse(&.{ "failfast", "1", "transport", "123" })).failfast; + try std.testing.expectEqualDeep(Failure{ .victim = 1, .mode = .transport, .n = 123 }, n); +} diff --git a/zig/src/fabric/tp/gpu_main.zig b/zig/src/fabric/tp/gpu_main.zig new file mode 100644 index 0000000000..1243ab794e --- /dev/null +++ b/zig/src/fabric/tp/gpu_main.zig @@ -0,0 +1,155 @@ +//! `tf-tp-test suite | failfast RANK fatal|kill|transport [N [HOLD_S]] | roce-host | hca | kernel`: one rank of TP=2. +const std = @import("std"); +const cuda = @import("cuda"); +const tp = @import("tp"); +const tp_kernel = @import("tp_kernel"); +const checks = @import("gpu_checks.zig"); +const bench = @import("gpu_bench.zig"); +const cli = @import("gpu_cli.zig"); + +const max_bytes = (4 << 20) + 4096; + +pub fn main(init: std.process.Init) !u8 { + const args = try init.minimal.args.toSlice(init.arena.allocator()); + const command = cli.parse(args[1..]) catch { + std.debug.print("{s}", .{cli.usage}); + return 2; + }; + // `kernel`: whether the build embeds the mailbox kernel (-Dtp-nvcc or -Dtp-fatbin), for scripts to check first + if (command == .kernel) { + std.debug.print("mailbox kernel {d} B\n", .{tp_kernel.mailbox.len}); + return if (tp_kernel.mailbox.len > 0) 0 else 1; + } + const cfg = try tp.Config.fromEnv(); + if (command == .@"roce-host") return @import("roce_host.zig").run(cfg, init.gpa); + if (command == .hca) return hca(cfg, init.io); + if (command == .failfast and (!cfg.failfast or command.failfast.hold != null) and !hostOnlyAllowed(cfg)) { + std.debug.print("REFUSED: failfast with TF_TP_FAILFAST=0 or a hold is host-only: TF_TP_ALLOW_HOST_ONLY=1 and no RDMA wire\n", .{}); + return 2; + } + if (cfg.backend != .nccl and tp_kernel.mailbox.len == 0) { + std.debug.print("FAIL TF_COMM_BACKEND={t} needs the mailbox kernel: rebuild with -Dtp-nvcc or -Dtp-fatbin\n", .{cfg.backend}); + return 2; + } + var driver = try cuda.Driver.open(); + defer driver.close(); + var ctx = try cuda.Context.init(&driver, @intCast(cfg.device)); + defer ctx.deinit(); + var name_buf: [128]u8 = undefined; + const t0 = tp.sock.nowNs(); + var s: tp.Session = undefined; + try s.open(&driver, cfg, tp_kernel.mailbox); + const up_ms = @as(f64, @floatFromInt(tp.sock.nowNs() - t0)) / 1e6; + std.debug.print("INFO rank {d}/{d} on {s} (sm_{d}), backend {t}, mailbox kernel {d} B, bootstrap + NCCL init {d:.0} ms\n", .{ cfg.rank, cfg.world, try ctx.name(&name_buf), try ctx.capability(), s.comm().kind(), tp_kernel.mailbox.len, up_ms }); + return switch (command) { + .suite => suite(&s, &driver, init.gpa, cfg), + .failfast => |f| if (f.mode == .transport) + @import("gpu_transport.zig").run(&s, &driver, init.gpa, cfg, f.victim, f.n, f.hold orelse 0) + else + failfast(&s, &driver, init.gpa, cfg, f.victim, @tagName(f.mode)), + else => unreachable, + }; +} + +/// `hca` (host only, no GPU): both ranks' RoCE pick over the bootstrap; this rank's `NCCL_IB_HCA=...` line on stdout. +fn hca(cfg: tp.Config, io: std.Io) !u8 { + var b = tp.Bootstrap.open(cfg) catch |e| { + const s = @as(f64, @floatFromInt(cfg.connect_timeout_ns)) / std.time.ns_per_s; + switch (e) { + // a peer that never runs `hca` (it names its ports, or is not up) leaves this rank waiting until here + error.Timeout => std.debug.print("FAIL hca rank {d}: no peer joined within TF_TP_PLAN_TIMEOUT ({d:.1} s): is its runner up, and leaving TF_TP_ROCE_HCA and NCCL_IB_HCA unset as this one does?\n", .{ cfg.rank, s }), + error.AddressInUse => std.debug.print("FAIL hca rank {d}: port {d} (TF_TP_PORT + 7) is in use on this host\n", .{ cfg.rank, cfg.link_port }), + error.Closed => std.debug.print("FAIL hca rank {d}: the peer closed the bootstrap connection\n", .{cfg.rank}), + else => std.debug.print("FAIL hca rank {d}: bootstrap: {t}\n", .{ cfg.rank, e }), + } + return 1; + }; + defer b.close(); + var specs: [tp.roce_verbs.max_hcas]tp.roce_verbs.HcaSpec = undefined; + const pick = tp.roce_pick.select(cfg, &b, &specs) catch |e| { + std.debug.print("FAIL hca rank {d}: {t} while picking the ports with the peer\n", .{ cfg.rank, e }); + return 1; + }; + var buf: [tp.roce_pick.list_bytes + 16]u8 = undefined; + const line = tp.roce_pick.hcaLine(pick, &specs, &buf) catch |e| { + const why = switch (e) { + error.LibraryUnavailable => "could not load libibverbs.so.1 (LibraryUnavailable); check the RDMA userspace installation", + error.MissingSymbol => "libibverbs.so.1 lacks required symbols (MissingSymbol); check the RDMA userspace installation", + error.NoRoceDevice => "no active Ethernet RDMA port with an IPv4 RoCE v2 GID on this host: name the peer's ports in TF_TP_ROCE_HCA or NCCL_IB_HCA", + error.NoSharedSubnet => "no RoCE port here shares an IPv4 /24 with the peer's: name the peer's ports in TF_TP_ROCE_HCA or NCCL_IB_HCA", + else => @errorName(e), + }; + std.debug.print("FAIL hca rank {d}: {s}\n", .{ cfg.rank, why }); + return 1; + }; + var names: [tp.roce_pick.list_bytes]u8 = undefined; + const how = if (pick.explicit) "TF_TP_ROCE_HCA" else "on a /24 the peer shares"; + std.debug.print("INFO hca rank {d}: {s} ({s})\n", .{ cfg.rank, try tp.roce_pick.joinNames(specs[0..pick.n], true, &names), how }); + try std.Io.File.stdout().writeStreamingAll(io, line); + return 0; +} + +fn suite(s: *tp.Session, d: *const cuda.Driver, gpa: std.mem.Allocator, cfg: tp.Config) !u8 { + var c = try checks.Ctx.init(d, gpa, cfg.rank, max_bytes); + defer c.deinit(); + var backends: [2]struct { comm: tp.Collective, label: []const u8 } = undefined; + var n_backends: usize = 1; + backends[0] = .{ .comm = s.nccl.iface(), .label = "nccl" }; + if (s.comm().kind() != .nccl) { + backends[1] = .{ .comm = s.comm(), .label = if (s.roce != null) "roce" else "mailbox" }; + n_backends = 2; + } + const sizes = [_]usize{ 6, 1000, 4096, 65536, 256 * 1024, 256 * 1024 + 16, 1 << 20, 4 << 20 }; + for (backends[0..n_backends]) |b| { + for (sizes, 0..) |n, i| try c.collectives(b.comm, b.label, n, 100 * i); + try c.pointToPoint(b.comm, b.label, 64 * 1024, 7); + for ([_]usize{ 4096, 65536, 1 << 20 }) |n| try c.graphs(b.comm, b.label, n, 4); + try b.comm.barrier(); + } + for (backends[0..n_backends]) |b| { + try bench.sweep(&c, b.comm, b.label, 4096, 4 << 20); + try bench.decodeWindow(&c, b.comm, b.label, 86, 8192); + } + // the stop: rank 0 marks it expected first, then both meet and close + if (s.fate) |*f| f.expectStop(); + try s.comm().barrier(); + std.debug.print("SUMMARY rank {d}: {d} passed, {d} failed\n", .{ cfg.rank, c.passes, c.failures }); + s.close(); + return if (c.failures == 0) 0 else 1; +} + +/// The failed-transport check's host-only forms (fail-fast off, a hold): TF_TP_ALLOW_HOST_ONLY=1 and no RDMA wire. +fn hostOnlyAllowed(cfg: tp.Config) bool { + const flag = std.mem.eql(u8, std.mem.span(std.c.getenv("TF_TP_ALLOW_HOST_ONLY") orelse ""), "1"); + return flag and (cfg.backend != .roce or cfg.roce_wire == .shm); +} + +pub fn realNow() f64 { + var ts: std.c.timespec = undefined; + _ = std.c.clock_gettime(.REALTIME, &ts); + return @as(f64, @floatFromInt(ts.sec)) + @as(f64, @floatFromInt(ts.nsec)) / 1e9; +} + +/// Exchanges until the victim fails; the survivor is left waiting inside one, which only the fate channel ends. +fn failfast(s: *tp.Session, d: *const cuda.Driver, gpa: std.mem.Allocator, cfg: tp.Config, victim: u32, mode: []const u8) !u8 { + var c = try checks.Ctx.init(d, gpa, cfg.rank, 1 << 20); + defer c.deinit(); + const comm = s.comm(); + var k: u32 = 0; + while (true) : (k += 1) { + if (k == 50 and cfg.rank == victim) { + std.debug.print("INJECT {s} rank {d} at {d:.6}\n", .{ mode, cfg.rank, realNow() }); + if (std.mem.eql(u8, mode, "kill")) _ = std.c.raise(.KILL); + s.fatal("window", error.Injected); + return 3; // not reached: the process exits 70 + } + try comm.allReduce(c.in.ptr, c.out.ptr, 2048, .bf16, .sum, c.stream.handle); + c.stream.synchronize() catch |e| { + std.debug.print("rank {d}: stream error {t} after the peer failed\n", .{ cfg.rank, e }); + }; + comm.check() catch |e| { + std.debug.print("rank {d}: transport reports {t} at exchange {d}; waiting for the fate channel\n", .{ cfg.rank, e, k }); + while (true) tp.sock.sleepNs(std.time.ns_per_s); + }; + } +} diff --git a/zig/src/fabric/tp/gpu_transport.zig b/zig/src/fabric/tp/gpu_transport.zig new file mode 100644 index 0000000000..49cd625844 --- /dev/null +++ b/zig/src/fabric/tp/gpu_transport.zig @@ -0,0 +1,71 @@ +//! `tf-tp-test failfast V transport [N [HOLD_S]]`: the failed-transport path on real HCAs, injected on an idle link. +const std = @import("std"); +const cuda = @import("cuda"); +const tp = @import("tp"); +const checks = @import("gpu_checks.zig"); +const realNow = @import("gpu_main.zig").realNow; + +/// N clean sums, an NCCL meet, the victim's `Roce.inject` once every link is idle, then its close (fail-fast: exit 70). +pub fn run(s: *tp.Session, d: *const cuda.Driver, gpa: std.mem.Allocator, cfg: tp.Config, victim: u32, n: u32, hold_s: u32) !u8 { + const r = s.roce orelse { + std.debug.print("failfast transport needs TF_COMM_BACKEND=roce (the session runs {t})\n", .{s.comm().kind()}); + return 2; + }; + var c = try checks.Ctx.init(d, gpa, cfg.rank, 4096); + defer c.deinit(); + const comm = s.comm(); + for (0..n) |_| { + try comm.allReduce(c.in.ptr, c.out.ptr, 2048, .bf16, .sum, c.stream.handle); + try c.stream.synchronize(); + try comm.check(); + } + try s.nccl.iface().barrier(); + std.debug.print("rank {d}: {d} clean exchanges; both ranks met over NCCL\n", .{ cfg.rank, n }); + // the inject only on an idle link: every rank's posts completed, else no rank injects + const t_idle = tp.sock.nowNs(); + while (!r.linkIdle() and tp.sock.nowNs() - t_idle < 2 * std.time.ns_per_s) tp.sock.sleepNs(std.time.ns_per_ms); + var line: [384]u8 = undefined; + std.debug.print("rank {d}: link {s} before the inject: {s}\n", .{ cfg.rank, if (r.linkIdle()) "idle" else "BUSY", r.describeLink(&line) }); + if (try idleRanks(s, &c, r.linkIdle()) != cfg.world) { + std.debug.print("REFUSED rank {d}: a link was not idle; no inject, a clean stop\n", .{cfg.rank}); + if (s.fate) |*f| f.expectStop(); + try s.nccl.iface().barrier(); + s.close(); + return 2; + } + if (cfg.rank == victim) { + std.debug.print("INJECT transport rank {d} at {d:.6}\n", .{ cfg.rank, realNow() }); + r.inject(); + comm.check() catch |e| std.debug.print("rank {d}: transport reports {t}\n", .{ cfg.rank, e }); + } else if (s.fate != null) { + while (true) tp.sock.sleepNs(std.time.ns_per_s); // the fate channel ends this rank (exit 70) + } + const kept0 = tp.roce.Roce.kept.load(.monotonic); + s.close(); // fail-fast on: exit 70 from here + const kept = tp.roce.Roce.kept.load(.monotonic) - kept0; + const want: u32 = @intFromBool(cfg.rank == victim); + const live = cfg.rank != victim or mapped(r.region); + std.debug.print("rank {d}: closed; regions kept {d} (want {d}){s}\n", .{ cfg.rank, kept, want, if (cfg.rank != victim) "" else if (live) ", region still mapped" else ", region NOT mapped" }); + if (hold_s > 0) { + std.debug.print("HOLD rank {d} pid {d} for {d} s\n", .{ cfg.rank, std.c.getpid(), hold_s }); + tp.sock.sleepNs(@as(u64, hold_s) * std.time.ns_per_s); + } + const ok = kept == want and live; + std.debug.print("{s} rank {d}\n", .{ if (ok) "PASS" else "FAIL", cfg.rank }); + return if (ok) 0 else 1; +} + +fn mapped(region: []const u8) bool { + var vec: [1]u8 = undefined; + return std.os.linux.errno(std.os.linux.mincore(@constCast(region.ptr), 4096, &vec)) == .SUCCESS; +} + +/// The ranks whose link was idle, summed over NCCL (one f32 a rank). +fn idleRanks(s: *tp.Session, c: *checks.Ctx, idle: bool) !u32 { + var v: [1]f32 = .{@floatFromInt(@intFromBool(idle))}; + try c.in.uploadAsync(0, std.mem.asBytes(&v), c.stream.handle); + try s.nccl.iface().allReduce(c.in.ptr, c.out.ptr, 1, .f32, .sum, c.stream.handle); + try c.stream.synchronize(); + try c.out.download(0, std.mem.asBytes(&v)); + return @intFromFloat(v[0]); +} diff --git a/zig/src/fabric/tp/host.zig b/zig/src/fabric/tp/host.zig new file mode 100644 index 0000000000..051daea537 --- /dev/null +++ b/zig/src/fabric/tp/host.zig @@ -0,0 +1,293 @@ +//! In-process ranks over host memory: the `Collective` contract without a GPU (desyncs refused, abort wakes waiters). +const std = @import("std"); +const collective = @import("collective.zig"); +const reference = @import("reference.zig"); +const DevicePtr = collective.DevicePtr; +const DType = collective.DType; +const Op = collective.Op; +const Error = collective.Error; + +pub const max_ranks = 8; + +const Call = enum(u8) { none, all_reduce, all_gather, all_gather_v, exchange, barrier }; + +const Post = struct { call: Call = .none, send: DevicePtr = 0, recv: DevicePtr = 0, count: usize = 0, t: DType = .u8, op: Op = .sum, peer: u32 = 0, lens: DevicePtr = 0 }; + +/// One-way message slot from rank `src` to rank `dst` (send / recv). +const Slot = struct { buf: std.atomic.Value(DevicePtr) = .init(0), bytes: usize = 0, taken: std.atomic.Value(bool) = .init(false) }; + +pub const Group = struct { + world: u32, + /// A wait longer than this is `error.Timeout` (a peer that never calls). + timeout_ns: u64 = 10 * std.time.ns_per_s, + posts: [max_ranks]Post = @splat(.{}), + arrived: std.atomic.Value(u32) = .init(0), + generation: std.atomic.Value(u32) = .init(0), + aborted: std.atomic.Value(bool) = .init(false), + /// Ranks (a bit each) whose transport refuses device lengths: their `all_gather_v` moves whole strides (tests). + var_refuse: u32 = 0, + slots: [max_ranks][max_ranks]Slot = @splat(@splat(.{})), + + pub fn init(world: u32) Group { + std.debug.assert(world >= 1 and world <= max_ranks); + return .{ .world = world }; + } + + pub fn rank(g: *Group, r: u32) Rank { + std.debug.assert(r < g.world); + return .{ .group = g, .me = r }; + } + + /// Sense-reversing barrier over every rank; `error.Aborted` once any rank aborts. + fn meet(g: *Group) Error!void { + const gen = g.generation.load(.acquire); + if (g.arrived.fetchAdd(1, .acq_rel) + 1 == g.world) { + g.arrived.store(0, .release); + _ = g.generation.fetchAdd(1, .acq_rel); + return if (g.aborted.load(.acquire)) error.Aborted else {}; + } + const t0 = now(); + while (g.generation.load(.acquire) == gen) { + if (g.aborted.load(.acquire)) return error.Aborted; + if (now() - t0 > g.timeout_ns) return error.Timeout; + std.Thread.yield() catch {}; + } + if (g.aborted.load(.acquire)) return error.Aborted; + } +}; + +fn now() u64 { + var ts: std.c.timespec = undefined; + _ = std.c.clock_gettime(.MONOTONIC, &ts); + return @as(u64, @intCast(ts.sec)) * std.time.ns_per_s + @as(u64, @intCast(ts.nsec)); +} + +fn bytesAt(p: DevicePtr, n: usize) []u8 { + if (n == 0) return &.{}; + return @as([*]u8, @ptrFromInt(p))[0..n]; +} + +pub const Rank = struct { + group: *Group, + me: u32, + /// `varAgreed`'s answer, once this communicator agreed. + var_agreed: ?bool = null, + + pub fn iface(self: *Rank) collective.Collective { + return .{ .ptr = self, .vtable = &vtable }; + } + + const vtable: collective.Collective.VTable = .{ + .kind = .host, + .rank = rankOf, + .world = worldOf, + .all_reduce = allReduce, + .all_gather = allGather, + .exchange = exchange, + .send = send, + .recv = recv, + .barrier = barrier, + .check = check, + .abort = abort, + .all_gather_v = allGatherV, + .var_fits = varFits, + .var_agreed = varAgreed, + }; + + fn self_(ptr: *anyopaque) *Rank { + return @ptrCast(@alignCast(ptr)); + } + + fn rankOf(ptr: *anyopaque) u32 { + return self_(ptr).me; + } + + fn worldOf(ptr: *anyopaque) u32 { + return self_(ptr).group.world; + } + + /// Posts this rank's call, waits for all, and refuses calls that do not match rank 0's. + fn enter(self: *Rank, p: Post) Error!void { + const g = self.group; + if (g.aborted.load(.acquire)) return error.Aborted; + g.posts[self.me] = p; + try g.meet(); + const r0 = g.posts[0]; + if (r0.call != p.call or r0.count != p.count or r0.t != p.t or r0.op != p.op) { + abort(self); + return error.Invalid; + } + } + + fn allReduce(ptr: *anyopaque, s: DevicePtr, r: DevicePtr, count: usize, t: DType, op: Op, _: collective.Stream) Error!void { + const self = self_(ptr); + const g = self.group; + try self.enter(.{ .call = .all_reduce, .send = s, .recv = r, .count = count, .t = t, .op = op }); + const n = count * t.size(); + var ins: [max_ranks][]const u8 = undefined; + for (0..g.world) |i| ins[i] = bytesAt(g.posts[i].send, n); + // the output may alias this rank's input: reduce into scratch first, then meet before writing + var scratch: [1 << 16]u8 = undefined; + if (n > scratch.len) return error.Unsupported; + if (n > 0) reference.reduce(scratch[0..n], ins[0..g.world], t, op); + try g.meet(); + @memcpy(bytesAt(r, n), scratch[0..n]); + try g.meet(); + } + + fn allGather(ptr: *anyopaque, s: DevicePtr, r: DevicePtr, count: usize, t: DType, _: collective.Stream) Error!void { + const self = self_(ptr); + const g = self.group; + try self.enter(.{ .call = .all_gather, .send = s, .recv = r, .count = count, .t = t }); + const n = count * t.size(); + const out = bytesAt(r, n * g.world); + for (0..g.world) |i| @memcpy(out[i * n ..][0..n], bytesAt(g.posts[i].send, n)); + try g.meet(); + } + + fn lensAt(p: DevicePtr, world: u32) []const i32 { + return @as([*]const i32, @ptrFromInt(p))[0..world]; + } + + /// Copies only each rank's lens[r] bytes (`var_refuse` ranks take whole strides); unequal lengths are refused. + fn allGatherV(ptr: *anyopaque, s: DevicePtr, r: DevicePtr, stride: usize, lens: DevicePtr, t: DType, _: collective.Stream) Error!void { + const self = self_(ptr); + const g = self.group; + try self.enter(.{ .call = .all_gather_v, .send = s, .recv = r, .count = stride, .t = t, .lens = lens }); + const n = stride * t.size(); + const mine = lensAt(lens, g.world); + for (0..g.world) |i| if (!std.mem.eql(i32, mine, lensAt(g.posts[i].lens, g.world))) { + abort(self); + return error.Invalid; + }; + const whole = g.var_refuse & (@as(u32, 1) << @intCast(self.me)) != 0; + const out = bytesAt(r, n * g.world); + for (0..g.world) |i| { + if (mine[i] < 0 or mine[i] > n) { + abort(self); + return error.Invalid; + } + const len: usize = if (whole) n else @intCast(mine[i]); + @memcpy(out[i * n ..][0..len], bytesAt(g.posts[i].send, len)); + } + try g.meet(); + } + + fn varFits(ptr: *anyopaque, bytes: usize) bool { + const self = self_(ptr); + return bytes > 0 and self.group.var_refuse & (@as(u32, 1) << @intCast(self.me)) == 0; + } + + /// One int all-gather of `varFits(16)` the first time, then the kept answer. + fn varAgreed(ptr: *anyopaque) Error!bool { + const self = self_(ptr); + if (self.var_agreed) |v| return v; + const mine: i32 = @intFromBool(varFits(ptr, 16)); + var all: [max_ranks]i32 = undefined; + try allGather(ptr, @intFromPtr(&mine), @intFromPtr(&all), 1, .i32, null); + self.var_agreed = std.mem.min(i32, all[0..self.group.world]) == 1; + return self.var_agreed.?; + } + + fn exchange(ptr: *anyopaque, s: DevicePtr, r: DevicePtr, count: usize, t: DType, peer: u32, _: collective.Stream) Error!void { + const self = self_(ptr); + const g = self.group; + try collective.checkPeer(self.me, g.world, peer); + try self.enter(.{ .call = .exchange, .send = s, .recv = r, .count = count, .t = t, .peer = peer }); + if (g.posts[peer].peer != self.me) { + abort(self); + return error.Invalid; + } + const n = count * t.size(); + @memcpy(bytesAt(r, n), bytesAt(g.posts[peer].send, n)); + try g.meet(); + } + + fn send(ptr: *anyopaque, buf: DevicePtr, count: usize, t: DType, peer: u32, _: collective.Stream) Error!void { + const self = self_(ptr); + const g = self.group; + try collective.checkPeer(self.me, g.world, peer); + const slot = &g.slots[self.me][peer]; + slot.bytes = count * t.size(); + slot.taken.store(false, .release); + slot.buf.store(if (buf == 0) 1 else buf, .release); + try waitFor(g, &slot.taken, true); + slot.buf.store(0, .release); + } + + fn recv(ptr: *anyopaque, buf: DevicePtr, count: usize, t: DType, peer: u32, _: collective.Stream) Error!void { + const self = self_(ptr); + const g = self.group; + try collective.checkPeer(self.me, g.world, peer); + const slot = &g.slots[peer][self.me]; + const t0 = now(); + var src = slot.buf.load(.acquire); + while (src == 0) : (src = slot.buf.load(.acquire)) { + if (g.aborted.load(.acquire)) return error.Aborted; + if (now() - t0 > g.timeout_ns) return error.Timeout; + std.Thread.yield() catch {}; + } + const n = count * t.size(); + if (slot.bytes != n) { + abort(self); + return error.Invalid; + } + @memcpy(bytesAt(buf, n), bytesAt(src, n)); + slot.taken.store(true, .release); + } + + fn waitFor(g: *Group, flag: *std.atomic.Value(bool), want: bool) Error!void { + const t0 = now(); + while (flag.load(.acquire) != want) { + if (g.aborted.load(.acquire)) return error.Aborted; + if (now() - t0 > g.timeout_ns) return error.Timeout; + std.Thread.yield() catch {}; + } + } + + fn barrier(ptr: *anyopaque) Error!void { + const self = self_(ptr); + try self.enter(.{ .call = .barrier }); + try self.group.meet(); + } + + fn check(ptr: *anyopaque) Error!void { + if (self_(ptr).group.aborted.load(.acquire)) return error.Aborted; + } + + fn abort(ptr: *anyopaque) void { + self_(ptr).group.aborted.store(true, .release); + } +}; + +/// Runs `body(rank_collective, rank, ctx)` on `world` threads at once and returns the first error any rank hit. +pub fn run(world: u32, ctx: anytype, comptime body: fn (collective.Collective, u32, @TypeOf(ctx)) anyerror!void) !void { + return runWith(world, .{}, ctx, body); +} + +pub const Options = struct { + /// `Group.var_refuse`: ranks whose transport takes no device lengths. + var_refuse: u32 = 0, +}; + +pub fn runWith(world: u32, opts: Options, ctx: anytype, comptime body: fn (collective.Collective, u32, @TypeOf(ctx)) anyerror!void) !void { + var group = Group.init(world); + group.var_refuse = opts.var_refuse; + var ranks: [max_ranks]Rank = undefined; + var errs: [max_ranks]?anyerror = @splat(null); + var threads: [max_ranks]std.Thread = undefined; + const Wrap = struct { + fn go(r: *Rank, c: @TypeOf(ctx), e: *?anyerror) void { + body(r.iface(), r.me, c) catch |err| { + e.* = err; + r.group.aborted.store(true, .release); + }; + } + }; + for (0..world) |i| { + ranks[i] = group.rank(@intCast(i)); + threads[i] = try std.Thread.spawn(.{}, Wrap.go, .{ &ranks[i], ctx, &errs[i] }); + } + for (threads[0..world]) |t| t.join(); + for (errs[0..world]) |e| if (e) |err| return err; +} diff --git a/zig/src/fabric/tp/host_test.zig b/zig/src/fabric/tp/host_test.zig new file mode 100644 index 0000000000..7de32008af --- /dev/null +++ b/zig/src/fabric/tp/host_test.zig @@ -0,0 +1,154 @@ +//! The `Collective` contract on the in-process ranks: every call's bits against the reference, desyncs and aborts. +const std = @import("std"); +const collective = @import("collective.zig"); +const reference = @import("reference.zig"); +const host = @import("host.zig"); +const Collective = collective.Collective; +const DType = collective.DType; + +fn addr(b: []u8) collective.DevicePtr { + return @intFromPtr(b.ptr); +} + +const n = 96; + +const Calls = struct { + fn go(c: Collective, rank: u32, _: void) !void { + const world = c.world(); + inline for (.{ DType.f32, DType.bf16, DType.i32, DType.f16 }) |t| { + const bytes = n * t.size(); + var mine: [n * 8]u8 = undefined; + var out: [n * 8 * host.max_ranks]u8 = undefined; + reference.fill(mine[0..bytes], t, rank, 7); + // all-reduce against the reference sum of every rank's input, in rank order + var ins: [host.max_ranks][n * 8]u8 = undefined; + var views: [host.max_ranks][]const u8 = undefined; + for (0..world) |r| { + reference.fill(ins[r][0..bytes], t, @intCast(r), 7); + views[r] = ins[r][0..bytes]; + } + var want: [n * 8]u8 = undefined; + reference.reduce(want[0..bytes], views[0..world], t, .sum); + try c.allReduce(addr(&mine), addr(&out), n, t, .sum, null); + try std.testing.expectEqualSlices(u8, want[0..bytes], out[0..bytes]); + // in place + try c.allReduce(addr(&mine), addr(&mine), n, t, .sum, null); + try std.testing.expectEqualSlices(u8, want[0..bytes], mine[0..bytes]); + // all-gather: rank order + reference.fill(mine[0..bytes], t, rank, 7); + try c.allGather(addr(&mine), addr(&out), n, t, null); + for (0..world) |r| try std.testing.expectEqualSlices(u8, views[r], out[r * bytes ..][0..bytes]); + } + try c.barrier(); + } +}; + +test "every call on two and three in-process ranks equals the reference" { + try host.run(2, {}, Calls.go); + try host.run(3, {}, Calls.go); +} + +const Pairwise = struct { + fn go(c: Collective, rank: u32, _: void) !void { + var mine: [64]u8 = @splat(@intCast(rank + 1)); + var got: [64]u8 = undefined; + const peer = 1 - rank; + try c.exchange(addr(&mine), addr(&got), 64, .u8, peer, null); + try std.testing.expectEqualSlices(u8, &@as([64]u8, @splat(@intCast(peer + 1))), &got); + // send / recv, ordered so neither waits forever + if (rank == 0) { + try c.send(addr(&mine), 16, .f32, 1, null); + } else { + var buf: [64]u8 = undefined; + try c.recv(addr(&buf), 16, .f32, 0, null); + try std.testing.expectEqualSlices(u8, &@as([64]u8, @splat(1)), &buf); + } + try std.testing.expectError(error.Invalid, c.exchange(addr(&mine), addr(&got), 64, .u8, rank, null)); + } +}; + +test "exchange, send and recv between two ranks" { + try host.run(2, {}, Pairwise.go); +} + +const Desync = struct { + fn go(c: Collective, rank: u32, _: void) !void { + var a: [64]u8 = @splat(0); + var b: [128]u8 = undefined; + // the ranks disagree on the count: both see it instead of hanging + const r = c.allGather(addr(&a), addr(&b), if (rank == 0) 16 else 32, .u8, null); + if (r) |_| return error.ShouldHaveFailed else |e| if (e != error.Invalid and e != error.Aborted) return e; + } +}; + +test "a desynchronised call is refused on every rank" { + try host.run(2, {}, Desync.go); +} + +const Aborts = struct { + fn go(c: Collective, rank: u32, _: void) !void { + var a: [16]u8 = @splat(0); + var b: [32]u8 = undefined; + if (rank == 1) { + // rank 1 never calls: it aborts after a while, which must free rank 0's waiting call + @import("sock.zig").sleepNs(50 * std.time.ns_per_ms); + c.abort(); + return; + } + try std.testing.expectError(error.Aborted, c.allGather(addr(&a), addr(&b), 16, .u8, null)); + try std.testing.expectError(error.Aborted, c.check()); + try std.testing.expectError(error.Aborted, c.barrier()); + } +}; + +test "abort frees a rank waiting in a collective" { + try host.run(2, {}, Aborts.go); +} + +const VarGather = struct { + fn go(c: Collective, rank: u32, refuse: u32) !void { + const world = c.world(); + const stride = 200; + const whole = refuse & (@as(u32, 1) << @intCast(rank)) != 0; + try std.testing.expectEqual(!whole, c.varFits(stride)); + try std.testing.expectEqual(refuse == 0, try c.varAgreed()); + try std.testing.expectEqual(refuse == 0, try c.varAgreed()); // kept: no second collective + var ins: [host.max_ranks][stride]u8 = undefined; + for (0..world) |r| reference.fill(&ins[r], .u8, @intCast(r), 11); + // lengths of 0, odd, 16 and the whole stride, in bytes; .f32 strides count elements + const cases = [_][3]i32{ .{ 0, 17, 200 }, .{ 200, 0, 3 }, .{ 16, 16, 16 }, .{ 199, 200, 1 } }; + inline for (.{ DType.u8, DType.f32 }) |t| for (cases) |lens| { + var out: [stride * host.max_ranks]u8 = @splat(0xEE); + try c.allGatherV(addr(&ins[rank]), addr(&out), stride / t.size(), @intFromPtr(&lens), t, null); + for (0..world) |r| { + const len: usize = @intCast(lens[r]); + try std.testing.expectEqualSlices(u8, ins[r][0..len], out[r * stride ..][0..len]); + // past a length: the transport's choice (untouched here, or the whole stride where lengths are refused) + const past = out[r * stride + len .. (r + 1) * stride]; + if (whole) try std.testing.expectEqualSlices(u8, ins[r][len..], past) else for (past) |x| try std.testing.expectEqual(@as(u8, 0xEE), x); + } + }; + try c.barrier(); + } +}; + +test "a variable-length all-gather moves each rank's length, and the ranks agree on device lengths once" { + for ([_]u32{ 2, 3 }) |w| { + try host.runWith(w, .{}, @as(u32, 0), VarGather.go); + try host.runWith(w, .{ .var_refuse = 2 }, @as(u32, 2), VarGather.go); + } +} + +const VarDesync = struct { + fn go(c: Collective, rank: u32, _: void) !void { + var a: [64]u8 = @splat(0); + var b: [128]u8 = undefined; + const lens = [2]i32{ 8, if (rank == 0) 8 else 16 }; + const r = c.allGatherV(addr(&a), addr(&b), 64, @intFromPtr(&lens), .u8, null); + if (r) |_| return error.ShouldHaveFailed else |e| if (e != error.Invalid and e != error.Aborted) return e; + } +}; + +test "lengths that differ between ranks are refused on every rank" { + try host.run(2, {}, VarDesync.go); +} diff --git a/zig/src/fabric/tp/hybrid.zig b/zig/src/fabric/tp/hybrid.zig new file mode 100644 index 0000000000..e5f6d72c31 --- /dev/null +++ b/zig/src/fabric/tp/hybrid.zig @@ -0,0 +1,183 @@ +//! Small exchanges through the mailbox (one host) or RoCE (two hosts), the rest through NCCL; ranks always agree. +const std = @import("std"); +const collective = @import("collective.zig"); +const Mailbox = @import("mailbox.zig").Mailbox; +const Roce = @import("roce.zig").Roce; +const Nccl = @import("nccl.zig").Nccl; +const DevicePtr = collective.DevicePtr; +const DType = collective.DType; +const Op = collective.Op; +const Error = collective.Error; + +/// The one-kernel path for small shards: both expose the same calls. +pub const Small = union(enum) { + mailbox: *Mailbox, + roce: *Roce, + + fn fits(s: Small, n: usize) bool { + return switch (s) { + inline else => |p| p.fits(n), + }; + } + + fn sum(s: Small, src: DevicePtr, dst: DevicePtr, count: usize, t: DType, stream: collective.Stream) Error!void { + return switch (s) { + inline else => |p| p.sum(src, dst, count, t, stream), + }; + } + + fn gather(s: Small, src: DevicePtr, dst: DevicePtr, n: usize, stream: collective.Stream) Error!void { + return switch (s) { + inline else => |p| p.allGatherBytes(src, dst, n, stream), + }; + } + + fn exchange(s: Small, src: DevicePtr, dst: DevicePtr, n: usize, stream: collective.Stream) Error!void { + return switch (s) { + inline else => |p| p.exchangeBytes(src, dst, n, stream), + }; + } + + fn check(s: Small) Error!void { + return switch (s) { + inline else => |p| p.check(), + }; + } + + fn abort(s: Small) void { + switch (s) { + inline else => |p| p.abort(), + } + } +}; + +/// Whether a variable-length gather of `n`-byte strides one op cannot take goes RoCE in slot-sized ops (needs the ABI). +pub fn chunkRoute(chunked_bytes: usize, n: usize, single: bool, abi: bool) bool { + return abi and !single and n > 0 and n <= chunked_bytes; +} + +pub const Hybrid = struct { + small: Small, + nccl: *Nccl, + /// Largest shard a rank sends through the small path. + max_bytes: usize, + /// Largest stride a variable-length gather takes on RoCE in slot-sized ops (TF_TP_ROCE_CHUNKED_KB; 0: none). + chunked_bytes: usize = 0, + /// Every rank's `varFits(16)`, agreed at `Session.open` (the bootstrap's all-gather): `varAgreed`'s answer. + var_agreed: bool = false, + + pub fn iface(h: *Hybrid) collective.Collective { + return .{ .ptr = h, .vtable = if (h.small == .roce) &roce_vtable else &mailbox_vtable }; + } + + fn vtable(comptime kind: collective.Kind) collective.Collective.VTable { + return .{ + .kind = kind, + .rank = rankOf, + .world = worldOf, + .all_reduce = allReduce, + .all_gather = allGather, + .exchange = exchange, + .send = send, + .recv = recv, + .barrier = barrier, + .check = check, + .abort = abort, + .all_gather_v = allGatherV, + .var_fits = varFits, + .var_agreed = varAgreed, + }; + } + + const mailbox_vtable = vtable(.hybrid); + const roce_vtable = vtable(.roce); + + fn self_(ptr: *anyopaque) *Hybrid { + return @ptrCast(@alignCast(ptr)); + } + + fn routed(h: *const Hybrid, n: usize) bool { + return n <= h.max_bytes and h.small.fits(n); + } + + fn rankOf(ptr: *anyopaque) u32 { + return self_(ptr).nccl.me; + } + + fn worldOf(ptr: *anyopaque) u32 { + return self_(ptr).nccl.size; + } + + fn allReduce(ptr: *anyopaque, s: DevicePtr, r: DevicePtr, count: usize, t: DType, op: Op, stream: collective.Stream) Error!void { + const h = self_(ptr); + if (op == .sum and (t == .f32 or t == .bf16) and h.routed(count * t.size())) return h.small.sum(s, r, count, t, stream); + return Nccl.allReduce(h.nccl, s, r, count, t, op, stream); + } + + fn allGather(ptr: *anyopaque, s: DevicePtr, r: DevicePtr, count: usize, t: DType, stream: collective.Stream) Error!void { + const h = self_(ptr); + if (h.routed(count * t.size())) return h.small.gather(s, r, count * t.size(), stream); + return Nccl.allGather(h.nccl, s, r, count, t, stream); + } + + /// RoCE moves each rank's device length; the mailbox and NCCL move whole strides (the same bytes where lens says). + fn allGatherV(ptr: *anyopaque, s: DevicePtr, r: DevicePtr, stride: usize, lens: DevicePtr, t: DType, stream: collective.Stream) Error!void { + const h = self_(ptr); + const n = stride * t.size(); + if (singleFits(h, n)) return h.small.roce.allGatherV(s, r, n, lens, stream); + if (varFits(ptr, n)) return h.small.roce.allGatherVChunked(s, r, n, lens, stream); + return allGather(ptr, s, r, stride, t, stream); + } + + fn singleFits(h: *const Hybrid, n: usize) bool { + return switch (h.small) { + .roce => |p| n > 0 and h.routed(n) and p.d != null, + .mailbox => false, + }; + } + + /// RoCE with its device side, in one op or in slot-sized ops (the route is the size's alone: every rank alike). + pub fn varFits(ptr: *anyopaque, n: usize) bool { + const h = self_(ptr); + const single = singleFits(h, n); + return single or switch (h.small) { + .roce => |p| chunkRoute(h.chunked_bytes, n, single, p.d != null and p.chunk_abi), + .mailbox => false, + }; + } + + fn varAgreed(ptr: *anyopaque) Error!bool { + return self_(ptr).var_agreed; + } + + fn exchange(ptr: *anyopaque, s: DevicePtr, r: DevicePtr, count: usize, t: DType, peer: u32, stream: collective.Stream) Error!void { + const h = self_(ptr); + try collective.checkPeer(h.nccl.me, h.nccl.size, peer); + if (h.routed(count * t.size())) return h.small.exchange(s, r, count * t.size(), stream); + return Nccl.exchange(h.nccl, s, r, count, t, peer, stream); + } + + fn send(ptr: *anyopaque, buf: DevicePtr, count: usize, t: DType, peer: u32, stream: collective.Stream) Error!void { + return Nccl.iface(self_(ptr).nccl).send(buf, count, t, peer, stream); + } + + fn recv(ptr: *anyopaque, buf: DevicePtr, count: usize, t: DType, peer: u32, stream: collective.Stream) Error!void { + return Nccl.iface(self_(ptr).nccl).recv(buf, count, t, peer, stream); + } + + fn barrier(ptr: *anyopaque) Error!void { + return Nccl.iface(self_(ptr).nccl).barrier(); + } + + fn check(ptr: *anyopaque) Error!void { + const h = self_(ptr); + try h.small.check(); + try Nccl.iface(h.nccl).check(); + } + + fn abort(ptr: *anyopaque) void { + const h = self_(ptr); + h.small.abort(); + Nccl.iface(h.nccl).abort(); + } +}; diff --git a/zig/src/fabric/tp/log.zig b/zig/src/fabric/tp/log.zig new file mode 100644 index 0000000000..4e3234ab49 --- /dev/null +++ b/zig/src/fabric/tp/log.zig @@ -0,0 +1,15 @@ +//! TP's log lines, silent under test: a test's stderr is the build runner's channel. +const std = @import("std"); +const quiet = @import("builtin").is_test; + +pub fn err(comptime format: []const u8, args: anytype) void { + if (!quiet) std.log.err(format, args); +} + +pub fn warn(comptime format: []const u8, args: anytype) void { + if (!quiet) std.log.warn(format, args); +} + +pub fn info(comptime format: []const u8, args: anytype) void { + if (!quiet) std.log.info(format, args); +} diff --git a/zig/src/fabric/tp/mailbox.cu b/zig/src/fabric/tp/mailbox.cu new file mode 100644 index 0000000000..843b4f1af2 --- /dev/null +++ b/zig/src/fabric/tp/mailbox.cu @@ -0,0 +1,564 @@ +// One-shot two-rank exchange through pinned host memory both GPUs map (the TP mailbox; mailbox.zig is its host side). + +#include +#include + +// Protocol adapted from b12x's RoCEnante (Apache-2.0): a device epoch, slots by seq parity, a seq flag after the data. +namespace tftp { + +constexpr int kMaxBlocks = 16; +constexpr int kFlagStride = 32; // words: one 128-byte line per flag +// ctrl words: [0] abort (any rank, any host thread); per rank r at 16 + 8 r: failed, seq, seen, block, why, waited us +constexpr int kCtrlAbort = 0; +constexpr int kCtrlRank = 16; + +enum Mode : uint32_t { kGather = 0, kSumF32 = 1, kSumBf16 = 2, kExchange = 3 }; + +struct MailArgs { + const uint8_t *in; // this rank's shard (device) + uint8_t *out; // gather [2 n], exchange / sums [n] + uint8_t *peer_slots; // the peer's inbox data, as this GPU maps it + uint32_t *peer_flags; // the peer's inbox flags + const uint8_t *my_slots; // this rank's inbox data + const uint32_t *my_flags; // this rank's inbox flags + uint32_t *state; // device: [0] epoch, [1] tail counter, [2] poison + uint32_t *ctrl; // shared control words + uint64_t nbytes; // bytes a rank + uint64_t slot_bytes; + uint64_t timeout_ns; + uint32_t rank; + uint32_t mode; +}; + +__device__ __forceinline__ uint32_t ld_relaxed_gpu(const uint32_t *p) { + uint32_t v; + asm volatile("ld.relaxed.gpu.global.u32 %0, [%1];" : "=r"(v) : "l"(p) : "memory"); + return v; +} + +__device__ __forceinline__ uint32_t ld_relaxed_sys(const uint32_t *p) { + uint32_t v; + asm volatile("ld.relaxed.sys.global.u32 %0, [%1];" : "=r"(v) : "l"(p) : "memory"); + return v; +} + +__device__ __forceinline__ uint32_t ld_acquire_sys(const uint32_t *p) { + uint32_t v; + asm volatile("ld.acquire.sys.global.u32 %0, [%1];" : "=r"(v) : "l"(p) : "memory"); + return v; +} + +__device__ __forceinline__ uint4 ld_relaxed_sys_v4(const void *p) { + uint4 v; + asm volatile("ld.relaxed.sys.global.v4.u32 {%0, %1, %2, %3}, [%4];" + : "=r"(v.x), "=r"(v.y), "=r"(v.z), "=r"(v.w) : "l"(p) : "memory"); + return v; +} + +__device__ __forceinline__ uint32_t ld_relaxed_sys_u8(const uint8_t *p) { + uint32_t v; + asm volatile("ld.relaxed.sys.global.u8 %0, [%1];" : "=r"(v) : "l"(p) : "memory"); + return v; +} + +__device__ __forceinline__ void st_relaxed_sys(uint32_t *p, uint32_t v) { + asm volatile("st.relaxed.sys.global.u32 [%0], %1;" ::"l"(p), "r"(v) : "memory"); +} + +__device__ __forceinline__ void st_release_sys(uint32_t *p, uint32_t v) { + asm volatile("st.release.sys.global.u32 [%0], %1;" ::"l"(p), "r"(v) : "memory"); +} + +__device__ __forceinline__ void st_release_gpu(uint32_t *p, uint32_t v) { + asm volatile("st.release.gpu.global.u32 [%0], %1;" ::"l"(p), "r"(v) : "memory"); +} + +__device__ __forceinline__ uint32_t atom_add_acq_rel_gpu(uint32_t *p, uint32_t v) { + uint32_t old; + asm volatile("atom.acq_rel.gpu.global.add.u32 %0, [%1], %2;" : "=r"(old) : "l"(p), "r"(v) : "memory"); + return old; +} + +__device__ __forceinline__ uint32_t atom_add_acq_rel_sys(uint32_t *p, uint32_t v) { + uint32_t old; + asm volatile("atom.acq_rel.sys.global.add.u32 %0, [%1], %2;" : "=r"(old) : "l"(p), "r"(v) : "memory"); + return old; +} + +__device__ __forceinline__ uint32_t atom_cas_gpu(uint32_t *p, uint32_t cmp, uint32_t v) { + uint32_t old; + asm volatile("atom.relaxed.gpu.global.cas.b32 %0, [%1], %2, %3;" : "=r"(old) : "l"(p), "r"(cmp), "r"(v) : "memory"); + return old; +} + +__device__ __forceinline__ void fence_sc_sys() { asm volatile("fence.sc.sys;" ::: "memory"); } + +__device__ __forceinline__ uint64_t globaltimer() { + uint64_t t; + asm volatile("mov.u64 %0, %%globaltimer;" : "=l"(t)); + return t; +} + +// dst[i] <- src[i] for i in [0, n), grid-stride, 16-byte vectors when aligned; SysSrc: system-scope loads. +template +__device__ __forceinline__ void copy_bytes(uint8_t *dst, const uint8_t *src, uint64_t n, uint64_t index, + uint64_t stride) { + const uint64_t a = reinterpret_cast(dst) | reinterpret_cast(src) | n; + if ((a & 15u) == 0u) { + const uint64_t packs = n >> 4; + for (uint64_t i = index; i < packs; i += stride) { + const uint4 v = SysSrc ? ld_relaxed_sys_v4(src + (i << 4)) : reinterpret_cast(src)[i]; + reinterpret_cast(dst)[i] = v; + } + } else { + for (uint64_t i = index; i < n; i += stride) { + dst[i] = static_cast(SysSrc ? ld_relaxed_sys_u8(src + i) : src[i]); + } + } +} + +// TF_TP_ROCE_FAST: copy_bytes with two grid strides' loads issued before their stores (same bytes, one round trip). +template +__device__ __forceinline__ void copy_bytes2(uint8_t *dst, const uint8_t *src, uint64_t n, uint64_t index, + uint64_t stride) { + const uint64_t a = reinterpret_cast(dst) | reinterpret_cast(src) | n; + if ((a & 15u) != 0u) { + copy_bytes(dst, src, n, index, stride); + return; + } + const uint64_t packs = n >> 4; + for (uint64_t i = index; i < packs; i += 2u * stride) { + const uint64_t j = i + stride; + uint4 v0, v1 = make_uint4(0u, 0u, 0u, 0u); + if (SysSrc) { + v0 = ld_relaxed_sys_v4(src + (i << 4)); + if (j < packs) v1 = ld_relaxed_sys_v4(src + (j << 4)); + } else { + v0 = reinterpret_cast(src)[i]; + if (j < packs) v1 = reinterpret_cast(src)[j]; + } + reinterpret_cast(dst)[i] = v0; + if (j < packs) reinterpret_cast(dst)[j] = v1; + } +} + +template +__device__ __forceinline__ void copy_any(bool fast, uint8_t *dst, const uint8_t *src, uint64_t n, uint64_t index, + uint64_t stride) { + if (fast) { + copy_bytes2(dst, src, n, index, stride); + } else { + copy_bytes(dst, src, n, index, stride); + } +} + +__device__ __forceinline__ uint32_t add_bf16x2(uint32_t x, uint32_t y) { + const float2 a = __bfloat1622float2(*reinterpret_cast(&x)); + const float2 b = __bfloat1622float2(*reinterpret_cast(&y)); + const __nv_bfloat162 r = __floats2bfloat162_rn(a.x + b.x, a.y + b.y); + return *reinterpret_cast(&r); +} + +__device__ __forceinline__ uint32_t add_f32(uint32_t x, uint32_t y) { + return __float_as_uint(__uint_as_float(x) + __uint_as_float(y)); +} + +__device__ __forceinline__ uint32_t ld_relaxed_sys_u16(const uint8_t *p) { + uint16_t v; + asm volatile("ld.relaxed.sys.global.u16 %0, [%1];" : "=h"(v) : "l"(p) : "memory"); + return v; +} + +__device__ __forceinline__ uint16_t add_bf16(uint16_t x, uint16_t y) { + const __nv_bfloat16 r = __float2bfloat16_rn(__bfloat162float(__ushort_as_bfloat16(x)) + + __bfloat162float(__ushort_as_bfloat16(y))); + return __bfloat16_as_ushort(r); +} + +// out <- mine + peer's, rank 0's first; IEEE addition commutes, so both ranks get the same bits. +template +__device__ __forceinline__ void sum_bytes(uint8_t *out, const uint8_t *mine, const uint8_t *peer, uint64_t n, + uint32_t rank, uint64_t index, uint64_t stride) { + const uint64_t al = reinterpret_cast(out) | reinterpret_cast(mine) | + reinterpret_cast(peer) | n; + if ((al & 15u) == 0u) { + const uint64_t packs = n >> 4; + for (uint64_t i = index; i < packs; i += stride) { + const uint4 m = reinterpret_cast(mine)[i]; + const uint4 p = ld_relaxed_sys_v4(peer + (i << 4)); + const uint4 a = rank == 0 ? m : p; + const uint4 b = rank == 0 ? p : m; + uint4 r; + r.x = Bf16 ? add_bf16x2(a.x, b.x) : add_f32(a.x, b.x); + r.y = Bf16 ? add_bf16x2(a.y, b.y) : add_f32(a.y, b.y); + r.z = Bf16 ? add_bf16x2(a.z, b.z) : add_f32(a.z, b.z); + r.w = Bf16 ? add_bf16x2(a.w, b.w) : add_f32(a.w, b.w); + reinterpret_cast(out)[i] = r; + } + } else if (Bf16) { + for (uint64_t i = index; i < (n >> 1); i += stride) { + const uint16_t m = reinterpret_cast(mine)[i]; + const uint16_t p = static_cast(ld_relaxed_sys_u16(peer + (i << 1))); + reinterpret_cast(out)[i] = rank == 0 ? add_bf16(m, p) : add_bf16(p, m); + } + } else { + for (uint64_t i = index; i < (n >> 2); i += stride) { + const uint32_t m = reinterpret_cast(mine)[i]; + const uint32_t p = ld_relaxed_sys(reinterpret_cast(peer) + i); + reinterpret_cast(out)[i] = rank == 0 ? add_f32(m, p) : add_f32(p, m); + } + } +} + +// Thread 0 of a block: spin until the peer block's flag holds seq; false on abort (why 1) or timeout (why 2). +__device__ __noinline__ bool wait_flag(const uint32_t *flag, uint32_t seq, const uint32_t *ctrl, uint64_t timeout_ns, + uint32_t *seen, uint32_t *why, uint32_t *waited_us) { + const uint64_t t0 = globaltimer(); + uint32_t polls = 0; + while (true) { + const uint32_t v = ld_acquire_sys(flag); + if (v == seq) { + *seen = v; + return true; + } + if ((++polls & 63u) == 0u) { + const uint64_t now = globaltimer(); + const bool aborted = ld_relaxed_sys(ctrl + kCtrlAbort) != 0u; + const bool late = now > t0 && now - t0 > timeout_ns; + if (aborted || late) { + *seen = ld_acquire_sys(flag); + *why = aborted ? 1u : 2u; + *waited_us = now > t0 ? static_cast((now - t0) / 1000u) : 0u; + return *seen == seq; + } + } + } +} + +// TF_TP_ROCE_FAST: wait_flag for one of several pollers of a flag; `done` (shared) ends the others once one saw it. +__device__ __noinline__ bool wait_flag_shared(const uint32_t *flag, uint32_t seq, const uint32_t *ctrl, + uint64_t timeout_ns, uint32_t *seen, uint32_t *why, uint32_t *waited_us, + volatile uint32_t *done) { + const uint64_t t0 = globaltimer(); + uint32_t polls = 0; + while (true) { + const uint32_t v = ld_acquire_sys(flag); + if (v == seq) { + *done = 1u; + *seen = v; + return true; + } + if (*done != 0u) { + *seen = seq; + return true; + } + if ((++polls & 63u) == 0u) { + const uint64_t now = globaltimer(); + const bool aborted = ld_relaxed_sys(ctrl + kCtrlAbort) != 0u; + const bool late = now > t0 && now - t0 > timeout_ns; + if (aborted || late) { + *seen = ld_acquire_sys(flag); + *why = aborted ? 1u : 2u; + *waited_us = now > t0 ? static_cast((now - t0) / 1000u) : 0u; + if (*seen == seq) { + *done = 1u; + return true; + } + return *done != 0u; + } + } + } +} + +// One failure record a rank (the first failing wait's): its fields, then its valid word, each fenced at system scope. +__device__ __forceinline__ void fail_record(uint32_t *poison, uint32_t *rec, uint32_t seq, uint32_t seen, uint32_t who, + uint32_t why, uint32_t waited) { + if (atom_cas_gpu(poison, 0u, 1u) == 0u) { + st_relaxed_sys(rec + 1, seq); + st_relaxed_sys(rec + 2, seen); + st_relaxed_sys(rec + 3, who); + st_relaxed_sys(rec + 4, why); + st_relaxed_sys(rec + 5, waited); + fence_sc_sys(); + st_relaxed_sys(rec, 1u); + fence_sc_sys(); + } +} + +} // namespace tftp + +using namespace tftp; + +extern "C" __global__ void __launch_bounds__(512) tp_mailbox(MailArgs a) { + __shared__ uint32_t s_head[2]; // poison, seq + __shared__ uint32_t s_bad; + const uint32_t tid = threadIdx.x; + const uint32_t gdim = gridDim.x; + const uint64_t index = static_cast(blockIdx.x) * blockDim.x + tid; + const uint64_t stride = static_cast(gdim) * blockDim.x; + + // a poisoned transport does nothing (the host raises on its next check); poison and epoch in one round trip + if (tid == 0) { + s_head[0] = ld_relaxed_gpu(a.state + 2); + s_head[1] = ld_relaxed_gpu(a.state) + 1u; + s_bad = 0u; + } + __syncthreads(); + if (s_head[0] != 0u) { + return; + } + const uint32_t seq = s_head[1]; + const uint64_t slot = seq & 1u; + const uint64_t flag_at = (slot * kMaxBlocks + blockIdx.x) * kFlagStride; + + // 1. this block's share of the shard into the peer's inbox, then its flag after it (system-scope release) + copy_bytes(a.peer_slots + slot * a.slot_bytes, a.in, a.nbytes, index, stride); + __syncthreads(); + if (tid == 0) { + fence_sc_sys(); + st_release_sys(a.peer_flags + flag_at, seq); + } + // the local shard while the peer's writes travel + if (a.mode == kGather) { + copy_bytes(a.out + static_cast(a.rank) * a.nbytes, a.in, a.nbytes, index, stride); + } + + // 2. the peer's block of the same index (it wrote exactly the bytes this block reads) + if (tid == 0) { + uint32_t seen = 0u, why = 0u, waited = 0u; + if (!wait_flag(a.my_flags + flag_at, seq, a.ctrl, a.timeout_ns, &seen, &why, &waited)) { + s_bad = 1u; + fail_record(a.state + 2, a.ctrl + kCtrlRank + 8u * a.rank, seq, seen, blockIdx.x, why, waited); + } + } + __syncthreads(); + + // 3. copy out, gated by this block's own wait + if (s_bad == 0u) { + const uint8_t *inbox = a.my_slots + slot * a.slot_bytes; + switch (a.mode) { + case kGather: + copy_bytes(a.out + static_cast(1u - a.rank) * a.nbytes, inbox, a.nbytes, index, + stride); + break; + case kExchange: + copy_bytes(a.out, inbox, a.nbytes, index, stride); + break; + case kSumF32: + sum_bytes(a.out, a.in, inbox, a.nbytes, a.rank, index, stride); + break; + default: + sum_bytes(a.out, a.in, inbox, a.nbytes, a.rank, index, stride); + break; + } + } + + // 4. the last block publishes the epoch, unless a wait of this launch failed (later launches stay no-ops) + __syncthreads(); + if (tid == 0) { + // the counter is back at 0 for the next launch (stream order), whatever its grid + const bool last = gdim == 1u || atom_add_acq_rel_gpu(a.state + 1, 1u) == gdim - 1u; + const bool ok = gdim == 1u ? s_bad == 0u : ld_relaxed_gpu(a.state + 2) == 0u; + if (last && gdim > 1u) { + st_release_gpu(a.state + 1, 0u); + } + if (last && ok) { + st_release_gpu(a.state, seq); + } + } +} + +// The RoCE variant (roce.zig): blocks stage into the send slot, the last rings the doorbell, a host proxy RDMA-writes. +namespace tftp { + +constexpr int kRoceHcas = 2; +// ctrl words shared with roce.zig's proxy: [0] doorbell seq, [4 + slot] padded bytes, [8] completed, [16..21] failure +constexpr int kRoceSeq = 0; +constexpr int kRoceSlotBytes = 4; +constexpr int kRoceCompleted = 8; +constexpr int kRoceFailed = 16; +constexpr int kRoceAbort = 12; // wait_flag reads ctrl[kCtrlAbort]: the RoCE abort word is passed as that ctrl + +struct RoceArgs { + const uint8_t *in; + uint8_t *out; + uint8_t *send_slots; // this rank's send slots (pinned host, read by the NIC) + const uint8_t *recv_slots; // this rank's receive slots (written by the peer's NIC) + const uint32_t *flags; // this rank's flags [2][kRoceHcas], written by the peer's NIC + uint32_t *ctrl; + uint32_t *state; // device: [0] epoch, [1] tail counter, [2] poison, [3] stage counter + uint64_t nbytes; + uint64_t slot_bytes; + uint64_t timeout_ns; + uint32_t rank; + uint32_t mode; + uint32_t n_hca; + uint32_t opts; // TF_TP_ROCE_FAST: bit 0 fast, bits 8-15 pollers a flag, bits 16-31 start stagger ns; 0: classic + // gather only (nullptr: every shard is nbytes): lens[r] = rank r's bytes, padded to 16 on the wire + const int32_t *lens; + uint64_t chunk; // chunked gathers: this op's offset into each shard (at most a slot of each from there; else 0) +}; + +// Rank r's bytes this op: ``nbytes``, or lens[r] capped at nbytes. +__device__ __forceinline__ uint64_t roce_shard(const RoceArgs &a, uint32_t r) { + if (a.lens == nullptr) { + return a.nbytes; + } + const int32_t v = __ldg(a.lens + r); + const uint64_t n = v > 0 ? static_cast(v) : 0u; + return n < a.nbytes ? n : a.nbytes; +} + +// The bytes a copy of an n-byte shard moves: n rounded up to 16 when that stays inside a 16-aligned stride, else n. +__device__ __forceinline__ uint64_t roce_span(const RoceArgs &a, uint64_t n) { + if (a.lens == nullptr || (a.nbytes & 15u) != 0u) { + return n; + } + return (n + 15u) & ~uint64_t(15u); +} + +// A shard's bytes past `off`, at most a slot (chunked gathers; off 0 and a shard within the slot: the shard). +__device__ __forceinline__ uint64_t roce_part(const RoceArgs &a, uint64_t shard, uint64_t off) { + const uint64_t rest = shard > off ? shard - off : 0u; + return rest < a.slot_bytes ? rest : a.slot_bytes; +} + +} // namespace tftp + +// Present in images whose RoceArgs carry ``chunk`` (roce.zig refuses TF_TP_ROCE_CHUNKED_KB without it); never launched. +extern "C" __global__ void tp_roce_chunk_abi() {} + +extern "C" __global__ void __launch_bounds__(512) tp_roce(RoceArgs a) { + __shared__ uint32_t s_head[2]; // poison, seq + __shared__ uint32_t s_bad; + __shared__ uint32_t s_done[kRoceHcas]; // fast: some poller of HCA h saw its flag + const bool fast = (a.opts & 1u) != 0u; + const uint32_t tid = threadIdx.x; + const uint32_t gdim = gridDim.x; + const uint64_t index = static_cast(blockIdx.x) * blockDim.x + tid; + const uint64_t stride = static_cast(gdim) * blockDim.x; + + if (tid == 0) { + s_head[0] = ld_relaxed_gpu(a.state + 2); + s_head[1] = ld_relaxed_gpu(a.state) + 1u; + s_bad = 0u; + } + if (tid < static_cast(kRoceHcas)) { + s_done[tid] = 0u; + } + __syncthreads(); + if (s_head[0] != 0u) { + return; + } + const uint32_t seq = s_head[1]; + const uint64_t slot = seq & 1u; + const bool var = a.mode == kGather && a.lens != nullptr; + const uint64_t off = var ? a.chunk : 0u; + const uint64_t mine = var ? roce_part(a, roce_shard(a, a.rank), off) : a.nbytes; + const uint64_t theirs = var ? roce_part(a, roce_shard(a, 1u - a.rank), off) : a.nbytes; + const uint64_t padded = var ? (mine < 16u ? 16u : (mine + 15u) & ~uint64_t(15u)) : (a.nbytes + 15u) & ~uint64_t(15u); + if (off != 0u && mine == 0u && theirs == 0u) { + return; // nothing past this offset on either rank: no op, the epoch stays (both ranks skip alike) + } + if (padded > a.slot_bytes) { // never staged past a slot: a refused wait (why 3), the transport poisoned + if (tid == 0 && blockIdx.x == 0) { + fail_record(a.state + 2, a.ctrl + kRoceFailed, seq, 0u, 0u, 3u, 0u); + } + return; + } + + // 1. stage this block's share into the send slot; the last block to finish rings the doorbell + copy_any(fast, a.send_slots + slot * a.slot_bytes, a.in + off, var ? roce_span(a, mine) : a.nbytes, index, + stride); + __syncthreads(); + if (tid == 0 && fast) { + // lean doorbell: release RMW per block arrival, the last acquires, then a release store (no SC system fences) + const bool last = gdim == 1u || atom_add_acq_rel_sys(a.state + 3, 1u) == gdim - 1u; + if (last) { + if (gdim > 1u) { + st_release_gpu(a.state + 3, 0u); + } + st_relaxed_sys(a.ctrl + kRoceSlotBytes + slot, static_cast(padded)); + st_release_sys(a.ctrl + kRoceSeq, seq); + } + } else if (tid == 0) { + fence_sc_sys(); + const bool last = gdim == 1u || atom_add_acq_rel_gpu(a.state + 3, 1u) == gdim - 1u; + if (last) { + if (gdim > 1u) { + st_release_gpu(a.state + 3, 0u); + fence_sc_sys(); // every block's staging (seen through the counter) before the doorbell + } + st_relaxed_sys(a.ctrl + kRoceSlotBytes + slot, static_cast(padded)); + st_release_sys(a.ctrl + kRoceSeq, seq); + } + } + if (a.mode == kGather) { + copy_any(fast, a.out + static_cast(a.rank) * a.nbytes + off, a.in + off, + var ? roce_span(a, mine) : a.nbytes, index, stride); + } + + // 2. every HCA's flag for this slot; fast: poller k of HCA h is thread 32 k + h, started k x stagger ns late + const uint32_t pollers = (a.opts >> 8) & 0xffu; + const uint32_t stagger_ns = a.opts >> 16; + if (fast) { + const uint32_t lane = tid & 31u; + const uint32_t k = tid >> 5; + if (lane < a.n_hca && k < (pollers == 0u ? 1u : pollers)) { + if (k > 0u && stagger_ns != 0u) { + __nanosleep(k * stagger_ns); + } + uint32_t seen = 0u, why = 0u, waited = 0u; + if (!wait_flag_shared(a.flags + (slot * kRoceHcas + lane) * kFlagStride, seq, a.ctrl + kRoceAbort, + a.timeout_ns, &seen, &why, &waited, &s_done[lane])) { + s_bad = 1u; + fail_record(a.state + 2, a.ctrl + kRoceFailed, seq, seen, lane, why, waited); + } + } + } else if (tid == 0) { + for (uint32_t h = 0; h < a.n_hca; h++) { + uint32_t seen = 0u, why = 0u, waited = 0u; + if (!wait_flag(a.flags + (slot * kRoceHcas + h) * kFlagStride, seq, a.ctrl + kRoceAbort, a.timeout_ns, &seen, + &why, &waited)) { + s_bad = 1u; + fail_record(a.state + 2, a.ctrl + kRoceFailed, seq, seen, h, why, waited); + break; + } + } + } + __syncthreads(); + + // 3. copy out of the receive slot the peer's NIC wrote + if (s_bad == 0u) { + const uint8_t *inbox = a.recv_slots + slot * a.slot_bytes; + switch (a.mode) { + case kGather: + copy_any(fast, a.out + static_cast(1u - a.rank) * a.nbytes + off, inbox, + var ? roce_span(a, theirs) : a.nbytes, index, stride); + break; + case kExchange: + copy_any(fast, a.out, inbox, a.nbytes, index, stride); + break; + case kSumF32: + sum_bytes(a.out, a.in, inbox, a.nbytes, a.rank, index, stride); + break; + default: + sum_bytes(a.out, a.in, inbox, a.nbytes, a.rank, index, stride); + break; + } + } + + // 4. the epoch, as tp_mailbox; the host also sees the last completed seq + __syncthreads(); + if (tid == 0) { + const bool last = gdim == 1u || atom_add_acq_rel_gpu(a.state + 1, 1u) == gdim - 1u; + const bool ok = gdim == 1u ? s_bad == 0u : ld_relaxed_gpu(a.state + 2) == 0u; + if (last && gdim > 1u) { + st_release_gpu(a.state + 1, 0u); + } + if (last && ok) { + st_release_gpu(a.state, seq); + st_relaxed_sys(a.ctrl + kRoceCompleted, seq); + } + } +} diff --git a/zig/src/fabric/tp/mailbox.zig b/zig/src/fabric/tp/mailbox.zig new file mode 100644 index 0000000000..902bb79393 --- /dev/null +++ b/zig/src/fabric/tp/mailbox.zig @@ -0,0 +1,215 @@ +//! The mailbox backend: two-rank exchanges in one kernel through pinned host memory both GPUs map (host side). +const std = @import("std"); +const log = @import("log.zig"); +const cuda = @import("cuda"); +const collective = @import("collective.zig"); +const Bootstrap = @import("bootstrap.zig").Bootstrap; +const sock = @import("sock.zig"); +const posix = std.posix; +const DevicePtr = collective.DevicePtr; +const DType = collective.DType; +const Error = collective.Error; + +pub const max_blocks = 16; +const threads = 512; +const flag_bytes = 2 * max_blocks * 128; +const ctrl_bytes = 4096; +const ctrl_abort = 0; +const ctrl_rank = 16; + +const Mode = enum(u32) { gather = 0, sum_f32 = 1, sum_bf16 = 2, exchange = 3 }; + +/// mailbox.cu's `MailArgs`, field for field. +const MailArgs = extern struct { + in: DevicePtr, + out: DevicePtr, + peer_slots: DevicePtr, + peer_flags: DevicePtr, + my_slots: DevicePtr, + my_flags: DevicePtr, + state: DevicePtr, + ctrl: DevicePtr, + nbytes: u64, + slot_bytes: u64, + timeout_ns: u64, + rank: u32, + mode: u32, +}; + +/// Driver calls the runtime's table does not carry (page-locking memory this process mapped itself). +pub const HostRegister = struct { + lib: std.DynLib, + register: *const fn (?*anyopaque, usize, c_uint) callconv(.c) cuda.abi.Result, + unregister: *const fn (?*anyopaque) callconv(.c) cuda.abi.Result, + + pub fn open() !HostRegister { + var lib = std.DynLib.open("libcuda.so.1") catch return error.DriverUnavailable; + errdefer lib.close(); + return .{ + .register = lib.lookup(@FieldType(HostRegister, "register"), "cuMemHostRegister_v2") orelse return error.MissingSymbol, + .unregister = lib.lookup(@FieldType(HostRegister, "unregister"), "cuMemHostUnregister") orelse return error.MissingSymbol, + .lib = lib, + }; + } +}; + +/// Grid for `n` bytes a rank: one block per 16 KiB, a power of two, at most `max_blocks` (both ranks pick the same). +pub fn blocksFor(n: usize) u32 { + const want = std.math.divCeil(usize, @max(n, 1), 16 * 1024) catch 1; + return @intCast(@min(std.math.ceilPowerOfTwoAssert(usize, want), max_blocks)); +} + +pub const Mailbox = struct { + d: *const cuda.Driver, + me: u32, + slot_bytes: usize, + timeout_ns: u64, + region: []align(std.heap.page_size_min) u8, + dev: DevicePtr, + hr: HostRegister, + state: cuda.DeviceBuffer, + module: cuda.Module, + func: cuda.Function, + aborted: std.atomic.Value(bool) = .init(false), + + /// Both ranks call it after the bootstrap (two ranks, one host); `image` is mailbox.cu's fatbin. + pub fn init(d: *const cuda.Driver, b: *Bootstrap, image: []const u8, max_bytes: usize, timeout_ns: u64) !Mailbox { + if (b.world != 2) return error.Unsupported; + if (image.len == 0) return error.NoKernel; + const slot = std.mem.alignForward(usize, max_bytes, 4096); + const len = ctrl_bytes + 2 * (flag_bytes + 2 * slot); + // rank 0 creates and sizes the file before anyone learns its name, the others open it, then rank 0 unlinks it + var name: [64]u8 = undefined; + var name_len: u8 = 0; + var fd: std.c.fd_t = -1; + defer if (fd >= 0) { + _ = std.c.close(fd); + }; + if (b.rank == 0) blk: { + const s = std.fmt.bufPrint(&name, "/dev/shm/tf-tp-{d}-{x}", .{ std.c.getpid(), sock.nowNs() }) catch break :blk; + name[s.len] = 0; + fd = std.c.open(name[0..s.len :0], .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true, .CLOEXEC = true }, @as(std.c.mode_t, 0o600)); + if (fd >= 0 and std.c.ftruncate(fd, @intCast(len)) == 0) name_len = @intCast(s.len); + } + // an empty name tells the other rank that rank 0 failed (it must not wait for a name that never comes) + try b.broadcast(std.mem.asBytes(&name_len), std.mem.asBytes(&name_len)); + if (name_len == 0) return error.SharedMemoryUnavailable; + try b.broadcast(name[0..name_len], name[0..name_len]); + name[name_len] = 0; + const path = name[0..name_len :0]; + if (b.rank != 0) { + fd = std.c.open(path, .{ .ACCMODE = .RDWR, .CLOEXEC = true }, @as(std.c.mode_t, 0)); + if (fd < 0) return error.SharedMemoryUnavailable; + } + try b.agree("mapped", "mailbox file"); + const region = try posix.mmap(null, len, .{ .READ = true, .WRITE = true }, .{ .TYPE = .SHARED }, fd, 0); + errdefer posix.munmap(region); + try b.agree("open", "mailbox mapping"); + if (b.rank == 0) _ = std.c.unlink(path); + var hr = try HostRegister.open(); + errdefer hr.lib.close(); + try d.check(hr.register(region.ptr, len, 1 | 2), "cuMemHostRegister"); // PORTABLE | DEVICEMAP + errdefer _ = hr.unregister(region.ptr); + var dev: DevicePtr = 0; + try d.check(d.api.cuMemHostGetDevicePointer_v2(&dev, region.ptr, 0), "cuMemHostGetDevicePointer"); + var state = try cuda.DeviceBuffer.alloc(d, 16); + errdefer state.free(); + try state.fill8(0, null); + var module = try cuda.Module.load(d, image); + errdefer module.unload(); + const func = try module.function("tp_mailbox"); + try b.agree("ready", "mailbox setup"); + return .{ .d = d, .me = b.rank, .slot_bytes = slot, .timeout_ns = timeout_ns, .region = region, .dev = dev, .hr = hr, .state = state, .module = module, .func = func }; + } + + pub fn deinit(m: *Mailbox) void { + m.module.unload(); + m.state.free(); + _ = m.hr.unregister(m.region.ptr); + m.hr.lib.close(); + posix.munmap(m.region); + } + + fn inbox(m: *const Mailbox, r: u32) usize { + return ctrl_bytes + r * (flag_bytes + 2 * m.slot_bytes); + } + + fn ctrlWord(m: *const Mailbox, i: usize) *std.atomic.Value(u32) { + return @ptrCast(@alignCast(m.region[4 * i ..].ptr)); + } + + /// Whether `n` bytes a rank fit one slot. + pub fn fits(m: *const Mailbox, n: usize) bool { + return n <= m.slot_bytes; + } + + fn launch(m: *Mailbox, mode: Mode, in: DevicePtr, out: DevicePtr, n: usize, stream: collective.Stream) Error!void { + if (m.aborted.load(.acquire)) return error.Aborted; + if (n > m.slot_bytes) return error.Unsupported; + if (n == 0) return; + const peer = 1 - m.me; + var a: MailArgs = .{ + .in = in, + .out = out, + .peer_slots = m.dev + m.inbox(peer) + flag_bytes, + .peer_flags = m.dev + m.inbox(peer), + .my_slots = m.dev + m.inbox(m.me) + flag_bytes, + .my_flags = m.dev + m.inbox(m.me), + .state = m.state.ptr, + .ctrl = m.dev, + .nbytes = n, + .slot_bytes = m.slot_bytes, + .timeout_ns = m.timeout_ns, + .rank = m.me, + .mode = @backingInt(mode), + }; + var params = [1]?*anyopaque{@ptrCast(&a)}; + const r = m.d.api.cuLaunchKernel(m.func.handle, blocksFor(n), 1, 1, threads, 1, 1, 0, stream, ¶ms, null); + m.d.check(r, "cuLaunchKernel tp_mailbox") catch return error.BackendFailed; + } + + pub fn allGatherBytes(m: *Mailbox, s: DevicePtr, r: DevicePtr, n: usize, stream: collective.Stream) Error!void { + return m.launch(.gather, s, r, n, stream); + } + + pub fn exchangeBytes(m: *Mailbox, s: DevicePtr, r: DevicePtr, n: usize, stream: collective.Stream) Error!void { + return m.launch(.exchange, s, r, n, stream); + } + + /// Sums only, f32 or bf16 (what the model's partials are); the rest is `error.Unsupported`. + pub fn sum(m: *Mailbox, s: DevicePtr, r: DevicePtr, count: usize, t: DType, stream: collective.Stream) Error!void { + const mode: Mode = switch (t) { + .f32 => .sum_f32, + .bf16 => .sum_bf16, + else => return error.Unsupported, + }; + return m.launch(mode, s, r, count * t.size(), stream); + } + + /// Ends pending and later exchanges on both ranks (the abort word is shared); this rank's launches stop. + pub fn abort(m: *Mailbox) void { + m.aborted.store(true, .release); + m.ctrlWord(ctrl_abort).store(1, .release); + } + + /// A wait that timed out or was aborted on either rank. + pub fn check(m: *Mailbox) Error!void { + for (0..2) |r| { + const rec = ctrl_rank + 8 * r; + if (m.ctrlWord(rec).load(.acquire) != 0) { + log.warn("tp mailbox: rank {d}'s wait for seq {d} failed ({s} after {d} us; block {d}, flag held {d})", .{ r, m.ctrlWord(rec + 1).load(.acquire), if (m.ctrlWord(rec + 4).load(.acquire) == 1) "aborted" else "timed out", m.ctrlWord(rec + 5).load(.acquire), m.ctrlWord(rec + 3).load(.acquire), m.ctrlWord(rec + 2).load(.acquire) }); + return error.PeerFailed; + } + } + if (m.aborted.load(.acquire) or m.ctrlWord(ctrl_abort).load(.acquire) != 0) return error.Aborted; + } +}; + +test "grids grow with the shard and stay powers of two" { + try std.testing.expectEqual(@as(u32, 1), blocksFor(4096)); + try std.testing.expectEqual(@as(u32, 1), blocksFor(16 * 1024)); + try std.testing.expectEqual(@as(u32, 2), blocksFor(16 * 1024 + 1)); + try std.testing.expectEqual(@as(u32, 4), blocksFor(64 * 1024)); + try std.testing.expectEqual(@as(u32, 16), blocksFor(4 << 20)); + try std.testing.expectEqual(@as(usize, 96), @sizeOf(MailArgs)); +} diff --git a/zig/src/fabric/tp/nccl.zig b/zig/src/fabric/tp/nccl.zig new file mode 100644 index 0000000000..c9369369ef --- /dev/null +++ b/zig/src/fabric/tp/nccl.zig @@ -0,0 +1,164 @@ +//! The NCCL backend: one communicator a rank, every collective one enqueue on the caller's stream. +const std = @import("std"); +const cuda = @import("cuda"); +const collective = @import("collective.zig"); +const nccl = cuda.nccl; +const DevicePtr = collective.DevicePtr; +const DType = collective.DType; +const Op = collective.Op; +const Error = collective.Error; + +pub fn dataType(t: DType) nccl.DataType { + return switch (t) { + .u8 => .u8, + .i32 => .i32, + .i64 => .i64, + .f16 => .f16, + .bf16 => .bf16, + .f32 => .f32, + .f64 => .f64, + }; +} + +pub fn redOp(op: Op) nccl.RedOp { + return switch (op) { + .sum => .sum, + .max => .max, + .min => .min, + }; +} + +pub const Nccl = struct { + lib: *const nccl.Library, + d: *const cuda.Driver, + comm: nccl.Comm, + me: u32, + size: u32, + aborted: std.atomic.Value(bool) = .init(false), + /// The barrier's one-float buffers and its own stream (host path only). + scratch: cuda.DeviceBuffer, + stream: cuda.Stream, + + /// Joins the communicator `id` names (every rank calls it with the same id; it returns once all have). + pub fn init(lib: *const nccl.Library, d: *const cuda.Driver, id: nccl.UniqueId, rank: u32, world: u32) !Nccl { + var comm: nccl.Comm = null; + try lib.check(lib.api.ncclCommInitRank(&comm, @intCast(world), id, @intCast(rank)), "ncclCommInitRank"); + errdefer _ = lib.api.ncclCommDestroy(comm); + var scratch = try cuda.DeviceBuffer.alloc(d, 4 * (world + 1)); + errdefer scratch.free(); + try scratch.fill8(0, null); + return .{ .lib = lib, .d = d, .comm = comm, .me = rank, .size = world, .scratch = scratch, .stream = try cuda.Stream.init(d, true) }; + } + + pub fn deinit(n: *Nccl) void { + _ = n.stream.synchronize() catch {}; + n.stream.deinit(); + n.scratch.free(); + if (!n.aborted.load(.acquire)) _ = n.lib.api.ncclCommDestroy(n.comm); + } + + pub fn iface(n: *Nccl) collective.Collective { + return .{ .ptr = n, .vtable = &vtable }; + } + + const vtable: collective.Collective.VTable = .{ + .kind = .nccl, + .rank = rankOf, + .world = worldOf, + .all_reduce = allReduce, + .all_gather = allGather, + .exchange = exchange, + .send = send, + .recv = recv, + .barrier = barrier, + .check = check, + .abort = abort, + }; + + fn self_(ptr: *anyopaque) *Nccl { + return @ptrCast(@alignCast(ptr)); + } + + fn rankOf(ptr: *anyopaque) u32 { + return self_(ptr).me; + } + + fn worldOf(ptr: *anyopaque) u32 { + return self_(ptr).size; + } + + fn call(n: *Nccl, r: nccl.Result, what: []const u8) Error!void { + if (r == 0) return; + if (n.aborted.load(.acquire)) return error.Aborted; + n.lib.check(r, what) catch return error.BackendFailed; + } + + fn live(n: *Nccl) Error!void { + if (n.aborted.load(.acquire)) return error.Aborted; + } + + /// NCCL adds in its ring's order: the rank-order sum `all_reduce` promises holds at two ranks, not past them. + pub fn allReduce(ptr: *anyopaque, s: DevicePtr, r: DevicePtr, count: usize, t: DType, op: Op, stream: collective.Stream) Error!void { + const n = self_(ptr); + try n.live(); + try n.call(n.lib.api.ncclAllReduce(s, r, count, dataType(t), redOp(op), n.comm, stream), "ncclAllReduce"); + } + + pub fn allGather(ptr: *anyopaque, s: DevicePtr, r: DevicePtr, count: usize, t: DType, stream: collective.Stream) Error!void { + const n = self_(ptr); + try n.live(); + try n.call(n.lib.api.ncclAllGather(s, r, count, dataType(t), n.comm, stream), "ncclAllGather"); + } + + pub fn exchange(ptr: *anyopaque, s: DevicePtr, r: DevicePtr, count: usize, t: DType, peer: u32, stream: collective.Stream) Error!void { + const n = self_(ptr); + try n.live(); + try collective.checkPeer(n.me, n.size, peer); + try n.call(n.lib.api.ncclGroupStart(), "ncclGroupStart"); + const a = n.lib.api.ncclSend(s, count, dataType(t), @intCast(peer), n.comm, stream); + const b = n.lib.api.ncclRecv(r, count, dataType(t), @intCast(peer), n.comm, stream); + const e = n.lib.api.ncclGroupEnd(); + try n.call(a, "ncclSend"); + try n.call(b, "ncclRecv"); + try n.call(e, "ncclGroupEnd"); + } + + fn send(ptr: *anyopaque, buf: DevicePtr, count: usize, t: DType, peer: u32, stream: collective.Stream) Error!void { + const n = self_(ptr); + try n.live(); + try collective.checkPeer(n.me, n.size, peer); + try n.call(n.lib.api.ncclSend(buf, count, dataType(t), @intCast(peer), n.comm, stream), "ncclSend"); + } + + fn recv(ptr: *anyopaque, buf: DevicePtr, count: usize, t: DType, peer: u32, stream: collective.Stream) Error!void { + const n = self_(ptr); + try n.live(); + try collective.checkPeer(n.me, n.size, peer); + try n.call(n.lib.api.ncclRecv(buf, count, dataType(t), @intCast(peer), n.comm, stream), "ncclRecv"); + } + + /// One float all-gathered on the barrier's own stream, then a sync of it (host path, never captured). + fn barrier(ptr: *anyopaque) Error!void { + const n = self_(ptr); + try n.live(); + try n.call(n.lib.api.ncclAllGather(n.scratch.ptr, n.scratch.ptr + 4, 1, .f32, n.comm, n.stream.handle), "ncclAllGather"); + n.stream.synchronize() catch return if (n.aborted.load(.acquire)) error.Aborted else error.BackendFailed; + } + + fn check(ptr: *anyopaque) Error!void { + const n = self_(ptr); + try n.live(); + var r: nccl.Result = 0; + try n.call(n.lib.api.ncclCommGetAsyncError(n.comm, &r), "ncclCommGetAsyncError"); + if (r != 0 and r != nccl.in_progress) { + n.lib.check(r, "NCCL async error") catch {}; + return error.PeerFailed; + } + } + + fn abort(ptr: *anyopaque) void { + const n = self_(ptr); + if (n.aborted.swap(true, .acq_rel)) return; + _ = n.lib.api.ncclCommAbort(n.comm); + } +}; diff --git a/zig/src/fabric/tp/planlink.zig b/zig/src/fabric/tp/planlink.zig new file mode 100644 index 0000000000..44dbb61a0c --- /dev/null +++ b/zig/src/fabric/tp/planlink.zig @@ -0,0 +1,467 @@ +//! The round plan from rank 0 to the other ranks over its own TCP connection: a 4-byte length, then int64 words. +const std = @import("std"); +const log = @import("log.zig"); +const sock = @import("sock.zig"); +const builtin = @import("builtin"); +const config = @import("config.zig"); +const linux = std.os.linux; + +pub const max_ranks = 64; + +/// The CPU a plan thread pinned itself to (TF_TP_PLAN_PIN), -1 until one did; an engine's CPU isolation keeps off it. +pub var pinned_cpu: std.atomic.Value(i32) = .init(-1); +const more: u32 = 0x8000; // MSG_MORE: the length and the ints leave as one segment + +pub const Error = error{ PeerClosed, TooLarge, NotRankZero, NotFollower }; + +/// A batch frame's first word (a plan's op is >= 0): [batch, n, len 1, plan 1, ..., len n, plan n]. +pub const batch: i64 = std.math.minInt(i64); +/// Words rank 0 holds at most (more flushes the held plans first), and the most a batch frame carries. +pub const hold_words: usize = 1024; +pub const batch_words: usize = 2 * hold_words + 2; + +/// Largest plan either side accepts: 64M words (512 MiB), so a garbled length cannot become a 4 GiB allocation. +pub const max_frame_words: usize = 64 << 20; + +comptime { + std.debug.assert(builtin.cpu.arch.endian() == .little); // the ints go out as they are in memory +} + +pub const PlanLink = struct { + rank: u32, + world: u32, + /// Rank 0: rank i's socket at index i; the others: rank 0's at index 0 (the bootstrap's `releasePlan`). + fds: [max_ranks]sock.Fd, + sent: u64 = 0, + received: u64 = 0, + /// TF_TP_PLAN_LINK=rdma (config.plan_spin_ns): the follower polls the socket this long before it blocks. + spin_ns: u64 = 0, + /// TF_TP_PLAN_PIN: the sending / receiving thread pins itself at its first plan; `avoid`: the RoCE proxy's CPU. + pin: ?config.PlanPin = null, + avoid: ?u32 = null, + pinned: bool = false, + /// rank 0: plans held for the next send (`hold`), each as [len, plan...]; `held_n` of them + held: [hold_words]i64 = undefined, + held_len: usize = 0, + held_n: usize = 0, + /// the follower: a batch frame's plans not handed out yet, as [len, plan...] from `inbox_at` + inbox: [batch_words]i64 = undefined, + inbox_len: usize = 0, + inbox_at: usize = 0, + batches: u64 = 0, + + pub fn init(rank: u32, world: u32, fds: [max_ranks]sock.Fd) PlanLink { + return .{ .rank = rank, .world = world, .fds = fds }; + } + + /// Rank 0: one plan to every other rank (host only); held plans go first, in the same frame when they fit. + pub fn send(p: *PlanLink, ints: []const i64) Error!void { + if (p.rank != 0) return error.NotRankZero; + if (ints.len > max_frame_words) return error.TooLarge; + if (p.held_n > 0) { + if (2 + p.held_len + 1 + ints.len <= batch_words) { + var frame: [batch_words]i64 = undefined; + frame[0] = batch; + frame[1] = @intCast(p.held_n + 1); + @memcpy(frame[2..][0..p.held_len], p.held[0..p.held_len]); + frame[2 + p.held_len] = @intCast(ints.len); + @memcpy(frame[3 + p.held_len ..][0..ints.len], ints); + const n = 3 + p.held_len + ints.len; + p.held_len = 0; + p.held_n = 0; + p.batches += 1; + return p.sendFrame(frame[0..n]); + } + try p.flush(); + } + return p.sendFrame(ints); + } + + /// Rank 0: a plan the followers run before the next sent one, carried in that plan's frame (`send` or `flush`). + pub fn hold(p: *PlanLink, ints: []const i64) Error!void { + if (p.rank != 0) return error.NotRankZero; + if (1 + ints.len > hold_words) { + try p.flush(); + return p.sendFrame(ints); + } + if (p.held_len + 1 + ints.len > hold_words) try p.flush(); + p.held[p.held_len] = @intCast(ints.len); + @memcpy(p.held[p.held_len + 1 ..][0..ints.len], ints); + p.held_len += 1 + ints.len; + p.held_n += 1; + } + + /// Rank 0: the held plans now, as one batch frame (nothing held: nothing sent). + pub fn flush(p: *PlanLink) Error!void { + if (p.held_n == 0) return; + var frame: [batch_words]i64 = undefined; + frame[0] = batch; + frame[1] = @intCast(p.held_n); + @memcpy(frame[2..][0..p.held_len], p.held[0..p.held_len]); + const n = 2 + p.held_len; + p.held_len = 0; + p.held_n = 0; + p.batches += 1; + return p.sendFrame(frame[0..n]); + } + + fn sendFrame(p: *PlanLink, ints: []const i64) Error!void { + p.pinOnce(); + const bytes = std.mem.sliceAsBytes(ints); + const len: u32 = @intCast(bytes.len); + for (p.fds[1..p.world]) |fd| { + if (std.c.send(fd, &len, 4, std.posix.MSG.NOSIGNAL | more) != 4) return error.PeerClosed; + sock.sendAll(fd, bytes) catch return error.PeerClosed; + } + p.sent += 1; + } + + /// The other ranks: the next plan into `out`; `error.PeerClosed` once rank 0 is gone, `error.TooLarge` skips one. + pub fn recv(p: *PlanLink, out: []i64) Error![]i64 { + if (p.inboxNext()) |m| { + if (m.len > out.len) return error.TooLarge; + @memcpy(out[0..m.len], m); + return out[0..m.len]; + } + const n = try p.header(); + if (n > out.len) { + try p.drop(n); + return error.TooLarge; + } + const got = try p.body(out[0..n]); + if (got.len > 0 and got[0] == batch) { + try p.unpack(got); + return p.recv(out); + } + return got; + } + + /// The next plan into `buf`, grown to the frame; the buffer is kept, so steady plans allocate nothing. + pub fn recvGrow(p: *PlanLink, gpa: std.mem.Allocator, buf: *std.ArrayList(i64)) (Error || std.mem.Allocator.Error)![]i64 { + if (p.inboxNext()) |m| { + try buf.resize(gpa, m.len); + @memcpy(buf.items, m); + return buf.items; + } + const n = try p.header(); + if (n > max_frame_words) { + try p.drop(n); + return error.TooLarge; + } + try buf.resize(gpa, n); + const got = try p.body(buf.items); + if (got.len > 0 and got[0] == batch) { + try p.unpack(got); + return p.recvGrow(gpa, buf); + } + return got; + } + + /// The follower: a batch frame's plans into the inbox (checked whole first: a garbled batch ends the link). + fn unpack(p: *PlanLink, frame: []const i64) Error!void { + if (frame.len < 2 or frame.len > batch_words or frame[1] < 1) return error.PeerClosed; + var at: usize = 2; + var k: i64 = 0; + while (k < frame[1]) : (k += 1) { + if (at >= frame.len or frame[at] < 0) return error.PeerClosed; + at += 1 + @as(usize, @intCast(frame[at])); + if (at > frame.len) return error.PeerClosed; + } + if (at != frame.len) return error.PeerClosed; + @memcpy(p.inbox[0 .. frame.len - 2], frame[2..]); + p.inbox_len = frame.len - 2; + p.inbox_at = 0; + p.batches += 1; + } + + /// The follower: the next plan of the last batch frame, if any (a slice of the inbox, valid until the next recv). + fn inboxNext(p: *PlanLink) ?[]const i64 { + if (p.inbox_at >= p.inbox_len) return null; + const n: usize = @intCast(p.inbox[p.inbox_at]); + const m = p.inbox[p.inbox_at + 1 ..][0..n]; + p.inbox_at += 1 + n; + return m; + } + + /// The next frame's length in words. + fn header(p: *PlanLink) Error!usize { + if (p.rank == 0) return error.NotFollower; + p.pinOnce(); + if (p.spin_ns > 0) p.spin(); + var len: u32 = undefined; + sock.recvExact(p.fds[0], std.mem.asBytes(&len), null) catch return error.PeerClosed; + if (len % 8 != 0) return error.PeerClosed; // not a frame boundary: the stream is lost + return len / 8; + } + + fn body(p: *PlanLink, out: []i64) Error![]i64 { + sock.recvExact(p.fds[0], std.mem.sliceAsBytes(out), null) catch return error.PeerClosed; + p.received += 1; + return out; + } + + /// Reads and drops `n` words. + fn drop(p: *PlanLink, n: usize) Error!void { + var left: usize = n * 8; + var scratch: [4096]u8 = undefined; + while (left > 0) : (left -= @min(left, scratch.len)) sock.recvExact(p.fds[0], scratch[0..@min(left, scratch.len)], null) catch return error.PeerClosed; + } + + /// Polls rank 0's socket until it is readable (or hung up) or `spin_ns` passed; the read that follows blocks. + fn spin(p: *PlanLink) void { + if (comptime builtin.os.tag != .linux) return; + var fd = [1]linux.pollfd{.{ .fd = p.fds[0], .events = linux.POLL.IN, .revents = 0 }}; + const end = sock.nowNs() + p.spin_ns; + while (true) { + const rc = linux.poll(&fd, 1, 0); + if (linux.errno(rc) == .SUCCESS and rc > 0) return; + if (sock.nowNs() >= end) return; + std.atomic.spinLoopHint(); + } + } + + /// Pins the calling thread once: an explicit CPU, or auto's highest cpu_capacity. + fn pinOnce(p: *PlanLink) void { + if (p.pinned) return; + p.pinned = true; + if (comptime builtin.os.tag != .linux) return; + const want = p.pin orelse return; + const cpu = switch (want) { + .cpu => |c| c, + .auto => bestCpu(p.avoid) orelse { + log.info("[tensorfold] tp: TF_TP_PLAN_PIN=auto: no cpu_capacity differs among the allowed CPUs; the plan thread stays unpinned", .{}); + return; + }, + }; + pinTo(cpu) catch |e| { + log.warn("[tensorfold] tp: TF_TP_PLAN_PIN: CPU {d} refused ({t}); the plan thread stays unpinned", .{ cpu, e }); + return; + }; + pinned_cpu.store(@intCast(cpu), .release); + log.info("[tensorfold] tp: plan link rank {d} pinned to CPU {d}", .{ p.rank, cpu }); + } + + pub fn close(p: *PlanLink) void { + for (&p.fds) |*fd| if (fd.* >= 0) { + sock.close(fd.*); + fd.* = -1; + }; + } +}; + +/// The calling thread onto `cpu` alone. +pub fn pinTo(cpu: u32) !void { + if (cpu >= linux.CPU_SETSIZE) return error.BadCpu; + var set: linux.cpu_set_t = @splat(0); + set[cpu / @bitSizeOf(usize)] |= @as(usize, 1) << @intCast(cpu % @bitSizeOf(usize)); + try linux.sched_setaffinity(0, &set); +} + +/// Auto pin: the allowed CPU (not `avoid`) with the highest cpu_capacity, lowest on ties; null when all are equal. +pub fn bestCpu(avoid: ?u32) ?u32 { + var set: linux.cpu_set_t = undefined; + if (linux.errno(linux.sched_getaffinity(0, @sizeOf(linux.cpu_set_t), &set)) != .SUCCESS) return null; + var best: ?u32 = null; + var best_cap: u32 = 0; + var low_cap: u32 = std.math.maxInt(u32); + for (set, 0..) |word, wi| { + var bits = word; + while (bits != 0) : (bits &= bits - 1) { + const cpu: u32 = @intCast(wi * @bitSizeOf(usize) + @ctz(bits)); + if (avoid == cpu) continue; + const c = capacity(cpu) orelse continue; + low_cap = @min(low_cap, c); + if (best == null or c > best_cap) { + best = cpu; + best_cap = c; + } + } + } + return if (best != null and best_cap > low_cap) best else null; +} + +fn capacity(cpu: u32) ?u32 { + var pb: [64]u8 = undefined; + const path = std.fmt.bufPrintSentinel(&pb, "/sys/devices/system/cpu/cpu{d}/cpu_capacity", .{cpu}, 0) catch return null; + const rc = linux.open(path, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); + if (linux.errno(rc) != .SUCCESS) return null; + const fd: i32 = @intCast(rc); + defer _ = linux.close(fd); + var b: [16]u8 = undefined; + const n = linux.read(fd, &b, b.len); + if (linux.errno(n) != .SUCCESS) return null; + return std.fmt.parseInt(u32, std.mem.trim(u8, b[0..n], " \n"), 10) catch null; +} + +test "plans arrive whole and in order; a closed rank 0 ends the follower" { + const bootstrap = @import("bootstrap.zig"); + const l = try bootstrap.testListener(); + defer sock.close(l.fd); + const deadline = sock.nowNs() + 5 * std.time.ns_per_s; + const a = try sock.connect("localhost", l.port, deadline); + const b = try sock.accept(l.fd, deadline); + var f0: [max_ranks]sock.Fd = @splat(-1); + var f1: [max_ranks]sock.Fd = @splat(-1); + f0[1] = b; + f1[0] = a; + var lead = PlanLink.init(0, 2, f0); + var follow = PlanLink.init(1, 2, f1); + defer follow.close(); + var big: [5000]i64 = undefined; + for (&big, 0..) |*v, i| v.* = @as(i64, @intCast(i)) * -3 + 0x7FFF_0000_0000; + try lead.send(&.{ 0x504C414E, 2, 1 }); + try lead.send(&big); + try lead.send(&.{}); + var buf: [8192]i64 = undefined; + try std.testing.expectEqualSlices(i64, &.{ 0x504C414E, 2, 1 }, try follow.recv(&buf)); + try std.testing.expectEqualSlices(i64, &big, try follow.recv(&buf)); + try std.testing.expectEqual(@as(usize, 0), (try follow.recv(&buf)).len); + try std.testing.expectError(error.NotRankZero, follow.send(&.{1})); + var small: [2]i64 = undefined; + try lead.send(&.{ 1, 2, 3 }); + try std.testing.expectError(error.TooLarge, follow.recv(&small)); + lead.close(); + try std.testing.expectError(error.PeerClosed, follow.recv(&buf)); +} + +test "an admission-sized plan (1.2M words) arrives whole through the growable path" { + const bootstrap = @import("bootstrap.zig"); + const l = try bootstrap.testListener(); + defer sock.close(l.fd); + const deadline = sock.nowNs() + 5 * std.time.ns_per_s; + const a = try sock.connect("localhost", l.port, deadline); + const b = try sock.accept(l.fd, deadline); + var f0: [max_ranks]sock.Fd = @splat(-1); + var f1: [max_ranks]sock.Fd = @splat(-1); + f0[1] = b; + f1[0] = a; + var lead = PlanLink.init(0, 2, f0); + defer lead.close(); + var follow = PlanLink.init(1, 2, f1); + defer follow.close(); + const gpa = std.testing.allocator; + const prompt = try gpa.alloc(i64, 1_200_000); + defer gpa.free(prompt); + for (prompt, 0..) |*v, i| v.* = @intCast((i *% 2654435761) % 163840); + // the socket buffer holds far less than 9.6 MB: rank 0 sends from its own thread, as it would + const Send = struct { + fn go(link: *PlanLink, ints: []const i64) void { + link.send(&.{ 0x504C414E, 3, 20 }) catch {}; + link.send(ints) catch {}; + link.send(&.{ 0x504C414E, 3, 21 }) catch {}; + } + }; + const t = try std.Thread.spawn(.{}, Send.go, .{ &lead, prompt }); + var buf: std.ArrayList(i64) = .empty; + defer buf.deinit(gpa); + try std.testing.expectEqualSlices(i64, &.{ 0x504C414E, 3, 20 }, try follow.recvGrow(gpa, &buf)); + try std.testing.expectEqualSlices(i64, prompt, try follow.recvGrow(gpa, &buf)); + try std.testing.expectEqualSlices(i64, &.{ 0x504C414E, 3, 21 }, try follow.recvGrow(gpa, &buf)); + t.join(); + try std.testing.expect(buf.capacity >= prompt.len); // kept for the next admission +} + +test "TF_TP_PLAN_LINK=rdma: a spinning follower gets every plan, late or early; TF_TP_PLAN_PIN pins the thread" { + if (builtin.os.tag != .linux) return error.SkipZigTest; + const bootstrap = @import("bootstrap.zig"); + const l = try bootstrap.testListener(); + defer sock.close(l.fd); + const deadline = sock.nowNs() + 5 * std.time.ns_per_s; + const a = try sock.connect("localhost", l.port, deadline); + const b = try sock.accept(l.fd, deadline); + var f0: [max_ranks]sock.Fd = @splat(-1); + var f1: [max_ranks]sock.Fd = @splat(-1); + f0[1] = b; + f1[0] = a; + var lead = PlanLink.init(0, 2, f0); + defer lead.close(); + var follow = PlanLink.init(1, 2, f1); + defer follow.close(); + follow.spin_ns = 2 * std.time.ns_per_ms; + // the CPU this thread may run on first: the pin keeps it allowed, so the test runner is not starved + var set: linux.cpu_set_t = undefined; + _ = linux.sched_getaffinity(0, @sizeOf(linux.cpu_set_t), &set); + var first: u32 = 0; + for (set, 0..) |w, i| if (w != 0) { + first = @intCast(i * @bitSizeOf(usize) + @ctz(w)); + break; + }; + const Follower = struct { + fn run(f: *PlanLink, cpu: u32, out: *[3]i64, ok: *bool) void { + f.pin = .{ .cpu = cpu }; + var buf: [8]i64 = undefined; + const x = (f.recv(&buf) catch return)[0]; // arrives within the spin + const y = (f.recv(&buf) catch return)[0]; // arrives after it: the blocking read + var now: linux.cpu_set_t = undefined; + _ = linux.sched_getaffinity(0, @sizeOf(linux.cpu_set_t), &now); + var count: usize = 0; + for (now) |w| count += @popCount(w); + out.* = .{ x, y, @intCast(count) }; + ok.* = f.pinned and (now[cpu / @bitSizeOf(usize)] >> @intCast(cpu % @bitSizeOf(usize))) & 1 == 1; + } + }; + var out: [3]i64 = .{ 0, 0, 0 }; + var ok = false; + const t = try std.Thread.spawn(.{}, Follower.run, .{ &follow, first, &out, &ok }); + try lead.send(&.{11}); + var ts: linux.timespec = .{ .sec = 0, .nsec = 20 * std.time.ns_per_ms }; + _ = linux.nanosleep(&ts, null); + try lead.send(&.{22}); + t.join(); + try std.testing.expectEqual([3]i64{ 11, 22, 1 }, out); + try std.testing.expect(ok); + if (bestCpu(null)) |c| try std.testing.expect((set[c / @bitSizeOf(usize)] >> @intCast(c % @bitSizeOf(usize))) & 1 == 1); +} + +test "held plans leave with the next plan, in order, as one frame; a large plan after them flushes them first" { + const bootstrap = @import("bootstrap.zig"); + const l = try bootstrap.testListener(); + defer sock.close(l.fd); + const deadline = sock.nowNs() + 5 * std.time.ns_per_s; + const a = try sock.connect("127.0.0.1", l.port, deadline); + const b = try sock.accept(l.fd, deadline); + var f0: [max_ranks]sock.Fd = @splat(-1); + var f1: [max_ranks]sock.Fd = @splat(-1); + f0[1] = b; + f1[0] = a; + const lead = try std.testing.allocator.create(PlanLink); + defer std.testing.allocator.destroy(lead); + lead.* = PlanLink.init(0, 2, f0); + const follow = try std.testing.allocator.create(PlanLink); + defer std.testing.allocator.destroy(follow); + follow.* = PlanLink.init(1, 2, f1); + defer follow.close(); + // a round: three keeps held, the window sent: one frame + try lead.hold(&.{ 22, 0, 3 }); + try lead.hold(&.{ 22, 1, 0 }); + try lead.hold(&.{ 22, 2, 5 }); + try lead.send(&.{ 21, 4, 9, 9, 9 }); + try std.testing.expectEqual(@as(u64, 1), lead.sent); + // a held plan, then one too large for a batch: two frames, the held one first + var big: [5000]i64 = undefined; + for (&big, 0..) |*v, i| v.* = @intCast(i); + try lead.hold(&.{ 22, 3, 1 }); + try lead.send(&big); + try std.testing.expectEqual(@as(u64, 3), lead.sent); + // flush alone, an empty plan held, a plain send after + try lead.hold(&.{}); + try lead.flush(); + try lead.flush(); // nothing held: nothing sent + try lead.send(&.{7}); + try std.testing.expectEqual(@as(u64, 5), lead.sent); + var buf: std.ArrayList(i64) = .empty; + defer buf.deinit(std.testing.allocator); + const gpa = std.testing.allocator; + try std.testing.expectEqualSlices(i64, &.{ 22, 0, 3 }, try follow.recvGrow(gpa, &buf)); + try std.testing.expectEqualSlices(i64, &.{ 22, 1, 0 }, try follow.recvGrow(gpa, &buf)); + try std.testing.expectEqualSlices(i64, &.{ 22, 2, 5 }, try follow.recvGrow(gpa, &buf)); + try std.testing.expectEqualSlices(i64, &.{ 21, 4, 9, 9, 9 }, try follow.recvGrow(gpa, &buf)); + var out: [8192]i64 = undefined; + try std.testing.expectEqualSlices(i64, &.{ 22, 3, 1 }, try follow.recv(&out)); + try std.testing.expectEqualSlices(i64, &big, try follow.recvGrow(gpa, &buf)); + try std.testing.expectEqual(@as(usize, 0), (try follow.recv(&out)).len); + try std.testing.expectEqualSlices(i64, &.{7}, try follow.recv(&out)); + try std.testing.expectEqual(@as(u64, 3), follow.batches); + try std.testing.expectError(error.NotRankZero, follow.hold(&.{1})); + lead.close(); +} diff --git a/zig/src/fabric/tp/reference.zig b/zig/src/fabric/tp/reference.zig new file mode 100644 index 0000000000..b98966fcc8 --- /dev/null +++ b/zig/src/fabric/tp/reference.zig @@ -0,0 +1,121 @@ +//! Host references for the collectives: reductions in rank order, bf16 / f16 added in fp32 and rounded once. +const std = @import("std"); +const collective = @import("collective.zig"); +const DType = collective.DType; +const Op = collective.Op; + +/// bf16 bits to fp32 (exact). +pub fn bf16ToF32(h: u16) f32 { + return @bitCast(@as(u32, h) << 16); +} + +/// fp32 to bf16 bits, round to nearest even; NaN stays a quiet NaN. +pub fn f32ToBf16(f: f32) u16 { + const u: u32 = @bitCast(f); + if (std.math.isNan(f)) return @intCast((u >> 16) | 0x40); + const round = 0x7FFF + ((u >> 16) & 1); + return @intCast((u +% round) >> 16); +} + +fn combine(comptime T: type, a: T, b: T, op: Op) T { + return switch (op) { + .sum => if (@typeInfo(T) == .int) a +% b else a + b, + .max => @max(a, b), + .min => @min(a, b), + }; +} + +fn reduceTyped(comptime T: type, acc: []u8, src: []const u8, op: Op) void { + const n = acc.len / @sizeOf(T); + for (0..n) |i| { + const a = std.mem.readInt(@Int(.unsigned, @bitSizeOf(T)), acc[i * @sizeOf(T) ..][0..@sizeOf(T)], .little); + const b = std.mem.readInt(@Int(.unsigned, @bitSizeOf(T)), src[i * @sizeOf(T) ..][0..@sizeOf(T)], .little); + const r: T = combine(T, @bitCast(a), @bitCast(b), op); + std.mem.writeInt(@Int(.unsigned, @bitSizeOf(T)), acc[i * @sizeOf(T) ..][0..@sizeOf(T)], @bitCast(r), .little); + } +} + +fn reduceHalf(acc: []u8, src: []const u8, op: Op, bf: bool) void { + const n = acc.len / 2; + for (0..n) |i| { + const a = std.mem.readInt(u16, acc[2 * i ..][0..2], .little); + const b = std.mem.readInt(u16, src[2 * i ..][0..2], .little); + const fa: f32 = if (bf) bf16ToF32(a) else @floatCast(@as(f16, @bitCast(a))); + const fb: f32 = if (bf) bf16ToF32(b) else @floatCast(@as(f16, @bitCast(b))); + const r = combine(f32, fa, fb, op); + const out: u16 = if (bf) f32ToBf16(r) else @bitCast(@as(f16, @floatCast(r))); + std.mem.writeInt(u16, acc[2 * i ..][0..2], out, .little); + } +} + +/// acc <- acc (op) src, element-wise; `acc` holds the lower ranks' result, `src` the next rank's input. +pub fn accumulate(acc: []u8, src: []const u8, t: DType, op: Op) void { + std.debug.assert(acc.len == src.len and acc.len % t.size() == 0); + switch (t) { + .u8 => reduceTyped(u8, acc, src, op), + .i32 => reduceTyped(i32, acc, src, op), + .i64 => reduceTyped(i64, acc, src, op), + .f32 => reduceTyped(f32, acc, src, op), + .f64 => reduceTyped(f64, acc, src, op), + .bf16 => reduceHalf(acc, src, op, true), + .f16 => reduceHalf(acc, src, op, false), + } +} + +/// out <- inputs[0] (op) inputs[1] (op) ..., in rank order. +pub fn reduce(out: []u8, inputs: []const []const u8, t: DType, op: Op) void { + @memcpy(out, inputs[0]); + for (inputs[1..]) |in| accumulate(out, in, t, op); +} + +/// Deterministic test data for `rank`'s input of `bytes`: finite values of type `t` that differ per rank and position. +pub fn fill(out: []u8, t: DType, rank: u32, seed: u64) void { + var rng = std.Random.DefaultPrng.init(seed ^ (@as(u64, rank) *% 0x9E3779B97F4A7C15)); + const r = rng.random(); + const n = out.len / t.size(); + for (0..n) |i| { + const x = r.float(f32) * 8.0 - 4.0; + switch (t) { + .u8 => out[i] = r.int(u8), + .i32 => std.mem.writeInt(i32, out[4 * i ..][0..4], r.int(i32), .little), + .i64 => std.mem.writeInt(i64, out[8 * i ..][0..8], r.int(i64), .little), + .f32 => std.mem.writeInt(u32, out[4 * i ..][0..4], @bitCast(x), .little), + .f64 => std.mem.writeInt(u64, out[8 * i ..][0..8], @bitCast(@as(f64, x) * 1.000000123), .little), + .bf16 => std.mem.writeInt(u16, out[2 * i ..][0..2], f32ToBf16(x), .little), + .f16 => std.mem.writeInt(u16, out[2 * i ..][0..2], @bitCast(@as(f16, @floatCast(x))), .little), + } + } +} + +test "bf16 rounding is nearest even" { + try std.testing.expectEqual(@as(u16, 0x3F80), f32ToBf16(1.0)); + // 1 + 2^-8 is exactly halfway between bf16 1.0 and 1.0078125: rounds to the even one (1.0) + try std.testing.expectEqual(@as(u16, 0x3F80), f32ToBf16(1.00390625)); + try std.testing.expectEqual(@as(u16, 0x3F82), f32ToBf16(1.01171875)); + try std.testing.expect(std.math.isNan(bf16ToF32(f32ToBf16(std.math.nan(f32))))); +} + +test "rank-order reduction of bf16 adds in fp32 and rounds once" { + var a: [4]u8 = undefined; + var b: [4]u8 = undefined; + std.mem.writeInt(u16, a[0..2], f32ToBf16(1.0), .little); + std.mem.writeInt(u16, b[0..2], f32ToBf16(0.00390625), .little); + std.mem.writeInt(u16, a[2..4], f32ToBf16(-2.5), .little); + std.mem.writeInt(u16, b[2..4], f32ToBf16(3.0), .little); + var out: [4]u8 = undefined; + reduce(&out, &.{ &a, &b }, .bf16, .sum); + try std.testing.expectEqual(f32ToBf16(1.0), std.mem.readInt(u16, out[0..2], .little)); + try std.testing.expectEqual(f32ToBf16(0.5), std.mem.readInt(u16, out[2..4], .little)); + reduce(&out, &.{ &a, &b }, .bf16, .max); + try std.testing.expectEqual(f32ToBf16(3.0), std.mem.readInt(u16, out[2..4], .little)); +} + +test "integer sums wrap" { + var a: [4]u8 = undefined; + var b: [4]u8 = undefined; + std.mem.writeInt(i32, &a, std.math.maxInt(i32), .little); + std.mem.writeInt(i32, &b, 1, .little); + var out: [4]u8 = undefined; + reduce(&out, &.{ &a, &b }, .i32, .sum); + try std.testing.expectEqual(@as(i32, std.math.minInt(i32)), std.mem.readInt(i32, &out, .little)); +} diff --git a/zig/src/fabric/tp/roce.zig b/zig/src/fabric/tp/roce.zig new file mode 100644 index 0000000000..c9bb72a2c3 --- /dev/null +++ b/zig/src/fabric/tp/roce.zig @@ -0,0 +1,525 @@ +//! The RoCE backend between two hosts: the tp_roce kernel stages and rings a doorbell, a proxy thread moves the slot. +const std = @import("std"); +const log = @import("log.zig"); +const cuda = @import("cuda"); +const collective = @import("collective.zig"); +const Bootstrap = @import("bootstrap.zig").Bootstrap; +const sock = @import("sock.zig"); +const rv = @import("roce_verbs.zig"); +const shm = @import("roce_shm.zig"); +const guard = @import("roce_guard.zig"); +const HostRegister = @import("mailbox.zig").HostRegister; +const posix = std.posix; +const DevicePtr = collective.DevicePtr; +const DType = collective.DType; +const Error = collective.Error; + +pub const max_blocks = 16; +const threads = 512; +const page = std.heap.page_size_min; +const flag_stride = 128; +// region: ctrl [4 KiB], flags [2 slots][2 HCAs] at 128 B, send [2][slot], recv [2][slot] (mailbox.cu's tp_roce) +const ctrl_off = 0; +const flag_off = 4096; +const send_off = 8192; +// ctrl words (mailbox.cu): doorbell, padded bytes per slot, completed, abort, proxy failure, a failed wait's record +const w_seq = 0; +const w_slot_bytes = 4; +const w_completed = 8; +const w_abort = 12; +const w_proxy_failed = 13; +const w_failed = 16; +/// Model graphs leave sub-millisecond gaps: the proxy stays hot this many idle polls, then naps 20 us at a time. +const idle_spins: u64 = 20_000_000; + +const Mode = enum(u32) { gather = 0, sum_f32 = 1, sum_bf16 = 2, exchange = 3 }; + +/// mailbox.cu's `RoceArgs`, field for field. +const RoceArgs = extern struct { + in: DevicePtr, + out: DevicePtr, + send_slots: DevicePtr, + recv_slots: DevicePtr, + flags: DevicePtr, + ctrl: DevicePtr, + state: DevicePtr, + nbytes: u64, + slot_bytes: u64, + timeout_ns: u64, + rank: u32, + mode: u32, + n_hca: u32, + /// TF_TP_ROCE_FAST (config.roce_fast; 0: the classic path) + opts: u32 = 0, + /// gather only: device int32 [2] of each rank's bytes (0: every shard is `nbytes`) + lens: DevicePtr = 0, + /// Chunked gathers: this op's byte offset into each rank's shard (it moves at most a slot from there; 0 otherwise). + chunk: u64 = 0, +}; + +pub const WireKind = enum { + /// RDMA writes over the HCAs. + verbs, + /// The proxy copies into the peer's region in shared memory: the same protocol on one host without RDMA (tests). + shm, +}; + +pub const Options = struct { + wire: WireKind = .verbs, + hcas: []const rv.HcaSpec = &.{}, + traffic_class: u8 = 0, + cpu: ?u32 = null, + max_bytes: usize = 256 * 1024, + timeout_ns: u64 = 120 * std.time.ns_per_s, + /// TF_TP_ROCE_FAST: tp_roce's `opts` word (config.roce_fast) + fast: u32 = 0, + /// TF_TP_ROCE_BLOCKS: the largest grid (config.roce_blocks) + blocks: u32 = 16, + /// The shm wire's stripes, as HCAs: its posts take the verbs wire's split and bounds. + shm_stripes: u32 = 1, + /// Tests: sees every shm post's stripes before the copy. + tap: ?*shm.PostTap = null, +}; + +const Wire = union(WireKind) { + verbs: rv.VerbsWire, + shm: shm.ShmWire, + + fn post(w: *Wire, slot: u32, seq: u32, padded: u32) !void { + switch (w.*) { + .verbs => |*v| try v.post(slot, seq, padded), + .shm => |*s| try s.post(slot, seq, padded), + } + } + + fn reap(w: *Wire) !void { + switch (w.*) { + .verbs => |*v| try v.reap(), + .shm => {}, + } + } + + fn hcas(w: *const Wire) u32 { + return switch (w.*) { + .verbs => |v| v.n_hca, + .shm => |s| s.stripes, + }; + } +}; + +pub const Roce = struct { + me: u32, + slot_bytes: usize, + timeout_ns: u64, + /// TF_TP_ROCE_FAST: the kernel's `opts` word + fast: u32 = 0, + /// TF_TP_ROCE_BLOCKS: the largest grid + max_grid: u32 = 16, + /// This rank's region (for shm: half of the mapping in `mapping`). + region: []align(page) u8, + mapping: []align(page) u8, + wire: Wire, + cpu: ?u32, + // device side (absent in host-only use: tests and `tf-tp-test roce-host`) + d: ?*const cuda.Driver = null, + hr: ?HostRegister = null, + dev: DevicePtr = 0, + state: ?cuda.DeviceBuffer = null, + module: ?cuda.Module = null, + func: ?cuda.Function = null, + /// The kernel image takes `RoceArgs.chunk` (it exports tp_roce_chunk_abi): TF_TP_ROCE_CHUNKED_KB needs it. + chunk_abi: bool = false, + // the proxy thread + thread: ?std.Thread = null, + running: std.atomic.Value(bool) = .init(false), + last_seq: u32 = 0, + err_buf: [160]u8 = undefined, + err_len: usize = 0, + // host emulation of the kernel's side (tests) + host_epoch: u32 = 0, + aborted: std.atomic.Value(bool) = .init(false), + + pub fn regionBytes(slot: usize) usize { + return send_off + 4 * slot; + } + + /// Both ranks call it after the bootstrap; any rank failing makes all return `error.RoceUnavailable` (collective). + pub fn init(d: ?*const cuda.Driver, b: *Bootstrap, image: []const u8, o: Options) !*Roce { + if (b.world != 2) return error.Unsupported; + if (o.max_bytes > guard.max_write) return error.WriteTooLarge; // a slot no post could fill in one write + const slot = std.mem.alignForward(usize, o.max_bytes, 4096); + const len = regionBytes(slot); + const r = try std.heap.page_allocator.create(Roce); + errdefer std.heap.page_allocator.destroy(r); + r.* = .{ .me = b.rank, .slot_bytes = slot, .timeout_ns = o.timeout_ns, .fast = o.fast, .max_grid = o.blocks, .region = undefined, .mapping = undefined, .wire = undefined, .cpu = o.cpu }; + // 1. the regions and the wire, each rank alone; every rank learns whether all did + const ok = r.openWire(b, o, len) catch |e| blk: { + log.warn("[tensorfold] tp roce: rank {d} setup failed: {t}", .{ b.rank, e }); + break :blk false; + }; + if (!try agreeAll(b, ok, "RoCE setup")) { + if (ok) r.closeWire(); + return error.RoceUnavailable; + } + errdefer r.closeWire(); + // 2. the verbs connection records, through the bootstrap + const connected = r.connectWire(b) catch |e| blk: { + log.warn("[tensorfold] tp roce: rank {d} connect failed: {t}", .{ b.rank, e }); + break :blk false; + }; + if (!try agreeAll(b, connected, "RoCE connect")) return error.RoceUnavailable; + // 3. the device side + if (d) |drv| { + const dev_ok = r.openDevice(drv, image) catch |e| blk: { + log.warn("[tensorfold] tp roce: rank {d} device setup failed: {t}", .{ b.rank, e }); + break :blk false; + }; + if (!try agreeAll(b, dev_ok, "RoCE device setup")) { + if (dev_ok) r.closeDevice(); + return error.RoceUnavailable; + } + } + r.running.store(true, .release); + r.thread = try std.Thread.spawn(.{}, proxyLoop, .{r}); + try b.agree("proxy", "RoCE proxy start"); + return r; + } + + fn agreeAll(b: *Bootstrap, ok: bool, _: []const u8) !bool { + var blobs: [2][]const u8 = undefined; + var buf: [2]u8 = undefined; + const all = try b.allGather(&.{@intFromBool(ok)}, &buf, &blobs); + return all[0][0] == 1 and all[1][0] == 1; + } + + fn openWire(r: *Roce, b: *Bootstrap, o: Options, len: usize) !bool { + switch (o.wire) { + .verbs => { + r.mapping = try posix.mmap(null, len, .{ .READ = true, .WRITE = true }, .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, -1, 0); + r.region = r.mapping; + errdefer posix.munmap(r.mapping); + try rv.prepareRegion(r.mapping); + r.wire = .{ .verbs = try rv.VerbsWire.open(o.hcas, r.region, .{ .send = send_off, .recv = send_off + 2 * r.slot_bytes, .flags = flag_off, .slot = r.slot_bytes }, o.traffic_class) }; + }, + .shm => { + r.mapping = try shm.sharedPair(b, len); + r.region = @alignCast(r.mapping[r.me * len ..][0..len]); + const layout: guard.Layout = .{ .send = send_off, .recv = send_off + 2 * r.slot_bytes, .flags = flag_off, .slot = r.slot_bytes, .len = len }; + r.wire = .{ .shm = .{ .peer = r.mapping[(1 - r.me) * len ..][0..len], .layout = layout, .local = r.region, .stripes = o.shm_stripes, .tap = o.tap } }; + }, + } + return true; + } + + fn connectWire(r: *Roce, b: *Bootstrap) !bool { + switch (r.wire) { + .shm => return true, + .verbs => |*v| { + const mine = v.record(); + var blobs: [2][]const u8 = undefined; + var buf: [2 * @sizeOf(rv.Record)]u8 = undefined; + const all = try b.allGather(std.mem.asBytes(&mine), &buf, &blobs); + var peer: rv.Record = undefined; + if (all[1 - r.me].len != @sizeOf(rv.Record)) return error.BadRecord; + @memcpy(std.mem.asBytes(&peer), all[1 - r.me]); + try v.connect(peer); + var line: [512]u8 = undefined; + log.info("[tensorfold] tp roce: rank {d} registered {s}", .{ r.me, v.describe(&line) }); + return true; + }, + } + } + + fn openDevice(r: *Roce, d: *const cuda.Driver, image: []const u8) !bool { + if (image.len == 0) return error.NoKernel; + r.d = d; + r.hr = try HostRegister.open(); + try d.check(r.hr.?.register(r.region.ptr, r.region.len, 1 | 2), "cuMemHostRegister"); // PORTABLE | DEVICEMAP + try d.check(d.api.cuMemHostGetDevicePointer_v2(&r.dev, r.region.ptr, 0), "cuMemHostGetDevicePointer"); + r.state = try cuda.DeviceBuffer.alloc(d, 16); + try r.state.?.fill8(0, null); + r.module = try cuda.Module.load(d, image); + r.func = try r.module.?.function("tp_roce"); + r.chunk_abi = if (r.module.?.function("tp_roce_chunk_abi")) |_| true else |_| false; + return true; + } + + fn closeDevice(r: *Roce) void { + if (r.module) |*m| m.unload(); + if (r.state) |*s| s.free(); + if (r.hr) |*h| { + _ = h.unregister(r.region.ptr); + h.lib.close(); + } + r.module = null; + r.state = null; + r.hr = null; + } + + fn closeWire(r: *Roce) void { + switch (r.wire) { + .verbs => |*v| v.close(), + .shm => {}, + } + posix.munmap(r.mapping); + } + + /// Regions a failed transport left registered and mapped (the process exit is their only teardown). + pub var kept: std.atomic.Value(u32) = .init(0); + + /// HCAs and GPU quiet, then unregister and unmap; a failed transport's region is kept until exit. + pub fn deinit(r: *Roce) void { + const was_failed = r.failed(); + r.word(w_abort).store(1, .release); + r.running.store(false, .release); + if (r.thread) |t| t.join(); + const quiet = r.quiesce(); + if (was_failed or !quiet) { + const why = if (was_failed) "failed" else "did not quiesce"; + log.warn("[tensorfold] tp roce: rank {d}: the transport {s}; region 0x{x} ({d} B) kept registered until exit", .{ r.me, why, @intFromPtr(r.region.ptr), r.region.len }); + _ = kept.fetchAdd(1, .monotonic); + return; + } + r.closeDevice(); + r.closeWire(); + std.heap.page_allocator.destroy(r); + } + + /// The QPs to error with every posted request reaped, and the GPU's work on the region done. + fn quiesce(r: *Roce) bool { + const wire = switch (r.wire) { + .verbs => |*v| v.quiesce(rv.quiesce_ns), + .shm => true, + }; + const gpu = if (r.d) |d| d.api.cuCtxSynchronize() == cuda.abi.success else true; + var line: [384]u8 = undefined; + log.info("[tensorfold] tp roce: rank {d} quiesced ({s}): {s}; GPU sync {s}", .{ r.me, if (wire) "drained" else "NOT drained", r.describeLink(&line), if (gpu) "done" else "FAILED" }); + return wire and gpu; + } + + /// Each HCA's QP state, posts and completions, and what a drain reaped (tf-tp-test's idle check; shm: none). + pub fn describeLink(r: *const Roce, buf: []u8) []const u8 { + return switch (r.wire) { + .verbs => |*v| v.report(buf), + .shm => "shm wire", + }; + } + + fn word(r: *const Roce, i: usize) *std.atomic.Value(u32) { + return @ptrCast(@alignCast(r.region[ctrl_off + 4 * i ..].ptr)); + } + + fn fail(r: *Roce, comptime fmt: []const u8, args: anytype) void { + const s = std.fmt.bufPrint(&r.err_buf, fmt, args) catch "proxy failure"; + r.err_len = s.len; + log.err("[tensorfold] tp roce proxy: {s}", .{s}); + r.word(w_proxy_failed).store(1, .release); + r.word(w_abort).store(1, .release); // waits on this rank end now instead of at the timeout + r.halt(); + } + + /// The HCAs stop: nothing of this rank's goes out and none of the peer's writes lands while the ranks go down. + fn halt(r: *Roce) void { + switch (r.wire) { + .verbs => |*v| { + const ok = v.halt(); + var line: [384]u8 = undefined; + log.warn("[tensorfold] tp roce: rank {d} halted{s}: {s}", .{ r.me, if (ok) "" else " (a QP refused ERR)", v.report(&line) }); + }, + .shm => {}, + } + } + + /// Every post of this rank's completed (shm: nothing is ever in flight). + pub fn linkIdle(r: *const Roce) bool { + return switch (r.wire) { + .verbs => |*v| v.idle(), + .shm => true, + }; + } + + /// tf-tp-test's `failfast RANK transport`: the proxy's failure path (`fail`), here with no op in flight. + pub fn inject(r: *Roce) void { + r.fail("failure injected by tf-tp-test (no RDMA fault)", .{}); + } + + /// The transport failed or was aborted: a device may still write to its region. + pub fn failed(r: *const Roce) bool { + if (r.aborted.load(.acquire) or r.word(w_abort).load(.acquire) != 0) return true; + return r.word(w_proxy_failed).load(.acquire) != 0 or r.word(w_failed).load(.acquire) != 0; + } + + /// The proxy loop: spin on the doorbell, post each new sequence (at most two pending), reap while idle, then nap. + fn proxyLoop(r: *Roce) void { + if (r.cpu) |cpu| pin(cpu); + var idle: u64 = 0; + while (r.running.load(.monotonic)) { + if (r.word(w_abort).load(.monotonic) != 0) return; + const seq = r.word(w_seq).load(.acquire); + if (seq == r.last_seq) { + idle += 1; + if (idle % 64 == 0) r.wire.reap() catch |e| return r.fail("completion: {t}", .{e}); + if (idle >= idle_spins) sock.sleepNs(20 * std.time.ns_per_us) else std.atomic.spinLoopHint(); + continue; + } + idle = 0; + var pending = seq -% r.last_seq; + if (pending > 2) return r.fail("doorbell skipped {d} ops (posted {d}, now {d})", .{ pending, r.last_seq, seq }); + var s = r.last_seq +% 1; + while (pending > 0) : ({ + s +%= 1; + pending -= 1; + }) { + const padded = r.word(w_slot_bytes + (s & 1)).load(.acquire); + if (padded == 0 or padded % 16 != 0 or padded > r.slot_bytes) return r.fail("op {d} of {d} bytes", .{ s, padded }); + r.wire.post(s & 1, s, padded) catch |e| return r.fail("post of op {d}: {t}", .{ s, e }); + r.last_seq = s; + } + r.wire.reap() catch |e| return r.fail("completion: {t}", .{e}); + } + } + + pub fn fits(r: *const Roce, n: usize) bool { + return n <= r.slot_bytes; + } + + fn launch(r: *Roce, mode: Mode, in: DevicePtr, out: DevicePtr, n: usize, lens: DevicePtr, stream: collective.Stream) Error!void { + return r.launchAt(mode, in, out, n, lens, 0, stream); + } + + fn launchAt(r: *Roce, mode: Mode, in: DevicePtr, out: DevicePtr, n: usize, lens: DevicePtr, chunk: u64, stream: collective.Stream) Error!void { + if (r.aborted.load(.acquire)) return error.Aborted; + if (n > r.slot_bytes and (lens == 0 or !r.chunk_abi)) return error.Unsupported; + if (n == 0) return; + const d = r.d orelse return error.Unsupported; + var a: RoceArgs = .{ + .in = in, + .out = out, + .send_slots = r.dev + send_off, + .recv_slots = r.dev + send_off + 2 * r.slot_bytes, + .flags = r.dev + flag_off, + .ctrl = r.dev + ctrl_off, + .state = r.state.?.ptr, + .nbytes = n, + .slot_bytes = r.slot_bytes, + .timeout_ns = r.timeout_ns, + .rank = r.me, + .mode = @backingInt(mode), + .n_hca = r.wire.hcas(), + .opts = r.fast, + .lens = lens, + .chunk = chunk, + }; + var params = [1]?*anyopaque{@ptrCast(&a)}; + const blocks = @min(@import("mailbox.zig").blocksFor(@min(n, r.slot_bytes)), r.max_grid); + d.check(d.api.cuLaunchKernel(r.func.?.handle, blocks, 1, 1, threads, 1, 1, 0, stream, ¶ms, null), "cuLaunchKernel tp_roce") catch return error.BackendFailed; + } + + pub fn allGatherBytes(r: *Roce, s: DevicePtr, o: DevicePtr, n: usize, stream: collective.Stream) Error!void { + return r.launch(.gather, s, o, n, 0, stream); + } + + /// `allGatherBytes` where rank r posts only lens[r] bytes (device int32 [2], padded to 16, at least 16). + pub fn allGatherV(r: *Roce, s: DevicePtr, o: DevicePtr, n: usize, lens: DevicePtr, stream: collective.Stream) Error!void { + if (n > r.slot_bytes) return error.Unsupported; // one op: past a slot is allGatherVChunked's + if (lens == 0) return error.Invalid; + return r.launch(.gather, s, o, n, lens, stream); + } + + /// `allGatherV` past the slot: op c moves at most a slot of each shard from c x slot (empty on both ranks: no op). + pub fn allGatherVChunked(r: *Roce, s: DevicePtr, o: DevicePtr, n: usize, lens: DevicePtr, stream: collective.Stream) Error!void { + if (lens == 0) return error.Invalid; + if (!r.chunk_abi) return error.Unsupported; + var off: usize = 0; + while (off < n) : (off += r.slot_bytes) try r.launchAt(.gather, s, o, n, lens, off, stream); + } + + pub fn exchangeBytes(r: *Roce, s: DevicePtr, o: DevicePtr, n: usize, stream: collective.Stream) Error!void { + return r.launch(.exchange, s, o, n, 0, stream); + } + + pub fn sum(r: *Roce, s: DevicePtr, o: DevicePtr, count: usize, t: DType, stream: collective.Stream) Error!void { + const mode: Mode = switch (t) { + .f32 => .sum_f32, + .bf16 => .sum_bf16, + else => return error.Unsupported, + }; + return r.launch(mode, s, o, count * t.size(), 0, stream); + } + + /// Ends this rank's waits and the proxy (the peer learns through the fate channel and aborts its own). + pub fn abort(r: *Roce) void { + r.aborted.store(true, .release); + r.word(w_abort).store(1, .release); + r.halt(); + } + + pub fn check(r: *Roce) Error!void { + if (r.word(w_failed).load(.acquire) != 0) { + log.warn("tp roce: rank {d}'s wait for seq {d} failed ({s} after {d} us; HCA {d}, flag held {d}); doorbell {d}, posted {d}, completed {d}, abort {d}, proxy failed {d}, flags {d} {d}", .{ r.me, r.word(w_failed + 1).load(.acquire), if (r.word(w_failed + 4).load(.acquire) == 1) "aborted" else "timed out", r.word(w_failed + 5).load(.acquire), r.word(w_failed + 3).load(.acquire), r.word(w_failed + 2).load(.acquire), r.word(w_seq).load(.acquire), r.last_seq, r.word(w_completed).load(.acquire), r.word(w_abort).load(.acquire), r.word(w_proxy_failed).load(.acquire), r.flagWord(0), r.flagWord(1) }); + return error.PeerFailed; + } + if (r.word(w_proxy_failed).load(.acquire) != 0) return error.BackendFailed; + if (r.aborted.load(.acquire) or r.word(w_abort).load(.acquire) != 0) return error.Aborted; + } + + fn flagWord(r: *const Roce, slot: usize) u32 { + const p: *std.atomic.Value(u32) = @ptrCast(@alignCast(r.region[flag_off + slot * 2 * flag_stride ..].ptr)); + return p.load(.acquire); + } + + pub fn proxyError(r: *const Roce) []const u8 { + return r.err_buf[0..r.err_len]; + } + + /// The kernel's side of one op done by the host (tests, `roce-host`): a gather through the same slots and flags. + pub fn hostGather(r: *Roce, in: []const u8, out: []u8) Error!void { + return r.hostGatherV(in, out, null); + } + + /// `hostGather` with variable lengths (`allGatherV`): rank k's first lens[k] bytes land at out + k x n. + pub fn hostGatherV(r: *Roce, in: []const u8, out: []u8, lens: ?[2]u32) Error!void { + if (in.len > r.slot_bytes) return error.Invalid; + return r.hostGatherAt(in, out, lens, 0); + } + + /// `allGatherVChunked`'s ops done by the host (the kernel's side of each, as `hostGatherV`). + pub fn hostGatherVChunked(r: *Roce, in: []const u8, out: []u8, lens: [2]u32) Error!void { + var off: usize = 0; + while (off < in.len) : (off += r.slot_bytes) try r.hostGatherAt(in, out, lens, off); + } + + /// One op from shard offset `off`: each rank's bytes past it, at most a slot; an op empty on both ranks is skipped. + fn hostGatherAt(r: *Roce, in: []const u8, out: []u8, lens: ?[2]u32, off: usize) Error!void { + const n = in.len; + if (n == 0 or out.len < 2 * n) return error.Invalid; + if (lens) |l| if (l[0] > n or l[1] > n) return error.Invalid; + const full: [2]usize = if (lens) |l| .{ l[0], l[1] } else .{ n, n }; + const mine: usize = @min(full[r.me] -| off, r.slot_bytes); + const theirs: usize = @min(full[1 - r.me] -| off, r.slot_bytes); + if (off > 0 and mine == 0 and theirs == 0) return; + const seq = r.host_epoch +% 1; + const slot: usize = seq & 1; + @memcpy(r.region[send_off + slot * r.slot_bytes ..][0..mine], in[off..][0..mine]); + r.word(w_slot_bytes + slot).store(@intCast(@max(16, std.mem.alignForward(usize, mine, 16))), .release); + r.word(w_seq).store(seq, .release); + @memcpy(out[r.me * n + off ..][0..mine], in[off..][0..mine]); + const t0 = sock.nowNs(); + for (0..r.wire.hcas()) |h| { + const flag: *std.atomic.Value(u32) = @ptrCast(@alignCast(r.region[flag_off + (slot * 2 + h) * flag_stride ..].ptr)); + while (flag.load(.acquire) != seq) { + if (r.word(w_abort).load(.monotonic) != 0) return error.Aborted; + if (sock.nowNs() - t0 > r.timeout_ns) return error.Timeout; + std.atomic.spinLoopHint(); + } + } + @memcpy(out[(1 - r.me) * n + off ..][0..theirs], r.region[send_off + 2 * r.slot_bytes + slot * r.slot_bytes ..][0..theirs]); + r.host_epoch = seq; + } +}; + +fn pin(cpu: u32) void { + var set: std.os.linux.cpu_set_t = @splat(0); + set[cpu / @bitSizeOf(usize)] |= @as(usize, 1) << @intCast(cpu % @bitSizeOf(usize)); + std.os.linux.sched_setaffinity(0, &set) catch log.warn("tp roce: could not pin the proxy to CPU {d}", .{cpu}); +} diff --git a/zig/src/fabric/tp/roce_fake.zig b/zig/src/fabric/tp/roce_fake.zig new file mode 100644 index 0000000000..c316448ef8 --- /dev/null +++ b/zig/src/fabric/tp/roce_fake.zig @@ -0,0 +1,143 @@ +//! Fake verbs for host tests of VerbsWire: each QP's posted writes counted from the requests, completions that lag. +const std = @import("std"); +const verbs = @import("verbs"); +const abi = verbs.abi; +const rv = @import("roce_verbs.zig"); + +pub const slot: u64 = 1 << 20; +pub const send_off = 8192; + +/// What the HCAs hold (posted, not completed); every count comes from the work requests themselves. +pub const Fake = struct { + pub var region: []const u8 = &.{}; + /// The wire's slot (`Hw.wire`), every data write's bound with the send slots. + pub var slot_bytes: u64 = slot; + pub var lag: u32 = 1; + pub var polls: u64 = 0; + pub var queue: [2][512]u64 = undefined; + pub var head: [2]u32 = .{ 0, 0 }; + pub var len: [2]u32 = .{ 0, 0 }; + pub var inflight: u64 = 0; + pub var max_inflight: u64 = 0; + pub var max_write: u64 = 0; + pub var outside: u64 = 0; + pub var bytes: u64 = 0; + pub var posts: u64 = 0; + + pub fn reset(l: u32) void { + lag = l; + polls = 0; + head = .{ 0, 0 }; + len = .{ 0, 0 }; + inflight = 0; + max_inflight = 0; + max_write = 0; + outside = 0; + bytes = 0; + posts = 0; + } + + pub fn postSend(qp: *abi.Qp, wr: *abi.SendWr, _: *?*abi.SendWr) callconv(.c) c_int { + const q = qp.qp_num; + posts += 1; + var sum: u64 = 0; + var at: ?*abi.SendWr = wr; + while (at) |w| : (at = w.next) { + if (w.send_flags & abi.send_inline == 0) { + const sge = w.sg_list[0]; + const lo = @intFromPtr(region.ptr); + if (sge.addr < lo + send_off or sge.addr + sge.length > lo + send_off + 2 * slot_bytes) outside += 1; + max_write = @max(max_write, sge.length); + sum += sge.length; + } + if (w.send_flags & abi.send_signaled != 0) { + queue[q][(head[q] + len[q]) % 512] = sum; + len[q] += 1; + inflight += sum; + bytes += sum; + max_inflight = @max(max_inflight, inflight); + sum = 0; + } + } + return 0; + } + + /// One completion every `lag` polls of a QP's CQ: the HCA finishes writes far slower than they are posted. + pub fn pollCq(cq: *abi.Cq, _: c_int, wc: [*]abi.Wc) callconv(.c) c_int { + const q = cq.handle; + polls += 1; + if (polls % lag != 0 or len[q] == 0) return 0; + inflight -= queue[q][head[q]]; + head[q] = (head[q] + 1) % 512; + len[q] -= 1; + wc[0] = std.mem.zeroes(abi.Wc); + wc[0].status = abi.wc_success; + return 1; + } +}; + +/// QPs that complete nothing on their own: a QP moved to error flushes what it holds, as an HCA reports it. +pub const Halts = struct { + pub var halted: [2]bool = .{ false, false }; + pub var flushed_before_halt: u32 = 0; + pub var flushes: u32 = 0; + + pub fn reset() void { + halted = .{ false, false }; + flushed_before_halt = 0; + flushes = 0; + } + + pub fn modifyQp(qp: *abi.Qp, attr: *abi.QpAttr, mask: c_int) callconv(.c) c_int { + if (attr.qp_state == abi.qps_err and mask == abi.mask.state) halted[qp.qp_num] = true; + return 0; + } + + /// ibv_query_qp as the device answers it: ERR once halted, else RTS. + pub fn queryQp(qp: *abi.Qp, attr: *abi.QpAttr, _: c_int, _: *abi.QpInitAttr) callconv(.c) c_int { + attr.qp_state = if (halted[qp.qp_num]) abi.qps_err else abi.qps_rts; + return 0; + } + + pub fn pollCq(cq: *abi.Cq, _: c_int, wc: [*]abi.Wc) callconv(.c) c_int { + const q = cq.handle; + if (Fake.len[q] == 0 or !halted[q]) return 0; + if (!halted[0] or !halted[1]) flushed_before_halt += 1; + Fake.inflight -= Fake.queue[q][Fake.head[q]]; + Fake.head[q] = (Fake.head[q] + 1) % 512; + Fake.len[q] -= 1; + flushes += 1; + wc[0] = std.mem.zeroes(abi.Wc); + wc[0].status = 5; // IBV_WC_WR_FLUSH_ERR + return 1; + } + + /// Points a wire's QPs at these handlers. + pub fn install(hw: *Hw, w: *rv.VerbsWire) void { + w.v.api.ibv_modify_qp = modifyQp; + w.v.query_qp = queryQp; + for (&hw.ctx) |*c| c.ops.poll_cq = pollCq; + } +}; + +/// Two fake HCAs behind one VerbsWire, connected to a peer whose region has this rank's layout. +pub const Hw = struct { + ctx: [2]abi.Context = undefined, + qp: [2]abi.Qp = undefined, + cq: [2]abi.Cq = undefined, + mr: [2]abi.Mr = undefined, + + pub fn wire(hw: *Hw, region: []u8) rv.VerbsWire { + const s = Fake.slot_bytes; + var w: rv.VerbsWire = .{ .v = undefined, .traffic_class = 0, .recv_off = send_off + 2 * s, .flag_off = 4096, .send_off = send_off, .slot_bytes = s, .region = region }; + for (0..2) |i| { + hw.ctx[i].ops = .{ ._before_poll = @splat(null), .poll_cq = Fake.pollCq, ._before_post = @splat(null), .post_send = Fake.postSend, .post_recv = undefined, ._after = @splat(null) }; + hw.qp[i].qp_num = @intCast(i); + hw.cq[i].handle = @intCast(i); + hw.mr[i].lkey = 7; + w.hcas[i] = .{ .ctx = &hw.ctx[i], .pd = undefined, .mr = &hw.mr[i], .cq = &hw.cq[i], .qp = &hw.qp[i], .gid_index = 0, .gid = @splat(0), .mtu = 0, .lid = 0 }; + } + w.n_hca = 2; + return w; + } +}; diff --git a/zig/src/fabric/tp/roce_guard.zig b/zig/src/fabric/tp/roce_guard.zig new file mode 100644 index 0000000000..8f85247afd --- /dev/null +++ b/zig/src/fabric/tp/roce_guard.zig @@ -0,0 +1,73 @@ +//! Every RoCE post's bounds before the verb: each HCA's stripe inside its slot and the registered region. +const std = @import("std"); + +pub const max_hcas = 2; +pub const flag_stride = 128; + +pub const Error = error{ OutOfRegion, WriteTooLarge }; + +/// The largest RDMA write a post may make, whatever the slot (a 9.6 MB post faulted a DGX Spark's HCA); never raised. +pub const max_write: u32 = 1 << 20; + +/// Both ranks' region layout (roce.zig's): byte offsets and the registered length every post must stay inside. +pub const Layout = struct { send: u64, recv: u64, flags: u64, slot: u64, len: u64 }; + +/// One HCA's share of a post: its data stripe in this rank's send slot and the peer's receive slot, and its flag word. +pub const Stripe = struct { local: u64, remote: u64, len: u32, flag: u64 }; + +/// Slot `slot`'s first `padded` bytes striped over `n_hca` HCAs; error.OutOfRegion before any verb past a slot or MR. +pub fn stripes(l: Layout, n_hca: u32, slot: u32, padded: u32, out: *[max_hcas]Stripe) Error!void { + if (n_hca == 0 or n_hca > max_hcas or slot > 1) return error.OutOfRegion; + if (padded > max_write) return error.WriteTooLarge; + if (padded == 0 or padded % 16 != 0 or padded > l.slot) return error.OutOfRegion; + const packs = padded / 16; + var offset: u64 = 0; + for (out[0..n_hca], 0..) |*o, i| { + var stripe = packs / n_hca; + if (i < packs % n_hca) stripe += 1; + o.* = .{ .local = l.send + slot * l.slot + offset, .remote = l.recv + slot * l.slot + offset, .len = stripe * 16, .flag = l.flags + (@as(u64, slot) * max_hcas + i) * flag_stride }; + if (!within(o.local, o.len, l.send + slot * l.slot, l.slot) or !within(o.remote, o.len, l.recv + slot * l.slot, l.slot)) return error.OutOfRegion; + if (!within(o.flag, 4, l.flags, l.send - l.flags) or !within(o.local, o.len, 0, l.len) or !within(o.remote, o.len, 0, l.len)) return error.OutOfRegion; + offset += o.len; + } +} + +fn within(at: u64, len: u64, lo: u64, size: u64) bool { + return at >= lo and at + len <= lo + size; +} + +/// roce.zig's layout for a 64 KiB slot: ctrl 4 KiB, flags 4 KiB, send [2][slot], recv [2][slot]. +fn testLayout() Layout { + const slot: u64 = 64 << 10; + return .{ .send = 8192, .recv = 8192 + 2 * slot, .flags = 4096, .slot = slot, .len = 8192 + 4 * slot }; +} + +test "a post striped over two HCAs: the stripes tile the slot's first bytes, each flag on its own word" { + const l = testLayout(); + var s: [max_hcas]Stripe = undefined; + try stripes(l, 2, 1, 48, &s); + try std.testing.expectEqual(Stripe{ .local = l.send + l.slot, .remote = l.recv + l.slot, .len = 32, .flag = l.flags + 2 * flag_stride }, s[0]); + try std.testing.expectEqual(Stripe{ .local = l.send + l.slot + 32, .remote = l.recv + l.slot + 32, .len = 16, .flag = l.flags + 3 * flag_stride }, s[1]); + try stripes(l, 2, 1, @intCast(l.slot), &s); + try std.testing.expectEqual(l.recv + 2 * l.slot, s[1].remote + s[1].len); +} + +test "a post past its slot, a third slot, an unpadded or empty post, or no HCA is refused" { + const l = testLayout(); + var s: [max_hcas]Stripe = undefined; + try std.testing.expectError(error.OutOfRegion, stripes(l, 2, 0, @intCast(l.slot + 16), &s)); + try std.testing.expectError(error.OutOfRegion, stripes(l, 2, 2, 16, &s)); + try std.testing.expectError(error.OutOfRegion, stripes(l, 2, 0, 24, &s)); + try std.testing.expectError(error.OutOfRegion, stripes(l, 2, 0, 0, &s)); + try std.testing.expectError(error.OutOfRegion, stripes(l, 0, 0, 16, &s)); + try std.testing.expectError(error.OutOfRegion, stripes(l, 3, 0, 16, &s)); + var big = l; + big.slot = 10 << 20; + big.recv = big.send + 2 * big.slot; + big.len = big.send + 4 * big.slot; + try std.testing.expectError(error.WriteTooLarge, stripes(big, 2, 0, max_write + 16, &s)); + try stripes(big, 2, 0, max_write, &s); + var short = l; + short.len -= 16; + try std.testing.expectError(error.OutOfRegion, stripes(short, 2, 1, @intCast(l.slot), &s)); +} diff --git a/zig/src/fabric/tp/roce_host.zig b/zig/src/fabric/tp/roce_host.zig new file mode 100644 index 0000000000..1dec07e9ba --- /dev/null +++ b/zig/src/fabric/tp/roce_host.zig @@ -0,0 +1,54 @@ +//! `tf-tp-test roce-host`: the RoCE wire and proxy between two hosts without a GPU, checked bit for bit, then timed. +const std = @import("std"); +const tp = @import("tp"); +const roce = tp.roce; +const rv = tp.roce_verbs; + +const sizes = [_]usize{ 16, 4096, 8192, 16384, 65536, 262144 }; + +pub fn run(cfg: tp.Config, gpa: std.mem.Allocator) !u8 { + var b = try tp.Bootstrap.open(cfg); + defer b.close(); + var specs: [rv.max_hcas]rv.HcaSpec = undefined; + var n: usize = 0; + if (cfg.roce_wire == .verbs) n = try tp.roce_pick.resolve(cfg, &b, &specs); + const r = try roce.Roce.init(null, &b, "", .{ .wire = if (cfg.roce_wire == .shm) .shm else .verbs, .hcas = specs[0..n], .traffic_class = cfg.roce_tc, .cpu = cfg.roce_cpu, .max_bytes = 256 * 1024, .timeout_ns = 10 * std.time.ns_per_s }); + defer r.deinit(); + const in = try gpa.alloc(u8, 256 * 1024); + defer gpa.free(in); + const peer = try gpa.alloc(u8, 256 * 1024); + defer gpa.free(peer); + const out = try gpa.alloc(u8, 512 * 1024); + defer gpa.free(out); + // 1. every size from 1 byte to the slot, both slots, bit for bit + var checked: u32 = 0; + for (0..1000) |k| { + const len = 1 + (k * 7919) % (256 * 1024); + tp.reference.fill(in[0..len], .u8, cfg.rank, k); + tp.reference.fill(peer[0..len], .u8, 1 - cfg.rank, k); + try r.hostGather(in[0..len], out[0 .. 2 * len]); + const mine_at = cfg.rank * len; + const peer_at = (1 - cfg.rank) * len; + if (!std.mem.eql(u8, out[mine_at..][0..len], in[0..len]) or !std.mem.eql(u8, out[peer_at..][0..len], peer[0..len])) { + std.debug.print("FAIL roce-host gather {d} of {d} bytes\n", .{ k, len }); + return 1; + } + checked += 1; + } + try r.check(); + std.debug.print("PASS roce-host: {d} gathers of 1 B to 256 KiB bit-exact over {s}\n", .{ checked, @tagName(cfg.roce_wire) }); + // 2. one exchange's host-to-host time by size: median and p90 of 2,000 back-to-back ops + var times: [2000]u64 = undefined; + for (sizes) |len| { + for (×) |*t| { + const t0 = tp.sock.nowNs(); + try r.hostGather(in[0..len], out[0 .. 2 * len]); + t.* = tp.sock.nowNs() - t0; + } + std.mem.sort(u64, ×, {}, std.sort.asc(u64)); + if (cfg.rank == 0) std.debug.print("RESULT {{\"backend\":\"roce-host\",\"wire\":\"{s}\",\"bytes\":{d},\"p50_us\":{d:.2},\"p90_us\":{d:.2}}}\n", .{ @tagName(cfg.roce_wire), len, @as(f64, @floatFromInt(times[1000])) / 1e3, @as(f64, @floatFromInt(times[1800])) / 1e3 }); + } + try r.check(); + try b.agree("done", "roce-host end"); + return 0; +} diff --git a/zig/src/fabric/tp/roce_inflight_test.zig b/zig/src/fabric/tp/roce_inflight_test.zig new file mode 100644 index 0000000000..fd7be53262 --- /dev/null +++ b/zig/src/fabric/tp/roce_inflight_test.zig @@ -0,0 +1,110 @@ +//! VerbsWire on fake verbs whose completions lag: what it holds in flight and what a failed transport leaves behind. +const std = @import("std"); +const rv = @import("roce_verbs.zig"); +const fake = @import("roce_fake.zig"); +const Roce = @import("roce.zig").Roce; +const Fake = fake.Fake; +const Halts = fake.Halts; +const slot = fake.slot; + +test "chunked posts: the wire's own in-flight count and peak match the HCAs', and reach the 2 MiB cap" { + const region = try std.testing.allocator.alignedAlloc(u8, .fromByteUnits(4096), Roce.regionBytes(slot)); + defer std.testing.allocator.free(region); + Fake.region = region; + Fake.slot_bytes = slot; + Fake.reset(3); + var hw: fake.Hw = .{}; + var w = hw.wire(region); + for (1..9) |seq| { + try w.post(@intCast(seq & 1), @intCast(seq), slot); + try std.testing.expectEqual(Fake.inflight, w.inflight()); + try std.testing.expect(w.inflight() <= rv.max_inflight); + } + while (w.inflight() > 0) try w.reap(); + try std.testing.expectEqual(@as(u64, 0), Fake.inflight); + try std.testing.expectEqual(Fake.max_inflight, w.peak.load(.monotonic)); + try std.testing.expectEqual(@as(u64, 2 << 20), w.peak.load(.monotonic)); +} + +test "a failed transport: both QPs go to error before the first flush and every posted write is reaped" { + const region = try std.testing.allocator.alignedAlloc(u8, .fromByteUnits(4096), Roce.regionBytes(slot)); + defer std.testing.allocator.free(region); + @memset(region, 0); + Fake.region = region; + Fake.slot_bytes = slot; + Fake.reset(1); + Halts.reset(); + var hw: fake.Hw = .{}; + var w = hw.wire(region); + Halts.install(&hw, &w); + // two 1 MiB ops posted, neither completed: 2 MiB in flight at the HCAs, the link busy + try std.testing.expect(w.idle()); + try w.post(1, 1, slot); + try w.post(0, 2, slot); + try std.testing.expectEqual(@as(u64, 2 * slot), w.inflight()); + try std.testing.expect(!w.idle()); + try std.testing.expect(w.quiesce(std.time.ns_per_s)); + try std.testing.expect(Halts.halted[0] and Halts.halted[1]); + try std.testing.expectEqual(@as(u32, 0), Halts.flushed_before_halt); + try std.testing.expectEqual(@as(u32, 4), Halts.flushes); + try std.testing.expectEqual(@as(u64, 0), w.inflight()); + try std.testing.expectEqual(@as(u64, 0), Fake.inflight); + // the line tf-tp-test's transport run reads: the device's states and every reap, all of them flushes + var line: [256]u8 = undefined; + try std.testing.expect(w.idle()); + try std.testing.expectEqualStrings("HCA 0 QP ERR; posted 2, completed 2; HCA 1 QP ERR; posted 2, completed 2; reaped 4 (flushed 4), still posted 0", w.report(&line)); + // an aborted transport reports itself failed: its deinit keeps the region + Halts.reset(); + var r: Roce = .{ .me = 0, .slot_bytes = slot, .timeout_ns = 1, .region = region, .mapping = region, .wire = .{ .verbs = w }, .cpu = null }; + try std.testing.expect(!r.failed()); + r.abort(); + try std.testing.expect(Halts.halted[0] and Halts.halted[1]); + try std.testing.expect(r.failed()); +} + +test "a QP whose inline capacity cannot take the 4-byte flag post is refused" { + try std.testing.expect(!rv.inlineFits(.{ .max_send_wr = 1, .max_recv_wr = 1, .max_send_sge = 1, .max_recv_sge = 1, .max_inline_data = 3 })); + try std.testing.expect(rv.inlineFits(.{ .max_send_wr = 1, .max_recv_wr = 1, .max_send_sge = 1, .max_recv_sge = 1, .max_inline_data = 4 })); +} + +/// /proc/self/smaps' VmFlags of the mapping that contains `at`. +fn vmFlags(at: usize, buf: []u8) ![]const u8 { + const fd = std.c.open("/proc/self/smaps", .{}); + if (fd < 0) return error.NoSmaps; + defer _ = std.c.close(fd); + var n: usize = 0; + while (n < buf.len) { + const got = std.c.read(fd, buf[n..].ptr, buf.len - n); + if (got <= 0) break; + n += @intCast(got); + } + var lines = std.mem.splitScalar(u8, buf[0..n], '\n'); + var inside = false; + while (lines.next()) |l| { + const sp = std.mem.indexOfScalar(u8, l, ' ') orelse l.len; + if (std.mem.indexOfScalar(u8, l[0..sp], '-')) |dash| { + const lo = std.fmt.parseInt(usize, l[0..dash], 16) catch continue; + const hi = std.fmt.parseInt(usize, l[dash + 1 .. sp], 16) catch continue; + inside = lo <= at and at < hi; + } + if (inside and std.mem.startsWith(u8, l, "VmFlags:")) return l; + } + return error.NotFound; +} + +test "the region is DONTFORK and NOHUGEPAGE before registration" { + if (@import("builtin").os.tag != .linux) return error.SkipZigTest; + const len = Roce.regionBytes(slot); + const m = try std.posix.mmap(null, len, .{ .READ = true, .WRITE = true }, .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, -1, 0); + defer std.posix.munmap(m); + const buf = try std.testing.allocator.alloc(u8, 4 << 20); + defer std.testing.allocator.free(buf); + // smaps: dc = MADV_DONTFORK, nh = MADV_NOHUGEPAGE + const before = try vmFlags(@intFromPtr(m.ptr), buf); + try std.testing.expect(std.mem.indexOf(u8, before, " dc") == null and std.mem.indexOf(u8, before, " nh") == null); + try rv.prepareRegion(m); + // An interior page stays inside the VMA even when a neighbouring mapping merges with it. + const flags = try vmFlags(@intFromPtr(m.ptr) + 4096, buf); + try std.testing.expect(std.mem.indexOf(u8, flags, " dc") != null); + try std.testing.expect(std.mem.indexOf(u8, flags, " nh") != null); +} diff --git a/zig/src/fabric/tp/roce_pick.zig b/zig/src/fabric/tp/roce_pick.zig new file mode 100644 index 0000000000..d1ed7a6529 --- /dev/null +++ b/zig/src/fabric/tp/roce_pick.zig @@ -0,0 +1,448 @@ +//! Which RoCE devices a rank stripes over: TF_TP_ROCE_HCA, else the usable ports on an IPv4 /24 the peer also has. +const std = @import("std"); +const log = @import("log.zig"); +const rv = @import("roce_verbs.zig"); +const bootstrap = @import("bootstrap.zig"); +const sock = @import("sock.zig"); +const Config = @import("config.zig").Config; +const Verbs = @import("verbs").Verbs; +const OpenFn = *const fn (?[:0]const u8) @import("verbs").Error!Verbs; + +/// Ports a rank lists to its peers (a DGX Spark's ConnectX-7 shows four RDMA devices). +pub const max_ports = 8; +const blob_bytes = 1 + 4 * max_ports; + +/// How many devices, and how: on a /24 the peer shares (`shared`), named by TF_TP_ROCE_HCA (`explicit`), or neither. +pub const Pick = struct { + n: usize, + shared: bool, + explicit: bool = false, + verbs_open_error: ?@import("verbs").Error = null, +}; + +fn usable(p: rv.Port) bool { + return p.active and p.ethernet and p.ipv4 != null; +} + +fn subnet(a: [4]u8) u32 { + return @as(u32, a[0]) << 16 | @as(u32, a[1]) << 8 | a[2]; +} + +fn countIn(list: []const [4]u8, net: u32) usize { + var k: usize = 0; + for (list) |a| k += @intFromBool(subnet(a) == net); + return k; +} + +/// Usable ports on a /24 the peers list, by subnet, each /24 as often as both sides have it; else the first usable. +pub fn choose(ports: []const rv.Port, peer: []const [4]u8, out: *[rv.max_hcas]rv.HcaSpec, limit: usize) Pick { + const cap = @min(limit, rv.max_hcas); + var order: [max_ports]usize = undefined; + var m: usize = 0; + for (ports[0..@min(ports.len, max_ports)], 0..) |p, i| if (usable(p)) { + order[m] = i; + m += 1; + }; + var n: usize = 0; + for (order[0..m]) |i| n += @intFromBool(countIn(peer, subnet(ports[i].ipv4.?)) > 0); + if (n == 0) { + for (order[0..@min(m, cap)], 0..) |i, k| out[k] = ports[i].spec; + return .{ .n = @min(m, cap), .shared = false }; + } + // stable insertion sort by subnet (at most max_ports entries) + for (1..m) |a| { + var j = a; + while (j > 0 and subnet(ports[order[j - 1]].ipv4.?) > subnet(ports[order[j]].ipv4.?)) : (j -= 1) { + std.mem.swap(usize, &order[j - 1], &order[j]); + } + } + n = 0; + var taken: [max_ports]u32 = undefined; + for (order[0..m]) |i| { + if (n == cap) break; + const net = subnet(ports[i].ipv4.?); + var mine: usize = 0; + for (taken[0..n]) |t| mine += @intFromBool(t == net); + if (mine >= countIn(peer, net)) continue; + taken[n] = net; + out[n] = ports[i].spec; + n += 1; + } + return .{ .n = n, .shared = true }; +} + +/// `[count][count x IPv4]` of the usable ports, the blob each rank sends. +fn encode(ports: []const rv.Port, out: *[blob_bytes]u8) []const u8 { + var n: u8 = 0; + for (ports[0..@min(ports.len, max_ports)]) |p| if (usable(p)) { + out[1 + 4 * @as(usize, n) ..][0..4].* = p.ipv4.?; + n += 1; + }; + out[0] = n; + return out[0 .. 1 + 4 * @as(usize, n)]; +} + +/// A peer's blob into `out`; a malformed one counts as no ports. +fn decode(blob: []const u8, out: [][4]u8) usize { + if (blob.len == 0 or blob[0] > max_ports or blob.len != 1 + 4 * @as(usize, blob[0]) or out.len < blob[0]) return 0; + for (0..blob[0]) |k| out[k] = blob[1 + 4 * k ..][0..4].*; + return blob[0]; +} + +/// The devices for the verbs wire. A collective: every rank calls it, including ranks that set TF_TP_ROCE_HCA. +pub fn resolve(cfg: Config, b: *bootstrap.Bootstrap, out: *[rv.max_hcas]rv.HcaSpec) !usize { + return (try select(cfg, b, out)).n; +} + +/// `resolve` with how the devices were chosen, over this host's ports (a collective, as `resolve`). +pub fn select(cfg: Config, b: *bootstrap.Bootstrap, out: *[rv.max_hcas]rv.HcaSpec) !Pick { + return selectWithOpen(cfg, b, out, Verbs.open); +} + +fn selectWithOpen(cfg: Config, b: *bootstrap.Bootstrap, out: *[rv.max_hcas]rv.HcaSpec, open: OpenFn) !Pick { + var ports: [max_ports]rv.Port = undefined; + var n_ports: usize = 0; + var open_error: ?@import("verbs").Error = null; + if (open(null)) |opened| { + var v = opened; + defer v.close(); + n_ports = rv.scanPorts(&v, &ports); + } else |e| { + open_error = e; + } + var pick = try selectFrom(cfg, b, ports[0..n_ports], out); + pick.verbs_open_error = open_error; + return pick; +} + +/// `select` over given ports (the scan's, or a test's fixture ports). +pub fn selectFrom(cfg: Config, b: *bootstrap.Bootstrap, ports: []const rv.Port, out: *[rv.max_hcas]rv.HcaSpec) !Pick { + var mine: [blob_bytes]u8 = undefined; + var buf: [bootstrap.max_ranks * blob_bytes]u8 = undefined; + var blobs: [bootstrap.max_ranks][]const u8 = undefined; + const all = try b.allGather(encode(ports, &mine), &buf, &blobs); + if (cfg.roce_hca) |text| return .{ .n = try rv.parseSpecs(text, out), .shared = false, .explicit = true }; + var peer: [bootstrap.max_ranks * max_ports][4]u8 = undefined; + var n_peer: usize = 0; + for (all, 0..) |blob, r| if (r != b.rank) { + n_peer += decode(blob, peer[n_peer..]); + }; + const pick = choose(ports, peer[0..n_peer], out, cfg.roce_hcas); + if (!pick.shared and pick.n > 0) log.warn("tp roce: no RoCE port shares an IPv4 /24 with the peer's; using the first {d} active (set TF_TP_ROCE_HCA)", .{pick.n}); + return pick; +} + +/// Device names joined by commas, with each `:gid` when `gid` (TF_TP_ROCE_HCA's form) or without (NCCL_IB_HCA's). +pub fn joinNames(specs: []const rv.HcaSpec, gid: bool, buf: []u8) error{WriteFailed}![]const u8 { + var w: std.Io.Writer = .fixed(buf); + for (specs, 0..) |s, i| { + try w.print("{s}{s}", .{ if (i == 0) "" else ",", s.name[0..s.name_len] }); + if (gid) if (s.gid) |g| try w.print(":{d}", .{g}); + } + return w.buffered(); +} + +/// Room for `joinNames` of every device a rank stripes over (a name is under 64 bytes, a GID index at most 3 digits). +pub const list_bytes = rv.max_hcas * (64 + 4); + +pub const HcaError = error{ + /// no active Ethernet port with an IPv4 RoCE v2 GID on this host + NoRoceDevice, + /// this host's ports share no IPv4 /24 with the peer's: NCCL would not reach the peer on them either + NoSharedSubnet, +} || @import("verbs").Error; + +/// `tf-tp-test hca`'s line for run_hosts.sh: `NCCL_IB_HCA=` the picked devices; never the first local ports alone. +pub fn hcaLine(pick: Pick, specs: []const rv.HcaSpec, buf: *[list_bytes + 16]u8) HcaError![]const u8 { + if (pick.n == 0) { + if (pick.verbs_open_error) |e| return e; + return error.NoRoceDevice; + } + if (!pick.shared and !pick.explicit) return error.NoSharedSubnet; + const prefix = "NCCL_IB_HCA="; + @memcpy(buf[0..prefix.len], prefix); + const joined = joinNames(specs[0..pick.n], false, buf[prefix.len .. buf.len - 1]) catch unreachable; // list_bytes holds them + buf[prefix.len + joined.len] = '\n'; + return buf[0 .. prefix.len + joined.len + 1]; +} + +fn port(name: []const u8, active: bool, ipv4: ?[4]u8) rv.Port { + var p: rv.Port = .{ .active = active, .ethernet = true, .ipv4 = ipv4 }; + p.spec.name_len = @intCast(name.len); + @memcpy(p.spec.name[0..name.len], name); + p.spec.gid = 3; + return p; +} + +fn names(specs: []const rv.HcaSpec, buf: []u8) []const u8 { + return joinNames(specs, false, buf) catch unreachable; +} + +// A host with two neighbours: port f0 cabled to one Spark (both functions on one /24), port f1 to the other. +const a_ports = [_]rv.Port{ + port("rocep1s0f0", true, .{ 203, 0, 113, 1 }), + port("rocep1s0f1", true, .{ 192, 0, 2, 1 }), + port("roceP2p1s0f0", true, .{ 203, 0, 113, 65 }), + port("roceP2p1s0f1", true, .{ 198, 51, 100, 1 }), +}; +// Its peer for this run: only port f1 is cabled, f0 is down. +const b_ports = [_]rv.Port{ + port("rocep1s0f0", false, null), + port("rocep1s0f1", true, .{ 192, 0, 2, 2 }), + port("roceP2p1s0f0", false, null), + port("roceP2p1s0f1", true, .{ 198, 51, 100, 2 }), +}; + +fn listed(ports: []const rv.Port, out: *[max_ports][4]u8) []const [4]u8 { + var blob: [blob_bytes]u8 = undefined; + return out[0..decode(encode(ports, &blob), out)]; +} + +test "a two-neighbour host picks the rails its peer shares, not its first two active ports" { + var specs: [rv.max_hcas]rv.HcaSpec = undefined; + var buf: [128]u8 = undefined; + // the old rule (no peer list): the first two active ports, one of them cabled to the other neighbour + const old = choose(&a_ports, &.{}, &specs, 2); + try std.testing.expect(!old.shared); + try std.testing.expectEqualStrings("rocep1s0f0,rocep1s0f1", names(specs[0..old.n], &buf)); + var peer_buf: [max_ports][4]u8 = undefined; + const b_addrs = listed(&b_ports, &peer_buf); + try std.testing.expectEqual(@as(usize, 0), countIn(b_addrs, subnet(.{ 203, 0, 113, 1 }))); + // with the peer's addresses: the two functions of the cabled port, on each side + const a = choose(&a_ports, b_addrs, &specs, 2); + try std.testing.expect(a.shared); + try std.testing.expectEqualStrings("rocep1s0f1,roceP2p1s0f1", names(specs[0..a.n], &buf)); + var a_buf: [max_ports][4]u8 = undefined; + const b = choose(&b_ports, listed(&a_ports, &a_buf), &specs, 2); + try std.testing.expectEqual(@as(usize, 2), b.n); + try std.testing.expectEqualStrings("rocep1s0f1,roceP2p1s0f1", names(specs[0..b.n], &buf)); + // one rail asked for: the lower /24 on both sides + try std.testing.expectEqualStrings("rocep1s0f1", names(specs[0..choose(&a_ports, b_addrs, &specs, 1).n], &buf)); +} + +test "two rails on one /24 count once per peer port; no shared /24 keeps the list order" { + var specs: [rv.max_hcas]rv.HcaSpec = undefined; + var buf: [128]u8 = undefined; + // the neighbour cabled to port f0 lists one address on that /24: one rail each, equal counts + const one = [_][4]u8{.{ 203, 0, 113, 2 }}; + const p = choose(&a_ports, &one, &specs, 2); + try std.testing.expectEqual(@as(usize, 1), p.n); + try std.testing.expectEqualStrings("rocep1s0f0", names(specs[0..p.n], &buf)); + const two = [_][4]u8{ .{ 203, 0, 113, 2 }, .{ 203, 0, 113, 66 } }; + try std.testing.expectEqualStrings("rocep1s0f0,roceP2p1s0f0", names(specs[0..choose(&a_ports, &two, &specs, 2).n], &buf)); + // inactive and non-Ethernet ports never count; a peer on other subnets falls back to the first usable ports + var mixed = b_ports; + mixed[1].ethernet = false; + const other = [_][4]u8{.{ 198, 51, 100, 9 }}; + const q = choose(&mixed, &.{.{ 203, 0, 113, 9 }}, &specs, 2); + try std.testing.expect(!q.shared); + try std.testing.expectEqualStrings("roceP2p1s0f1", names(specs[0..q.n], &buf)); + try std.testing.expectEqualStrings("roceP2p1s0f1", names(specs[0..choose(&mixed, &other, &specs, 2).n], &buf)); + // a malformed blob is no ports; the detected GID index travels with the pick + var out: [max_ports][4]u8 = undefined; + try std.testing.expectEqual(@as(usize, 0), decode(&.{ 2, 1, 2, 3, 4 }, &out)); + try std.testing.expectEqual(@as(?u8, 3), specs[0].gid); +} + +test "hca's line: the picked names for NCCL; no device or no shared /24 is refused, an explicit list is taken" { + var specs: [rv.max_hcas]rv.HcaSpec = undefined; + var line: [list_bytes + 16]u8 = undefined; + var peer_buf: [max_ports][4]u8 = undefined; + const shared = choose(&a_ports, listed(&b_ports, &peer_buf), &specs, 2); + try std.testing.expectEqualStrings("NCCL_IB_HCA=rocep1s0f1,roceP2p1s0f1\n", try hcaLine(shared, &specs, &line)); + var buf: [list_bytes]u8 = undefined; + try std.testing.expectEqualStrings("rocep1s0f1:3,roceP2p1s0f1:3", try joinNames(specs[0..shared.n], true, &buf)); + // the old fallback (first active ports, no /24 in common) is what NCCL failed on: refused, never printed + try std.testing.expectError(error.NoSharedSubnet, hcaLine(choose(&a_ports, &.{.{ 10, 9, 9, 9 }}, &specs, 2), &specs, &line)); + try std.testing.expectError(error.NoRoceDevice, hcaLine(choose(&.{}, &.{}, &specs, 2), &specs, &line)); + const n = try rv.parseSpecs("rocep1s0f1:3, roceP2p1s0f1", &specs); + try std.testing.expectEqualStrings("NCCL_IB_HCA=rocep1s0f1,roceP2p1s0f1\n", try hcaLine(.{ .n = n, .shared = false, .explicit = true }, &specs, &line)); +} + +/// One RDMA device for `rv.scanPorts` through a stub verbs table: port 1 up or down, Ethernet, mlx5's GIDs 0-3. +const FakeDev = struct { name: [:0]const u8, up: bool, ipv4: [4]u8 = @splat(0) }; + +const ScanFixture = struct { + const vabi = @import("verbs").abi; + var host: []const FakeDev = &.{}; + var list: [max_ports:null]?*vabi.Device = undefined; + + fn handle(i: usize) usize { + return 0x10000 * (i + 1); + } + fn index(p: *const anyopaque) usize { + return @intFromPtr(p) / 0x10000 - 1; + } + fn getList(num: *c_int) callconv(.c) ?[*:null]?*vabi.Device { + for (host, 0..) |_, i| list[i] = @ptrFromInt(handle(i)); + list[host.len] = null; + num.* = @intCast(host.len); + return &list; + } + fn freeList(_: [*:null]?*vabi.Device) callconv(.c) void {} + fn devName(d: *vabi.Device) callconv(.c) ?[*:0]const u8 { + return host[index(d)].name.ptr; + } + fn openDev(d: *vabi.Device) callconv(.c) ?*vabi.Context { + return @ptrFromInt(@intFromPtr(d)); + } + fn closeDev(_: *vabi.Context) callconv(.c) c_int { + return 0; + } + fn queryPort(c: *vabi.Context, _: u8, a: *vabi.PortAttr) callconv(.c) c_int { + a.* = std.mem.zeroes(vabi.PortAttr); + a.state = if (host[index(c)].up) vabi.port_active else 1; + a.link_layer = 2; // Ethernet + a.gid_tbl_len = 4; + return 0; + } + fn queryGid(c: *vabi.Context, _: u8, i: c_int, g: *vabi.Gid) callconv(.c) c_int { + g.* = .{ .raw = @splat(0) }; + if (i < 2) { + g.raw[0] = 0xfe; + g.raw[1] = 0x80; + g.raw[15] = @intCast(index(c) + 1); + } else { + g.raw[10] = 0xff; + g.raw[11] = 0xff; + g.raw[12..16].* = host[index(c)].ipv4; + } + return 0; + } + fn gidEx(_: *vabi.Context, _: u32, i: u32, e: *vabi.GidEntry, _: u32, _: usize) callconv(.c) c_int { + e.* = std.mem.zeroes(vabi.GidEntry); + e.gid_type = if (i % 2 == 1) 2 else 1; // odd indices RoCE v2 + return 0; + } + + /// `rv.scanPorts` over `devs` (names that no real /sys/class/infiniband has, so sysfs never answers for them). + fn scan(devs: []const FakeDev, out: *[max_ports]rv.Port) []const rv.Port { + host = devs; + var v: Verbs = undefined; + v.api.ibv_get_device_list = getList; + v.api.ibv_free_device_list = freeList; + v.api.ibv_get_device_name = devName; + v.api.ibv_open_device = openDev; + v.api.ibv_close_device = closeDev; + v.api.ibv_query_port = queryPort; + v.api.ibv_query_gid = queryGid; + v.gid_ex = gidEx; + return out[0..rv.scanPorts(&v, out)]; + } +}; + +/// #636's reported two GB10 hosts, modelled (no capture): device order and picks reported; addresses, states assumed. +const hosts = [2][4]FakeDev{ .{ + .{ .name = "fx_rocep1s0f0", .up = true, .ipv4 = .{ 192, 0, 2, 1 } }, + .{ .name = "fx_rocep1s0f1", .up = false }, + .{ .name = "fx_roceP2p1s0f0", .up = true, .ipv4 = .{ 198, 51, 100, 1 } }, + .{ .name = "fx_roceP2p1s0f1", .up = false }, +}, .{ + .{ .name = "fx_rocep1s0f0", .up = false }, + .{ .name = "fx_rocep1s0f1", .up = true, .ipv4 = .{ 192, 0, 2, 2 } }, + .{ .name = "fx_roceP2p1s0f0", .up = false }, + .{ .name = "fx_roceP2p1s0f1", .up = true, .ipv4 = .{ 198, 51, 100, 2 } }, +} }; + +test "the port scan reads a host's devices through verbs: state, Ethernet, the IPv4 RoCE v2 GID index" { + var out: [max_ports]rv.Port = undefined; + const a = ScanFixture.scan(&hosts[0], &out); + try std.testing.expectEqual(@as(usize, 4), a.len); + try std.testing.expectEqualStrings("fx_rocep1s0f0", a[0].spec.name[0..a[0].spec.name_len]); + try std.testing.expect(a[0].active and a[0].ethernet and !a[1].active); + try std.testing.expectEqual(@as(?[4]u8, .{ 192, 0, 2, 1 }), a[0].ipv4); + try std.testing.expectEqual(@as(?u8, 3), a[0].spec.gid); // the v2 IPv4 GID, not v1's index 2 + try std.testing.expectEqual(@as(?[4]u8, null), a[1].ipv4); +} + +/// One rank's `selectFrom` over a real bootstrap (rank 0 on `l`), its hca line or error into `out`. +fn hcaRank(cfg: Config, l: ?sock.Fd, ports: []const rv.Port, open: ?OpenFn, out: *[list_bytes + 16]u8, line: *HcaError![]const u8, err: *?anyerror) void { + var b = (if (l) |fd| bootstrap.Bootstrap.acceptOn(fd, 2, 5 * std.time.ns_per_s) else bootstrap.Bootstrap.open(cfg)) catch |e| { + err.* = e; + return; + }; + defer b.close(); + var specs: [rv.max_hcas]rv.HcaSpec = undefined; + const pick = (if (open) |f| selectWithOpen(cfg, &b, &specs, f) else selectFrom(cfg, &b, ports, &specs)) catch |e| { + err.* = e; + return; + }; + line.* = hcaLine(pick, &specs, out); +} + +fn hcaPair(cfgs: [2]Config, ports: [2][]const rv.Port, lines: *[2]HcaError![]const u8, bufs: *[2][list_bytes + 16]u8) !void { + return hcaPairWithOpen(cfgs, ports, .{ null, null }, lines, bufs); +} + +fn hcaPairWithOpen(cfgs: [2]Config, ports: [2][]const rv.Port, open: [2]?OpenFn, lines: *[2]HcaError![]const u8, bufs: *[2][list_bytes + 16]u8) !void { + const l = try bootstrap.testListener(); + defer sock.close(l.fd); + var errs: [2]?anyerror = @splat(null); + var c1 = cfgs[1]; + c1.link_port = l.port; + c1.connect_timeout_ns = 5 * std.time.ns_per_s; + const t = try std.Thread.spawn(.{}, hcaRank, .{ c1, null, ports[1], open[1], &bufs[1], &lines[1], &errs[1] }); + hcaRank(cfgs[0], l.fd, ports[0], open[0], &bufs[0], &lines[0], &errs[0]); + t.join(); + for (errs) |e| if (e) |x| return x; +} + +fn missingLibrary(_: ?[:0]const u8) @import("verbs").Error!Verbs { + return error.LibraryUnavailable; +} + +fn missingSymbol(_: ?[:0]const u8) @import("verbs").Error!Verbs { + return error.MissingSymbol; +} + +test "hca keeps a verbs load failure while completing the peer's port exchange" { + var lines: [2]HcaError![]const u8 = undefined; + var bufs: [2][list_bytes + 16]u8 = undefined; + var scanned: [max_ports]rv.Port = undefined; + const peer = ScanFixture.scan(&hosts[1], &scanned); + const cfgs: [2]Config = .{ .{ .rank = 0 }, .{ .rank = 1 } }; + try hcaPairWithOpen(cfgs, .{ &.{}, peer }, .{ missingLibrary, null }, &lines, &bufs); + try std.testing.expectError(error.LibraryUnavailable, lines[0]); + try std.testing.expectError(error.NoSharedSubnet, lines[1]); +} + +test "hca distinguishes missing verbs symbols from an empty successful scan" { + const cfg: Config = .{ .world = 1 }; + var b = try bootstrap.Bootstrap.open(cfg); + defer b.close(); + var specs: [rv.max_hcas]rv.HcaSpec = undefined; + var buf: [list_bytes + 16]u8 = undefined; + const pick = try selectWithOpen(cfg, &b, &specs, missingSymbol); + try std.testing.expectError(error.MissingSymbol, hcaLine(pick, &specs, &buf)); + try std.testing.expectError(error.NoRoceDevice, hcaLine(try selectFrom(cfg, &b, &.{}, &specs), &specs, &buf)); +} + +test "hca keeps an explicit list when the verbs scan cannot open its library" { + const cfg: Config = .{ .world = 1, .roce_hca = "fx_rocep1s0f0:3" }; + var b = try bootstrap.Bootstrap.open(cfg); + defer b.close(); + var specs: [rv.max_hcas]rv.HcaSpec = undefined; + var buf: [list_bytes + 16]u8 = undefined; + const pick = try selectWithOpen(cfg, &b, &specs, missingLibrary); + try std.testing.expectEqualStrings("NCCL_IB_HCA=fx_rocep1s0f0\n", try hcaLine(pick, &specs, &buf)); +} + +test "hca over a real bootstrap: both ranks print their peer-linked ports; an explicit rank keeps its list; no ports fail both" { + var lines: [2]HcaError![]const u8 = undefined; + var bufs: [2][list_bytes + 16]u8 = undefined; + var scanned: [2][max_ports]rv.Port = undefined; + const ports = [2][]const rv.Port{ ScanFixture.scan(&hosts[0], &scanned[0]), ScanFixture.scan(&hosts[1], &scanned[1]) }; + const auto: [2]Config = .{ .{ .rank = 0 }, .{ .rank = 1 } }; + try hcaPair(auto, ports, &lines, &bufs); + try std.testing.expectEqualStrings("NCCL_IB_HCA=fx_rocep1s0f0,fx_roceP2p1s0f0\n", try lines[0]); + try std.testing.expectEqualStrings("NCCL_IB_HCA=fx_rocep1s0f1,fx_roceP2p1s0f1\n", try lines[1]); + // TF_TP_ROCE_HCA on one rank: its list as given (no GID for NCCL); it still sends its ports to the other + const one: [2]Config = .{ .{ .rank = 0, .roce_hca = "fx_rocep1s0f0:3" }, .{ .rank = 1 } }; + try hcaPair(one, ports, &lines, &bufs); + try std.testing.expectEqualStrings("NCCL_IB_HCA=fx_rocep1s0f0\n", try lines[0]); + try std.testing.expectEqualStrings("NCCL_IB_HCA=fx_rocep1s0f1,fx_roceP2p1s0f1\n", try lines[1]); + // a host without usable ports: it has none, its peer shares no /24 with it, and both refuse + try hcaPair(auto, .{ &.{}, ports[1] }, &lines, &bufs); + try std.testing.expectError(error.NoRoceDevice, lines[0]); + try std.testing.expectError(error.NoSharedSubnet, lines[1]); +} diff --git a/zig/src/fabric/tp/roce_post_test.zig b/zig/src/fabric/tp/roce_post_test.zig new file mode 100644 index 0000000000..a284293000 --- /dev/null +++ b/zig/src/fabric/tp/roce_post_test.zig @@ -0,0 +1,120 @@ +//! VerbsWire.post on fake verbs: a post that would leave its slot or region is refused before any verb. +const std = @import("std"); +const fake = @import("roce_fake.zig"); +const Roce = @import("roce.zig").Roce; +const Config = @import("config.zig").Config; +const Fake = fake.Fake; + +test "a post past its slot is refused before any verb; a full slot posts inside the region" { + const small: u64 = 64 << 10; + const region = try std.testing.allocator.alignedAlloc(u8, .fromByteUnits(4096), Roce.regionBytes(small)); + defer std.testing.allocator.free(region); + Fake.region = region; + Fake.slot_bytes = small; + defer Fake.slot_bytes = fake.slot; + Fake.reset(1); + var hw: fake.Hw = .{}; + var w = hw.wire(region); + try std.testing.expectError(error.OutOfRegion, w.post(1, 1, small + 16)); + try std.testing.expectError(error.OutOfRegion, w.post(2, 1, 16)); + try std.testing.expectEqual(@as(u64, 0), Fake.posts); + try w.post(1, 1, small); + try std.testing.expectEqual(@as(u64, 2), Fake.posts); + try std.testing.expectEqual(@as(u64, 0), Fake.outside); +} + +test "no RDMA write past 1 MiB, whatever the slot: a 9.57 MB and a 1 MiB + 16 post refused in a 10 MiB slot" { + const big: u64 = 10 << 20; + const region = try std.testing.allocator.alignedAlloc(u8, .fromByteUnits(4096), Roce.regionBytes(big)); + defer std.testing.allocator.free(region); + Fake.region = region; + Fake.slot_bytes = big; + defer Fake.slot_bytes = fake.slot; + Fake.reset(1); + var hw: fake.Hw = .{}; + var w = hw.wire(region); + try std.testing.expectError(error.WriteTooLarge, w.post(0, 1, 9_568_256)); + try std.testing.expectError(error.WriteTooLarge, w.post(1, 1, (1 << 20) + 16)); + try std.testing.expectEqual(@as(u64, 0), Fake.posts); + try w.post(0, 1, 1 << 20); + try std.testing.expectEqual(@as(u64, 1 << 20), Fake.max_write * 2); +} + +const Env = struct { + var pairs: []const [2][]const u8 = &.{}; + + fn get(name: []const u8) ?[]const u8 { + for (pairs) |p| if (std.mem.eql(u8, p[0], name)) return p[1]; + return null; + } +}; + +test "boot refuses a RoCE slot past 1 MiB (TF_TP_MAILBOX_MAX_KB), never clamping it" { + for ([_][]const u8{ "1028", "2048", "16384" }) |kb| { + Env.pairs = &.{ .{ "TF_COMM_BACKEND", "roce" }, .{ "TF_TP_MAILBOX_MAX_KB", kb } }; + try std.testing.expectError(error.BadSetting, Config.parse(&Env.get)); + } + Env.pairs = &.{ .{ "TF_COMM_BACKEND", "roce" }, .{ "TF_TP_MAILBOX_MAX_KB", "1024" } }; + try std.testing.expectEqual(@as(usize, 1 << 20), (try Config.parse(&Env.get)).mailbox_max_bytes); + // the cap is RoCE's: the same-host mailbox keeps its larger slots + Env.pairs = &.{ .{ "TF_COMM_BACKEND", "mailbox" }, .{ "TF_TP_MAILBOX_MAX_KB", "4096" } }; + try std.testing.expectEqual(@as(usize, 4 << 20), (try Config.parse(&Env.get)).mailbox_max_bytes); +} + +test "chunked strides posted back to back: at most 2 MiB in flight over both HCAs and no write past 1 MiB" { + const region = try std.testing.allocator.alignedAlloc(u8, .fromByteUnits(4096), Roce.regionBytes(fake.slot)); + defer std.testing.allocator.free(region); + Fake.region = region; + Fake.slot_bytes = fake.slot; + // gathers of R x 512 x 584 B strides and 584 B rows up to a 128 MiB union, each in <= 1 MiB ops + var strides: [16]u64 = undefined; + var n: usize = 0; + for ([_]u64{ 1, 2, 3, 4, 6, 16, 32, 64 }) |r| { + strides[n] = r * 512 * 584; + n += 1; + } + for ([_]u64{ 1795, 1796, (128 << 20) / (3 * 584) }) |m| { + strides[n] = m * 584; + n += 1; + } + for ([_]u32{ 1, 3, 17 }) |lag| { + Fake.reset(lag); + var hw: fake.Hw = .{}; + var w = hw.wire(region); + var seq: u32 = 0; + var want: u64 = 0; + for (strides[0..n]) |stride| { + var off: u64 = 0; + while (off < stride) : (off += fake.slot) { + seq += 1; + const padded: u32 = @intCast(std.mem.alignForward(u64, @min(fake.slot, stride - off), 16)); + try w.post(seq & 1, seq, padded); + want += padded; + } + } + while (Fake.inflight > 0) try w.reap(); + try std.testing.expectEqual(want, Fake.bytes); + try std.testing.expectEqual(@as(u64, 0), Fake.outside); + // the cap's numbers, not roce_verbs.zig's constants: 1 MiB a write, 2 MiB in flight + try std.testing.expect(Fake.max_write <= 1 << 20); + try std.testing.expect(Fake.max_inflight <= 2 << 20); + // the bound is reached, not avoided: posts outran completions + try std.testing.expect(Fake.max_inflight > 1 << 20); + } +} + +test "Roce.abort (the fail-fast hook) moves both QPs to error: no send goes out and no peer write lands" { + const region = try std.testing.allocator.alignedAlloc(u8, .fromByteUnits(4096), Roce.regionBytes(fake.slot)); + defer std.testing.allocator.free(region); + @memset(region, 0); + Fake.region = region; + Fake.slot_bytes = fake.slot; + Fake.reset(1); + fake.Halts.reset(); + var hw: fake.Hw = .{}; + var w = hw.wire(region); + fake.Halts.install(&hw, &w); + var r: Roce = .{ .me = 0, .slot_bytes = fake.slot, .timeout_ns = 1, .region = region, .mapping = region, .wire = .{ .verbs = w }, .cpu = null }; + r.abort(); + try std.testing.expect(fake.Halts.halted[0] and fake.Halts.halted[1]); +} diff --git a/zig/src/fabric/tp/roce_shm.zig b/zig/src/fabric/tp/roce_shm.zig new file mode 100644 index 0000000000..3924be3e96 --- /dev/null +++ b/zig/src/fabric/tp/roce_shm.zig @@ -0,0 +1,63 @@ +//! The RoCE protocol's shm wire: both ranks' regions in one shared mapping, posts through the guard's stripes. +const std = @import("std"); +const Bootstrap = @import("bootstrap.zig").Bootstrap; +const sock = @import("sock.zig"); +const guard = @import("roce_guard.zig"); +const posix = std.posix; +const page = std.heap.page_size_min; + +/// A test's view of each post: slot, padded bytes and the stripes the wire is about to move. +pub const PostTap = struct { + ctx: *anyopaque, + seen: *const fn (ctx: *anyopaque, l: guard.Layout, slot: u32, padded: u32, s: []const guard.Stripe) void, +}; + +/// The peer's region is mapped here; a post copies each stripe, then stores its flag with release order. +pub const ShmWire = struct { + peer: []u8, + layout: guard.Layout, + local: []u8, + /// Stripes a post makes, as HCAs would: the verbs wire's split, flags and bounds on one host. + stripes: u32 = 1, + tap: ?*PostTap = null, + + pub fn post(w: *ShmWire, slot: u32, seq: u32, padded: u32) guard.Error!void { + var plan: [guard.max_hcas]guard.Stripe = undefined; + try guard.stripes(w.layout, w.stripes, slot, padded, &plan); + if (w.tap) |t| t.seen(t.ctx, w.layout, slot, padded, plan[0..w.stripes]); + for (plan[0..w.stripes]) |s| { + @memcpy(w.peer[s.remote..][0..s.len], w.local[s.local..][0..s.len]); + const flag: *std.atomic.Value(u32) = @ptrCast(@alignCast(w.peer[s.flag..].ptr)); + flag.store(seq, .release); + } + } +}; + +/// One shared mapping holding both ranks' regions (rank 0 creates and sizes the file before anyone learns its name). +pub fn sharedPair(b: *Bootstrap, len: usize) ![]align(page) u8 { + var name: [64]u8 = undefined; + var name_len: u8 = 0; + var fd: std.c.fd_t = -1; + defer if (fd >= 0) { + _ = std.c.close(fd); + }; + if (b.rank == 0) blk: { + const s = std.fmt.bufPrint(&name, "/dev/shm/tf-tp-roce-{d}-{x}", .{ std.c.getpid(), sock.nowNs() }) catch break :blk; + name[s.len] = 0; + fd = std.c.open(name[0..s.len :0], .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true, .CLOEXEC = true }, @as(std.c.mode_t, 0o600)); + if (fd >= 0 and std.c.ftruncate(fd, @intCast(2 * len)) == 0) name_len = @intCast(s.len); + } + // an empty name tells the other rank that rank 0 failed (it must not wait for a name that never comes) + try b.broadcast(std.mem.asBytes(&name_len), std.mem.asBytes(&name_len)); + if (name_len == 0) return error.SharedMemoryUnavailable; + try b.broadcast(name[0..name_len], name[0..name_len]); + name[name_len] = 0; + if (b.rank != 0) { + fd = std.c.open(name[0..name_len :0], .{ .ACCMODE = .RDWR, .CLOEXEC = true }, @as(std.c.mode_t, 0)); + if (fd < 0) return error.SharedMemoryUnavailable; + } + const m = try posix.mmap(null, 2 * len, .{ .READ = true, .WRITE = true }, .{ .TYPE = .SHARED }, fd, 0); + try b.agree("mapped", "RoCE shm wire"); + if (b.rank == 0) _ = std.c.unlink(name[0..name_len :0]); + return m; +} diff --git a/zig/src/fabric/tp/roce_test.zig b/zig/src/fabric/tp/roce_test.zig new file mode 100644 index 0000000000..ec6bc4d65e --- /dev/null +++ b/zig/src/fabric/tp/roce_test.zig @@ -0,0 +1,173 @@ +//! The RoCE protocol end to end on host threads: two ranks, TCP bootstrap, proxy, shm or verbs (TF_TP_ROCE_TEST_HCA). +const std = @import("std"); +const roce = @import("roce.zig"); +const rv = @import("roce_verbs.zig"); +const shm = @import("roce_shm.zig"); +const guard = @import("roce_guard.zig"); +const bootstrap = @import("bootstrap.zig"); +const sock = @import("sock.zig"); +const reference = @import("reference.zig"); +const Bootstrap = bootstrap.Bootstrap; + +const Run = struct { + port: u16, + wire: roce.WireKind, + devs: [2][]const u8 = .{ "", "" }, + ops: u32, + timeout_ns: u64 = 10 * std.time.ns_per_s, + /// Rank 1 stops after this many ops (rank 0 must then fail its next wait, by timeout or abort). + stop_after: ?u32 = null, + abort_after_ms: ?u64 = null, + errs: [2]?anyerror = .{ null, null }, + done: [2]u32 = .{ 0, 0 }, + max_bytes: usize = 16 * 1024, + /// Variable lengths (`Roce.allGatherV`'s contract): each op's lens[r] bytes of rank r's n, 0 and n included. + var_lens: bool = false, + /// Strides up to four slots through `hostGatherVChunked` (variable lengths, slot-sized ops). + chunked: bool = false, + /// The shm wire's stripes (2: the verbs wire's split over two HCAs). + stripes: u32 = 1, + /// Each rank's shm posts as the tap saw them: how many, and how many left their slot or the region. + taps: [2]Tap = .{ .{}, .{} }, +}; + +/// Counts an shm wire's posts and any stripe outside its slot, its flag words or the region. +const Tap = struct { + posts: u32 = 0, + outside: u32 = 0, + tap: shm.PostTap = undefined, + + fn seen(ctx: *anyopaque, l: guard.Layout, slot: u32, padded: u32, s: []const guard.Stripe) void { + const t: *Tap = @ptrCast(@alignCast(ctx)); + t.posts += 1; + var sum: u64 = 0; + for (s) |x| { + sum += x.len; + const in_slot = x.local >= l.send + slot * l.slot and x.local + x.len <= l.send + (slot + 1) * l.slot; + const in_peer = x.remote >= l.recv + slot * l.slot and x.remote + x.len <= l.recv + (slot + 1) * l.slot; + if (!in_slot or !in_peer or x.remote + x.len > l.len or x.flag < l.flags or x.flag + 4 > l.send) t.outside += 1; + } + if (sum != padded) t.outside += 1; + } +}; + +fn rank(run: *Run, me: u32, lfd: sock.Fd) void { + run.errs[me] = body(run, me, lfd); +} + +fn body(run: *Run, me: u32, lfd: sock.Fd) ?anyerror { + var b = (if (me == 0) Bootstrap.acceptOn(lfd, 2, 5 * std.time.ns_per_s) else Bootstrap.open(.{ .world = 2, .rank = 1, .link_port = run.port, .connect_timeout_ns = 5 * std.time.ns_per_s })) catch |e| return e; + defer b.close(); + var spec: [1]rv.HcaSpec = .{.{}}; + if (run.wire == .verbs) _ = rv.parseSpecs(run.devs[me], @ptrCast(&spec)) catch |e| return e; + const mine = &run.taps[me]; + mine.tap = .{ .ctx = mine, .seen = Tap.seen }; + const r = roce.Roce.init(null, &b, "", .{ .wire = run.wire, .hcas = if (run.wire == .verbs) &spec else &.{}, .max_bytes = run.max_bytes, .timeout_ns = run.timeout_ns, .shm_stripes = run.stripes, .tap = &mine.tap }) catch |e| return e; + defer r.deinit(); + var abort_thread: ?std.Thread = null; + defer if (abort_thread) |t| t.join(); + var in: [64 * 1024]u8 = undefined; + var peer: [64 * 1024]u8 = undefined; + var out: [128 * 1024]u8 = undefined; + for (0..run.ops) |k| { + if (run.stop_after) |s| if (k == s) { + // rank 1 skips op s; rank 0 waits in it until its timeout, or until the abort armed now + if (me == 1) { + sock.sleepNs(300 * std.time.ns_per_ms); + return null; + } + if (run.abort_after_ms) |ms| abort_thread = std.Thread.spawn(.{}, abortLater, .{ r, ms }) catch null; + }; + // sizes from 1 byte to the slot, odd ones included, both slots and wrap-arounds + const n = 1 + (k * 7919) % (if (run.chunked) 4 * run.max_bytes else run.max_bytes); + reference.fill(in[0..n], .u8, me, k); + reference.fill(peer[0..n], .u8, 1 - me, k); + const lens: [2]u32 = if (k % 5 == 0) .{ 0, @intCast(n) } else .{ @intCast((k * 31) % (n + 1)), @intCast((k * 17 + 5) % (n + 1)) }; + const l: [2]usize = if (run.var_lens or run.chunked) .{ lens[0], lens[1] } else .{ n, n }; + if (run.chunked) { + r.hostGatherVChunked(in[0..n], out[0 .. 2 * n], lens) catch |e| return e; + } else r.hostGatherV(in[0..n], out[0 .. 2 * n], if (run.var_lens) lens else null) catch |e| return e; + const r0 = if (me == 0) in[0..n] else peer[0..n]; + const r1 = if (me == 1) in[0..n] else peer[0..n]; + if (!std.mem.eql(u8, out[0..l[0]], r0[0..l[0]]) or !std.mem.eql(u8, out[n..][0..l[1]], r1[0..l[1]])) return error.WrongBytes; + run.done[me] += 1; + } + r.check() catch |e| return e; + // both ranks finish before either tears down (the peer may still read a flag this rank's wire writes) + b.agree("done", "test end") catch |e| return e; + return null; +} + +fn abortLater(r: *roce.Roce, ms: u64) void { + sock.sleepNs(ms * std.time.ns_per_ms); + r.abort(); +} + +fn twoRanks(run: *Run) !void { + const l = try bootstrap.testListener(); + defer sock.close(l.fd); + run.port = l.port; + const t1 = try std.Thread.spawn(.{}, rank, .{ run, 1, l.fd }); + rank(run, 0, l.fd); + t1.join(); +} + +test "the RoCE protocol over the shm wire: 2,000 gathers of every size, bit for bit" { + var run: Run = .{ .port = 0, .wire = .shm, .ops = 2000 }; + try twoRanks(&run); + for (run.errs) |e| if (e) |err| return err; + try std.testing.expectEqual([2]u32{ 2000, 2000 }, run.done); +} + +test "the RoCE protocol with variable lengths over the shm wire: each rank posts its own padded count, empty shards included" { + var run: Run = .{ .port = 0, .wire = .shm, .ops = 2000, .var_lens = true }; + try twoRanks(&run); + for (run.errs) |e| if (e) |err| return err; + try std.testing.expectEqual([2]u32{ 2000, 2000 }, run.done); +} + +test "the shm wire striped as over two HCAs: every post inside its slot and the region, bytes and lengths exact" { + var run: Run = .{ .port = 0, .wire = .shm, .ops = 2000, .var_lens = true, .stripes = 2 }; + try twoRanks(&run); + for (run.errs) |e| if (e) |err| return err; + try std.testing.expectEqual([2]u32{ 2000, 2000 }, run.done); + for (run.taps) |t| { + try std.testing.expectEqual(@as(u32, 2000), t.posts); + try std.testing.expectEqual(@as(u32, 0), t.outside); + } +} + +test "chunked gathers over the shm wire: strides up to four slots in slot-sized ops, tails empty on both ranks skipped" { + var run: Run = .{ .port = 0, .wire = .shm, .ops = 2000, .chunked = true, .stripes = 2 }; + try twoRanks(&run); + for (run.errs) |e| if (e) |err| return err; + try std.testing.expectEqual([2]u32{ 2000, 2000 }, run.done); + for (run.taps) |t| { + try std.testing.expect(t.posts > 2000); + try std.testing.expectEqual(@as(u32, 0), t.outside); + } +} + +test "a peer that stops makes the waiting rank time out, and abort ends a wait at once" { + var run: Run = .{ .port = 0, .wire = .shm, .ops = 50, .stop_after = 20, .timeout_ns = 100 * std.time.ns_per_ms }; + try twoRanks(&run); + try std.testing.expectEqual(@as(?anyerror, error.Timeout), run.errs[0]); + try std.testing.expectEqual(@as(u32, 20), run.done[0]); + var run2: Run = .{ .port = 0, .wire = .shm, .ops = 50, .stop_after = 20, .abort_after_ms = 50 }; + const kept = roce.Roce.kept.load(.monotonic); + try twoRanks(&run2); + // the aborted rank keeps its region until exit, the other frees it + try std.testing.expectEqual(kept + 1, roce.Roce.kept.load(.monotonic)); + try std.testing.expectEqual(@as(?anyerror, error.Aborted), run2.errs[0]); + try std.testing.expectEqual(@as(?anyerror, null), run2.errs[1]); +} + +test "the RoCE protocol over verbs (TF_TP_ROCE_TEST_HCA=dev0,dev1: soft-RoCE or real HCAs)" { + const raw = std.c.getenv("TF_TP_ROCE_TEST_HCA") orelse return error.SkipZigTest; + var it = std.mem.splitScalar(u8, std.mem.span(raw), ','); + var run: Run = .{ .port = 0, .wire = .verbs, .ops = 2000 }; + run.devs = .{ it.next() orelse return error.SkipZigTest, it.next() orelse return error.SkipZigTest }; + try twoRanks(&run); + for (run.errs) |e| if (e) |err| return err; + try std.testing.expectEqual([2]u32{ 2000, 2000 }, run.done); +} diff --git a/zig/src/fabric/tp/roce_verbs.zig b/zig/src/fabric/tp/roce_verbs.zig new file mode 100644 index 0000000000..6917d292be --- /dev/null +++ b/zig/src/fabric/tp/roce_verbs.zig @@ -0,0 +1,455 @@ +//! The RoCE wire over verbs: one RC queue pair a HCA to the peer, a send slot striped over the HCAs with seq flags. +const std = @import("std"); +const log = @import("log.zig"); +const verbs = @import("verbs"); +const abi = verbs.abi; +const Verbs = verbs.Verbs; +const guard = @import("roce_guard.zig"); +const sock = @import("sock.zig"); + +pub const max_hcas = guard.max_hcas; +/// Work requests a queue pair holds; at most a quarter outstanding (two requests a stripe). +const send_depth = 256; +const link_ethernet = 2; +const record_magic: u32 = 0x5446_5244 + 2; // "TFRD", bumped for each change of this record's layout + +pub const Error = error{ NoHca, VerbsFailed, PostFailed, Completion, BadRecord } || guard.Error; + +/// Bytes posted and not yet completed, every HCA together: a post waits for completions past it; never raised. +pub const max_inflight: u64 = 2 * @as(u64, guard.max_write); +/// A post that waits on completions this long fails the proxy (a peer or HCA that stopped answering). +const inflight_wait_ns: u64 = 10 * std.time.ns_per_s; +const ring_len = send_depth / 4; +/// The flag post's bytes: a u32 on the stack under lkey 0, legal only while the QP sends it inline. +pub const flag_bytes: u32 = @sizeOf(u32); +/// The longest `quiesce` waits for the HCAs to flush what was posted. +pub const quiesce_ns: u64 = 5 * std.time.ns_per_s; + +/// Before ibv_reg_mr: no fork shares or copies the region (DONTFORK), its pages stay 4 KiB (NOHUGEPAGE); else no start. +pub fn prepareRegion(region: []align(std.heap.page_size_min) u8) Error!void { + if (comptime @import("builtin").os.tag != .linux) return; // the verbs wire runs on Linux only + const advice = [_]u32{ std.os.linux.MADV.DONTFORK, std.os.linux.MADV.NOHUGEPAGE }; + for (advice) |a| std.posix.madvise(region.ptr, region.len, a) catch |e| { + log.warn("tp roce: madvise({d}) on the {d} B region failed: {t}; refusing to start", .{ a, region.len, e }); + return error.VerbsFailed; + }; +} + +/// Whether a QP's actual inline capacity takes the flag post. +pub fn inlineFits(cap: abi.QpCap) bool { + return cap.max_inline_data >= flag_bytes; +} + +/// One HCA as configured: its device name and RoCE v2 GID index (null: detected). +pub const HcaSpec = struct { name: [64]u8 = @splat(0), name_len: u8 = 0, gid: ?u8 = null }; + +/// What a rank tells its peer to connect (both ranks build it the same way; sent through the bootstrap). +pub const Record = extern struct { + magic: u32 = record_magic, + n_hca: u32 = 0, + region: u64 = 0, + /// The region's slot and registered length: a peer whose layout differs is refused at connect. + slot_bytes: u64 = 0, + region_len: u64 = 0, + rkey: [max_hcas]u32 = @splat(0), + lid: [max_hcas]u32 = @splat(0), + mtu: [max_hcas]u32 = @splat(0), + qpn: [max_hcas]u32 = @splat(0), + gid: [max_hcas][16]u8 = @splat(@splat(0)), +}; + +const Hca = struct { + ctx: *abi.Context, + pd: *abi.Pd, + mr: *abi.Mr, + cq: *abi.Cq, + qp: *abi.Qp, + gid_index: u8, + gid: [16]u8, + mtu: u32, + lid: u16, + outstanding: u32 = 0, + /// Each outstanding post's stripe bytes, oldest at `head` (completions arrive in order). + ring: [ring_len]u32 = undefined, + head: u32 = 0, + inflight: u64 = 0, + /// Posts made and their completions reaped (the proxy writes them, others read): equal = nothing in flight. + posts: std.atomic.Value(u64) = .init(0), + done: std.atomic.Value(u64) = .init(0), +}; + +/// Parses `name[:gid],name[:gid]` (TF_TP_ROCE_HCA). +pub fn parseSpecs(text: []const u8, out: *[max_hcas]HcaSpec) !usize { + var n: usize = 0; + var it = std.mem.tokenizeScalar(u8, text, ','); + while (it.next()) |item| { + if (n == max_hcas) return error.TooManyHcas; + var parts = std.mem.splitScalar(u8, std.mem.trim(u8, item, " "), ':'); + const name = parts.next().?; + if (name.len == 0 or name.len >= 64) return error.BadHcaName; + out[n] = .{ .name_len = @intCast(name.len) }; + @memcpy(out[n].name[0..name.len], name); + if (parts.next()) |g| out[n].gid = try std.fmt.parseInt(u8, g, 10); + n += 1; + } + return n; +} + +/// RoCE v2 by sysfs (the kernel's own word for each GID); null when sysfs cannot say. +fn sysfsRoceV2(name: []const u8, index: u32) ?bool { + var path: [256]u8 = undefined; + const p = std.fmt.bufPrintSentinel(&path, "/sys/class/infiniband/{s}/ports/1/gid_attrs/types/{d}", .{ name, index }, 0) catch return null; + const fd = std.c.open(p, .{ .ACCMODE = .RDONLY }); + if (fd < 0) return null; + defer _ = std.c.close(fd); + var buf: [32]u8 = undefined; + const n = std.c.read(fd, &buf, buf.len); + if (n <= 0) return false; + return std.mem.startsWith(u8, buf[0..@intCast(n)], "RoCE v2"); +} + +/// An IPv4-mapped GID (::ffff:a.b.c.d): the address the HCA's netdev carries. +pub fn ipv4Mapped(g: [16]u8) bool { + return std.mem.allEqual(u8, g[0..10], 0) and g[10] == 0xff and g[11] == 0xff; +} + +/// The lowest RoCE v2 GID index that is IPv4-mapped (GID indices move on reboot: never hard-coded). +fn detectGid(v: *const Verbs, ctx: *abi.Context, name: []const u8, table: c_int) ?u8 { + var i: u32 = 0; + while (i < @min(@as(u32, @intCast(@max(table, 0))), 255)) : (i += 1) { + const g = v.gid(ctx, 1, i) catch continue; + if (!ipv4Mapped(g.raw)) continue; + const v2 = sysfsRoceV2(name, i) orelse (if (v.gidType(ctx, 1, i)) |t| t == 2 else true); + if (v2) return @intCast(i); + } + return null; +} + +/// A device's port 1 as `roce_pick.choose` sees it: up, Ethernet, and its IPv4 RoCE v2 GID (index and address). +pub const Port = struct { spec: HcaSpec = .{}, active: bool = false, ethernet: bool = false, ipv4: ?[4]u8 = null }; + +/// Port 1 of every device that opens, in the device list's order, at most `out.len`. +pub fn scanPorts(v: *const Verbs, out: []Port) usize { + var count: c_int = 0; + const list = v.api.ibv_get_device_list(&count) orelse return 0; + defer v.api.ibv_free_device_list(list); + var n: usize = 0; + var i: usize = 0; + while (list[i]) |dev| : (i += 1) { + if (n == out.len) break; + const name = std.mem.span(v.api.ibv_get_device_name(dev) orelse continue); + const ctx = v.api.ibv_open_device(dev) orelse continue; + defer _ = v.api.ibv_close_device(ctx); + const port = v.port(ctx, 1) catch continue; + var p: Port = .{ .active = port.state == abi.port_active, .ethernet = port.link_layer == link_ethernet }; + p.spec.name_len = @intCast(@min(name.len, 63)); + @memcpy(p.spec.name[0..p.spec.name_len], name[0..p.spec.name_len]); + if (p.active and p.ethernet) if (detectGid(v, ctx, name, port.gid_tbl_len)) |gi| { + if (v.gid(ctx, 1, gi)) |g| { + p.spec.gid = gi; + p.ipv4 = g.raw[12..16].*; + } else |_| {} + }; + out[n] = p; + n += 1; + } + return n; +} + +pub const VerbsWire = struct { + v: Verbs, + hcas: [max_hcas]Hca = undefined, + n_hca: u32 = 0, + traffic_class: u8, + peer_region: u64 = 0, + peer_rkey: [max_hcas]u32 = @splat(0), + /// Offsets in both ranks' regions (the same layout): receive slots and flags. + recv_off: usize, + flag_off: usize, + send_off: usize, + slot_bytes: usize, + region: []u8, + posted: u64 = 0, + /// The most stripe bytes in flight at once (posted, not completed), for tests and reports. + peak: std.atomic.Value(u64) = .init(0), + completed: u64 = 0, + /// Completions `quiesce` reaped, and those of them flushed (not sent: the QP was in error). + reaped: u64 = 0, + flushed: u64 = 0, + + /// Opens the HCAs, registers `region` on each and creates the queue pairs (INIT); `record` then goes to the peer. + pub fn open(specs: []const HcaSpec, region: []u8, layout: struct { send: usize, recv: usize, flags: usize, slot: usize }, traffic_class: u8) !VerbsWire { + if (specs.len == 0 or specs.len > max_hcas) return error.NoHca; + var w: VerbsWire = .{ .v = try Verbs.open(null), .traffic_class = traffic_class, .recv_off = layout.recv, .flag_off = layout.flags, .send_off = layout.send, .slot_bytes = layout.slot, .region = region }; + errdefer w.close(); + for (specs) |s| { + try w.openHca(s); + } + return w; + } + + fn openHca(w: *VerbsWire, s: HcaSpec) !void { + const api = w.v.api; + const name = s.name[0..s.name_len]; + const ctx = try w.v.openDevice(name); + errdefer _ = api.ibv_close_device(ctx); + const port = try w.v.port(ctx, 1); + if (port.state != abi.port_active) { + log.warn("tp roce: {s} port 1 is not active", .{name}); + return error.NoHca; + } + const gi = s.gid orelse detectGid(&w.v, ctx, name, port.gid_tbl_len) orelse { + log.warn("tp roce: {s} has no IPv4 RoCE v2 GID", .{name}); + return error.NoHca; + }; + const gid = try w.v.gid(ctx, 1, gi); + const pd = api.ibv_alloc_pd(ctx) orelse return error.VerbsFailed; + errdefer _ = api.ibv_dealloc_pd(pd); + const mr = api.ibv_reg_mr(pd, w.region.ptr, w.region.len, abi.access_local_write | abi.access_remote_write) orelse { + log.warn("tp roce: ibv_reg_mr on {s} failed (memlock limit? ulimit -l unlimited)", .{name}); + return error.VerbsFailed; + }; + errdefer _ = api.ibv_dereg_mr(mr); + const cq = api.ibv_create_cq(ctx, send_depth, null, null, 0) orelse return error.VerbsFailed; + errdefer _ = api.ibv_destroy_cq(cq); + var init: abi.QpInitAttr = .{ .send_cq = cq, .recv_cq = cq, .cap = .{ .max_send_wr = send_depth, .max_recv_wr = 1, .max_send_sge = 1, .max_recv_sge = 1, .max_inline_data = 16 }, .qp_type = abi.qpt_rc }; + const qp = api.ibv_create_qp(pd, &init) orelse return error.VerbsFailed; + errdefer _ = api.ibv_destroy_qp(qp); + if (!inlineFits(init.cap)) { + log.warn("tp roce: {s}'s QP sends {d} B inline, the flag post needs {d}; refusing to start", .{ name, init.cap.max_inline_data, flag_bytes }); + return error.VerbsFailed; + } + var a: abi.QpAttr = .{ .qp_state = abi.qps_init, .port_num = 1, .qp_access_flags = abi.access_remote_write }; + if (api.ibv_modify_qp(qp, &a, abi.mask.state | abi.mask.pkey_index | abi.mask.port | abi.mask.access_flags) != 0) return error.VerbsFailed; + w.hcas[w.n_hca] = .{ .ctx = ctx, .pd = pd, .mr = mr, .cq = cq, .qp = qp, .gid_index = gi, .gid = gid.raw, .mtu = @intCast(port.active_mtu), .lid = port.lid }; + w.n_hca += 1; + log.info("[tensorfold] tp roce: {s} GID {d} ({d}.{d}.{d}.{d}), MTU enum {d}", .{ name, gi, gid.raw[12], gid.raw[13], gid.raw[14], gid.raw[15], port.active_mtu }); + } + + pub fn close(w: *VerbsWire) void { + const api = w.v.api; + for (w.hcas[0..w.n_hca]) |h| { + _ = api.ibv_destroy_qp(h.qp); + _ = api.ibv_destroy_cq(h.cq); + _ = api.ibv_dereg_mr(h.mr); + _ = api.ibv_dealloc_pd(h.pd); + _ = api.ibv_close_device(h.ctx); + } + w.n_hca = 0; + w.v.close(); + } + + /// The region and each HCA's keys, this side's and the peer's (a protection error is checked against them). + pub fn describe(w: *const VerbsWire, buf: []u8) []const u8 { + var out: std.Io.Writer = .fixed(buf); + out.print("region 0x{x} len {d}", .{ @intFromPtr(w.region.ptr), w.region.len }) catch return out.buffered(); + for (w.hcas[0..w.n_hca], 0..) |h, i| { + out.print("; HCA {d} lkey 0x{x} rkey 0x{x}, peer region 0x{x} rkey 0x{x}", .{ i, h.mr.lkey, h.mr.rkey, w.peer_region, w.peer_rkey[i] }) catch break; + } + return out.buffered(); + } + + /// The QPs to error: the HCAs stop sending and refuse the peer's writes, and what was posted completes as flushed. + pub fn halt(w: *VerbsWire) bool { + var ok = true; + for (w.hcas[0..w.n_hca]) |h| { + var a: abi.QpAttr = .{ .qp_state = abi.qps_err }; + if (w.v.api.ibv_modify_qp(h.qp, &a, abi.mask.state) != 0) ok = false; + } + return ok; + } + + /// `halt`, then every posted request's completion reaped, flushed or not: no request of the region left at an HCA. + pub fn quiesce(w: *VerbsWire, timeout_ns: u64) bool { + if (!w.halt()) return false; + const t0 = sock.nowNs(); + for (w.hcas[0..w.n_hca]) |*h| while (h.outstanding > 0) { + var wc: [32]abi.Wc = undefined; + const n = h.ctx.ops.poll_cq(h.cq, 32, &wc); + if (n < 0) return false; + for (wc[0..@intCast(n)]) |c| w.flushed += @intFromBool(c.status != abi.wc_success); + w.reaped += @intCast(n); + if (n > 0) w.popped(h, @intCast(n)) else if (sock.nowNs() - t0 > timeout_ns) return false; + }; + return true; + } + + /// Every HCA's posts completed: no request of the region at an HCA (readable beside the proxy). + pub fn idle(w: *const VerbsWire) bool { + for (w.hcas[0..w.n_hca]) |*h| if (h.posts.load(.acquire) != h.done.load(.acquire)) return false; + return true; + } + + /// Each QP's state as its device reports it (ibv_query_qp), then what `quiesce` reaped and what is still posted. + pub fn report(w: *const VerbsWire, buf: []u8) []const u8 { + const names = [_][]const u8{ "RESET", "INIT", "RTR", "RTS", "SQD", "SQE", "ERR" }; + var out: std.Io.Writer = .fixed(buf); + for (w.hcas[0..w.n_hca], 0..) |*h, i| { + var a: abi.QpAttr = .{}; + var init: abi.QpInitAttr = undefined; + const q = w.v.query_qp orelse { + out.print("HCA {d} QP state unknown (no ibv_query_qp); ", .{i}) catch break; + continue; + }; + if (q(h.qp, &a, abi.mask.state, &init) != 0) { + out.print("HCA {d} QP query failed; ", .{i}) catch break; + } else if (a.qp_state >= 0 and a.qp_state < names.len) { + out.print("HCA {d} QP {s}; ", .{ i, names[@intCast(a.qp_state)] }) catch break; + } else out.print("HCA {d} QP state {d}; ", .{ i, a.qp_state }) catch break; + out.print("posted {d}, completed {d}; ", .{ h.posts.load(.acquire), h.done.load(.acquire) }) catch break; + } + var held: u64 = 0; + for (w.hcas[0..w.n_hca]) |h| held += h.outstanding; + out.print("reaped {d} (flushed {d}), still posted {d}", .{ w.reaped, w.flushed, held }) catch {}; + return out.buffered(); + } + + pub fn record(w: *const VerbsWire) Record { + var r: Record = .{ .n_hca = w.n_hca, .region = @intFromPtr(w.region.ptr), .slot_bytes = w.slot_bytes, .region_len = w.region.len }; + for (w.hcas[0..w.n_hca], 0..) |h, i| { + r.rkey[i] = h.mr.rkey; + r.lid[i] = h.lid; + r.mtu[i] = h.mtu; + r.qpn[i] = h.qp.qp_num; + r.gid[i] = h.gid; + } + return r; + } + + /// The peer HCA for each of ours: the same IPv4 /24 (functions pair by subnet, names may differ), else position. + pub fn pairing(mine: Record, peer: Record) ![max_hcas]u32 { + if (peer.magic != record_magic) return error.BadRecord; + if (peer.n_hca != mine.n_hca or peer.slot_bytes != mine.slot_bytes or peer.region_len != mine.region_len) return error.BadRecord; + var out: [max_hcas]u32 = undefined; + for (0..mine.n_hca) |i| { + out[i] = @intCast(i); + var hits: u32 = 0; + for (0..peer.n_hca) |j| if (std.mem.eql(u8, mine.gid[i][12..15], peer.gid[j][12..15])) { + out[i] = @intCast(j); + hits += 1; + }; + if (hits > 1) out[i] = @intCast(i); // one subnet for both: keep the order + } + return out; + } + + /// INIT to RTR to RTS against the peer's record. + pub fn connect(w: *VerbsWire, peer: Record) !void { + const pairs = try pairing(w.record(), peer); + const api = w.v.api; + const m = abi.mask; + w.peer_region = peer.region; + for (w.hcas[0..w.n_hca], 0..) |*h, i| { + const j = pairs[i]; + w.peer_rkey[i] = peer.rkey[j]; + var rtr: abi.QpAttr = .{ .qp_state = abi.qps_rtr, .path_mtu = @intCast(@min(peer.mtu[j], h.mtu)), .dest_qp_num = peer.qpn[j], .rq_psn = 0, .max_dest_rd_atomic = 1, .min_rnr_timer = 12 }; + rtr.ah_attr = .{ .grh = .{ .dgid = .{ .raw = peer.gid[j] }, .sgid_index = h.gid_index, .hop_limit = 64, .traffic_class = w.traffic_class }, .dlid = @intCast(peer.lid[j]), .is_global = 1, .port_num = 1 }; + if (api.ibv_modify_qp(h.qp, &rtr, m.state | m.av | m.path_mtu | m.dest_qpn | m.rq_psn | m.max_dest_rd_atomic | m.min_rnr_timer) != 0) return error.VerbsFailed; + var rts: abi.QpAttr = .{ .qp_state = abi.qps_rts, .timeout = 14, .retry_cnt = 7, .rnr_retry = 7, .sq_psn = 0, .max_rd_atomic = 1 }; + if (api.ibv_modify_qp(h.qp, &rts, m.state | m.timeout | m.retry_cnt | m.rnr_retry | m.sq_psn | m.max_qp_rd_atomic) != 0) return error.VerbsFailed; + } + } + + /// Slot `slot`'s first `padded` bytes to the peer, striped over the HCAs, each stripe's `seq` flag on the same QP. + pub fn post(w: *VerbsWire, slot: u32, seq: u32, padded: u32) !void { + var plan: [max_hcas]guard.Stripe = undefined; + try guard.stripes(w.regionLayout(), w.n_hca, slot, padded, &plan); + while (w.inflight() + padded > max_inflight) try w.reapWait(); + var seq_copy = seq; + for (w.hcas[0..w.n_hca], plan[0..w.n_hca], 0..) |*h, s, i| { + while (h.outstanding >= ring_len) try w.reapHca(h); + var flag_sge: abi.Sge = .{ .addr = @intFromPtr(&seq_copy), .length = 4, .lkey = 0 }; + var flag_wr: abi.SendWr = .{ .wr_id = seq, .sg_list = @ptrCast(&flag_sge), .num_sge = 1, .opcode = abi.wr_rdma_write, .send_flags = abi.send_signaled | abi.send_inline, .remote_addr = w.peer_region + s.flag, .rkey = w.peer_rkey[i] }; + var data_sge: abi.Sge = .{ .addr = @intFromPtr(w.region.ptr) + s.local, .length = s.len, .lkey = h.mr.lkey }; + var data_wr: abi.SendWr = .{ .wr_id = seq, .next = &flag_wr, .sg_list = @ptrCast(&data_sge), .num_sge = 1, .opcode = abi.wr_rdma_write, .send_flags = 0, .remote_addr = w.peer_region + s.remote, .rkey = w.peer_rkey[i] }; + var bad: ?*abi.SendWr = null; + const first: *abi.SendWr = if (s.len > 0) &data_wr else &flag_wr; + if (h.ctx.ops.post_send(h.qp, first, &bad) != 0) return error.PostFailed; + h.ring[(h.head + h.outstanding) % ring_len] = s.len; + h.outstanding += 1; + h.inflight += s.len; + _ = h.posts.fetchAdd(1, .release); + } + if (w.inflight() > w.peak.load(.monotonic)) w.peak.store(w.inflight(), .monotonic); + w.posted += 1; + } + + /// This rank's layout; the MR covers `region` exactly and the peer's record matched it at connect. + pub fn regionLayout(w: *const VerbsWire) guard.Layout { + return .{ .send = w.send_off, .recv = w.recv_off, .flags = w.flag_off, .slot = w.slot_bytes, .len = w.region.len }; + } + + fn reapHca(w: *VerbsWire, h: *Hca) !void { + var wc: [32]abi.Wc = undefined; + const n = h.ctx.ops.poll_cq(h.cq, 32, &wc); + if (n < 0) return error.Completion; + for (wc[0..@intCast(n)]) |c| if (c.status != abi.wc_success) { + log.err("tp roce: RDMA write of seq {d} failed: {s} (vendor 0x{x})", .{ c.wr_id, w.v.statusText(c.status), c.vendor_err }); + return error.Completion; + }; + w.popped(h, @intCast(n)); + } + + fn popped(w: *VerbsWire, h: *Hca, n: u32) void { + for (0..n) |_| { + h.inflight -= h.ring[h.head]; + h.head = (h.head + 1) % ring_len; + } + h.outstanding -= n; + w.completed += n; + _ = h.done.fetchAdd(n, .release); + } + + /// Bytes posted and not yet completed over every HCA. + pub fn inflight(w: *const VerbsWire) u64 { + var sum: u64 = 0; + for (w.hcas[0..w.n_hca]) |h| sum += h.inflight; + return sum; + } + + /// Reaps until some post completes (error.Completion when none does within `inflight_wait_ns`). + fn reapWait(w: *VerbsWire) !void { + const before = w.completed; + const t0 = sock.nowNs(); + while (w.completed == before) { + try w.reap(); + if (w.completed == before and sock.nowNs() - t0 > inflight_wait_ns) return error.Completion; + std.atomic.spinLoopHint(); + } + } + + /// Reap every HCA's completions (after each op, and in the proxy's idle loop). + pub fn reap(w: *VerbsWire) !void { + for (w.hcas[0..w.n_hca]) |*h| try w.reapHca(h); + } +}; + +test "HCA lists parse and subnets pair the functions" { + var specs: [max_hcas]HcaSpec = undefined; + const n = try parseSpecs("rocep1s0f1:3, roceP2p1s0f1", &specs); + try std.testing.expectEqual(@as(usize, 2), n); + try std.testing.expectEqualStrings("rocep1s0f1", specs[0].name[0..specs[0].name_len]); + try std.testing.expectEqual(@as(?u8, 3), specs[0].gid); + try std.testing.expectEqual(@as(?u8, null), specs[1].gid); + var a: Record = .{ .n_hca = 2 }; + var b: Record = .{ .n_hca = 2 }; + const sub100 = [16]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 100, 10 }; + var sub101 = sub100; + sub101[14] = 101; + a.gid = .{ sub100, sub101 }; + b.gid = .{ sub101, sub100 }; // the peer lists its functions the other way round + b.gid[0][15] = 11; + b.gid[1][15] = 11; + const p = try VerbsWire.pairing(a, b); + try std.testing.expectEqual(@as(u32, 1), p[0]); + try std.testing.expectEqual(@as(u32, 0), p[1]); + try std.testing.expect(ipv4Mapped(sub100)); + b.n_hca = 1; + try std.testing.expectError(error.BadRecord, VerbsWire.pairing(a, b)); + b.n_hca = 2; + b.slot_bytes = 1 << 20; + try std.testing.expectError(error.BadRecord, VerbsWire.pairing(a, b)); + b.slot_bytes = 0; + b.region_len = 4096; + try std.testing.expectError(error.BadRecord, VerbsWire.pairing(a, b)); +} diff --git a/zig/src/fabric/tp/run_hosts.sh b/zig/src/fabric/tp/run_hosts.sh new file mode 100644 index 0000000000..75f0c7900b --- /dev/null +++ b/zig/src/fabric/tp/run_hosts.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +# One rank of the two-host TP=2 gate over RoCE: `run_hosts.sh RANK RANK0_ADDR ./tf-tp-test OUTDIR` on both hosts. +set -u +RANK=${1:?rank}; MASTER=${2:?master address}; BIN=${3:?tf-tp-test}; OUT=${4:?outdir} +mkdir -p "$OUT" +[ -n "${NCCL_SOCKET_IFNAME:-}" ] && export NCCL_SOCKET_IFNAME # else NCCL picks the interface +# NCCL uses the RoCE wire's devices unless NCCL_IB_HCA is set: the names without `:gid` (NCCL reads `:N` as a port) +[ -n "${TF_TP_ROCE_HCA:-}" ] && [ -z "${NCCL_IB_HCA:-}" ] && NCCL_IB_HCA=$(echo "$TF_TP_ROCE_HCA" | sed 's/:[0-9]*//g; s/ //g') +[ -n "${TF_TP_ROCE_HCA:-}" ] && export TF_TP_ROCE_HCA +"$BIN" kernel > /dev/null 2>&1 || { echo "error: $BIN has no mailbox kernel: rebuild with -Dtp-nvcc=PATH or -Dtp-fatbin=PATH" >&2; exit 2; } +ulimit -l unlimited 2> /dev/null || echo "warning: memlock is $(ulimit -l) KiB: ibv_reg_mr of the RoCE region may fail" >&2 +# neither named: NCCL takes the ports the RoCE picker chose with the peer (`tf-tp-test hca`, both ranks), not its own +if [ -z "${TF_TP_ROCE_HCA:-}${NCCL_IB_HCA:-}" ]; then + # hca gives up after TF_TP_PLAN_TIMEOUT (default 300 s) with its reason; the cap here only catches a hang past that + CAP=$(awk 'BEGIN { t = ARGV[1] + 0; if (t <= 0) t = 300; printf "%d", int(t) + 31 }' "${TF_TP_PLAN_TIMEOUT:-300}") + echo "hca: rank $RANK picking the ports with the peer (up to $CAP s)" + PICK=$(TF_TP_RANK=$RANK TF_TP_MASTER=$MASTER TF_TP_PORT=29700 timeout "$CAP" "$BIN" hca 2> "$OUT/hca.log") + rc=$? + if [ "$rc" -eq 124 ]; then + echo "error: $BIN hca still running after $CAP s: killed ($OUT/hca.log)" >&2 + exit 3 + fi + if [ "$rc" -ne 0 ]; then + echo "error: $BIN hca exited $rc: $(grep -h '^FAIL\|^error' "$OUT/hca.log" | tail -n 1 | grep . || tail -n 1 "$OUT/hca.log") ($OUT/hca.log)" >&2 + exit 3 + fi + if ! echo "$PICK" | grep -Eqx 'NCCL_IB_HCA=[A-Za-z0-9_.-]+(,[A-Za-z0-9_.-]+)*'; then + echo "error: $BIN hca printed '$PICK', not NCCL_IB_HCA=name,... ($OUT/hca.log)" >&2 + exit 3 + fi + NCCL_IB_HCA=${PICK#NCCL_IB_HCA=} + echo "hca: rank $RANK NCCL_IB_HCA=$NCCL_IB_HCA ($(grep -h '^INFO hca' "$OUT/hca.log"))" +fi +[ -n "${NCCL_IB_HCA:-}" ] && export NCCL_IB_HCA +{ hostname; nvidia-smi -L; ibv_devices 2>&1; ulimit -l; env | grep -E '^(TF_TP_ROCE_HCA|NCCL_IB_HCA|NCCL_SOCKET_IFNAME)='; } > "$OUT/host.txt" 2>&1 +PORT=29700 +step() { # NAME BACKEND ARGS...: this host's rank; log OUT/NAME.log ending with an EXIT line + local name=$1 backend=$2; shift 2 + PORT=$((PORT + 10)) + ( TF_TP_RANK=$RANK TF_TP_MASTER=$MASTER TF_TP_PORT=$PORT TF_COMM_BACKEND=$backend TF_TP_ROCE_FALLBACK=error \ + timeout 900 "$BIN" "$@"; echo "EXIT rank $RANK rc $? at $(date +%s.%N)" ) > "$OUT/$name.log" 2>&1 + echo "$name: $(grep -h '^SUMMARY\|^PASS roce-host\|^INJECT\|^EXIT' "$OUT/$name.log" | tr '\n' ' ')" +} +step roce-host roce roce-host +step suite-roce roce suite +step suite-nccl nccl suite +for backend in roce nccl; do + for mode in fatal kill; do + for victim in 0 1; do step "ff-$backend-$mode-$victim" "$backend" failfast "$victim" "$mode"; done + done +done diff --git a/zig/src/fabric/tp/run_pair.sh b/zig/src/fabric/tp/run_pair.sh new file mode 100644 index 0000000000..1cf73c6261 --- /dev/null +++ b/zig/src/fabric/tp/run_pair.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# Two ranks of tf-tp-test on one two-GPU host: `[SUITES=..] [FAILFAST=..] run_pair.sh BIN OUTDIR`. +set -u +BIN=${1:?bin}; OUT=${2:?outdir} +mkdir -p "$OUT" +nvidia-smi > "$OUT/smi.txt" 2>&1 +nvidia-smi topo -m > "$OUT/topo.txt" 2>&1 +ldconfig -p | grep -E "libnccl|libcuda" > "$OUT/libs.txt" 2>&1 +PORT=29500 +pair() { # NAME BACKEND ARGS...: both ranks, logs OUT/NAME.rR.log ending with an EXIT line + local name=$1 backend=$2 r; shift 2 + PORT=$((PORT + 10)) + for r in 0 1; do + ( TF_TP_RANK=$r TF_TP_DEVICE=$r TF_TP_PORT=$PORT TF_COMM_BACKEND=$backend NCCL_DEBUG=${NCCL_DEBUG:-WARN} \ + timeout 600 "$BIN" "$@"; echo "EXIT rank $r rc $? at $(date +%s.%N)" ) > "$OUT/$name.r$r.log" 2>&1 & + done + wait +} +# ffsum OUT NAME VICTIM: a fail-fast pair's verdict line (exit codes, exit times after the injection) +ffsum() { + [ -f "$1/$2.r0.log" ] && [ -f "$1/$2.r1.log" ] || { echo "ffsum: $1/$2.r0.log or .r1.log missing" >&2; return 1; } + awk -v name="$2" -v victim="$3" ' + FNR == 1 { r = (FILENAME ~ /\.r1\.log$/) ? 1 : 0 } + !(r in ex) && match($0, /EXIT rank [0-9] rc [0-9]+ at [0-9.]+/) { + split(substr($0, RSTART, RLENGTH), f, " "); rc[r] = f[5]; at[r] = f[7]; ex[r] = 1 + } + r == victim && !inj && match($0, /INJECT [A-Za-z0-9_]+ rank [0-9]+ at [0-9.]+/) { + split(substr($0, RSTART, RLENGTH), g, " "); t0 = g[6]; inj = 1 + } + END { + if (!inj || !(0 in ex) || !(1 in ex)) { print name ": FAIL (no inject / exit line)"; exit } + for (i = 0; i < 2; i++) { want[i] = (i == victim && name ~ /kill/) ? 137 : 70; dt[i] = at[i] - t0 } + ok = rc[0] == want[0] && rc[1] == want[1] && dt[1 - victim] < 3.0 + printf "%s: %s rc [%d, %d] (want [%d, %d]), exit after injection: rank0 %.3f s, rank1 %.3f s\n", name, + ok ? "PASS" : "FAIL", rc[0], rc[1], want[0], want[1], dt[0], dt[1] + }' "$1/$2.r0.log" "$1/$2.r1.log" +} +for backend in ${SUITES:-mailbox nccl}; do + pair "suite-$backend" "$backend" suite +done +: > "$OUT/failfast.txt" +for backend in ${FAILFAST:-nccl mailbox}; do + for mode in fatal kill; do + for victim in 0 1; do + name=ff-$backend-$mode-$victim + pair "$name" "$backend" failfast "$victim" "$mode" + ffsum "$OUT" "$name" "$victim" >> "$OUT/failfast.txt" + done + done +done +grep -h "^SUMMARY\|^FAIL" "$OUT"/suite*.log +cat "$OUT/failfast.txt" diff --git a/zig/src/fabric/tp/run_transport.sh b/zig/src/fabric/tp/run_transport.sh new file mode 100755 index 0000000000..68ebcddd9a --- /dev/null +++ b/zig/src/fabric/tp/run_transport.sh @@ -0,0 +1,120 @@ +#!/usr/bin/env bash +# The failed-transport check on two hosts (tf-tp-test failfast V transport); `run_transport.sh` alone prints how. +set -u +usage() { + cat << 'TXT' +run_transport.sh MASTER_ADDR PEER_SSH OUTDIR (on rank 0's host, as root; rank 1 runs over ssh) + Two hosts with this script and tf-tp-test (built with the mailbox kernel) at the same path on both. Before the steps, + on both hosts: no GPU process and memlock unlimited. Steps: T0 (suite), T1 (victim 1), T3 (victim 0), fail-fast on; + tf-tp-test injects only when both links are idle. After each step, on both hosts: the kernel log since the step + began and every HCA *err* counter against the step's start. A matching line, a counter that grew, an unexpected rc + or a failure line prints STOP: and ends the run (exit 1), with no later step. +run_transport.sh t2 OUTDIR (one host: TF_TP_ALLOW_HOST_ONLY=1) + T2 (fail-fast off, a hold) runs both ranks on this host over the shm wire; tf-tp-test refuses it on an RDMA wire. +TXT +} +[ $# -ge 2 ] || { usage; exit 2; } +KIT=$(cd "$(dirname "$0")" && pwd) +BIN=$KIT/tf-tp-test +KLOG='EXT4-fs|smmu|iommu|mlx5|AER|nvme|Xid' +BAD='RDMA write of seq|local protection|NOT drained|GPU sync FAILED|REFUSED|refused ERR|panic' +"$BIN" kernel > /dev/null 2>&1 || { echo "STOP: $BIN has no mailbox kernel: rebuild with -Dtp-nvcc=PATH or -Dtp-fatbin=PATH" >&2; exit 2; } + +if [ "${1:-}" = t2 ]; then + OUT=${2:?outdir} + [ "${TF_TP_ALLOW_HOST_ONLY:-}" = 1 ] || { echo "STOP: T2 is host-only: set TF_TP_ALLOW_HOST_ONLY=1" >&2; exit 2; } + mkdir -p "$OUT" + for r in 0 1; do + TF_TP_RANK=$r TF_TP_MASTER=127.0.0.1 TF_TP_PORT=29990 TF_COMM_BACKEND=roce TF_TP_ROCE_WIRE=shm TF_TP_FAILFAST=0 \ + timeout 300 "$BIN" failfast 1 transport 200 5 > "$OUT/T2-r$r.log" 2>&1 & + done + wait + grep -h '^PASS\|^FAIL\|REFUSED\|regions kept' "$OUT"/T2-r*.log + exit 0 +fi + +MASTER=${1:?master address}; PEER=${2:?peer ssh target}; OUT=${3:?outdir} +mkdir -p "$OUT" +stop() { echo "STOP: $*" | tee -a "$OUT/verdict.txt"; exit 1; } + +precheck() { + [ -z "$(nvidia-smi --query-compute-apps=pid --format=csv,noheader 2> /dev/null)" ] || { echo "a GPU process runs"; return 1; } + [ "$(bash -c 'ulimit -l unlimited 2> /dev/null; ulimit -l')" = unlimited ] || { echo "memlock not unlimited"; return 1; } + echo "ok $(hostname) $(uname -r)" +} +counters() { + find -L /sys/class/infiniband/ -maxdepth 5 -path '*ports*counters*' -name '*err*' 2> /dev/null | sort | while read -r f; do + echo "$f $(cat "$f" 2> /dev/null)" + done +} +klog() { # kernel lines since epoch $1 matching $2 (journalctl must answer: an unreadable log is a STOP) + journalctl -k --since "@$1" --no-pager -o short-iso 2> /dev/null | grep -E "$2" + return "${PIPESTATUS[0]}" +} +probe() { # this host's tf-tp-test QPs and MRs once a second until it exits + local pid="" t + for _ in $(seq 200); do pid=$(pgrep -nx tf-tp-test) && break; sleep 0.1; done + [ -z "$pid" ] && return + for t in $(seq 180); do + kill -0 "$pid" 2> /dev/null || break + echo "== t=$t pid $pid" + rdma resource show qp pid "$pid" 2>&1 + rdma resource show mr pid "$pid" 2>&1 + sleep 1 + done +} +on() { # NODE CMD ARGS...: a command or one of the functions above, here (local) or on the peer + local node=$1 fn=$2 def + shift 2 + if [ "$node" = local ]; then "$fn" "$@"; return; fi + def=$(declare -f "$fn" 2> /dev/null) + ssh -o BatchMode=yes "$PEER" "${def:+$def +}$fn $(printf '%q ' "$@")" +} +grew() { # BEFORE AFTER: counters that went up + awk 'NR == FNR { b[$1] = $2; next } ($1 in b) && $2 > b[$1] { print $1 " " b[$1] " -> " $2 }' "$1" "$2" +} + +for n in local peer; do + v=$(on $n precheck) || stop "$n: $v" + echo "precheck $n: $v" | tee -a "$OUT/verdict.txt" +done +on peer test -x "$BIN" || stop "peer: no $BIN (the kit must sit at the same path on both hosts)" + +PORT=29900 +step() { # NAME WANT_RC -- ARGS... + local name=$1 want=$2 n rc rc0 rc1 hits g + shift 3 + PORT=$((PORT + 10)) + declare -A t + for n in local peer; do + t[$n]=$(on $n date +%s) + on $n counters > "$OUT/$name.$n.err0" + done + on local probe > "$OUT/$name.local.rdma.txt" 2>&1 & + on peer probe > "$OUT/$name.peer.rdma.txt" 2>&1 & + local envs=(TF_TP_MASTER="$MASTER" TF_TP_PORT=$PORT TF_COMM_BACKEND=roce TF_TP_ROCE_FALLBACK=error TF_TP_MAILBOX_MAX_KB=1024 + TF_TP_ROCE_FAST=1 TF_TP_FAILFAST_GRACE_S=15) + ssh -o BatchMode=yes "$PEER" "ulimit -l unlimited && env $(printf '%q ' "${envs[@]}") TF_TP_RANK=1 timeout 300 $(printf '%q ' "$BIN" "$@"); echo \"EXIT rank 1 rc \$? at \$(date +%s.%N)\"" > "$OUT/$name-r1.log" 2>&1 & + ( ulimit -l unlimited && env "${envs[@]}" TF_TP_RANK=0 timeout 300 "$BIN" "$@"; echo "EXIT rank 0 rc $? at $(date +%s.%N)" ) > "$OUT/$name-r0.log" 2>&1 + wait + rc0=$(grep -oP '^EXIT rank 0 rc \K[0-9]+' "$OUT/$name-r0.log") + rc1=$(grep -oP '^EXIT rank 1 rc \K[0-9]+' "$OUT/$name-r1.log") + echo "$name: rank 0 rc ${rc0:-?}, rank 1 rc ${rc1:-?} (want $want)" | tee -a "$OUT/verdict.txt" + for n in local peer; do + on $n counters > "$OUT/$name.$n.err1" + g=$(grew "$OUT/$name.$n.err0" "$OUT/$name.$n.err1") + [ -n "$g" ] && stop "$name $n: HCA error counters grew: $g" + hits=$(on $n klog "${t[$n]}" "$KLOG"); rc=$? + [ "$rc" -ne 0 ] && stop "$name $n: the kernel log is unreadable (journalctl rc $rc)" + [ -n "$hits" ] && { echo "$hits" > "$OUT/$name.$n.klog.txt"; stop "$name $n: kernel lines: $hits"; } + done + hits=$(grep -hE "$BAD" "$OUT/$name-r0.log" "$OUT/$name-r1.log") + [ -n "$hits" ] && stop "$name: $hits" + [ "$rc0" = "$want" ] && [ "$rc1" = "$want" ] || stop "$name: rc $rc0 / $rc1, want $want" + grep -h 'halted\|quiesced\|idle before\|regions kept' "$OUT/$name"-r*.log | sed "s/^/ /" +} +step T0 0 -- suite +step T1 70 -- failfast 1 transport 200 +step T3 70 -- failfast 0 transport 200 +echo "PASS transport check (T0, T1, T3)" | tee -a "$OUT/verdict.txt" diff --git a/zig/src/fabric/tp/sock.zig b/zig/src/fabric/tp/sock.zig new file mode 100644 index 0000000000..b382884034 --- /dev/null +++ b/zig/src/fabric/tp/sock.zig @@ -0,0 +1,169 @@ +//! TCP for the bootstrap and the fate channel: listen, accept and connect with deadlines, whole sends and receives. +const std = @import("std"); +const c = std.c; +const posix = std.posix; + +pub const Fd = c.fd_t; + +pub const Error = error{ SocketFailed, AddressUnresolved, AddressInUse, Timeout, Closed, Interrupted }; + +pub fn nowNs() u64 { + var ts: c.timespec = undefined; + _ = c.clock_gettime(.MONOTONIC, &ts); + return @as(u64, @intCast(ts.sec)) * std.time.ns_per_s + @as(u64, @intCast(ts.nsec)); +} + +pub fn sleepNs(ns: u64) void { + var ts: c.timespec = .{ .sec = @intCast(ns / std.time.ns_per_s), .nsec = @intCast(ns % std.time.ns_per_s) }; + while (c.nanosleep(&ts, &ts) != 0) {} +} + +/// TCP_NODELAY (frames are tiny and latency matters) and keepalive (a vanished host fails a read in ~90 s, not never). +fn tune(fd: Fd) void { + const one: c_int = 1; + _ = c.setsockopt(fd, posix.IPPROTO.TCP, posix.TCP.NODELAY, &one, @sizeOf(c_int)); + _ = c.setsockopt(fd, posix.SOL.SOCKET, posix.SO.KEEPALIVE, &one, @sizeOf(c_int)); + const idle: c_int = 30; + const intvl: c_int = 10; + const cnt: c_int = 6; + _ = c.setsockopt(fd, posix.IPPROTO.TCP, posix.TCP.KEEPIDLE, &idle, @sizeOf(c_int)); + _ = c.setsockopt(fd, posix.IPPROTO.TCP, posix.TCP.KEEPINTVL, &intvl, @sizeOf(c_int)); + _ = c.setsockopt(fd, posix.IPPROTO.TCP, posix.TCP.KEEPCNT, &cnt, @sizeOf(c_int)); +} + +/// Rank 0's listening socket on every IPv4 address at `port`. +pub fn listen(port: u16) Error!Fd { + const fd = c.socket(posix.AF.INET, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); + if (fd < 0) return error.SocketFailed; + errdefer _ = c.close(fd); + const one: c_int = 1; + _ = c.setsockopt(fd, posix.SOL.SOCKET, posix.SO.REUSEADDR, &one, @sizeOf(c_int)); + var addr: posix.sockaddr.in = .{ .port = std.mem.nativeToBig(u16, port), .addr = 0 }; + if (c.bind(fd, @ptrCast(&addr), @sizeOf(posix.sockaddr.in)) != 0) return error.AddressInUse; + if (c.listen(fd, 64) != 0) return error.SocketFailed; + return fd; +} + +/// One connection before `deadline_ns` (monotonic). +pub fn accept(fd: Fd, deadline_ns: u64) Error!Fd { + while (true) { + try waitReadable(fd, deadline_ns); + const conn = c.accept(fd, null, null); + if (conn >= 0) { + tune(conn); + return conn; + } + } +} + +/// Connects to `host:port`, retrying every 50 ms (the master may not listen yet) until `deadline_ns`. +pub fn connect(host: []const u8, port: u16, deadline_ns: u64) Error!Fd { + var name: [256]u8 = undefined; + if (host.len >= name.len) return error.AddressUnresolved; + @memcpy(name[0..host.len], host); + name[host.len] = 0; + var service: [8]u8 = undefined; + const svc = std.fmt.bufPrintSentinel(&service, "{d}", .{port}, 0) catch unreachable; + const hints: c.addrinfo = .{ .flags = .{}, .family = posix.AF.INET, .socktype = posix.SOCK.STREAM, .protocol = 0, .addrlen = 0, .addr = null, .canonname = null, .next = null }; + while (true) { + var res: ?*c.addrinfo = null; + if (@backingInt(c.getaddrinfo(name[0..host.len :0], svc, &hints, &res)) == 0) { + defer c.freeaddrinfo(res.?); + const fd = c.socket(posix.AF.INET, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); + if (fd < 0) return error.SocketFailed; + if (c.connect(fd, res.?.addr.?, res.?.addrlen) == 0) { + tune(fd); + return fd; + } + _ = c.close(fd); + } + if (nowNs() >= deadline_ns) return error.Timeout; + sleepNs(50 * std.time.ns_per_ms); + } +} + +fn waitReadable(fd: Fd, deadline_ns: u64) Error!void { + while (true) { + const t = nowNs(); + if (t >= deadline_ns) return error.Timeout; + const ms: c_int = @intCast(@min((deadline_ns - t) / std.time.ns_per_ms + 1, 1000)); + var p = [_]c.pollfd{.{ .fd = fd, .events = posix.POLL.IN, .revents = 0 }}; + const n = c.poll(&p, 1, ms); + if (n > 0) return; + } +} + +/// Every byte of `bytes`, or `error.Closed`. +pub fn sendAll(fd: Fd, bytes: []const u8) Error!void { + var off: usize = 0; + while (off < bytes.len) { + const n = c.send(fd, bytes[off..].ptr, bytes.len - off, posix.MSG.NOSIGNAL); + if (n <= 0) { + if (n < 0 and posix.errno(n) == .INTR) continue; + return error.Closed; + } + off += @intCast(n); + } +} + +/// Fills `out` before `deadline_ns` (null: no deadline), or `error.Closed` at the end of the stream. +pub fn recvExact(fd: Fd, out: []u8, deadline_ns: ?u64) Error!void { + var off: usize = 0; + while (off < out.len) { + if (deadline_ns) |d| try waitReadable(fd, d); + const n = c.recv(fd, out[off..].ptr, out.len - off, 0); + if (n <= 0) { + if (n < 0 and posix.errno(n) == .INTR) continue; + return error.Closed; + } + off += @intCast(n); + } +} + +/// A non-blocking send of the whole of `bytes` if the socket buffer has room (false: dropped, or the peer is gone). +pub fn trySend(fd: Fd, bytes: []const u8) bool { + const n = c.send(fd, bytes.ptr, bytes.len, posix.MSG.NOSIGNAL | posix.MSG.DONTWAIT); + return n == @as(isize, @intCast(bytes.len)); +} + +pub fn hangup(fd: Fd) void { + _ = c.shutdown(fd, posix.SHUT.RDWR); +} + +pub fn close(fd: Fd) void { + _ = c.close(fd); +} + +/// This host's identity (the kernel's boot id, else the host name): ranks with equal ids share memory. +pub fn hostId(out: *[64]u8) []const u8 { + @memset(out, 0); + const f = c.open("/proc/sys/kernel/random/boot_id", .{ .ACCMODE = .RDONLY }); + if (f >= 0) { + defer _ = c.close(f); + const n = c.read(f, out, out.len); + if (n > 0) return std.mem.trim(u8, out[0..@intCast(n)], " \n"); + } + if (c.gethostname(out, out.len) != 0) return ""; + return std.mem.sliceTo(out, 0); +} + +test "a listener and a client trade bytes and see the hang-up" { + const l = try listen(0); + defer close(l); + var addr: posix.sockaddr.in = undefined; + var len: posix.socklen_t = @sizeOf(posix.sockaddr.in); + try std.testing.expectEqual(@as(c_int, 0), c.getsockname(l, @ptrCast(&addr), &len)); + const port = std.mem.bigToNative(u16, addr.port); + const deadline = nowNs() + 5 * std.time.ns_per_s; + const a = try connect("localhost", port, deadline); + defer close(a); + const b = try accept(l, deadline); + defer close(b); + try sendAll(a, "hello"); + var got: [5]u8 = undefined; + try recvExact(b, &got, deadline); + try std.testing.expectEqualStrings("hello", &got); + try std.testing.expectError(error.Timeout, recvExact(b, &got, nowNs() + 20 * std.time.ns_per_ms)); + hangup(a); + try std.testing.expectError(error.Closed, recvExact(b, &got, deadline)); +} diff --git a/zig/src/fabric/tp/tp.zig b/zig/src/fabric/tp/tp.zig new file mode 100644 index 0000000000..2c9eeb85ec --- /dev/null +++ b/zig/src/fabric/tp/tp.zig @@ -0,0 +1,217 @@ +//! Tensor parallelism across processes, one GPU each: bootstrap, collectives, fail-fast; `Session.open` starts a rank. +const std = @import("std"); +const log = @import("log.zig"); +const cuda = @import("cuda"); + +pub const collective = @import("collective.zig"); +pub const reference = @import("reference.zig"); +pub const config = @import("config.zig"); +pub const sock = @import("sock.zig"); +pub const bootstrap = @import("bootstrap.zig"); +pub const fate = @import("fate.zig"); +pub const host = @import("host.zig"); +pub const nccl = @import("nccl.zig"); +pub const mailbox = @import("mailbox.zig"); +pub const hybrid = @import("hybrid.zig"); +pub const roce = @import("roce.zig"); +pub const roce_verbs = @import("roce_verbs.zig"); +pub const roce_pick = @import("roce_pick.zig"); +pub const planlink = @import("planlink.zig"); +pub const Verbs = @import("verbs").Verbs; + +pub const Collective = collective.Collective; +pub const DType = collective.DType; +pub const Op = collective.Op; +pub const Config = config.Config; +pub const Bootstrap = bootstrap.Bootstrap; +pub const Fate = fate.Fate; + +pub const Session = struct { + cfg: Config, + lib: cuda.nccl.Library, + nccl: nccl.Nccl, + mail: ?mailbox.Mailbox = null, + roce: ?*roce.Roce = null, + route: hybrid.Hybrid = undefined, + /// The round plan's link (rank 0 sends, the others receive), on its own TCP connection. + plan: planlink.PlanLink = undefined, + fate: ?Fate = null, + + /// Brings this rank up in place; `kernel_image` is mailbox.cu's fatbin, empty sends those backends to NCCL. + pub fn open(s: *Session, d: *const cuda.Driver, cfg: Config, kernel_image: []const u8) !void { + var b = try Bootstrap.open(cfg); + defer b.close(); + try b.agree(&cfg.digest(), "settings (TF_TP_WORLD, TF_COMM_BACKEND, TF_TP_MAILBOX_MAX_KB, TF_TP_ROCE_CHUNKED_KB, TF_TP_FAILFAST, TF_TP_ROCE_WIRE / _FALLBACK, TF_GRAPH_CACHE)"); + var lib = try cuda.nccl.Library.openPath(cfg.nccl_lib); + errdefer lib.close(); + const v = try lib.version(); + try b.agree(std.mem.asBytes(&v), "NCCL version"); + var id: cuda.nccl.UniqueId = undefined; + if (cfg.rank == 0) try lib.check(lib.api.ncclGetUniqueId(&id), "ncclGetUniqueId"); + try b.broadcast(&id.internal, &id.internal); + s.* = .{ .cfg = cfg, .lib = lib, .nccl = undefined }; + s.nccl = try nccl.Nccl.init(&s.lib, d, id, cfg.rank, cfg.world); + errdefer s.nccl.deinit(); + switch (cfg.backend) { + .nccl => {}, + .mailbox => if (try mailboxPossible(&b, kernel_image)) { + s.mail = try mailbox.Mailbox.init(d, &b, kernel_image, cfg.mailbox_max_bytes, cfg.mailbox_timeout_ns); + s.route = .{ .small = .{ .mailbox = &s.mail.? }, .nccl = &s.nccl, .max_bytes = cfg.mailbox_max_bytes }; + } else if (cfg.rank == 0) { + log.warn("[tensorfold] tp: TF_COMM_BACKEND=mailbox needs two ranks on one host and the kernel; every rank uses NCCL", .{}); + }, + .roce => try s.openRoce(d, &b, kernel_image), + } + try s.agreeVar(&b); + s.plan = planlink.PlanLink.init(cfg.rank, cfg.world, b.releasePlan()); + s.plan.spin_ns = cfg.plan_spin_ns; + s.plan.pin = cfg.plan_pin; + s.plan.avoid = cfg.roce_cpu; + if (cfg.failfast) { + s.fate = Fate.init(cfg, b.release()); + s.fate.?.onFail(.{ .ctx = s, .call = abortHook }); + try s.fate.?.start(); + } + log.info("[tensorfold] tp: rank {d} of {d} up ({t}, NCCL {d}, fail-fast {s}, roce fast {s}: {d} poller(s), stagger {d} ns)", .{ cfg.rank, cfg.world, s.comm().kind(), v, if (cfg.failfast) "on" else "off", if (cfg.roce_fast & 1 != 0) "on" else "off", (cfg.roce_fast >> 8) & 0xff, cfg.roce_fast >> 16 }); + } + + /// RoCE for the small exchanges, probed against NCCL at load; on failure every rank obeys TF_TP_ROCE_FALLBACK. + fn openRoce(s: *Session, d: *const cuda.Driver, b: *Bootstrap, image: []const u8) !void { + const cfg = s.cfg; + var specs: [roce_verbs.max_hcas]roce_verbs.HcaSpec = undefined; + var n_specs: usize = 0; + if (cfg.roce_wire == .verbs) n_specs = try roce_pick.resolve(cfg, b, &specs); + const opts: roce.Options = .{ .wire = if (cfg.roce_wire == .shm) .shm else .verbs, .hcas = specs[0..n_specs], .traffic_class = cfg.roce_tc, .cpu = cfg.roce_cpu, .max_bytes = cfg.mailbox_max_bytes, .timeout_ns = cfg.mailbox_timeout_ns, .fast = cfg.roce_fast, .blocks = cfg.roce_blocks }; + const r = roce.Roce.init(d, b, image, opts) catch |e| return s.roceFallback(e); + s.roce = r; + if (cfg.roce_chunked_bytes > 0 and !r.chunk_abi) { + log.warn("TF_TP_ROCE_CHUNKED_KB={d}: the RoCE kernel image has no chunked ABI (tp_roce_chunk_abi); refusing to start", .{cfg.roce_chunked_bytes / 1024}); + r.deinit(); + s.roce = null; + return error.ChunkAbiMissing; + } + s.route = .{ .small = .{ .roce = r }, .nccl = &s.nccl, .max_bytes = cfg.mailbox_max_bytes, .chunked_bytes = cfg.roce_chunked_bytes }; + const good = probe(s, d) catch false; + var blobs: [bootstrap.max_ranks][]const u8 = undefined; + var buf: [bootstrap.max_ranks]u8 = undefined; + const all = try b.allGather(&.{@intFromBool(good)}, &buf, &blobs); + for (all) |x| if (x[0] != 1) { + r.deinit(); + s.roce = null; + return s.roceFallback(error.ProbeMismatch); + }; + } + + /// `Collective.varAgreed`: whether every rank's transport moves device lengths, agreed once a session. + fn agreeVar(s: *Session, b: *Bootstrap) !void { + var blobs: [bootstrap.max_ranks][]const u8 = undefined; + var buf: [bootstrap.max_ranks]u8 = undefined; + const all = try b.allGather(&.{@intFromBool(s.comm().varFits(16))}, &buf, &blobs); + var ok = true; + for (all) |x| ok = ok and x[0] == 1; + if (s.mail != null or s.roce != null) s.route.var_agreed = ok; + } + + fn roceFallback(s: *Session, e: anyerror) !void { + if (s.cfg.roce_fallback == .@"error") return e; + if (s.cfg.rank == 0) log.warn("[tensorfold] tp: RoCE unavailable ({t}); every rank uses NCCL (TF_TP_ROCE_FALLBACK=error refuses instead)", .{e}); + } + + /// One 4 KiB all-gather and one bf16 sum through RoCE and through NCCL: the bytes must be equal. + fn probe(s: *Session, d: *const cuda.Driver) !bool { + const n = 4096; + // input [0, n), gathers [n, 3n) and [3n, 5n), sums [5n, 6n) and [6n, 7n) + var a = try cuda.DeviceBuffer.alloc(d, 7 * n); + defer a.free(); + var stream = try cuda.Stream.init(d, true); + defer stream.deinit(); + var bytes: [n]u8 = undefined; + reference.fill(&bytes, .bf16, s.cfg.rank, 0x7072_6f62); + try a.uploadAsync(0, &bytes, stream.handle); + const fast = s.route.iface(); + const slow = s.nccl.iface(); + try fast.allGather(a.ptr, a.ptr + n, n, .u8, stream.handle); + try slow.allGather(a.ptr, a.ptr + 3 * n, n, .u8, stream.handle); + try fast.allReduce(a.ptr, a.ptr + 5 * n, n / 2, .bf16, .sum, stream.handle); + try slow.allReduce(a.ptr, a.ptr + 6 * n, n / 2, .bf16, .sum, stream.handle); + try stream.synchronize(); + try fast.check(); + var got: [6 * n]u8 = undefined; + try a.download(n, &got); + const gathers = std.mem.eql(u8, got[0 .. 2 * n], got[2 * n .. 4 * n]); + const sums = std.mem.eql(u8, got[4 * n .. 5 * n], got[5 * n .. 6 * n]); + if (!gathers or !sums) log.warn("[tensorfold] tp roce probe: gather {s}, sum {s} against NCCL", .{ if (gathers) "equal" else "DIFFERS", if (sums) "equal" else "DIFFERS" }); + return gathers and sums; + } + + fn mailboxPossible(b: *Bootstrap, image: []const u8) !bool { + if (b.world != 2) return false; + var id_buf: [64]u8 = undefined; + const id = sock.hostId(&id_buf); + var mine: [65]u8 = @splat(0); + mine[0] = @intFromBool(image.len > 0); + @memcpy(mine[1..][0..id.len], id); + var blobs: [bootstrap.max_ranks][]const u8 = undefined; + var buf: [2 * 65]u8 = undefined; + const all = try b.allGather(&mine, &buf, &blobs); + return all[0][0] == 1 and std.mem.eql(u8, all[0], all[1]); + } + + fn abortHook(ctx: *anyopaque, _: []const u8) void { + const s: *Session = @ptrCast(@alignCast(ctx)); + s.comm().abort(); + } + + /// The communicator the model uses: NCCL, or the hybrid route over the mailbox / RoCE. + pub fn comm(s: *Session) Collective { + if (s.mail != null or s.roce != null) return s.route.iface(); + return s.nccl.iface(); + } + + /// This rank failed inside a round: every rank goes down (exit 70). Without fail-fast it only aborts. + pub fn fatal(s: *Session, where: []const u8, err: anyerror) void { + if (s.fate) |*f| return f.fatal(where, err); + s.comm().abort(); + } + + /// A clean stop announced by rank 0 before it closes: followers end with no fail-fast exit. + pub fn announceStop(s: *Session) void { + if (s.fate) |*f| f.announceStop(); + } + + /// This rank failed after `open`, before serving: the other ranks learn why and go down (fail-fast). + pub fn bootFailed(s: *Session, err: anyerror) void { + if (s.fate) |*f| f.failBoot("boot", err); + s.close(); + } + + /// A normal stop: every rank calls it (rank 0 first marks the stop as expected with `fate.expectStop`). + pub fn close(s: *Session) void { + // a failed transport: every rank goes down (the peers stop posting) before this one lets go of anything + if (s.roce) |r| if (r.failed()) if (s.fate) |*f| f.fatal("transport", error.BackendFailed); + if (s.fate) |*f| f.close(); + s.plan.close(); + if (s.roce) |r| r.deinit(); + if (s.mail) |*m| m.deinit(); + s.nccl.deinit(); + s.lib.close(); + } +}; + +test { + _ = collective; + _ = reference; + _ = config; + _ = sock; + _ = bootstrap; + _ = mailbox; + _ = roce_verbs; + _ = roce_pick; + _ = planlink; + _ = @import("roce_guard.zig"); + _ = @import("roce_test.zig"); + _ = @import("roce_post_test.zig"); + _ = @import("roce_inflight_test.zig"); + _ = @import("host_test.zig"); + _ = @import("fate_test.zig"); +} diff --git a/zig/src/fabric/verbs.zig b/zig/src/fabric/verbs.zig index 9b9353c554..3c32714d30 100644 --- a/zig/src/fabric/verbs.zig +++ b/zig/src/fabric/verbs.zig @@ -2,7 +2,7 @@ const std = @import("std"); const platform = @import("sys"); const builtin = @import("builtin"); -const abi = @import("verbs_abi.zig"); +pub const abi = @import("verbs_abi.zig"); pub const Error = error{ LibraryUnavailable, MissingSymbol, NoDeviceList, NoSuchDevice, VerbsFailed, BadDeviceList, NoUsableGid }; pub const max_links = 8; @@ -51,12 +51,15 @@ pub const Api = struct { /// Exported by rdma-core 34+ but not in every header; used only when present. const GidEx = *const fn (ctx: Ctx, port: u32, index: u32, entry: *abi.GidEntry, flags: u32, size: usize) callconv(.c) c_int; const InOrder = *const fn (qp: *abi.Qp, op: c_int, flags: u32) callconv(.c) c_int; +/// ibv_query_qp: the QP's state as the device holds it (not every library exports it; used only when present). +pub const QueryQp = *const fn (qp: *abi.Qp, attr: *abi.QpAttr, mask: c_int, init: *abi.QpInitAttr) callconv(.c) c_int; pub const Verbs = struct { lib: platform.DynLib, api: Api, gid_ex: ?GidEx = null, in_order: ?InOrder = null, + query_qp: ?QueryQp = null, pub fn open(path: ?[:0]const u8) Error!Verbs { var lib = platform.DynLib.openZ((path orelse default_path).ptr) catch return error.LibraryUnavailable; @@ -69,6 +72,7 @@ pub const Verbs = struct { var v: Verbs = .{ .lib = lib, .api = api }; v.gid_ex = v.lib.lookup(GidEx, "_ibv_query_gid_ex"); v.in_order = v.lib.lookup(InOrder, "ibv_query_qp_data_in_order"); + v.query_qp = v.lib.lookup(QueryQp, "ibv_query_qp"); return v; } diff --git a/zig/src/families/deepseek_v41/block.zig b/zig/src/families/deepseek_v41/block.zig new file mode 100644 index 0000000000..cee25c90e4 --- /dev/null +++ b/zig/src/families/deepseek_v41/block.zig @@ -0,0 +1,520 @@ +//! A decode window as calls: every launch with its arguments and role-named tensors ("s." / "w." / "L." lifetimes). + +const std = @import("std"); +const dk = @import("dsv41_kernels"); +const calls = @import("block_calls.zig"); +const config = @import("config.zig"); +const knob_types = @import("knob_types.zig"); +const moe = @import("block_moe.zig"); +const block_prefetch = @import("block_prefetch.zig"); +const block_attn = @import("block_attn.zig"); +const block_index = @import("block_index.zig"); +const Config = config.Config; +const Arg = calls.Arg; +const Named = calls.Named; +const Dt = calls.Dt; +const dense = dk.dense; + +/// The widths the loader picked: K2 of each dense group and each routed layer's expert K2 range (gate/up, down). +pub const Widths = struct { + dense: std.StringHashMapUnmanaged(u32) = .empty, + experts: std.AutoHashMapUnmanaged(u32, [2][2]u32) = .empty, + /// each routed layer's expert K2s present (bit k2 set), gate/up and down: x3gm runs one launch a width present + gm: std.AutoHashMapUnmanaged(u32, [2]u32) = .empty, + + pub fn k2(w: *const Widths, name: []const u8) !u32 { + return w.dense.get(name) orelse error.NoWidth; + } +}; + +/// Run-time sizes fixed at load (scratch and pools), and the L2 prefetch settings. +pub const Options = struct { + /// TF_DSV41_IMAGES=native: this prefill segment holds image positions, so Engram fuses with a keep vector + image_keep: bool = false, + /// TF_DSV41_BIAS_VL: the segment's image rows (MoE call apart, gate.bias_vl); 0: none; < 0: the buffer plan's + image_rows: i64 = 0, + /// TP ranks (a boot constant): every per-rank width below is the model's / world; the plan checked divisibility + world: u32 = 2, + /// KV positions a slot holds (pool rows = limit / ratio + 1) + limit: i64 = 4096, + /// rope table rows (cos / sin pairs of 32) + rope_rows: i64 = 6144, + /// rows the routed experts' scratch holds: the backbone's and a DSpark block's + expert_rows: i64 = 1024, + dspark_rows: i64 = 64, + /// TF_DSV41_BRANCHES: the attention sublayer's indexer / compressor chains tagged for the side stream + branches: bool = false, + branch_rows: i64 = 64, + /// TF_DSV41_MHC_DEFER (with `r1`): its coefficient launch on the deferred stream, joined by the next mHC call + mhc_defer: bool = false, + /// TF_DSV41_MHC_DEFER_AT=exchange: deferred launches held to the next exchange; _ROWS: only up to that many rows + mhc_defer_hold: bool = false, + /// TF_DSV41_COEF_LATE (with MHC_DEFER): a deferred coef launch moves to just before its sublayer's exchange + coef_late: bool = false, + mhc_defer_hold_rows: i64 = 0, + /// TF_DSV41_MHC_PFDEC (0 = off): mixing sites of windows of at least this many rows on mhc_pf (block_wide.zig) + mhc_pf_rows: i64 = 0, + /// TF_DSV41_L2PF_MB (bytes), _CHUNK_KB, _PACE_GBPS, _PACE_CTAS; the GPU's SMs (a segments launch's grid bound) + l2pf_budget: u64 = 12 << 20, + l2pf_chunk: i64 = 32768, + l2pf_pace_gbps: f64 = 150, + l2pf_pace_ctas: i64 = 2, + sms: i64 = 188, + /// TF_DSV41_EXPERT_TOPP (lossy). null: no pruning; the router's tail rounds kit weights and groups experts itself + expert_topp: ?f64 = 0.85, + /// after each block, its streams with the last post applied out of place, to glue step "trace" (never served) + trace: bool = false, + /// DSpark's taps: boundaries entering target layers write the streams' mean into "w.taps" bf16 [n, 3 x hidden] + taps: bool = false, + /// rows a prefill segment holds at most (forward.pchunk): sizes the SWA staging ring (block_prefill.zig) + prefill_rows: i64 = 2048, + /// TF_DSV41_PF_4K: prefill segments up to 4,096 rows, router in 2,048-row launches; off: 2,048 (longer refused) + pf4k: bool = false, + /// TF_DSV41_PF_OVERLAP: a prefill layer's two exchanges in this many row pieces on the side stream (branches, TP=2) + pf_overlap: i64 = 0, + /// TF_DSV41_PF_OVERLAP_SITE (with pf_overlap): all exchange pieces on the side stream, the next site in row pieces + pf_overlap_site: bool = false, + /// TF_DSV41_MHC_SITE_ROWS (0 off; 256-4,096, multiple of 16): a prefill boundary site in row pieces on main + mhc_site_rows: i64 = 0, + /// TF_DSV41_PF_TBO: CED encoder segments two at a time, the second a layer behind on the side stream (`branches`) + pf_tbo: bool = false, + /// TF_DSV41_STREAM_RB (0 off; 1 / 2 / 4): the long-context stream top-k as the row-blocked twin `_stream_rb` + stream_rb: i64 = 0, + /// TF_DSV41_INDEX_BOUND: decode index scores and top-k as row-bounded twins that stop at each row's visible end + index_bound: bool = false, + /// decode knob set (same bits): ATTN_SPLIT, ATTN_ROPE, MHC_TAIL, MHC_DEFER, RG_PRUNE, PRUNE_KIT, MOE_BF16, RMS_FOLD + r1: bool = false, + /// TF_DSV41_ROUTER_GROUP_ROT: wide grouping and expert input rotation in one launch (default off). + router_group_rot: bool = false, + /// TF_DSV41_X3LD_EPI: decode x3ld + epilogues as two fused launches (ticket words "s.ex.epi" / "s.dx.epi") + x3ld_epi: bool = false, + /// KV-only RMS feeding SWA store; separately gated, default off. + kv_norm_store: bool = false, + /// `r1` off but a twin's bindings that always take its trailing arguments, at their off values (same launches) + r1_sig: bool = false, + /// MHC_TAIL_SPIN (SM cycles a CTA waits for the last arrival) + mhc_spin: i64 = 8000, + /// the KV families in the paged pool (kv.DevicePool) instead of the slot's contiguous buffers; null: contiguous + pool: ?Pool = null, + /// TF_DSV41_PF_DENSE=fused: prefill dense EXL3 GEMMs on the pfdense kernel; null: W_q unpack + `_gemm` (same bits) + pfd: ?*const knob_types.PfDense = null, + /// TF_DSV41_GM_V2 (gm2pf.zig): prefill's routed experts on x3gm v2; off: v1. The same bits either way. + gm_v2: knob_types.GmMode = .off, + /// TF_DSV41_GM_GU2 (with gm_v2 `one`): a 4,096-row segment's gate / up at the 128-member tile GU2 + gm_gu2: bool = false, + /// TF_DSV41_INDEX_BUDGET_MIB (bytes): indexer scores + sort keys a launch; past it, row blocks (same bits) + index_budget: i64 = 256 << 20, + /// a row-mode program (batch.zig, rowmode.zig): row blocks score the context bucket's keys; no streamed top-k + rows: bool = false, + + /// TF_DSV41_MHC_DEFER_AT(_ROWS): a program of `rows` rows issues its deferred launches at the next exchange. + pub fn holdsDeferred(o: Options, rows: i64) bool { + return o.mhc_defer_hold and (o.mhc_defer_hold_rows == 0 or rows <= o.mhc_defer_hold_rows); + } +}; + +/// The paged pool as a window's calls see it: comp rows, index keys, page table and split table (TF_DSV41_KV_SPLIT). +pub const Pool = struct { + /// pages each family tensor holds (comp: split's local pages + null + discard) + comp_pages: i64, + ik_pages: i64, + /// pages a slot's table holds + pts: i64, + split: bool = false, + /// TF_DSV41_KV_SPLIT_EARLY: an index layer's exchange right after its selection (side stream), beside the q group + early: bool = false, + /// split's union cap (TF_DSV41_KV_SPLIT_UNION_MIB): the prefill receive buffer's bytes + union_cap: i64 = 128 << 20, + pub const page: i64 = 256; + pub const row_bytes: i64 = 584; + pub const ik_bytes: i64 = 132; + + /// log2 of a family's rows a page + pub fn shift(ratio: i64) i64 { + return std.math.log2_int(u64, @intCast(@divExact(page, ratio))); + } +}; + +pub const Emitter = struct { + a: std.mem.Allocator, + cfg: *const Config, + w: *const Widths, + o: Options, + /// rows this window and the position of its first + n: i64, + start: i64, + out: std.ArrayList(calls.Call) = .empty, + /// which of the two stream buffers holds the streams, and which coefficient set the last boundary wrote + cur: u1 = 0, + coef: u1 = 0, + begin: calls.Begin = .window, + /// the current layer's compression ratio (picks its rope table) + layer_ratio: u32 = 0, + /// TF_DSV41_BRANCHES: the stream the next calls go to, and the marks the next call carries + br_side: bool = false, + br_fork: bool = false, + br_join: bool = false, + br_mark: u8 = 0, + br_wait: u8 = 0, + /// TF_DSV41_MHC_DEFER: the next call is the coefficient launch / joins it; a coefficient launch not joined yet + df_side: bool = false, + df_join: bool = false, + df_pending: bool = false, + /// TF_DSV41_COEF_LATE: the index of a deferred coefficient launch not joined yet (moved to the next exchange) + df_at: ?usize = null, + /// a multi-segment prefill: the current segment's first row in the run's per-row roles; 0 elsewhere + row0: i64 = 0, + /// TF_DSV41_PF_TBO (block_prefill.emitTbo): the call indexes where each layer's attention part and MoE part start + marks: ?*std.ArrayList(usize) = null, + /// its first row in the run's narrow per-row roles, a multiple of 4 (16-byte aligned slices); 0 elsewhere + cnt0: i64 = 0, + + pub fn fmt(e: *Emitter, comptime f: []const u8, args: anytype) ![]const u8 { + return std.fmt.allocPrint(e.a, f, args); + } + + /// A contiguous tensor of `role`. + pub fn t(e: *Emitter, role: calls.Role, dt: Dt, shape: []const i64) !Arg { + const s = try e.a.dupe(i64, shape); + return .{ .t = .{ .role = role, .dt = dt, .shape = s, .stride = try calls.contiguous(e.a, s) } }; + } + + /// A view of `role`: its own strides and a byte offset. + pub fn view(e: *Emitter, role: calls.Role, dt: Dt, shape: []const i64, stride: []const i64, offset: i64) !Arg { + return .{ .t = .{ .role = role, .dt = dt, .shape = try e.a.dupe(i64, shape), .stride = try e.a.dupe(i64, stride), .offset = offset } }; + } + + pub fn buf(e: *Emitter, comptime f: []const u8, args: anytype, dt: Dt, shape: []const i64) !Arg { + return e.t(.{ .buf = try e.fmt(f, args) }, dt, shape); + } + + pub fn weight(e: *Emitter, comptime f: []const u8, args: anytype, dt: Dt, shape: []const i64) !Arg { + return e.t(.{ .weight = try e.fmt(f, args) }, dt, shape); + } + + pub fn empty(e: *Emitter, dt: Dt) !Arg { + return e.t(.empty, dt, &.{0}); + } + + pub fn ints(e: *Emitter, xs: []const i64) !Arg { + const l = try e.a.alloc(Arg, xs.len); + for (xs, l) |x, *y| y.* = .{ .i = x }; + return .{ .list = l }; + } + + fn push(e: *Emitter, c: calls.Call) !void { + var x = c; + x.begin = e.begin; + e.begin = .none; + x.side = e.br_side; + x.fork = e.br_fork; + x.join = e.br_join; + x.mark = e.br_mark; + x.wait = e.br_wait; + e.br_fork = false; + e.br_join = false; + e.br_mark = 0; + e.br_wait = 0; + x.defer_side = e.df_side; + x.defer_join = e.df_join; + e.df_side = false; + e.df_join = false; + if (e.o.coef_late and e.marks == null) try e.lateCoef(&x); + try e.out.append(e.a, x); + } + + /// TF_DSV41_COEF_LATE: a deferred coef launch moves to just before the next exchange of its fork region; same data. + fn lateCoef(e: *Emitter, x: *const calls.Call) !void { + if (x.defer_join) e.df_at = null; + if (x.defer_side) { + e.df_at = e.out.items.len; + return; + } + const at = e.df_at orelse return; + if (!(x.glue and std.mem.startsWith(u8, x.name, "glue.exchange"))) return; + e.df_at = null; + const c = e.out.orderedRemove(at); + std.debug.assert(c.defer_side and c.begin == .none); + try e.out.append(e.a, c); + } + + /// Appends positional arguments to the last emitted call (`r1`'s trailing arguments). + pub fn appendLast(e: *Emitter, args: []const Arg) !void { + const last = &e.out.items[e.out.items.len - 1]; + const more = try e.a.alloc(calls.Named, last.args.len + args.len); + @memcpy(more[0..last.args.len], last.args); + for (more[last.args.len..], args) |*m, x| m.* = .{ .arg = x }; + last.args = more; + } + + pub fn ext(e: *Emitter, name: []const u8, args: []const Arg) !void { + const l = try e.a.alloc(Named, args.len); + for (args, l) |x, *y| y.* = .{ .arg = x }; + try e.push(.{ .triton = false, .name = name, .args = l }); + } + + /// A glue step ("glue."): the roles it reads / writes and its scalars, for the forward to run. + pub fn glue(e: *Emitter, name: []const u8, args: []const Arg) !void { + const l = try e.a.alloc(Named, args.len); + for (args, l) |x, *y| y.* = .{ .arg = x }; + try e.push(.{ .triton = false, .glue = true, .name = try e.fmt("glue.{s}", .{name}), .args = l }); + } + + pub fn triton(e: *Emitter, name: []const u8, grid: [3]i64, args: []const Named) !void { + try e.push(.{ .triton = true, .name = name, .grid = grid, .args = try e.a.dupe(Named, args) }); + } + + /// A config value's float as the JSON number (f64), not the f32 we keep. + pub fn dec(x: f32) f64 { + var b: [64]u8 = undefined; + const s = std.fmt.bufPrint(&b, "{e}", .{x}) catch return x; + return std.fmt.parseFloat(f64, s) catch x; + } + + /// Positions after this window: the last row's + 1. + pub fn end(e: *const Emitter) i64 { + return e.start + e.n; + } + + pub fn streams(e: *Emitter) !Arg { + return e.buf("w.x{d}", .{e.cur}, .bf16, &.{ e.n, 4 * @as(i64, e.cfg.hidden) }); + } + + pub fn spare(e: *Emitter) !Arg { + return e.buf("w.x{d}", .{1 - e.cur}, .bf16, &.{ e.n, 4 * @as(i64, e.cfg.hidden) }); + } + + // dense EXL3 projections --------------------------------------------------------------------------------------- + + /// A trellis's run-time words (int32 [N/128, K/16, 8, 4 K2]): strips "s..T" or dense3 lanes "s..lanes". + pub fn strips(e: *Emitter, w: []const u8, k: i64, n: i64) !Arg { + return e.words(w, k, n, "T"); + } + + fn words(e: *Emitter, w: []const u8, k: i64, n: i64, form: []const u8) !Arg { + const k2: i64 = try e.w.k2(w); + return e.t(.{ .buf = try e.fmt("s.{s}.{s}", .{ w, form }) }, .i32, &.{ @divExact(n, 128), @divExact(k, 16), 8, 4 * k2 }); + } + + /// Bytes of a group's trellis (= its strips). + pub fn trellisBytes(e: *Emitter, w: []const u8, k: i64, n: i64) !u64 { + const k2: u64 = try e.w.k2(w); + return @as(u64, @intCast(@divExact(k, 16) * @divExact(n, 16))) * 8 * k2 * 2; + } + + /// One projection of a fused group: weight prefix, input, output (a whole buffer; `col` its first column), K x N. + pub const Proj = struct { w: []const u8, x: Arg, y: Arg, col: usize = 0, k: usize, n: usize }; + + /// `r1`'s RMS_FOLD: rot_in norms its rows itself (rms(x, w, eps), rmsnorm's order) before the rotation. + pub const Norm = struct { w: Arg, eps: f64 }; + + /// x3seg rot_in, then dense3 a width: the group's projections in one rot_in launch, one linear launch a K2. + pub fn fused(e: *Emitter, tag: []const u8, ps: []const Proj) !void { + return e.fusedNorm(tag, ps, null); + } + + pub fn fusedNorm(e: *Emitter, tag: []const u8, ps: []const Proj, norm: ?Norm) !void { + const m: usize = @intCast(e.n); + const segs = try e.a.alloc(dense.Seg, ps.len); + for (ps, segs, 0..) |p, *s, i| { + const pl = dense.plan(p.k, p.n); + s.* = .{ .k = p.k, .n = p.n, .k2 = try e.w.k2(p.w), .sk = pl[0], .wk = pl[1], .out = i, .col = p.col }; + } + const lay = try dense.layout(segs, m); + const xs = try e.a.alloc(Arg, ps.len); + const suh = try e.a.alloc(Arg, ps.len); + const offs = try e.a.alloc(i64, ps.len); + for (ps, xs, suh, offs, 0..) |p, *x, *s, *o, i| { + x.* = p.x; + s.* = try e.weight("{s}.suh", .{p.w}, .f16, &.{@intCast(p.k)}); + o.* = @intCast(lay.xh_off[i]); + } + const xh = try e.buf("L.{s}.xh", .{tag}, .f16, &.{@intCast(lay.xh_total)}); + if (norm) |nm| { + const ws = try e.a.alloc(Arg, ps.len); + @memset(ws, nm.w); + try e.ext("tf_dsv41_x3seg_v3.rot_in", &.{ .{ .list = xs }, .{ .list = suh }, xh, try e.ints(offs), .{ .b = false }, .{ .list = ws }, .{ .f = nm.eps } }); + } else try e.ext("tf_dsv41_x3seg_v3.rot_in", &.{ .{ .list = xs }, .{ .list = suh }, xh, try e.ints(offs), .{ .b = false } }); + const z: Arg = if (lay.z_total > 0) try e.buf("L.{s}.z", .{tag}, .f32, &.{@intCast(lay.z_total)}) else .none; + const counters = try e.buf("L.{s}.cnt", .{tag}, .i32, &.{@intCast(lay.c_total)}); + for (lay.launches[0..lay.n_launches]) |l| { + const T = try e.a.alloc(Arg, l.count); + const svh = try e.a.alloc(Arg, l.count); + const ys = try e.a.alloc(Arg, l.count); + var meta: std.ArrayList(i64) = .empty; + for (l.segs[0..l.count], 0..) |si, j| { + const s = segs[si]; + const p = ps[si]; + T[j] = try e.words(p.w, @intCast(p.k), @intCast(p.n), "lanes"); + svh[j] = try e.weight("{s}.svh", .{p.w}, .f16, &.{@intCast(p.n)}); + ys[j] = p.y; + const st = dense.strides(s.k, s.k2); + try meta.appendSlice(e.a, &.{ @intCast(s.k), @intCast(s.n), @intCast(s.sk), @intCast(s.wk), st[0], st[1], @intCast(lay.xh_off[si]), @intCast(lay.z_off[si]), @intCast(lay.c_off[si]), @intCast(s.col), @intCast(l.p0[j]) }); + } + try e.ext("tf_dsv41_dense3_v1.linear", &.{ + xh, .{ .list = T }, .{ .list = svh }, .{ .list = ys }, + z, counters, try e.ints(meta.items), .{ .i = @intCast(l.programs) }, + .{ .i = l.k2 }, .{ .i = @intCast(l.wk) }, .{ .i = if (l.wk == 8) 1 else 3 }, .{ .b = false }, + }); + } + } + + /// Upstream's rot_in + linear: one projection, its trellis as strips unless one 128-column block. + pub fn upstream(e: *Emitter, w: []const u8, x: Arg, k: i64, n: i64, y: Arg) !void { + const k2: i64 = try e.w.k2(w); + const xh = try e.buf("L.{s}.xh", .{w}, .f16, &.{ e.n, k }); + try e.ext("tensorfold_exl3_linear_v4.rot_in", &.{ x, try e.weight("{s}.suh", .{w}, .f16, &.{k}), xh }); + const pl = dense.plan(@intCast(k), @intCast(n)); + const st = dense.strides(@intCast(k), @intCast(k2)); + const T = if (n == 128) + try e.view(.{ .weight = try e.fmt("{s}.trellis", .{w}) }, .i32, &.{ 1, @divExact(k, 16), 8, 4 * k2 }, &.{ 32 * k2, 32 * k2, 4 * k2, 1 }, 0) + else + try e.strips(w, k, n); + if (pl[0] != 1) return error.Unsupported; // no upstream linear on the decode path splits K + try e.ext("tensorfold_exl3_linear_v4.linear", &.{ + xh, T, .{ .i = st[0] }, .{ .i = st[1] }, try e.weight("{s}.svh", .{w}, .f16, &.{n}), .none, y, .none, + try e.buf("L.{s}.cnt", .{w}, .i32, &.{8 * @divExact(n, 128)}), .{ .i = k2 }, .{ .i = 2 }, .{ .i = @intCast(pl[0]) }, .{ .i = @intCast(pl[1]) }, + }); + } + + // L2 prefetch (TF_DSV41_L2PF): table rows filled by l2pf.zig at boot, issued on the runner's side stream ------ + + /// What a site prefetches, in read order: a weight, a projection's trellis words, or an expert entry's trellis. + pub const L2Src = struct { kind: enum { weight, words, expert }, name: []const u8, index: u32 = 0, bytes: u64 }; + pub const L2Site = enum { o, f, x }; + + pub const linSrcs = block_prefetch.linSrcs; + pub const l2pfSources = block_prefetch.l2pfSources; + pub const srcSizes = block_prefetch.srcSizes; + pub const take = block_prefetch.take; + pub const prefetchO = block_prefetch.prefetchO; + pub const paced = block_prefetch.paced; + pub const prefetchF = block_prefetch.prefetchF; + pub const prefetchX = block_prefetch.prefetchX; + + const Group = struct { ws: []const []const u8, ks: []const i64, ns: []const i64 }; + + /// The x group (fused_proj): wq_a, wkv, then a KV source's compressor projections. + pub fn xGroup(e: *Emitter, L: u32) !Group { + const D: i64 = e.cfg.hidden; + const ratio = e.cfg.compressRatio(L); + const comp: usize = if (e.cfg.isKvSource(L)) (if (ratio == 2) 2 else 1) else 0; + const ws = try e.a.alloc([]const u8, 2 + comp); + const ns = try e.a.alloc(i64, 2 + comp); + const names = [_][]const u8{ "wq_a", "wkv", "comp_wkv", "comp_wgate" }; + for (ws, ns, 0..) |*w, *n, i| { + w.* = try e.fmt("L{d}.attn.{s}", .{ L, names[i] }); + n.* = if (i == 0) e.cfg.q_lora else e.cfg.head_dim; + } + const ks = try e.a.alloc(i64, ws.len); + @memset(ks, D); + return .{ .ws = ws, .ks = ks, .ns = ns }; + } + + /// The q group: wq_b, and an indexer's ix_wq_b. + pub fn qGroup(e: *Emitter, L: u32) !Group { + const ix = switch (e.cfg.mode(L)) { + .full, .reindex => true, + else => false, + }; + const ws = try e.a.alloc([]const u8, if (ix) 2 else 1); + const ns = try e.a.alloc(i64, ws.len); + ws[0] = try e.fmt("L{d}.attn.wq_b", .{L}); + ns[0] = @as(i64, e.cfg.heads / e.o.world) * e.cfg.head_dim; + if (ix) { + ws[1] = try e.fmt("L{d}.attn.ix_wq_b", .{L}); + ns[1] = @as(i64, e.cfg.index_heads) * e.cfg.index_dim; + } + const ks = try e.a.alloc(i64, ws.len); + @memset(ks, e.cfg.q_lora); + return .{ .ws = ws, .ks = ks, .ns = ns }; + } + + pub const rope = block_attn.rope; + pub const ropeTable = block_attn.ropeTable; + pub const pos = block_attn.pos; + pub const kvStore = block_attn.kvStore; + + /// A family's paging: the slot's table, rows-a-page shift and row strides (pool mode); none for ring / contiguous. + pub const Paging = struct { pt: Arg, psh: i64, vs: i64, ss: i64 }; + + pub const paging = block_attn.paging; + pub const pool = block_attn.pool; + pub const indexKeys = block_attn.indexKeys; + + /// Split KV: source `src`'s selected rows from their owners (dense up to 16 rows, else a union), read unpaged. + pub const Received = struct { cv: Arg, cs: Arg, tok: Arg }; + + pub const received = block_attn.received; + pub const exchange = block_attn.exchange; + pub const selection = block_index.selection; + pub const ticketLen = block_index.ticketLen; + pub const attnSplit = block_index.attnSplit; + pub const rms = block_attn.rms; + pub const kvNormStoreOn = block_attn.kvNormStoreOn; + pub const normStore = block_attn.normStore; + pub const swaStore = block_attn.swaStore; + pub const branching = block_attn.branching; + pub const fork = block_attn.fork; + pub const onMain = block_attn.onMain; + pub const join = block_attn.join; + pub const attention = block_attn.attention; + pub const index = block_index.index; + pub const scoreRows = block_index.scoreRows; + pub const blocked = block_index.blocked; + pub const blockPos = block_index.blockPos; + pub const topk = block_index.topk; + pub const topkOf = block_index.topkOf; + + // the window ------------------------------------------------------------------------------------------------- + + /// Every launch of a decode window of `n` rows from `start` over `layers` (ascending), and the head when `head`. + pub fn window(e: *Emitter, layers: []const u32, head: bool) !void { + // the window's positions and streams (this rank's embedding rows, their all-sum: the first exchange) + try e.glue("positions", &.{ try e.pos(), try e.buf("w.pos64", .{}, .i64, &.{e.n}), try e.buf("w.lo", .{}, .i32, &.{e.n}) }); + try e.glue("embed", &.{ try e.streams(), try e.buf("w.recv.moe", .{}, .bf16, &.{ e.o.world, e.n, e.cfg.hidden }) }); + for (layers, 0..) |L, i| { + if (i > 0) e.begin = .layer; + e.layer_ratio = e.cfg.compressRatio(L); + if (e.cfg.isEngram(L)) { + // an Engram block after the first: the last exchange's post alone, Engram, then a site2 collapse + if (i > 0) try moe.postOnly(e, null); + try moe.engram(e, L); + try moe.mhc(e, L, "hc_attn", if (i == 0) .site1 else .site2); + } else try moe.mhc(e, L, "hc_attn", if (i == 0) .site1 else .boundary); + try e.attention(L); + try e.prefetchF(L); + const D: i64 = e.cfg.hidden; + try e.glue("exchange", &.{ try e.buf("L.part", .{}, .bf16, &.{ e.n, D }), try e.buf("w.recv.attn", .{}, .bf16, &.{ e.o.world, e.n, D }) }); + try moe.mhc(e, L, "hc_ffn", .boundary); + try moe.moe(e, L); + if (i + 1 < layers.len) try e.prefetchX(layers[i + 1]); + // `r1` (MOE_BF16): down_combine stores the partial as bf16, the exchange takes it as is + if (e.o.r1) + try e.glue("exchange", &.{ try e.buf("L.moe", .{}, .bf16, &.{ e.n, D }), try e.buf("w.recv.moe", .{}, .bf16, &.{ e.o.world, e.n, D }) }) + else + try e.glue("exchange_f32", &.{ try e.buf("L.moe", .{}, .f32, &.{ e.n, D }), try e.buf("w.recv.moe", .{}, .bf16, &.{ e.o.world, e.n, D }) }); + if (e.o.trace) { + const y = try e.buf("w.trace", .{}, .bf16, &.{ e.n, 4 * D }); + try moe.postOnly(e, y); + try e.glue("trace", &.{ y, .{ .i = L } }); + } + } + if (head) try moe.finish(e); + } +}; + +/// The window's calls (arena-owned by `a`). +pub fn emit(a: std.mem.Allocator, cfg: *const Config, w: *const Widths, o: Options, layers: []const u32, n: i64, start: i64, head: bool) ![]calls.Call { + var e: Emitter = .{ .a = a, .cfg = cfg, .w = w, .o = o, .n = n, .start = start }; + try e.window(layers, head); + if (o.holdsDeferred(n)) calls.holdDeferred(e.out.items); + return e.out.items; +} + +/// exl3 expert_loads.k2_range: the instance covering a layer's half-bit widths [lo, hi]. +pub fn k2Range(lo: u32, hi: u32) ![2]i64 { + const ranges = [_][2]u32{ .{ 8, 8 }, .{ 2, 10 }, .{ 2, 12 } }; + for (ranges) |r| if (r[0] <= lo and hi <= r[1]) return .{ r[0], r[1] }; + return error.NoInstance; +} diff --git a/zig/src/families/deepseek_v41/block_attn.zig b/zig/src/families/deepseek_v41/block_attn.zig new file mode 100644 index 0000000000..24b828879f --- /dev/null +++ b/zig/src/families/deepseek_v41/block_attn.zig @@ -0,0 +1,344 @@ +//! A decode window's attention: rope, the KV stores and pools, the compressed exchange, SWA and CSA2 launches. + +const calls = @import("block_calls.zig"); +const block = @import("block.zig"); +const Arg = calls.Arg; +const Pool = block.Pool; +const Emitter = block.Emitter; +const dec = block.Emitter.dec; +const Proj = block.Emitter.Proj; +const Norm = block.Emitter.Norm; +const Paging = block.Emitter.Paging; +const Received = block.Emitter.Received; +const attnSplit = block.Emitter.attnSplit; + +// attention ---------------------------------------------------------------------------------------------------- + +pub fn rope(e: *Emitter, x: Arg, heads: i64, dim: i64, nope: i64) !void { + const n = e.n; + const xv = try e.view(x.t.role, .bf16, &.{ n, heads, dim }, &.{ heads * dim, dim, 1 }, 0); + try e.triton("_rope", .{ n, @divExact(heads, 4), 1 }, &.{ + .{ .name = "X", .arg = xv }, .{ .name = "x_rs", .arg = .{ .i = heads * dim } }, + .{ .name = "x_hs", .arg = .{ .i = dim } }, .{ .name = "OUT", .arg = xv }, + .{ .name = "o_rs", .arg = .{ .i = heads * dim } }, .{ .name = "o_hs", .arg = .{ .i = dim } }, + .{ .name = "CS", .arg = try e.ropeTable() }, .{ .name = "cs_rs", .arg = .{ .i = 64 } }, + .{ .name = "POS", .arg = try e.buf("w.pos64", .{}, .i64, &.{n}) }, .{ .name = "H", .arg = .{ .i = heads } }, + .{ .name = "NOPE", .arg = .{ .i = nope } }, .{ .name = "NB", .arg = .{ .i = 64 } }, + .{ .name = "HALF", .arg = .{ .i = 32 } }, .{ .name = "BH", .arg = .{ .i = 4 } }, + .{ .name = "INV", .arg = .{ .b = false } }, .{ .name = "COPY", .arg = .{ .b = false } }, + .{ .name = "PDL", .arg = .{ .b = false } }, + }); +} + +/// The rope table (cos / sin) a layer uses: the compressed layers' (compress_theta) or the SWA layers'. +pub fn ropeTable(e: *Emitter) !Arg { + return e.buf("s.rope.{s}", .{if (e.layer_ratio > 0) "comp" else "main"}, .f32, &.{ e.o.rope_rows, 64 }); +} + +pub fn pos(e: *Emitter) !Arg { + return e.buf("w.pos", .{}, .i32, &.{1}); +} + +/// The KV store (_kv_store): the SWA ring or a compressed family (split KV: only the rows this rank owns). +pub fn kvStore(e: *Emitter, lat: Arg, src: u32, ratio: i64) !void { + const rows = try e.pool(src, ratio); + const pg = try e.paging(ratio, true); + try e.triton("_kv_store", .{ e.n, 1, 1 }, &.{ + .{ .name = "LAT", .arg = lat }, .{ .name = "l_stride", .arg = .{ .i = e.cfg.head_dim } }, + .{ .name = "CS", .arg = try e.ropeTable() }, .{ .name = "cs_stride", .arg = .{ .i = 64 } }, + .{ .name = "V", .arg = rows[0] }, .{ .name = "S", .arg = rows[1] }, + .{ .name = "v_stride", .arg = .{ .i = pg.vs } }, .{ .name = "s_stride", .arg = .{ .i = pg.ss } }, + .{ .name = "POS", .arg = try e.pos() }, .{ .name = "RATIO", .arg = .{ .i = ratio } }, + .{ .name = "RING", .arg = .{ .i = if (ratio == 0) 2 * @as(i64, e.cfg.window) else 1 } }, .{ .name = "PT", .arg = pg.pt }, + .{ .name = "PSH", .arg = .{ .i = pg.psh } }, .{ .name = "SL", .arg = .none }, + .{ .name = "PTS", .arg = .{ .i = 0 } }, .{ .name = "ROWS", .arg = .{ .b = false } }, + .{ .name = "PDL", .arg = .{ .b = false } }, + }); +} + +pub fn paging(e: *Emitter, ratio: i64, store: bool) !Paging { + const p = e.o.pool orelse return .{ .pt = .none, .psh = 0, .vs = 576, .ss = 8 }; + if (ratio == 0) return .{ .pt = .none, .psh = 0, .vs = 576, .ss = 8 }; + const tab: []const u8 = if (store and p.split) "s.kv.ct" else "s.kv.pt"; + return .{ .pt = try e.buf("{s}", .{tab}, .i32, &.{p.pts}), .psh = Pool.shift(ratio), .vs = Pool.row_bytes, .ss = Pool.row_bytes }; +} + +/// A layer's KV rows (576 B) and scales (8 B): SWA ring, contiguous compressed rows, or the pool's 584 B records. +pub fn pool(e: *Emitter, L: u32, ratio: i64) ![2]Arg { + if (ratio > 0) if (e.o.pool) |p| { + const rows = p.comp_pages * @divExact(Pool.page, ratio); + const role: calls.Role = .{ .buf = try e.fmt("s.kv.comp.L{d}", .{L}) }; + return .{ try e.view(role, .u8, &.{ rows, 576 }, &.{ Pool.row_bytes, 1 }, 0), try e.view(role, .u8, &.{ rows, 8 }, &.{ Pool.row_bytes, 1 }, 576) }; + }; + const rows: i64 = if (ratio == 0) 2 * @as(i64, e.cfg.window) else @divFloor(e.o.limit, ratio) + 1; + const kind = if (ratio == 0) "swa" else "comp"; + return .{ try e.buf("s.L{d}.{s}.v", .{ L, kind }, .u8, &.{ rows, 576 }), try e.buf("s.L{d}.{s}.s", .{ L, kind }, .u8, &.{ rows, 8 }) }; +} + +pub fn indexKeys(e: *Emitter, L: u32) !Arg { + const r: i64 = e.cfg.compressRatio(L); + if (e.o.pool) |p| return e.buf("s.kv.ik.L{d}", .{L}, .u8, &.{ p.ik_pages * @divExact(Pool.page, r), Pool.ik_bytes }); + return e.buf("s.L{d}.ik", .{L}, .u8, &.{ @divFloor(e.o.limit, r) + 1, @as(i64, e.cfg.index_dim) + 4 }); +} + +/// The rows and tokens of the last exchange (a reuse layer: the same selection and kv source). +pub fn received(e: *Emitter, src: u32, ratio: i64) !Received { + _ = src; + _ = ratio; + const p = e.o.pool.?; + const n = e.n; + const K: i64 = e.cfg.index_topk; + const W: i64 = e.o.world; + const sent: i64 = if (n <= 16) n * K else @divFloor(p.union_cap, (W + 1) * Pool.row_bytes); + const recv: calls.Role = .{ .buf = "w.kx.recv" }; + return .{ + .cv = try e.view(recv, .u8, &.{ W * sent, 576 }, &.{ Pool.row_bytes, 1 }, 0), + .cs = try e.view(recv, .u8, &.{ W * sent, 8 }, &.{ Pool.row_bytes, 1 }, 576), + .tok = try e.buf("w.kx.tok", .{}, .i32, &.{ n, K }), + }; +} + +pub fn exchange(e: *Emitter, src: u32, ratio: i64, sel: Arg) !Received { + const p = e.o.pool.?; + const n = e.n; + const K: i64 = e.cfg.index_topk; + const W: i64 = e.o.world; + // row windows exchange dense at any rows, as the reference does + const small = n <= 16 or e.o.rows; + // dense: every rank sends R x K rows; union: (W + 1) x M rows under the cap + const sent: i64 = if (small) n * K else @divFloor(p.union_cap, (W + 1) * Pool.row_bytes); + const recv = try e.buf("w.kx.recv", .{}, .u8, &.{ W * sent, Pool.row_bytes }); + const tok = try e.buf("w.kx.tok", .{}, .i32, &.{ n, K }); + const comp = try e.buf("s.kv.comp.L{d}", .{src}, .u8, &.{ p.comp_pages * @divExact(Pool.page, ratio), Pool.row_bytes }); + try e.glue(if (small) "kx_dense" else "kx_union", &.{ + sel, comp, + try e.buf("s.kv.ct", .{}, .i32, &.{p.pts}), try e.buf("w.kx.send", .{}, .u8, &.{ sent, Pool.row_bytes }), + recv, tok, + .{ .i = Pool.shift(ratio) }, + }); + return .{ + .cv = try e.view(recv.t.role, .u8, &.{ W * sent, 576 }, &.{ Pool.row_bytes, 1 }, 0), + .cs = try e.view(recv.t.role, .u8, &.{ W * sent, 8 }, &.{ Pool.row_bytes, 1 }, 576), + .tok = tok, + }; +} + +/// Triton `_rms` (rmsnorm.rms: a weight, bf16 in and out): `r1`'s kv_norm beside the SWA store. +pub fn rms(e: *Emitter, x: Arg, w: Arg, out: Arg, k: i64) !void { + try e.triton("_rms", .{ e.n, 1, 1 }, &.{ + .{ .name = "X", .arg = x }, .{ .name = "x_rs", .arg = .{ .i = k } }, + .{ .name = "W", .arg = w }, .{ .name = "OUT", .arg = out }, + .{ .name = "o_rs", .arg = .{ .i = k } }, .{ .name = "K", .arg = .{ .i = k } }, + .{ .name = "inv_k", .arg = .{ .f = 1.0 / @as(f64, @floatFromInt(k)) } }, .{ .name = "eps", .arg = .{ .f = dec(e.cfg.eps) } }, + .{ .name = "BK", .arg = .{ .i = 512 } }, .{ .name = "HAS_W", .arg = .{ .b = true } }, + .{ .name = "NARROW", .arg = .{ .b = true } }, .{ .name = "PDL", .arg = .{ .b = false } }, + }); +} + +/// Only the 512-wide KV producer belongs to this fusion; other RMS norms keep their calls. +pub fn kvNormStoreOn(e: *const Emitter) bool { + return e.o.kv_norm_store and e.cfg.head_dim == 512 and e.n >= 1 and e.n <= 64; +} + +pub fn normStore(e: *Emitter, x: Arg, w: Arg, v: Arg, s: Arg, p: Arg, sl: Arg, ring: i64, rows: bool) !void { + try e.ext("tf_dsv41_kv_glue_v1.norm_store", &.{ + x, w, try e.ropeTable(), v, s, p, sl, + .{ .i = 512 }, .{ .i = 64 }, .{ .i = 576 }, .{ .i = 8 }, .{ .f = 1.0 / 512.0 }, .{ .f = dec(e.cfg.eps) }, .{ .i = ring }, + .{ .b = rows }, + }); +} + +/// The SWA store of the window's kv rows (`r1`: kv_norm runs here, RMS_FOLD's `Normed.tensor()`). +pub fn swaStore(e: *Emitter, L: u32, kv: Arg, kvn: Arg) !void { + if (e.o.r1 and e.kvNormStoreOn()) { + const rows = try e.pool(L, 0); + try e.normStore(kv, try e.weight("L{d}.attn.kv_norm", .{L}, .f32, &.{e.cfg.head_dim}), rows[0], rows[1], try e.pos(), .none, 2 * @as(i64, e.cfg.window), false); + } else { + if (e.o.r1) try e.rms(kv, try e.weight("L{d}.attn.kv_norm", .{L}, .f32, &.{e.cfg.head_dim}), kvn, e.cfg.head_dim); + try e.kvStore(kvn, L, 0); + } +} + +/// branches.plan's marks (`fork`, `onMain`, `join`); nothing unless TF_DSV41_BRANCHES branches this window +pub fn branching(e: *const Emitter) bool { + return e.o.branches and e.n <= e.o.branch_rows; +} + +pub fn fork(e: *Emitter) void { + if (!e.branching()) return; + e.br_fork = true; + e.br_side = true; +} + +pub fn onMain(e: *Emitter) void { + e.br_side = false; +} + +pub fn join(e: *Emitter) void { + if (!e.branching()) return; + e.br_join = true; + e.br_side = false; +} + +pub fn attention(e: *Emitter, L: u32) !void { + const n = e.n; + const D: i64 = e.cfg.hidden; + const md = e.cfg.mode(L); + const ratio: i64 = e.cfg.compressRatio(L); + const kv_src = e.cfg.kvSource(L); + const H: i64 = e.cfg.heads / e.o.world; + const hd: i64 = e.cfg.head_dim; + const IH: i64 = e.cfg.index_heads; + const ID: i64 = e.cfg.index_dim; + const x = try e.buf("w.out", .{}, .bf16, &.{ n, D }); + const indexer = md == .full or md == .reindex; + const own_kv = md == .full; + // the indexer's head weights (a bf16 GEMV over the normed input) + const iw = try e.buf("L.ix_w", .{}, .bf16, &.{ n, IH }); + // index layer: fork, side indexer chain, main q group, join, attn; other layers: side SWA store, main q group + if (indexer) e.fork(); + if (indexer) try e.triton("_plain", .{ n, IH, 1 }, &.{ + .{ .name = "X", .arg = x }, .{ .name = "x_rs", .arg = .{ .i = D } }, + .{ .name = "W", .arg = try e.weight("L{d}.attn.ix_wp", .{L}, .f32, &.{ IH, D }) }, .{ .name = "w_rs", .arg = .{ .i = D } }, + .{ .name = "OUT", .arg = iw }, .{ .name = "K", .arg = .{ .i = D } }, + .{ .name = "N", .arg = .{ .i = IH } }, .{ .name = "BK", .arg = .{ .i = 256 } }, + .{ .name = "PDL", .arg = .{ .b = false } }, .{ .name = "BF16", .arg = .{ .b = true } }, + }); + e.onMain(); + // x group: wq_a, wkv (+ the compressor's on a KV source) + const xg = try e.xGroup(L); + const qa = try e.buf("L.qa", .{}, .bf16, &.{ n, e.cfg.q_lora }); + const kv = try e.buf("L.kv", .{}, .bf16, &.{ n, hd }); + const cw: i64 = if (ratio == 2) 2 * hd else hd; + // a KV source's compressor projection outlives its layer: keep() carries the accepted row (ratio 2) + const comp = if (ratio == 2) try e.buf("w.L{d}.comp", .{L}, .bf16, &.{ n, cw }) else try e.buf("L.comp", .{}, .bf16, &.{ n, cw }); + var ps: [4]Proj = undefined; + for (xg.ws, 0..) |w, i| ps[i] = .{ .w = w, .x = x, .k = @intCast(D), .n = @intCast(xg.ns[i]), .y = switch (i) { + 0 => qa, + 1 => kv, + else => comp, + }, .col = if (i == 3) @intCast(hd) else 0 }; + try e.fused("x", ps[0..xg.ws.len]); + const qn = try e.buf("L.qn", .{}, .bf16, &.{ n, e.cfg.q_lora }); + const kvn = try e.buf("L.kvn", .{}, .bf16, &.{ n, hd }); + // `r1` (RMS_FOLD): no launch here; q_norm runs in its consumers' rot_in, kv_norm beside the SWA store + const qnorm: ?Norm = if (e.o.r1) .{ .w = try e.weight("L{d}.attn.q_norm", .{L}, .f32, &.{e.cfg.q_lora}), .eps = dec(e.cfg.eps) } else null; + if (!e.o.r1) try e.triton("_rms2", .{ n, 2, 1 }, &.{ + .{ .name = "XA", .arg = qa }, .{ .name = "xa_rs", .arg = .{ .i = e.cfg.q_lora } }, + .{ .name = "WA", .arg = try e.weight("L{d}.attn.q_norm", .{L}, .f32, &.{e.cfg.q_lora}) }, .{ .name = "OA", .arg = qn }, + .{ .name = "oa_rs", .arg = .{ .i = e.cfg.q_lora } }, .{ .name = "KA", .arg = .{ .i = e.cfg.q_lora } }, + .{ .name = "inv_ka", .arg = .{ .f = 1.0 / @as(f64, @floatFromInt(e.cfg.q_lora)) } }, .{ .name = "XB", .arg = kv }, + .{ .name = "xb_rs", .arg = .{ .i = hd } }, .{ .name = "WB", .arg = try e.weight("L{d}.attn.kv_norm", .{L}, .f32, &.{hd}) }, + .{ .name = "OB", .arg = kvn }, .{ .name = "ob_rs", .arg = .{ .i = hd } }, + .{ .name = "KB", .arg = .{ .i = hd } }, .{ .name = "inv_kb", .arg = .{ .f = 1.0 / @as(f64, @floatFromInt(hd)) } }, + .{ .name = "eps", .arg = .{ .f = dec(e.cfg.eps) } }, .{ .name = "BK", .arg = .{ .i = 512 } }, + .{ .name = "NARROW", .arg = .{ .b = true } }, .{ .name = "PDL", .arg = .{ .b = false } }, + }); + if (!indexer) { + e.fork(); + try e.swaStore(L, kv, kvn); // `r1` (RMS_FOLD): its kv_norm with it, on the side + e.onMain(); + } + if (indexer) e.fork(); + if (own_kv) { + // the compressor: pooled rows, normed, into the pool; the indexer's keys from the same rows + const lat = try e.buf("L.lat", .{}, .bf16, &.{ n, hd }); + try e.triton("_pool_norm", .{ n, 1, 1 }, &.{ + .{ .name = "BUF", .arg = comp }, .{ .name = "b_stride", .arg = .{ .i = cw } }, + .{ .name = "W", .arg = try e.weight("L{d}.attn.comp_norm", .{L}, .f32, &.{hd}) }, .{ .name = "LAT", .arg = lat }, + .{ .name = "POS", .arg = try e.pos() }, .{ .name = "n", .arg = .{ .i = n } }, + .{ .name = "EPS", .arg = .{ .f = dec(e.cfg.eps) } }, .{ .name = "RATIO", .arg = .{ .i = ratio } }, + .{ .name = "SL", .arg = .none }, .{ .name = "PREV", .arg = .none }, + .{ .name = "OFF", .arg = .{ .i = if (ratio == 2) 1 else 0 } }, .{ .name = "ROWS", .arg = .{ .b = false } }, + .{ .name = "PDL", .arg = .{ .b = false } }, .{ .name = "CARRY", .arg = if (ratio == 2) try e.buf("s.L{d}.carry", .{L}, .f32, &.{ 1, cw }) else .none }, + .{ .name = "c_stride", .arg = .{ .i = if (ratio == 2) cw else 0 } }, .{ .name = "SPLIT", .arg = .{ .b = ratio == 2 } }, + }); + try e.kvStore(lat, L, ratio); + const kp = try e.buf("L.ix_k", .{}, .bf16, &.{ n, ID }); + try e.upstream(try e.fmt("L{d}.attn.ix_wk", .{L}), lat, hd, ID, kp); + const kpg = try e.paging(ratio, false); + try e.triton("_index_k", .{ n, 1, 1 }, &.{ + .{ .name = "KP", .arg = kp }, .{ .name = "k_stride", .arg = .{ .i = ID } }, + .{ .name = "W", .arg = try e.weight("L{d}.attn.ix_knorm", .{L}, .f32, &.{ID}) }, .{ .name = "CS", .arg = try e.ropeTable() }, + .{ .name = "cs_stride", .arg = .{ .i = 64 } }, .{ .name = "IK", .arg = try e.indexKeys(L) }, + .{ .name = "POS", .arg = try e.pos() }, .{ .name = "EPS", .arg = .{ .f = dec(e.cfg.eps) } }, + .{ .name = "RATIO", .arg = .{ .i = ratio } }, .{ .name = "PT", .arg = kpg.pt }, + .{ .name = "PSH", .arg = .{ .i = kpg.psh } }, .{ .name = "KFP8", .arg = .{ .b = true } }, + .{ .name = "SL", .arg = .none }, .{ .name = "PTS", .arg = .{ .i = 0 } }, + .{ .name = "ROWS", .arg = .{ .b = false } }, .{ .name = "PDL", .arg = .{ .b = false } }, + }); + } + if (indexer) try e.index(L, if (e.o.r1) qa else qn, iw, qnorm); + const early: ?Received = if (indexer and e.o.pool != null and e.o.pool.?.split and e.o.pool.?.early and (n <= 16 or e.o.rows)) + try e.exchange(kv_src.?, e.cfg.compressRatio(kv_src.?), (try e.selection(L))[0]) + else + null; + e.onMain(); + // q group: wq_b + const q = try e.buf("L.q", .{}, .bf16, &.{ n, H * hd }); + try e.fusedNorm("q", &.{.{ .w = try e.fmt("L{d}.attn.wq_b", .{L}), .x = if (e.o.r1) qa else qn, .y = q, .k = e.cfg.q_lora, .n = @intCast(H * hd) }}, qnorm); + try e.prefetchO(L); + if (!e.o.r1) try e.rope(q, H, hd, hd - 64); // `r1` (ATTN_ROPE): the attention kernel rotates q as it loads it + if (indexer) try e.swaStore(L, kv, kvn); + e.join(); // the attention core reads both streams' values + // attention over the SWA ring and (a compressed layer) the source's pool at the source's selection + const o = try e.buf("L.o", .{}, .bf16, &.{ n, H, hd }); + const swa = try e.pool(L, 0); + var cv = try e.empty(.u8); + var cs = try e.empty(.u8); + var tok = try e.empty(.i32); + var cnt = try e.empty(.i32); + var apg: Paging = .{ .pt = try e.empty(.i32), .psh = 0, .vs = 0, .ss = 0 }; + if (ratio > 0) { + const src_ratio = e.cfg.compressRatio(kv_src.?); + const sel = try e.selection(e.cfg.indexSource(L).?); + cnt = sel[1]; + if (e.o.pool != null and e.o.pool.?.split) { + // split: an index layer's selection brings its rows over the exchange; its reuse layers read them too + const got = early orelse if (indexer) try e.exchange(kv_src.?, src_ratio, sel[0]) else try e.received(kv_src.?, src_ratio); + cv = got.cv; + cs = got.cs; + tok = got.tok; + } else { + const p = try e.pool(kv_src.?, src_ratio); + cv = p[0]; + cs = p[1]; + tok = sel[0]; + if (e.o.pool != null) { + const pg = try e.paging(src_ratio, false); + apg = .{ .pt = pg.pt, .psh = pg.psh, .vs = 0, .ss = 0 }; + } + } + } + const rows_scr = n * 5 * H; + try e.ext("tf_dsv41_attn_cuda_v1.attn", &.{ + try e.view(q.t.role, .bf16, &.{ n, H, hd }, &.{ H * hd, hd, 1 }, 0), cv, cs, tok, cnt, swa[0], swa[1], + try e.buf("w.lo", .{}, .i32, &.{n}), try e.empty(.i32), try e.pos(), try e.empty(.i64), apg.pt, try e.weight("L{d}.attn.sink", .{L}, .f32, &.{H}), try e.ropeTable(), + try e.buf("s.attn.po", .{}, .f32, &.{rows_scr * 512}), try e.buf("s.attn.pm", .{}, .f32, &.{rows_scr}), try e.buf("s.attn.pl", .{}, .f32, &.{rows_scr}), o, try e.buf("s.attn.ticket", .{}, .i32, &.{e.ticketLen()}), .{ .i = 2 * @as(i64, e.cfg.window) }, .{ .i = apg.psh }, + .{ .i = 0 }, .{ .i = 4 }, + }); + if (e.o.r1) { + // ATTN_SPLIT 4 (a cluster of 4 a chunk up to ATTN_SPLIT_ROWS = 2 rows, 1 above) and ATTN_ROPE + const last = &e.out.items[e.out.items.len - 1]; + const more = try e.a.alloc(calls.Named, last.args.len + 2); + @memcpy(more[0..last.args.len], last.args); + more[last.args.len] = .{ .arg = .{ .i = attnSplit(n) } }; + more[last.args.len + 1] = .{ .arg = .{ .i = 1 } }; + last.args = more; + } else if (e.o.r1_sig) try e.appendLast(&.{ .{ .i = 0 }, .{ .i = 0 } }); + // o: wo_a's groups over the heads' columns, then wo_b (this rank's partial) + const g: usize = e.cfg.o_groups / e.o.world; + const gk: i64 = @divExact(H * hd, @as(i64, @intCast(g))); + const oa = try e.buf("L.oa", .{}, .bf16, &.{ n, @as(i64, @intCast(g)) * e.cfg.o_lora }); + var po: [8]Proj = undefined; + for (po[0..g], 0..) |*p, i| p.* = .{ + .w = try e.fmt("L{d}.attn.wo_a.{d}", .{ L, i }), + .x = try e.view(o.t.role, .bf16, &.{ n, gk }, &.{ H * hd, 1 }, @as(i64, @intCast(i)) * gk * 2), + .y = oa, + .col = i * e.cfg.o_lora, + .k = @intCast(gk), + .n = e.cfg.o_lora, + }; + try e.fused("o", po[0..g]); + try e.fused("wo_b", &.{.{ .w = try e.fmt("L{d}.attn.wo_b", .{L}), .x = oa, .y = try e.buf("L.part", .{}, .bf16, &.{ n, D }), .k = g * e.cfg.o_lora, .n = @intCast(D) }}); +} diff --git a/zig/src/families/deepseek_v41/block_calls.zig b/zig/src/families/deepseek_v41/block_calls.zig new file mode 100644 index 0000000000..99765cf940 --- /dev/null +++ b/zig/src/families/deepseek_v41/block_calls.zig @@ -0,0 +1,257 @@ +//! A block's launches as the forward issues them, tensors named by role, and the check of each against a captured op. + +const std = @import("std"); +const Value = std.json.Value; + +pub const Dt = enum { bf16, f16, f32, f64, i8, u8, i16, i32, i64, bool }; + +/// Where a tensor's bytes live: one of our loader's weights, a named buffer of the forward, or nothing (numel 0). +pub const Role = union(enum) { + weight: []const u8, + /// scope by prefix: "s." across windows (slot state, tables), "L." its layer, anything else the window + buf: []const u8, + empty, +}; + +pub const Tensor = struct { + role: Role, + dt: Dt, + shape: []const i64, + stride: []const i64, + /// bytes from the role's first byte + offset: i64 = 0, +}; + +pub const Arg = union(enum) { + t: Tensor, + i: i64, + f: f64, + b: bool, + none, + list: []const Arg, + /// the capture's table contents are not compared (an L2 prefetch list of addresses) + opaque_table: Tensor, +}; + +pub const Named = struct { name: []const u8 = "", arg: Arg }; + +/// Where a call starts a scope: the window's or a layer's roles begin fresh there. +pub const Begin = enum { none, window, layer }; + +pub const Call = struct { + triton: bool, + glue: bool = false, // the forward's own step (positions, exchanges, Engram rows): no captured launch + begin: Begin = .none, + /// the compiled kernel's name (Triton) or "." + name: []const u8, + grid: [3]i64 = .{ 1, 1, 1 }, + args: []const Named, + side: bool = false, // TF_DSV41_BRANCHES: issued on the side stream (same launch; checks compare launches) + fork: bool = false, // the side stream first waits for every main-stream launch so far + join: bool = false, // the main stream first waits for the side stream + mark: u8 = 0, // TF_DSV41_PF_OVERLAP_SITE: on a side call, record side mark m after it + wait: u8 = 0, // on a main call, wait for mark m only (a `join` still waits for all) + defer_side: bool = false, // TF_DSV41_MHC_DEFER: an mHC coefficient launch on the deferred stream + defer_join: bool = false, // the main stream waits for it first (the coefficients' reader) +}; + +/// TF_DSV41_MHC_DEFER_AT=exchange: move each deferred launch to just before the next exchange or its join, in-region. +pub fn holdDeferred(cs: []Call) void { + var i: usize = 0; + while (i < cs.len) : (i += 1) { + const c = cs[i]; + if (!c.defer_side or c.defer_join or c.side or c.fork or c.join or c.begin != .none) continue; + var j = i + 1; + while (j < cs.len and !cs[j].defer_join and !cs[j].defer_side and !isExchange(cs[j])) : (j += 1) {} + if (j == cs.len or cs[j].defer_side) continue; // no exchange or join after it: left where it was + std.mem.copyForwards(Call, cs[i .. j - 1], cs[i + 1 .. j]); + cs[j - 1] = c; + i = j - 1; + } +} + +/// A collective exchange step of the forward (glue.exchange / glue.exchange_f32). +pub fn isExchange(c: Call) bool { + return c.glue and std.mem.startsWith(u8, c.name, "glue.exchange"); +} + +/// The calls a capture records (the glue steps left out), in order; a dropped step's scope start moves to the next. +pub fn launches(a: std.mem.Allocator, cs: []const Call) ![]const Call { + var out: std.ArrayList(Call) = .empty; + var pending: Begin = .none; + for (cs) |c| { + if (c.glue) { + if (@backingInt(c.begin) > @backingInt(pending)) pending = c.begin; + continue; + } + var x = c; + if (pending == .window or (pending == .layer and x.begin == .none)) x.begin = pending; + pending = .none; + try out.append(a, x); + } + return out.items; +} + +/// Contiguous strides of `shape` (elements). +pub fn contiguous(a: std.mem.Allocator, shape: []const i64) ![]const i64 { + const s = try a.alloc(i64, shape.len); + var acc: i64 = 1; + var i = shape.len; + while (i > 0) { + i -= 1; + s[i] = acc; + acc *= @max(shape[i], 1); + } + return s; +} + +fn dtName(dt: Dt) []const u8 { + return switch (dt) { + .bf16 => "bfloat16", + .f16 => "float16", + .f32 => "float32", + .f64 => "float64", + .i8 => "int8", + .u8 => "uint8", + .i16 => "int16", + .i32 => "int32", + .i64 => "int64", + .bool => "bool", + }; +} + +/// Role -> captured storage key within its scope, learned as the check goes; another key for a role is a mismatch. +pub const Roles = struct { + a: std.mem.Allocator, + keys: std.StringHashMapUnmanaged([]const u8) = .empty, + /// storage size a role maps to (the GPU run sizes its buffer from it) + sizes: std.StringHashMapUnmanaged(u64) = .empty, + + /// Forgets the roles whose scope ends at `b`: "L." at a layer, "L." and "w." at a window. + pub fn begin(r: *Roles, b: Begin) void { + switch (b) { + .none => {}, + .layer => r.endScope("w."), + .window => r.endScope(""), + } + } + + /// Forgets every role but the persistent ("s.") ones and those starting with `prefix_keep`. + pub fn endScope(r: *Roles, prefix_keep: []const u8) void { + var it = r.keys.iterator(); + var drop: std.ArrayList([]const u8) = .empty; + defer drop.deinit(r.a); + while (it.next()) |e| { + const k = e.key_ptr.*; + if (std.mem.startsWith(u8, k, "s.")) continue; + if (prefix_keep.len > 0 and std.mem.startsWith(u8, k, prefix_keep)) continue; + drop.append(r.a, k) catch {}; + } + for (drop.items) |k| _ = r.keys.remove(k); + } +}; + +pub const Mismatch = struct { what: []const u8 }; + +/// The first difference between `c` and the captured op `op` (null: they agree); learns the roles' storage keys. +pub fn check(a: std.mem.Allocator, c: *const Call, op: std.json.ObjectMap, roles: *Roles) !?[]const u8 { + const name = op.get("name").?.string; + if (!std.mem.eql(u8, name, c.name)) return try std.fmt.allocPrint(a, "name {s} vs captured {s}", .{ c.name, name }); + if (c.triton) { + const g = op.get("grid").?.array.items; + for (c.grid, 0..) |x, i| if (g[i].integer != x) return try std.fmt.allocPrint(a, "grid {any} vs captured [{d},{d},{d}]", .{ c.grid, g[0].integer, g[1].integer, g[2].integer }); + } + const got = op.get("args").?.array.items; + if (got.len != c.args.len) return try std.fmt.allocPrint(a, "{d} args vs captured {d}", .{ c.args.len, got.len }); + var keys: std.StringHashMapUnmanaged(i64) = .empty; // this op's role -> buffer id (aliasing within the op) + const bufs = op.get("buffers").?.array.items; + for (c.args, got, 0..) |want, cap, i| { + if (c.triton) { + const cn = cap.object.get("name").?.string; + if (!std.mem.eql(u8, cn, want.name)) return try std.fmt.allocPrint(a, "arg {d} named {s} vs captured {s}", .{ i, want.name, cn }); + } + if (try argCheck(a, want.arg, cap, roles, &keys, bufs)) |why| return try std.fmt.allocPrint(a, "arg {d} ({s}): {s}", .{ i, if (want.name.len > 0) want.name else "-", why }); + } + return null; +} + +fn argCheck(a: std.mem.Allocator, want: Arg, cap: Value, roles: *Roles, keys: *std.StringHashMapUnmanaged(i64), bufs: []const Value) !?[]const u8 { + const o = cap.object; + const t = o.get("t").?.string; + switch (want) { + .none => return if (std.mem.eql(u8, t, "none")) null else try std.fmt.allocPrint(a, "none vs {s}", .{t}), + .b => |v| { + if (!std.mem.eql(u8, t, "bool")) return try std.fmt.allocPrint(a, "bool vs {s}", .{t}); + return if (o.get("v").?.bool == v) null else try std.fmt.allocPrint(a, "{} vs {}", .{ v, o.get("v").?.bool }); + }, + .i => |v| { + if (!std.mem.eql(u8, t, "int")) return try std.fmt.allocPrint(a, "int {d} vs {s}", .{ v, t }); + return if (o.get("v").?.integer == v) null else try std.fmt.allocPrint(a, "{d} vs {d}", .{ v, o.get("v").?.integer }); + }, + .f => |v| { + if (!std.mem.eql(u8, t, "float")) return try std.fmt.allocPrint(a, "float vs {s}", .{t}); + const bits = try std.fmt.parseInt(u64, o.get("f64").?.string[2..], 16); + return if (bits == @as(u64, @bitCast(v))) null else try std.fmt.allocPrint(a, "{e} vs {e}", .{ v, @as(f64, @bitCast(bits)) }); + }, + .list => |items| { + if (!std.mem.eql(u8, t, "list")) return try std.fmt.allocPrint(a, "list vs {s}", .{t}); + const ci = o.get("items").?.array.items; + if (ci.len != items.len) return try std.fmt.allocPrint(a, "list of {d} vs {d}", .{ items.len, ci.len }); + for (items, ci, 0..) |x, y, j| if (try argCheck(a, x, y, roles, keys, bufs)) |why| return try std.fmt.allocPrint(a, "[{d}] {s}", .{ j, why }); + return null; + }, + .t, .opaque_table => |w| { + if (!std.mem.eql(u8, t, "tensor")) return try std.fmt.allocPrint(a, "tensor vs {s}", .{t}); + if (!std.mem.eql(u8, o.get("dtype").?.string, dtName(w.dt))) return try std.fmt.allocPrint(a, "dtype {s} vs {s}", .{ dtName(w.dt), o.get("dtype").?.string }); + const shape = o.get("shape").?.array.items; + if (!intsEq(w.shape, shape)) return try std.fmt.allocPrint(a, "shape {any} vs captured", .{w.shape}); + var numel: i64 = 1; + for (w.shape) |d| numel *= d; + if (numel == 0) return null; // an empty tensor: only its dtype and shape count + if (w.shape.len > 0 and !intsEq(w.stride, o.get("stride").?.array.items)) return try std.fmt.allocPrint(a, "stride {any} vs captured", .{w.stride}); + switch (w.role) { + .empty => return try std.fmt.allocPrint(a, "empty role for a tensor of {d} elements", .{numel}), + .weight => |name| { + const cw = o.get("weight") orelse return try std.fmt.allocPrint(a, "weight {s} vs a captured buffer", .{name}); + if (!std.mem.eql(u8, cw.string, name)) return try std.fmt.allocPrint(a, "weight {s} vs {s}", .{ name, cw.string }); + return if (o.get("offset").?.integer == w.offset) null else try std.fmt.allocPrint(a, "offset {d} vs {d}", .{ w.offset, o.get("offset").?.integer }); + }, + .buf => |role| { + if (o.get("weight")) |cw| return try std.fmt.allocPrint(a, "buffer {s} vs captured weight {s}", .{ role, cw.string }); + if (o.get("offset").?.integer != w.offset) return try std.fmt.allocPrint(a, "{s} offset {d} vs {d}", .{ role, w.offset, o.get("offset").?.integer }); + const id = o.get("buf").?.integer; + // within the op: the same role is the same buffer and two roles are two buffers + if (keys.get(role)) |prev| { + if (prev != id) return try std.fmt.allocPrint(a, "{s} is two captured buffers", .{role}); + } else { + var it = keys.iterator(); + while (it.next()) |e| if (e.value_ptr.* == id) return try std.fmt.allocPrint(a, "{s} and {s} are one captured buffer", .{ role, e.key_ptr.* }); + try keys.put(a, role, id); + } + const key = for (bufs) |b| { + if (b.object.get("id").?.integer == id) break b.object.get("key").?.string; + } else return "no buffer entry"; + const gop = try roles.keys.getOrPut(roles.a, role); + if (gop.found_existing) { + if (!std.mem.eql(u8, gop.value_ptr.*, key)) return try std.fmt.allocPrint(a, "{s} moved to another captured storage", .{role}); + } else { + gop.key_ptr.* = try roles.a.dupe(u8, role); + gop.value_ptr.* = try roles.a.dupe(u8, key); + } + const size: u64 = @intCast(for (bufs) |b| { + if (b.object.get("id").?.integer == id) break b.object.get("nbytes").?.integer; + } else 0); + const sg = try roles.sizes.getOrPut(roles.a, gop.key_ptr.*); + if (!sg.found_existing or sg.value_ptr.* < size) sg.value_ptr.* = size; + return null; + }, + } + }, + } +} + +fn intsEq(w: []const i64, c: []const Value) bool { + if (w.len != c.len) return false; + for (w, c) |x, y| if (y.integer != x) return false; + return true; +} diff --git a/zig/src/families/deepseek_v41/block_index.zig b/zig/src/families/deepseek_v41/block_index.zig new file mode 100644 index 0000000000..3e5019c282 --- /dev/null +++ b/zig/src/families/deepseek_v41/block_index.zig @@ -0,0 +1,224 @@ +//! A decode window's indexer: index scores (whole, row-blocked), the selections, attn_cuda's top-k. + +const std = @import("std"); +const calls = @import("block_calls.zig"); +const ft = @import("forward_types.zig"); +const block = @import("block.zig"); +const Arg = calls.Arg; +const Emitter = block.Emitter; +const Norm = block.Emitter.Norm; + +/// attn_cuda's MAX_EPT: past 8 CTAs x 512 threads x 31 entries a selection runs the Triton dtopk instead. +const topk_max_ept: i64 = 31; + +/// topk.plan: (cluster CTAs, entries a thread) for `nk` entries. +fn topkPlan(nk: i64, cl_in: ?i64) [2]i64 { + const cl = cl_in orelse @max(1, @min(8, std.math.divCeil(i64, nk, 4096) catch 1)); + var ept = @max(1, std.math.divCeil(i64, nk, cl * 512) catch 1); + ept += 1 - @mod(ept, 2); + return .{ cl, ept }; +} + +/// The window's index selection of layer `src` (reuse layers read the source's). +pub fn selection(e: *Emitter, src: u32) ![2]Arg { + const k: i64 = e.cfg.index_topk; + return .{ + try e.view(.{ .buf = try e.fmt("w.sel{d}", .{src}) }, .i32, &.{ e.n, k }, &.{ k, 1 }, e.row0 * k * 4), + // the counts' 4-byte rows: a run's segment from a row a multiple of 4 (cnt0), so the slice is 16-aligned + try e.view(.{ .buf = try e.fmt("w.cnt{d}", .{src}) }, .i32, &.{e.n}, &.{1}, e.cnt0 * 4), + }; +} + +/// attn_cuda's ticket words: a (row, head tile)'s ranks, 4 with the split kernel (`r1` or `r1_sig`) +pub fn ticketLen(e: *const Emitter) i64 { + return if (e.o.r1 or e.o.r1_sig) 1024 else 256; +} + +/// attn_cuda.split: CTAs a chunk with `r1` (ATTN_SPLIT 4 up to ATTN_SPLIT_ROWS = 2 rows, 1 above) +pub fn attnSplit(n: i64) i64 { + return if (n <= 2) 4 else 1; +} + +/// The indexer: query projection and rope, scores over the keys, top-k (and a candidate source's blocks). +pub fn index(e: *Emitter, L: u32, qn: Arg, iw: Arg, qnorm: ?Norm) !void { + const n = e.n; + const IH: i64 = e.cfg.index_heads; + const ID: i64 = e.cfg.index_dim; + const qi = try e.buf("L.qi", .{}, .bf16, &.{ n, IH * ID }); + try e.fusedNorm("qi", &.{.{ .w = try e.fmt("L{d}.attn.ix_wq_b", .{L}), .x = qn, .y = qi, .k = e.cfg.q_lora, .n = @intCast(IH * ID) }}, qnorm); + try e.rope(qi, IH, ID, ID - 64); + const reindex = e.cfg.mode(L) == .reindex; + const src = if (reindex) e.cfg.kvSource(L).? else L; + const ratio: i64 = e.cfg.compressRatio(src); + const cbs: i64 = e.cfg.candidate_block_size; + const nk: i64 = if (reindex) @as(i64, e.cfg.candidate_blocks) * cbs else @divFloor(e.end(), ratio); + const cand = try e.buf("w.cand", .{}, .i32, &.{ n, e.cfg.candidate_blocks }); + // the materialised scores + sort keys within the index budget, else row blocks through `_keys` + if (reindex) { + if (n * nk * 16 > e.o.index_budget) return error.Unsupported; // index.reindex in row blocks: > 256 rows at 64 MiB + } else { + const nvis = @max(nk, 1); + if (!e.o.rows and n > 16 and nvis >= ft.stream_min and !e.cfg.isCandidateSource(L) and @popCount(e.cfg.index_topk) == 1) return error.Unsupported; + if (n * nvis * 12 > e.o.index_budget) return e.blocked(L, qi, iw, src, ratio, nvis); + } + const sc = try e.buf("L.scores", .{}, .f32, &.{ n, nk }); + try e.scoreRows(qi, iw, src, ratio, 0, n, try e.pos(), nk, sc, if (reindex) cand else null); + try e.topkOf(L, sc, cand, nk, ratio, e.o.index_bound); +} + +/// index.scores over rows [a, a + rows) into `out` [rows, nk]: dense, or over candidate blocks `cand` (Reindex). +pub fn scoreRows(e: *Emitter, qi: Arg, iw: Arg, src: u32, ratio: i64, a: i64, rows: i64, p: Arg, nk: i64, out: Arg, cand: ?Arg) !void { + const IH: i64 = e.cfg.index_heads; + const ID: i64 = e.cfg.index_dim; + const cbs: i64 = e.cfg.candidate_block_size; + const spg = try e.paging(ratio, false); + try e.triton(if (e.o.index_bound and cand == null) "_scores_b" else "_scores", .{ rows, std.math.divCeil(i64, nk, 64) catch unreachable, 1 }, &.{ + .{ .name = "QI", .arg = try e.view(qi.t.role, .bf16, &.{ rows, IH, ID }, &.{ IH * ID, ID, 1 }, a * IH * ID * 2) }, + .{ .name = "W", .arg = try e.view(iw.t.role, .bf16, &.{ rows, IH }, &.{ IH, 1 }, a * IH * 2) }, + .{ .name = "w_stride", .arg = .{ .i = IH } }, + .{ .name = "IK", .arg = try e.indexKeys(src) }, + .{ .name = "OUT", .arg = out }, + .{ .name = "POS", .arg = p }, + .{ .name = "KEYS", .arg = cand orelse out }, + .{ .name = "k_stride", .arg = .{ .i = if (cand != null) e.cfg.candidate_blocks else nk } }, + .{ .name = "NK", .arg = .{ .i = nk } }, + .{ .name = "o_stride", .arg = .{ .i = nk } }, + .{ .name = "RATIO", .arg = .{ .i = ratio } }, + .{ .name = "H", .arg = .{ .i = IH } }, + .{ .name = "D", .arg = .{ .i = ID } }, + .{ .name = "BP", .arg = .{ .i = 64 } }, + .{ .name = "WS", .arg = .{ .f = 1.0 / @sqrt(@as(f64, @floatFromInt(IH))) } }, + .{ .name = "SCALE", .arg = .{ .f = 1.0 / @sqrt(@as(f64, @floatFromInt(ID))) } }, + .{ .name = "GATHER", .arg = .{ .b = cand != null } }, + .{ .name = "PT", .arg = spg.pt }, + .{ .name = "PSH", .arg = .{ .i = spg.psh } }, + .{ .name = "KFP8", .arg = .{ .b = true } }, + .{ .name = "SL", .arg = .none }, + .{ .name = "PTS", .arg = .{ .i = 0 } }, + .{ .name = "ROWS", .arg = .{ .b = false } }, + .{ .name = "CBS", .arg = .{ .i = if (cand != null) cbs else 0 } }, + }); +} + +/// Row-blocked selection past the index budget: per block `_scores`, `_keys`, top positions; then counts. +pub fn blocked(e: *Emitter, L: u32, qi: Arg, iw: Arg, src: u32, ratio: i64, nvis: i64) !void { + const n = e.n; + const k: i64 = e.cfg.index_topk; + const cb: i64 = e.cfg.candidate_blocks; + const cbs: i64 = e.cfg.candidate_block_size; + const sel = try e.selection(L); + const cand_src = e.cfg.isCandidateSource(L); + const cand = try e.buf("w.cand", .{}, .i32, &.{ n, cb }); + const st: i64 = if (n > 16) @max(16, @divFloor(e.o.index_budget, nvis * 12)) else n; + var a: i64 = 0; + var j: usize = 0; + while (a < n) : ({ + a += st; + j += 1; + }) { + const rows = @min(st, n - a); + const p = if (st >= n) try e.pos() else try e.blockPos(j, a, rows); + const nk = if (st >= n or e.o.rows) nvis else @max(@divFloor(e.start + a + rows, ratio), 1); + const sc = try e.buf("L.scores", .{}, .f32, &.{ rows, nk }); + try e.scoreRows(qi, iw, src, ratio, a, rows, p, nk, sc, null); + const keys = try e.buf("L.ix.keys", .{}, .i64, &.{ rows, nk }); + try e.triton("_keys", .{ rows, std.math.divCeil(i64, nk, 1024) catch unreachable, 1 }, &.{ + .{ .name = "S", .arg = sc }, .{ .name = "s_stride", .arg = .{ .i = nk } }, + .{ .name = "K", .arg = keys }, .{ .name = "k_stride", .arg = .{ .i = nk } }, + .{ .name = "POSN", .arg = sc }, .{ .name = "p_stride", .arg = .{ .i = nk } }, + .{ .name = "NK", .arg = .{ .i = nk } }, .{ .name = "BLOCK", .arg = .{ .i = 1024 } }, + .{ .name = "GATHER", .arg = .{ .b = false } }, + }); + try e.glue("topk", &.{ keys, try e.view(sel[0].t.role, .i32, &.{ rows, k }, &.{ k, 1 }, a * k * 4), .{ .i = k } }); + if (!cand_src) continue; + const nb = std.math.divCeil(i64, nk, cbs) catch unreachable; + const bk = try e.buf("L.ix.bkeys", .{}, .i64, &.{ rows, nb }); + try e.triton("_block_keys", .{ rows, std.math.divCeil(i64, nb, 256) catch unreachable, 1 }, &.{ + .{ .name = "S", .arg = sc }, .{ .name = "s_stride", .arg = .{ .i = nk } }, + .{ .name = "K", .arg = bk }, .{ .name = "k_stride", .arg = .{ .i = nb } }, + .{ .name = "POS", .arg = p }, .{ .name = "NB", .arg = .{ .i = nb } }, + .{ .name = "RATIO", .arg = .{ .i = ratio } }, .{ .name = "BS", .arg = .{ .i = cbs } }, + .{ .name = "TB", .arg = .{ .i = 256 } }, .{ .name = "ROWS", .arg = .{ .b = false } }, + }); + try e.glue("topk", &.{ bk, try e.view(cand.t.role, .i32, &.{ rows, cb }, &.{ cb, 1 }, a * cb * 4), .{ .i = cb } }); + } + try e.glue("counts", &.{ sel[0], sel[1], try e.buf("w.pos64", .{}, .i64, &.{n}), .{ .i = ratio } }); +} + +/// Win.sub's first position (int32 [1]) of row block [a, a + rows); row mode reads the block's table rows instead. +pub fn blockPos(e: *Emitter, j: usize, a: i64, rows: i64) !Arg { + const p = try e.buf("L.ix.pos{d}", .{j}, .i32, &.{1}); + try e.glue("block_pos", &.{ p, .{ .i = e.start + a }, .{ .i = a }, .{ .i = rows } }); + return p; +} + +/// attn_cuda topk over `scores` [n, nk] of index layer L (up to two jobs a launch); candidate blocks into `cand`. +pub fn topk(e: *Emitter, L: u32, scores: Arg, cand: Arg, nk: i64, ratio: i64) !void { + return e.topkOf(L, scores, cand, nk, ratio, false); +} + +/// `topk`; `bound` (TF_DSV41_INDEX_BOUND): dense select and candidate blocks as the row-bounded twins, same bytes. +pub fn topkOf(e: *Emitter, L: u32, scores: Arg, cand: Arg, nk: i64, ratio: i64, bound: bool) !void { + const n = e.n; + const dtopk = if (bound) "_dtopk_b" else "_dtopk"; + const topk_ext = if (bound and e.cfg.mode(L) != .reindex) "tf_dsv41_topk_b_v1.topk" else "tf_dsv41_attn_cuda_v1.topk"; + const reindex = e.cfg.mode(L) == .reindex; + const cbs: i64 = e.cfg.candidate_block_size; + const sel = try e.selection(L); + const k: i64 = e.cfg.index_topk; + const pl = topkPlan(nk, null); + if (pl[1] > topk_max_ept) { + // past attn_cuda's plan limit (8 x 512 x 31 entries): Triton _dtopk with counts, then candidate blocks + if (reindex) return error.Unsupported; + try e.triton(dtopk, .{ n, 1, 1 }, &.{ + .{ .name = "S", .arg = scores }, .{ .name = "s_stride", .arg = .{ .i = scores.t.stride[0] } }, + .{ .name = "P", .arg = scores }, .{ .name = "p_stride", .arg = .{ .i = 0 } }, + .{ .name = "POS", .arg = try e.pos() }, .{ .name = "NK", .arg = .{ .i = nk } }, + .{ .name = "OUT", .arg = sel[0] }, .{ .name = "o_stride", .arg = .{ .i = k } }, + .{ .name = "CNT", .arg = sel[1] }, .{ .name = "RATIO", .arg = .{ .i = ratio } }, + .{ .name = "K", .arg = .{ .i = k } }, .{ .name = "MODE", .arg = .{ .i = 0 } }, + .{ .name = "ROWS", .arg = .{ .b = false } }, .{ .name = "T", .arg = .{ .i = 4096 } }, + .{ .name = "BS", .arg = .{ .i = 8 } }, .{ .name = "RB", .arg = .{ .i = 8 } }, + .{ .name = "NPASS", .arg = .{ .i = 8 } }, .{ .name = "SORT", .arg = .{ .b = false } }, + .{ .name = "COUNT", .arg = .{ .b = true } }, .{ .name = "KP", .arg = .{ .i = @intCast(std.math.ceilPowerOfTwo(u64, @intCast(k)) catch unreachable) } }, + }); + if (!e.cfg.isCandidateSource(L)) return; + const nb = std.math.divCeil(i64, nk, cbs) catch unreachable; + const bp = topkPlan(nb, null); + if (bp[1] > topk_max_ept) { + // past 8 x 512 x 31 blocks too (positions past ~1,015,808): dtopk.blocks (_dtopk mode 2, no counts) + const cb: i64 = e.cfg.candidate_blocks; + return e.triton(dtopk, .{ n, 1, 1 }, &.{ + .{ .name = "S", .arg = scores }, .{ .name = "s_stride", .arg = .{ .i = scores.t.stride[0] } }, + .{ .name = "P", .arg = scores }, .{ .name = "p_stride", .arg = .{ .i = 0 } }, + .{ .name = "POS", .arg = try e.pos() }, .{ .name = "NK", .arg = .{ .i = nb } }, + .{ .name = "OUT", .arg = cand }, .{ .name = "o_stride", .arg = .{ .i = cand.t.stride[0] } }, + .{ .name = "CNT", .arg = cand }, .{ .name = "RATIO", .arg = .{ .i = ratio } }, + .{ .name = "K", .arg = .{ .i = cb } }, .{ .name = "MODE", .arg = .{ .i = 2 } }, + .{ .name = "ROWS", .arg = .{ .b = false } }, .{ .name = "T", .arg = .{ .i = 4096 } }, + .{ .name = "BS", .arg = .{ .i = cbs } }, .{ .name = "RB", .arg = .{ .i = 8 } }, + .{ .name = "NPASS", .arg = .{ .i = 8 } }, .{ .name = "SORT", .arg = .{ .b = false } }, + .{ .name = "COUNT", .arg = .{ .b = false } }, .{ .name = "KP", .arg = .{ .i = @intCast(std.math.ceilPowerOfTwo(u64, @intCast(cb)) catch unreachable) } }, + }); + } + const job: [8]Arg = .{ scores, try e.empty(.i32), cand, try e.view(cand.t.role, .i32, &.{0}, &.{e.cfg.candidate_blocks}, 0), .{ .i = nb }, .{ .i = e.cfg.candidate_blocks }, .{ .i = 2 }, .{ .i = bp[1] } }; + var one: std.ArrayList(Arg) = .empty; + try one.appendSlice(e.a, &job); + try one.appendSlice(e.a, &job); + try one.appendSlice(e.a, &.{ .{ .i = 1 }, try e.pos(), .{ .i = ratio }, .{ .i = cbs }, .{ .i = n }, .{ .i = bp[0] } }); + return e.ext(topk_ext, one.items); + } + var jobs: [2][8]Arg = undefined; + jobs[0] = .{ scores, if (reindex) cand else try e.empty(.i32), sel[0], sel[1], .{ .i = nk }, .{ .i = k }, .{ .i = if (reindex) 3 else 0 }, .{ .i = pl[1] } }; + const blocks = e.cfg.isCandidateSource(L); + jobs[1] = jobs[0]; + if (blocks) { + const nb = std.math.divCeil(i64, nk, cbs) catch unreachable; + jobs[1] = .{ scores, try e.empty(.i32), cand, try e.view(cand.t.role, .i32, &.{0}, &.{e.cfg.candidate_blocks}, 0), .{ .i = nb }, .{ .i = e.cfg.candidate_blocks }, .{ .i = 2 }, .{ .i = topkPlan(nb, pl[0])[1] } }; + } + var args: std.ArrayList(Arg) = .empty; + try args.appendSlice(e.a, &jobs[0]); + try args.appendSlice(e.a, &jobs[1]); + try args.appendSlice(e.a, &.{ .{ .i = if (blocks) 2 else 1 }, try e.pos(), .{ .i = ratio }, .{ .i = if (blocks or reindex) cbs else 1 }, .{ .i = n }, .{ .i = pl[0] } }); + try e.ext(topk_ext, args.items); +} diff --git a/zig/src/families/deepseek_v41/block_moe.zig b/zig/src/families/deepseek_v41/block_moe.zig new file mode 100644 index 0000000000..34168fa012 --- /dev/null +++ b/zig/src/families/deepseek_v41/block_moe.zig @@ -0,0 +1,319 @@ +//! The decode window's mHC sites, MoE (router, prune, group, x3ld, epilogues), Engram and head, as calls. + +const std = @import("std"); +const calls = @import("block_calls.zig"); +const block = @import("block.zig"); +const E = block.Emitter; +const Arg = calls.Arg; +const wide = @import("block_wide.zig"); + +/// mhc_cuda.run modes: first site (no post), boundary (post, collapse 2, mix), final norm (post, collapse 2, no mix). +pub const Mode = enum(i64) { boundary = 0, site1 = 1, site2 = 2, final = 3 }; + +fn coefs(e: *E, set: u1) ![3]Arg { + return .{ + try e.buf("w.c{d}.pre", .{set}, .f32, &.{ e.n, 4 }), + try e.buf("w.c{d}.post", .{set}, .f32, &.{ e.n, 4 }), + try e.buf("w.c{d}.comb", .{set}, .f32, &.{ e.n, 16 }), + }; +} + +/// One mHC site of layer L (`which`: hc_attn / hc_ffn), or the final norm (L null). +pub fn mhc(e: *E, layer: ?u32, which: []const u8, mode: Mode) !void { + return mhcInto(e, layer, which, mode, false); +} + +/// `mhc`, writing the streams in place when `in_place` (a run without the second streams buffer: CED's replay). +pub fn mhcInto(e: *E, layer: ?u32, which: []const u8, mode: Mode, in_place: bool) !void { + // past 16 rows or with TF_DSV41_MHC_PFDEC at the knob's rows: the wide path (block_wide.zig) + if (e.n > wide.cuda_rows or wide.pfOn(e, mode)) return wide.decode(e, layer, which, mode, in_place); + const n = e.n; + const D: i64 = e.cfg.hidden; + const post = mode == .boundary or mode == .final; + const mix = mode != .final; + const f0 = try e.empty(.f32); + const b0 = try e.empty(.bf16); + const x = try e.streams(); + const prev = try coefs(e, e.coef); + const next = try coefs(e, 1 - e.coef); + var nw: Arg = undefined; + var hc = [3]Arg{ b0, f0, f0 }; + if (layer) |L| { + hc = .{ + try e.buf("s.L{d}.{s}.fn16", .{ L, which }, .bf16, &.{ 6 * @as(i64, e.cfg.hc_mult), 4 * D }), + try e.weight("L{d}.{s}.base", .{ L, which }, .f32, &.{6 * @as(i64, e.cfg.hc_mult)}), + try e.weight("L{d}.{s}.scale", .{ L, which }, .f32, &.{3}), + }; + nw = try e.weight("L{d}.{s}", .{ L, if (std.mem.eql(u8, which, "hc_attn")) "attn_norm" else "ffn_norm" }, .f32, &.{D}); + } else nw = try e.weight("norm", .{}, .f32, &.{D}); + // DSpark's tap: the mean of the new streams at a boundary entering a target layer (not the window's first site) + var tap: Arg = b0; + if (e.o.taps and mode == .boundary and std.mem.eql(u8, which, "hc_attn")) if (layer) |L| { + const t = e.cfg.dspark_targets.items(); + if (std.mem.indexOfScalar(u32, t, L)) |j| { + const w: i64 = @as(i64, @intCast(t.len)) * D; + tap = try e.view(.{ .buf = "w.taps" }, .bf16, &.{ n, D }, &.{ w, 1 }, @as(i64, @intCast(j)) * D * 2); + } + }; + const xout: Arg = switch (mode) { + .site1, .site2 => b0, + .final => x, + .boundary => if (in_place) x else try e.spare(), + }; + awaitCoefs(e); + try e.ext("tf_dsv41_mhc_cuda_v1.run", &.{ + x, xout, + // the exchange that ran last: the attention's partials at the FFN site, the MoE's at the next attention / final + if (post) try e.buf("w.recv.{s}", .{if (std.mem.eql(u8, which, "hc_ffn")) "attn" else "moe"}, .bf16, &.{ e.o.world, n, D }) else b0, if (post) prev[1] else f0, + if (post) prev[2] else f0, if (post or mode == .site2) prev[0] else f0, + hc[0], hc[1], + hc[2], try e.buf("L.mhc.part", .{}, .f32, &.{ n, 4, 40, 32 }), + try e.buf("L.mhc.c", .{}, .bf16, &.{ n, D }), tap, + nw, try e.buf("w.out", .{}, .bf16, &.{ n, D }), + if (mix) next[0] else f0, if (mix) next[1] else f0, + if (mix) next[2] else f0, try e.buf("w.mhc.cnt", .{}, .i32, &.{4}), + .{ .i = n }, .{ .i = @backingInt(mode) }, + .{ .f = E.dec(e.cfg.eps) }, .{ .f = E.dec(e.cfg.hc_eps) }, + .{ .f = E.dec(e.cfg.hc_post_alpha) }, .{ .i = e.cfg.hc_sinkhorn_iters }, + }); + if (e.o.r1) { + // MHC_TAIL (parallel finish) and MHC_DEFER (a mixing site's coefficients as their own launch, joined later) + const last = &e.out.items[e.out.items.len - 1]; + const more = try e.a.alloc(calls.Named, last.args.len + 2); + @memcpy(more[0..last.args.len], last.args); + more[last.args.len] = .{ .arg = .{ .i = e.o.mhc_spin } }; + more[last.args.len + 1] = .{ .arg = .{ .i = @intFromBool(mix) } }; + last.args = more; + if (mix and e.o.mhc_defer) { + e.df_side = true; // MHC_DEFER: on the deferred stream, the next mHC call joins it + e.df_pending = true; + } + if (mix) try e.ext("tf_dsv41_mhc_cuda_v1.coef", &.{ + try e.buf("L.mhc.part", .{}, .f32, &.{ n, 4, 40, 32 }), hc[1], + hc[2], next[0], + next[1], next[2], + .{ .i = n }, .{ .f = E.dec(e.cfg.eps) }, + .{ .f = E.dec(e.cfg.hc_eps) }, .{ .f = E.dec(e.cfg.hc_post_alpha) }, + .{ .i = e.cfg.hc_sinkhorn_iters }, + }); + } else if (e.o.r1_sig) try e.appendLast(&.{ .{ .i = -1 }, .{ .i = 0 } }); // the twin's binding, `r1` off: no tail + if (mix) e.coef = 1 - e.coef; + if (mode == .boundary and !in_place) e.cur = 1 - e.cur; +} + +/// mhc.await_coefs: an mHC call reads the coefficients a deferred launch wrote: the main stream joins it first. +pub fn awaitCoefs(e: *E) void { + if (!e.df_pending) return; + e.df_pending = false; + e.df_join = true; +} + +/// mHC post only: the last exchange's post applied to the streams in place, before a non-first Engram block. +pub fn postOnly(e: *E, out: ?calls.Arg) !void { + const n = e.n; + const D: i64 = e.cfg.hidden; + const x = try e.streams(); + const xout = out orelse x; + const prev = try coefs(e, e.coef); + awaitCoefs(e); + try e.triton("_site", .{ std.math.divCeil(i64, n, 16) catch unreachable, 40, 1 }, &.{ + .{ .name = "X", .arg = x }, .{ .name = "x_stride", .arg = .{ .i = 4 * D } }, + .{ .name = "XOUT", .arg = xout }, .{ .name = "G", .arg = try e.buf("w.recv.moe", .{}, .bf16, &.{ e.o.world, n, D }) }, + .{ .name = "g_rank", .arg = .{ .i = n * D } }, .{ .name = "POST", .arg = prev[1] }, + .{ .name = "COMB", .arg = prev[2] }, .{ .name = "PRE", .arg = x }, + .{ .name = "FN", .arg = x }, .{ .name = "PART", .arg = x }, + .{ .name = "C", .arg = x }, .{ .name = "TAP", .arg = x }, + .{ .name = "tap_stride", .arg = .{ .i = 0 } }, .{ .name = "R", .arg = .{ .i = n } }, + .{ .name = "D", .arg = .{ .i = D } }, .{ .name = "NB", .arg = .{ .i = 40 } }, + .{ .name = "BM", .arg = .{ .i = 16 } }, .{ .name = "BK", .arg = .{ .i = 16 } }, + .{ .name = "WORLD", .arg = .{ .i = e.o.world } }, .{ .name = "POST_ON", .arg = .{ .b = true } }, + .{ .name = "COLLAPSE", .arg = .{ .i = 0 } }, .{ .name = "MIX", .arg = .{ .b = false } }, + .{ .name = "TAP_ON", .arg = .{ .b = false } }, .{ .name = "SPLIT", .arg = .{ .b = false } }, + }); +} + +/// Router GEMV, top-p prune, group, then routed + shared experts: rot_in, x3ld gate/up, epilogue, x3ld down, combine. +pub fn moe(e: *E, L: u32) !void { + const n = e.n; + const D: i64 = e.cfg.hidden; + const backbone = e.cfg.isBackbone(L); + // the router folds kit rounding + grouping into its tail when no prune follows; with `r1` it folds the prune too + const fold = !backbone or e.o.expert_topp == null or e.o.r1; + const ex = e.cfg.expertsOf(L); + const slots: i64 = ex.topk + 1; + const Et: i64 = ex.count + 1; + const I: i64 = e.cfg.expert_width / e.o.world; + const P = n * slots; + const rows: i64 = if (backbone) e.o.expert_rows else e.o.dspark_rows; + const sc: []const u8 = if (backbone) "s.ex" else "s.dx"; + const x = try e.buf("w.out", .{}, .bf16, &.{ n, D }); + const pick = try e.buf("L.pick", .{}, .i32, &.{ n, slots }); + const wts = try e.buf("L.wts", .{}, .f32, &.{ n, slots }); + const uids = try e.buf("{s}.ids", .{sc}, .i32, &.{P}); + const ucount = try e.buf("{s}.count", .{sc}, .i32, &.{1}); + const members = try e.buf("{s}.members", .{sc}, .i32, &.{ P, n }); + // router_gemv.config / row_tile: the narrow kernel's (2, 0) to 16 rows, then the prefill config (block_wide.zig) + const rc = wide.routerCfg(n); + // the route groups in its tail only on the narrow kernel (<= 16 rows) and GE <= 512; else upstream's group follows + const grouped = fold and rc[2] == 0 and Et <= 512; + const scale: f64 = E.dec(e.cfg.routed_scale); + try e.ext("tf_dsv41_router_gemv_v2.route", &.{ + x, try e.weight("L{d}.moe.gate", .{L}, .bf16, &.{ ex.count, D }), + try e.weight("L{d}.moe.bias", .{L}, .f32, &.{ex.count}), pick, + wts, try e.buf("L.logits", .{}, .f32, &.{ n, ex.count }), + try e.buf("s.router.cnt", .{}, .i32, &.{128}), .{ .i = ex.topk }, + .{ .i = slots }, .{ .f = scale }, + .{ .i = rc[0] }, .{ .i = rc[1] }, + .{ .i = rc[2] }, .{ .b = true }, + .{ .b = fold }, if (!grouped) try e.empty(.i32) else uids, + if (!grouped) try e.empty(.i32) else ucount, if (!grouped) try e.t(.empty, .i32, &.{ 0, 1 }) else members, + .{ .i = if (!grouped) 0 else Et }, .{ .b = grouped }, + }); + if (e.o.r1) { + // prune fold args [min_w, topp, scale, min_k, orig, drop, dup] (empty for DSpark); drop = table size + const pa: []const Arg = if (backbone and e.o.expert_topp != null) &.{ + .{ .f = 0.0 }, .{ .f = e.o.expert_topp.? }, .{ .f = scale }, .{ .i = 3 }, .{ .i = 0 }, .{ .i = Et }, .{ .i = 0 }, + } else &.{}; + const last = &e.out.items[e.out.items.len - 1]; + const more = try e.a.alloc(calls.Named, last.args.len + 1); + @memcpy(more[0..last.args.len], last.args); + more[last.args.len] = .{ .arg = .{ .list = try e.a.dupe(Arg, pa) } }; + last.args = more; + } else if (e.o.r1_sig) try e.appendLast(&.{.{ .list = &.{} }}); // the twin's route always takes the prune list + if (!fold) { + try e.triton("_prune", .{ std.math.divCeil(i64, n, 16) catch unreachable, 1, 1 }, &.{ + .{ .name = "PICK", .arg = pick }, .{ .name = "WTS", .arg = wts }, + .{ .name = "R", .arg = .{ .i = n } }, .{ .name = "stride", .arg = .{ .i = slots } }, + .{ .name = "MIN_W", .arg = .{ .f = 0.0 } }, .{ .name = "TOPP", .arg = .{ .f = e.o.expert_topp.? } }, + .{ .name = "SCALE", .arg = .{ .f = scale } }, .{ .name = "DROP", .arg = .{ .i = Et } }, + .{ .name = "MIN_K", .arg = .{ .i = 3 } }, .{ .name = "K", .arg = .{ .i = ex.topk } }, + .{ .name = "KP", .arg = .{ .i = 8 } }, .{ .name = "DUP", .arg = .{ .b = false } }, + .{ .name = "BR", .arg = .{ .i = 16 } }, .{ .name = "ORIG", .arg = .{ .b = false } }, + }); + if (e.o.r1_sig) { // the twin's `_prune` takes KIT (`r1`'s PRUNE_KIT) as its last constexpr + const last = &e.out.items[e.out.items.len - 1]; + const more = try e.a.alloc(calls.Named, last.args.len + 1); + @memcpy(more[0..last.args.len], last.args); + more[last.args.len] = .{ .name = "KIT", .arg = .{ .b = false } }; + last.args = more; + } + try e.glue("kit_weights", &.{ wts, try e.buf("L.wts16", .{}, .f32, &.{ n, slots }) }); + } + const group_rot = !grouped and e.o.router_group_rot and n <= 64 and slots <= 9 and Et <= 512 and D == 5120; + if (!grouped and !group_rot) try e.ext("tensorfold_exl3_experts_v1.group", &.{ pick, uids, ucount, members, .{ .i = n }, .{ .i = slots }, .{ .i = Et } }); + const pre = try e.fmt("s.L{d}.ex", .{L}); + const xg = try e.buf("{s}.xg", .{sc}, .f16, &.{ rows * slots, D }); + const xu = try e.buf("{s}.xu", .{sc}, .f16, &.{ rows * slots, D }); + const xd = try e.buf("{s}.xd", .{sc}, .f16, &.{ rows * slots, I }); + const z = try e.buf("{s}.z", .{sc}, .f32, &.{rows * slots * @max(4 * 2 * I, D)}); + try e.ext(if (group_rot) "tensorfold_exl3_experts_v1.group_rot_in" else "tensorfold_exl3_experts_v1.rot_in", &.{ + x, .{ .i = D }, pick, try e.buf("{s}.suh_g", .{pre}, .f16, &.{ Et, D }), try e.buf("{s}.suh_u", .{pre}, .f16, &.{ Et, D }), + xg, xu, .{ .i = n }, .{ .i = D }, .{ .i = slots }, + .{ .i = Et }, + }); + if (group_rot) try e.appendLast(&.{ uids, ucount, members }); + const r = e.w.experts.get(L) orelse return error.NoWidth; + const gu = try block.k2Range(r[0][0], r[0][1]); + const dn = try block.k2Range(r[1][0], r[1][1]); + const svh_g = try e.buf("{s}.svh_g", .{pre}, .f16, &.{ Et, I }); + const svh_u = try e.buf("{s}.svh_u", .{pre}, .f16, &.{ Et, I }); + const suh_d = try e.buf("{s}.suh_d", .{pre}, .f16, &.{ Et, I }); + const tp_g = try e.buf("{s}.tp_g", .{pre}, .i64, &.{Et}); + const tp_u = try e.buf("{s}.tp_u", .{pre}, .i64, &.{Et}); + const k2_g = try e.buf("{s}.k2_g", .{pre}, .i32, &.{Et}); + const k2_u = try e.buf("{s}.k2_u", .{pre}, .i32, &.{Et}); + const tp_d = try e.buf("{s}.tp_d", .{pre}, .i64, &.{Et}); + const k2_d = try e.buf("{s}.k2_d", .{pre}, .i32, &.{Et}); + const svh_d = try e.buf("{s}.svh_d", .{pre}, .f16, &.{ Et, D }); + const y = try e.buf("{s}.y", .{sc}, .f32, &.{ rows * slots, D }); + // after a prune, kit_weights' fp16 rounding (a pointwise op the forward runs) gives new weights + const cw = if (!fold) try e.buf("L.wts16", .{}, .f32, &.{ n, slots }) else wts; + const out = try e.buf("L.moe", .{}, if (e.o.r1 and backbone) .bf16 else .f32, &.{ n, D }); + const limit: Arg = .{ .f = E.dec(e.cfg.swiglu_limit) }; + if (e.o.x3ld_epi) { + // TF_DSV41_X3LD_EPI: the same x3ld work with the epilogues in its tail behind a last-arrival ticket; same bits + const mt = std.math.divCeil(i64, n, 16) catch unreachable; + const ticket = try e.buf("{s}.epi", .{sc}, .i32, &.{@max(P * mt * @divExact(I, 128), n * @divExact(D, 128))}); + try e.ext("tf_dsv41_x3ld_epi_v1.gateup", &.{ + xg, xu, tp_g, tp_u, k2_g, k2_u, uids, ucount, + members, z, .{ .i = 2 }, .{ .i = D }, .{ .i = I }, .{ .i = P }, .{ .i = 4 }, .{ .i = slots }, + .{ .i = 2 }, .{ .i = 8 }, .{ .i = 1 }, .{ .i = gu[0] }, .{ .i = gu[1] }, pick, svh_g, svh_u, + suh_d, xd, .{ .i = Et }, limit, .{ .i = 1 }, ticket, + }); + try e.ext("tf_dsv41_x3ld_epi_v1.down", &.{ + xd, xd, tp_d, tp_d, k2_d, k2_d, uids, ucount, + members, z, .{ .i = 1 }, .{ .i = I }, .{ .i = D }, .{ .i = P }, .{ .i = 1 }, .{ .i = slots }, + .{ .i = 2 }, .{ .i = 8 }, .{ .i = 1 }, .{ .i = dn[0] }, .{ .i = dn[1] }, pick, svh_d, y, + cw, out, .{ .i = Et }, ticket, + }); + return; + } + try e.ext("tf_dsv41_x3ld_v1.grouped", &.{ + xg, xu, + tp_g, tp_u, + k2_g, k2_u, + uids, ucount, + members, z, + .{ .i = 2 }, .{ .i = D }, + .{ .i = I }, .{ .i = P }, + .{ .i = 4 }, .{ .i = slots }, + .{ .i = 2 }, .{ .i = 8 }, + .{ .i = 1 }, .{ .i = 0 }, + .{ .i = gu[0] }, .{ .i = gu[1] }, + .{ .b = false }, + }); + try e.ext("tensorfold_exl3_experts_v1.gateup_epilogue", &.{ + z, pick, + svh_g, svh_u, + suh_d, xd, + .{ .i = n }, .{ .i = P }, + .{ .i = I }, .{ .i = 4 }, + .{ .i = slots }, .{ .i = Et }, + limit, .{ .i = 1 }, + }); + try e.ext("tf_dsv41_x3ld_v1.grouped", &.{ + xd, xd, tp_d, tp_d, k2_d, k2_d, uids, ucount, + members, z, .{ .i = 1 }, .{ .i = I }, .{ .i = D }, .{ .i = P }, .{ .i = 1 }, .{ .i = slots }, + .{ .i = 2 }, .{ .i = 8 }, .{ .i = 1 }, .{ .i = 0 }, .{ .i = dn[0] }, .{ .i = dn[1] }, .{ .b = false }, + }); + try e.ext("tensorfold_exl3_experts_v1.down_combine", &.{ + z, pick, + svh_d, y, + cw, out, + .{ .i = n }, .{ .i = P }, + .{ .i = D }, .{ .i = 1 }, + .{ .i = slots }, .{ .i = Et }, + }); +} + +/// Engram (layers 1 and 14), before the layer's first mHC site: wkv over the hashed rows, gated fusion into streams. +pub fn engram(e: *E, L: u32) !void { + const n = e.n; + const D: i64 = e.cfg.hidden; + const K: i64 = @as(i64, e.cfg.hashCols()) * e.cfg.engram_head_dim; + const N: i64 = (@as(i64, e.cfg.engram_max_ngram) + 1) * D; + const kv = try e.buf("L.engram.kv", .{}, .bf16, &.{ n, N }); + try e.glue("engram_rows", &.{ try e.buf("L.engram.rows", .{}, .bf16, &.{ n, K }), .{ .i = L } }); + try e.upstream(try e.fmt("L{d}.engram.wkv", .{L}), try e.buf("L.engram.rows", .{}, .bf16, &.{ n, K }), K, N, kv); + const qk = try e.weight("L{d}.engram.qk", .{L}, .f32, &.{ e.cfg.hc_mult, D }); + try e.triton("_fuse_dec", .{ n, 1, 1 }, &.{ + .{ .name = "X", .arg = try e.streams() }, .{ .name = "x_stride", .arg = .{ .i = 4 * D } }, + .{ .name = "KV", .arg = kv }, .{ .name = "kv_stride", .arg = .{ .i = N } }, + .{ .name = "QK", .arg = qk }, .{ .name = "KEEP", .arg = qk }, + .{ .name = "GATE", .arg = qk }, .{ .name = "eps", .arg = .{ .f = E.dec(e.cfg.eps) } }, + .{ .name = "clamp", .arg = .{ .f = E.dec(e.cfg.engram_gate_clamp) } }, .{ .name = "sqrt_d", .arg = .{ .f = @sqrt(@as(f64, @floatFromInt(D))) } }, + .{ .name = "D", .arg = .{ .i = D } }, .{ .name = "BK", .arg = .{ .i = 64 } }, + .{ .name = "UB", .arg = .{ .i = 1024 } }, .{ .name = "HAS_KEEP", .arg = .{ .b = false } }, + .{ .name = "HAS_GATE", .arg = .{ .b = false } }, + }); +} + +/// The final norm (mHC final) and this rank's head columns (fp32 logits). +pub fn finish(e: *E) !void { + try mhc(e, null, "", .final); + const D: i64 = e.cfg.hidden; + const V: i64 = e.cfg.vocab / e.o.world; + const logits = try e.buf("w.logits", .{}, .f32, &.{ e.n, V }); + try e.upstream("head", try e.buf("w.out", .{}, .bf16, &.{ e.n, D }), D, V, logits); + try e.glue("kit_logits", &.{logits}); +} diff --git a/zig/src/families/deepseek_v41/block_prefetch.zig b/zig/src/families/deepseek_v41/block_prefetch.zig new file mode 100644 index 0000000000..47a9d4bcd2 --- /dev/null +++ b/zig/src/families/deepseek_v41/block_prefetch.zig @@ -0,0 +1,110 @@ +//! A decode window's L2 prefetch (TF_DSV41_L2PF): each site's sources, the table rows, the side-stream launches. + +const std = @import("std"); +const block = @import("block.zig"); +const Emitter = block.Emitter; +const L2Src = block.Emitter.L2Src; +const L2Site = block.Emitter.L2Site; + +/// l2pf._lin_tensors: every projection's suh and svh, then their trellis words. +pub fn linSrcs(e: *Emitter, out: *std.ArrayList(L2Src), ws: []const []const u8, ks: []const i64, ns: []const i64) !void { + for (ws, ks, ns) |w, k, n| try out.appendSlice(e.a, &.{ + .{ .kind = .weight, .name = try e.fmt("{s}.suh", .{w}), .bytes = @intCast(2 * k) }, + .{ .kind = .weight, .name = try e.fmt("{s}.svh", .{w}), .bytes = @intCast(2 * n) }, + }); + for (ws, ks, ns) |w, k, n| try out.append(e.a, .{ .kind = .words, .name = w, .bytes = try e.trellisBytes(w, k, n) }); +} + +/// Site `site`'s sources: "o" and "f" of block `L`, "x" of the block before `L` (its table is "s.L.pf.x"). +pub fn l2pfSources(e: *Emitter, L: u32, site: L2Site) ![]const L2Src { + var out: std.ArrayList(L2Src) = .empty; + switch (site) { + .o => { + const D: i64 = e.cfg.hidden; + const g = e.cfg.o_groups / e.o.world; + const ki: i64 = @divExact(@as(i64, e.cfg.heads) * e.cfg.head_dim, @as(i64, e.cfg.o_groups)); + const ws = try e.a.alloc([]const u8, g); + const ks = try e.a.alloc(i64, g); + const ns = try e.a.alloc(i64, g); + for (ws, ks, ns, 0..) |*w, *k, *n, i| { + w.* = try e.fmt("L{d}.attn.wo_a.{d}", .{ L, i }); + k.* = ki; + n.* = e.cfg.o_lora; + } + try e.linSrcs(&out, ws, ks, ns); + try e.linSrcs(&out, &.{try e.fmt("L{d}.attn.wo_b", .{L})}, &.{@as(i64, g) * e.cfg.o_lora}, &.{D}); + }, + .f => { + const D: i64 = e.cfg.hidden; + const I: i64 = e.cfg.expert_width / e.o.world; + const ex = e.cfg.expertsOf(L); + const sh = try e.fmt("L{d}.moe.shared.0.w1", .{L}); + const k2: u64 = try e.w.k2(sh); + const shared: u64 = @as(u64, @intCast(@divExact(D, 16) * @divExact(I, 16))) * 8 * k2 * 2; + try out.appendSlice(e.a, &.{ + .{ .kind = .weight, .name = try e.fmt("L{d}.moe.gate", .{L}), .bytes = @as(u64, ex.count) * @as(u64, @intCast(D)) * 2 }, + .{ .kind = .expert, .name = try e.fmt("s.L{d}.ex.tp_g", .{L}), .index = ex.count, .bytes = shared }, + .{ .kind = .expert, .name = try e.fmt("s.L{d}.ex.tp_u", .{L}), .index = ex.count, .bytes = shared }, + }); + }, + .x => { + const xg = try e.xGroup(L); + try e.linSrcs(&out, xg.ws, xg.ks, xg.ns); + const qg = try e.qGroup(L); + try e.linSrcs(&out, qg.ws, qg.ks, qg.ns); + }, + } + return out.items; +} + +pub fn srcSizes(e: *Emitter, srcs: []const L2Src) ![]const u64 { + const out = try e.a.alloc(u64, srcs.len); + for (srcs, out) |x, *y| y.* = x.bytes; + return out; +} + +/// l2pf.take: spans of `sizes` in order within the budget, the last one's head. +pub fn take(e: *const Emitter, sizes: []const u64) i64 { + var left = e.o.l2pf_budget; + var rows: i64 = 0; + for (sizes) |s| { + if (left < 16) break; + const n = @min(left, s) / 16 * 16; + if (n == 0) continue; + rows += 1; + left -= n; + } + return rows; +} + +/// Site "o" (after wq_b): wo_a's groups, then wo_b; l2pf segments. +pub fn prefetchO(e: *Emitter, L: u32) !void { + const sizes = try e.srcSizes(try e.l2pfSources(L, .o)); + var total: u64 = 0; + var left = e.o.l2pf_budget; + for (sizes) |s| { + if (left < 16) break; + const n = @min(left, s) / 16 * 16; + total += n; + left -= n; + } + const pieces = std.math.divCeil(u64, total, @intCast(e.o.l2pf_chunk)) catch 1; + const grid: i64 = @max(1, @min(e.o.sms, @as(i64, @intCast(std.math.divCeil(u64, pieces, 128) catch 1)))); + const rows = e.take(sizes); + try e.ext("tf_dsv41_l2pf_v1.segments", &.{ .{ .opaque_table = (try e.buf("s.L{d}.pf.o", .{L}, .i64, &.{ rows, 2 })).t }, .{ .i = grid }, .{ .i = 128 }, .{ .i = e.o.l2pf_chunk }, .{ .b = true } }); +} + +pub fn paced(e: *Emitter, role: []const u8, rows: i64) !void { + const ns: i64 = @max(1, @as(i64, @intFromFloat(@round(@as(f64, @floatFromInt(e.o.l2pf_chunk)) / e.o.l2pf_pace_gbps)))); + try e.ext("tf_dsv41_l2pace_v1.paced", &.{ .{ .opaque_table = (try e.buf("{s}", .{role}, .i64, &.{ rows, 2 })).t }, .{ .i = e.o.l2pf_pace_ctas }, .{ .i = e.o.l2pf_chunk }, .{ .i = ns }, .{ .i = 0 } }); +} + +/// Site "f" (after the attention sublayer): the router's gate, then the shared expert's gate and up trellises. +pub fn prefetchF(e: *Emitter, L: u32) !void { + try e.paced(try e.fmt("s.L{d}.pf.f", .{L}), e.take(try e.srcSizes(try e.l2pfSources(L, .f)))); +} + +/// Site "x" of the block before `nxt` (after its MoE): `nxt`'s x group, then its q group. +pub fn prefetchX(e: *Emitter, nxt: u32) !void { + try e.paced(try e.fmt("s.L{d}.pf.x", .{nxt}), e.take(try e.srcSizes(try e.l2pfSources(nxt, .x)))); +} diff --git a/zig/src/families/deepseek_v41/block_prefill.zig b/zig/src/families/deepseek_v41/block_prefill.zig new file mode 100644 index 0000000000..888271fd72 --- /dev/null +++ b/zig/src/families/deepseek_v41/block_prefill.zig @@ -0,0 +1,407 @@ +//! A prefill segment on the fast-prefill path, emitted as calls (block_calls.zig) by the Emitter in reference order. + +// Short segments (<= 16 rows) take the decode window's row paths; past TF_DSV41_INDEX_STREAM_MIN keys, stream_topk. + +const std = @import("std"); +const dk = @import("dsv41_kernels"); +const calls = @import("block_calls.zig"); +const block = @import("block.zig"); +const config = @import("config.zig"); +const moe = @import("block_moe.zig"); +const longpf = @import("longpf.zig"); +const gm2pf = @import("gm2pf.zig"); +const ced = @import("ced.zig"); +const Config = config.Config; +pub const E = block.Emitter; +const Arg = calls.Arg; +const Dt = calls.Dt; +const dense = dk.dense; +const PfDense = @import("knob_types.zig").PfDense; +const block_prefill_mhc = @import("block_prefill_mhc.zig"); +const block_prefill_attn = @import("block_prefill_attn.zig"); +const block_prefill_select = @import("block_prefill_select.zig"); +const block_prefill_moe = @import("block_prefill_moe.zig"); +const block_prefill_emit = @import("block_prefill_emit.zig"); + +// reference knobs and constants +const ws_limit: i64 = 64 << 20; // TF_DSV41_PREFILL_WS_MIB: a workspace W_q's bytes at most (column blocks) +const had_scale: f64 = 0.08838834764831845; // exl3.prefill.HAD_SCALE (the literal: 1 / sqrt(128) rounds the other way) +pub const pf_rows: i64 = 256; // TF_DSV41_MHC_PF_ROWS / TF_DSV41_MHC_ROWS: mhc_pf and the wide `_site` tile from here +pub const mhc_bm: i64 = 64; // TF_DSV41_MHC_BM +pub const split_rows: i64 = 32; // TF_DSV41_MHC_SPLIT_ROWS: out-of-place boundaries, a program a stream +pub const stream_min = @import("forward_types.zig").stream_min; +pub const bmq: i64 = 32; // TF_DSV41_PREFILL_ATTN_BMQ +pub const kt: i64 = 32; // csa2.attn.KT +pub const router_chunk: i64 = 2048; // router_gemv.CHUNK: rows a route launch +pub const gm_bm: i64 = 64; // x3gm: members a pass of the gate/up (cfg 1) and down (cfg 0) tiles below 3,072 rows +pub const gm_widths: u32 = 7; // x3gm.MAX_WIDTHS: the ticket's down half starts there +const index_budget: i64 = 256 << 20; // TF_DSV41_INDEX_BUDGET_MIB's default: block.Options.index_budget is the run's +pub const dtopk_rows: i64 = 64; // dtopk.MAX_ROWS: windows up to this many rows select with attn_cuda's top-k (backend._dt) +const overlap_min: i64 = 256; // TF_DSV41_PF_OVERLAP: min rows a piece (smaller GEMMs cost more than they hide) + +pub fn size(dt: Dt) i64 { + return switch (dt) { + .bf16, .f16, .i16 => 2, + .f32, .i32 => 4, + .f64, .i64 => 8, + .i8, .u8, .bool => 1, + }; +} + +pub fn cdiv(a: i64, b: i64) i64 { + return @divFloor(a + b - 1, b); +} + +/// How a matrix's words are kept: dense3's lanes, upstream's strips, or the checkpoint's tiles (the shared expert). +const Form = enum { lanes, strips, stored }; + +/// Where a matmul's [m, N] result goes: `role`'s rows of `stride` elements from byte `offset`. +pub const Out = struct { role: calls.Role, dt: Dt, stride: i64, offset: i64 = 0 }; + +/// One prefill segment: the emitter, the SWA ring its rows go through, and what its layers left (the candidates). +pub const Seg = struct { + e: *E, + /// the SWA ring the layers store into and attend over: the slot's (2 x window rows) or the staging ring + staged: bool, + ring: i64, + /// the candidate source ran in this segment (reindex layers read its candidates) + cand: bool = false, + /// at most 16 rows: the short segment's kernel mix + short: bool = false, + /// CED replay's passes (`emitReplay`, ced.zig); `.whole`: a full segment + part: Part = .whole, + multi: ?[]const Span = null, // a multi-segment run: core and compressor a segment at a time (glue "pf_seg") + sent_attn: bool = false, // TF_DSV41_PF_OVERLAP: the partial already went out in pieces; no whole exchange + sent_moe: bool = false, + pend: i64 = 0, // TF_DSV41_PF_OVERLAP_SITE: the last exchange's k side pieces (marks 1..k); 0: none pending + + // -- TF_DSV41_PF_OVERLAP: the exchanges in row pieces, overlapped with their producers' next piece ------------- + + /// Row pieces of this segment's exchanges: Options.pf_overlap when it applies, else 1. + pub fn pieces(s: *const Seg) i64 { + const e = s.e; + const k = e.o.pf_overlap; + if (k < 2 or s.short or !e.o.branches or e.o.pf_tbo or e.o.world != 2 or e.n < k * overlap_min) return 1; + return k; + } + + /// Piece `p` of `k` over `n` rows: [r0, r1) on the 16-row grid, the last to `n`. + pub fn pieceAt(n: i64, k: i64, p: i64) [2]i64 { + const cut = struct { + fn at(nn: i64, kk: i64, q: i64) i64 { + return if (q >= kk) nn else @divFloor(@divFloor(nn * q, kk), 16) * 16; + } + }; + return .{ cut.at(n, k, p), cut.at(n, k, p + 1) }; + } + + /// Exchange of rows [r0, r0 + rows) of `part` into `into` [world, n, D]: side-stream pieces, the last joins main. + pub fn sendPiece(s: *Seg, part: Arg, into: Arg, r0: i64, rows: i64, p: i64, k: i64) !void { + const e = s.e; + const send = try rowsOf(e, part, .bf16, r0, rows, e.cfg.hidden); + const last = p + 1 == k; + if (last and !e.o.pf_overlap_site) e.br_join = true else { + e.br_fork = true; + e.br_side = true; + if (e.o.pf_overlap_site) e.br_mark = @intCast(p + 1); + } + defer e.br_side = false; + // the piece's all-gather lands in "L.xrows" [world, rows, D], then each rank's rows are copied into `into` + const tmp = try e.buf("L.xrows", .{}, .bf16, &.{ e.o.world, rows, e.cfg.hidden }); + try e.glue("exchange_rows", &.{ send, into, .{ .i = r0 }, tmp }); + if (last and e.o.pf_overlap_site) { + s.pend = k; + e.br_join = true; + } + } + + // -- dense EXL3 projections ---------------------------------------------------------------------------------- + + /// y = x [m, K] @ W [K, N] (`w`'s prefix): per column block, upstream's rot_in, the W_q unpack, `_gemm`. + pub fn matmul(s: *Seg, w: []const u8, form: Form, x: Arg, k: i64, n: i64, y: Out) !void { + const e = s.e; + const m = e.n; + const k2: i64 = try e.w.k2(w); + const tw = 4 * k2; + const nb = @divExact(n, 128); + if (e.o.pfd) |p| if (PfDense.eligible(k, n, @intCast(k2), form == .lanes)) return s.fused(p, w, form, x, k, n, y); + // prefill_mm.column_blocks: whole 128-column blocks whose W_q fits the workspace (strips / lanes only) + const per: i64 = if (k * n * 2 <= ws_limit or form == .stored) nb else @max(1, @divFloor(ws_limit, k * 2 * 128)); + const st = dense.strides(@intCast(k), @intCast(k2)); + var b0: i64 = 0; + while (b0 < nb) : (b0 += per) { + const b1 = @min(nb, b0 + per); + const c0 = 128 * b0; + const nc = 128 * (b1 - b0); + const xh = try e.buf("s.pf.xh", .{}, .f16, &.{ m, k }); + try e.ext("tensorfold_exl3_linear_v4.rot_in", &.{ x, try e.weight("{s}.suh", .{w}, .f16, &.{k}), xh }); + const wq = try e.buf("s.pf.w", .{}, .f16, &.{ k, nc }); + const blk_words = @divExact(k, 16) * 8 * tw; // words of a 128-column block + switch (form) { + .lanes => try e.ext("tf_dsv41_dense3_v1.unpack", &.{ + try e.view(.{ .buf = try e.fmt("s.{s}.lanes", .{w}) }, .i32, &.{ b1 - b0, @divExact(k, 16), 8, tw }, &.{ blk_words, 8 * tw, tw, 1 }, b0 * blk_words * 4), + wq, + .{ .i = st[0] }, + .{ .i = st[1] }, + .{ .i = k2 }, + }), + .strips => try e.ext("tensorfold_exl3_linear_v4.unpack", &.{ + // one block of 128 columns: the stored trellis is the strips (block.zig's upstream) + if (n == 128) + try e.view(.{ .weight = try e.fmt("{s}.trellis", .{w}) }, .i32, &.{ 1, @divExact(k, 16), 8, tw }, &.{ 8 * tw, 8 * tw, tw, 1 }, 0) + else + try e.view(.{ .buf = try e.fmt("s.{s}.T", .{w}) }, .i32, &.{ b1 - b0, @divExact(k, 16), 8, tw }, &.{ blk_words, 8 * tw, tw, 1 }, b0 * blk_words * 4), + wq, + .{ .i = st[0] }, + .{ .i = st[1] }, + .{ .i = k2 }, + .{ .i = 2 }, + }), + // Exl3Linear.strides of the stored layout: (words between k tiles, between 16-column tiles) + .stored => try e.ext("tensorfold_exl3_linear_v4.unpack", &.{ + try e.view(.{ .weight = try e.fmt("{s}.trellis", .{w}) }, .i32, &.{ @divExact(k, 16), @divExact(n, 16), tw }, &.{ @divExact(n, 16) * tw, tw, 1 }, 0), + wq, + .{ .i = @divExact(n, 16) * tw }, + .{ .i = 8 * tw }, + .{ .i = k2 }, + .{ .i = 2 }, + }), + } + const svh = try e.view(.{ .weight = try e.fmt("{s}.svh", .{w}) }, .f16, &.{nc}, &.{1}, c0 * 2); + const out = try e.view(y.role, y.dt, &.{ m, nc }, &.{ y.stride, 1 }, y.offset + c0 * size(y.dt)); + // exl3.prefill.tiles: BM 128, BK 32, group 8 (the shape's alone) + try e.triton("_gemm", .{ cdiv(m, 128) * @divExact(nc, 128), 1, 1 }, &.{ + .{ .name = "X", .arg = xh }, .{ .name = "W", .arg = wq }, + .{ .name = "H", .arg = try e.buf("s.pf.had", .{}, .bf16, &.{ 128, 128 }) }, .{ .name = "SVH", .arg = svh }, + .{ .name = "BIAS", .arg = svh }, .{ .name = "OUT", .arg = out }, + .{ .name = "M", .arg = .{ .i = m } }, .{ .name = "o_stride", .arg = .{ .i = y.stride } }, + .{ .name = "K", .arg = .{ .i = k } }, .{ .name = "N", .arg = .{ .i = nc } }, + .{ .name = "BM", .arg = .{ .i = 128 } }, .{ .name = "BK", .arg = .{ .i = 32 } }, + .{ .name = "GROUP", .arg = .{ .i = 8 } }, .{ .name = "HAS_BIAS", .arg = .{ .b = false } }, + .{ .name = "SCALE", .arg = .{ .f = had_scale } }, + }); + } + } + + /// TF_DSV41_PF_DENSE=fused: rot_in into the workspace, then one fused GEMM over the whole layer (no column blocks). + fn fused(s: *Seg, p: *const PfDense, w: []const u8, form: Form, x: Arg, k: i64, n: i64, y: Out) !void { + const e = s.e; + const m = e.n; + const k2: u32 = try e.w.k2(w); + const tw: i64 = 4 * @as(i64, k2); + const nb = @divExact(n, 128); + const blk_words = @divExact(k, 16) * 8 * tw; + const xh = try e.buf("s.pf.xh", .{}, .f16, &.{ m, k }); + try e.ext("tensorfold_exl3_linear_v4.rot_in", &.{ x, try e.weight("{s}.suh", .{w}, .f16, &.{k}), xh }); + const st = dense.strides(@intCast(k), k2); + const words: Arg, const sk: i64, const snb: i64 = switch (form) { + .lanes => .{ try e.view(.{ .buf = try e.fmt("s.{s}.lanes", .{w}) }, .i32, &.{ nb, @divExact(k, 16), 8, tw }, &.{ blk_words, 8 * tw, tw, 1 }, 0), st[0], st[1] }, + .strips => .{ if (n == 128) + try e.view(.{ .weight = try e.fmt("{s}.trellis", .{w}) }, .i32, &.{ 1, @divExact(k, 16), 8, tw }, &.{ 8 * tw, 8 * tw, tw, 1 }, 0) + else + try e.view(.{ .buf = try e.fmt("s.{s}.T", .{w}) }, .i32, &.{ nb, @divExact(k, 16), 8, tw }, &.{ blk_words, 8 * tw, tw, 1 }, 0), st[0], st[1] }, + .stored => .{ try e.view(.{ .weight = try e.fmt("{s}.trellis", .{w}) }, .i32, &.{ @divExact(k, 16), @divExact(n, 16), tw }, &.{ @divExact(n, 16) * tw, tw, 1 }, 0), @divExact(n, 16) * tw, 8 * tw }, + }; + const c = p.pick(@intCast(k), @intCast(n), k2, @intCast(m)); + try e.ext("tf_dsv41_pfdense_v1.gemm", &.{ + xh, words, + .{ .i = sk }, .{ .i = snb }, + try e.weight("{s}.svh", .{w}, .f16, &.{n}), .none, + try e.view(y.role, y.dt, &.{ m, n }, &.{ y.stride, 1 }, y.offset), .{ .i = k2 }, + .{ .b = form == .lanes }, .{ .i = c[0] }, + .{ .i = c[1] }, + }); + } + + /// A projection into a whole contiguous buffer. + pub fn proj(s: *Seg, w: []const u8, form: Form, x: Arg, k: i64, n: i64, y: Arg) !void { + return s.matmul(w, form, x, k, n, .{ .role = y.t.role, .dt = y.t.dt, .stride = n, .offset = y.t.offset }); + } + + pub const coefs = block_prefill_mhc.coefs; + pub const site = block_prefill_mhc.site; + pub const sitePieces = block_prefill_mhc.sitePieces; + pub const siteTriton = block_prefill_mhc.siteTriton; + pub const finish = block_prefill_mhc.finish; + pub const postOnly = block_prefill_mhc.postOnly; + pub const final = block_prefill_mhc.final; + pub const pos = block_prefill_attn.pos; + pub const swa = block_prefill_attn.swa; + pub const indexKeys = block_prefill_attn.indexKeys; + pub const kvStore = block_prefill_attn.kvStore; + pub const compress = block_prefill_attn.compress; + pub const selection = block_prefill_select.selection; + pub const rowsOf = block_prefill_select.rowsOf; + pub const scores = block_prefill_select.scores; + pub const sortKeys = block_prefill_select.sortKeys; + pub const candidates = block_prefill_select.candidates; + pub const select = block_prefill_select.select; + pub const streamSelect = block_prefill_select.streamSelect; + pub const blockPos = block_prefill_attn.blockPos; + pub const compProj = block_prefill_attn.compProj; + pub const encoderEnd = block_prefill_attn.encoderEnd; + pub const attention = block_prefill_attn.attention; + pub const core = block_prefill_attn.core; + + /// A multi-segment run's segment `j`: the emitter at its rows and position, the glue on its slot (pf_seg). + pub fn enter(s: *Seg, sp: Span, j: usize) !void { + const e = s.e; + e.row0 = sp.row0; + e.cnt0 = sp.cnt0; + e.n = sp.n; + e.start = sp.start; + s.setRing(); + // the decoder replay: every row's window from the segment's first row (replay.finish's ctx.lo) + try e.glue("pf_seg", &.{ .{ .i = @intCast(j) }, try pos(e), try e.buf("w.pos64", .{}, .i64, &.{sp.n}), try e.buf("w.lo", .{}, .i32, &.{sp.n}), .{ .b = s.part == .decoder } }); + } + + /// Back at the whole run (`total` rows from its first segment's start): the glue's state the run's again. + pub fn leave(s: *Seg, total: i64) !void { + const e = s.e; + const first = s.multi.?[0]; + e.row0 = 0; + e.cnt0 = 0; + e.n = total; + e.start = first.start; + s.setRing(); + try e.glue("pf_seg", &.{.{ .i = -1 }}); + } + + /// forward.staging for `e.n` rows: the slot's ring (2 x window rows), or the staging ring past it. + pub fn setRing(s: *Seg) void { + const e = s.e; + const W: i64 = e.cfg.window; + s.staged = e.n + W - 1 > 2 * W; + s.ring = if (s.staged) stageRows(e) else 2 * W; + } + + pub const moeBlock = block_prefill_moe.moeBlock; + pub const moeSplit = block_prefill_moe.moeSplit; + pub const moeRows = block_prefill_moe.moeRows; + pub const engram = block_prefill_moe.engram; + + // -- the segment --------------------------------------------------------------------------------------------- + + /// Where an exchange (`which`: attn / moe) lands: the layer's (`gathered`), or a short segment's "w.recv.*". + pub fn recv(s: *Seg, comptime which: []const u8) !Arg { + const e = s.e; + return e.buf("{s}.recv." ++ which, .{if (s.short) "w" else "L"}, .bf16, &.{ e.o.world, e.n, e.cfg.hidden }); + } + + pub fn run(s: *Seg, layers: []const u32, head: bool) !void { + const e = s.e; + const n = e.n; + const D: i64 = e.cfg.hidden; + if (s.part == .decoder and s.multi != null) { + // several slots' decoder replays: every segment's positions, then each one's entering rows from its stash + try e.glue("mpositions", &.{try e.buf("w.mpos64", .{}, .i64, &.{n})}); + for (s.multi.?, 0..) |sp, j| { + try s.enter(sp, j); + try e.glue("ced_load", &try stashArgs(e)); + } + try s.leave(n); + } else if (s.part == .decoder) { + // decoder replay: every row's window starts at R0 (ctx.lo); the entering rows come from the stash + try e.glue("positions", &.{ try pos(e), try e.buf("w.pos64", .{}, .i64, &.{n}), try e.buf("w.lo", .{}, .i32, &.{n}), .{ .i = e.start } }); + const st = try stashArgs(e); + try e.glue("ced_load", &st); + } else if (s.multi != null) { + // every segment's positions (rope over the run's rows); each segment's own at its CSA2 steps (pf_seg) + try e.glue("mpositions", &.{try e.buf("w.mpos64", .{}, .i64, &.{n})}); + try e.glue("embed", &.{ try e.streams(), try e.buf("w.recv.moe", .{}, .bf16, &.{ e.o.world, n, D }) }); + } else { + try e.glue("positions", &.{ try pos(e), try e.buf("w.pos64", .{}, .i64, &.{n}), try e.buf("w.lo", .{}, .i32, &.{n}) }); + try e.glue("embed", &.{ try e.streams(), try e.buf("w.recv.moe", .{}, .bf16, &.{ e.o.world, n, D }) }); + } + for (layers, 0..) |L, i| { + if (i > 0) e.begin = .layer; + // TF_DSV41_PF_TBO: a layer's attention part starts here (the first's at the program's first call) + if (i > 0) if (e.marks) |m| try m.append(e.a, e.out.items.len); + e.layer_ratio = e.cfg.compressRatio(L); + if (s.part == .decoder and i == 0) { + // entered (forward._layers): the first block's attention site is the stash's + } else if (i == 0 or e.cfg.isEngram(L)) { + // the run's first block, or an Engram block: post alone, Engram, then a site collapsing with the pre + if (i > 0) try s.postOnly(try e.streams()); + if (e.cfg.isEngram(L)) try s.engram(L); + try s.site(L, "hc_attn", if (i == 0) .site1 else .site2); + } else try s.site(L, "hc_attn", .boundary); + // CED's encoder pass stops at the decoder's first layer once its site and compressor ran (`upto`) + if (s.part == .encoder and i + 1 == layers.len) return s.encoderEnd(L); + try s.attention(L); + if (s.short) try e.prefetchF(L); + // TF_DSV41_PF_OVERLAP: the partial went out a piece at a time behind its producer + if (!s.sent_attn) try e.glue("exchange", &.{ try e.buf("L.part", .{}, .bf16, &.{ n, D }), try s.recv("attn") }); + s.sent_attn = false; + if (e.marks) |m| try m.append(e.a, e.out.items.len); // its MoE part + try s.site(L, "hc_ffn", .boundary); + try s.moeBlock(L); + if (s.short and i + 1 < layers.len) try e.prefetchX(layers[i + 1]); + if (!s.sent_moe) try e.glue("exchange", &.{ try e.buf("L.moe16", .{}, .bf16, &.{ n, D }), try s.recv("moe") }); + s.sent_moe = false; + if (e.o.trace) { + const y = try e.buf("w.trace", .{}, .bf16, &.{ n, 4 * D }); + try s.postOnly(y); + try e.glue("trace", &.{ y, .{ .i = L } }); + } + } + if (head) try s.final(); + s.unpend(); + } + + /// A program ending on an exchange's pieces: its last piece back on the main stream after a join (no open fork). + fn unpend(s: *Seg) void { + const e = s.e; + if (s.pend == 0) return; + s.pend = 0; + e.br_join = false; + const c = &e.out.items[e.out.items.len - 1]; + std.debug.assert(std.mem.eql(u8, c.name, "glue.exchange_rows") and c.side); + c.side = false; + c.fork = false; + c.mark = 0; + c.join = true; + } +}; + +/// The CED stash glue's tensors: the run's streams, normed input and coefficients, then the ring's roles. +pub fn stashArgs(e: *E) ![10]Arg { + const D: i64 = e.cfg.hidden; + const R: i64 = ced.ring; + const c = try Seg.coefs(e, e.coef); + const x = try e.streams(); + const r0 = e.row0; + return .{ + try Seg.rowsOf(e, x, .bf16, r0, e.n, 4 * D), try Seg.rowsOf(e, try e.buf("w.out", .{}, .bf16, &.{ e.n, D }), .bf16, r0, e.n, D), + try Seg.rowsOf(e, c[0], .f32, r0, e.n, 4), try Seg.rowsOf(e, c[1], .f32, r0, e.n, 4), + try Seg.rowsOf(e, c[2], .f32, r0, e.n, 16), try e.buf(ced.roles[0], .{}, .bf16, &.{ R, 4 * D }), + try e.buf(ced.roles[1], .{}, .bf16, &.{ R, D }), try e.buf(ced.roles[2], .{}, .f32, &.{ R, 4 }), + try e.buf(ced.roles[3], .{}, .f32, &.{ R, 4 }), try e.buf(ced.roles[4], .{}, .f32, &.{ R, 16 }), + }; +} + +/// One segment of a multi-segment run: its slot's rows from position `start`, rows [row0, row0 + n) of the run. +pub const Span = struct { slot: u32, start: i64, n: i64, row0: i64 = 0, cnt0: i64 = 0 }; + +/// forward.staging's ring rows for a prefill segment the slot's ring cannot hold. +fn stageRows(e: *const E) i64 { + const W: i64 = e.cfg.window; + return @intCast(std.math.ceilPowerOfTwo(u64, @intCast(e.o.prefill_rows + W - 1)) catch unreachable); +} + +pub const emitMulti = block_prefill_emit.emitMulti; +pub const planRuns = block_prefill_emit.planRuns; + +/// A prefill program's part: a whole segment, or one of CED replay's passes (ced.zig). +pub const Part = enum { whole, encoder, decoder }; + +pub const emitReplay = block_prefill_emit.emitReplay; +pub const emitPrefill = block_prefill_emit.emitPrefill; +pub const tbo_min = block_prefill_emit.tbo_min; +pub const own_scratch = block_prefill_emit.own_scratch; +pub const ownRole = block_prefill_emit.ownRole; +pub const tboRole = block_prefill_emit.tboRole; +pub const Keep = block_prefill_emit.Keep; +pub const ownCalls = block_prefill_emit.ownCalls; +pub const ownCallsKeep = block_prefill_emit.ownCallsKeep; +pub const emitTbo = block_prefill_emit.emitTbo; diff --git a/zig/src/families/deepseek_v41/block_prefill_attn.zig b/zig/src/families/deepseek_v41/block_prefill_attn.zig new file mode 100644 index 0000000000..e13c038762 --- /dev/null +++ b/zig/src/families/deepseek_v41/block_prefill_attn.zig @@ -0,0 +1,346 @@ +//! A prefill segment's attention: rope, the KV stores, the compressor, CSA2's core over the selected blocks. + +const calls = @import("block_calls.zig"); +const prefill = @import("block_prefill.zig"); +const E = prefill.E; +const Arg = calls.Arg; +const bmq = prefill.bmq; +const kt = prefill.kt; +const Out = prefill.Out; +const Seg = prefill.Seg; +const pieceAt = prefill.Seg.pieceAt; +const selection = prefill.Seg.selection; +const rowsOf = prefill.Seg.rowsOf; +const stashArgs = prefill.stashArgs; + +// -- attention ----------------------------------------------------------------------------------------------- + +pub fn pos(e: *E) !Arg { + return e.buf("w.pos", .{}, .i32, &.{1}); +} + +fn rope(e: *E, x: Arg, heads: i64, dim: i64) !void { + return ropeAt(e, x, heads, dim, "w.pos64"); +} + +/// `rope` reading each row's position from `positions` (a multi-segment run: every segment's, "w.mpos64"). +fn ropeAt(e: *E, x: Arg, heads: i64, dim: i64, positions: []const u8) !void { + const n = e.n; + const xv = try e.view(x.t.role, .bf16, &.{ n, heads, dim }, &.{ heads * dim, dim, 1 }, 0); + try e.triton("_rope", .{ n, @divExact(heads, 4), 1 }, &.{ + .{ .name = "X", .arg = xv }, .{ .name = "x_rs", .arg = .{ .i = heads * dim } }, + .{ .name = "x_hs", .arg = .{ .i = dim } }, .{ .name = "OUT", .arg = xv }, + .{ .name = "o_rs", .arg = .{ .i = heads * dim } }, .{ .name = "o_hs", .arg = .{ .i = dim } }, + .{ .name = "CS", .arg = try e.ropeTable() }, .{ .name = "cs_rs", .arg = .{ .i = 64 } }, + .{ .name = "POS", .arg = try e.t(.{ .buf = positions }, .i64, &.{n}) }, .{ .name = "H", .arg = .{ .i = heads } }, + .{ .name = "NOPE", .arg = .{ .i = dim - 64 } }, .{ .name = "NB", .arg = .{ .i = 64 } }, + .{ .name = "HALF", .arg = .{ .i = 32 } }, .{ .name = "BH", .arg = .{ .i = 4 } }, + .{ .name = "INV", .arg = .{ .b = false } }, .{ .name = "COPY", .arg = .{ .b = false } }, + .{ .name = "PDL", .arg = .{ .b = false } }, + }); +} + +/// The SWA ring this segment's layer L stores into (values, scales): the staging ring or the slot's. +pub fn swa(s: *Seg, L: u32) ![2]Arg { + const e = s.e; + if (s.staged) return .{ try e.buf("s.stage.v", .{}, .u8, &.{ s.ring, 576 }), try e.buf("s.stage.s", .{}, .u8, &.{ s.ring, 8 }) }; + return slotRing(e, L); +} + +fn slotRing(e: *E, L: u32) ![2]Arg { + const r = 2 * @as(i64, e.cfg.window); + return .{ try e.buf("s.L{d}.swa.v", .{L}, .u8, &.{ r, 576 }), try e.buf("s.L{d}.swa.s", .{L}, .u8, &.{ r, 8 }) }; +} + +/// A KV source's compressed rows (values, scales): the slot's contiguous ones, or the pool's (block.zig). +fn pool(e: *E, L: u32) ![2]Arg { + return e.pool(L, e.cfg.compressRatio(L)); +} + +pub fn indexKeys(e: *E, L: u32) !Arg { + return e.indexKeys(L); +} + +/// Rows into the SWA ring (ratio 0) or a compressed family (pool mode: through the slot's table or split table). +pub fn kvStore(s: *Seg, lat: Arg, rows: [2]Arg, ratio: i64) !void { + const e = s.e; + const pg = try e.paging(ratio, true); + try e.triton("_kv_store", .{ e.n, 1, 1 }, &.{ + .{ .name = "LAT", .arg = lat }, .{ .name = "l_stride", .arg = .{ .i = e.cfg.head_dim } }, + .{ .name = "CS", .arg = try e.ropeTable() }, .{ .name = "cs_stride", .arg = .{ .i = 64 } }, + .{ .name = "V", .arg = rows[0] }, .{ .name = "S", .arg = rows[1] }, + .{ .name = "v_stride", .arg = .{ .i = pg.vs } }, .{ .name = "s_stride", .arg = .{ .i = pg.ss } }, + .{ .name = "POS", .arg = try pos(e) }, .{ .name = "RATIO", .arg = .{ .i = ratio } }, + .{ .name = "RING", .arg = .{ .i = if (ratio == 0) s.ring else 1 } }, .{ .name = "PT", .arg = pg.pt }, + .{ .name = "PSH", .arg = .{ .i = pg.psh } }, .{ .name = "SL", .arg = .none }, + .{ .name = "PTS", .arg = .{ .i = 0 } }, .{ .name = "ROWS", .arg = .{ .b = false } }, + .{ .name = "PDL", .arg = .{ .b = false } }, + }); +} + +/// A KV source's compressor (blocks._compress): pooled rows normed into its pool, the index keys from them. +pub fn compress(s: *Seg, L: u32, projection: Arg) !void { + const e = s.e; + const n = e.n; + const hd: i64 = e.cfg.head_dim; + const ID: i64 = e.cfg.index_dim; + const ratio: i64 = e.cfg.compressRatio(L); + // ratio 2: [the slot's carry (position start - 1) | the rows] + const buf = if (ratio == 2) try e.buf("L.cbuf", .{}, .f32, &.{ n + 1, 2 * hd }) else projection; + if (ratio == 2) try e.glue("proj_carry", &.{ try e.buf("s.L{d}.carry", .{L}, .f32, &.{ 1, 2 * hd }), projection, buf }); + const lat = try e.buf("L.lat", .{}, .bf16, &.{ n, hd }); + try e.glue("zeros", &.{lat}); + try e.triton("_pool_norm", .{ n, 1, 1 }, &.{ + .{ .name = "BUF", .arg = buf }, .{ .name = "b_stride", .arg = .{ .i = ratio * hd } }, + .{ .name = "W", .arg = try e.weight("L{d}.attn.comp_norm", .{L}, .f32, &.{hd}) }, .{ .name = "LAT", .arg = lat }, + .{ .name = "POS", .arg = try pos(e) }, .{ .name = "n", .arg = .{ .i = n } }, + .{ .name = "EPS", .arg = .{ .f = E.dec(e.cfg.eps) } }, .{ .name = "RATIO", .arg = .{ .i = ratio } }, + .{ .name = "SL", .arg = .none }, .{ .name = "PREV", .arg = .none }, + .{ .name = "OFF", .arg = .{ .i = 0 } }, .{ .name = "ROWS", .arg = .{ .b = false } }, + .{ .name = "PDL", .arg = .{ .b = false } }, .{ .name = "CARRY", .arg = .none }, + .{ .name = "c_stride", .arg = .{ .i = 0 } }, .{ .name = "SPLIT", .arg = .{ .b = false } }, + }); + try s.kvStore(lat, try pool(e, L), ratio); + const kp = try e.buf("L.ix_k", .{}, .bf16, &.{ n, ID }); + try s.proj(try e.fmt("L{d}.attn.ix_wk", .{L}), .strips, lat, hd, ID, kp); + const kpg = try e.paging(ratio, false); + try e.triton("_index_k", .{ n, 1, 1 }, &.{ + .{ .name = "KP", .arg = kp }, .{ .name = "k_stride", .arg = .{ .i = ID } }, + .{ .name = "W", .arg = try e.weight("L{d}.attn.ix_knorm", .{L}, .f32, &.{ID}) }, .{ .name = "CS", .arg = try e.ropeTable() }, + .{ .name = "cs_stride", .arg = .{ .i = 64 } }, .{ .name = "IK", .arg = try indexKeys(e, L) }, + .{ .name = "POS", .arg = try pos(e) }, .{ .name = "EPS", .arg = .{ .f = E.dec(e.cfg.eps) } }, + .{ .name = "RATIO", .arg = .{ .i = ratio } }, .{ .name = "PT", .arg = kpg.pt }, + .{ .name = "PSH", .arg = .{ .i = kpg.psh } }, .{ .name = "KFP8", .arg = .{ .b = true } }, + .{ .name = "SL", .arg = .none }, .{ .name = "PTS", .arg = .{ .i = 0 } }, + .{ .name = "ROWS", .arg = .{ .b = false } }, .{ .name = "PDL", .arg = .{ .b = false } }, + }); +} + +/// Win.sub's first position (int32 [1]) of the row block starting at row `a`. +pub fn blockPos(s: *Seg, j: usize, a: i64) !Arg { + const e = s.e; + const p = try e.buf("L.ix.pos{d}", .{j}, .i32, &.{1}); + try e.glue("block_pos", &.{ p, .{ .i = e.start + a } }); + return p; +} + +/// A kv source's compressor projection of "w.out" as fp32 rows (ratio 2: [kv | gate], kept for the carry). +pub fn compProj(s: *Seg, L: u32) !Arg { + const e = s.e; + const n = e.n; + const D: i64 = e.cfg.hidden; + const hd: i64 = e.cfg.head_dim; + const x = try e.buf("w.out", .{}, .bf16, &.{ n, D }); + const cw = try e.buf("L.comp", .{}, .bf16, &.{ n, hd }); + try s.proj(try e.fmt("L{d}.attn.comp_wkv", .{L}), .lanes, x, D, hd, cw); + if (e.cfg.compressRatio(L) == 2) { + const cg = try e.buf("L.comp_gate", .{}, .bf16, &.{ n, hd }); + try s.proj(try e.fmt("L{d}.attn.comp_wgate", .{L}), .lanes, x, D, hd, cg); + const p = try e.buf("w.L{d}.proj", .{L}, .f32, &.{ n, 2 * hd }); + try e.glue("proj", &.{ cw, cg, p }); + return p; + } + const p = try e.buf("L.proj", .{}, .f32, &.{ n, hd }); + try e.glue("proj", &.{ cw, p }); + return p; +} + +/// CED's encoder end at the decoder's first layer: its compressor alone, then the stash of the entering rows. +pub fn encoderEnd(s: *Seg, L: u32) !void { + const e = s.e; + if (e.cfg.mode(L) != .full) return error.Unsupported; + const p = try s.compProj(L); + const segs = s.multi orelse { + try s.compress(L, p); + return e.glue("ced_stash", &try stashArgs(e)); + }; + // blocks.compress_rows: the projection over every row, then each segment's compressor and stash (replay.encode) + const total = e.n; + for (segs, 0..) |sp, j| { + try s.enter(sp, j); + try s.compress(L, try rowsOf(e, p, .f32, e.row0, e.n, p.t.stride[0])); + try e.glue("ced_stash", &try stashArgs(e)); + } + try s.leave(total); +} + +pub fn attention(s: *Seg, L: u32) !void { + const e = s.e; + const n = e.n; + const D: i64 = e.cfg.hidden; + const md = e.cfg.mode(L); + const H: i64 = e.cfg.heads / e.o.world; + const hd: i64 = e.cfg.head_dim; + const IH: i64 = e.cfg.index_heads; + const ID: i64 = e.cfg.index_dim; + const x = try e.buf("w.out", .{}, .bf16, &.{ n, D }); + const indexer = md == .full or md == .reindex; + // the decoder replay skips the kv source's compressor (blocks.Context.comp_done: the encoder pass stored it) + const own_kv = md == .full and s.part != .decoder; + // the x projections (wq_a, wkv, a KV source's compressor), each its own matmul + const qa = try e.buf("L.qa", .{}, .bf16, &.{ n, e.cfg.q_lora }); + const kva = try e.buf("L.kva", .{}, .bf16, &.{ n, hd }); + try s.proj(try e.fmt("L{d}.attn.wq_a", .{L}), .lanes, x, D, e.cfg.q_lora, qa); + try s.proj(try e.fmt("L{d}.attn.wkv", .{L}), .lanes, x, D, hd, kva); + const cproj: Arg = if (own_kv) try s.compProj(L) else undefined; + const qn = try e.buf("L.qn", .{}, .bf16, &.{ n, e.cfg.q_lora }); + const kv = try e.buf("L.kv", .{}, .bf16, &.{ n, hd }); + try e.triton("_rms2", .{ n, 2, 1 }, &.{ + .{ .name = "XA", .arg = qa }, .{ .name = "xa_rs", .arg = .{ .i = e.cfg.q_lora } }, + .{ .name = "WA", .arg = try e.weight("L{d}.attn.q_norm", .{L}, .f32, &.{e.cfg.q_lora}) }, .{ .name = "OA", .arg = qn }, + .{ .name = "oa_rs", .arg = .{ .i = e.cfg.q_lora } }, .{ .name = "KA", .arg = .{ .i = e.cfg.q_lora } }, + .{ .name = "inv_ka", .arg = .{ .f = 1.0 / @as(f64, @floatFromInt(e.cfg.q_lora)) } }, .{ .name = "XB", .arg = kva }, + .{ .name = "xb_rs", .arg = .{ .i = hd } }, .{ .name = "WB", .arg = try e.weight("L{d}.attn.kv_norm", .{L}, .f32, &.{hd}) }, + .{ .name = "OB", .arg = kv }, .{ .name = "ob_rs", .arg = .{ .i = hd } }, + .{ .name = "KB", .arg = .{ .i = hd } }, .{ .name = "inv_kb", .arg = .{ .f = 1.0 / @as(f64, @floatFromInt(hd)) } }, + .{ .name = "eps", .arg = .{ .f = E.dec(e.cfg.eps) } }, .{ .name = "BK", .arg = .{ .i = 512 } }, + .{ .name = "NARROW", .arg = .{ .b = true } }, .{ .name = "PDL", .arg = .{ .b = false } }, + }); + const q = try e.buf("L.q", .{}, .bf16, &.{ n, H * hd }); + try s.proj(try e.fmt("L{d}.attn.wq_b", .{L}), .lanes, qn, e.cfg.q_lora, H * hd, q); + const qi = try e.buf("L.qi", .{}, .bf16, &.{ n, IH * ID }); + if (indexer) try s.proj(try e.fmt("L{d}.attn.ix_wq_b", .{L}), .lanes, qn, e.cfg.q_lora, IH * ID, qi); + if (s.short) try e.prefetchO(L); + const positions = if (s.multi != null) "w.mpos64" else "w.pos64"; + try ropeAt(e, q, H, hd, positions); + if (indexer) { + try ropeAt(e, qi, IH, ID, positions); + // the head weights: `_plain`'s fp64 sums, cast to bf16 then fp32 by torch + const w64 = try e.buf("L.ix_w64", .{}, .f64, &.{ n, IH }); + try e.triton("_plain", .{ n, IH, 1 }, &.{ + .{ .name = "X", .arg = x }, .{ .name = "x_rs", .arg = .{ .i = D } }, + .{ .name = "W", .arg = try e.weight("L{d}.attn.ix_wp", .{L}, .f32, &.{ IH, D }) }, .{ .name = "w_rs", .arg = .{ .i = D } }, + .{ .name = "OUT", .arg = w64 }, .{ .name = "K", .arg = .{ .i = D } }, + .{ .name = "N", .arg = .{ .i = IH } }, .{ .name = "BK", .arg = .{ .i = 256 } }, + .{ .name = "PDL", .arg = .{ .b = false } }, .{ .name = "BF16", .arg = .{ .b = false } }, + }); + try e.glue("ix_w", &.{ w64, try e.buf("L.ix_w", .{}, .f32, &.{ n, IH }) }); + } + // the CSA2 steps a segment at a time, into the group-major core output + const groups: i64 = e.cfg.o_groups / e.o.world; + if (@mod(H, bmq) != 0 or @mod(H, groups) != 0) return error.Unsupported; + const hg = @divExact(H, groups); + const o = try e.buf("L.o", .{}, .bf16, &.{ groups, n, hg * hd }); + if (s.multi) |segs| { + for (segs, 0..) |sp, j| { + try s.enter(sp, j); + try s.core(L, q, kv, cproj, own_kv, indexer, o, n); + } + try s.leave(n); + } else try s.core(L, q, kv, cproj, own_kv, indexer, o, n); + // _out_pf: each wo_a group's GEMM over its contiguous slice into its columns of z, then wo_b (bf16 partial) + const ol: i64 = e.cfg.o_lora; + const gk = hg * hd; + const z: Out = .{ .role = .{ .buf = "L.z" }, .dt = .bf16, .stride = groups * ol }; + const k = s.pieces(); + if (k == 1) { + var g: i64 = 0; + while (g < groups) : (g += 1) { + var zg = z; + zg.offset = g * ol * 2; + try s.matmul(try e.fmt("L{d}.attn.wo_a.{d}", .{ L, g }), .lanes, try e.view(o.t.role, .bf16, &.{ n, gk }, &.{ gk, 1 }, g * n * gk * 2), gk, ol, zg); + } + return s.proj(try e.fmt("L{d}.attn.wo_b", .{L}), .lanes, try e.buf("L.z", .{}, .bf16, &.{ n, groups * ol }), groups * ol, D, try e.buf("L.part", .{}, .bf16, &.{ n, D })); + } + // TF_DSV41_PF_OVERLAP: wo_a / wo_b a row piece at a time (row-independent), each partial sent behind the next + const part = try e.buf("L.part", .{}, .bf16, &.{ n, D }); + const zall = try e.buf("L.z", .{}, .bf16, &.{ n, groups * ol }); + const gath = try s.recv("attn"); + defer e.n = n; + var p: i64 = 0; + while (p < k) : (p += 1) { + const r = pieceAt(n, k, p); + const m = r[1] - r[0]; + e.n = m; + var g: i64 = 0; + while (g < groups) : (g += 1) { + var zg = z; + zg.offset = g * ol * 2 + r[0] * groups * ol * 2; + try s.matmul(try e.fmt("L{d}.attn.wo_a.{d}", .{ L, g }), .lanes, try e.view(o.t.role, .bf16, &.{ m, gk }, &.{ gk, 1 }, (g * n + r[0]) * gk * 2), gk, ol, zg); + } + try s.matmul(try e.fmt("L{d}.attn.wo_b", .{L}), .lanes, try rowsOf(e, zall, .bf16, r[0], m, groups * ol), groups * ol, D, .{ .role = part.t.role, .dt = .bf16, .stride = D, .offset = r[0] * D * 2 }); + e.n = n; + try s.sendPiece(part, gath, r[0], m, p, k); + } + s.sent_attn = true; +} + +/// The CSA2 steps of the current segment into rows [row0, row0 + n) of the run's group-major output `o`. +pub fn core(s: *Seg, L: u32, q: Arg, kv: Arg, cproj: Arg, own_kv: bool, indexer: bool, o: Arg, total: i64) !void { + const e = s.e; + const n = e.n; + const ratio: i64 = e.cfg.compressRatio(L); + const H: i64 = e.cfg.heads / e.o.world; + const hd: i64 = e.cfg.head_dim; + const groups: i64 = e.cfg.o_groups / e.o.world; + const hg = @divExact(H, groups); + // blocks._segment: a segment the slot's ring cannot hold runs on the staging ring, the window before it in + const rows = try s.swa(L); + const slot = try slotRing(e, L); + const W: i64 = e.cfg.window; + const ring0 = 2 * W; + if (s.staged) try e.glue("stage_in", &.{ rows[0], rows[1], .{ .i = s.ring }, slot[0], slot[1], .{ .i = ring0 }, .{ .i = @max(0, e.start - W + 1) }, .{ .i = e.start } }); + try s.kvStore(try rowsOf(e, kv, .bf16, e.row0, n, hd), rows, 0); + if (own_kv) try s.compress(L, try rowsOf(e, cproj, .f32, e.row0, n, cproj.t.stride[0])); + if (indexer) try s.select(L); + // the fused prefill core over the SWA ring and (a compressed layer) the source's pool at the latest selection + const lo = try e.buf("w.lo", .{}, .i32, &.{n}); + var cv = rows[0]; + var cs = rows[1]; + var tok = lo; + var cnt = lo; + var t_stride: i64 = 0; + var fpg: E.Paging = .{ .pt = .none, .psh = 0, .vs = 0, .ss = 0 }; + if (ratio > 0) { + const src = e.cfg.kvSource(L).?; + const sel = try selection(e, e.cfg.indexSource(L).?); + cnt = sel[1]; + t_stride = e.cfg.index_topk; + if (e.o.pool != null and e.o.pool.?.split) { + // split: an index layer's selection brings its rows over the exchange; its reuse layers read them too + const got = if (indexer) try e.exchange(src, e.cfg.compressRatio(src), sel[0]) else try e.received(src, e.cfg.compressRatio(src)); + cv = got.cv; + cs = got.cs; + tok = got.tok; + } else { + const p = try pool(e, src); + cv = p[0]; + cs = p[1]; + tok = sel[0]; + fpg = try e.paging(e.cfg.compressRatio(src), false); + } + } + try e.triton("_fused", .{ n, @divExact(H, bmq), 1 }, &.{ + .{ .name = "Q", .arg = try e.view(q.t.role, .bf16, &.{ n, H, hd }, &.{ H * hd, hd, 1 }, e.row0 * H * hd * 2) }, + .{ .name = "CV", .arg = cv }, + .{ .name = "CSC", .arg = cs }, + .{ .name = "cvs", .arg = .{ .i = cv.t.stride[0] } }, + .{ .name = "css", .arg = .{ .i = cs.t.stride[0] } }, + .{ .name = "TOK", .arg = tok }, + .{ .name = "t_stride", .arg = .{ .i = t_stride } }, + .{ .name = "CNT", .arg = cnt }, + .{ .name = "SV", .arg = rows[0] }, + .{ .name = "SSC", .arg = rows[1] }, + .{ .name = "svs", .arg = .{ .i = 576 } }, + .{ .name = "sss", .arg = .{ .i = 8 } }, + .{ .name = "LO", .arg = lo }, + .{ .name = "POS", .arg = try pos(e) }, + .{ .name = "SINK", .arg = try e.weight("L{d}.attn.sink", .{L}, .f32, &.{H}) }, + .{ .name = "CS", .arg = try e.ropeTable() }, + .{ .name = "cs_stride", .arg = .{ .i = 64 } }, + .{ .name = "OUT", .arg = try e.view(o.t.role, .bf16, &.{ groups, n, hg * hd }, &.{ total * hg * hd, hg * hd, 1 }, e.row0 * hg * hd * 2) }, + .{ .name = "H", .arg = .{ .i = H } }, + .{ .name = "KT", .arg = .{ .i = kt } }, + .{ .name = "BMQ", .arg = .{ .i = bmq } }, + // D ** -0.5 (libm pow, correctly rounded): sqrt of the exact 1 / 512 rounds the same way + .{ .name = "SCALE", .arg = .{ .f = @sqrt(1.0 / @as(f64, @floatFromInt(hd))) } }, + .{ .name = "RING", .arg = .{ .i = s.ring } }, + .{ .name = "WINDOW", .arg = .{ .i = W } }, + .{ .name = "COMP", .arg = .{ .b = ratio > 0 } }, + .{ .name = "PT", .arg = fpg.pt }, + .{ .name = "PSH", .arg = .{ .i = fpg.psh } }, + .{ .name = "HG", .arg = .{ .i = hg } }, + .{ .name = "OR", .arg = .{ .i = total } }, + }); + if (s.staged) try e.glue("stage_out", &.{ slot[0], slot[1], .{ .i = ring0 }, rows[0], rows[1], .{ .i = s.ring }, .{ .i = @max(0, e.end() - W) }, .{ .i = e.end() } }); +} diff --git a/zig/src/families/deepseek_v41/block_prefill_emit.zig b/zig/src/families/deepseek_v41/block_prefill_emit.zig new file mode 100644 index 0000000000..b8d29d2299 --- /dev/null +++ b/zig/src/families/deepseek_v41/block_prefill_emit.zig @@ -0,0 +1,234 @@ +//! Prefill programs as calls: single and multi-segment runs, CED replay passes, TBO micro-batches. + +const std = @import("std"); +const calls = @import("block_calls.zig"); +const block = @import("block.zig"); +const config = @import("config.zig"); +const ced = @import("ced.zig"); +const prefill = @import("block_prefill.zig"); +const Config = config.Config; +const E = prefill.E; +const Arg = calls.Arg; +const split_rows = prefill.split_rows; +const router_chunk = prefill.router_chunk; +const Seg = prefill.Seg; +const Span = prefill.Span; +const Part = prefill.Part; + +/// The most rows a prefill program takes: one router CHUNK, 4,096 under TF_DSV41_PF_4K. +fn rowsCap(o: block.Options) i64 { + return if (o.pf4k) 2 * router_chunk else router_chunk; +} + +/// Several slots' prompt segments or CED decoder replays in one run; each segment's CSA2 steps on its own slot. +pub fn emitMulti(a: std.mem.Allocator, cfg: *const Config, w: *const block.Widths, o: block.Options, layers: []const u32, spans_in: []const Span, part: Part) ![]calls.Call { + if (spans_in.len == 0 or o.image_rows != 0) return error.Unsupported; + if (o.pool) |pl| if (pl.split) return error.Unsupported; // split KV's exchanges: one segment a run + const spans = try a.dupe(Span, spans_in); + var total: i64 = 0; + // decoder replay rows past the split mHC programs' 32; a prompt piece's past the short segment's 16 + const floor: i64 = if (part == .decoder) split_rows else 16; + var cnt: i64 = 0; + for (spans) |*sp| { + if (sp.n <= floor) return error.Unsupported; + sp.row0 = total; + sp.cnt0 = cnt; + total += sp.n; + cnt = std.mem.alignForward(i64, cnt + sp.n, 4); + } + if (total > o.prefill_rows or total > rowsCap(o)) return error.Unsupported; + var e: E = .{ .a = a, .cfg = cfg, .w = w, .o = o, .n = total, .start = spans[0].start }; + var s: Seg = .{ .e = &e, .staged = false, .ring = 0, .part = part, .multi = spans }; + s.setRing(); + try s.run(layers, false); + return e.out.items; +} + +/// The multi-segment runs' programs at their largest, for the buffer plan; a run its options refuse adds nothing. +pub fn planRuns(a: std.mem.Allocator, cfg: *const Config, w: *const block.Widths, o: block.Options, enc: []const u32, dec: ?[]const u32, rows: i64, part: Part) !std.ArrayList([]const calls.Call) { + var out: std.ArrayList([]const calls.Call) = .empty; + if (rows > 2 * 17) { + const sp = [_]Span{ .{ .slot = 0, .start = 0, .n = rows - 17 }, .{ .slot = 1, .start = 0, .n = 17 } }; + if (emitMulti(a, cfg, w, o, enc, &sp, part)) |cs| try out.append(a, cs) else |e| if (e != error.Unsupported) return e; + } + // the counts' widest layout: 15 segments of 17 rows (each padded 3 to the next multiple of 4) and the rest + if (rows > 16 * 17) { + var sp: [16]Span = undefined; + for (sp[0..15], 0..) |*x, i| x.* = .{ .slot = @intCast(i), .start = 0, .n = 17 }; + sp[15] = .{ .slot = 15, .start = 0, .n = rows - 15 * 17 }; + if (emitMulti(a, cfg, w, o, enc, &sp, part)) |cs| try out.append(a, cs) else |e| if (e != error.Unsupported) return e; + } + if (dec) |d| { + var tails: [16]Span = undefined; + const k: usize = @intCast(@min(16, @divFloor(rows, ced.keep))); + if (k >= 2) { + for (tails[0..k], 0..) |*t, i| t.* = .{ .slot = @intCast(i), .start = 0, .n = ced.keep }; + if (emitMulti(a, cfg, w, o, d, tails[0..k], .decoder)) |cs| try out.append(a, cs) else |e| if (e != error.Unsupported) return e; + } + } + return out; +} + +/// CED replay's programs (TF_DSV41_PREFILL=replay), `n` rows from `start`: `.encoder` or `.decoder` (see ced.zig). +pub fn emitReplay(a: std.mem.Allocator, cfg: *const Config, w: *const block.Widths, o: block.Options, layers: []const u32, n: i64, start: i64, part: Part) ![]calls.Call { + if (layers.len == 0) return error.Unsupported; + return emitPart(a, cfg, w, o, layers, n, start, part == .whole, part); +} + +/// The calls of a prefill segment of `n` rows from `start` over `layers`, plus final norm + head when `head`. +pub fn emitPrefill(a: std.mem.Allocator, cfg: *const Config, w: *const block.Widths, o: block.Options, layers: []const u32, n: i64, start: i64, head: bool) ![]calls.Call { + return emitPart(a, cfg, w, o, layers, n, start, head, .whole); +} + +fn emitPart(a: std.mem.Allocator, cfg: *const Config, w: *const block.Widths, o: block.Options, layers: []const u32, n: i64, start: i64, head: bool, part: Part) ![]calls.Call { + return emitMarked(a, cfg, w, o, layers, n, start, head, part, null); +} + +pub const tbo_min: i64 = 512; // TF_DSV41_PF_TBO: micro-batches of at least this many rows (smaller run alone) + +/// Persistent roles used as scratch: a program in its own namespace (`ownRole`) takes its own copy of each. +pub const own_scratch = [_][]const u8{ "s.ex.", "s.pf.xh", "s.pf.w", "s.router.", "s.stage.", "s.attn." }; + +/// A role in namespace `tag`: "w.x" -> "w.x", "L.x" -> "L.x", scratch "s.x" -> "s.x"; state keeps its. +pub fn ownRole(a: std.mem.Allocator, r: []const u8, comptime tag: []const u8) ![]const u8 { + if (std.mem.startsWith(u8, r, "s.")) { + // a role already in a namespace ("s.b~ex.z") is scratch by its bare name + var bare = r[2..]; + while (std.mem.indexOf(u8, bare, "~")) |i| { + if (std.mem.indexOfScalar(u8, bare[0..i], '.') != null) break; + bare = bare[i + 1 ..]; + } + for (own_scratch) |p| if (std.mem.startsWith(u8, bare, p[2..])) return std.fmt.allocPrint(a, "s." ++ tag ++ "{s}", .{r[2..]}); + return r; + } + const dot = std.mem.indexOfScalar(u8, r, '.') orelse return std.fmt.allocPrint(a, tag ++ "{s}", .{r}); + return std.fmt.allocPrint(a, "{s}" ++ tag ++ "{s}", .{ r[0 .. dot + 1], r[dot + 1 ..] }); +} + +/// Micro-batch B's name of a role (TF_DSV41_PF_TBO). +pub fn tboRole(a: std.mem.Allocator, r: []const u8) ![]const u8 { + return ownRole(a, r, "b~"); +} + +/// The roles a namespace leaves as they are (ownCallsKeep): the forward's, which hold them already. +pub const Keep = std.StringHashMapUnmanaged(void); + +fn ownArg(a: std.mem.Allocator, x: Arg, comptime tag: []const u8, keep: ?*const Keep) !Arg { + return switch (x) { + .t, .opaque_table => |t| blk: { + if (t.role != .buf) break :blk x; + if (keep) |k| if (k.contains(t.role.buf)) break :blk x; + var u = t; + u.role = .{ .buf = try ownRole(a, t.role.buf, tag) }; + break :blk if (x == .t) .{ .t = u } else .{ .opaque_table = u }; + }, + .list => |l| blk: { + const out = try a.alloc(Arg, l.len); + for (l, out) |y, *z| z.* = try ownArg(a, y, tag, keep); + break :blk .{ .list = out }; + }, + else => x, + }; +} + +fn tboArg(a: std.mem.Allocator, x: Arg) !Arg { + return ownArg(a, x, "b~", null); +} + +/// `cs` with every role in namespace `tag` (ownRole), the calls otherwise as they are. +pub fn ownCalls(a: std.mem.Allocator, cs: []const calls.Call, comptime tag: []const u8) ![]calls.Call { + return ownCallsKeep(a, cs, tag, null); +} + +/// `ownCalls` but for the roles in `keep`, which stay the forward's. +pub fn ownCallsKeep(a: std.mem.Allocator, cs: []const calls.Call, comptime tag: []const u8, keep: ?*const Keep) ![]calls.Call { + const out = try a.alloc(calls.Call, cs.len); + for (cs, out) |c, *x| { + x.* = c; + const args = try a.alloc(calls.Named, c.args.len); + for (c.args, args) |y, *z| z.* = .{ .name = y.name, .arg = try ownArg(a, y.arg, tag, keep) }; + x.args = args; + } + return out; +} + +/// TF_DSV41_PF_TBO: two consecutive CED encoder segments as one program on two streams, B half a layer behind A. +pub fn emitTbo(a: std.mem.Allocator, cfg: *const Config, w: *const block.Widths, o: block.Options, layers: []const u32, na: i64, nb: i64, start: i64) ![]calls.Call { + if (!o.pf_tbo or !o.branches or na < tbo_min or nb < tbo_min or o.image_rows != 0) return error.Unsupported; + // B's first row starts a ratio-2 pair: its compressed rows begin after A's last + if (@mod(start, 2) != 0 or @mod(na, 2) != 0) return error.Unsupported; + if (o.pool) |pl| if (pl.split) return error.Unsupported; + var ma: std.ArrayList(usize) = .empty; + var mb: std.ArrayList(usize) = .empty; + const pa = try emitMarked(a, cfg, w, o, layers, na, start, false, .encoder, &ma); + const pb = try emitMarked(a, cfg, w, o, layers, nb, start + na, false, .encoder, &mb); + if (ma.items.len != mb.items.len) return error.Unsupported; + // the phases: alternating attention / MoE parts, the last the encoder's end (D's site + compressor) + const nph = ma.items.len + 1; + var out: std.ArrayList(calls.Call) = .empty; + var started = false; // B issued any call since the last join (its next part forks) + var b_engram = false; // B issued an Engram step since the last join (A's next Engram step joins it first) + for (0..nph) |k| { + const a0 = if (k == 0) 0 else ma.items[k - 1]; + const a1 = if (k < ma.items.len) ma.items[k] else pa.len; + const b0 = if (k == 0) 0 else mb.items[k - 1]; + const b1 = if (k < mb.items.len) mb.items[k] else pb.len; + const attn = k % 2 == 0; + const L = layers[k / 2]; + // A's part on the main stream + const first_a = out.items.len; + for (pa[a0..a1]) |c| { + if (std.mem.eql(u8, c.name, "glue.ced_stash")) continue; + try out.append(a, c); + } + if (b_engram and attn and out.items.len > first_a) { + for (pa[a0..a1]) |c| if (std.mem.eql(u8, c.name, "glue.engram_rows")) { + out.items[first_a].join = true; + started = false; + b_engram = false; + break; + }; + } + if (attn and cfg.isKvSource(L) and cfg.compressRatio(L) == 2) { + // the slot's ratio-2 carry: A's last projection row, as A's commit (forward_prefill.commitAt) would copy it + const row: i64 = 2 * @as(i64, cfg.head_dim); + const src: Arg = .{ .t = .{ .role = .{ .buf = try std.fmt.allocPrint(a, "w.L{d}.proj", .{L}) }, .dt = .f32, .shape = try a.dupe(i64, &.{ 1, row }), .stride = try a.dupe(i64, &.{ row, 1 }), .offset = (na - 1) * row * 4 } }; + const dst: Arg = .{ .t = .{ .role = .{ .buf = try std.fmt.allocPrint(a, "s.L{d}.carry", .{L}) }, .dt = .f32, .shape = try a.dupe(i64, &.{ 1, row }), .stride = try a.dupe(i64, &.{ row, 1 }) } }; + const args = try a.alloc(calls.Named, 2); + args[0] = .{ .arg = src }; + args[1] = .{ .arg = dst }; + try out.append(a, .{ .triton = false, .glue = true, .name = "glue.copy_rows", .args = args }); + } + // B's part on the side stream, its attention parts forked behind A's + for (pb[b0..b1], 0..) |c, j| { + var x = c; + const args = try a.alloc(calls.Named, c.args.len); + for (c.args, args) |y, *z| z.* = .{ .name = y.name, .arg = try tboArg(a, y.arg) }; + x.args = args; + x.side = true; + x.fork = j == 0 and (attn or !started); + x.join = false; + x.begin = .none; + try out.append(a, x); + started = true; + if (std.mem.eql(u8, c.name, "glue.engram_rows")) b_engram = true; + } + } + try out.append(a, .{ .triton = false, .glue = true, .name = "glue.tbo_join", .args = &.{}, .join = true }); + return out.items; +} + +fn emitMarked(a: std.mem.Allocator, cfg: *const Config, w: *const block.Widths, o: block.Options, layers: []const u32, n: i64, start: i64, head: bool, part: Part, marks: ?*std.ArrayList(usize)) ![]calls.Call { + if (n < 1 or n > o.prefill_rows or n > rowsCap(o)) return error.Unsupported; + var e: E = .{ .a = a, .cfg = cfg, .w = w, .o = o, .n = n, .start = start, .marks = marks }; + const W: i64 = cfg.window; + // forward.staging: the slot's ring holds 2 x window rows; a longer segment goes through the staging ring + const staged = n + W - 1 > 2 * W; + const stage: i64 = @intCast(std.math.ceilPowerOfTwo(u64, @intCast(o.prefill_rows + W - 1)) catch return error.Unsupported); + const ring = if (staged) stage else 2 * W; + if (ring < n + W - 1) return error.Unsupported; // attn_pf: the ring must hold the rows and their windows + var s: Seg = .{ .e = &e, .staged = staged, .ring = ring, .short = n <= 16, .part = part }; + try s.run(layers, head); + if (o.holdsDeferred(n)) calls.holdDeferred(e.out.items); + return e.out.items; +} diff --git a/zig/src/families/deepseek_v41/block_prefill_mhc.zig b/zig/src/families/deepseek_v41/block_prefill_mhc.zig new file mode 100644 index 0000000000..4f0cfe427b --- /dev/null +++ b/zig/src/families/deepseek_v41/block_prefill_mhc.zig @@ -0,0 +1,208 @@ +//! A prefill segment's mHC sites: the coefficient sets, site / boundary pieces, `_finish_k`, the head. + +const std = @import("std"); +const calls = @import("block_calls.zig"); +const moe = @import("block_moe.zig"); +const prefill = @import("block_prefill.zig"); +const E = prefill.E; +const Arg = calls.Arg; +const pf_rows = prefill.pf_rows; +const mhc_bm = prefill.mhc_bm; +const split_rows = prefill.split_rows; +const cdiv = prefill.cdiv; +const Seg = prefill.Seg; +const pieceAt = prefill.Seg.pieceAt; +const rowsOf = prefill.Seg.rowsOf; + +// -- mHC ----------------------------------------------------------------------------------------------------- + +pub fn coefs(e: *E, set: u1) ![3]Arg { + return .{ + try e.buf("w.c{d}.pre", .{set}, .f32, &.{ e.n, 4 }), + try e.buf("w.c{d}.post", .{set}, .f32, &.{ e.n, 4 }), + try e.buf("w.c{d}.comb", .{set}, .f32, &.{ e.n, 16 }), + }; +} + +/// DSpark's tap (forward._layers): the new streams' mean at a boundary entering a target layer. +fn tapOf(e: *E, L: u32, which: []const u8, mode: moe.Mode) !?Arg { + if (!e.o.taps or mode != .boundary or !std.mem.eql(u8, which, "hc_attn")) return null; + const t = e.cfg.dspark_targets.items(); + const j = std.mem.indexOfScalar(u32, t, L) orelse return null; + const D: i64 = e.cfg.hidden; + return try e.view(.{ .buf = "w.taps" }, .bf16, &.{ e.n, D }, &.{ @as(i64, @intCast(t.len)) * D, 1 }, @as(i64, @intCast(j)) * D * 2); +} + +/// The exchange a post reads: the attention's partials at the FFN site, the MoE's anywhere else (layer-scoped). +fn gathered(e: *E, which: []const u8) !Arg { + const D: i64 = e.cfg.hidden; + if (std.mem.eql(u8, which, "hc_ffn")) return e.buf("L.recv.attn", .{}, .bf16, &.{ e.o.world, e.n, D }); + return e.buf("L.recv.moe", .{}, .bf16, &.{ e.o.world, e.n, D }); +} + +/// One mixing site of layer L (`which`: hc_attn / hc_ffn): site or boundary, then `_finish_k` (short: mhc_cuda). +pub fn site(s: *Seg, L: u32, which: []const u8, mode: moe.Mode) !void { + const e = s.e; + const pend = s.pend; + s.pend = 0; + // the decoder replay's boundaries write the streams in place (replay.finish's Streams has no `alt`) + if (s.short) return moe.mhcInto(e, L, which, mode, s.part == .decoder); + const n = e.n; + if (pend > 1 and mode == .boundary and n >= pend * pf_rows and n > split_rows) return s.sitePieces(L, which, pend, true); + // TF_DSV41_MHC_SITE_ROWS: the boundary in row pieces, each piece's inputs still in L2 for its `_finish_k` + if (mode == .boundary and e.o.mhc_site_rows > 0 and n > e.o.mhc_site_rows) { + const k = cdiv(n, e.o.mhc_site_rows); + if (n >= k * pf_rows) return s.sitePieces(L, which, k, false); + } + const D: i64 = e.cfg.hidden; + const post = mode == .boundary; + const x = try e.streams(); + const prev = try coefs(e, e.coef); + const next = try coefs(e, 1 - e.coef); + const b0 = try e.empty(.bf16); + const f0 = try e.empty(.f32); + const fnw = try e.buf("s.L{d}.{s}.fn16", .{ L, which }, .bf16, &.{ 6 * @as(i64, e.cfg.hc_mult), 4 * D }); + const part = try e.buf("L.mhc.part", .{}, .f32, &.{ n, 4, 40, 32 }); + const c = try e.buf("L.mhc.c", .{}, .bf16, &.{ n, D }); + const tap = try tapOf(e, L, which, mode); + // the forward's second streams buffer (mhc.out_of_place): a boundary of at most 32 rows writes it + const spare = post and n <= split_rows and s.part != .decoder; + const xout = if (spare) try e.spare() else x; + if (n >= pf_rows) { + try e.ext("tf_dsv41_mhc_pf_v1.run", &.{ + x, if (post) xout else b0, + if (post) try gathered(e, which) else b0, if (post) prev[1] else f0, + if (post) prev[2] else f0, if (mode != .site1) prev[0] else f0, + fnw, part, + c, tap orelse b0, + .{ .i = n }, .{ .i = @backingInt(mode) }, + }); + } else try s.siteTriton(x, if (post) xout else null, if (post) try gathered(e, which) else null, prev, if (mode != .site1) prev[0] else null, fnw, part, c, tap, if (mode == .site1) 1 else 2, true); + try s.finish(.{ L, which }, next, try e.weight("L{d}.{s}", .{ L, if (std.mem.eql(u8, which, "hc_attn")) "attn_norm" else "ffn_norm" }, .f32, &.{D}), part, c, 0); + e.coef = 1 - e.coef; + if (spare) e.cur = 1 - e.cur; +} + +/// A boundary site over the last exchange's k row pieces; row-wise, so the same bits (`overlapped`: with marks). +pub fn sitePieces(s: *Seg, L: u32, which: []const u8, k: i64, overlapped: bool) !void { + const e = s.e; + const n = e.n; + const D: i64 = e.cfg.hidden; + const W: i64 = e.o.world; + const x = try e.streams(); + const prev = try coefs(e, e.coef); + const next = try coefs(e, 1 - e.coef); + const b0 = try e.empty(.bf16); + const fnw = try e.buf("s.L{d}.{s}.fn16", .{ L, which }, .bf16, &.{ 6 * @as(i64, e.cfg.hc_mult), 4 * D }); + const part = try e.buf("L.mhc.part", .{}, .f32, &.{ n, 4, 40, 32 }); + const c = try e.buf("L.mhc.c", .{}, .bf16, &.{ n, D }); + const g = try gathered(e, which); + const tap = try tapOf(e, L, which, .boundary); + const nw = try e.weight("L{d}.{s}", .{ L, if (std.mem.eql(u8, which, "hc_attn")) "attn_norm" else "ffn_norm" }, .f32, &.{D}); + if (overlapped) e.br_join = false; // piece p waits for its own exchange piece; the last joins + defer e.n = n; + var p: i64 = 0; + while (p < k) : (p += 1) { + const r = pieceAt(n, k, p); + const m = r[1] - r[0]; + e.n = m; + if (overlapped) { + if (p + 1 < k) e.br_wait = @intCast(p + 1) else e.br_join = true; + } + const xr = try rowsOf(e, x, .bf16, r[0], m, 4 * D); + const nx: [3]Arg = .{ try rowsOf(e, next[0], .f32, r[0], m, 4), try rowsOf(e, next[1], .f32, r[0], m, 4), try rowsOf(e, next[2], .f32, r[0], m, 16) }; + const pr = try e.view(part.t.role, .f32, &.{ m, 4, 40, 32 }, &.{ 4 * 40 * 32, 40 * 32, 32, 1 }, r[0] * 4 * 40 * 32 * 4); + const cr = try rowsOf(e, c, .bf16, r[0], m, D); + try e.ext("tf_dsv41_mhc_pf_v1.run", &.{ + xr, xr, + try e.view(g.t.role, .bf16, &.{ W, m, D }, &.{ n * D, D, 1 }, r[0] * D * 2), try rowsOf(e, prev[1], .f32, r[0], m, 4), + try rowsOf(e, prev[2], .f32, r[0], m, 16), try rowsOf(e, prev[0], .f32, r[0], m, 4), + fnw, pr, + cr, if (tap) |t| try e.view(t.t.role, .bf16, &.{ m, D }, t.t.stride, t.t.offset + r[0] * t.t.stride[0] * 2) else b0, + .{ .i = m }, .{ .i = @backingInt(moe.Mode.boundary) }, + }); + try s.finish(.{ L, which }, nx, nw, pr, cr, r[0]); + } + e.coef = 1 - e.coef; +} + +/// mhc._launch's Triton `_site`: `xout` / `g` with the post, `pre` with collapse 2, `fnw` with the mix. +pub fn siteTriton(s: *Seg, x: Arg, xout: ?Arg, g: ?Arg, prev: [3]Arg, pre: ?Arg, fnw: ?Arg, part: Arg, c: Arg, tap: ?Arg, collapse: i64, mix: bool) !void { + const e = s.e; + const n = e.n; + const D: i64 = e.cfg.hidden; + const post = g != null; + const bm: i64 = if (n >= pf_rows) mhc_bm else 16; + // a program a stream for small mixing windows, never in place with a post + const split = mix and n <= split_rows and !(post and std.mem.eql(u8, xout.?.t.role.buf, x.t.role.buf)); + try e.triton("_site", .{ cdiv(n, bm), 40, if (split) 4 else 1 }, &.{ + .{ .name = "X", .arg = x }, .{ .name = "x_stride", .arg = .{ .i = 4 * D } }, + .{ .name = "XOUT", .arg = xout orelse x }, .{ .name = "G", .arg = g orelse x }, + .{ .name = "g_rank", .arg = .{ .i = if (post) n * D else 0 } }, .{ .name = "POST", .arg = if (post) prev[1] else x }, + .{ .name = "COMB", .arg = if (post) prev[2] else x }, .{ .name = "PRE", .arg = pre orelse x }, + .{ .name = "FN", .arg = fnw orelse x }, .{ .name = "PART", .arg = part }, + .{ .name = "C", .arg = c }, .{ .name = "TAP", .arg = tap orelse x }, + .{ .name = "tap_stride", .arg = .{ .i = if (tap) |t| t.t.stride[0] else 0 } }, .{ .name = "R", .arg = .{ .i = n } }, + .{ .name = "D", .arg = .{ .i = D } }, .{ .name = "NB", .arg = .{ .i = 40 } }, + .{ .name = "BM", .arg = .{ .i = bm } }, .{ .name = "BK", .arg = .{ .i = 16 } }, + .{ .name = "WORLD", .arg = .{ .i = if (post) e.o.world else 1 } }, .{ .name = "POST_ON", .arg = .{ .b = post } }, + .{ .name = "COLLAPSE", .arg = .{ .i = collapse } }, .{ .name = "MIX", .arg = .{ .b = mix } }, + .{ .name = "TAP_ON", .arg = .{ .b = tap != null } }, .{ .name = "SPLIT", .arg = .{ .b = split } }, + }); +} + +/// The partials' sums, the next coefficients and the normed input into "w.out" rows from `r0`. +pub fn finish(s: *Seg, hc: ?struct { u32, []const u8 }, next: [3]Arg, nw: Arg, part: Arg, c: Arg, r0: i64) !void { + const e = s.e; + const D: i64 = e.cfg.hidden; + const coef = hc != null; + const base = if (hc) |h| try e.weight("L{d}.{s}.base", .{ h[0], h[1] }, .f32, &.{6 * @as(i64, e.cfg.hc_mult)}) else part; + const scale = if (hc) |h| try e.weight("L{d}.{s}.scale", .{ h[0], h[1] }, .f32, &.{3}) else part; + try e.triton("_finish_k", .{ e.n, 1, 1 }, &.{ + .{ .name = "PART", .arg = part }, .{ .name = "BASE", .arg = base }, + .{ .name = "SCALE", .arg = scale }, .{ .name = "PRE", .arg = if (coef) next[0] else part }, + .{ .name = "POST", .arg = if (coef) next[1] else part }, .{ .name = "COMB", .arg = if (coef) next[2] else part }, + .{ .name = "C", .arg = c }, .{ .name = "NW", .arg = nw }, + .{ .name = "OUT", .arg = try rowsOf(e, try e.buf("w.out", .{}, .bf16, &.{ e.n, D }), .bf16, r0, e.n, D) }, .{ .name = "eps", .arg = .{ .f = E.dec(e.cfg.eps) } }, + .{ .name = "hc_eps", .arg = .{ .f = E.dec(e.cfg.hc_eps) } }, .{ .name = "post_alpha", .arg = .{ .f = E.dec(e.cfg.hc_post_alpha) } }, + .{ .name = "D", .arg = .{ .i = D } }, .{ .name = "NB", .arg = .{ .i = 40 } }, + .{ .name = "ITERS", .arg = .{ .i = e.cfg.hc_sinkhorn_iters } }, .{ .name = "COEF", .arg = .{ .b = coef } }, + .{ .name = "BLOCK", .arg = .{ .i = @intCast(std.math.gcd(@as(u64, 1024), @as(u64, @intCast(D)))) } }, .{ .name = "UNROLL", .arg = .{ .b = true } }, + .{ .name = "NORM_FIRST", .arg = .{ .b = true } }, + }); +} + +/// mhc.post_only: the last exchange's post into `xout` (the streams in place, or the trace's copy). +pub fn postOnly(s: *Seg, xout: Arg) !void { + const e = s.e; + s.pend = 0; // reads the whole gathered buffer: it joined (sendPiece) + if (s.short) return moe.postOnly(e, xout); + const prev = try coefs(e, e.coef); + const x = try e.streams(); + try s.siteTriton(x, xout, try gathered(e, ""), prev, null, null, x, x, null, 0, false); +} + +/// mhc.final: the last post, collapse 2, the final norm; then the head's columns over every row. +pub fn final(s: *Seg) !void { + const e = s.e; + s.pend = 0; + if (s.short) return moe.finish(e); // mhc_cuda's final, then the same head + const n = e.n; + const D: i64 = e.cfg.hidden; + const x = try e.streams(); + const prev = try coefs(e, e.coef); + const part = try e.buf("L.mhc.part", .{}, .f32, &.{ n, 4, 40, 32 }); + const c = try e.buf("L.mhc.c", .{}, .bf16, &.{ n, D }); + try s.siteTriton(x, x, try gathered(e, ""), prev, prev[0], null, part, c, null, 2, false); + try s.finish(null, prev, try e.weight("norm", .{}, .f32, &.{D}), part, c, 0); + // the head: the decode path's linear (prefill_mm.fast off), upstream's 128 rows a launch + const V: i64 = e.cfg.vocab / e.o.world; + var a: i64 = 0; + while (a < n) : (a += 128) { + const m = @min(n - a, 128); + e.n = m; + defer e.n = n; + try e.upstream("head", try e.view(.{ .buf = "w.out" }, .bf16, &.{ m, D }, &.{ D, 1 }, a * D * 2), D, V, try e.view(.{ .buf = "w.logits" }, .f32, &.{ m, V }, &.{ V, 1 }, a * V * 4)); + } + try e.glue("kit_logits", &.{try e.buf("w.logits", .{}, .f32, &.{ n, V })}); +} diff --git a/zig/src/families/deepseek_v41/block_prefill_moe.zig b/zig/src/families/deepseek_v41/block_prefill_moe.zig new file mode 100644 index 0000000000..ca57efb0af --- /dev/null +++ b/zig/src/families/deepseek_v41/block_prefill_moe.zig @@ -0,0 +1,231 @@ +//! A prefill segment's MoE: router, x3gm's ragged launches, the shared expert's GEMMs, and Engram. + +const std = @import("std"); +const calls = @import("block_calls.zig"); +const gm2pf = @import("gm2pf.zig"); +const prefill = @import("block_prefill.zig"); +const E = prefill.E; +const Arg = calls.Arg; +const router_chunk = prefill.router_chunk; +const gm_bm = prefill.gm_bm; +const gm_widths = prefill.gm_widths; +const Seg = prefill.Seg; +const pieceAt = prefill.Seg.pieceAt; +const rowsOf = prefill.Seg.rowsOf; + +// -- MoE: router, x3gm's ragged launches, the shared expert's GEMMs ------------------------------------------- + +pub fn moeBlock(s: *Seg, L: u32) !void { + const e = s.e; + if (e.o.image_rows != 0) return s.moeSplit(L); + const D: i64 = e.cfg.hidden; + return s.moeRows(L, e.n, try e.buf("w.out", .{}, .bf16, &.{ e.n, D }), "bias", try e.buf("L.moe16", .{}, .bf16, &.{ e.n, D }), false); +} + +/// TF_DSV41_BIAS_VL: image rows through the MoE with gate.bias_vl, text rows with gate.bias, partials merged back. +pub fn moeSplit(s: *Seg, L: u32) !void { + const e = s.e; + const D: i64 = e.cfg.hidden; + const n = e.n; + const ni: i64 = if (e.o.image_rows < 0) n else e.o.image_rows; + const nt: i64 = if (e.o.image_rows < 0) n else n - ni; + const x = try e.buf("w.out", .{}, .bf16, &.{ n, D }); + const xi = try e.buf("w.out.img", .{}, .bf16, &.{ ni, D }); + const xt: Arg = if (nt > 0) try e.buf("w.out.txt", .{}, .bf16, &.{ nt, D }) else try e.empty(.bf16); + try e.glue("image_split", &.{ x, xi, xt }); + const pi = try e.buf("L.moe16.img", .{}, .bf16, &.{ ni, D }); + try s.moeRows(L, ni, xi, "bias_vl", pi, false); + var pt = try e.empty(.bf16); + if (nt > 0) { + pt = try e.buf("L.moe16.txt", .{}, .bf16, &.{ nt, D }); + try s.moeRows(L, nt, xt, "bias", pt, true); + } + try e.glue("image_merge", &.{ pi, pt, try e.buf("L.moe16", .{}, .bf16, &.{ n, D }) }); +} + +/// The MoE over `n` rows at `x` into `part`; `again`: the layer's second call (its own MoE ticket). +pub fn moeRows(s: *Seg, L: u32, n: i64, x: Arg, comptime bias: []const u8, part: Arg, again: bool) !void { + const e = s.e; + // the call's own rows are the emitter's rows (the projections' M, their scratch): a plain n-row segment's MoE + const seg_rows = e.n; + e.n = n; + defer e.n = seg_rows; + const D: i64 = e.cfg.hidden; + const ex = e.cfg.expertsOf(L); + const topk: i64 = ex.topk; + const slots: i64 = topk + 1; + const Et: i64 = ex.count; + const I: i64 = e.cfg.expert_width / e.o.world; + const P = n * topk; + const pick = try e.buf("L.pick", .{}, .i32, &.{ n, slots }); + const wts = try e.buf("L.wts", .{}, .f32, &.{ n, slots }); + // the router a launch a CHUNK of rows; calls of <= 16 rows the narrow kernel (TF_DSV41_RG_NARROW 2) + const logits = try e.buf("L.logits", .{}, .f32, &.{ n, Et }); + var c0: i64 = 0; + while (c0 < n) : (c0 += router_chunk) { + const m = @min(router_chunk, n - c0); + const whole = m == n; + const tile: i64 = for ([_]i64{ 1, 2, 4, 8, 16 }) |t| { + if (t >= @min(m, 16)) break t; + } else 16; + const cfg: [2]i64 = if (n <= 16) .{ 2, 0 } else if (m > 16) .{ 8, 2 } else .{ 8, 1 }; + try e.ext("tf_dsv41_router_gemv_v2.route", &.{ + if (whole) x else try rowsOf(e, x, .bf16, c0, m, D), try e.weight("L{d}.moe.gate", .{L}, .bf16, &.{ Et, D }), + try e.weight("L{d}.moe." ++ bias, .{L}, .f32, &.{Et}), if (whole) pick else try rowsOf(e, pick, .i32, c0, m, slots), + if (whole) wts else try rowsOf(e, wts, .f32, c0, m, slots), if (whole) logits else try rowsOf(e, logits, .f32, c0, m, Et), + try e.buf("s.router.cnt", .{}, .i32, &.{128}), .{ .i = topk }, + .{ .i = slots }, .{ .f = E.dec(e.cfg.routed_scale) }, + .{ .i = tile }, .{ .i = cfg[0] }, + .{ .i = cfg[1] }, .{ .b = true }, + .{ .b = false }, try e.empty(.i32), + try e.empty(.i32), try e.t(.empty, .i32, &.{ 0, 1 }), + .{ .i = 0 }, .{ .b = false }, + }); + if (e.o.r1) { + // the R1 twin's route always takes the prune list; prefill's is empty (the prune runs after it) + const last = &e.out.items[e.out.items.len - 1]; + const more = try e.a.alloc(calls.Named, last.args.len + 1); + @memcpy(more[0..last.args.len], last.args); + more[last.args.len] = .{ .arg = .{ .list = &.{} } }; + last.args = more; + } else if (e.o.r1_sig) try e.appendLast(&.{.{ .list = &.{} }}); + } + // the kit's fp16 weights, the shared slot masked, the routed slots' contiguous copies + const rs: i64 = if (n == 1) slots else topk; + const pk = try e.view(.{ .buf = "L.gm.pick" }, .i32, &.{ n, topk }, &.{ rs, 1 }, 0); + const w6 = try e.view(.{ .buf = "L.gm.wts" }, .f32, &.{ n, topk }, &.{ rs, 1 }, 0); + try e.glue("gm_picks", &.{ pick, wts, pk, w6 }); + // one ragged row block: rot, a gate/up launch a gate width present, a down launch a down width, combine + const widths = e.w.gm.get(L); + const rows: i64 = e.o.expert_rows; + const zb = rows * (topk + 1) * @max(4 * 2 * I, D); // the scratch z's floats (block_moe) + // past the 1,024-row scratch rows halve; under TF_DSV41_PF_4K the block is the whole segment + if (!e.o.pf4k and (P * D > zb or P * I > rows * (topk + 1) * D)) return error.Unsupported; + const pre = try e.fmt("L{d}.moe", .{L}); + const xg = try e.view(.{ .buf = "s.ex.z" }, .f16, &.{ P, D }, &.{ D, 1 }, 0); + const xu = try e.view(.{ .buf = "s.ex.z" }, .f16, &.{ P, D }, &.{ D, 1 }, P * D * 2); + const xd = try e.view(.{ .buf = "s.ex.xg" }, .f16, &.{ P, I }, &.{ I, 1 }, 0); + const y = try e.view(.{ .buf = "s.ex.z" }, .f32, &.{ P, D }, &.{ D, 1 }, 0); + try e.ext("tf_dsv41_x3gm_v1.rot", &.{ + x, .{ .i = D }, pk, try e.weight("{s}.w1.suh", .{pre}, .f16, &.{ Et, D }), try e.weight("{s}.w3.suh", .{pre}, .f16, &.{ Et, D }), + xg, xu, .{ .i = D }, .{ .i = topk }, .{ .i = P }, + .{ .i = 2 }, + }); + const ticket = try e.buf("L.gm.ticket", .{}, .i32, &.{2 * gm_widths}); + try e.glue(if (again) "gm_ticket_again" else "gm_ticket", &.{ticket}); + const tmax = @divFloor(P, gm_bm) + Et + 1; + const lim: f64 = E.dec(e.cfg.swiglu_limit); + // TF_DSV41_GM_V2 (gm2pf.zig): x3gm._run_v2's gm2_kernel launches in place of the loop below + if (e.o.gm_v2 != .off) try gm2pf.emit(e, e.o.gm_v2, .{ .L = L, .pre = pre, .P = P, .Et = Et, .D = D, .I = I, .pk = pk, .xg = xg, .xu = xu, .xd = xd, .y = y, .ticket = ticket, .limit = lim, .widths = widths }) else for (0..2) |down| { + const wd = (widths orelse return error.NoWidth)[down]; + var j: i64 = 0; + for (0..32) |kk| { + const k2: i64 = @intCast(kk); + if (wd >> @intCast(kk) & 1 == 0) continue; + const tag = if (down == 1) "d" else "g"; + const plan = [5]Arg{ + try e.buf("L.gm.{s}{d}.order", .{ tag, j }, .i32, &.{P}), + try e.buf("L.gm.{s}{d}.pe", .{ tag, j }, .i32, &.{tmax}), + try e.buf("L.gm.{s}{d}.poff", .{ tag, j }, .i32, &.{tmax}), + try e.buf("L.gm.{s}{d}.pcnt", .{ tag, j }, .i32, &.{tmax}), + try e.buf("L.gm.{s}{d}.npass", .{ tag, j }, .i32, &.{1}), + }; + try e.glue("gm_plan", &.{ pk, plan[0], plan[1], plan[2], plan[3], plan[4], .{ .i = k2 }, .{ .b = down == 1 }, .{ .i = gm_bm }, .{ .i = Et } }); + const t = try e.view(ticket.t.role, .i32, &.{1}, &.{1}, (j + if (down == 1) @as(i64, gm_widths) else 0) * 4); + if (down == 0) { + try e.ext("tf_dsv41_x3gm_v1.gateup", &.{ + xg, xu, + try e.buf("s.L{d}.gm.tg", .{L}, .i64, &.{Et}), try e.buf("s.L{d}.gm.tu", .{L}, .i64, &.{Et}), + plan[0], plan[1], + plan[2], plan[3], + plan[4], t, + try e.weight("{s}.w1.svh", .{pre}, .f16, &.{ Et, I }), try e.weight("{s}.w3.svh", .{pre}, .f16, &.{ Et, I }), + try e.weight("{s}.w2.suh", .{pre}, .f16, &.{ Et, I }), xd, + .{ .i = D }, .{ .i = I }, + .{ .i = k2 }, .{ .b = false }, + .{ .i = 1 }, .{ .b = true }, + .{ .f = lim }, .{ .b = true }, + }); + } else { + try e.ext("tf_dsv41_x3gm_v1.down", &.{ + xd, try e.buf("s.L{d}.gm.td", .{L}, .i64, &.{Et}), plan[0], plan[1], + plan[2], plan[3], plan[4], t, + try e.weight("{s}.w2.svh", .{pre}, .f16, &.{ Et, D }), y, .{ .i = I }, .{ .i = D }, + .{ .i = k2 }, .{ .i = 0 }, .{ .b = true }, .{ .b = true }, + }); + } + j += 1; + } + } + const routed = try e.buf("L.routed", .{}, .f32, &.{ n, D }); + try e.ext("tensorfold_exl3_experts_v1.combine", &.{ y, w6, routed, .{ .i = n }, .{ .i = D }, .{ .i = topk } }); + // the shared expert (gate / up GEMMs, clamped SiLU, down); TF_DSV41_PF_OVERLAP: a row piece at a time + const k: i64 = if (e.cfg.shared_experts > 0 and n == seg_rows and part.t.role == .buf and std.mem.eql(u8, part.t.role.buf, "L.moe16")) s.pieces() else 1; + if (k > 1) { + const sh = try e.fmt("{s}.shared.0", .{pre}); + const g = try e.buf("L.sh.g", .{}, .f32, &.{ n, I }); + const u = try e.buf("L.sh.u", .{}, .f32, &.{ n, I }); + const act = try e.buf("L.sh.act", .{}, .f32, &.{ n, I }); + const sy = try e.buf("L.sh.y", .{}, .f32, &.{ n, D }); + const gath = try s.recv("moe"); + var p: i64 = 0; + while (p < k) : (p += 1) { + const r = pieceAt(n, k, p); + const m = r[1] - r[0]; + e.n = m; + const xr = try rowsOf(e, x, .bf16, r[0], m, D); + const gr = try rowsOf(e, g, .f32, r[0], m, I); + const ur = try rowsOf(e, u, .f32, r[0], m, I); + const ar = try rowsOf(e, act, .f32, r[0], m, I); + const yr = try rowsOf(e, sy, .f32, r[0], m, D); + try s.proj(try e.fmt("{s}.w1", .{sh}), .stored, xr, D, I, gr); + try s.proj(try e.fmt("{s}.w3", .{sh}), .stored, xr, D, I, ur); + try e.glue("swiglu", &.{ gr, ur, ar, .{ .f = lim } }); + try s.proj(try e.fmt("{s}.w2", .{sh}), .stored, ar, I, D, yr); + try e.glue("moe_sum", &.{ try rowsOf(e, routed, .f32, r[0], m, D), yr, try rowsOf(e, part, .bf16, r[0], m, D) }); + e.n = n; + try s.sendPiece(part, gath, r[0], m, p, k); + } + s.sent_moe = true; + return; + } + if (e.cfg.shared_experts > 0) { + const sh = try e.fmt("{s}.shared.0", .{pre}); + const g = try e.buf("L.sh.g", .{}, .f32, &.{ n, I }); + const u = try e.buf("L.sh.u", .{}, .f32, &.{ n, I }); + const act = try e.buf("L.sh.act", .{}, .f32, &.{ n, I }); + const sy = try e.buf("L.sh.y", .{}, .f32, &.{ n, D }); + try s.proj(try e.fmt("{s}.w1", .{sh}), .stored, x, D, I, g); + try s.proj(try e.fmt("{s}.w3", .{sh}), .stored, x, D, I, u); + try e.glue("swiglu", &.{ g, u, act, .{ .f = lim } }); + try s.proj(try e.fmt("{s}.w2", .{sh}), .stored, act, I, D, sy); + try e.glue("moe_sum", &.{ routed, sy, part }); + } else try e.glue("moe_sum", &.{ routed, part }); +} + +/// Engram before the layer's first site (blocks.engram): wkv over the hashed rows (a prefill matmul), the fusion. +pub fn engram(s: *Seg, L: u32) !void { + const e = s.e; + const n = e.n; + const D: i64 = e.cfg.hidden; + const K: i64 = @as(i64, e.cfg.hashCols()) * e.cfg.engram_head_dim; + const N: i64 = (@as(i64, e.cfg.engram_max_ngram) + 1) * D; + const kv = try e.buf("L.engram.kv", .{}, .bf16, &.{ n, N }); + const rws = try e.buf("L.engram.rows", .{}, .bf16, &.{ n, K }); + try e.glue("engram_rows", &.{ rws, .{ .i = L } }); + try s.proj(try e.fmt("L{d}.engram.wkv", .{L}), .strips, rws, K, N, kv); + const qk = try e.weight("L{d}.engram.qk", .{L}, .f32, &.{ e.cfg.hc_mult, D }); + // image positions (TF_DSV41_IMAGES=native): the gate times vision.window's keep (0 there) + const keep: ?Arg = if (e.o.image_keep) try e.buf("w.engram.keep", .{}, .f32, &.{n}) else null; + if (keep) |k| try e.glue("image_keep", &.{k}); + try e.triton("_fuse_dec", .{ n, 1, 1 }, &.{ + .{ .name = "X", .arg = try e.streams() }, .{ .name = "x_stride", .arg = .{ .i = 4 * D } }, + .{ .name = "KV", .arg = kv }, .{ .name = "kv_stride", .arg = .{ .i = N } }, + .{ .name = "QK", .arg = qk }, .{ .name = "KEEP", .arg = keep orelse qk }, + .{ .name = "GATE", .arg = qk }, .{ .name = "eps", .arg = .{ .f = E.dec(e.cfg.eps) } }, + .{ .name = "clamp", .arg = .{ .f = E.dec(e.cfg.engram_gate_clamp) } }, .{ .name = "sqrt_d", .arg = .{ .f = @sqrt(@as(f64, @floatFromInt(D))) } }, + .{ .name = "D", .arg = .{ .i = D } }, .{ .name = "BK", .arg = .{ .i = 64 } }, + .{ .name = "UB", .arg = .{ .i = 1024 } }, .{ .name = "HAS_KEEP", .arg = .{ .b = keep != null } }, + .{ .name = "HAS_GATE", .arg = .{ .b = false } }, + }); +} diff --git a/zig/src/families/deepseek_v41/block_prefill_select.zig b/zig/src/families/deepseek_v41/block_prefill_select.zig new file mode 100644 index 0000000000..9feccccca0 --- /dev/null +++ b/zig/src/families/deepseek_v41/block_prefill_select.zig @@ -0,0 +1,264 @@ +//! A prefill segment's index selection: indexer scores, candidates, top-k (whole or streamed). + +const calls = @import("block_calls.zig"); +const longpf = @import("longpf.zig"); +const prefill = @import("block_prefill.zig"); +const E = prefill.E; +const Arg = calls.Arg; +const Dt = calls.Dt; +const stream_min = prefill.stream_min; +const dtopk_rows = prefill.dtopk_rows; +const size = prefill.size; +const cdiv = prefill.cdiv; +const Seg = prefill.Seg; +const pos = prefill.Seg.pos; +const indexKeys = prefill.Seg.indexKeys; + +/// The segment's selection of index layer `src` (reuse layers read the latest source's). +pub fn selection(e: *E, src: u32) ![2]Arg { + const k: i64 = e.cfg.index_topk; + return .{ + try e.view(.{ .buf = try e.fmt("w.sel{d}", .{src}) }, .i32, &.{ e.n, k }, &.{ k, 1 }, e.row0 * k * 4), + // the counts' 4-byte rows: a run's segment from a row a multiple of 4 (cnt0), so the slice is 16-aligned + try e.view(.{ .buf = try e.fmt("w.cnt{d}", .{src}) }, .i32, &.{e.n}, &.{1}, e.cnt0 * 4), + }; +} + +/// The segment's rows of the run's per-row candidates (layer 20's, read by the reindex layers). +fn cands(e: *E) !Arg { + const cb: i64 = e.cfg.candidate_blocks; + return e.view(.{ .buf = "w.cand" }, .i32, &.{ e.n, cb }, &.{ cb, 1 }, e.row0 * cb * 4); +} + +/// Rows [a, a + rows) of a row-major [n, cols] role. +pub fn rowsOf(e: *E, x: Arg, dt: Dt, a: i64, rows: i64, cols: i64) !Arg { + return e.view(x.t.role, dt, &.{ rows, cols }, &.{ cols, 1 }, x.t.offset + a * cols * size(dt)); +} + +/// Index scores over rows [a, a + rows): dense, or over `keys`' positions (`cbs` > 0: candidate blocks). +pub fn scores(s: *Seg, keys_of: u32, ratio: i64, a: i64, rows: i64, p: Arg, nk: i64, keys: ?Arg, cbs: i64) !Arg { + const e = s.e; + const IH: i64 = e.cfg.index_heads; + const ID: i64 = e.cfg.index_dim; + const out = try e.buf("L.ix.scores", .{}, .f32, &.{ rows, nk }); + const spg = try e.paging(ratio, false); + try e.triton("_scores", .{ rows, cdiv(nk, 64), 1 }, &.{ + .{ .name = "QI", .arg = try e.view(.{ .buf = "L.qi" }, .bf16, &.{ rows, IH, ID }, &.{ IH * ID, ID, 1 }, (e.row0 + a) * IH * ID * 2) }, + .{ .name = "W", .arg = try e.view(.{ .buf = "L.ix_w" }, .f32, &.{ rows, IH }, &.{ IH, 1 }, (e.row0 + a) * IH * 4) }, + .{ .name = "w_stride", .arg = .{ .i = IH } }, + .{ .name = "IK", .arg = try indexKeys(e, keys_of) }, + .{ .name = "OUT", .arg = out }, + .{ .name = "POS", .arg = p }, + .{ .name = "KEYS", .arg = keys orelse out }, + .{ .name = "k_stride", .arg = .{ .i = if (cbs > 0) keys.?.t.shape[1] else nk } }, + .{ .name = "NK", .arg = .{ .i = nk } }, + .{ .name = "o_stride", .arg = .{ .i = nk } }, + .{ .name = "RATIO", .arg = .{ .i = ratio } }, + .{ .name = "H", .arg = .{ .i = IH } }, + .{ .name = "D", .arg = .{ .i = ID } }, + .{ .name = "BP", .arg = .{ .i = 64 } }, + .{ .name = "WS", .arg = .{ .f = 1.0 / @sqrt(@as(f64, @floatFromInt(IH))) } }, + .{ .name = "SCALE", .arg = .{ .f = 1.0 / @sqrt(@as(f64, @floatFromInt(ID))) } }, + .{ .name = "GATHER", .arg = .{ .b = keys != null } }, + .{ .name = "PT", .arg = spg.pt }, + .{ .name = "PSH", .arg = .{ .i = spg.psh } }, + .{ .name = "KFP8", .arg = .{ .b = true } }, + .{ .name = "SL", .arg = .none }, + .{ .name = "PTS", .arg = .{ .i = 0 } }, + .{ .name = "ROWS", .arg = .{ .b = false } }, + .{ .name = "CBS", .arg = .{ .i = cbs } }, + }); + return out; +} + +/// index.sort_keys: a unique int64 key a score (`posn`: the gathered keys' positions). +pub fn sortKeys(s: *Seg, sc: Arg, rows: i64, nk: i64, posn: ?Arg) !Arg { + const e = s.e; + const k = try e.buf("L.ix.keys", .{}, .i64, &.{ rows, nk }); + try e.triton("_keys", .{ rows, cdiv(nk, 1024), 1 }, &.{ + .{ .name = "S", .arg = sc }, .{ .name = "s_stride", .arg = .{ .i = nk } }, + .{ .name = "K", .arg = k }, .{ .name = "k_stride", .arg = .{ .i = nk } }, + .{ .name = "POSN", .arg = posn orelse sc }, .{ .name = "p_stride", .arg = .{ .i = nk } }, + .{ .name = "NK", .arg = .{ .i = nk } }, .{ .name = "BLOCK", .arg = .{ .i = 1024 } }, + .{ .name = "GATHER", .arg = .{ .b = posn != null } }, + }); + return k; +} + +/// The candidate blocks' keys from the scores, then the top-k (glue) into rows [a, ..) of the candidates. +pub fn candidates(s: *Seg, sc: Arg, a: i64, rows: i64, nk: i64, p: Arg, ratio: i64) !void { + const e = s.e; + const cbs: i64 = e.cfg.candidate_block_size; + const cb: i64 = e.cfg.candidate_blocks; + const nb = cdiv(nk, cbs); + const k = try e.buf("L.ix.bkeys", .{}, .i64, &.{ rows, nb }); + try e.triton("_block_keys", .{ rows, cdiv(nb, 256), 1 }, &.{ + .{ .name = "S", .arg = sc }, .{ .name = "s_stride", .arg = .{ .i = nk } }, + .{ .name = "K", .arg = k }, .{ .name = "k_stride", .arg = .{ .i = nb } }, + .{ .name = "POS", .arg = p }, .{ .name = "NB", .arg = .{ .i = nb } }, + .{ .name = "RATIO", .arg = .{ .i = ratio } }, .{ .name = "BS", .arg = .{ .i = cbs } }, + .{ .name = "TB", .arg = .{ .i = 256 } }, .{ .name = "ROWS", .arg = .{ .b = false } }, + }); + try e.glue("topk", &.{ k, try rowsOf(e, try cands(e), .i32, a, rows, cb), .{ .i = cb } }); +} + +/// Row blocks of the materialised indexer (backend._row_blocks): `per_row` bytes a row under the index budget. +fn step(e: *E, per_row: i64) i64 { + return if (e.n > 16) @max(16, @divFloor(e.o.index_budget, @max(1, per_row))) else e.n; +} + +/// An index layer's selection (blocks._select over backend.select / select_cand / reindex). +pub fn select(s: *Seg, L: u32) !void { + const e = s.e; + const n = e.n; + const md = e.cfg.mode(L); + const src = e.cfg.kvSource(L).?; + const ratio: i64 = e.cfg.compressRatio(L); + const topk: i64 = e.cfg.index_topk; + const cbs: i64 = e.cfg.candidate_block_size; + const cb: i64 = e.cfg.candidate_blocks; + const sel = try selection(e, L); + // <= 64 rows with scores within the index budget: attn_cuda's top-k; past the stream threshold: stream top-k + const reindexing = md == .reindex and e.cfg.usesCandidates(L) and s.cand; + const nvis = @max(@divFloor(e.end(), ratio), 1); + const streamed = !e.cfg.isCandidateSource(L) and !reindexing and n > 16 and nvis >= stream_min and @popCount(topk) == 1; + const fits = if (reindexing) n * cb * cbs * 16 <= e.o.index_budget else n * nvis * 12 <= e.o.index_budget; + if (n <= dtopk_rows and fits and !streamed) { + // the whole segment's scores, then attn_cuda's top-k (over a source's candidate blocks) and visible counts + const cand = try cands(e); + if (reindexing) { + const sc = try s.scores(src, ratio, 0, n, try pos(e), cb * cbs, cand, cbs); + return e.topk(L, sc, cand, cb * cbs, ratio); + } + const sc = try s.scores(src, ratio, 0, n, try pos(e), nvis, null, 0); + try e.topk(L, sc, cand, nvis, ratio); + if (e.cfg.isCandidateSource(L)) s.cand = true; + return; + } + if (md == .reindex and e.cfg.usesCandidates(L) and s.cand) { + // backend.reindex: the row's own scores over its candidates' positions, in row blocks under the budget + const NK = cb * cbs; + const whole = n * NK * 16 <= e.o.index_budget; + const st = if (whole) n else step(e, NK * 16); + var a: i64 = 0; + var j: usize = 0; + while (a < n) : ({ + a += st; + j += 1; + }) { + const rows = @min(st, n - a); + const p = if (whole) try pos(e) else try s.blockPos(j, a); + const ck = try e.buf("L.ix.ckeys", .{}, .i32, &.{ rows, NK }); + try e.glue("cand_keys", &.{ try rowsOf(e, try cands(e), .i32, a, rows, cb), ck, .{ .i = cbs } }); + const sc = try s.scores(src, ratio, a, rows, p, NK, ck, 0); + try e.glue("topk", &.{ try s.sortKeys(sc, rows, NK, ck), try rowsOf(e, sel[0], .i32, a, rows, topk), .{ .i = topk } }); + } + } else { + const cand = e.cfg.isCandidateSource(L); + // backend.select's stream path (full-mode layers without the candidates' scores): stream_topk.select + if (streamed) { + try s.streamSelect(src, ratio, topk, sel[0]); + try e.glue("counts", &.{ sel[0], sel[1], try e.buf("w.pos64", .{}, .i64, &.{n}), .{ .i = ratio } }); + return; + } + const whole = n * nvis * 12 <= e.o.index_budget; + const st = if (whole) n else step(e, nvis * 12); + var a: i64 = 0; + var j: usize = 0; + while (a < n) : ({ + a += st; + j += 1; + }) { + const rows = @min(st, n - a); + const p = if (whole) try pos(e) else try s.blockPos(j, a); + const nk = if (whole) nvis else @max(@divFloor(e.start + a + rows, ratio), 1); + const sc = try s.scores(src, ratio, a, rows, p, nk, null, 0); + try e.glue("topk", &.{ try s.sortKeys(sc, rows, nk, null), try rowsOf(e, sel[0], .i32, a, rows, topk), .{ .i = topk } }); + if (cand) try s.candidates(sc, a, rows, nk, p, ratio); + } + if (cand) s.cand = true; + } + try e.glue("counts", &.{ sel[0], sel[1], try e.buf("w.pos64", .{}, .i64, &.{n}), .{ .i = ratio } }); +} + +/// Stream top-k in row blocks within its scratch budget: each block's `_stream`, then the merge glue. +pub fn streamSelect(s: *Seg, src: u32, ratio: i64, topk: i64, out: Arg) !void { + const e = s.e; + const n = e.n; + const IH: i64 = e.cfg.index_heads; + const ID: i64 = e.cfg.index_dim; + const st = longpf.plan(n, @max(@divFloor(e.end(), ratio), 1), topk); + const spg = try e.paging(ratio, false); + var a: i64 = 0; + var j: usize = 0; + while (a < n) : ({ + a += st; + j += 1; + }) { + const rows = @min(st, n - a); + const p = if (st >= n) try pos(e) else try s.blockPos(j, a); + const nk = @max(@divFloor(e.start + a + rows, ratio), 1); + const ns = longpf.splits(nk); + const buf = try e.buf("L.ix.stream", .{}, .i64, &.{ rows, ns, 2 * topk }); + if (e.o.stream_rb == 1 or e.o.stream_rb == 2 or e.o.stream_rb == 4) { + // TF_DSV41_STREAM_RB: `_stream_pf` (1) or `_stream_rb`, N rows a program (same buffers and merge) + const rb = e.o.stream_rb; + try e.triton(switch (rb) { + 1 => "_stream_pf", + 2 => "_stream_rb2", + else => "_stream_rb4", + }, .{ cdiv(rows, rb), ns, 1 }, &.{ + .{ .name = "QI", .arg = try e.view(.{ .buf = "L.qi" }, .bf16, &.{ rows, IH, ID }, &.{ IH * ID, ID, 1 }, (e.row0 + a) * IH * ID * 2) }, + .{ .name = "W", .arg = try e.view(.{ .buf = "L.ix_w" }, .f32, &.{ rows, IH }, &.{ IH, 1 }, (e.row0 + a) * IH * 4) }, + .{ .name = "w_stride", .arg = .{ .i = IH } }, + .{ .name = "IK", .arg = try indexKeys(e, src) }, + .{ .name = "POS", .arg = p }, + .{ .name = "R", .arg = .{ .i = rows } }, + .{ .name = "BUF", .arg = buf }, + .{ .name = "nsplit", .arg = .{ .i = ns } }, + .{ .name = "RATIO", .arg = .{ .i = ratio } }, + .{ .name = "H", .arg = .{ .i = IH } }, + .{ .name = "D", .arg = .{ .i = ID } }, + .{ .name = "BP", .arg = .{ .i = 64 } }, + .{ .name = "WS", .arg = .{ .f = 1.0 / @sqrt(@as(f64, @floatFromInt(IH))) } }, + .{ .name = "SCALE", .arg = .{ .f = 1.0 / @sqrt(@as(f64, @floatFromInt(ID))) } }, + .{ .name = "SPLIT", .arg = .{ .i = longpf.split_keys } }, + .{ .name = "K", .arg = .{ .i = topk } }, + .{ .name = "CAP", .arg = .{ .i = 2 * topk } }, + .{ .name = "PT", .arg = spg.pt }, + .{ .name = "PSH", .arg = .{ .i = spg.psh } }, + .{ .name = "KFP8", .arg = .{ .b = true } }, + }); + try e.glue("stream_merge", &.{ buf, try rowsOf(e, out, .i32, a, rows, topk), .{ .i = topk }, try e.buf("L.ix.keys", .{}, .i64, &.{ rows, ns * topk }) }); + continue; + } + try e.triton("_stream", .{ rows, ns, 1 }, &.{ + .{ .name = "QI", .arg = try e.view(.{ .buf = "L.qi" }, .bf16, &.{ rows, IH, ID }, &.{ IH * ID, ID, 1 }, (e.row0 + a) * IH * ID * 2) }, + .{ .name = "W", .arg = try e.view(.{ .buf = "L.ix_w" }, .f32, &.{ rows, IH }, &.{ IH, 1 }, (e.row0 + a) * IH * 4) }, + .{ .name = "w_stride", .arg = .{ .i = IH } }, + .{ .name = "IK", .arg = try indexKeys(e, src) }, + .{ .name = "POS", .arg = p }, + .{ .name = "KEYS", .arg = buf }, + .{ .name = "k_stride", .arg = .{ .i = 0 } }, + .{ .name = "NK", .arg = .{ .i = nk } }, + .{ .name = "BUF", .arg = buf }, + .{ .name = "nsplit", .arg = .{ .i = ns } }, + .{ .name = "RATIO", .arg = .{ .i = ratio } }, + .{ .name = "H", .arg = .{ .i = IH } }, + .{ .name = "D", .arg = .{ .i = ID } }, + .{ .name = "BP", .arg = .{ .i = 64 } }, + .{ .name = "WS", .arg = .{ .f = 1.0 / @sqrt(@as(f64, @floatFromInt(IH))) } }, + .{ .name = "SCALE", .arg = .{ .f = 1.0 / @sqrt(@as(f64, @floatFromInt(ID))) } }, + .{ .name = "MODE", .arg = .{ .i = 0 } }, + .{ .name = "SPLIT", .arg = .{ .i = longpf.split_keys } }, + .{ .name = "K", .arg = .{ .i = topk } }, + .{ .name = "CAP", .arg = .{ .i = 2 * topk } }, + .{ .name = "BS", .arg = .{ .i = 8 } }, // index.BLOCK + .{ .name = "PT", .arg = spg.pt }, + .{ .name = "PSH", .arg = .{ .i = spg.psh } }, + .{ .name = "KFP8", .arg = .{ .b = true } }, + }); + // the merge's gathered keys [rows, ns x topk] share the materialised path's sort keys' storage + try e.glue("stream_merge", &.{ buf, try rowsOf(e, out, .i32, a, rows, topk), .{ .i = topk }, try e.buf("L.ix.keys", .{}, .i64, &.{ rows, ns * topk }) }); + } +} diff --git a/zig/src/families/deepseek_v41/block_wide.zig b/zig/src/families/deepseek_v41/block_wide.zig new file mode 100644 index 0000000000..db83dcdd8f --- /dev/null +++ b/zig/src/families/deepseek_v41/block_wide.zig @@ -0,0 +1,167 @@ +//! Decode windows of more than 16 rows (TF_DSV41_ROWS_CAP past 16): mHC sites and router config past mhc_cuda's 16. + +const std = @import("std"); +const calls = @import("block_calls.zig"); +const block = @import("block.zig"); +const moe = @import("block_moe.zig"); +const E = block.Emitter; +const Arg = calls.Arg; + +/// mhc_cuda / mhc_dec's MAX_ROWS: windows of more rows take this file's path. +pub const cuda_rows: i64 = 16; +/// TF_DSV41_MHC_SPLIT_ROWS (32): split launches and the second streams buffer up to here. +pub const split_rows: i64 = 32; +/// attn_cuda.MAX_ROWS / dtopk.MAX_ROWS / rowtab's largest bucket: the widest decode window. +pub const max_rows: i64 = 64; + +pub const Mode = enum { boundary, site1, site2, final }; + +fn coefs(e: *E, set: u1) ![3]Arg { + return .{ + try e.buf("w.c{d}.pre", .{set}, .f32, &.{ e.n, 4 }), + try e.buf("w.c{d}.post", .{set}, .f32, &.{ e.n, 4 }), + try e.buf("w.c{d}.comb", .{set}, .f32, &.{ e.n, 16 }), + }; +} + +/// One mHC site of layer L (`which`: hc_attn / hc_ffn) or the final norm (L null) for a window of more than 16 rows. +pub fn mhc(e: *E, layer: ?u32, which: []const u8, mode: Mode, in_place: bool, norm: ?[]const u8, tap: ?Arg) !void { + const n = e.n; + const pf = pfOn(e, mode); + if ((n <= cuda_rows and !pf) or n > max_rows) return error.WideRows; + // PFDEC's deferred coefficients (MHC_DEFER): this site reads them (a no-op without a pending launch) + moe.awaitCoefs(e); + const D: i64 = e.cfg.hidden; + const hm: i64 = e.cfg.hc_mult; + const post = mode == .boundary or mode == .final; + const mix = mode != .final; + const x = try e.streams(); + const prev = try coefs(e, e.coef); + const next = try coefs(e, 1 - e.coef); + const part = try e.buf("L.mhc.part", .{}, .f32, &.{ n, 4, 40, 32 }); + const c = try e.buf("L.mhc.c", .{}, .bf16, &.{ n, D }); + // the forward's second streams buffer: a boundary of at most 32 rows writes it; a final writes the streams + const spare = mode == .boundary and !in_place and n <= split_rows; + const xout: Arg = if (spare) try e.spare() else x; + const g: ?Arg = if (post) try e.buf("w.recv.{s}", .{if (std.mem.eql(u8, which, "hc_ffn")) "attn" else "moe"}, .bf16, &.{ e.o.world, n, D }) else null; + const collapse: i64 = if (mode == .site1) 1 else 2; + const pre: ?Arg = if (collapse == 2) prev[0] else null; + const fnw: ?Arg = if (mix) try e.buf("s.L{d}.{s}.fn16", .{ layer.?, which }, .bf16, &.{ 6 * hm, hm * D }) else null; + if (pf) { + const L = layer.?; + try e.ext("tf_dsv41_mhc_pf_v1.run", &.{ + x, if (post) xout else try e.empty(.bf16), + g orelse try e.empty(.bf16), if (post) prev[1] else try e.empty(.f32), + if (post) prev[2] else try e.empty(.f32), pre orelse try e.empty(.f32), + fnw.?, part, + c, tap orelse try e.empty(.bf16), + .{ .i = n }, .{ .i = @backingInt(mode) }, + }); + try finishK(e, null, part, c, try normOf(e, layer, which, norm)); + if (e.o.mhc_defer) { + e.df_side = true; // MHC_DEFER: on the deferred stream, the next mHC call joins it + e.df_pending = true; + } + try e.ext("tf_dsv41_mhc_cuda_v1.coef", &.{ + part, try e.weight("L{d}.{s}.base", .{ L, which }, .f32, &.{6 * hm}), + try e.weight("L{d}.{s}.scale", .{ L, which }, .f32, &.{3}), next[0], + next[1], next[2], + .{ .i = n }, .{ .f = E.dec(e.cfg.eps) }, + .{ .f = E.dec(e.cfg.hc_eps) }, .{ .f = E.dec(e.cfg.hc_post_alpha) }, + .{ .i = e.cfg.hc_sinkhorn_iters }, + }); + e.coef = 1 - e.coef; + if (spare) e.cur = 1 - e.cur; + return; + } + const split = mix and n <= split_rows and !(post and !spare); + try e.triton("_site", .{ std.math.divCeil(i64, n, 16) catch unreachable, 40, if (split) 4 else 1 }, &.{ + .{ .name = "X", .arg = x }, .{ .name = "x_stride", .arg = .{ .i = hm * D } }, + .{ .name = "XOUT", .arg = if (post) xout else x }, .{ .name = "G", .arg = g orelse x }, + .{ .name = "g_rank", .arg = .{ .i = if (post) n * D else 0 } }, .{ .name = "POST", .arg = if (post) prev[1] else x }, + .{ .name = "COMB", .arg = if (post) prev[2] else x }, .{ .name = "PRE", .arg = pre orelse x }, + .{ .name = "FN", .arg = fnw orelse x }, .{ .name = "PART", .arg = part }, + .{ .name = "C", .arg = c }, .{ .name = "TAP", .arg = tap orelse x }, + .{ .name = "tap_stride", .arg = .{ .i = if (tap) |t| t.t.stride[0] else 0 } }, .{ .name = "R", .arg = .{ .i = n } }, + .{ .name = "D", .arg = .{ .i = D } }, .{ .name = "NB", .arg = .{ .i = 40 } }, + .{ .name = "BM", .arg = .{ .i = 16 } }, .{ .name = "BK", .arg = .{ .i = 16 } }, + .{ .name = "WORLD", .arg = .{ .i = if (post) e.o.world else 1 } }, .{ .name = "POST_ON", .arg = .{ .b = post } }, + .{ .name = "COLLAPSE", .arg = .{ .i = collapse } }, .{ .name = "MIX", .arg = .{ .b = mix } }, + .{ .name = "TAP_ON", .arg = .{ .b = tap != null } }, .{ .name = "SPLIT", .arg = .{ .b = split } }, + }); + try finishK(e, if (mix) .{ layer.?, which, next } else null, part, c, try normOf(e, layer, which, norm)); + if (mix) e.coef = 1 - e.coef; + if (spare) e.cur = 1 - e.cur; +} + +/// TF_DSV41_MHC_PFDEC: this window's mixing sites take mhc_pf + `coef_kernel` (`mhc`'s `pf` branch). +pub fn pfOn(e: *const E, mode: anytype) bool { + const m = e.o.mhc_pf_rows; + return m > 0 and e.n >= m and e.n <= max_rows and @backingInt(mode) != @backingInt(Mode.final); +} + +/// A site's (or the final's) norm weight: the layer's attn / ffn norm, the model's (or `norm`'s) at the final. +fn normOf(e: *E, layer: ?u32, which: []const u8, norm: ?[]const u8) !Arg { + const D: i64 = e.cfg.hidden; + if (layer) |L| return e.weight("L{d}.{s}", .{ L, if (std.mem.eql(u8, which, "hc_attn")) "attn_norm" else "ffn_norm" }, .f32, &.{D}); + return e.weight("{s}", .{norm orelse "norm"}, .f32, &.{D}); +} + +/// The mHC finish kernel: with `coef` also the next site's coefficients, else the normed input alone. +fn finishK(e: *E, coef: ?struct { u32, []const u8, [3]Arg }, part: Arg, c: Arg, nw: Arg) !void { + const n = e.n; + const D: i64 = e.cfg.hidden; + const hm: i64 = e.cfg.hc_mult; + const mix = coef != null; + try e.triton("_finish_k", .{ n, 1, 1 }, &.{ + .{ .name = "PART", .arg = part }, + .{ .name = "BASE", .arg = if (coef) |k| try e.weight("L{d}.{s}.base", .{ k[0], k[1] }, .f32, &.{6 * hm}) else part }, + .{ .name = "SCALE", .arg = if (coef) |k| try e.weight("L{d}.{s}.scale", .{ k[0], k[1] }, .f32, &.{3}) else part }, + .{ .name = "PRE", .arg = if (coef) |k| k[2][0] else part }, + .{ .name = "POST", .arg = if (coef) |k| k[2][1] else part }, + .{ .name = "COMB", .arg = if (coef) |k| k[2][2] else part }, + .{ .name = "C", .arg = c }, + .{ .name = "NW", .arg = nw }, + .{ .name = "OUT", .arg = try e.buf("w.out", .{}, .bf16, &.{ n, D }) }, + .{ .name = "eps", .arg = .{ .f = E.dec(e.cfg.eps) } }, + .{ .name = "hc_eps", .arg = .{ .f = E.dec(e.cfg.hc_eps) } }, + .{ .name = "post_alpha", .arg = .{ .f = E.dec(e.cfg.hc_post_alpha) } }, + .{ .name = "D", .arg = .{ .i = D } }, + .{ .name = "NB", .arg = .{ .i = 40 } }, + .{ .name = "ITERS", .arg = .{ .i = e.cfg.hc_sinkhorn_iters } }, + .{ .name = "COEF", .arg = .{ .b = mix } }, + .{ .name = "BLOCK", .arg = .{ .i = @intCast(std.math.gcd(@as(u64, 1024), @as(u64, @intCast(D)))) } }, + .{ .name = "UNROLL", .arg = .{ .b = true } }, + .{ .name = "NORM_FIRST", .arg = .{ .b = true } }, + }); +} + +/// block_moe.mhcInto's call for a window of more than 16 rows (`mode`: block_moe.Mode), with its DSpark tap. +pub fn decode(e: *E, layer: ?u32, which: []const u8, mode: anytype, in_place: bool) !void { + const m = std.meta.stringToEnum(Mode, @tagName(mode)).?; + var tap: ?Arg = null; + if (e.o.taps and m == .boundary and std.mem.eql(u8, which, "hc_attn")) if (layer) |L| { + const t = e.cfg.dspark_targets.items(); + if (std.mem.indexOfScalar(u32, t, L)) |j| { + const D: i64 = e.cfg.hidden; + const w: i64 = @as(i64, @intCast(t.len)) * D; + tap = try e.view(.{ .buf = "w.taps" }, .bf16, &.{ e.n, D }, &.{ w, 1 }, @as(i64, @intCast(j)) * D * 2); + } + }; + return mhc(e, layer, which, m, in_place, null, tap); +} + +/// router_gemv config at `n` rows: (row tile, warps, experts a warp); past 16 rows the prefill config. +pub fn routerCfg(n: i64) [3]i64 { + if (n > cuda_rows) return .{ 16, 8, 2 }; + const t = std.math.ceilPowerOfTwo(u64, @intCast(n)) catch unreachable; + return .{ @intCast(t), 2, 0 }; +} + +test "wide rows: router config as router_gemv.config / row_tile" { + try std.testing.expectEqual([3]i64{ 1, 2, 0 }, routerCfg(1)); + try std.testing.expectEqual([3]i64{ 8, 2, 0 }, routerCfg(5)); + try std.testing.expectEqual([3]i64{ 16, 2, 0 }, routerCfg(16)); + try std.testing.expectEqual([3]i64{ 16, 8, 2 }, routerCfg(17)); + try std.testing.expectEqual([3]i64{ 16, 8, 2 }, routerCfg(64)); +} diff --git a/zig/src/families/deepseek_v41/buffers.zig b/zig/src/families/deepseek_v41/buffers.zig new file mode 100644 index 0000000000..9d547dea86 --- /dev/null +++ b/zig/src/families/deepseek_v41/buffers.zig @@ -0,0 +1,89 @@ +//! The buffer plan: every role a window's calls name, sized by its largest view, over every row bucket. + +const std = @import("std"); +const calls = @import("block_calls.zig"); + +pub const Scope = enum { persistent, window, layer }; // "s." own allocation; "w."/"L." one arena + +pub fn scopeOf(role: []const u8) Scope { + if (std.mem.startsWith(u8, role, "s.")) return .persistent; + if (std.mem.startsWith(u8, role, "w.")) return .window; + return .layer; +} + +pub fn dtSize(dt: calls.Dt) u64 { + return switch (dt) { + .bool, .i8, .u8 => 1, + .bf16, .f16, .i16 => 2, + .f32, .i32 => 4, + .f64, .i64 => 8, + }; +} + +/// Bytes from the role's first byte that a view reaches (0: an empty tensor). +pub fn extent(t: calls.Tensor) u64 { + var last: i64 = 0; + for (t.shape, t.stride) |n, s| { + if (n == 0) return 0; + last += (n - 1) * s; + } + return @as(u64, @intCast(t.offset)) + (@as(u64, @intCast(last)) + 1) * dtSize(t.dt); +} + +pub const Plan = struct { + a: std.mem.Allocator, + sizes: std.StringArrayHashMapUnmanaged(u64) = .empty, + /// the weights the programs read by name (forms_inplace.zig leaves those in place) + weights: std.StringArrayHashMapUnmanaged(void) = .empty, + + fn note(p: *Plan, x: calls.Arg) !void { + switch (x) { + .list => |items| for (items) |y| try p.note(y), + .t, .opaque_table => |t| switch (t.role) { + .buf => |r| { + const n = extent(t); + if (n == 0) return; + const g = try p.sizes.getOrPut(p.a, r); + if (!g.found_existing) { + g.key_ptr.* = try p.a.dupe(u8, r); + g.value_ptr.* = 0; + } + g.value_ptr.* = @max(g.value_ptr.*, n); + }, + .weight => |w| { + const g = try p.weights.getOrPut(p.a, w); + if (!g.found_existing) g.key_ptr.* = try p.a.dupe(u8, w); + }, + else => {}, + }, + else => {}, + } + } + + /// Takes every role of `cs` into the plan (call once a row bucket). + pub fn add(p: *Plan, cs: []const calls.Call) !void { + for (cs) |c| for (c.args) |x| try p.note(x.arg); + } + + pub const Totals = struct { roles: [3]usize = @splat(0), bytes: [3]u64 = @splat(0) }; + + /// Bytes by scope, each role rounded up to 256 (its place in the arena or its own allocation). + pub fn totals(p: *const Plan) Totals { + var t: Totals = .{}; + for (p.sizes.keys(), p.sizes.values()) |k, v| { + const s = @backingInt(scopeOf(k)); + t.roles[s] += 1; + t.bytes[s] += std.mem.alignForward(u64, v, 256); + } + return t; + } +}; + +const testing = std.testing; + +test "extent: a view's reach from the role's first byte" { + const t: calls.Tensor = .{ .role = .{ .buf = "w.x" }, .dt = .bf16, .shape = &.{ 3, 4096 }, .stride = &.{ 16384, 1 }, .offset = 8192 }; + try testing.expectEqual(@as(u64, 8192 + (2 * 16384 + 4095 + 1) * 2), extent(t)); + const e: calls.Tensor = .{ .role = .{ .buf = "w.x" }, .dt = .f32, .shape = &.{0}, .stride = &.{1} }; + try testing.expectEqual(@as(u64, 0), extent(e)); +} diff --git a/zig/src/families/deepseek_v41/calls_check.zig b/zig/src/families/deepseek_v41/calls_check.zig new file mode 100644 index 0000000000..8e79744eff --- /dev/null +++ b/zig/src/families/deepseek_v41/calls_check.zig @@ -0,0 +1,394 @@ +//! `tf-dsv41 check`: the emitters' calls against a capture's launches, on the host (kernels, shapes, roles). + +const std = @import("std"); +const calls = @import("block_calls.zig"); +const block = @import("block.zig"); +const block_prefill = @import("block_prefill.zig"); +const Config = @import("config.zig").Config; +const buffers = @import("buffers.zig"); +const cuda = @import("cuda"); +const tc = @import("triton_call.zig"); +const Value = std.json.Value; + +/// Dense K2s from the capture's weights.json (each ".trellis"), the experts' ranges from a listing. +pub fn widthsFromCapture(a: std.mem.Allocator, weights_json: []const u8, experts: []const u8) !block.Widths { + var w: block.Widths = .{}; + const root = try std.json.parseFromSliceLeaky(Value, a, weights_json, .{}); + var it = root.object.iterator(); + while (it.next()) |e| { + const name = e.key_ptr.*; + if (!std.mem.endsWith(u8, name, ".trellis")) continue; + const shape = e.value_ptr.object.get("shape").?.array.items; + if (shape.len != 3) continue; + try w.dense.put(a, name[0 .. name.len - ".trellis".len], @intCast(@divExact(shape[2].integer, 8))); + } + try parseExperts(a, &w, experts); + return w; +} + +/// A "prefix k2" listing (fixtures/dense-k2.txt) into the dense widths. +pub fn parseDense(a: std.mem.Allocator, w: *block.Widths, text: []const u8) !void { + var lines = std.mem.tokenizeScalar(u8, text, '\n'); + while (lines.next()) |line| { + if (line[0] == '#') continue; + var f = std.mem.tokenizeScalar(u8, line, ' '); + const name = f.next() orelse return error.BadListing; + try w.dense.put(a, try a.dupe(u8, name), try std.fmt.parseInt(u32, f.next() orelse return error.BadListing, 10)); + } +} + +/// The whole model emitted on the host from the K2 listings: every call's count and the buffer plan's bytes. +pub fn planAll(gpa: std.mem.Allocator, dense: []const u8, experts: []const u8, log: *std.Io.Writer) !void { + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + const a = arena.allocator(); + var w: block.Widths = .{}; + try parseDense(a, &w, dense); + try parseExperts(a, &w, experts); + const cfg: Config = .{}; + var backbone: [40]u32 = undefined; + for (&backbone, 0..) |*l, i| l.* = @intCast(i); + var plan: buffers.Plan = .{ .a = a }; + for ([_]i64{ 1, 2, 3, 4, 8, 16 }) |n| { + const cs = try block.emit(a, &cfg, &w, .{}, &backbone, n, 4096 - 16, true); + try plan.add(cs); + var tri: usize = 0; + var pf: usize = 0; + for (cs) |c| { + if (c.triton) tri += 1; + if (std.mem.startsWith(u8, c.name, "tf_dsv41_l2p")) pf += 1; + } + try log.print("backbone window, {d} rows: {d} launches ({d} Triton, {d} extension incl. {d} L2 prefetch)\n", .{ n, cs.len, tri, cs.len - tri, pf }); + } + for ([_]u32{ 40, 41, 42 }) |L| { + const cs = try block.emit(a, &cfg, &w, .{}, &.{L}, 16, 4096 - 16, false); + try plan.add(cs); + try log.print("DSpark block {d}, 16 rows: {d} launches\n", .{ L, cs.len }); + } + const t = plan.totals(); + const names = [_][]const u8{ "persistent (s.)", "window (w.)", "layer (L.)" }; + for (names, 0..) |nm, i| try log.print("{s}: {d} roles, {d:.1} MiB\n", .{ nm, t.roles[i], @as(f64, @floatFromInt(t.bytes[i])) / (1 << 20) }); +} + +pub fn parseExperts(a: std.mem.Allocator, w: *block.Widths, text: []const u8) !void { + var lines = std.mem.tokenizeScalar(u8, text, '\n'); + while (lines.next()) |line| { + if (line[0] == '#') continue; + var f = std.mem.tokenizeScalar(u8, line, ' '); + var v: [5]u32 = undefined; + for (&v) |*x| x.* = try std.fmt.parseInt(u32, f.next() orelse return error.BadListing, 10); + try w.experts.put(a, v[0], .{ .{ v[1], v[2] }, .{ v[3], v[4] } }); + } +} + +/// (role, captured buffer id) of every buffer-backed tensor of a call (its arguments and the capture's, same shape). +pub fn bindings(a: std.mem.Allocator, want: calls.Arg, cap: Value, out: *std.ArrayList(Binding)) !void { + switch (want) { + .list => |items| for (items, cap.object.get("items").?.array.items) |x, y| try bindings(a, x, y, out), + .t, .opaque_table => |t| switch (t.role) { + .buf => |r| if (cap.object.get("buf")) |id| { + var numel: i64 = 1; + for (t.shape) |d| numel *= d; + if (numel > 0) try out.append(a, .{ .role = r, .id = id.integer }); + }, + else => {}, + }, + else => {}, + } +} + +pub const Binding = struct { role: []const u8, id: i64 }; + +/// The window's dataflow by content: a role read from another role's leftover storage is wired wrong (reported). +pub const Flow = struct { + a: std.mem.Allocator, + /// role -> (captured storage, its bytes' digest) after the role's last call + last: std.StringHashMapUnmanaged([2][]const u8) = .empty, + glue: std.StringArrayHashMapUnmanaged(u32) = .empty, + /// window / layer roles first touched by a call that leaves them unchanged: inputs the forward's glue fills + inputs: std.StringArrayHashMapUnmanaged(u32) = .empty, + /// roles a glue step touched since coming into scope: a first read equal to freed storage is not a wrong wire + glued: std.StringHashMapUnmanaged(void) = .empty, + miswired: u32 = 0, + + pub fn begin(f: *Flow, b: calls.Begin) void { + if (b == .none) return; + inline for (.{ &f.last, &f.glued }) |m| { + var drop: std.ArrayList([]const u8) = .empty; + var it = m.keyIterator(); + while (it.next()) |k| { + if (std.mem.startsWith(u8, k.*, "s.")) continue; + if (b == .layer and std.mem.startsWith(u8, k.*, "w.")) continue; + drop.append(f.a, k.*) catch {}; + } + for (drop.items) |k| _ = m.remove(k); + } + } + + /// A glue step of ours (no captured launch): the roles it reads or writes. + pub fn glueCall(f: *Flow, c: *const calls.Call) !void { + for (c.args) |x| if (x.arg == .t and x.arg.t.role == .buf) try f.glued.put(f.a, x.arg.t.role.buf, {}); + } + + pub fn call(f: *Flow, c: *const calls.Call, op: std.json.ObjectMap, log: *std.Io.Writer, where: []const u8) !void { + var bs: std.ArrayList(Binding) = .empty; + const got = op.get("args").?.array.items; + const m = @min(c.args.len, got.len); // a count mismatch is calls.check's to report + for (c.args[0..m], got[0..m]) |x, y| try bindings(f.a, x.arg, y, &bs); + const bufs = op.get("buffers").?.array.items; + for (bs.items) |b| { + const o = for (bufs) |v| { + if (v.object.get("id").?.integer == b.id) break v.object; + } else continue; + const before = o.get("before").?; + if (before != .string) continue; + const key = o.get("key").?.string; + if (f.last.get(b.role)) |h| { + if (!std.mem.eql(u8, h[1], before.string)) { + const g = try f.glue.getOrPut(f.a, b.role); + if (!g.found_existing) g.value_ptr.* = 0; + g.value_ptr.* += 1; + } + continue; + } + // a first touch the call leaves as it was is a read: something outside the calls filled it + if (!std.mem.startsWith(u8, b.role, "s.") and std.mem.eql(u8, before.string, o.get("after").?.string)) { + const g = try f.inputs.getOrPut(f.a, b.role); + if (!g.found_existing) g.value_ptr.* = 0; + g.value_ptr.* += 1; + } + // a first touch that writes overwrites whatever a freed storage held; only a read can be miswired + const read = std.mem.eql(u8, before.string, o.get("after").?.string) and !f.glued.contains(b.role); + var it = f.last.iterator(); + while (it.next()) |e| if (read and std.mem.eql(u8, e.value_ptr[0], key) and std.mem.eql(u8, e.value_ptr[1], before.string)) { + f.miswired += 1; + try log.print(" {s} {s}: {s} first read holds what {s} last held\n", .{ where, c.name, b.role, e.key_ptr.* }); + break; + }; + } + for (bs.items) |b| { + const o = for (bufs) |v| { + if (v.object.get("id").?.integer == b.id) break v.object; + } else continue; + try f.last.put(f.a, b.role, .{ try f.a.dupe(u8, o.get("key").?.string), try f.a.dupe(u8, o.get("after").?.string) }); + } + } +}; + +/// The emitter options a capture ran with, from meta.json's env (R1 when its knobs were set). +pub fn optionsOf(a: std.mem.Allocator, io: std.Io, dir: []const u8) !block.Options { + const text = try std.Io.Dir.cwd().readFileAlloc(io, try std.fs.path.join(a, &.{ dir, "meta.json" }), a, .limited(1 << 24)); + const meta = try std.json.parseFromSliceLeaky(Value, a, text, .{}); + var o: block.Options = .{}; + const env = meta.object.get("env") orelse return o; + const on = struct { + fn f(e: Value, k: []const u8) bool { + const v = e.object.get(k) orelse return false; + return v == .string and std.mem.eql(u8, v.string, "1"); + } + }.f; + o.r1 = on(env, "TF_DSV41_MHC_DEFER") and on(env, "TF_DSV41_RG_PRUNE") and on(env, "TF_DSV41_ATTN_ROPE") and on(env, "TF_DSV41_RMS_FOLD"); + return o; +} + +pub const Window = struct { set: []const u8, phase: []const u8, layers: []const u32, n: i64, start: i64, ops: []std.json.ObjectMap, prefill: bool = false }; + +/// The capture's phases in order: each set's decode windows from the prompt's end, then its prefill segment. +pub fn windows(a: std.mem.Allocator, io: std.Io, dir: []const u8) ![]Window { + const cwd = std.Io.Dir.cwd(); + const meta = try std.json.parseFromSliceLeaky(Value, a, try cwd.readFileAlloc(io, try std.fs.path.join(a, &.{ dir, "meta.json" }), a, .limited(1 << 24)), .{}); + const prompt = meta.object.get("prompt").?.integer; + const sizes = meta.object.get("windows").?.array.items; + const text = try cwd.readFileAlloc(io, try std.fs.path.join(a, &.{ dir, "ops.jsonl" }), a, .limited(1 << 34)); + var out: std.ArrayList(Window) = .empty; + var lines = std.mem.tokenizeScalar(u8, text, '\n'); + var cur: ?*Window = null; + var ops: std.ArrayList(std.json.ObjectMap) = .empty; + while (lines.next()) |line| { + const op = (try std.json.parseFromSliceLeaky(Value, a, line, .{})).object; + const set = op.get("set").?.string; + const phase = op.get("phase").?.string; + if (phase.len < 2 or (phase[0] != 'w' and phase[0] != 'p')) continue; + if (cur == null or !std.mem.eql(u8, cur.?.set, set) or !std.mem.eql(u8, cur.?.phase, phase)) { + if (cur) |c| c.ops = try ops.toOwnedSlice(a); + const n = try std.fmt.parseInt(i64, phase[1..], 10); + var start = prompt; + for (sizes) |s| { + if (phase[0] == 'w' and s.integer == n) break; + start += s.integer; + } + const ls = meta.object.get("sets").?.object.get(set).?.object.get("layers").?.array.items; + const layers = try a.alloc(u32, ls.len); + for (ls, layers) |x, *y| y.* = @intCast(x.integer); + try out.append(a, .{ .set = set, .phase = phase, .layers = layers, .n = n, .start = start, .ops = &.{}, .prefill = phase[0] == 'p' }); + cur = &out.items[out.items.len - 1]; + } + try ops.append(a, op); + } + if (cur) |c| c.ops = try ops.toOwnedSlice(a); + return out.items; +} + +/// The routed experts' K2s per layer (Widths.gm) from the prefill phases' x3gm launches (the widths launched). +pub fn gmWidths(a: std.mem.Allocator, w: *block.Widths, ws: []const Window) !void { + for (ws) |win| for (win.ops) |op| { + const name = op.get("name").?.string; + const down = std.mem.eql(u8, name, "tf_dsv41_x3gm_v1.down"); + if (!down and !std.mem.eql(u8, name, "tf_dsv41_x3gm_v1.gateup")) continue; + const args = op.get("args").?.array.items; + const svh = args[if (down) 8 else 10].object.get("weight").?.string; + const L = try std.fmt.parseInt(u32, svh[1..std.mem.indexOfScalar(u8, svh, '.').?], 10); + const k2: u5 = @intCast(args[if (down) 12 else 16].object.get("v").?.integer); + const gop = try w.gm.getOrPut(a, L); + if (!gop.found_existing) gop.value_ptr.* = .{ 0, 0 }; + gop.value_ptr[@intFromBool(down)] |= @as(u32, 1) << k2; + }; +} + +/// True for a capture whose mHC boundary launched with 26 arguments and spin -1 (`block.Options.r1_sig`). +pub fn r1Sig(ops_lists: []const []std.json.ObjectMap) bool { + for (ops_lists) |ops| for (ops) |op| { + const name = (op.get("name") orelse continue); + if (name != .string or !std.mem.eql(u8, name.string, "tf_dsv41_mhc_cuda_v1.run")) continue; + const args = (op.get("args") orelse continue).array.items; + if (args.len != 26) return false; + const spin = args[24].object.get("v") orelse return false; + return spin == .integer and spin.integer < 0; + }; + return false; +} + +/// `r1Sig` over a capture's windows. +pub fn r1SigOf(a: std.mem.Allocator, ws: []const Window) !bool { + const lists = try a.alloc([]std.json.ObjectMap, ws.len); + for (lists, ws) |*l, w| l.* = w.ops; + return r1Sig(lists); +} + +pub fn gmMode(ws: []const Window) @import("knob_types.zig").GmMode { + const Names = struct { + ws: []const Window, + w: usize = 0, + i: usize = 0, + pub fn next(it: *@This()) ?[]const u8 { + while (it.w < it.ws.len) { + const ops = it.ws[it.w].ops; + if (it.i < ops.len) { + it.i += 1; + return ops[it.i - 1].get("name").?.string; + } + it.w += 1; + it.i = 0; + } + return null; + } + }; + var it: Names = .{ .ws = ws }; + return @import("gm2pf.zig").ofLaunches(&it); +} + +/// Triton variant choice on the host: exactly one compiled variant may match, the one the capture launched. +pub const Variants = struct { + specs: []const cuda.aot.Spec, + checked: usize = 0, + wrong: usize = 0, + + fn base(_: *anyopaque, t: calls.Tensor) anyerror!u64 { + var numel: i64 = 1; + for (t.shape) |n| numel *= n; + return if (numel == 0) 0 else 0x7f00_0000_0000 + @as(u64, @intCast(t.offset)); // a 256-aligned buffer + the view + } + + pub fn check(v: *Variants, a: std.mem.Allocator, c: *const calls.Call, op: std.json.ObjectMap) !?[]const u8 { + if (!c.triton) return null; + var mine: std.ArrayList(cuda.aot.Spec) = .empty; + for (v.specs) |s| if (std.mem.eql(u8, s.@"fn", c.name)) try mine.append(a, s); + var dummy: u8 = 0; + const sp = try tc.split(a, c, mine.items, .{ .ctx = &dummy, .of = base }); + v.checked += 1; + var hit: ?[]const u8 = null; + var n: usize = 0; + for (mine.items) |s| if (cuda.aot.specMatches(s, sp.args, sp.consts)) { + n += 1; + hit = s.hash; + }; + const want = op.get("hash").?.string; + if (n == 1 and std.mem.eql(u8, hit.?, want)) return null; + v.wrong += 1; + return try std.fmt.allocPrint(a, "{d} of {d} variants match (want {s})", .{ n, mine.items.len, want[0..12] }); + } +}; + +/// Checks every window; one line a window plus each mismatch (up to `show` a window) on `log`. Returns mismatches. +pub fn checkAll(gpa: std.mem.Allocator, io: std.Io, dir: []const u8, experts: []const u8, log: *std.Io.Writer, show: usize) !usize { + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + const a = arena.allocator(); + const cwd = std.Io.Dir.cwd(); + var w = try widthsFromCapture(a, try cwd.readFileAlloc(io, try std.fs.path.join(a, &.{ dir, "weights.json" }), a, .limited(1 << 28)), experts); + const cfg: Config = .{}; + const ws = try windows(a, io, dir); + try gmWidths(a, &w, ws); + var opts = try optionsOf(a, io, dir); + opts.gm_v2 = gmMode(ws); + opts.r1_sig = !opts.r1 and try r1SigOf(a, ws); + if (opts.r1) try log.print("options: R1 (the capture's knobs)\n", .{}); + if (opts.r1_sig) try log.print("options: R1's trailing arguments at their off values (the twin's bindings)\n", .{}); + if (opts.gm_v2 != .off) try log.print("options: x3gm v2 {t} (the capture's launches)\n", .{opts.gm_v2}); + var bad: usize = 0; + // the variant gate when the capture's AOT set carries the specialization + var variants: ?Variants = null; + if (cwd.readFileAlloc(io, try std.fs.path.join(a, &.{ dir, "aot", "aot.json" }), a, .limited(1 << 26))) |text| { + if (std.mem.indexOf(u8, text, "\"consts\"") != null) variants = .{ .specs = (try cuda.aot.parseSpecs(a, text)).value.kernels }; + } else |_| {} + var roles: calls.Roles = .{ .a = a }; + var flow: Flow = .{ .a = a }; + var last_set: []const u8 = ""; + for (ws) |win| { + if (!std.mem.eql(u8, last_set, win.set)) { // each set ran in its own engine + roles = .{ .a = a }; + flow.last = .empty; + } + last_set = win.set; + const all = if (win.prefill) + try block_prefill.emitPrefill(a, &cfg, &w, opts, win.layers, win.n, win.start, true) + else + try block.emit(a, &cfg, &w, opts, win.layers, win.n, win.start, true); + const cs = try calls.launches(a, all); + var equal: usize = 0; + var shown: usize = 0; + const m = @min(cs.len, win.ops.len); + var g: usize = 0; // the next of `all`: our glue steps and the scopes in emission order + for (cs[0..m], win.ops[0..m], 0..) |*c, op, i| { + roles.begin(c.begin); + while (all[g].glue) : (g += 1) { + flow.begin(all[g].begin); + try flow.glueCall(&all[g]); + } + flow.begin(all[g].begin); + g += 1; + try flow.call(c, op, log, try std.fmt.allocPrint(a, "{s} {s} #{d}", .{ win.set, win.phase, i })); + const vwhy = if (variants) |*v| try v.check(a, c, op) else null; + if (vwhy) |why| { + bad += 1; + if (shown < show) try log.print(" {s} {s} #{d} {s}: variant: {s}\n", .{ win.set, win.phase, i, c.name, why }); + shown += 1; + } + if (try calls.check(a, c, op, &roles)) |why| { + bad += 1; + if (shown < show) try log.print(" {s} {s} #{d} {s}: {s}\n", .{ win.set, win.phase, i, c.name, why }); + shown += 1; + } else equal += 1; + } + if (cs.len != win.ops.len) bad += 1; + try log.print("{s} {s} (layers {any}, n {d}, from {d}): {d}/{d} calls equal, {d} emitted\n", .{ win.set, win.phase, win.layers, win.n, win.start, equal, win.ops.len, cs.len }); + try log.flush(); + } + try log.print("dataflow: {d} first reads hold another role's bytes; roles written between calls by uncaptured ops (reads):", .{flow.miswired}); + for (flow.glue.keys(), flow.glue.values()) |k, v| try log.print(" {s}={d}", .{ k, v }); + if (variants) |v| try log.print("\nTriton variants: {d} calls, {d} picked wrong", .{ v.checked, v.wrong }) else try log.print("\nTriton variants: not checked (no aot.json with constexprs)", .{}); + try log.print("\nread-only first touches (inputs glue fills; in-place updates not listed):", .{}); + for (flow.inputs.keys(), flow.inputs.values()) |k, v| try log.print(" {s}={d}", .{ k, v }); + try log.print("\n", .{}); + return bad + flow.miswired; +} diff --git a/zig/src/families/deepseek_v41/ced.zig b/zig/src/families/deepseek_v41/ced.zig new file mode 100644 index 0000000000..2e6c720002 --- /dev/null +++ b/zig/src/families/deepseek_v41/ced.zig @@ -0,0 +1,137 @@ +//! CED bounded replay prefill (`TF_DSV41_PREFILL=replay`): the replay rules and the stash bookkeeping (host only). + +const std = @import("std"); +const Config = @import("config.zig").Config; +const ft = @import("forward_types.zig"); + +pub const replay_rows = ft.replay_rows; +pub const keep = ft.keep; +pub const ring = ft.ring; +/// snapshot and piece alignment +pub const grid: u64 = 16; +/// a prompt snapshot needs this many tokens +pub const session_min: u64 = 64; +/// session tag prefill field: the fast prefill path, prompt snapshots only; exact-tag (0) turn entries never resume +pub const tag_fast: u32 = 1 << 8; +/// with the CED field (replay mode index << 4): replay entries never resume full ones +pub const tag: u32 = tag_fast | 1 << 4; + +pub const Error = error{ NoDecoder, EngramInDecoder, StashGap }; + +/// The first ratio-1 layer: must be a kv source, and no decoder layer may be an Engram layer. +pub fn decoderStart(cfg: *const Config) Error!u32 { + var d: u32 = cfg.layers; + for (0..cfg.layers) |i| if (cfg.compressRatio(@intCast(i)) == 1) { + d = @intCast(i); + break; + }; + if (d >= cfg.layers or !cfg.isKvSource(d)) return error.NoDecoder; + for (cfg.engram_layers.items()) |L| if (L >= d) return error.EngramInDecoder; + return d; +} + +/// The decoder replay of a prompt whose prefilled part ends at `end` (= n - 1): rows [r0, end), m = end - r0. +pub const Tail = struct { r0: u64, m: u32 }; + +pub fn tailOf(end: u64) Tail { + const m: u32 = @intCast(@min(end, keep)); + return .{ .r0 = end - m, .m = m }; +} + +/// The last grid point strictly before a prompt's end. +pub fn snapshotPoint(n: u64) u64 { + return if (n > 0) (n - 1) / grid * grid else 0; +} + +/// Where a prompt of `n` tokens resumed at `cached` is snapshotted, or null. +pub fn savePoint(n: u64, cached: u64) ?u64 { + const s = snapshotPoint(n); + return if (s > cached and s >= session_min) s else null; +} + +pub const Stash = ft.Stash; + +/// The drafter hand-off before the first verify row: one ingest of the last `window` taps rows from `row0` at `start`. +pub const Handoff = struct { start: u64, row0: u32, n: u32 }; + +pub fn handoff(at: u64, rows: u32, window: u32) ?Handoff { + if (rows == 0 or window == 0) return null; + const skip = rows -| window; + return .{ .start = at + skip, .row0 = skip, .n = rows - skip }; +} + +/// One contiguous run of positions in the ring: rows [row, row + n) hold positions [at, at + n) of a range. +pub const Span = struct { row: u32, n: u32, at: u32 }; + +/// Positions [a, b) (at most `ring`) as at most two contiguous ring runs; `at` counts from a. +pub fn spans(a: u64, b: u64, out: *[2]Span) []const Span { + std.debug.assert(b >= a and b - a <= ring); + const total: u32 = @intCast(b - a); + if (total == 0) return out[0..0]; + const r: u32 = @intCast(a % ring); + const first = @min(total, ring - r); + out[0] = .{ .row = r, .n = first, .at = 0 }; + if (first == total) return out[0..1]; + out[1] = .{ .row = 0, .n = total - first, .at = first }; + return out[0..2]; +} + +/// The stash roles (streams [ring, 4 D], normed input, fp32 pre / post [ring, 4], comb [ring, 16]) in glue-step order. +pub const roles = [_][]const u8{ "s.ced.x", "s.ced.out", "s.ced.pre", "s.ced.post", "s.ced.comb" }; + +test "replay rules as replay.py / sessions.py / batch.py" { + const cfg: Config = .{}; + try std.testing.expectEqual(@as(u32, 20), try decoderStart(&cfg)); + // n <= 128: R0 = 0, the decoder sees the whole prefilled part + try std.testing.expectEqual(Tail{ .r0 = 0, .m = 0 }, tailOf(0)); + try std.testing.expectEqual(Tail{ .r0 = 0, .m = 44 }, tailOf(44)); + try std.testing.expectEqual(Tail{ .r0 = 0, .m = 127 }, tailOf(127)); + // n = 129 (end 128): R0 = 1 = n - 128 + try std.testing.expectEqual(Tail{ .r0 = 1, .m = 127 }, tailOf(128)); + try std.testing.expectEqual(Tail{ .r0 = 32768 - 128, .m = 127 }, tailOf(32767)); + try std.testing.expectEqual(@as(u64, 32), snapshotPoint(33)); + try std.testing.expectEqual(@as(u64, 32), snapshotPoint(48)); + try std.testing.expectEqual(@as(u64, 48), snapshotPoint(49)); + try std.testing.expectEqual(@as(?u64, null), savePoint(60, 0)); // S = 48 < session_min + try std.testing.expectEqual(@as(?u64, 64), savePoint(65, 0)); + try std.testing.expectEqual(@as(?u64, null), savePoint(65, 64)); // nothing past the resumed position + try std.testing.expectEqual(@as(?u64, 4096), savePoint(4100, 1024)); + // a decoder layer with Engram, or no ratio-1 layer: refused + var bad = cfg; + bad.engram_layers = @TypeOf(cfg.engram_layers).of(&.{ 1, 24 }); + try std.testing.expectError(error.EngramInDecoder, decoderStart(&bad)); +} + +test "the DSpark hand-off: the decoder pass's rows whole, a longer run's last window" { + // replay, n = 1,536: the tail [1408, 1535), 127 rows, one ingest at R0 + try std.testing.expectEqual(Handoff{ .start = 1408, .row0 = 0, .n = 127 }, handoff(tailOf(1535).r0, tailOf(1535).m, 128).?); + // a prompt of 45 tokens: its 44 prefilled rows from 0 + try std.testing.expectEqual(Handoff{ .start = 0, .row0 = 0, .n = 44 }, handoff(tailOf(44).r0, tailOf(44).m, 128).?); + // full mode, a last segment of 2,048 rows at 4,096: its last 128 + try std.testing.expectEqual(Handoff{ .start = 4096 + 1920, .row0 = 1920, .n = 128 }, handoff(4096, 2048, 128).?); + try std.testing.expectEqual(@as(?Handoff, null), handoff(0, 0, 128)); +} + +test "the stash: the last rows of every segment, a gap restarts it, two ring runs at most" { + var s: Stash = .{}; + var ids: [300]u32 = undefined; + for (&ids, 0..) |*t, i| t.* = @intCast(1000 + i); + s.extend(0, ids[0..100]); + try std.testing.expect(s.covers(0, 100)); + s.extend(100, ids[100..300]); + try std.testing.expectEqual(@as(u64, 300 - keep), s.lo()); + try std.testing.expect(s.covers(173, 300)); + try std.testing.expect(!s.covers(172, 300)); + try std.testing.expect(!s.covers(173, 299)); + var got: [keep]u32 = undefined; + s.tail(173, 300, &got); + try std.testing.expectEqualSlices(u32, ids[173..300], &got); + // a segment that does not start at hi: the stash starts over there (a restored slot whose stash ended elsewhere) + s.extend(500, ids[0..10]); + try std.testing.expectEqual(@as(u64, 500), s.lo()); + try std.testing.expect(!s.covers(495, 510)); + var sp: [2]Span = undefined; + try std.testing.expectEqualSlices(Span, &.{ .{ .row = 44, .n = 84, .at = 0 }, .{ .row = 0, .n = 43, .at = 84 } }, spans(300, 427, &sp)); + try std.testing.expectEqualSlices(Span, &.{.{ .row = 0, .n = 127, .at = 0 }}, spans(256, 383, &sp)); + try std.testing.expectEqual(@as(usize, 0), spans(7, 7, &sp).len); +} diff --git a/zig/src/families/deepseek_v41/config.zig b/zig/src/families/deepseek_v41/config.zig new file mode 100644 index 0000000000..85e1401978 --- /dev/null +++ b/zig/src/families/deepseek_v41/config.zig @@ -0,0 +1,340 @@ +//! DeepSeek-V4.1-Flash text config (config.json -> text_config) and the per-layer CSA2 topology. + +const std = @import("std"); + +/// Fixed-capacity id lists of the config (layers, sources, Engram tables); the release's longest has 43 entries. +pub fn List(comptime T: type, comptime cap: usize) type { + return struct { + const Self = @This(); + buf: [cap]T = undefined, + len: usize = 0, + + pub fn of(values: []const T) Self { + var s: Self = .{}; + for (values) |v| s.push(v) catch unreachable; + return s; + } + + pub fn push(s: *Self, v: T) !void { + if (s.len == cap) return error.ConfigListTooLong; + s.buf[s.len] = v; + s.len += 1; + } + + pub fn items(s: *const Self) []const T { + return s.buf[0..s.len]; + } + + pub fn contains(s: *const Self, v: T) bool { + return std.mem.indexOfScalar(T, s.items(), v) != null; + } + }; +} + +pub const Ids = List(u32, 64); + +pub const Rope = struct { + theta: f64 = 10000.0, + compress_theta: f64 = 160000.0, + factor: f64 = 16.0, + original_max_positions: u64 = 65536, + beta_fast: f64 = 32.0, + beta_slow: f64 = 1.0, + max_positions: u64 = 1048576, + yarn: bool = true, +}; + +/// The attention a layer runs: sliding window only, KV source + indexer, own indexer on a source's keys, or reuse. +pub const Mode = enum { swa, full, reindex, reuse }; + +pub const Config = struct { + vocab: u32 = 129280, + hidden: u32 = 5120, + layers: u32 = 40, + heads: u32 = 64, + head_dim: u32 = 512, + rope_dim: u32 = 64, + q_lora: u32 = 1280, + o_lora: u32 = 1024, + o_groups: u32 = 8, + eps: f32 = 1e-20, + window: u32 = 128, + experts: u32 = 384, + shared_experts: u32 = 1, + topk: u32 = 6, + expert_width: u32 = 2304, + routed_scale: f32 = 1.5, + norm_topk: bool = true, + swiglu_limit: f32 = 10.0, + compress_ratios: Ids = Ids.of(&.{ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 }), + kv_sources: Ids = Ids.of(&.{ 2, 8, 14, 20 }), + index_sources: Ids = Ids.of(&.{ 2, 8, 14, 20, 24, 28, 32, 36 }), + index_heads: u32 = 32, + index_dim: u32 = 128, + index_topk: u32 = 512, + candidate_source: i64 = 20, + candidate_blocks: u32 = 2048, + candidate_block_size: u32 = 8, + hc_mult: u32 = 4, + hc_sinkhorn_iters: u32 = 20, + hc_eps: f32 = 1e-6, + hc_post_alpha: f32 = 2.0, + engram_layers: Ids = Ids.of(&.{ 1, 14 }), + engram_rows: List(u64, 8) = List(u64, 8).of(&.{ 384006168, 384016682 }), + engram_max_ngram: u32 = 4, + engram_vocab: u64 = 16000000, + engram_heads: u32 = 8, + engram_head_dim: u32 = 256, + engram_pad: u32 = 2, + engram_compressed_vocab: u32 = 99092, + engram_gate_clamp: f32 = 1e-6, + mtp_layers: u32 = 3, + dspark_block: u32 = 5, + dspark_noise_token: u32 = 128799, + dspark_targets: Ids = Ids.of(&.{ 37, 38, 39 }), + dspark_markov_rank: u32 = 256, + dspark_experts: u32 = 128, + dspark_topk: u32 = 3, + rope: Rope = .{}, + bos: u32 = 0, + eos: u32 = 1, + + /// config.json in `dir`. + pub fn read(gpa: std.mem.Allocator, io: std.Io, dir: []const u8) !Config { + const path = try std.fs.path.join(gpa, &.{ dir, "config.json" }); + defer gpa.free(path); + const bytes = try std.Io.Dir.cwd().readFileAlloc(io, path, gpa, .limited(1 << 24)); + defer gpa.free(bytes); + return parse(gpa, bytes); + } + + /// Checkpoint JSON (text_config, else top level); absent keys keep release values, unknown routing is refused. + pub fn parse(gpa: std.mem.Allocator, json: []const u8) !Config { + const parsed = try std.json.parseFromSlice(std.json.Value, gpa, json, .{}); + defer parsed.deinit(); + if (parsed.value != .object) return error.BadConfig; + const top = parsed.value.object; + const text = if (top.get("text_config")) |t| (if (t == .object) t.object else return error.BadConfig) else top; + var c: Config = .{}; + const ints = .{ + .{ "vocab", "vocab_size" }, .{ "hidden", "hidden_size" }, + .{ "layers", "num_hidden_layers" }, .{ "heads", "num_attention_heads" }, + .{ "head_dim", "head_dim" }, .{ "rope_dim", "qk_rope_head_dim" }, + .{ "q_lora", "q_lora_rank" }, .{ "o_lora", "o_lora_rank" }, + .{ "o_groups", "o_groups" }, .{ "window", "sliding_window" }, + .{ "experts", "n_routed_experts" }, .{ "shared_experts", "n_shared_experts" }, + .{ "topk", "num_experts_per_tok" }, .{ "expert_width", "moe_intermediate_size" }, + .{ "index_heads", "index_n_heads" }, .{ "index_dim", "index_head_dim" }, + .{ "index_topk", "index_topk" }, .{ "candidate_source", "candidate_source_layer_id" }, + .{ "candidate_blocks", "candidate_topk_blocks" }, .{ "candidate_block_size", "candidate_block_size" }, + .{ "hc_mult", "hc_mult" }, .{ "hc_sinkhorn_iters", "hc_sinkhorn_iters" }, + .{ "engram_max_ngram", "engram_max_ngram_size" }, .{ "engram_vocab", "engram_vocab_size" }, + .{ "engram_heads", "engram_n_heads" }, .{ "engram_head_dim", "engram_head_dim" }, + .{ "engram_pad", "engram_pad_token_id" }, .{ "engram_compressed_vocab", "engram_compressed_vocab_size" }, + .{ "mtp_layers", "num_nextn_predict_layers" }, .{ "dspark_block", "dspark_block_size" }, + .{ "dspark_noise_token", "dspark_noise_token_id" }, .{ "dspark_markov_rank", "dspark_markov_rank" }, + .{ "dspark_experts", "dspark_n_routed_experts" }, .{ "dspark_topk", "dspark_num_experts_per_tok" }, + }; + inline for (ints) |p| if (text.get(p[1])) |v| { + @field(c, p[0]) = try intAs(@TypeOf(@field(c, p[0])), v); + }; + const floats = .{ + .{ "eps", "rms_norm_eps" }, .{ "routed_scale", "routed_scaling_factor" }, .{ "swiglu_limit", "swiglu_limit" }, + .{ "hc_eps", "hc_eps" }, .{ "hc_post_alpha", "hc_post_alpha" }, .{ "engram_gate_clamp", "engram_gate_clamp" }, + }; + inline for (floats) |p| if (text.get(p[1])) |v| { + @field(c, p[0]) = @floatCast(try float(v)); + }; + if (text.get("norm_topk_prob")) |v| c.norm_topk = if (v == .bool) v.bool else return error.BadConfig; + const lists = .{ + .{ "compress_ratios", "compress_ratios" }, .{ "kv_sources", "kv_source_layer_ids" }, + .{ "index_sources", "index_source_layer_ids" }, .{ "engram_layers", "engram_layer_ids" }, + .{ "dspark_targets", "dspark_target_layer_ids" }, .{ "engram_rows", "engram_num_embeddings" }, + }; + inline for (lists) |p| if (text.get(p[1])) |v| { + @field(c, p[0]) = try list(@TypeOf(@field(c, p[0])), v); + }; + // routing the kernels implement: sqrt(softplus) scores, bias-corrected top-k (vLLM noaux_tc) + if (text.get("scoring_func")) |v| if (v != .string or !std.mem.eql(u8, v.string, "sqrtsoftplus")) return error.UnsupportedScoring; + if (text.get("topk_method")) |v| if (v != .string or !std.mem.eql(u8, v.string, "noaux_tc")) return error.UnsupportedScoring; + c.rope = try rope(text); + if (top.get("bos_token_id")) |v| c.bos = try intAs(u32, v); + if (top.get("eos_token_id")) |v| c.eos = try intAs(u32, v); + try c.validate(); + return c; + } + + /// Shapes the kernels and the loader assume; a release that breaks one is refused before any allocation. + pub fn validate(c: *const Config) !void { + if (c.hidden % 128 != 0 or c.expert_width % 128 != 0 or c.vocab % 128 != 0) return error.BadConfig; + if (c.heads % c.o_groups != 0 or c.rope_dim > c.head_dim or c.topk > c.experts) return error.BadConfig; + if (c.dspark_topk > c.dspark_experts and c.dspark_experts != 0) return error.BadConfig; + if (c.compress_ratios.len < c.layers) return error.BadConfig; + for (c.compress_ratios.items()) |r| if (r > 2) return error.BadConfig; + for (c.kv_sources.items()) |s| if (s >= c.layers or c.compressRatio(s) == 0) return error.BadConfig; + for (c.index_sources.items()) |s| if (s >= c.layers or c.compressRatio(s) == 0) return error.BadConfig; + if (c.engram_layers.len != c.engram_rows.len) return error.BadConfig; + // every compressed layer needs a KV source and an index source at or before it + var l: u32 = 0; + while (l < c.layers) : (l += 1) if (c.compressRatio(l) > 0 and (c.kvSource(l) == null or c.indexSource(l) == null)) return error.BadConfig; + } + + /// Layers the forward runs: the backbone, then the DSpark blocks (checkpoint mtp.*). + pub fn allLayers(c: *const Config) u32 { + return c.layers + c.mtp_layers; + } + + pub fn compressRatio(c: *const Config, layer: u32) u32 { + return if (layer < c.compress_ratios.len) c.compress_ratios.buf[layer] else 0; + } + + pub fn isBackbone(c: *const Config, layer: u32) bool { + return layer < c.layers; + } + + /// The latest source at or before `layer` (null for sliding-window layers). + fn latest(c: *const Config, sources: *const Ids, layer: u32) ?u32 { + if (c.compressRatio(layer) == 0) return null; + var best: ?u32 = null; + for (sources.items()) |s| if (s <= layer and (best == null or s > best.?)) { + best = s; + }; + return best; + } + + pub fn kvSource(c: *const Config, layer: u32) ?u32 { + return c.latest(&c.kv_sources, layer); + } + + pub fn indexSource(c: *const Config, layer: u32) ?u32 { + return c.latest(&c.index_sources, layer); + } + + pub fn isKvSource(c: *const Config, layer: u32) bool { + return c.isBackbone(layer) and c.kv_sources.contains(layer); + } + + pub fn isIndexSource(c: *const Config, layer: u32) bool { + return c.isBackbone(layer) and c.index_sources.contains(layer); + } + + pub fn isCandidateSource(c: *const Config, layer: u32) bool { + return c.candidate_source == layer and c.candidate_blocks > 0; + } + + pub fn usesCandidates(c: *const Config, layer: u32) bool { + return c.isIndexSource(layer) and c.candidate_blocks > 0 and c.candidate_source >= 0 and c.candidate_source < layer; + } + + pub fn mode(c: *const Config, layer: u32) Mode { + if (c.compressRatio(layer) == 0) return .swa; + if (c.isKvSource(layer)) return .full; + if (c.isIndexSource(layer)) return .reindex; + return .reuse; + } + + pub fn isEngram(c: *const Config, layer: u32) bool { + return c.engram_layers.contains(layer); + } + + /// (routed experts, experts a token) of a backbone layer or a DSpark block. + pub fn expertsOf(c: *const Config, layer: u32) struct { count: u32, topk: u32 } { + if (c.isBackbone(layer)) return .{ .count = c.experts, .topk = c.topk }; + return .{ .count = if (c.dspark_experts != 0) c.dspark_experts else c.experts, .topk = if (c.dspark_topk != 0) c.dspark_topk else c.topk }; + } + + /// Engram's hash columns: (max n-gram - 1) x heads. + pub fn hashCols(c: *const Config) u32 { + return (c.engram_max_ngram - 1) * c.engram_heads; + } +}; + +fn intAs(comptime T: type, v: std.json.Value) !T { + return switch (v) { + .integer => |i| std.math.cast(T, i) orelse error.BadConfig, + .float => |f| if (@floor(f) == f) std.math.cast(T, @as(i64, @intFromFloat(f))) orelse error.BadConfig else error.BadConfig, + else => error.BadConfig, + }; +} + +fn float(v: std.json.Value) !f64 { + return switch (v) { + .integer => |i| @floatFromInt(i), + .float => |f| f, + else => error.BadConfig, + }; +} + +fn list(comptime L: type, v: std.json.Value) !L { + if (v != .array) return error.BadConfig; + var out: L = .{}; + const T = @TypeOf(out.buf[0]); + for (v.array.items) |x| try out.push(try intAs(T, x)); + return out; +} + +/// rope_theta, compress_rope_theta and rope_scaling (or rope_parameters), as RopeConfig.from_dict. +fn rope(text: std.json.ObjectMap) !Rope { + var r: Rope = .{}; + if (text.get("rope_theta")) |v| r.theta = try float(v); + if (text.get("compress_rope_theta")) |v| r.compress_theta = try float(v); + if (text.get("max_position_embeddings")) |v| r.max_positions = try intAs(u64, v); + const scaling = text.get("rope_scaling") orelse text.get("rope_parameters"); + r.factor = 1.0; + r.original_max_positions = r.max_positions; + r.yarn = false; + if (scaling) |s| if (s == .object) { + const o = s.object; + if (o.get("factor")) |v| r.factor = try float(v); + if (o.get("original_max_position_embeddings")) |v| r.original_max_positions = try intAs(u64, v); + if (o.get("beta_fast")) |v| r.beta_fast = try float(v); + if (o.get("beta_slow")) |v| r.beta_slow = try float(v); + const kind = o.get("rope_type") orelse o.get("type"); + r.yarn = if (kind) |k| (k == .string and !std.mem.eql(u8, k.string, "default")) else false; + }; + return r; +} + +/// A small config with every V4.1 mechanism; split widths are 256 so TP=2 keeps 128-value blocks. +pub fn tiny() Config { + return .{ + .vocab = 256, + .hidden = 256, + .layers = 8, + .heads = 4, + .head_dim = 64, + .rope_dim = 8, + .q_lora = 32, + .o_lora = 128, + .o_groups = 2, + .eps = 1e-6, + .window = 4, + .experts = 8, + .topk = 2, + .expert_width = 256, + .compress_ratios = Ids.of(&.{ 0, 0, 2, 2, 1, 1, 1, 1, 0, 0 }), + .kv_sources = Ids.of(&.{ 2, 4 }), + .index_sources = Ids.of(&.{ 2, 4, 6 }), + .index_heads = 2, + .index_dim = 16, + .index_topk = 3, + .candidate_source = 4, + .candidate_blocks = 2, + .candidate_block_size = 2, + .engram_layers = Ids.of(&.{1}), + .engram_rows = List(u64, 8).of(&.{0}), + .engram_max_ngram = 3, + .engram_vocab = 31, + .engram_heads = 2, + .engram_head_dim = 8, + .engram_compressed_vocab = 50, + .mtp_layers = 2, + .dspark_block = 3, + .dspark_noise_token = 96, + .dspark_targets = Ids.of(&.{ 5, 6, 7 }), + .dspark_markov_rank = 8, + .dspark_experts = 4, + .dspark_topk = 2, + .rope = .{ .factor = 4.0, .original_max_positions = 64, .max_positions = 256 }, + }; +} diff --git a/zig/src/families/deepseek_v41/deepseek_v41.zig b/zig/src/families/deepseek_v41/deepseek_v41.zig new file mode 100644 index 0000000000..6a98f7c267 --- /dev/null +++ b/zig/src/families/deepseek_v41/deepseek_v41.zig @@ -0,0 +1,51 @@ +//! DeepSeek-V4.1-Flash on the Zig engine: release config, EXL3 pack headers, a rank's TP weight plan (host side). + +const std = @import("std"); + +pub const config = @import("config.zig"); +pub const Config = config.Config; +pub const exl3 = @import("exl3.zig"); +pub const names = @import("names.zig"); +pub const pack = @import("pack.zig"); +pub const Pack = pack.Pack; +pub const plan = @import("plan.zig"); +pub const Plan = plan.Plan; +pub const stage = @import("stage.zig"); +pub const named = @import("named.zig"); +pub const prepare = @import("prepare.zig"); +pub const engram_host = @import("engram_host.zig"); + +test { + _ = config; + _ = exl3; + _ = names; + _ = pack; + _ = plan; + _ = stage; + _ = named; + _ = prepare; + _ = engram_host; + // the device-side weight load, compiled on the host (no GPU work) + inline for (.{ @import("load.zig"), @import("forms.zig"), @import("forms_inplace.zig"), @import("host_stage.zig"), @import("triton_capture.zig"), @import("ex_stacks.zig"), @import("buffers.zig"), @import("block_calls.zig") }) |t| std.testing.refAllDecls(t); + // a block's emitter (attention, index, MoE, read-ahead, windows past 16 rows) over the launch bindings + const block = @import("block.zig"); + inline for (.{ block, block.Emitter, block.Pool, @import("block_attn.zig"), @import("block_index.zig"), @import("block_moe.zig"), @import("block_prefetch.zig"), @import("block_wide.zig"), @import("knob_types.zig"), @import("forward_types.zig") }) |t| std.testing.refAllDecls(t); + _ = @import("host_test.zig"); + _ = @import("engram_rows_test.zig"); // Engram's shard rows + _ = @import("phases.zig"); // the phase timers + // the forward over the block emitter, split KV and the decode graphs (hooks for later stages unset) + inline for (.{ @import("forward.zig"), @import("forward_engram.zig"), @import("forward_follow.zig"), @import("forward_glue.zig"), @import("forward_graph.zig"), @import("forward_hooks.zig"), @import("forward_sample.zig"), @import("kv_state.zig"), @import("run.zig"), @import("triton_call.zig"), @import("kv_glue_emit_test.zig") }) |t| std.testing.refAllDecls(t); + _ = @import("graphs.zig"); // the decode graphs' buckets, statics and floor + _ = @import("greedy.zig"); // greedy's host step + // the prompt's prefill: segments over the prefill emitter, CED, long prompts, x3gm v2 (hooks for images unset) + inline for (.{ @import("block_prefill.zig"), @import("block_prefill_attn.zig"), @import("block_prefill_emit.zig"), @import("block_prefill_mhc.zig"), @import("block_prefill_moe.zig"), @import("block_prefill_select.zig"), @import("forward_prefill.zig"), @import("forward_prefill_finish.zig"), @import("forward_prefill_glue.zig"), @import("forward_prefill_k4.zig"), @import("gm2pf.zig"), @import("longpf.zig"), @import("calls_check.zig"), @import("rowmode.zig"), @import("serve_knobs.zig") }) |t| std.testing.refAllDecls(t); + _ = @import("ced.zig"); // CED's keep and the drafter hand-off + _ = @import("image_opts.zig"); // image prompts' block options + _ = @import("rowtab.zig"); // row mode's per-row table + _ = @import("multi_test.zig"); // several slots' prompt segments in one call + _ = @import("streamrb_test.zig"); // the stream top-k's read-back twins on the prefill emitter + // the keyed sampler's pipeline and device steps (TF_DSV41_SAMPLING; its golden runs in test-dsv41-sampling) + inline for (.{ @import("sampling_pipe.zig"), @import("sampling_dev.zig"), @import("sampling_gpu.zig") }) |t| std.testing.refAllDecls(t); + // prepared weight folders (TF_DSV41_PREPARED) + _ = @import("prepared.zig"); +} diff --git a/zig/src/families/deepseek_v41/draft/acclog.zig b/zig/src/families/deepseek_v41/draft/acclog.zig new file mode 100644 index 0000000000..d94910640a --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/acclog.zig @@ -0,0 +1,59 @@ +//! TF_DSV41_ACCEPT_LOG=: one JSON line per draft-policy event (rank 0) for draft-depth studies; off when unset. + +const std = @import("std"); +const quiet = @import("builtin").is_test; // a test's stderr is the build runner's channel + +var fd: c_int = -2; // -2: not looked up yet, -1: off +var round: u64 = 0; +var buf: [16384]u8 = undefined; + +fn on() bool { + if (fd == -2) { + fd = -1; + if (std.c.getenv("TF_DSV41_ACCEPT_LOG")) |p| { + const f = std.c.open(p, .{ .ACCMODE = .WRONLY, .CREAT = true, .APPEND = true }, @as(std.c.mode_t, 0o644)); + if (f >= 0) fd = f else if (!quiet) std.log.warn("TF_DSV41_ACCEPT_LOG: cannot open {s}", .{std.mem.span(p)}); + } + } + return fd >= 0; +} + +fn nowNs() u64 { + var ts: std.c.timespec = undefined; + _ = std.c.clock_gettime(.MONOTONIC, &ts); + return @as(u64, @intCast(ts.sec)) * std.time.ns_per_s + @as(u64, @intCast(ts.nsec)); +} + +fn put(s: []const u8) void { + var at: usize = 0; + while (at < s.len) { + const n = std.c.write(fd, s[at..].ptr, s.len - at); + if (n <= 0) return; + at += @intCast(n); + } +} + +pub const Ask = struct { slot: u32, start: u64, most: usize, k: usize, conf: []const f64, q: []const f64 }; + +pub fn choose(windows: usize, rows: u64, others: u64, asks: []const Ask) void { + if (!on()) return; + round += 1; + var w = std.Io.Writer.fixed(&buf); + w.print("{{\"ev\":\"choose\",\"r\":{d},\"t\":{d},\"windows\":{d},\"rows\":{d},\"others\":{d},\"asks\":[", .{ round, nowNs(), windows, rows, others }) catch return; + for (asks, 0..) |a, i| { + w.print("{s}{{\"slot\":{d},\"start\":{d},\"most\":{d},\"k\":{d},\"conf\":[", .{ if (i > 0) "," else "", a.slot, a.start, a.most, a.k }) catch return; + for (a.conf, 0..) |v, j| w.print("{s}{d:.5}", .{ if (j > 0) "," else "", v }) catch return; + w.writeAll("],\"q\":[") catch return; + for (a.q, 0..) |v, j| w.print("{s}{d:.5}", .{ if (j > 0) "," else "", v }) catch return; + w.writeAll("]}") catch return; + } + w.writeAll("]}\n") catch return; + put(w.buffered()); +} + +pub fn commit(slot: u32, rows: u32, keep: usize, drafted: bool, finished: bool, generated: usize, max_new: u32) void { + if (!on()) return; + var w = std.Io.Writer.fixed(&buf); + w.print("{{\"ev\":\"commit\",\"r\":{d},\"t\":{d},\"slot\":{d},\"rows\":{d},\"keep\":{d},\"drafted\":{},\"finished\":{},\"generated\":{d},\"max_new\":{d}}}\n", .{ round, nowNs(), slot, rows, keep, drafted, finished, generated, max_new }) catch return; + put(w.buffered()); +} diff --git a/zig/src/families/deepseek_v41/draft/branches.zig b/zig/src/families/deepseek_v41/draft/branches.zig new file mode 100644 index 0000000000..02834ea43f --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/branches.zig @@ -0,0 +1,125 @@ +//! Tree verify windows on a chain-only target: siblings run lazily as fresh chain windows on the slot itself, exact. +const std = @import("std"); +const lanes = @import("lanes"); +const iface = @import("iface.zig"); + +pub const max_rows = 64; +pub const unrun: u32 = std.math.maxInt(u32); +const none: u8 = std.math.maxInt(u8); + +pub const Error = error{ TreeRows, TreeParents, TreeDraws, BranchUnstable, NotResolved, NoTree }; + +/// A target's chain operations on one slot. +pub const Chains = struct { + ptr: *anyopaque, + vtable: *const VTable, + + pub const VTable = struct { + /// Chain window: `tokens` at `start` (row r at start + r, choice keyed at draws[r]); unresolved keeps all rows. + run: *const fn (ptr: *anyopaque, slot: u32, start: u64, tokens: []const u32, draws: []const u64, sampling: ?lanes.Sampling, choices: []u32) anyerror!void, + /// Drop the slot's pending chain window without committing a row (the slot stays at its start). + drop: *const fn (ptr: *anyopaque, slot: u32) anyerror!void, + }; +}; + +/// One slot's resolved tree window: the last chain's rows (tree row ids) and each tree row's row in it. +pub const Resolver = struct { + live: bool = false, + /// the last chain: rows[i] is the tree row at chain row i + rows: [max_rows]u8 = undefined, + n: u8 = 0, + /// tree row -> its row in the last chain (none: not in it); the taps' map for lanes' ingest + of: [max_rows]u32 = undefined, + tree_rows: u8 = 0, + stats: Stats = .{}, + + pub const Stats = struct { trees: u64 = 0, chains: u64 = 0, rerun_rows: u64 = 0, deep: u64 = 0 }; + + /// Resolve tree window `s` (parents set) through `ch` into `choices`; the accepted path's chain stays pending. + pub fn window(r: *Resolver, ch: Chains, s: iface.Segment, choices: []u32) !void { + const n = s.tokens.len; + const parents = s.parents orelse return error.NoTree; + if (n == 0 or n > max_rows or parents.len != n or s.draws.len != n or choices.len < n) return error.TreeRows; + // each row's children in row order (a linked list: first child, next sibling), parents before children + var first: [max_rows]u8 = undefined; + var next: [max_rows]u8 = undefined; + var last: [max_rows]u8 = undefined; + @memset(first[0..n], none); + @memset(next[0..n], none); + if (parents[0] != -1) return error.TreeParents; + for (parents[1..], 1..) |q, row| { + if (q < 0 or q >= row) return error.TreeParents; + const p: usize = @intCast(q); + if (s.draws[row] != s.draws[p] + 1) return error.TreeDraws; // a child sits one position after its parent + if (first[p] == none) first[p] = @intCast(row) else next[last[p]] = @intCast(row); + last[p] = @intCast(row); + } + @memset(choices[0..n], unrun); + r.live = false; + r.stats.trees += 1; + var tok: [max_rows]u32 = undefined; + var draws: [max_rows]u64 = undefined; + var picks: [max_rows]u32 = undefined; + var held: usize = 0; // chain rows already on the path (re-run in front of a branch) + var head: u8 = 0; + var runs: usize = 0; + while (true) { + // the chain: the path so far, then `head` and its first children + var m = held; + var at = head; + while (at != none) : (at = first[at]) { + r.rows[m] = at; + m += 1; + } + for (r.rows[0..m], tok[0..m], draws[0..m]) |row, *t, *d| { + t.* = s.tokens[row]; + d.* = s.draws[row]; + } + if (runs > 0) try ch.vtable.drop(ch.ptr, s.slot); + try ch.vtable.run(ch.ptr, s.slot, s.start, tok[0..m], draws[0..m], s.sampling, picks[0..m]); + runs += 1; + r.stats.chains += 1; + r.stats.rerun_rows += held; + for (r.rows[0..held], picks[0..held]) |row, p| if (choices[row] != p) return error.BranchUnstable; + for (r.rows[0..m], picks[0..m]) |row, p| choices[row] = p; + // follow the choices from the head: the first child by row whose token is the row's choice + var i = held; + const branch: ?u8 = while (i < m) : (i += 1) { + var c = first[r.rows[i]]; + while (c != none and s.tokens[c] != picks[i]) c = next[c]; + if (c == none) break null; // the path ends at chain row i + if (i + 1 < m and r.rows[i + 1] == c) continue; + break c; // a later sibling: its chain runs behind the path so far + } else null; + const b = branch orelse { + r.n = @intCast(m); + break; + }; + held = i + 1; + head = b; + r.stats.deep += @intFromBool(held > 1); // a branch below row 0 + } + @memset(r.of[0..n], unrun); + for (r.rows[0..r.n], 0..) |row, i| r.of[row] = @intCast(i); + r.tree_rows = @intCast(n); + r.live = true; + } + + /// The kept path (tree rows, row 0 first) as the last chain's accepted count; the map stays until the next window. + pub fn keep(r: *Resolver, path: []const u32) !u32 { + if (!r.live) return error.NoTree; + if (path.len == 0 or path.len > r.n) return error.NotResolved; + for (path, 0..) |row, i| if (row >= r.tree_rows or r.of[row] != i) return error.NotResolved; + return @intCast(path.len - 1); + } + + /// Tree row -> taps row of the last chain (unrun: not in it), while the tree is the slot's last window. + pub fn map(r: *const Resolver) ?[]const u32 { + return if (r.live) r.of[0..r.tree_rows] else null; + } + + /// The slot moved on (a chain window, a prefill, a release): its taps are no longer the tree's. + pub fn clear(r: *Resolver) void { + r.live = false; + } +}; diff --git a/zig/src/families/deepseek_v41/draft/branches_oracle.zig b/zig/src/families/deepseek_v41/draft/branches_oracle.zig new file mode 100644 index 0000000000..89eea8c097 --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/branches_oracle.zig @@ -0,0 +1,112 @@ +//! Tree gates without a trained drafter: `TreeOracle` drafts the serial decode with a sibling that loses or wins. +const std = @import("std"); +const lanes = @import("lanes"); +const iface = @import("iface.zig"); +const ln = @import("lanes.zig"); +const dspark = @import("dspark.zig"); +const tree = @import("tree.zig"); +const costs_mod = @import("costs.zig"); + +pub const TreeOracle = struct { + serial: []const u32, + prompt_len: u64, + vocab: u32, + /// zero Markov heads: a candidate's draft logit is its base logit + w1: []u16, + w2: []u16, + round: u64 = 0, + off_anchor: u64 = 0, + + pub fn init(gpa: std.mem.Allocator, serial: []const u32, prompt_len: u64, vocab: u32) !TreeOracle { + const w1 = try gpa.alloc(u16, vocab); + errdefer gpa.free(w1); + const w2 = try gpa.alloc(u16, vocab); + @memset(w1, 0); + @memset(w2, 0); + return .{ .serial = serial, .prompt_len = prompt_len, .vocab = vocab, .w1 = w1, .w2 = w2 }; + } + + pub fn deinit(x: *TreeOracle, gpa: std.mem.Allocator) void { + gpa.free(x.w1); + gpa.free(x.w2); + } + + pub fn pass(x: *TreeOracle) iface.Pass { + return .{ .ptr = x, .vtable = &vtable }; + } + const vtable: iface.Pass.VTable = .{ .ingest = ingest, .propose = propose, .reset = reset, .markov = markov }; + fn ingest(_: *anyopaque, _: u32, _: u64, _: iface.Taps, _: []const u32) anyerror!void {} + fn reset(_: *anyopaque, _: u32) void {} + + fn self(p: *anyopaque) *TreeOracle { + return @ptrCast(@alignCast(p)); + } + + fn markov(p: *anyopaque) ?dspark.Markov { + const x = self(p); + return .{ .w1 = x.w1, .w2 = x.w2, .rank = 1 }; + } + + fn at(x: *const TreeOracle, pos: u64) u32 { + if (pos < x.prompt_len) return x.vocab - 1; + const j = pos - x.prompt_len; + return if (j < x.serial.len) x.serial[@intCast(j)] else x.vocab - 1; + } + + /// Round r: r % 4 == 3 corrupts draft 0 (sibling wins), r % 4 == 1 draft (r / 4) % block; candidates [draft, alt]. + fn propose(p: *anyopaque, asks: []const iface.Ask, out: []iface.Proposal) anyerror!void { + const x = self(p); + for (asks, out) |a, o| { + if (a.anchor != x.at(a.start)) x.off_anchor += 1; + const b = o.drafts.len; + for (o.drafts, o.conf, 0..) |*d, *c, i| { + d.* = x.at(a.start + 1 + i); + c.* = 0.9; + } + const wrong: ?usize = switch (x.round % 4) { + 1 => @intCast((x.round / 4) % b), + 3 => 0, + else => null, + }; + if (wrong) |i| o.drafts[i] = (o.drafts[i] + 1) % x.vocab; + x.round += 1; + const cand = o.cand orelse continue; + const base = o.base.?; + const c = cand.len / b; + @memset(cand, -1); + @memset(base, 0.0); + for (0..b) |i| { + const truth = x.at(a.start + 1 + i); + cand[i * c] = @intCast(o.drafts[i]); + base[i * c] = 2.0; + if (c < 2) continue; + cand[i * c + 1] = @intCast(if (o.drafts[i] != truth) truth else (truth + 1) % x.vocab); + base[i * c + 1] = 1.9; + } + } + } +}; + +pub const Result = struct { tokens: []u32, drafted: u64, accepted: u64, trees: u64, sib_wins: u64, rounds: u64 }; + +/// `max_new` tokens after `prompt` through lanes (one slot, greedy) with tree knobs `set`, default cost table. +pub fn generate(gpa: std.mem.Allocator, target: iface.Target, pass: iface.Pass, shape: dspark.Shape, prompt: []const u32, max_new: u32, set: tree.Settings) !Result { + var c = try costs_mod.defaults(gpa, 64); + defer c.deinit(gpa); + const x = try ln.Lanes.init(gpa, target, pass, c, .{ .shape = shape, .slots = 1, .siblings = set.siblings, .sib_rows = set.sib_rows, .dup = set.dup, .spec = try @import("spec.zig").Settings.fromEnv() }); + defer x.deinit(); + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + var cfg = try lanes.Config.init(gpa, try x.model(arena.allocator(), 64), 16, 15); + defer cfg.deinit(gpa); + var clock: lanes.fake.FixedClock = .{}; + var e = lanes.Engine.init(gpa, &cfg, x.backend(), clock.clock()); + defer e.deinit(); + x.attach(&e); + var s = try lanes.Stream.init(gpa, .{ .id = "tree-gate", .prompt = prompt, .max_new = max_new, .drafts = true }); + defer s.deinit(gpa); + try e.addStream(&s); + while (e.activeCount() > 0) try e.step(); + x.reportSpec(); + return .{ .tokens = try gpa.dupe(u32, s.emitted()), .drafted = e.drafted, .accepted = e.accepted, .trees = x.policy.trees, .sib_wins = x.policy.sib_wins, .rounds = s.rounds }; +} diff --git a/zig/src/families/deepseek_v41/draft/branches_test.zig b/zig/src/families/deepseek_v41/draft/branches_test.zig new file mode 100644 index 0000000000..54f626ae28 --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/branches_test.zig @@ -0,0 +1,231 @@ +//! branches.zig on the CPU twin: chain-only tree windows match the native tree window, and lanes over both agree. +const std = @import("std"); +const lanes = @import("lanes"); +const iface = @import("iface.zig"); +const twin = @import("twin.zig"); +const ln = @import("lanes.zig"); +const tree = @import("tree.zig"); +const branches = @import("branches.zig"); +const oracle = @import("branches_oracle.zig"); +const costs_mod = @import("costs.zig"); +const accept = lanes.accept; + +const gpa = std.testing.allocator; + +/// The twin as a chain-only target (GpuTarget's shape): tree windows go through a Resolver, keeps through its map. +const ChainTwin = struct { + t: *twin.Twin, + res: [4]branches.Resolver = @splat(.{}), + native_trees: u64 = 0, // tree windows the twin ran itself (must stay 0) + + fn target(c: *ChainTwin) iface.Target { + return .{ .ptr = c, .vtable = &.{ .prefill = prefill, .window = window, .keep = keep, .taps = taps, .release = release } }; + } + + fn chains(c: *ChainTwin) branches.Chains { + return .{ .ptr = c, .vtable = &.{ .run = run, .drop = drop } }; + } + + fn self(p: *anyopaque) *ChainTwin { + return @ptrCast(@alignCast(p)); + } + + fn prefill(p: *anyopaque, slot: u32, ids: []const u32, s: ?lanes.Sampling, draw: u64) anyerror!u32 { + const c = self(p); + c.res[slot].clear(); + return c.t.target().prefill(slot, ids, s, draw); + } + + fn window(p: *anyopaque, segments: []const iface.Segment, choices: [][]u32) anyerror!void { + const c = self(p); + for (segments, choices) |g, out| { + if (g.parents != null) { + try c.res[g.slot].window(c.chains(), g, out); + continue; + } + c.res[g.slot].clear(); + var one = [_][]u32{out}; + try c.t.target().window(&.{g}, &one); + } + } + + fn run(p: *anyopaque, slot: u32, start: u64, tokens: []const u32, draws: []const u64, s: ?lanes.Sampling, out: []u32) anyerror!void { + const c = self(p); + var one = [_][]u32{out}; + try c.t.target().window(&.{.{ .slot = slot, .start = start, .tokens = tokens, .parents = null, .draws = draws, .sampling = s }}, &one); + } + + fn drop(p: *anyopaque, slot: u32) anyerror!void { + self(p).t.drop(slot); + } + + fn keep(p: *anyopaque, slot: u32, path: []const u32) anyerror!void { + const c = self(p); + if (!c.res[slot].live) return c.t.target().keep(slot, path); + const k = try c.res[slot].keep(path); + var rows: [branches.max_rows]u32 = undefined; + for (rows[0 .. k + 1], 0..) |*r, i| r.* = @intCast(i); + try c.t.target().keep(slot, rows[0 .. k + 1]); + } + + fn taps(p: *anyopaque, slot: u32) iface.Taps { + const c = self(p); + var t = c.t.target().taps(slot); + t.map = c.res[slot].map(); + return t; + } + + fn release(p: *anyopaque, slot: u32) void { + const c = self(p); + c.res[slot].clear(); + c.t.target().release(slot); + } +}; + +const Stats = struct { out: []u32, drafted: u64, accepted: u64, trees: u64, sib_wins: u64, chains: u64 = 0 }; + +/// One stream through lanes with the twin's DSpark and first-position siblings, over the native or chain-only target. +fn runLanes(prompt: []const u32, max_new: u32, smp: ?lanes.Sampling, set: tree.Settings, chain_only: bool) !Stats { + const dims: twin.Dims = .{}; + const t = try twin.Twin.init(gpa, dims, 2); + defer t.deinit(); + const d = try twin.Drafter.init(gpa, t, 2); + defer d.deinit(); + var ct: ChainTwin = .{ .t = t }; + var c = try costs_mod.defaults(gpa, 64); + defer c.deinit(gpa); + const x = try ln.Lanes.init(gpa, if (chain_only) ct.target() else t.target(), d.pass(), c, .{ .shape = d.shape, .siblings = set.siblings, .sib_rows = set.sib_rows, .dup = set.dup, .slots = 1 }); + defer x.deinit(); + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + var cfg = try lanes.Config.init(gpa, try x.model(arena.allocator(), 64), 16, 15); + defer cfg.deinit(gpa); + var clock: lanes.fake.FixedClock = .{}; + var e = lanes.Engine.init(gpa, &cfg, x.backend(), clock.clock()); + defer e.deinit(); + x.attach(&e); + var s = try lanes.Stream.init(gpa, .{ .id = "s", .prompt = prompt, .max_new = max_new, .sampling = smp }); + defer s.deinit(gpa); + try e.addStream(&s); + while (e.activeCount() > 0) try e.step(); + return .{ .out = try gpa.dupe(u32, s.emitted()), .drafted = e.drafted, .accepted = e.accepted, .trees = x.policy.trees, .sib_wins = x.policy.sib_wins, .chains = ct.res[0].stats.chains }; +} + +const p1 = [_]u32{ 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3, 8, 4 }; +const p2 = [_]u32{ 2, 7, 1, 8, 2, 8, 1, 8, 2, 8, 4, 5, 9, 0, 4, 5 }; +const p3 = [_]u32{ 10, 20, 30, 40, 10, 20, 30, 40, 10, 20, 30 }; +const hot: lanes.Sampling = .{ .seed = 11, .temperature = 0.8, .top_k = 0, .top_p = 0.9 }; + +test "trees resolved as chains: the native tree window's rounds, the serial decode" { + var trees: u64 = 0; + var wins: u64 = 0; + const sets = [_]tree.Settings{ .{ .siblings = 2, .dup = .{} }, .{ .siblings = 2 }, .{ .siblings = 3, .sib_rows = 6 } }; + for ([_][]const u32{ &p1, &p2, &p3 }) |prompt| for (sets) |set| for ([_]?lanes.Sampling{ null, hot }) |smp| { + const native = try runLanes(prompt, 70, smp, set, false); + defer gpa.free(native.out); + const mine = try runLanes(prompt, 70, smp, set, true); + defer gpa.free(mine.out); + const serial = blk: { + const t = try twin.Twin.init(gpa, .{}, 1); + defer t.deinit(); + break :blk try t.serial(0, prompt, 70, smp, null); + }; + defer gpa.free(serial); + try std.testing.expectEqualSlices(u32, serial, mine.out); + try std.testing.expectEqualSlices(u32, native.out, mine.out); + try std.testing.expectEqual(native.drafted, mine.drafted); + try std.testing.expectEqual(native.accepted, mine.accepted); + try std.testing.expectEqual(native.trees, mine.trees); + try std.testing.expectEqual(native.sib_wins, mine.sib_wins); + trees += mine.trees; + wins += mine.sib_wins; + }; + try std.testing.expect(trees > 0 and wins > 0); +} + +test "the tree oracle's siblings lose and win through a chain-only target, as through tree windows" { + const prompt = [_]u32{ 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3 }; + const dims: twin.Dims = .{}; + const want = blk: { + const t = try twin.Twin.init(gpa, dims, 1); + defer t.deinit(); + break :blk try t.serial(0, &prompt, 90, null, null); + }; + defer gpa.free(want); + const shape: @import("dspark.zig").Shape = .{ .block = dims.block, .window = dims.window, .hidden = dims.dim, .candidates = 8 }; + var got: [2]oracle.Result = undefined; + for ([_]bool{ false, true }, &got) |chain_only, *g| { + const t = try twin.Twin.init(gpa, dims, 2); + defer t.deinit(); + var ct: ChainTwin = .{ .t = t }; + var o = try oracle.TreeOracle.init(gpa, want, prompt.len, dims.vocab); + defer o.deinit(gpa); + g.* = try oracle.generate(gpa, if (chain_only) ct.target() else t.target(), o.pass(), shape, &prompt, 90, .{ .siblings = 2 }); + try std.testing.expectEqualSlices(u32, want, g.tokens); + try std.testing.expectEqual(@as(u64, 0), o.off_anchor); + try std.testing.expect(g.trees > 0 and g.sib_wins > 0 and g.sib_wins < g.trees); + } + defer for (got) |g| gpa.free(g.tokens); + try std.testing.expectEqual(got[0].drafted, got[1].drafted); + try std.testing.expectEqual(got[0].accepted, got[1].accepted); + try std.testing.expectEqual(got[0].trees, got[1].trees); + try std.testing.expectEqual(got[0].sib_wins, got[1].sib_wins); + try std.testing.expectEqual(got[0].rounds, got[1].rounds); +} + +test "any tree shape: the resolver's accepted path and its choices equal the tree window's" { + var prng = std.Random.DefaultPrng.init(42); + const r = prng.random(); + const t = try twin.Twin.init(gpa, .{}, 2); + defer t.deinit(); + var ct: ChainTwin = .{ .t = t }; + const prompt = [_]u32{ 5, 9, 2, 6, 5, 3, 5, 8 }; + _ = try t.target().prefill(0, &prompt, null, prompt.len); + var start: u64 = prompt.len; + for (0..200) |_| { + const n = r.intRangeAtMost(usize, 2, 14); + var parents: [16]i32 = undefined; + var depth: [16]u64 = undefined; + var tokens: [16]u32 = undefined; + var draws: [16]u64 = undefined; + var native: [16]u32 = undefined; + parents[0] = -1; + depth[0] = 0; + tokens[0] = r.uintLessThan(u32, t.dims.vocab); + for (1..n) |row| { + parents[row] = @intCast(r.uintLessThan(usize, row)); + depth[row] = depth[@intCast(parents[row])] + 1; + } + for (0..n) |row| draws[row] = start + depth[row] + 1; + // tokens row by row: often the parent's choice (from the tree window over the rows so far), so paths branch + for (1..n) |row| { + if (r.float(f32) < 0.65) { + try nativeWindow(t, start, tokens[0..row], parents[0..row], draws[0..row], native[0..row]); + tokens[row] = native[@intCast(parents[row])]; + } else tokens[row] = r.uintLessThan(u32, t.dims.vocab); + } + try nativeWindow(t, start, tokens[0..n], parents[0..n], draws[0..n], native[0..n]); + const want = try accept.acceptPath(gpa, tokens[0..n], parents[0..n], native[0..n]); + defer gpa.free(want); + var mine: [16]u32 = undefined; + var outs = [_][]u32{mine[0..n]}; + try ct.target().window(&.{.{ .slot = 0, .start = start, .tokens = tokens[0..n], .parents = parents[0..n], .draws = draws[0..n], .sampling = null }}, &outs); + const path = try accept.acceptPath(gpa, tokens[0..n], parents[0..n], mine[0..n]); + defer gpa.free(path); + try std.testing.expectEqualSlices(u32, want, path); + for (path) |row| try std.testing.expectEqual(native[row], mine[row]); + // the path kept: the slot moves on by its rows, the taps map them to the chain's first rows + try ct.target().keep(0, path); + const m = ct.target().taps(0).map.?; + for (path, 0..) |row, i| try std.testing.expectEqual(@as(u32, @intCast(i)), m[row]); + start += path.len; + } + try std.testing.expect(ct.res[0].stats.deep > 0); // branches below row 0 re-ran their path's rows +} + +/// The twin's own tree window over the rows, dropped after (the slot unchanged). +fn nativeWindow(t: *twin.Twin, start: u64, tokens: []const u32, parents: []const i32, draws: []const u64, out: []u32) !void { + var outs = [_][]u32{out}; + try t.target().window(&.{.{ .slot = 0, .start = start, .tokens = tokens, .parents = parents, .draws = draws, .sampling = null }}, &outs); + t.drop(0); +} diff --git a/zig/src/families/deepseek_v41/draft/calib.zig b/zig/src/families/deepseek_v41/draft/calib.zig new file mode 100644 index 0000000000..74b97cb461 --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/calib.zig @@ -0,0 +1,105 @@ +//! The confidence head's correction per draft position, the siblings' per rank, and the acceptance report. +const std = @import("std"); +const Allocator = std.mem.Allocator; + +pub const decay = 0.97; // evidence at a position fades by this each round that reaches it +pub const prior = 2.0; // the factor's prior weight, in units of summed probability +pub const f_min = 0.2; +pub const f_max = 3.0; +pub const q_max = 0.995; // a calibrated chance never reaches 1 + +/// Per draft position: decayed kept drafts and summed confidences. +pub const Calibration = struct { + kept: []f64, + prob: []f64, + + pub fn init(gpa: Allocator, positions: usize) !Calibration { + const kept = try gpa.alloc(f64, positions); + errdefer gpa.free(kept); + const prob = try gpa.alloc(f64, positions); + @memset(kept, 0.0); + @memset(prob, 0.0); + return .{ .kept = kept, .prob = prob }; + } + + pub fn deinit(c: *Calibration, gpa: Allocator) void { + gpa.free(c.kept); + gpa.free(c.prob); + } + + pub fn factor(c: Calibration, j: usize) f64 { + const at = @min(j, c.kept.len - 1); + return @min(@max((c.kept[at] + prior) / (c.prob[at] + prior), f_min), f_max); + } + + /// Draft j's chance to be kept given the ones before it were, from the head's confidence `p`. + pub fn q(c: Calibration, j: usize, p: f64) f64 { + return @min(@max(p, 0.0) * c.factor(j), q_max); + } + + /// A verified window: its drafts' confidences, `keep` the rows committed (1 + drafts kept). + pub fn record(c: *Calibration, probs: []const f64, keep: usize) void { + for (probs[0..@min(probs.len, c.kept.len)], 0..) |p, j| { + if (j >= keep) break; + c.kept[j] = c.kept[j] * decay + (if (j + 1 < keep) @as(f64, 1.0) else 0.0); + c.prob[j] = c.prob[j] * decay + @max(p, 0.0); + } + } +}; + +/// Per sibling rank (2nd, 3rd... first token): decayed kept and summed draft probabilities. +pub const SibCal = struct { + pub const s_prior = 2.0; + pub const s_min = 0.05; + pub const s_max = 4.0; + pub const s_q_max = 0.95; + kept: [3]f64 = @splat(0.0), + prob: [3]f64 = @splat(0.0), + + pub fn factor(c: SibCal, j: usize) f64 { + return @min(@max((c.kept[j] + s_prior) / (c.prob[j] + s_prior), s_min), s_max); + } + + pub fn q(c: SibCal, j: usize, p: f64) f64 { + return @min(@max(p, 0.0) * c.factor(j), s_q_max); + } + + pub fn record(c: *SibCal, j: usize, p: f64, kept: bool) void { + c.kept[j] = c.kept[j] * decay + @as(f64, @floatFromInt(@intFromBool(kept))); + c.prob[j] = c.prob[j] * decay + @max(p, 0.0); + } +}; + +/// Acceptance evidence for reports: per position the verified / kept drafts, first drafts kept by calibrated q bin. +pub const Stats = struct { + pub const bins = 10; + reached: [16]u64 = @splat(0), + kept: [16]u64 = @splat(0), + positions: usize, + q1: [bins][2]u64 = @splat(.{ 0, 0 }), + rounds: u64 = 0, + zero: u64 = 0, + joint: [4]u64 = @splat(0), // joint rounds, their rows, their drafts verified, zero-depth slots + + pub fn add(s: *Stats, q1: ?f64, verified: usize, keep: usize, first_kept: ?bool) void { + for (0..@min(@min(verified, keep), s.positions)) |j| { + s.reached[j] += 1; + s.kept[j] += @intFromBool(j + 1 < keep); + } + if (q1) |q| if (first_kept) |k| { + const b: usize = @min(@as(usize, @intFromFloat(q * bins)), bins - 1); + s.q1[b][0] += 1; + s.q1[b][1] += @intFromBool(k); + }; + } +}; + +test "the factor moves toward kept over summed confidence" { + const gpa = std.testing.allocator; + var c = try Calibration.init(gpa, 5); + defer c.deinit(gpa); + try std.testing.expectEqual(@as(f64, 1.0), c.factor(0)); + for (0..50) |_| c.record(&.{ 0.5, 0.5 }, 3); // both drafts kept every time at confidence 0.5 + try std.testing.expect(c.factor(0) > 1.5 and c.factor(9) == c.factor(4)); + try std.testing.expectEqual(q_max, c.q(0, 0.9)); +} diff --git a/zig/src/families/deepseek_v41/draft/calib_env.zig b/zig/src/families/deepseek_v41/draft/calib_env.zig new file mode 100644 index 0000000000..9505e344ac --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/calib_env.zig @@ -0,0 +1,251 @@ +//! The served engine's draft pricing: a stored calibration table picked by engine shape (TF_DSV41_CALIB), or defaults. +const std = @import("std"); +const Allocator = std.mem.Allocator; +const costs_mod = @import("costs.zig"); +const depth = @import("depth.zig"); +const joint = @import("joint.zig"); +const measure_mod = @import("calib_measure.zig"); +const Costs = costs_mod.Costs; + +pub const Mode = enum { cached, default, real, measure, zig }; + +/// The engine the table must have been measured on (slots, world, DSpark; context preferred). +pub const Shape = struct { slots: u32, world: u32, dspark: bool, context: ?u64 = null }; + +/// `measure`: the engine must measure (defaults until it has); `measured`: this boot's own measurement. +pub const Source = enum { default, cached, measure, measured }; + +/// Whose measurement an entry is: the reference engine's (no "engine" key) or Zig's (`"engine": "zig"`). +pub const Engine = enum { python, zig }; + +pub const Loaded = struct { + costs: Costs, + source: Source, + /// the entry read (gpa-owned; null for the defaults) + path: ?[]u8 = null, + + pub fn deinit(l: *Loaded, gpa: Allocator) void { + l.costs.deinit(gpa); + if (l.path) |p| gpa.free(p); + } +}; + +/// What the environment asks for (strings as read; null: unset). +pub const Knobs = struct { + mode: ?[]const u8 = null, + dir: ?[]const u8 = null, + file: ?[]const u8 = null, + home: ?[]const u8 = null, + + pub fn fromEnv() Knobs { + return .{ .mode = env("TF_DSV41_CALIB"), .dir = env("TF_DSV41_CALIB_DIR"), .file = env("TF_DSV41_CALIB_FILE"), .home = env("HOME") }; + } +}; + +pub fn mode(raw: ?[]const u8) !Mode { + const v = trimmed(raw) orelse return .cached; + var buf: [16]u8 = undefined; + if (v.len > buf.len) return error.CalibMode; + return std.meta.stringToEnum(Mode, std.ascii.lowerString(&buf, v)) orelse error.CalibMode; +} + +/// The cost table for `want` per `k`; `default_rows`: the defaults' row count (16, the served engine prices 64). +pub fn load(gpa: Allocator, io: std.Io, k: Knobs, want: Shape, default_rows: usize) !Loaded { + const m = try mode(k.mode); + switch (m) { + .default => return defaults(gpa, default_rows), + .measure => return measure(gpa, default_rows), + .cached, .real, .zig => {}, // real: the newest stored reference measurement + } + if (trimmed(k.file)) |f| return .{ .costs = try readEntry(gpa, io, f), .source = .cached, .path = try gpa.dupe(u8, f) }; + // nothing to read: the defaults, or (zig) a measurement + var dir_buf: [std.fs.max_path_bytes]u8 = undefined; + const dir = dirOf(k, &dir_buf) orelse return if (m == .zig) measure(gpa, default_rows) else defaults(gpa, default_rows); + const path = (try pickOf(gpa, io, dir, want, if (m == .zig) .zig else .python)) orelse return if (m == .zig) measure(gpa, default_rows) else defaults(gpa, default_rows); + errdefer gpa.free(path); + return .{ .costs = try readEntry(gpa, io, path), .source = .cached, .path = path }; +} + +/// TF_DSV41_CALIB_DIR, else ~/.cache/tensorfold/dsv41-calib (null: neither). +pub fn dirOf(k: Knobs, buf: []u8) ?[]const u8 { + return trimmed(k.dir) orelse std.fmt.bufPrint(buf, "{s}/.cache/tensorfold/dsv41-calib", .{trimmed(k.home) orelse return null}) catch null; +} + +fn measure(gpa: Allocator, rows: usize) !Loaded { + return .{ .costs = try costs_mod.defaults(gpa, rows), .source = .measure }; +} + +fn defaults(gpa: Allocator, rows: usize) !Loaded { + return .{ .costs = try costs_mod.defaults(gpa, rows), .source = .default }; +} + +/// One entry's table as the ranks share it (calib.load, then Costs.encode / decode). +pub fn readEntry(gpa: Allocator, io: std.Io, path: []const u8) !Costs { + const bytes = try std.Io.Dir.cwd().readFileAlloc(io, path, gpa, .limited(1 << 24)); + defer gpa.free(bytes); + var raw = try Costs.parseJson(gpa, bytes); + defer raw.deinit(gpa); + return shared(gpa, raw); +} + +/// `c` after rank 0's share: microsecond ints and back (`Costs.decode(costs.encode())`). +pub fn shared(gpa: Allocator, c: Costs) !Costs { + const ints = try c.encode(gpa); + defer gpa.free(ints); + return Costs.decode(gpa, ints); +} + +const Meta = struct { + engine: []const u8 = "", + meta: struct { + shape: struct { slots: u32 = 0, world: u32 = 0, dspark: bool = false, context: ?u64 = null } = .{}, + time: []const u8 = "", + } = .{}, +}; + +/// The directory's reference-engine `calib-*.json` for `want`'s engine shape; null: none. +pub fn pick(gpa: Allocator, io: std.Io, dir: []const u8, want: Shape) !?[]u8 { + return pickOf(gpa, io, dir, want, .python); +} + +/// `pick` over one engine's entries (`"engine": "zig"` or not). +pub fn pickOf(gpa: Allocator, io: std.Io, dir: []const u8, want: Shape, engine: Engine) !?[]u8 { + var d = std.Io.Dir.cwd().openDir(io, dir, .{ .iterate = true }) catch return null; + defer d.close(io); + var best: ?[]u8 = null; + errdefer if (best) |b| gpa.free(b); + var best_rank: u8 = 0; + var best_time: [32]u8 = undefined; + var best_time_len: usize = 0; + var it = d.iterate(); + while (try it.next(io)) |e| { + if (e.kind != .file and e.kind != .sym_link) continue; + if (!std.mem.startsWith(u8, e.name, "calib-") or !std.mem.endsWith(u8, e.name, ".json")) continue; + const path = try std.fs.path.join(gpa, &.{ dir, e.name }); + var keep = false; + defer if (!keep) gpa.free(path); + const bytes = std.Io.Dir.cwd().readFileAlloc(io, path, gpa, .limited(1 << 24)) catch continue; + defer gpa.free(bytes); + const parsed = std.json.parseFromSlice(Meta, gpa, bytes, .{ .ignore_unknown_fields = true }) catch continue; + defer parsed.deinit(); + const m = parsed.value.meta; + if (std.mem.eql(u8, parsed.value.engine, "zig") != (engine == .zig)) continue; + if (m.shape.world != want.world or m.shape.slots < want.slots or m.shape.dspark != want.dspark) continue; + // a usable table (calib.load's rule) before it can win + var c = Costs.parseJson(gpa, bytes) catch continue; + c.deinit(gpa); + const rank: u8 = 1 + @as(u8, if (m.shape.slots == want.slots) 2 else 0) + @intFromBool(want.context != null and m.shape.context == want.context); + const t = m.time[0..@min(m.time.len, best_time.len)]; + const newer = std.mem.order(u8, t, best_time[0..best_time_len]) == .gt; + if (best == null or rank > best_rank or (rank == best_rank and newer)) { + if (best) |b| gpa.free(b); + best = path; + keep = true; + best_rank = rank; + @memcpy(best_time[0..t.len], t); + best_time_len = t.len; + } + } + return best; +} + +/// A Zig measurement's file: calib-zig-.json (one per shape; a new one replaces it). +pub fn zigName(buf: []u8, want: Shape) ![]const u8 { + var key: [96]u8 = undefined; + const k = try std.fmt.bufPrint(&key, "slots={d} world={d} dspark={d} context={d}", .{ want.slots, want.world, @intFromBool(want.dspark), want.context orelse 0 }); + var h: [32]u8 = undefined; + std.crypto.hash.sha2.Sha256.hash(k, &h, .{}); + return std.fmt.bufPrint(buf, "calib-zig-{x}.json", .{h[0..8]}); +} + +/// What a Zig measurement stores beside its table (meta: method, seconds; rank 0's kept rows). +pub const Record = struct { + method: measure_mod.Method, + seconds: f64, + /// rank 0's rows 1..16 as kept (the lower of sweep and recheck), ms + kept: []const f64 = &.{}, + /// the calibration prompt's ids (rank 0's text, before the slot cuts) + tokens: usize = 0, +}; + +/// Writes `c` for `want` into `dir` as zigName atomically; returns its path (gpa-owned); `now_s` stamps meta.time. +pub fn store(gpa: Allocator, io: std.Io, dir: []const u8, want: Shape, c: Costs, rec: Record, now_s: i64) ![]u8 { + try std.Io.Dir.cwd().createDirPath(io, dir); + var nb: [64]u8 = undefined; + const path = try std.fs.path.join(gpa, &.{ dir, try zigName(&nb, want) }); + errdefer gpa.free(path); + const tmp = try std.fmt.allocPrint(gpa, "{s}.{d}.tmp", .{ path, std.c.getpid() }); + defer gpa.free(tmp); + var tb: [32]u8 = undefined; + const es: std.time.epoch.EpochSeconds = .{ .secs = @intCast(@max(0, now_s)) }; + const yd = es.getEpochDay().calculateYearDay(); + const md = yd.calculateMonthDay(); + const ds = es.getDaySeconds(); + const time = try std.fmt.bufPrint(&tb, "{d:0>4}-{d:0>2}-{d:0>2}T{d:0>2}:{d:0>2}:{d:0>2}", .{ yd.year, @backingInt(md.month), md.day_index + 1, ds.getHoursIntoDay(), ds.getMinutesIntoHour(), ds.getSecondsIntoMinute() }); + const m = rec.method; + const doc = .{ + .verify = c.verify, + .draft = c.draft, + .slot = c.slot, + .engine = "zig", + .meta = .{ + .shape = .{ .slots = want.slots, .world = want.world, .dspark = want.dspark, .context = want.context }, + .time = time, + .method = .{ .reps = m.reps, .warm = m.warm, .stat = @tagName(m.stat), .recheck = m.recheck, .shape = @tagName(m.shape), .cycle = m.cycle, .deep = m.deep }, + .seconds = rec.seconds, + .kept_rank0 = rec.kept, + .prompt_tokens = rec.tokens, + .engine = "zig", + }, + }; + const bytes = try std.json.Stringify.valueAlloc(gpa, doc, .{ .whitespace = .indent_1 }); + defer gpa.free(bytes); + try std.Io.Dir.cwd().writeFile(io, .{ .sub_path = tmp, .data = bytes }); + errdefer std.Io.Dir.cwd().deleteFile(io, tmp) catch {}; + try std.Io.Dir.cwd().rename(tmp, std.Io.Dir.cwd(), path, io); + return path; +} + +/// The depth's knobs (TF_DSV41_DEPTH, _DRAFT_DEPTH, _DRAFT_SKIP, _DEPTH_JOINT, _DEPTH_FAIR, _DEPTH_HOST_MS, rows cap). +pub fn depthSettings(get: *const fn ([*:0]const u8) ?[]const u8) !depth.Settings { + var s: depth.Settings = .{}; + if (trimmed(get("TF_DSV41_CODE_ACCEPT"))) |v| s.code_accept = try flag(v, error.CodeAccept); + if (s.code_accept) if (trimmed(get("TF_DSV41_GRAPH_BUCKET"))) |v| { + s.context_bucket = try std.fmt.parseInt(u32, v, 10); + if (s.context_bucket == 0) return error.BadGraphBucket; + }; + const m = trimmed(get("TF_DSV41_DEPTH")) orelse "cost"; + s.mode = if (std.ascii.eqlIgnoreCase(m, "cost")) .cost else if (std.ascii.eqlIgnoreCase(m, "static")) .static else return error.DepthMode; + if (trimmed(get("TF_DSV41_DRAFT_DEPTH"))) |v| { + s.cap = try std.fmt.parseInt(usize, v, 10); + if (s.cap > 15) return error.DraftDepth; + } else s.cap = if (s.mode == .cost) 5 else 3; + if (trimmed(get("TF_DSV41_DRAFT_SKIP"))) |v| s.skip = try flag(v, error.DraftSkip); + if (trimmed(get("TF_DSV41_DEPTH_JOINT"))) |v| s.joint = if (std.mem.eql(u8, v, "2")) 2 else @intFromBool(try flag(v, error.DepthJoint)); + s.fair = try float(get("TF_DSV41_DEPTH_FAIR"), joint.fair, 0.0, 4.0); + s.host_ms = try float(get("TF_DSV41_DEPTH_HOST_MS"), joint.host_ms, 0.0, 100.0); + if (trimmed(get("TF_DSV41_GRAPH_ROWS_MAX"))) |v| s.max_rows = @max(1, @min(try std.fmt.parseInt(u64, v, 10), costs_mod.max_rows)); + return s; +} + +fn flag(v: []const u8, bad: anyerror) !bool { + for ([_][]const u8{ "1", "on", "true" }) |t| if (std.ascii.eqlIgnoreCase(v, t)) return true; + for ([_][]const u8{ "0", "off", "false" }) |t| if (std.ascii.eqlIgnoreCase(v, t)) return false; + return bad; +} + +fn float(raw: ?[]const u8, default: f64, lo: f64, hi: f64) !f64 { + const v = trimmed(raw) orelse return default; + const x = try std.fmt.parseFloat(f64, v); + if (x < lo or x > hi) return error.DepthKnob; + return x; +} + +pub fn env(name: [*:0]const u8) ?[]const u8 { + return if (std.c.getenv(name)) |v| std.mem.span(v) else null; +} + +fn trimmed(v: ?[]const u8) ?[]const u8 { + const t = std.mem.trim(u8, v orelse return null, " \t\r\n"); + return if (t.len == 0) null else t; +} diff --git a/zig/src/families/deepseek_v41/draft/calib_env_test.zig b/zig/src/families/deepseek_v41/draft/calib_env_test.zig new file mode 100644 index 0000000000..97cf7eae87 --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/calib_env_test.zig @@ -0,0 +1,180 @@ +//! calib_env.zig against fixtures/calib-golden.json: same entry picked, same shared table bits, same depth and tree. +const std = @import("std"); +const Value = std.json.Value; +const calib_env = @import("calib_env.zig"); +const depth = @import("depth.zig"); +const costs_mod = @import("costs.zig"); + +const gpa = std.testing.allocator; +const io = std.testing.io; +const golden = @embedFile("fixtures/calib-golden.json"); + +fn num(v: Value) f64 { + return switch (v) { + .float => |f| f, + .integer => |i| @floatFromInt(i), + else => unreachable, + }; +} + +test "a served cached calibration: the entry, the shared table and every depth choice equal Python's" { + var parsed = try std.json.parseFromSlice(Value, gpa, golden, .{}); + defer parsed.deinit(); + const g = parsed.value.object; + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + const a = arena.allocator(); + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + for (g.get("files").?.array.items) |f| { + const content = try std.json.Stringify.valueAlloc(a, f.object.get("content").?, .{}); + try tmp.dir.writeFile(io, .{ .sub_path = f.object.get("name").?.string, .data = content }); + } + const root = try std.fmt.allocPrint(a, ".zig-cache/tmp/{s}", .{tmp.sub_path}); + const w = g.get("want").?.object; + const want: calib_env.Shape = .{ .slots = @intCast(w.get("slots").?.integer), .world = @intCast(w.get("world").?.integer), .dspark = w.get("dspark").?.bool, .context = @intCast(w.get("context").?.integer) }; + var got = try calib_env.load(gpa, io, .{ .dir = root }, want, 16); + defer got.deinit(gpa); + try std.testing.expectEqual(calib_env.Source.cached, got.source); + try std.testing.expectEqualStrings(g.get("pick").?.string, std.fs.path.basename(got.path.?)); + const c = g.get("costs").?.object; + const verify = c.get("verify").?.array.items; + try std.testing.expectEqual(verify.len, got.costs.verify.len); + for (verify, got.costs.verify) |x, y| try std.testing.expectEqual(num(x), y); + try std.testing.expectEqual(num(c.get("draft").?), got.costs.draft); + try std.testing.expectEqual(num(c.get("slot").?), got.costs.slot); + // every ask on a fresh depth (block=5, mode="cost", cap=5, skip=True, joint=1) + var trees: usize = 0; + for (g.get("asks").?.array.items) |ask| { + const o = ask.object; + var d = try depth.Depth.init(gpa, got.costs, 5, .{}); + defer d.deinit(); + var conf: [5]f64 = undefined; + for (&conf, o.get("conf").?.array.items) |*q, x| q.* = num(x); + const most: usize = @intCast(o.get("most").?.integer); + const first: ?u32 = if (o.get("first").? == .null) null else @intCast(o.get("first").?.integer); + if (o.get("sibs")) |sv| { + var sibs: [3]f64 = undefined; + for (sv.array.items, 0..) |x, i| sibs[i] = num(x); + const t = try d.chooseTree(0, &conf, most, sibs[0..sv.array.items.len], true, first, .{ .ms = 0.4, .rows = true }, 4); + try std.testing.expectEqual(@as(usize, @intCast(o.get("k").?.integer)), t.k); + for (o.get("lens").?.array.items, 0..) |l, i| try std.testing.expectEqual(@as(usize, @intCast(l.integer)), t.lens[i]); + trees += 1; + } else try std.testing.expectEqual(@as(usize, @intCast(o.get("k").?.integer)), try d.choose(0, &conf, most, true, first)); + } + try std.testing.expect(trees > 0); +} + +test "no entry for this engine: the defaults; more slots fit; a named file wins; the modes" { + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + var buf: [256]u8 = undefined; + const root = try std.fmt.bufPrint(&buf, ".zig-cache/tmp/{s}", .{tmp.sub_path}); + const want: calib_env.Shape = .{ .slots = 1, .world = 2, .dspark = true }; + var none = try calib_env.load(gpa, io, .{ .dir = root }, want, 16); + defer none.deinit(gpa); + try std.testing.expectEqual(calib_env.Source.default, none.source); + try std.testing.expectEqual(@as(f64, 42.0), none.costs.verify[0]); // depth.default_costs: 37 + 5 a row + try tmp.dir.writeFile(io, .{ .sub_path = "calib-x.json", .data = "{\"verify\": [20.0004999, 25.0005], \"draft\": 3.0, \"meta\": {\"shape\": {\"slots\": 1, \"world\": 1, \"dspark\": true}}}" }); + var wrong = try calib_env.load(gpa, io, .{ .dir = root }, want, 16); // world 1: not ours + defer wrong.deinit(gpa); + try std.testing.expectEqual(calib_env.Source.default, wrong.source); + var p: [300]u8 = undefined; + var named = try calib_env.load(gpa, io, .{ .dir = root, .file = try std.fmt.bufPrint(&p, "{s}/calib-x.json", .{root}) }, want, 16); + defer named.deinit(gpa); + try std.testing.expectEqualSlices(f64, &.{ 20.0, 25.0 }, named.costs.verify); // microseconds, half to even + var dflt = try calib_env.load(gpa, io, .{ .mode = "Default", .dir = root }, want, 16); + defer dflt.deinit(gpa); + try std.testing.expectEqual(calib_env.Source.default, dflt.source); + // TF_DSV41_CALIB=real with a 4-slot table: a 1-slot engine reads its newest + try tmp.dir.writeFile(io, .{ .sub_path = "calib-p.json", .data = "{\"verify\": [21.0, 26.0], \"draft\": 3.6, \"meta\": {\"shape\": {\"slots\": 4, \"world\": 2, \"dspark\": true}, \"time\": \"2026-10-07T12:00:00\"}}" }); + try tmp.dir.writeFile(io, .{ .sub_path = "calib-q.json", .data = "{\"verify\": [22.0, 27.0], \"draft\": 3.6, \"meta\": {\"shape\": {\"slots\": 4, \"world\": 2, \"dspark\": true}, \"time\": \"2026-10-06T12:00:00\"}}" }); + var served = try calib_env.load(gpa, io, .{ .mode = "real", .dir = root }, want, 16); + defer served.deinit(gpa); + try std.testing.expectEqualStrings("calib-p.json", std.fs.path.basename(served.path.?)); + try std.testing.expectError(error.CalibMode, calib_env.load(gpa, io, .{ .mode = "measured" }, want, 16)); +} + +test "code acceptance is default off and rejects invalid flags" { + Env.pairs = &.{}; + try std.testing.expect(!(try calib_env.depthSettings(&Env.get)).code_accept); + Env.pairs = &.{.{ "TF_DSV41_CODE_ACCEPT", "on" }}; + try std.testing.expect((try calib_env.depthSettings(&Env.get)).code_accept); + Env.pairs = &.{.{ "TF_DSV41_CODE_ACCEPT", "maybe" }}; + try std.testing.expectError(error.CodeAccept, calib_env.depthSettings(&Env.get)); + Env.pairs = &.{}; +} + +const Env = struct { + var pairs: []const [2][]const u8 = &.{}; + fn get(name: [*:0]const u8) ?[]const u8 { + for (pairs) |kv| if (std.mem.eql(u8, kv[0], std.mem.span(name))) return kv[1]; + return null; + } +}; + +test "the depth's knobs read as Python's" { + Env.pairs = &.{}; + const d = try calib_env.depthSettings(&Env.get); + try std.testing.expect(d.mode == .cost and d.cap == 5 and d.skip and d.joint == 1); + Env.pairs = &.{ .{ "TF_DSV41_DEPTH", "static" }, .{ "TF_DSV41_DRAFT_SKIP", "off" }, .{ "TF_DSV41_DEPTH_JOINT", "2" }, .{ "TF_DSV41_DEPTH_FAIR", "1.5" }, .{ "TF_DSV41_GRAPH_ROWS_MAX", "32" } }; + const s = try calib_env.depthSettings(&Env.get); + try std.testing.expect(s.mode == .static and s.cap == 3 and !s.skip and s.joint == 2 and s.fair == 1.5 and s.max_rows == 32); + Env.pairs = &.{.{ "TF_DSV41_DRAFT_DEPTH", "16" }}; + try std.testing.expectError(error.DraftDepth, calib_env.depthSettings(&Env.get)); + Env.pairs = &.{.{ "TF_DSV41_DEPTH_JOINT", "3" }}; + try std.testing.expectError(error.DepthJoint, calib_env.depthSettings(&Env.get)); +} + +test "TF_DSV41_CALIB=measure / zig: the engine measures; a stored Zig table is read back by zig only" { + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + var buf: [256]u8 = undefined; + const root = try std.fmt.bufPrint(&buf, ".zig-cache/tmp/{s}", .{tmp.sub_path}); + const want: calib_env.Shape = .{ .slots = 4, .world = 2, .dspark = true, .context = 1048576 }; + var m = try calib_env.load(gpa, io, .{ .mode = "Measure", .dir = root }, want, 64); + defer m.deinit(gpa); + try std.testing.expectEqual(calib_env.Source.measure, m.source); + try std.testing.expectEqual(@as(usize, 64), m.costs.verify.len); // the defaults until measured + var none = try calib_env.load(gpa, io, .{ .mode = "zig", .dir = root }, want, 64); + defer none.deinit(gpa); + try std.testing.expectEqual(calib_env.Source.measure, none.source); + // a reference-engine entry for the shape: cached reads it, zig does not + try tmp.dir.writeFile(io, .{ .sub_path = "calib-py.json", .data = "{\"verify\": [21.0, 26.0], \"draft\": 3.6, \"meta\": {\"shape\": {\"slots\": 4, \"world\": 2, \"dspark\": true}, \"time\": \"2026-10-07T12:00:00\"}}" }); + var verify = [_]f64{ 20.0004999, 24.5, 24.25, 30.0 }; + const c: costs_mod.Costs = .{ .verify = &verify, .draft = 3.5005, .slot = 0.611 }; + const kept = [_]f64{ 20.0, 24.4 }; + const path = try calib_env.store(gpa, io, root, want, c, .{ .method = .{}, .seconds = 7.25, .kept = &kept, .tokens = 201 }, 1_791_504_000); + defer gpa.free(path); + var nb: [64]u8 = undefined; + const name = try calib_env.zigName(&nb, want); + try std.testing.expect(std.mem.startsWith(u8, name, "calib-zig-") and name.len == "calib-zig-.json".len + 16); + try std.testing.expectEqualStrings(name, std.fs.path.basename(path)); + var z = try calib_env.load(gpa, io, .{ .mode = "zig", .dir = root }, want, 64); + defer z.deinit(gpa); + try std.testing.expectEqual(calib_env.Source.cached, z.source); + try std.testing.expectEqualStrings(name, std.fs.path.basename(z.path.?)); + try std.testing.expectEqualSlices(f64, &.{ 20.0, 24.5, 24.25, 30.0 }, z.costs.verify); // shared: microseconds + try std.testing.expectEqual(@as(f64, 3.5), z.costs.draft); // 3500.5 us: half to even + try std.testing.expectEqual(@as(f64, 0.611), z.costs.slot); + var py = try calib_env.load(gpa, io, .{ .mode = "cached", .dir = root }, want, 64); + defer py.deinit(gpa); + try std.testing.expectEqualStrings("calib-py.json", std.fs.path.basename(py.path.?)); // never the Zig entry + var real = try calib_env.load(gpa, io, .{ .mode = "real", .dir = root }, .{ .slots = 1, .world = 2, .dspark = true }, 64); + defer real.deinit(gpa); + try std.testing.expectEqualStrings("calib-py.json", std.fs.path.basename(real.path.?)); + // the stored file: the reference keys, the engine, a UTC time + const bytes = try tmp.dir.readFileAlloc(io, name, gpa, .limited(1 << 20)); + defer gpa.free(bytes); + const F = struct { engine: []const u8, meta: struct { time: []const u8, seconds: f64, prompt_tokens: u64, shape: struct { slots: u32, context: ?u64 }, method: struct { stat: []const u8, shape: []const u8, reps: u32 } } }; + const f = try std.json.parseFromSlice(F, gpa, bytes, .{ .ignore_unknown_fields = true }); + defer f.deinit(); + try std.testing.expectEqualStrings("zig", f.value.engine); + try std.testing.expectEqualStrings("2026-10-09T00:00:00", f.value.meta.time); + try std.testing.expectEqual(@as(u64, 1048576), f.value.meta.shape.context.?); + try std.testing.expectEqualStrings("median", f.value.meta.method.stat); + // another shape's Zig entry is not ours + var other = try calib_env.load(gpa, io, .{ .mode = "zig", .dir = root }, .{ .slots = 4, .world = 1, .dspark = true }, 64); + defer other.deinit(gpa); + try std.testing.expectEqual(calib_env.Source.measure, other.source); +} diff --git a/zig/src/families/deepseek_v41/draft/calib_measure.zig b/zig/src/families/deepseek_v41/draft/calib_measure.zig new file mode 100644 index 0000000000..b227b37be8 --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/calib_measure.zig @@ -0,0 +1,346 @@ +//! TF_DSV41_CALIB=measure host side: the boot calibration run plan, its samples -> `Costs`; calib_gpu.zig runs it. +const std = @import("std"); +const Allocator = std.mem.Allocator; +const costs_mod = @import("costs.zig"); +const Costs = costs_mod.Costs; + +pub const max_rows = 16; // depth.MAX_ROWS: rows 1..16 each timed +pub const early = 8; // EARLY: rows timed with WARM warm-ups and REPS runs +pub const light_reps = 3; // REPS: the wide buckets and the DSpark pass, after one warm-up +pub const cut = 7; // CUT: slot i's text is the calibration text less its last CUT x i tokens +pub const slot_rows = costs_mod.slot_rows; // SLOT_ROWS +pub const wide = costs_mod.wide; // WIDE +pub const deep_from = 8; // rows past this fit a line of their own (the `fit` shape) +pub const max_samples = 64; // a statistic's runs at most (REPS / DEEP_REPS <= 50) + +pub const Shape = enum { raw, fit }; +pub const Stat = enum { median, min }; + +/// The timing method (TF_DSV41_CALIB_SHAPE / _REPS / _WARM / _STAT / _RECHECK / _CYCLE / _DEEP_REPS). +pub const Method = struct { + reps: u32 = 5, + warm: u32 = 3, + stat: Stat = .median, + recheck: u32 = 4, + shape: Shape = .raw, + cycle: u32 = 4, + deep: u32 = 5, +}; + +/// The defaults by shape (raw: G15; fit: G14, VERSION 3), then the explicit knobs, each range-checked. +pub fn method(get: *const fn ([*:0]const u8) ?[]const u8) !Method { + const shape_s = trimmed(get("TF_DSV41_CALIB_SHAPE")) orelse "raw"; + const shape: Shape = if (std.ascii.eqlIgnoreCase(shape_s, "raw")) .raw else if (std.ascii.eqlIgnoreCase(shape_s, "fit")) .fit else return error.CalibShape; + const d: Method = if (shape == .raw) .{} else .{ .shape = .fit, .warm = 2, .recheck = 2, .cycle = 0, .deep = 3 }; + const stat_s = trimmed(get("TF_DSV41_CALIB_STAT")) orelse "median"; + const stat: Stat = if (std.ascii.eqlIgnoreCase(stat_s, "median")) .median else if (std.ascii.eqlIgnoreCase(stat_s, "min")) .min else return error.CalibStat; + return .{ + .reps = try num(get("TF_DSV41_CALIB_REPS"), 5, 1, 50), + .warm = try num(get("TF_DSV41_CALIB_WARM"), d.warm, 0, 20), + .stat = stat, + .recheck = try num(get("TF_DSV41_CALIB_RECHECK"), d.recheck, 0, early), + .shape = shape, + .cycle = try num(get("TF_DSV41_CALIB_CYCLE"), d.cycle, 0, early), + .deep = try num(get("TF_DSV41_CALIB_DEEP_REPS"), d.deep, 1, 50), + }; +} + +fn num(raw: ?[]const u8, default: u32, lo: u32, hi: u32) !u32 { + const v: i64 = if (trimmed(raw)) |s| try std.fmt.parseInt(i64, s, 10) else default; + if (v < lo or v > hi) return error.CalibKnob; + return @intCast(v); +} + +fn trimmed(v: ?[]const u8) ?[]const u8 { + const t = std.mem.trim(u8, v orelse return null, " \t\r\n"); + return if (t.len == 0) null else t; +} + +/// The median (the mean of the middle two when even) or the minimum of a window's timed runs. +pub fn statistic(samples: []const f64, stat: Stat) !f64 { + if (samples.len == 0) return error.NoRuns; + if (samples.len > max_samples) return error.TooManyRuns; + var buf: [max_samples]f64 = undefined; + const xs = buf[0..samples.len]; + @memcpy(xs, samples); + std.mem.sort(f64, xs, {}, std.sort.asc(f64)); + if (stat == .min) return xs[0]; + const m = xs.len / 2; + return if (xs.len % 2 == 1) xs[m] else (xs[m - 1] + xs[m]) / 2.0; +} + +/// The median of a slice (sorted in place). +fn median(xs: []f64) f64 { + std.mem.sort(f64, xs, {}, std.sort.asc(f64)); + const m = xs.len / 2; + return if (xs.len % 2 == 1) xs[m] else (xs[m - 1] + xs[m]) / 2.0; +} + +/// The median of pairwise slopes, then the median intercept. +pub fn fitLine(gpa: Allocator, rows: []const f64, ys: []const f64) !struct { base: f64, slope: f64 } { + var slopes: std.ArrayList(f64) = .empty; + defer slopes.deinit(gpa); + for (0..rows.len) |i| for (i + 1..rows.len) |j| try slopes.append(gpa, (ys[j] - ys[i]) / (rows[j] - rows[i])); + const slope = median(slopes.items); + const bases = try gpa.alloc(f64, rows.len); + defer gpa.free(bases); + for (bases, rows, ys) |*b, r, y| b.* = y - slope * r; + return .{ .base = median(bases), .slope = slope }; +} + +/// VERSION 3's table: 1 row as measured, 2..8 on a fitted line, 9+ on their own line from the 8-row value, monotone. +pub fn fit(gpa: Allocator, v: []const f64) ![]f64 { + if (v.len < 3) return gpa.dupe(f64, v); + const first = @min(v.len, deep_from); + var xs: [max_rows]f64 = undefined; + var ys: [max_rows]f64 = undefined; + if (v.len > max_rows) return error.TooManyRows; + for (2..first + 1, 0..) |r, i| { + xs[i] = @floatFromInt(r); + ys[i] = v[r - 1]; + } + const l = try fitLine(gpa, xs[0 .. first - 1], ys[0 .. first - 1]); + const out = try gpa.alloc(f64, v.len); + errdefer gpa.free(out); + out[0] = v[0]; + for (2..first + 1) |r| out[r - 1] = l.base + l.slope * @as(f64, @floatFromInt(r)); + if (v.len > first) { + for (first..v.len + 1, 0..) |r, i| { + xs[i] = @floatFromInt(r); + ys[i] = v[r - 1]; + } + const l2 = try fitLine(gpa, xs[0 .. v.len - first + 1], ys[0 .. v.len - first + 1]); + for (first + 1..v.len + 1) |r| out[r - 1] = out[first - 1] + @max(l2.slope, 0.0) * @as(f64, @floatFromInt(r - first)); + } + for (1..out.len) |i| out[i] = @max(out[i], out[i - 1]); + return out; +} + +/// `raw` = monotone, `fit` = fit. +pub fn tableOf(gpa: Allocator, times: []const f64, shape: Shape) ![]f64 { + if (shape == .fit) return fit(gpa, times); + const t = try gpa.dupe(f64, times); + errdefer gpa.free(t); + try costs_mod.monotone(gpa, t); + return t; +} + +/// `total` rows over `out.len` slots, as even as can be (the first slots one more). +pub fn split(total: u32, out: []u32) void { + const n: u32 = @intCast(out.len); + for (out, 0..) |*o, i| o.* = total / n + @intFromBool(i < total % n); +} + +/// The wide buckets `slots` slots can time (at most SLOT_ROWS rows a slot), past `rows`. +pub fn wideBuckets(slots: u32, rows_cap: u64, rows: u32, out: *[wide.len]u32) []u32 { + var n: usize = 0; + const top = @min(rows_cap, @as(u64, slot_rows) * @max(1, slots)); + for (wide) |b| if (rows < b and b <= top) { + out[n] = b; + n += 1; + }; + return out[0..n]; +} + +fn ceilDiv(a: u32, b: u32) u32 { + return (a + b - 1) / b; +} + +/// One timed run's segments (slot, rows), a DSpark pass when `draft`. +pub const Seg = struct { slot: u32, rows: u32 }; +pub const What = union(enum) { sweep: u32, recheck: u32, shared: u32, draft }; +pub const Run = struct { + segs: [4]Seg = undefined, + nsegs: u8 = 0, + warm: bool, + what: What, + + pub fn segments(r: *const Run) []const Seg { + return r.segs[0..r.nsegs]; + } +}; + +/// The measurement's timed runs, in the reference order. +pub const Plan = struct { + rows: u32, + /// the slots the windows use + n: u32, + wide_buf: [wide.len]u32 = undefined, + nwide: u32 = 0, + /// the 2-slot 4 + 4 window (Costs.slot) is timed + per_slot: bool, + draft: bool, + m: Method, + runs: std.ArrayList(Run) = .empty, + + /// `slots`: engine slots; `rows_cap`: TF_DSV41_GRAPH_ROWS_MAX (64); `rows`: widest window (16); `draft`: DSpark. + pub fn init(gpa: Allocator, slots: u32, rows_cap: u64, rows: u32, m: Method, draft: bool) !Plan { + if (rows == 0 or rows > max_rows) return error.BadRows; + var p: Plan = .{ .rows = rows, .n = 1, .per_slot = false, .draft = draft, .m = m }; + errdefer p.runs.deinit(gpa); + const w = wideBuckets(slots, rows_cap, rows, &p.wide_buf); + p.nwide = @intCast(w.len); + const want: u32 = if (w.len > 0) @max(2, ceilDiv(w[w.len - 1], slot_rows)) else 2; + p.n = @min(@max(1, slots), want); + p.per_slot = p.n >= 2 and rows >= 8; + try p.block(gpa, @min(rows, early), false); + var r: u32 = early + 1; + while (r <= rows) : (r += 1) try p.add(gpa, &.{.{ .slot = 0, .rows = r }}, .{ .sweep = r }, 1, m.deep); + var j: u32 = 0; + if (p.per_slot) { + try p.add(gpa, &.{ .{ .slot = 0, .rows = 4 }, .{ .slot = 1, .rows = 4 } }, .{ .shared = j }, 1, light_reps); + j += 1; + } + for (w) |b| { + const k = ceilDiv(b, slot_rows); + var parts: [4]u32 = undefined; + split(b, parts[0..k]); + var segs: [4]Seg = undefined; + for (segs[0..k], parts[0..k], 0..) |*s, x, i| s.* = .{ .slot = @intCast(i), .rows = x }; + try p.add(gpa, segs[0..k], .{ .shared = j }, 1, light_reps); + j += 1; + } + try p.block(gpa, @min(m.recheck, rows), true); + if (draft) try p.add(gpa, &.{}, .draft, 1, light_reps); + return p; + } + + pub fn deinit(p: *Plan, gpa: Allocator) void { + p.runs.deinit(gpa); + } + + pub fn wideBucketsOf(p: *const Plan) []const u32 { + return p.wide_buf[0..p.nwide]; + } + + /// Entries a rank gathers: [draft, rows 1..rows, the shared windows...]. + pub fn entries(p: *const Plan) usize { + return 1 + p.rows + p.nwide + @intFromBool(p.per_slot); + } + + /// Rows 1..min(cycle, hi) as interleaved cycles (`warm` dropped, `reps` timed), the rest up to `hi` row by row. + fn block(p: *Plan, gpa: Allocator, hi: u32, recheck: bool) !void { + const cyc = @min(p.m.cycle, hi); + for (0..p.m.warm + p.m.reps) |k| { + var r: u32 = 1; + while (r <= cyc) : (r += 1) try p.runs.append(gpa, one(r, k < p.m.warm, recheck)); + } + var r: u32 = cyc + 1; + while (r <= hi) : (r += 1) for (0..p.m.warm + p.m.reps) |k| try p.runs.append(gpa, one(r, k < p.m.warm, recheck)); + } + + fn one(r: u32, warm: bool, recheck: bool) Run { + var x: Run = .{ .warm = warm, .what = if (recheck) .{ .recheck = r } else .{ .sweep = r } }; + x.segs[0] = .{ .slot = 0, .rows = r }; + x.nsegs = 1; + return x; + } + + fn add(p: *Plan, gpa: Allocator, segs: []const Seg, what: What, warm: u32, reps: u32) !void { + for (0..warm + reps) |k| { + var x: Run = .{ .warm = k < warm, .what = what, .nsegs = @intCast(segs.len) }; + @memcpy(x.segs[0..segs.len], segs); + try p.runs.append(gpa, x); + } + } + + /// One rank's kept ms [draft, rows 1..rows (lower of sweep, recheck), shared...] from `runs`-order samples. + pub fn values(p: *const Plan, samples: []const f64, out: []f64) !void { + if (samples.len != p.runs.items.len or out.len != p.entries()) return error.BadSamples; + var buf: [max_samples]f64 = undefined; + // each entry's timed samples in run order: rows (sweep), rows (recheck), shared, draft + out[0] = 0.0; + for (0..p.rows) |i| out[1 + i] = (try p.stat(samples, .{ .sweep = @intCast(i + 1) }, &buf)) orelse return error.NoRuns; + for (0..p.rows) |i| if (try p.stat(samples, .{ .recheck = @intCast(i + 1) }, &buf)) |x| { + out[1 + i] = @min(out[1 + i], x); + }; + const ns = p.nwide + @intFromBool(p.per_slot); + for (0..ns) |j| out[1 + p.rows + j] = (try p.stat(samples, .{ .shared = @intCast(j) }, &buf)) orelse return error.NoRuns; + if (p.draft) out[0] = (try p.stat(samples, .draft, &buf)) orelse return error.NoRuns; + } + + fn stat(p: *const Plan, samples: []const f64, what: What, buf: *[max_samples]f64) !?f64 { + var n: usize = 0; + for (p.runs.items, samples) |r, x| if (!r.warm and std.meta.eql(r.what, what)) { + if (n == buf.len) return error.TooManyRuns; + buf[n] = x; + n += 1; + }; + if (n == 0) return null; + return try statistic(buf[0..n], p.m.stat); + } + + /// The shared table: per-rank microsecond ints, max over ranks, table_of, Costs.slot, wide buckets, extended. + pub fn build(p: *const Plan, gpa: Allocator, ranks: []const []const f64) !Costs { + const e = p.entries(); + const both = try gpa.alloc(f64, e); + defer gpa.free(both); + for (0..e) |i| { + var hi: i64 = std.math.minInt(i64); + for (ranks) |v| { + if (v.len != e) return error.BadSamples; + hi = @max(hi, costs_mod.roundEven(v[i] * 1000.0)); + } + both[i] = @as(f64, @floatFromInt(hi)) / 1000.0; + } + const table = try tableOf(gpa, both[1 .. p.rows + 1], p.m.shape); + defer gpa.free(table); + var rest = both[p.rows + 1 ..]; + var per_slot: f64 = 0.0; + if (p.per_slot) { + per_slot = @max(0.0, rest[0] - table[7]); + rest = rest[1..]; + } + var pts: [wide.len]costs_mod.Point = undefined; + for (p.wideBucketsOf(), rest, pts[0..p.nwide]) |b, t, *pt| pt.* = .{ .rows = b, .ms = t - per_slot * @as(f64, @floatFromInt(ceilDiv(b, slot_rows) - 1)) }; + return .{ .verify = try costs_mod.extend(gpa, table, pts[0..p.nwide]), .draft = both[0], .slot = per_slot }; + } +}; + +/// The calibration text (prose, a numbered explanation and code); calib_gpu.zig tokenizes it, first PROMPT_MAX ids. +pub const text = + \\The committee met on Tuesday to review the quarterly results. Revenue grew by eleven percent, driven mostly + \\by the new subscription plans, while operating costs stayed flat. Two questions remained open: whether to expand + \\the support team before the summer, and how to price the enterprise tier for customers with more than fifty seats. + \\ + \\Here is a short function that answers the second question from the usage data: + \\ + \\```python + \\def enterprise_price(seats: int, base: float = 12.0, discount: float = 0.15) -> float: + \\ """Monthly price for a team: the first fifty seats at the base rate, the rest discounted.""" + \\ if seats <= 50: + \\ return seats * base + \\ return 50 * base + (seats - 50) * base * (1.0 - discount) + \\ + \\ + \\for team in (10, 50, 120): + \\ print(f"{team} seats: ${enterprise_price(team):,.2f} a month") + \\``` + \\ + \\In summary, the pricing keeps small teams unchanged and rewards larger ones, and the +; +pub const prompt_max = 256; + +/// Ids below `vocab` (filtered in place), the first PROMPT_MAX, or `fallbackIds` into `fb` when fewer than 16 remain. +pub fn promptIds(ids: []u32, vocab: u32, fb: *[64]u32) []const u32 { + var n: usize = 0; + for (ids) |t| if (t < vocab and n < prompt_max) { + ids[n] = t; + n += 1; + }; + if (n >= 16) return ids[0..n]; + return fallbackIds(vocab, fb); +} + +/// 64 ids spread over the vocabulary by a fixed rule. +pub fn fallbackIds(vocab: u32, out: *[64]u32) []const u32 { + const m: u64 = @intCast(@max(@as(i64, vocab) - 2, 1)); + for (out, 0..) |*o, i| o.* = @intCast((7919 * (@as(u64, i) + 1) + 104729) % m + 1); + return out; +} + +/// Slot i's prompt is `prompt` less its last CUT x i tokens (when more than 8 remain). +pub fn cutOf(prompt_len: usize, i: u32) usize { + const c = @as(usize, cut) * i; + return if (prompt_len > c + 8) prompt_len - c else prompt_len; +} diff --git a/zig/src/families/deepseek_v41/draft/calib_measure_test.zig b/zig/src/families/deepseek_v41/draft/calib_measure_test.zig new file mode 100644 index 0000000000..86f2e3300e --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/calib_measure_test.zig @@ -0,0 +1,183 @@ +//! calib_measure.zig against fixtures/calib-measure-golden.json: same runs, same gathered ints, same Costs bits. +const std = @import("std"); +const Value = std.json.Value; +const cm = @import("calib_measure.zig"); +const costs_mod = @import("costs.zig"); + +const gpa = std.testing.allocator; +const golden = @embedFile("fixtures/calib-measure-golden.json"); + +fn num(v: Value) f64 { + return switch (v) { + .float => |f| f, + .integer => |i| @floatFromInt(i), + else => unreachable, + }; +} + +fn floats(a: std.mem.Allocator, v: Value) ![]f64 { + const out = try a.alloc(f64, v.array.items.len); + for (out, v.array.items) |*o, x| o.* = num(x); + return out; +} + +const Env = struct { + var obj: ?std.json.ObjectMap = null; + fn get(name: [*:0]const u8) ?[]const u8 { + const o = obj orelse return null; + return if (o.get(std.mem.span(name))) |v| v.string else null; + } +}; + +fn methodOf(env: Value) !cm.Method { + Env.obj = env.object; + defer Env.obj = null; + return cm.method(&Env.get); +} + +test "the measurement equals Python's calib.measure: runs, gathered ints and the table" { + var parsed = try std.json.parseFromSlice(Value, gpa, golden, .{}); + defer parsed.deinit(); + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + const a = arena.allocator(); + const cases = parsed.value.object.get("cases").?.array.items; + try std.testing.expect(cases.len >= 5); + for (cases) |cv| { + const c = cv.object; + const m = try methodOf(c.get("env").?); + const slots: u32 = @intCast(c.get("slots").?.integer); + var p = try cm.Plan.init(gpa, slots, @intCast(c.get("cap").?.integer), cm.max_rows, m, c.get("draft").?.bool); + defer p.deinit(gpa); + try std.testing.expectEqual(@as(u32, @intCast(c.get("n").?.integer)), p.n); + const wide = c.get("wide").?.array.items; + try std.testing.expectEqual(wide.len, p.wideBucketsOf().len); + for (wide, p.wideBucketsOf()) |w, b| try std.testing.expectEqual(@as(u32, @intCast(w.integer)), b); + // the same runs in the same order: each window's (slot, rows) segments, or the DSpark pass + const calls = c.get("calls").?.array.items; + try std.testing.expectEqual(calls.len, p.runs.items.len); + for (calls, p.runs.items) |call, run| { + if (call == .string) { + try std.testing.expectEqualStrings("draft", call.string); + try std.testing.expect(run.what == .draft and run.nsegs == 0); + continue; + } + const segs = call.array.items; + try std.testing.expectEqual(segs.len, run.nsegs); + for (segs, run.segments()) |s, r| { + try std.testing.expectEqual(@as(u32, @intCast(s.array.items[0].integer)), r.slot); + try std.testing.expectEqual(@as(u32, @intCast(s.array.items[1].integer)), r.rows); + } + } + // each rank's kept values as the golden's gathered ints, then the table from both + var ranks: [2][]f64 = undefined; + for (0..2) |rk| { + const samples = try floats(a, c.get("samples").?.array.items[rk]); + ranks[rk] = try a.alloc(f64, p.entries()); + try p.values(samples, ranks[rk]); + const ints = c.get("ints").?.array.items[rk].array.items; + try std.testing.expectEqual(ints.len, ranks[rk].len); + for (ints, ranks[rk]) |want, got| try std.testing.expectEqual(want.integer, costs_mod.roundEven(got * 1000.0)); + } + var got = try p.build(gpa, &.{ ranks[0], ranks[1] }); + defer got.deinit(gpa); + const want = c.get("costs").?.object; + const verify = want.get("verify").?.array.items; + try std.testing.expectEqual(verify.len, got.verify.len); + for (verify, got.verify) |x, y| try std.testing.expectEqual(num(x), y); + try std.testing.expectEqual(num(want.get("draft").?), got.draft); + try std.testing.expectEqual(num(want.get("slot").?), got.slot); + // and as the ranks share a cached table (Costs.encode / decode): microseconds, half to even + const ints = try got.encode(gpa); + defer gpa.free(ints); + var back = try costs_mod.Costs.decode(gpa, ints); + defer back.deinit(gpa); + for (back.verify, got.verify) |x, y| try std.testing.expectEqual(@as(f64, @floatFromInt(@max(1, costs_mod.roundEven(y * 1000.0)))) / 1000.0, x); + } +} + +test "method, statistic, table_of, extend, split, wide_buckets and fallback_ids equal Python's" { + var parsed = try std.json.parseFromSlice(Value, gpa, golden, .{}); + defer parsed.deinit(); + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + const a = arena.allocator(); + const pure = parsed.value.object.get("pure").?.object; + for (pure.get("statistic").?.array.items) |x| { + const xs = try floats(a, x.object.get("xs").?); + try std.testing.expectEqual(num(x.object.get("median").?), try cm.statistic(xs, .median)); + try std.testing.expectEqual(num(x.object.get("min").?), try cm.statistic(xs, .min)); + } + for (pure.get("table").?.array.items) |x| { + const times = try floats(a, x.object.get("times").?); + for ([_]cm.Shape{ .raw, .fit }) |sh| { + const want = try floats(a, x.object.get(@tagName(sh)).?); + const got = try cm.tableOf(gpa, times, sh); + defer gpa.free(got); + try std.testing.expectEqualSlices(f64, want, got); + } + } + for (pure.get("extend").?.array.items) |x| { + const table = try floats(a, x.object.get("table").?); + const pv = x.object.get("points").?.array.items; + const pts = try a.alloc(costs_mod.Point, pv.len); + for (pts, pv) |*pt, v| pt.* = .{ .rows = @intCast(v.array.items[0].integer), .ms = num(v.array.items[1]) }; + const got = try costs_mod.extend(gpa, table, pts); + defer gpa.free(got); + try std.testing.expectEqualSlices(f64, try floats(a, x.object.get("out").?), got); + } + for (pure.get("split").?.array.items) |x| { + var out: [8]u32 = undefined; + const n: usize = @intCast(x.object.get("n").?.integer); + cm.split(@intCast(x.object.get("total").?.integer), out[0..n]); + for (x.object.get("out").?.array.items, out[0..n]) |w, g| try std.testing.expectEqual(@as(u32, @intCast(w.integer)), g); + } + for (pure.get("wide").?.array.items) |x| { + var buf: [4]u32 = undefined; + const got = cm.wideBuckets(@intCast(x.object.get("slots").?.integer), @intCast(x.object.get("cap").?.integer), cm.max_rows, &buf); + const want = x.object.get("out").?.array.items; + try std.testing.expectEqual(want.len, got.len); + for (want, got) |w, g| try std.testing.expectEqual(@as(u32, @intCast(w.integer)), g); + } + try std.testing.expectEqualStrings(pure.get("text").?.string, cm.text); // the calibration text + var methods: usize = 0; + for (pure.get("method").?.array.items) |x| { + const got = methodOf(x.object.get("env").?); + const want = x.object.get("out").?; + if (want == .null) { + if (got) |_| return error.TestExpectedError else |_| {} + continue; + } + const m = try got; + const o = want.object; + try std.testing.expectEqual(@as(u32, @intCast(o.get("reps").?.integer)), m.reps); + try std.testing.expectEqual(@as(u32, @intCast(o.get("warm").?.integer)), m.warm); + try std.testing.expectEqual(@as(u32, @intCast(o.get("recheck").?.integer)), m.recheck); + try std.testing.expectEqual(@as(u32, @intCast(o.get("cycle").?.integer)), m.cycle); + try std.testing.expectEqual(@as(u32, @intCast(o.get("deep").?.integer)), m.deep); + try std.testing.expectEqualStrings(o.get("stat").?.string, @tagName(m.stat)); + try std.testing.expectEqualStrings(o.get("shape").?.string, @tagName(m.shape)); + methods += 1; + } + try std.testing.expect(methods >= 4); + for (pure.get("fallback").?.array.items) |x| { + var buf: [64]u32 = undefined; + const got = cm.fallbackIds(@intCast(x.object.get("vocab").?.integer), &buf); + for (x.object.get("out").?.array.items, got) |w, g| try std.testing.expectEqual(@as(u32, @intCast(w.integer)), g); + } +} + +test "the calibration prompt: ids past the vocabulary dropped, at most 256, fallback under 16; slot cuts" { + var ids: [300]u32 = undefined; + for (&ids, 0..) |*t, i| t.* = @intCast(if (i % 10 == 3) 200_000 else i); + var fb: [64]u32 = undefined; + const got = cm.promptIds(&ids, 129280, &fb); + try std.testing.expectEqual(@as(usize, 256), got.len); + try std.testing.expectEqual(@as(u32, 4), got[3]); + var few = [_]u32{ 1, 2, 3 }; + try std.testing.expectEqual(@as(usize, 64), cm.promptIds(&few, 129280, &fb).len); + try std.testing.expectEqual(@as(usize, 120), cm.cutOf(120, 0)); + try std.testing.expectEqual(@as(usize, 113), cm.cutOf(120, 1)); + try std.testing.expectEqual(@as(usize, 20), cm.cutOf(20, 2)); // 20 > 14 + 8 fails: the whole prompt + try std.testing.expect(std.mem.endsWith(u8, cm.text, "and the") and std.mem.startsWith(u8, cm.text, "The committee")); +} diff --git a/zig/src/families/deepseek_v41/draft/costs.zig b/zig/src/families/deepseek_v41/draft/costs.zig new file mode 100644 index 0000000000..915b65bd6b --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/costs.zig @@ -0,0 +1,168 @@ +//! The verify / DSpark cost table the depth prices with (Python depth.Costs, calib.py's table shape and cache file). +const std = @import("std"); +const Allocator = std.mem.Allocator; + +pub const max_rows = 64; // the widest row-graph bucket (TF_DSV41_GRAPH_ROWS_MAX) +pub const slot_rows = 16; // a slot's window at most (MAX_ROWS) +pub const wide = [_]u32{ 24, 32, 48, 64 }; // the shared buckets calib.py times on several slots + +/// `verify[r - 1]`: ms of a window of r rows in total; `draft`: one DSpark pass; `slot`: each further slot's ms. +pub const Costs = struct { + verify: []f64, + draft: f64, + slot: f64 = 0.0, + + pub fn deinit(c: *Costs, gpa: Allocator) void { + gpa.free(c.verify); + } + + pub fn clone(c: Costs, gpa: Allocator) !Costs { + return .{ .verify = try gpa.dupe(f64, c.verify), .draft = c.draft, .slot = c.slot }; + } + + /// A window of `rows` rows, clamped to the table (Python window_ms). + pub fn windowMs(c: Costs, rows: i64) f64 { + const n: i64 = @intCast(c.verify.len); + return c.verify[@intCast(@max(0, @min(rows, n) - 1))]; + } + + /// A window of `rows` total rows; past the table on the line of its last two entries (Python rows_ms). + pub fn rowsMs(c: Costs, rows: i64) f64 { + const v = c.verify; + const r = @max(1, rows); + if (r <= v.len) return v[@intCast(r - 1)]; + const slope = if (v.len >= 2) @max(v[v.len - 1] - v[v.len - 2], 0.0) else 0.0; + return v[v.len - 1] + slope * @as(f64, @floatFromInt(r - @as(i64, @intCast(v.len)))); + } + + /// Microsecond ints [draft, slot, verify...] (both ranks share these; Python encode, round half to even). + pub fn encode(c: Costs, gpa: Allocator) ![]i64 { + const out = try gpa.alloc(i64, 2 + c.verify.len); + out[0] = micro(c.draft); + out[1] = micro(c.slot); + for (out[2..], c.verify) |*o, v| o.* = micro(v); + return out; + } + + pub fn decode(gpa: Allocator, ints: []const i64) !Costs { + if (ints.len < 3) return error.BadCosts; + const verify = try gpa.alloc(f64, ints.len - 2); + for (verify, ints[2..]) |*v, x| v.* = @as(f64, @floatFromInt(x)) / 1000.0; + return .{ .verify = verify, .draft = @as(f64, @floatFromInt(ints[0])) / 1000.0, .slot = @as(f64, @floatFromInt(ints[1])) / 1000.0 }; + } + + /// Python's cached calibration (`calib-.json`: verify, draft, slot); at least 2 rows. + pub fn parseJson(gpa: Allocator, bytes: []const u8) !Costs { + const File = struct { verify: []f64, draft: f64, slot: f64 = 0.0 }; + const parsed = try std.json.parseFromSlice(File, gpa, bytes, .{ .ignore_unknown_fields = true }); + defer parsed.deinit(); + if (parsed.value.verify.len < 2) return error.BadCosts; + return .{ .verify = try gpa.dupe(f64, parsed.value.verify), .draft = parsed.value.draft, .slot = parsed.value.slot }; + } +}; + +fn micro(v: f64) i64 { + return @max(1, @min(roundEven(v * 1000.0), 1_000_000_000)); +} + +/// Python's round(): half to even. +pub fn roundEven(x: f64) i64 { + const f = @floor(x); + const d = x - f; + var r = f; + if (d > 0.5 or (d == 0.5 and @mod(f, 2.0) != 0.0)) r = f + 1.0; + return @intFromFloat(r); +} + +/// Section 1.3's round model before any measurement: ~37 ms + 5 ms a row, the pass 5.6 ms (Python default_costs). +pub fn defaults(gpa: Allocator, rows: usize) !Costs { + const verify = try gpa.alloc(f64, rows); + for (verify, 0..) |*v, r| v.* = 37.0 + 5.0 * @as(f64, @floatFromInt(r + 1)); + return .{ .verify = verify, .draft = 5.6 }; +} + +/// Each row's value made non-decreasing by pooling adjacent violators (Python calib.monotone), in place. +pub fn monotone(gpa: Allocator, times: []f64) !void { + const Block = struct { sum: f64, n: f64 }; + var blocks: std.ArrayList(Block) = .empty; + defer blocks.deinit(gpa); + for (times) |x| { + try blocks.append(gpa, .{ .sum = x, .n = 1.0 }); + while (blocks.items.len > 1) { + const b = blocks.items[blocks.items.len - 1]; + const a = &blocks.items[blocks.items.len - 2]; + if (!(a.sum / a.n > b.sum / b.n)) break; + a.sum += b.sum; + a.n += b.n; + blocks.items.len -= 1; + } + } + var at: usize = 0; + for (blocks.items) |b| { + for (0..@intFromFloat(b.n)) |_| { + times[at] = b.sum / b.n; + at += 1; + } + } +} + +pub const Point = struct { rows: u32, ms: f64 }; + +/// `table` (rows 1 .. len) continued to the widest point, linear between them, non-decreasing (Python calib.extend). +pub fn extend(gpa: Allocator, table: []const f64, points: []const Point) ![]f64 { + var out: std.ArrayList(f64) = .empty; + errdefer out.deinit(gpa); + try out.appendSlice(gpa, table); + if (table.len == 0) return out.toOwnedSlice(gpa); + var x0: u32 = @intCast(table.len); + var y0 = table[table.len - 1]; + for (points) |p| { // ascending rows + if (p.rows <= x0) continue; + var r = x0 + 1; + while (r <= p.rows) : (r += 1) { + try out.append(gpa, y0 + (p.ms - y0) * @as(f64, @floatFromInt(r - x0)) / @as(f64, @floatFromInt(p.rows - x0))); + } + x0 = p.rows; + y0 = p.ms; + } + for (1..out.items.len) |i| out.items[i] = @max(out.items[i], out.items[i - 1]); + return out.toOwnedSlice(gpa); +} + +/// The table calib.py measures: rows 1..16 each its own (monotone), the wide buckets less the slot overhead, extended. +pub fn measured(gpa: Allocator, rows: []const f64, buckets: []const Point, draft: f64, slot: f64) !Costs { + const table = try gpa.dupe(f64, rows); + defer gpa.free(table); + try monotone(gpa, table); + const pts = try gpa.alloc(Point, buckets.len); + defer gpa.free(pts); + for (pts, buckets) |*p, b| { + const slots: f64 = @floatFromInt((b.rows + slot_rows - 1) / slot_rows); + p.* = .{ .rows = b.rows, .ms = b.ms - slot * (slots - 1.0) }; + } + return .{ .verify = try extend(gpa, table, pts), .draft = draft, .slot = slot }; +} + +test "rows past the table follow its last line; encode rounds like Python" { + const gpa = std.testing.allocator; + var c = try defaults(gpa, 16); + defer c.deinit(gpa); + try std.testing.expectEqual(@as(f64, 42.0), c.windowMs(1)); + try std.testing.expectEqual(@as(f64, 117.0), c.windowMs(99)); + try std.testing.expectEqual(@as(f64, 127.0), c.rowsMs(18)); + try std.testing.expectEqual(@as(i64, 2), roundEven(2.5)); + try std.testing.expectEqual(@as(i64, 4), roundEven(3.5)); + const ints = try c.encode(gpa); + defer gpa.free(ints); + var back = try Costs.decode(gpa, ints); + defer back.deinit(gpa); + try std.testing.expectEqualSlices(f64, c.verify, back.verify); + try std.testing.expectEqual(@as(f64, 5.6), back.draft); +} + +test "monotone pools a dip with the row before it" { + const gpa = std.testing.allocator; + var t = [_]f64{ 1, 3, 2, 4 }; + try monotone(gpa, &t); + try std.testing.expectEqualSlices(f64, &.{ 1, 2.5, 2.5, 4 }, &t); +} diff --git a/zig/src/families/deepseek_v41/draft/depth.zig b/zig/src/families/deepseek_v41/draft/depth.zig new file mode 100644 index 0000000000..f1953f6cfa --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/depth.zig @@ -0,0 +1,449 @@ +//! Cost-derived DSpark depth: verify the k drafts maximizing E(k) - lambda verify[k]; clock-free, so ranks agree. +const std = @import("std"); +const Allocator = std.mem.Allocator; +const Costs = @import("costs.zig").Costs; +const calib = @import("calib.zig"); +const joint = @import("joint.zig"); +const tree = @import("tree.zig"); + +pub const rate_window = 16; // rounds in a slot's running rate +pub const slot_rows = 16; // a slot's window at most + +pub const Mode = enum { cost, static }; + +/// The knobs (TF_DSV41_DEPTH, _DRAFT_DEPTH, _DRAFT_SKIP, _DEPTH_JOINT, _DEPTH_FAIR, _DEPTH_HOST_MS, _GRAPH_ROWS_MAX). +pub const Settings = struct { + mode: Mode = .cost, + cap: usize = 5, + skip: bool = true, + joint: u2 = 1, + fair: f64 = joint.fair, + host_ms: f64 = joint.host_ms, + max_rows: u64 = 64, + code_accept: bool = false, // TF_DSV41_CODE_ACCEPT: expand high-acceptance short-context chains + context_bucket: u64 = 2048, // TF_DSV41_GRAPH_BUCKET; short contexts only use its linear buckets +}; + +/// [E(0) .. E(len)]: tokens a round verifying k drafts commits on average, into `out` (len + 1). +pub fn expectedTokens(qs: []const f64, out: []f64) void { + var run: f64 = 1.0; + var e: f64 = 1.0; + out[0] = 1.0; + for (qs, out[1..]) |q, *o| { + run *= q; + e += run; + o.* = e; + } +} + +pub const Best = struct { k: usize, surplus: f64 }; + +/// The k from `least` to len(qs) (bounded by the table) maximizing E(k) - rate verify[k]; ties to the smaller k. +pub fn bestK(qs: []const f64, verify: []const f64, rate: f64, least: usize) Best { + var es: [slot_rows + 1]f64 = undefined; + const n = @min(qs.len, slot_rows); + expectedTokens(qs[0..n], es[0 .. n + 1]); + const top = @min(n, verify.len - 1); + var best: Best = .{ .k = 0, .surplus = -std.math.inf(f64) }; + var k = least; + while (k <= top) : (k += 1) { + const s = es[k] - rate * verify[k]; + if (s > best.surplus + 1e-12) best = .{ .k = k, .surplus = s }; + } + return best; +} + +const Round = struct { tokens: f64, ms: f64 }; + +/// The last `rate_window` rounds: (tokens, modelled ms), oldest first. +const Ring = struct { + items: [rate_window]Round = undefined, + len: usize = 0, + head: usize = 0, + + fn push(r: *Ring, x: Round) void { + r.items[(r.head + r.len) % rate_window] = x; + if (r.len < rate_window) r.len += 1 else r.head = (r.head + 1) % rate_window; + } + + fn sums(r: Ring) Round { + var tokens: f64 = 0.0; // whole tokens: exact + var ms: joint.PySum = .{}; + for (0..r.len) |i| { + const x = r.items[(r.head + i) % rate_window]; + tokens += x.tokens; + ms.add(x.ms); + } + return .{ .tokens = tokens, .ms = ms.total() }; + } +}; + +const First = struct { token: u32, p: f64 }; + +pub const Slot = struct { + rounds: Ring = .{}, + used: [slot_rows]f64 = undefined, // this window's confidences + used_len: usize = 0, + drafted: bool = false, + first: ?First = null, // a zero-depth round's first draft, until its bonus + q1: ?f64 = null, + rows: usize = 0, + keep: usize = 0, + ms: ?f64 = null, // a tree round's modelled ms + + fn use(st: *Slot, conf: []const f64) void { + st.used_len = @min(conf.len, slot_rows); + @memcpy(st.used[0..st.used_len], conf[0..st.used_len]); + } +}; + +const Acc = struct { slot: u32, tokens: f64, rows: u64, drafted: bool }; + +pub const Depth = struct { + gpa: Allocator, + set: Settings, + costs: Costs, + cal: calib.Calibration, + stats: calib.Stats, + slots: std.ArrayList(?Slot) = .empty, + acc: std.ArrayList(Acc) = .empty, // the open round's commits, in commit order + agg: [costs_rows + 1]Ring = @splat(.{}), // by windows in a round: recent (tokens, modelled ms) + expanded_rounds: u64 = 0, + expanded_rows: u64 = 0, + forward_rows: u64 = 16, // Lanes.model supplies the real forward cap, not the allocator's graph limit + + const costs_rows = 64; + + /// `costs` is copied; `block` is the DSpark block (the calibration's positions). + pub fn init(gpa: Allocator, costs: Costs, block: usize, set: Settings) !Depth { + var cal = try calib.Calibration.init(gpa, block); + errdefer cal.deinit(gpa); + return .{ .gpa = gpa, .set = set, .costs = try costs.clone(gpa), .cal = cal, .stats = .{ .positions = block } }; + } + + pub fn deinit(d: *Depth) void { + d.costs.deinit(d.gpa); + d.cal.deinit(d.gpa); + d.slots.deinit(d.gpa); + d.acc.deinit(d.gpa); + } + + pub fn reset(d: *Depth, slot: u32) void { + if (slot < d.slots.items.len) d.slots.items[slot] = null; + } + + pub fn state(d: *Depth, slot: u32) !*Slot { + while (d.slots.items.len <= slot) try d.slots.append(d.gpa, null); + const s = &d.slots.items[slot]; + if (s.* == null) s.* = .{}; + return &s.*.?; + } + + /// The slot's committed tokens per modelled ms over its recent rounds (two a drafting 2-row round before any). + pub fn rate(d: *Depth, slot: u32) !f64 { + const st = try d.state(slot); + if (st.rounds.len > 0) { + const s = st.rounds.sums(); + return s.tokens / s.ms; + } + return 2.0 / (d.costs.windowMs(2) + d.costs.draft); + } + + fn least(d: *const Depth, alone: bool, first: ?u32) usize { + return if (d.set.skip and alone and first != null) 0 else 1; + } + + /// Drafts to verify of `conf` (1 .. min(most, len, cap); 0 .. when alone with zero depth on and `first` known). + pub fn choose(d: *Depth, slot: u32, conf: []const f64, most: usize, alone: bool, first: ?u32) !usize { + const top = @min(@min(most, conf.len), d.set.cap); + const st = try d.state(slot); + st.first = null; + st.q1 = null; + st.ms = null; + if (top == 0) { + st.used_len = 0; + st.drafted = false; + return 0; + } + var k = top; + if (d.set.mode == .cost) { + var qs: [slot_rows]f64 = undefined; + for (conf[0..top], qs[0..top], 0..) |p, *q, j| q.* = d.cal.q(j, p); + const lo = d.least(alone, first); + const r = try d.rate(slot); + k = @max(lo, @min(bestK(qs[0..top], d.costs.verify, r, lo).k, top)); + st.q1 = qs[0]; + if (k == 0) st.first = .{ .token = first.?, .p = conf[0] }; + } + st.use(conf[0..k]); + st.drafted = true; + return k; + } + + /// Modelled ms of a round: its window of `rows` total rows, each further window's overhead, the pass. + pub fn roundMs(d: *const Depth, rows: u64, windows: u64, drafted: bool) f64 { + return d.costs.rowsMs(@intCast(rows)) + d.costs.slot * @as(f64, @floatFromInt(windows -| 1)) + + (if (drafted) d.costs.draft else 0.0) + (if (d.set.joint == 2) d.set.host_ms else 0.0); + } + + /// The open round's commits -> one (tokens, modelled ms) of the aggregate rate by its windows. + fn close(d: *Depth) void { + if (d.acc.items.len == 0) return; + var tokens: f64 = 0.0; + var rows: u64 = 0; + var drafted = false; + for (d.acc.items) |a| { + tokens += a.tokens; + rows += a.rows; + drafted = drafted or a.drafted; + } + const n = d.acc.items.len; + d.agg[@min(n, costs_rows)].push(.{ .tokens = tokens, .ms = d.roundMs(rows, n, drafted) }); + d.acc.clearRetainingCapacity(); + } + + /// The aggregate rate of a round of `windows` windows, else two tokens a drafting window. + pub fn jointRate(d: *const Depth, windows: usize) f64 { + const r = d.agg[@min(windows, costs_rows)]; + if (r.len > 0) { + const s = r.sums(); + return s.tokens / s.ms; + } + const n: u64 = @max(1, windows); + return 2.0 * @as(f64, @floatFromInt(n)) / d.roundMs(2 * n, n, true); + } + + /// The slot's recent tokens a round, null before its first commit. + pub fn tpr(d: *Depth, slot: u32) !?f64 { + const st = try d.state(slot); + if (st.rounds.len == 0) return null; + return st.rounds.sums().tokens / @as(f64, @floatFromInt(st.rounds.len)); + } + + pub const Ask = struct { slot: u32, conf: []const f64, most: usize, first: ?u32 = null, position: u64 = std.math.maxInt(u64) }; + + fn eligible(d: *Depth, a: Ask) !bool { + const st = try d.state(a.slot); + return a.position <= 4096 and st.rounds.len >= 4 and (try d.tpr(a.slot)).? >= 3.0; + } + + fn expandable(d: *Depth, a: Ask, k: usize) !bool { + const top = @min(@min(@min(a.most, a.conf.len), d.set.cap), slot_rows - 1); + // Keep context graph keys stable. Past the linear buckets leave the old policy in charge. + return top > k and d.set.context_bucket > 0 and a.position + top < 16 * d.set.context_bucket and + (a.position + k) / d.set.context_bucket == (a.position + top) / d.set.context_bucket and try d.eligible(a); + } + + /// Keep every old depth as a floor. Prose/cold slots are frozen; any long-context ask preserves the whole round. + fn expand(d: *Depth, asks: []const Ask, others: u64, other_windows: usize, ks: []usize) !void { + if (!d.set.code_accept or d.set.mode != .cost or d.set.joint == 2 or others != 0 or asks.len < 2) return; + for (asks) |a| if (a.position > 4096) return; + var baseline_rows: u64 = 0; + for (ks) |k| baseline_rows += 1 + k; + const bucket: u64 = blk: { + // These are rowtab.BUCKETS. Never enlarge the existing graph or turn one forward into two. + for ([_]u64{ 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 24, 32, 48, 64 }) |r| { + if (r >= baseline_rows and r <= d.forward_rows) break :blk r; + } + return; + }; + if (baseline_rows >= @min(d.set.max_rows, bucket)) return; + var any = false; + for (asks, ks) |a, k| any = any or try d.expandable(a, k); + if (!any) return; // cold/prose/full windows allocate no policy scratch + const slots = try d.gpa.alloc(joint.Slot, asks.len); + defer d.gpa.free(slots); + const qs = try d.gpa.alloc([slot_rows]f64, asks.len); + defer d.gpa.free(qs); + for (asks, ks, slots, qs) |a, k, *s, *q| { + const top_available = @min(@min(@min(a.most, a.conf.len), d.set.cap), slot_rows - 1); + const active = try d.expandable(a, k); + const top = if (active) top_available else k; + for (a.conf[0..top], q[0..top], 0..) |p, *out, j| out.* = d.cal.q(j, p); + s.* = .{ .qs = q[0..top], .least = k, .top = top }; + } + const n = asks.len + other_windows; + const fixed = d.costs.draft + d.costs.slot * @as(f64, @floatFromInt(n -| 1)); + const added = try joint.expand(d.gpa, slots, others, d.costs, fixed, @min(d.set.max_rows, bucket), bucket, ks); + if (added == 0) return; + d.expanded_rounds += 1; + d.expanded_rows += added; + for (asks, ks) |a, k| (try d.state(a.slot)).use(a.conf[0..k]); + } + + /// Every DSpark slot's k of one round, together; `others`: rows of the round's other windows. Writes `ks`. + pub fn chooseJoint(d: *Depth, asks: []const Ask, others: u64, other_windows: usize, alone_in: ?bool, ks: []usize) !void { + d.close(); + const alone = (alone_in orelse (asks.len == 1 and other_windows == 0)) and asks.len == 1 and other_windows == 0; + if (d.set.joint == 0 or d.set.mode == .static or alone or asks.len + other_windows <= 1) { + for (asks, ks) |a, *k| k.* = try d.choose(a.slot, a.conf, a.most, alone, a.first); + // Exact-row single windows have no paid padding to spend. + return; + } + const two = d.set.joint == 2; + const qbuf = try d.gpa.alloc([slot_rows]f64, asks.len); + defer d.gpa.free(qbuf); + const slots = try d.gpa.alloc(joint.Slot, asks.len); + defer d.gpa.free(slots); + const tops = try d.gpa.alloc(usize, asks.len); + defer d.gpa.free(tops); + for (asks, qbuf, slots, tops) |a, *qb, *s, *t| { + t.* = @min(@min(@min(a.most, a.conf.len), d.set.cap), slot_rows - 1); + const st = try d.state(a.slot); + st.first = null; + st.q1 = null; + st.ms = null; + for (a.conf[0..t.*], qb[0..t.*], 0..) |p, *q, j| q.* = d.cal.q(j, p); + st.q1 = if (t.* > 0) qb[0] else null; + s.* = .{ .qs = qb[0..t.*], .least = if (t.* == 0 or (two and d.set.skip and a.first != null)) 0 else 1, .top = t.* }; + } + const r = d.jointRate(asks.len + other_windows); + if (two) { + const tp = try d.gpa.alloc(?f64, asks.len); + defer d.gpa.free(tp); + const w = try d.gpa.alloc(f64, asks.len); + defer d.gpa.free(w); + for (asks, tp) |a, *x| x.* = try d.tpr(a.slot); + joint.weights(tp, d.set.fair, w); + for (slots, w) |*s, x| s.weight = x; + } + _ = try joint.allocate(d.gpa, slots, others, d.costs, r, d.set.max_rows, ks); + try d.expand(asks, others, other_windows, ks); + var rows: u64 = others; + var drafts: u64 = 0; + var zero: u64 = 0; + for (asks, ks, tops) |a, k, t| { + const st = try d.state(a.slot); + st.use(a.conf[0..k]); + st.drafted = t > 0; + if (k == 0 and t > 0) { + st.first = .{ .token = a.first.?, .p = a.conf[0] }; + zero += 1; + } + rows += 1 + k; + drafts += k; + } + d.stats.joint[0] += 1; + d.stats.joint[1] += rows; + d.stats.joint[2] += drafts; + d.stats.joint[3] += zero; + } + + pub const TreeChoice = struct { k: usize, lens: [3]usize = @splat(0) }; + + /// choose with first-position siblings (`sibs`: their calibrated chances); `dup`: tree.plan's duplicate pricing. + pub fn chooseTree(d: *Depth, slot: u32, conf: []const f64, most: usize, sibs: []const f64, alone: bool, first: ?u32, dup: tree.Dup, sib_rows: usize) !TreeChoice { + const top = @min(@min(most, conf.len), d.set.cap); + if (top == 0 or d.set.mode == .static or sibs.len == 0) return .{ .k = try d.choose(slot, conf, most, alone, first) }; + const st = try d.state(slot); + st.first = null; + st.q1 = null; + st.ms = null; + var qs: [slot_rows]f64 = undefined; + for (conf[0..top], qs[0..top], 0..) |p, *q, j| q.* = d.cal.q(j, p); + const lo = d.least(alone, first); + const p = tree.plan(qs[0..top], sibs, qs[1..top], d.costs.verify, try d.rate(slot), lo, dup, @min(sib_rows, top), slot_rows); + var out: TreeChoice = .{ .k = @max(lo, @min(p.k, top)), .lens = p.lens }; + st.q1 = qs[0]; + if (out.k == 0) { + st.first = .{ .token = first.?, .p = conf[0] }; + out.lens = @splat(0); + } + if (out.lens[0] + out.lens[1] + out.lens[2] > 0) st.ms = p.ms + d.costs.draft; + st.use(conf[0..out.k]); + st.drafted = true; + return out; + } + + /// The slot's last window had `rows` rows (its main chain's) and committed `keep`; `tokens`: a tree round's. + pub fn record(d: *Depth, slot: u32, rows: usize, keep: usize, tokens: ?usize) !void { + for (d.acc.items) |a| if (a.slot == slot) { + d.close(); + break; + }; + const st = try d.state(slot); + const got: f64 = @floatFromInt(tokens orelse keep); + try d.acc.append(d.gpa, .{ .slot = slot, .tokens = got, .rows = rows, .drafted = st.drafted }); + if (st.drafted) { + const used = st.used[0..@min(st.used_len, rows -| 1)]; + d.cal.record(used, keep); + d.stats.rounds += 1; + if (st.first == null) d.stats.add(st.q1, used.len, keep, if (used.len > 0) keep > 1 else null); + } + var ms = d.costs.windowMs(@intCast(rows)) + (if (st.drafted) d.costs.draft else 0.0); + if (st.ms) |m| ms = m; + st.rounds.push(.{ .tokens = got, .ms = ms }); + st.used_len = 0; + st.drafted = false; + st.ms = null; + st.rows = rows; + st.keep = keep; + } + + /// After record: the token the window chose after its last kept row teaches a zero-depth round's first draft. + pub fn bonus(d: *Depth, slot: u32, token: u32) void { + if (slot >= d.slots.items.len) return; + const st = &(d.slots.items[slot] orelse return); + const f = st.first orelse return; + st.first = null; + if (st.rows != 1 or st.keep != 1) return; + const kept = token == f.token; + d.cal.record(&.{f.p}, if (kept) 2 else 1); + d.stats.zero += 1; + d.stats.add(st.q1, 0, 1, kept); + } +}; + +test "a lone window skips a weak first draft and keeps a strong chain" { + const gpa = std.testing.allocator; + var verify: [16]f64 = undefined; + for (&verify, 0..) |*v, r| v.* = 21.4 + 4.6 * @as(f64, @floatFromInt(r)); + var d = try Depth.init(gpa, .{ .verify = &verify, .draft = 3.52, .slot = 0.65 }, 5, .{}); + defer d.deinit(); + try std.testing.expectEqual(@as(usize, 0), try d.choose(0, &.{ 0.05, 0.05, 0.05, 0.05, 0.05 }, 5, true, 7)); + try d.record(0, 1, 1, null); + d.bonus(0, 7); // the skipped draft would have been kept + try std.testing.expect(d.cal.kept[0] == 1.0 and d.stats.zero == 1); + try std.testing.expectEqual(@as(usize, 1), try d.choose(0, &.{ 0.05, 0.05, 0.05, 0.05, 0.05 }, 5, false, 7)); + try std.testing.expectEqual(@as(usize, 5), try d.choose(1, &.{ 0.99, 0.99, 0.99, 0.99, 0.99 }, 5, true, 7)); +} + +test "high acceptance fills paid padding and preserves cold, prose, context and forward limits" { + const gpa = std.testing.allocator; + var verify: [64]f64 = undefined; + for (&verify, 0..) |*v, i| v.* = 20 + 3 * @as(f64, @floatFromInt(i)); + const asks = [_]Depth.Ask{ + .{ .slot = 0, .conf = &.{ 0.95, 0.95, 0.95, 0.95, 0.95 }, .most = 5, .position = 100 }, + .{ .slot = 1, .conf = &.{ 0.95, 0.95, 0.95, 0.95, 0.95 }, .most = 5, .position = 100 }, + .{ .slot = 2, .conf = &.{ 0.95, 0.95, 0.95, 0.95, 0.95 }, .most = 5, .position = 100 }, + .{ .slot = 3, .conf = &.{ 0.95, 0.95, 0.95, 0.95, 0.95 }, .most = 5, .position = 100 }, + }; + for (0..7) |case| { + var d = try Depth.init(gpa, .{ .verify = &verify, .draft = 4 }, 5, .{ .code_accept = true }); + defer d.deinit(); + d.forward_rows = if (case == 1) 16 else 64; + if (case == 2) d.set.max_rows = 20; + var input = asks; + if (case == 3) input[0].position = 4097; + if (case == 4) for (&input) |*a| { + a.position = 2043; + }; + for (0..4) |slot| { + const st = try d.state(@intCast(slot)); + for (0..(if (case == 5) @as(usize, 3) else 4)) |_| st.rounds.push(.{ .tokens = if (case == 6) 2 else 4, .ms = 40 }); + } + var ks = [_]usize{ 4, 3, 3, 3 }; // 17 real rows, already paying for a 24-row graph + try d.expand(&input, 0, 0, &ks); + var rows: usize = 4; + for (ks) |k| rows += k; + if (case == 0) { + try std.testing.expectEqualSlices(usize, &.{ 5, 5, 5, 5 }, &ks); + try std.testing.expectEqual(@as(u64, 7), d.expanded_rows); + } else if (case == 2) { + try std.testing.expectEqual(@as(usize, 20), rows); + } else { + try std.testing.expectEqualSlices(usize, &.{ 4, 3, 3, 3 }, &ks); + try std.testing.expectEqual(@as(u64, 0), d.expanded_rounds); + } + } +} diff --git a/zig/src/families/deepseek_v41/draft/draft.zig b/zig/src/families/deepseek_v41/draft/draft.zig new file mode 100644 index 0000000000..9276ccb2bc --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/draft.zig @@ -0,0 +1,51 @@ +//! DeepSeek-V4.1 drafting: DSpark glue, cost-derived depth, trees and lanes over iface.zig's `Target` / `Pass`. + +pub const costs = @import("costs.zig"); +pub const Costs = costs.Costs; +pub const calib = @import("calib.zig"); +pub const joint = @import("joint.zig"); +pub const depth = @import("depth.zig"); +pub const Depth = depth.Depth; +pub const tree = @import("tree.zig"); +pub const dspark = @import("dspark.zig"); +pub const iface = @import("iface.zig"); +pub const policy = @import("policy.zig"); +pub const lanes = @import("lanes.zig"); +pub const Lanes = lanes.Lanes; +pub const twin = @import("twin.zig"); +pub const drive = @import("drive.zig"); +pub const branches = @import("branches.zig"); +pub const lookup = @import("lookup.zig"); +pub const calib_env = @import("calib_env.zig"); +pub const calib_measure = @import("calib_measure.zig"); +pub const branches_oracle = @import("branches_oracle.zig"); +pub const spec = @import("spec.zig"); + +test { + _ = costs; + _ = calib; + _ = joint; + _ = depth; + _ = tree; + _ = dspark; + _ = @import("dspark_test.zig"); // the chain lanes, bit for bit + _ = iface; + _ = policy; + _ = lanes; + _ = twin; + _ = drive; + _ = branches; + _ = calib_env; + _ = @import("calib_env_test.zig"); + _ = calib_measure; + _ = @import("calib_measure_test.zig"); + _ = branches_oracle; + _ = @import("branches_test.zig"); + _ = @import("drive_test.zig"); + _ = @import("lanes_test.zig"); + _ = @import("golden_test.zig"); + _ = spec; + _ = @import("spec_test.zig"); + _ = lookup; + _ = @import("lookup_test.zig"); +} diff --git a/zig/src/families/deepseek_v41/draft/drive.zig b/zig/src/families/deepseek_v41/draft/drive.zig new file mode 100644 index 0000000000..063bdf5502 --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/drive.zig @@ -0,0 +1,92 @@ +//! One stream through lanes over any `Target` for gates; passes `NoDraft` (drafts off) and `Oracle` (corrupted serial). +const std = @import("std"); +const lanes = @import("lanes"); +const iface = @import("iface.zig"); +const ln = @import("lanes.zig"); +const dspark = @import("dspark.zig"); +const costs_mod = @import("costs.zig"); + +pub const Result = struct { + tokens: []u32, + drafted: u64, + accepted: u64, + /// Oracle only: asks whose anchor was not the serial decode's token there (the stream had already diverged) + off_anchor: u64 = 0, +}; + +pub const NoDraft = struct { + pub fn pass(x: *NoDraft) iface.Pass { + return .{ .ptr = x, .vtable = &vtable }; + } + const vtable: iface.Pass.VTable = .{ .ingest = ingest, .propose = propose, .reset = reset, .markov = markov }; + fn ingest(_: *anyopaque, _: u32, _: u64, _: iface.Taps, _: []const u32) anyerror!void {} + fn propose(_: *anyopaque, _: []const iface.Ask, _: []iface.Proposal) anyerror!void { + return error.NoDrafter; + } + fn reset(_: *anyopaque, _: u32) void {} + fn markov(_: *anyopaque) ?dspark.Markov { + return null; + } +}; + +/// Drafts `serial[j]` (position prompt_len + j); round r corrupts draft (r/4)%block if r%4==1, draft 0 if r%4==3. +pub const Oracle = struct { + serial: []const u32, + prompt_len: u64, + vocab: u32, + round: u64 = 0, + off_anchor: u64 = 0, + + pub fn pass(x: *Oracle) iface.Pass { + return .{ .ptr = x, .vtable = &vtable }; + } + const vtable: iface.Pass.VTable = .{ .ingest = ingest, .propose = propose, .reset = NoDraft.reset, .markov = NoDraft.markov }; + fn ingest(_: *anyopaque, _: u32, _: u64, _: iface.Taps, _: []const u32) anyerror!void {} + + fn at(x: *const Oracle, pos: u64) u32 { + if (pos < x.prompt_len) return x.vocab - 1; + const j = pos - x.prompt_len; + return if (j < x.serial.len) x.serial[@intCast(j)] else x.vocab - 1; + } + + fn propose(p: *anyopaque, asks: []const iface.Ask, out: []iface.Proposal) anyerror!void { + const x: *Oracle = @ptrCast(@alignCast(p)); + for (asks, out) |a, o| { + if (a.anchor != x.at(a.start)) x.off_anchor += 1; + const b = o.drafts.len; + for (o.drafts, o.conf, 0..) |*d, *c, i| { + d.* = x.at(a.start + 1 + i); + c.* = 0.9; + } + const wrong: ?usize = switch (x.round % 4) { + 1 => @intCast((x.round / 4) % b), + 3 => 0, + else => null, + }; + if (wrong) |i| o.drafts[i] = (o.drafts[i] + 1) % x.vocab; + x.round += 1; + } + } +}; + +/// `max_new` tokens after `prompt` through lanes (one slot, greedy), the target's first choice included. +pub fn generate(gpa: std.mem.Allocator, target: iface.Target, pass: iface.Pass, shape: dspark.Shape, prompt: []const u32, max_new: u32, drafts: bool) !Result { + var c = try costs_mod.defaults(gpa, 64); + defer c.deinit(gpa); + const x = try ln.Lanes.init(gpa, target, pass, c, .{ .shape = shape, .slots = 1, .spec = try @import("spec.zig").Settings.fromEnv() }); + defer x.deinit(); + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + var cfg = try lanes.Config.init(gpa, try x.model(arena.allocator(), 64), 16, 15); + defer cfg.deinit(gpa); + var clock: lanes.fake.FixedClock = .{}; + var e = lanes.Engine.init(gpa, &cfg, x.backend(), clock.clock()); + defer e.deinit(); + x.attach(&e); + var s = try lanes.Stream.init(gpa, .{ .id = "gate", .prompt = prompt, .max_new = max_new, .drafts = drafts }); + defer s.deinit(gpa); + try e.addStream(&s); + while (e.activeCount() > 0) try e.step(); + x.reportSpec(); + return .{ .tokens = try gpa.dupe(u32, s.emitted()), .drafted = e.drafted, .accepted = e.accepted }; +} diff --git a/zig/src/families/deepseek_v41/draft/drive_test.zig b/zig/src/families/deepseek_v41/draft/drive_test.zig new file mode 100644 index 0000000000..cc9245379a --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/drive_test.zig @@ -0,0 +1,33 @@ +//! drive.zig on the CPU twin: lanes with oracle-corrupted drafts == the serial decode; drafts off == serial. +const std = @import("std"); +const twin = @import("twin.zig"); +const drive = @import("drive.zig"); + +const gpa = std.testing.allocator; + +test "oracle drafts and no drafts both give the serial decode" { + const dims: twin.Dims = .{}; + const prompt = [_]u32{ 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3 }; + const max_new = 90; + const want = blk: { + const t = try twin.Twin.init(gpa, dims, 1); + defer t.deinit(); + break :blk try t.serial(0, &prompt, max_new, null, null); + }; + defer gpa.free(want); + const shape: @import("dspark.zig").Shape = .{ .block = dims.block, .window = dims.window, .hidden = dims.dim, .candidates = 8 }; + for ([_]bool{ false, true }) |drafts| { + const t = try twin.Twin.init(gpa, dims, 2); + defer t.deinit(); + var nd: drive.NoDraft = .{}; + var o: drive.Oracle = .{ .serial = want, .prompt_len = prompt.len, .vocab = dims.vocab }; + const r = try drive.generate(gpa, t.target(), if (drafts) o.pass() else nd.pass(), shape, &prompt, max_new, drafts); + defer gpa.free(r.tokens); + try std.testing.expectEqualSlices(u32, want, r.tokens); + if (drafts) { + try std.testing.expectEqual(@as(u64, 0), o.off_anchor); + try std.testing.expect(r.accepted > 0 and r.accepted < r.drafted); // some kept, some rejected + try std.testing.expect(o.round >= 8); // every corruption kind came round twice + } else try std.testing.expectEqual(@as(u64, 0), r.drafted); + } +} diff --git a/zig/src/families/deepseek_v41/draft/dspark.zig b/zig/src/families/deepseek_v41/draft/dspark.zig new file mode 100644 index 0000000000..21e412885f --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/dspark.zig @@ -0,0 +1,298 @@ +//! DSpark host glue: block ids/positions, context rows, keyed params, the chain rule; drafts only ever propose. +const std = @import("std"); +const Allocator = std.mem.Allocator; +const lanes = @import("lanes"); +const Sampling = lanes.Sampling; + +extern "c" fn exp(x: f64) f64; + +/// The drafter's dimensions from the checkpoint's config (dspark_* and sliding_window). +pub const Shape = struct { + block: u32 = 5, // dspark_block_size: drafts a pass + noise: u32 = 128799, // dspark_noise_token_id: the block's rows after the anchor + window: u32 = 128, // sliding_window: context rows a draft row sees + rank: u32 = 256, // dspark_markov_rank + hidden: u32 = 4096, + candidates: u32 = 64, // a position's candidates, gathered over the ranks (world x min(64, 128 / world)) + + /// The context ring's rows: a power of two holding the window and the block. + pub fn ring(s: Shape) u32 { + return std.math.ceilPowerOfTwo(u32, s.window + s.block) catch unreachable; + } +}; + +/// Block ids [anchor, noise x (n - 1)] for one slot. +pub fn blockIds(s: Shape, anchor: u32, out: []u32) void { + @memset(out, s.noise); + out[0] = anchor; +} + +/// A slot's draft rows sit at P .. P + n - 1 (P: the pending token's position). +pub fn positions(p: u64, out: []u64) void { + for (out, 0..) |*o, i| o.* = p + i; +} + +/// The context a draft row sees: positions first .. P - 1 (at most `window`, not before `valid`), ring slots in order. +pub const Context = struct { + first: u64, + count: u32, + + pub fn of(s: Shape, p: u64, valid: u64) Context { + const a = @max(p -| s.window, valid); + return .{ .first = a, .count = @intCast(if (p > a) p - a else 0) }; + } + + /// The ring slots of the context rows (the attention's "compressed" list), -1 past them; `out.len` = window. + pub fn slots(c: Context, s: Shape, out: []i32) void { + const ring = s.ring(); + @memset(out, -1); + for (out[0..c.count], 0..) |*o, i| o.* = @intCast((c.first + i) % ring); + } +}; + +/// The chain kernel's per-slot parameters: (seed, POS0, top_k, sampled) and (T, top_p, min_p). +pub const Params = struct { + seed: u64 = 0, + pos0: u64, + top_k: u32 = 0, + sampled: bool = false, + temperature: f64 = 1.0, + top_p: f64 = 1.0, + min_p: f64 = 0.0, + + /// For a slot whose pending token sits at `p`: the first draft is the token at p + 1, keyed there. + pub fn of(smp: ?Sampling, p: u64) Params { + const s = smp orelse return .{ .pos0 = p + 1 }; + if (s.temperature <= 0.0) return .{ .pos0 = p + 1 }; + return .{ .seed = s.seed & ((@as(u64, 1) << 63) - 1), .pos0 = p + 1, .top_k = s.top_k, .sampled = true, .temperature = s.temperature, .top_p = s.top_p, .min_p = s.min_p }; + } + + pub fn sampling(p: Params) ?Sampling { + if (!p.sampled) return null; + return .{ .seed = p.seed, .temperature = p.temperature, .top_k = p.top_k, .top_p = p.top_p, .min_p = p.min_p }; + } +}; + +pub fn bf16(bits: u16) f32 { + return @bitCast(@as(u32, bits) << 16); +} + +pub fn toBf16(x: f32) u16 { + const u: u32 = @bitCast(x); + return @intCast((u + 0x7FFF + ((u >> 16) & 1)) >> 16); // round to nearest even +} + +/// The Markov head's tables (bf16 [V, rank]) and the confidence head's projection (fp32 [hidden + rank]). +pub const Markov = struct { + w1: []const u16, // markov_head.embed + w2: []const u16, // markov_head.head + rank: u32, + conf: ?[]const f32 = null, + + /// markov_w2[id] . markov_w1[prev] (bf16 products, fp32 sum). + pub fn bias(m: Markov, prev: u32, id: u32) f32 { + const a = m.w1[@as(usize, prev) * m.rank ..][0..m.rank]; + const b = m.w2[@as(usize, id) * m.rank ..][0..m.rank]; + var acc: f32 = 0.0; + for (a, b) |x, y| acc += bf16(x) * bf16(y); + return acc; + } + + /// sigmoid(w_conf . [h ; markov_w1[prev]]). + pub fn confidence(m: Markov, hid: []const f32, prev: u32) f32 { + if (m.conf == null) return 0.5; + var acc: [1]f32 = undefined; + m.hidDots(hid, 1, &acc); + return m.confidenceAfter(acc[0], hid.len, prev); + } + + /// Each row's hidden confidence part w_conf[0..d] . h, rows in vector lanes, each in index order: same bits. + pub fn hidDots(m: Markov, hid: []const f32, rows: usize, out: []f32) void { + const cw = m.conf.?; + const d = hid.len / rows; + var r0: usize = 0; + while (r0 < rows) : (r0 += vlanes) { + const nr = @min(vlanes, rows - r0); + var acc: V = @splat(0.0); + var col: [vlanes][*]const f32 = undefined; + for (0..vlanes) |j| col[j] = hid[(r0 + @min(j, nr - 1)) * d ..].ptr; + for (0..d) |t| { + var h: V = undefined; + inline for (0..vlanes) |j| h[j] = col[j][t]; + acc += h * @as(V, @splat(cw[t])); + } + const sums: [vlanes]f32 = acc; + for (0..nr) |j| out[r0 + j] = sums[j]; + } + } + + /// A confidence from its hidden part (`hidDots`): the Markov part after `prev` added in order, then the sigmoid. + pub fn confidenceAfter(m: Markov, hid_part: f32, d: usize, prev: u32) f32 { + const cw = m.conf orelse return 0.5; + var acc = hid_part; + const me = m.w1[@as(usize, prev) * m.rank ..][0..m.rank]; + for (me, cw[d..][0..m.rank]) |x, w| acc += bf16(x) * w; + return @floatCast(1.0 / (1.0 + exp(-@as(f64, acc)))); + } +}; + +/// Vector lanes of the chain's sums: each lane one candidate's (or row's) sum in its own sequential order. +const vlanes = 8; +const V = @Vector(vlanes, f32); + +/// z = base + Markov bias after `prev` (-inf where id < 0); candidates in vector lanes, each in rank order: same bits. +pub fn markovZ(m: Markov, ids: []const i32, base: []const f32, prev: u32, z: []f32) void { + const a = m.w1[@as(usize, prev) * m.rank ..][0..m.rank]; + var c0: usize = 0; + while (c0 < ids.len) : (c0 += vlanes) { + const nc = @min(vlanes, ids.len - c0); + var row: [vlanes][*]const u16 = undefined; + for (0..vlanes) |j| { + const id = if (j < nc) ids[c0 + j] else -1; + row[j] = if (id < 0) a.ptr else m.w2[@as(usize, @intCast(id)) * m.rank ..].ptr; // dead lanes: any row + } + var acc: V = @splat(0.0); + for (a, 0..) |x, r| { + var b: V = undefined; + inline for (0..vlanes) |j| b[j] = bf16(row[j][r]); + acc += @as(V, @splat(bf16(x))) * b; + } + const sums: [vlanes]f32 = acc; + for (0..nc) |j| { + const id = ids[c0 + j]; + z[c0 + j] = if (id < 0) -std.math.inf(f32) else base[c0 + j] + sums[j]; + } + } +} + +/// The first of (value desc, id asc) among live candidates. +pub fn greedy(ids: []const i32, z: []const f32) ?u32 { + var best: ?usize = null; + for (ids, z, 0..) |id, v, i| { + if (id < 0) continue; + if (best) |b| { + if (v > z[b] or (v == z[b] and id < ids[b])) best = i; + } else best = i; + } + return if (best) |b| @intCast(ids[b]) else null; +} + +/// The keyed choice over a row's live candidates (greedy when `s` is null). +pub fn pick(scratch: Allocator, ids: []const i32, z: []const f32, position: u64, s: ?Sampling) !?u32 { + const smp = s orelse return greedy(ids, z); + var vals: std.ArrayList(f64) = .empty; + defer vals.deinit(scratch); + var live: std.ArrayList(u64) = .empty; + defer live.deinit(scratch); + for (ids, z) |id, v| if (id >= 0) { + try vals.append(scratch, v); + try live.append(scratch, @intCast(id)); + }; + if (live.items.len == 0) return null; + return @intCast(try lanes.sampling.choose(scratch, vals.items, live.items, position, smp)); +} + +/// One slot's chain: candidates `ids` / base logits `base` [n][c] -> drafts and confidences [n]. +pub fn chain(scratch: Allocator, m: Markov, ids: []const i32, base: []const f32, c: usize, anchor: u32, params: Params, hid: ?[]const f32, drafts: []u32, conf: []f32) !void { + const z = try scratch.alloc(f32, c); + defer scratch.free(z); + const d = if (hid) |h| h.len / drafts.len else 0; + // every row's hidden part of its confidence at once (it does not depend on the chain) + const hp = try scratch.alloc(f32, drafts.len); + defer scratch.free(hp); + if (hid != null and m.conf != null) m.hidDots(hid.?[0 .. d * drafts.len], drafts.len, hp); + var prev = anchor; + for (drafts, conf, 0..) |*t, *q, i| { + const row_ids = ids[i * c ..][0..c]; + markovZ(m, row_ids, base[i * c ..][0..c], prev, z); + const tok = (try pick(scratch, row_ids, z, params.pos0 + i, params.sampling())) orelse prev; + q.* = if (hid != null) m.confidenceAfter(hp[i], d, prev) else 0.5; + t.* = tok; + prev = tok; + } +} + +const Ranked = struct { value: f32, id: u32 }; + +fn rankedBefore(_: void, a: Ranked, b: Ranked) bool { + return a.value > b.value or (a.value == b.value and a.id < b.id); +} + +/// A row's best `ids.len` by (value desc, id asc) as token ids (`id0`: first column), bounded selection, no full sort. +pub fn candidates(scratch: Allocator, logits: []const f32, id0: u32, ids: []i32, vals: []f32) !void { + const k = @min(ids.len, logits.len); + const best = try scratch.alloc(Ranked, k); + defer scratch.free(best); + var n: usize = 0; + for (logits, 0..) |v, i| { + const r: Ranked = .{ .value = v, .id = @intCast(i) }; + if (n == k) { + if (k == 0 or !rankedBefore({}, r, best[k - 1])) continue; + n -= 1; + } + var j = n; + while (j > 0 and rankedBefore({}, r, best[j - 1])) : (j -= 1) best[j] = best[j - 1]; + best[j] = r; + n += 1; + } + for (ids[0..k], vals[0..k], best[0..k]) |*i, *v, r| { + i.* = @intCast(r.id + id0); + v.* = r.value; + } + @memset(ids[k..], -1); + @memset(vals[k..], -std.math.inf(f32)); +} + +test "candidates: the bounded selection == the first k of a full sort (ties by id)" { + const a = std.testing.allocator; + var prng = std.Random.DefaultPrng.init(4101); + const rnd = prng.random(); + const logits = try a.alloc(f32, 5000); + defer a.free(logits); + for (logits) |*v| v.* = @floatFromInt(rnd.intRangeAtMost(i32, -40, 40)); // many ties + const all = try a.alloc(Ranked, logits.len); + defer a.free(all); + for (all, logits, 0..) |*r, v, i| r.* = .{ .value = v, .id = @intCast(i) }; + std.sort.pdq(Ranked, all, {}, rankedBefore); + for ([_]usize{ 1, 3, 64, 200 }) |k| { + var ids: [256]i32 = undefined; + var vals: [256]f32 = undefined; + try candidates(a, logits, 7, ids[0 .. k + 2], vals[0 .. k + 2]); + for (all[0..k], ids[0..k], vals[0..k]) |r, i, v| { + try std.testing.expectEqual(@as(i32, @intCast(r.id + 7)), i); + try std.testing.expectEqual(r.value, v); + } + } + // a row shorter than the list: its ids, then -1 + var ids: [4]i32 = undefined; + var vals: [4]f32 = undefined; + try candidates(a, &.{ 1.0, 3.0, 3.0 }, 10, &ids, &vals); + try std.testing.expectEqualSlices(i32, &.{ 11, 12, 10, -1 }, &ids); +} + +/// window = [pending, d_1 .. d_k], chosen = the target's choice after each row -> drafts kept. +pub fn accept(window: []const u32, chosen: []const u32) usize { + var a: usize = 0; + for (window[1..], chosen[0 .. window.len - 1]) |d, c| { + if (d != c) break; + a += 1; + } + return a; +} + +test "the context ring and the block's ids" { + const s: Shape = .{}; + try std.testing.expectEqual(@as(u32, 256), s.ring()); + const c = Context.of(s, 300, 0); + try std.testing.expectEqual(@as(u64, 172), c.first); + try std.testing.expectEqual(@as(u32, 128), c.count); + var sl: [128]i32 = undefined; + c.slots(s, &sl); + try std.testing.expectEqual(@as(i32, 172), sl[0]); + try std.testing.expectEqual(@as(i32, 299 % 256), sl[127]); + try std.testing.expectEqual(@as(u32, 0), Context.of(s, 10, 10).count); + var ids: [5]u32 = undefined; + blockIds(s, 42, &ids); + try std.testing.expectEqualSlices(u32, &.{ 42, 128799, 128799, 128799, 128799 }, &ids); + try std.testing.expectEqual(@as(usize, 2), accept(&.{ 1, 5, 6, 9 }, &.{ 5, 6, 7, 8 })); +} diff --git a/zig/src/families/deepseek_v41/draft/dspark_test.zig b/zig/src/families/deepseek_v41/draft/dspark_test.zig new file mode 100644 index 0000000000..ed019585cc --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/dspark_test.zig @@ -0,0 +1,94 @@ +//! draft/dspark.zig's chain lanes against one-at-a-time sums (run in draft.zig's tests: the chain needs lanes). +const std = @import("std"); +const dspark = @import("dspark.zig"); +const Markov = dspark.Markov; +const Params = dspark.Params; +const bf16 = dspark.bf16; +const toBf16 = dspark.toBf16; +const markovZ = dspark.markovZ; +const chain = dspark.chain; +const pick = dspark.pick; + +extern "c" fn exp(x: f64) f64; + +/// The chain's sums one candidate / one row at a time (the code before the lanes): the exactness tests' reference. +const reference = struct { + fn bias(m: Markov, prev: u32, id: u32) f32 { + const a = m.w1[@as(usize, prev) * m.rank ..][0..m.rank]; + const b = m.w2[@as(usize, id) * m.rank ..][0..m.rank]; + var acc: f32 = 0.0; + for (a, b) |x, y| acc += bf16(x) * bf16(y); + return acc; + } + + fn confidence(m: Markov, hid: []const f32, prev: u32) f32 { + const cw = m.conf orelse return 0.5; + var acc: f32 = 0.0; + for (hid, cw[0..hid.len]) |h, w| acc += h * w; + const me = m.w1[@as(usize, prev) * m.rank ..][0..m.rank]; + for (me, cw[hid.len..][0..m.rank]) |x, w| acc += bf16(x) * w; + return @floatCast(1.0 / (1.0 + exp(-@as(f64, acc)))); + } +}; + +test "the chain's lanes: markovZ and the confidences bit for bit the one-at-a-time sums (the served shape and ragged ones)" { + const a = std.testing.allocator; + var prng = std.Random.DefaultPrng.init(0x4101_5eed); + const rnd = prng.random(); + const vocab = 700; + for ([_][3]usize{ .{ 256, 64, 4096 }, .{ 256, 64, 5120 }, .{ 13, 21, 37 }, .{ 1, 1, 1 } }) |shape| { + const rank = shape[0]; + const c = shape[1]; + const D = shape[2]; + const w1 = try a.alloc(u16, vocab * rank); + defer a.free(w1); + const w2 = try a.alloc(u16, vocab * rank); + defer a.free(w2); + const cw = try a.alloc(f32, D + rank); + defer a.free(cw); + for (w1) |*x| x.* = toBf16(rnd.floatNorm(f32) * 0.3); + for (w2) |*x| x.* = toBf16(rnd.floatNorm(f32) * 0.3); + for (cw) |*x| x.* = rnd.floatNorm(f32) * 0.05; + const m: Markov = .{ .w1 = w1, .w2 = w2, .rank = @intCast(rank), .conf = cw }; + const n = 5; + const ids = try a.alloc(i32, n * c); + defer a.free(ids); + const base = try a.alloc(f32, n * c); + defer a.free(base); + const hid = try a.alloc(f32, n * D); + defer a.free(hid); + for (ids, 0..) |*x, i| x.* = if (i % 9 == 4) -1 else rnd.intRangeLessThan(i32, 0, vocab); + for (base) |*x| x.* = rnd.floatNorm(f32) * 4.0; + for (hid) |*x| x.* = bf16(toBf16(rnd.floatNorm(f32))); + // markovZ against the reference bias, for several anchors + const z = try a.alloc(f32, c); + defer a.free(z); + for (0..8) |_| { + const prev = rnd.intRangeLessThan(u32, 0, vocab); + for (0..n) |i| { + markovZ(m, ids[i * c ..][0..c], base[i * c ..][0..c], prev, z); + for (ids[i * c ..][0..c], base[i * c ..][0..c], z) |id, b, got| { + const want = if (id < 0) -std.math.inf(f32) else b + reference.bias(m, prev, @intCast(id)); + try std.testing.expectEqual(@as(u32, @bitCast(want)), @as(u32, @bitCast(got))); + } + } + for (0..n) |i| try std.testing.expectEqual(@as(u32, @bitCast(reference.confidence(m, hid[i * D ..][0..D], prev))), @as(u32, @bitCast(m.confidence(hid[i * D ..][0..D], prev)))); + } + // the whole chain, greedy and keyed, against the reference's rule + for ([_]bool{ false, true }) |sampled| { + const params: Params = if (sampled) .{ .seed = 77, .pos0 = 1000, .sampled = true, .temperature = 0.7, .top_p = 0.9, .top_k = 20 } else .{ .pos0 = 1000 }; + var drafts: [n]u32 = undefined; + var conf: [n]f32 = undefined; + try chain(a, m, ids, base, c, 3, params, hid, &drafts, &conf); + var prev: u32 = 3; + for (0..n) |i| { + const row = ids[i * c ..][0..c]; + for (row, base[i * c ..][0..c], z) |id, b, *o| o.* = if (id < 0) -std.math.inf(f32) else b + reference.bias(m, prev, @intCast(id)); + const tok = (try pick(a, row, z, params.pos0 + i, params.sampling())) orelse prev; + try std.testing.expectEqual(tok, drafts[i]); + try std.testing.expectEqual(@as(u32, @bitCast(reference.confidence(m, hid[i * D ..][0..D], prev))), @as(u32, @bitCast(conf[i]))); + prev = tok; + } + } + } +} diff --git a/zig/src/families/deepseek_v41/draft/fixtures/calib-golden.json b/zig/src/families/deepseek_v41/draft/fixtures/calib-golden.json new file mode 100644 index 0000000000..512134dadf --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/fixtures/calib-golden.json @@ -0,0 +1,6271 @@ +{ +"want": { +"slots": 1, +"world": 2, +"dspark": true, +"context": 300000 +}, +"pick": "calib-1bb.json", +"files": [ +{ +"name": "calib-0aa.json", +"content": { +"verify": [ +21.6479653, +25.9964947, +30.555484903, +34.989354999999996, +39.442625, +43.649612, +46.3674453, +49.793495, +55.292328, +58.085249, +58.9663, +60.2597465, +65.96, +67.900485, +68.385, +70.0825, +73.573348125, +77.06419625, +80.555044375, +84.04589250000001, +87.536740625, +91.02758874999999, +94.51843687499999, +98.00928499999999, +99.83179375, +101.6543025, +103.47681124999998, +105.29932, +107.12182875, +108.94433749999999, +110.76684624999999, +112.589355, +114.56503281250001, +116.54071062499999, +118.5163884375, +120.49206625, +122.46774406249999, +124.44342187500001, +126.4190996875, +128.39477749999998, +130.3704553125, +132.346133125, +134.32181093749998, +136.29748875, +138.2731665625, +140.24884437499998, +142.2245221875, +144.2002, +147.19892468749998, +150.197649375, +153.19637406249998, +156.19509874999997, +159.1938234375, +162.192548125, +165.19127281250002, +168.1899975, +171.1887221875, +174.187446875, +177.1861715625, +180.18489625, +183.1836209375, +186.18234562499998, +189.1810703125, +192.179795 +], +"draft": 3.61449, +"slot": 0.8305, +"meta": { +"shape": { +"slots": 1, +"world": 2, +"dspark": true, +"context": 196608, +"gpu": "NVIDIA GB10" +}, +"time": "2026-10-07T09:00:00", +"image": "py" +} +} +}, +{ +"name": "calib-1bb.json", +"content": { +"verify": [ +22.31749, +26.80051, +31.5004999, +36.0715, +40.6625, +44.9996, +47.80149, +51.3335, +57.0024, +59.8817, +60.79, +62.12345, +68.0, +70.0005, +70.5, +72.25, +75.8488125, +79.447625, +83.0464375, +86.64525, +90.2440625, +93.84287499999999, +97.4416875, +101.0405, +102.919375, +104.79825, +106.67712499999999, +108.556, +110.434875, +112.31375, +114.19262499999999, +116.0715, +118.10828125, +120.1450625, +122.18184375, +124.218625, +126.25540625, +128.2921875, +130.32896875, +132.36575, +134.40253125, +136.4393125, +138.47609375, +140.512875, +142.54965625, +144.5864375, +146.62321875, +148.66, +151.75146875, +154.8429375, +157.93440625, +161.02587499999998, +164.11734375, +167.2088125, +170.30028125, +173.39175, +176.48321875, +179.5746875, +182.66615625, +185.75762500000002, +188.84909375, +191.9405625, +195.03203125, +198.1235 +], +"draft": 3.61449, +"slot": 0.8305, +"meta": { +"shape": { +"slots": 1, +"world": 2, +"dspark": true, +"context": 300000, +"gpu": "NVIDIA GB10" +}, +"time": "2026-10-07T08:00:00", +"image": "py" +} +} +}, +{ +"name": "calib-2cc.json", +"content": { +"verify": [ +33.476235, +40.200765, +47.250749850000005, +54.10725, +60.993750000000006, +67.49940000000001, +71.702235, +77.00025, +85.5036, +89.82255, +91.185, +93.185175, +102.0, +105.00075000000001, +105.75, +108.375, +113.77321874999998, +119.1714375, +124.56965625, +129.967875, +135.36609375, +140.7643125, +146.16253125, +151.56074999999998, +154.3790625, +157.197375, +160.01568749999998, +162.834, +165.6523125, +168.47062499999998, +171.28893749999997, +174.10725, +177.162421875, +180.21759375, +183.272765625, +186.32793750000002, +189.383109375, +192.43828125000002, +195.493453125, +198.548625, +201.603796875, +204.65896874999999, +207.71414062499997, +210.7693125, +213.824484375, +216.87965624999998, +219.93482812500002, +222.99, +227.627203125, +232.26440625, +236.901609375, +241.53881249999998, +246.176015625, +250.81321874999998, +255.45042187500002, +260.087625, +264.724828125, +269.36203125000003, +273.999234375, +278.63643750000006, +283.273640625, +287.91084375, +292.54804687499995, +297.18525 +], +"draft": 3.61449, +"slot": 0.8305, +"meta": { +"shape": { +"slots": 1, +"world": 1, +"dspark": true, +"context": 300000, +"gpu": "NVIDIA GB10" +}, +"time": "2026-10-07T10:00:00", +"image": "py" +} +} +}, +{ +"name": "calib-3dd.json", +"content": { +"verify": [ +26.780987999999997, +32.160612, +37.80059988, +43.2858, +48.795, +53.99952, +57.361788, +61.6002, +68.40288, +71.85804, +72.948, +74.54813999999999, +81.6, +84.0006, +84.6, +86.7, +91.01857499999998, +95.33715, +99.65572499999999, +103.9743, +108.292875, +112.61144999999999, +116.930025, +121.24859999999998, +123.50325, +125.75789999999999, +128.01254999999998, +130.2672, +132.52185, +134.7765, +137.03115, +139.2858, +141.7299375, +144.174075, +146.6182125, +149.06235, +151.5064875, +153.950625, +156.39476249999998, +158.8389, +161.2830375, +163.727175, +166.17131249999997, +168.61545, +171.0595875, +173.50372499999997, +175.9478625, +178.392, +182.10176249999998, +185.811525, +189.5212875, +193.23104999999998, +196.9408125, +200.65057499999998, +204.3603375, +208.0701, +211.77986249999998, +215.48962500000002, +219.1993875, +222.90915, +226.6189125, +230.328675, +234.0384375, +237.7482 +], +"draft": 3.61449, +"slot": 0.8305, +"meta": { +"shape": { +"slots": 4, +"world": 2, +"dspark": true, +"context": 300000, +"gpu": "NVIDIA GB10" +}, +"time": "2026-10-07T11:00:00", +"image": "py" +} +} +} +], +"asks": [ +{ +"conf": [ +0.182752, +0.07627, +0.622451, +0.596704, +0.345946 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.657215, +0.353653, +0.632036, +0.119772, +0.066453 +], +"most": 2, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.984664, +0.19336, +0.002129, +0.265213, +0.263453 +], +"most": 4, +"first": null, +"sibs": [ +0.043568, +0.008424, +0.01834 +], +"k": 1, +"lens": [ +0, +0, +0 +] +}, +{ +"conf": [ +0.370792, +0.454513, +0.34209, +0.895694, +0.052983 +], +"most": 3, +"first": 24, +"k": 1 +}, +{ +"conf": [ +0.402547, +0.131557, +0.77494, +0.251566, +0.994983 +], +"most": 4, +"first": 80, +"k": 1 +}, +{ +"conf": [ +0.025436, +0.224125, +0.243332, +0.626085, +0.139273 +], +"most": 3, +"first": null, +"sibs": [ +0.445436, +0.207897 +], +"k": 1, +"lens": [ +1, +0 +] +}, +{ +"conf": [ +0.222248, +0.000693, +0.133964, +0.491826, +0.086199 +], +"most": 2, +"first": 0, +"k": 0 +}, +{ +"conf": [ +0.118456, +0.117572, +0.767072, +0.186625, +0.405849 +], +"most": 3, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.231372, +0.243297, +0.0125, +0.657628, +0.099194 +], +"most": 0, +"first": null, +"sibs": [ +0.438647, +0.196888 +], +"k": 0, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.226001, +0.63057, +0.383466, +0.283553, +0.474232 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.54372, +0.658287, +0.240936, +0.238322, +0.460637 +], +"most": 5, +"first": null, +"k": 2 +}, +{ +"conf": [ +0.172611, +0.513745, +0.343184, +0.531978, +0.292116 +], +"most": 4, +"first": 87, +"sibs": [ +0.390845 +], +"k": 0, +"lens": [ +0 +] +}, +{ +"conf": [ +0.115282, +0.26531, +0.165758, +0.466936, +0.023213 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.392145, +0.290016, +0.09406, +0.02913, +0.828086 +], +"most": 2, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.38223, +0.301616, +0.317613, +0.367037, +0.195032 +], +"most": 0, +"first": null, +"sibs": [ +0.420286 +], +"k": 0, +"lens": [ +0 +] +}, +{ +"conf": [ +0.1981, +0.562702, +0.834039, +0.224562, +0.705938 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.246031, +0.354353, +0.329588, +0.440956, +0.655302 +], +"most": 3, +"first": 31, +"k": 0 +}, +{ +"conf": [ +0.393557, +0.04376, +0.3746, +0.531779, +0.550145 +], +"most": 3, +"first": 25, +"sibs": [ +0.354835, +0.087652 +], +"k": 1, +"lens": [ +1, +0 +] +}, +{ +"conf": [ +0.549168, +0.152563, +0.474946, +0.427101, +0.54783 +], +"most": 1, +"first": 32, +"k": 1 +}, +{ +"conf": [ +0.510162, +0.601889, +0.186847, +0.50948, +0.472905 +], +"most": 2, +"first": 86, +"k": 1 +}, +{ +"conf": [ +0.200079, +0.752652, +0.062117, +0.119741, +0.683706 +], +"most": 3, +"first": null, +"sibs": [ +0.238393 +], +"k": 1, +"lens": [ +0 +] +}, +{ +"conf": [ +0.115066, +0.096677, +0.286181, +0.793401, +0.088014 +], +"most": 4, +"first": 66, +"k": 0 +}, +{ +"conf": [ +0.284828, +0.273106, +0.688441, +0.732246, +0.164697 +], +"most": 4, +"first": 20, +"k": 0 +}, +{ +"conf": [ +0.392151, +0.647959, +0.092928, +0.169209, +0.855288 +], +"most": 4, +"first": 97, +"sibs": [ +0.060077, +0.191755 +], +"k": 1, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.741754, +0.001297, +0.254084, +0.026248, +0.126241 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.613686, +0.008195, +0.29484, +0.266028, +0.311941 +], +"most": 0, +"first": 1, +"k": 0 +}, +{ +"conf": [ +0.336283, +0.29073, +0.651768, +0.39848, +0.177335 +], +"most": 2, +"first": null, +"sibs": [ +0.28192, +0.363735 +], +"k": 1, +"lens": [ +0, +1 +] +}, +{ +"conf": [ +0.743091, +0.239936, +0.468532, +0.251362, +0.531867 +], +"most": 1, +"first": 2, +"k": 1 +}, +{ +"conf": [ +0.325975, +0.416126, +0.2761, +0.402683, +0.041046 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.885203, +0.086235, +0.291622, +0.239341, +0.304002 +], +"most": 3, +"first": 73, +"sibs": [ +0.362719, +0.050578 +], +"k": 1, +"lens": [ +1, +0 +] +}, +{ +"conf": [ +0.806662, +0.158281, +0.001238, +0.419313, +0.219538 +], +"most": 2, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.483105, +0.344152, +0.914266, +0.010963, +0.314309 +], +"most": 1, +"first": 77, +"k": 1 +}, +{ +"conf": [ +0.025489, +0.324351, +0.211246, +0.053768, +0.796972 +], +"most": 2, +"first": null, +"sibs": [ +0.196752, +0.192146, +0.196972 +], +"k": 1, +"lens": [ +0, +0, +0 +] +}, +{ +"conf": [ +0.052371, +0.922254, +0.831648, +0.282258, +0.223313 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.237271, +0.344213, +0.045495, +0.378287, +0.52897 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.948522, +0.307904, +0.622596, +0.119572, +0.198011 +], +"most": 3, +"first": null, +"sibs": [ +0.429921, +0.197823 +], +"k": 1, +"lens": [ +1, +0 +] +}, +{ +"conf": [ +0.22573, +0.396451, +0.877062, +0.13176, +0.126874 +], +"most": 5, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.2889, +0.51653, +0.548148, +0.287841, +0.183276 +], +"most": 2, +"first": 75, +"k": 0 +}, +{ +"conf": [ +0.644151, +0.334678, +0.726641, +0.500211, +0.888407 +], +"most": 0, +"first": null, +"sibs": [ +0.037935, +0.294598 +], +"k": 0, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.504465, +0.094636, +0.938558, +0.225752, +0.153678 +], +"most": 3, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.368541, +0.602769, +0.016752, +0.974425, +0.322844 +], +"most": 2, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.406704, +0.794816, +0.793884, +0.260758, +0.711165 +], +"most": 5, +"first": 2, +"sibs": [ +0.021963 +], +"k": 2, +"lens": [ +0 +] +}, +{ +"conf": [ +0.207677, +0.882158, +0.45074, +0.748868, +0.093242 +], +"most": 1, +"first": 16, +"k": 0 +}, +{ +"conf": [ +0.338844, +0.3209, +0.722502, +0.021346, +0.791533 +], +"most": 5, +"first": 11, +"k": 1 +}, +{ +"conf": [ +0.03251, +0.362675, +0.079005, +0.35287, +0.378846 +], +"most": 3, +"first": null, +"sibs": [ +0.204238, +0.341815, +0.297439 +], +"k": 1, +"lens": [ +0, +1, +0 +] +}, +{ +"conf": [ +0.616381, +0.270612, +0.119632, +0.458422, +0.287313 +], +"most": 1, +"first": 14, +"k": 1 +}, +{ +"conf": [ +0.775346, +0.475259, +0.343959, +0.53009, +0.586266 +], +"most": 4, +"first": null, +"k": 2 +}, +{ +"conf": [ +0.058784, +0.708167, +0.342246, +0.063295, +0.487032 +], +"most": 3, +"first": 79, +"sibs": [ +0.123812 +], +"k": 0, +"lens": [ +0 +] +}, +{ +"conf": [ +0.500512, +0.827372, +0.208563, +0.615289, +0.210544 +], +"most": 0, +"first": 91, +"k": 0 +}, +{ +"conf": [ +0.256395, +0.635333, +0.54222, +0.688462, +0.112061 +], +"most": 5, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.394741, +0.11414, +0.185889, +0.777842, +0.106689 +], +"most": 4, +"first": null, +"sibs": [ +0.203621 +], +"k": 1, +"lens": [ +0 +] +}, +{ +"conf": [ +0.134804, +0.096366, +0.058724, +0.789976, +0.518988 +], +"most": 3, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.402358, +0.151062, +0.839533, +0.00484, +0.22288 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.883079, +0.799255, +0.079986, +0.054217, +0.240558 +], +"most": 5, +"first": 74, +"sibs": [ +0.494763, +0.332716 +], +"k": 2, +"lens": [ +2, +2 +] +}, +{ +"conf": [ +0.992859, +0.631552, +0.506241, +0.467543, +0.258631 +], +"most": 1, +"first": 92, +"k": 1 +}, +{ +"conf": [ +0.051494, +0.556413, +0.17592, +0.054921, +0.941655 +], +"most": 5, +"first": 45, +"k": 0 +}, +{ +"conf": [ +0.236211, +0.073031, +0.426405, +0.30069, +0.171258 +], +"most": 1, +"first": null, +"sibs": [ +0.157423, +0.014453, +0.069252 +], +"k": 1, +"lens": [ +0, +0, +0 +] +}, +{ +"conf": [ +0.651594, +0.37947, +0.732852, +0.017049, +0.153247 +], +"most": 5, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.262532, +0.295824, +0.266858, +0.433243, +0.006596 +], +"most": 2, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.387568, +0.274114, +0.349166, +0.64033, +0.317491 +], +"most": 3, +"first": null, +"sibs": [ +0.125306 +], +"k": 1, +"lens": [ +0 +] +}, +{ +"conf": [ +0.473979, +0.339965, +0.537274, +0.360044, +0.231161 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.372537, +0.804817, +0.030211, +0.605393, +0.217789 +], +"most": 3, +"first": 92, +"k": 1 +}, +{ +"conf": [ +0.481369, +0.238557, +0.737874, +0.384689, +0.186642 +], +"most": 2, +"first": null, +"sibs": [ +0.283797 +], +"k": 1, +"lens": [ +0 +] +}, +{ +"conf": [ +0.242972, +0.377206, +0.667573, +0.007481, +0.155855 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.22222, +0.560773, +0.955301, +0.373826, +0.981855 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.734636, +0.054084, +0.159627, +0.032493, +0.395074 +], +"most": 1, +"first": null, +"sibs": [ +0.008556, +0.236354, +0.359816 +], +"k": 1, +"lens": [ +0, +0, +1 +] +}, +{ +"conf": [ +0.745632, +0.512559, +0.041987, +0.694656, +0.070513 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.510527, +0.172638, +0.276106, +0.430332, +0.134592 +], +"most": 3, +"first": 23, +"k": 1 +}, +{ +"conf": [ +0.893631, +0.093914, +0.664708, +0.111827, +0.005759 +], +"most": 5, +"first": null, +"sibs": [ +0.457805 +], +"k": 1, +"lens": [ +1 +] +}, +{ +"conf": [ +0.774236, +0.511543, +0.727213, +0.39944, +0.01198 +], +"most": 5, +"first": 66, +"k": 2 +}, +{ +"conf": [ +0.21744, +0.201009, +0.315626, +0.185745, +0.050101 +], +"most": 4, +"first": 33, +"k": 0 +}, +{ +"conf": [ +0.028982, +0.315945, +0.216488, +0.088948, +0.238172 +], +"most": 3, +"first": null, +"sibs": [ +0.216244, +0.37907 +], +"k": 1, +"lens": [ +0, +1 +] +}, +{ +"conf": [ +0.028712, +0.223601, +0.576236, +0.59129, +0.397802 +], +"most": 5, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.50249, +0.27382, +0.102331, +0.239462, +0.921737 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.541209, +0.190858, +0.285673, +0.079767, +0.352903 +], +"most": 4, +"first": null, +"sibs": [ +0.122434, +0.406917, +0.447803 +], +"k": 1, +"lens": [ +0, +1, +1 +] +}, +{ +"conf": [ +0.270674, +0.343453, +0.491065, +0.173793, +0.633364 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.497378, +0.243548, +0.363983, +0.160728, +0.121328 +], +"most": 5, +"first": 62, +"k": 1 +}, +{ +"conf": [ +0.116677, +0.268458, +0.791229, +0.085194, +0.271096 +], +"most": 0, +"first": 93, +"sibs": [ +0.032115, +0.246208, +0.382174 +], +"k": 0, +"lens": [ +0, +0, +0 +] +}, +{ +"conf": [ +0.406774, +0.995398, +0.012492, +0.094047, +0.262414 +], +"most": 2, +"first": 12, +"k": 2 +}, +{ +"conf": [ +0.613491, +0.219648, +0.061105, +0.353247, +0.346841 +], +"most": 5, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.719593, +0.56401, +0.720538, +0.20606, +0.917496 +], +"most": 0, +"first": null, +"sibs": [ +0.134577, +0.280558, +0.302358 +], +"k": 0, +"lens": [ +0, +0, +0 +] +}, +{ +"conf": [ +0.645385, +0.442265, +0.556534, +0.164194, +0.280497 +], +"most": 5, +"first": 45, +"k": 1 +}, +{ +"conf": [ +0.125265, +0.123605, +0.228585, +0.106643, +0.171324 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.464398, +0.787621, +0.559524, +0.438783, +0.824455 +], +"most": 0, +"first": 15, +"sibs": [ +0.175302, +0.260406, +0.164255 +], +"k": 0, +"lens": [ +0, +0, +0 +] +}, +{ +"conf": [ +0.635227, +0.343073, +0.09898, +0.533342, +0.13134 +], +"most": 2, +"first": 66, +"k": 1 +}, +{ +"conf": [ +0.546705, +0.552522, +0.896269, +0.042064, +0.339421 +], +"most": 3, +"first": 2, +"k": 1 +}, +{ +"conf": [ +0.100574, +0.297527, +0.077118, +0.163597, +0.048634 +], +"most": 1, +"first": null, +"sibs": [ +0.00136, +0.429177 +], +"k": 1, +"lens": [ +0, +1 +] +}, +{ +"conf": [ +0.507445, +0.61498, +0.049128, +0.044884, +0.414564 +], +"most": 5, +"first": null, +"k": 2 +}, +{ +"conf": [ +0.574566, +0.342801, +0.280682, +0.762797, +0.261173 +], +"most": 2, +"first": 44, +"k": 1 +}, +{ +"conf": [ +0.307181, +0.84201, +0.046877, +0.174638, +0.487126 +], +"most": 5, +"first": null, +"sibs": [ +0.474601 +], +"k": 1, +"lens": [ +2 +] +}, +{ +"conf": [ +0.064791, +0.029633, +0.290691, +0.361669, +0.75701 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.246657, +0.190381, +0.775706, +0.552032, +0.437722 +], +"most": 2, +"first": 94, +"k": 0 +}, +{ +"conf": [ +0.681707, +0.002547, +0.033799, +0.241499, +0.951236 +], +"most": 4, +"first": null, +"sibs": [ +0.376481 +], +"k": 1, +"lens": [ +1 +] +}, +{ +"conf": [ +0.967881, +0.156116, +0.233213, +0.728333, +0.200163 +], +"most": 5, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.066804, +0.13334, +0.317677, +0.450751, +0.159301 +], +"most": 5, +"first": 95, +"k": 0 +}, +{ +"conf": [ +0.035562, +0.059042, +0.039882, +0.568991, +0.975593 +], +"most": 5, +"first": null, +"sibs": [ +0.210728, +0.48846 +], +"k": 1, +"lens": [ +0, +1 +] +}, +{ +"conf": [ +0.424692, +0.270882, +0.30596, +0.176706, +0.421565 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.695786, +0.762982, +0.342896, +0.037233, +0.899699 +], +"most": 4, +"first": 43, +"k": 2 +}, +{ +"conf": [ +0.372043, +0.135498, +0.488278, +0.277609, +0.136357 +], +"most": 3, +"first": 51, +"sibs": [ +0.364452, +0.139558, +0.333476 +], +"k": 1, +"lens": [ +1, +0, +1 +] +}, +{ +"conf": [ +0.273417, +0.630331, +0.777721, +0.052526, +0.048867 +], +"most": 2, +"first": 68, +"k": 0 +}, +{ +"conf": [ +0.185046, +0.110731, +0.329862, +0.839214, +0.352767 +], +"most": 3, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.276311, +0.386645, +0.994956, +0.059555, +0.186553 +], +"most": 1, +"first": null, +"sibs": [ +0.134386, +0.194576, +0.398491 +], +"k": 1, +"lens": [ +0, +0, +1 +] +}, +{ +"conf": [ +0.378012, +0.655785, +0.495701, +0.820474, +0.275768 +], +"most": 0, +"first": 39, +"k": 0 +}, +{ +"conf": [ +0.458622, +0.784407, +0.098824, +0.507863, +0.316529 +], +"most": 3, +"first": 37, +"k": 2 +}, +{ +"conf": [ +0.444469, +0.202154, +0.088923, +0.549334, +0.567932 +], +"most": 1, +"first": 41, +"sibs": [ +0.179078, +0.389811, +0.116577 +], +"k": 1, +"lens": [ +0, +1, +0 +] +}, +{ +"conf": [ +0.603326, +0.039429, +0.106759, +0.500489, +0.37469 +], +"most": 3, +"first": 10, +"k": 1 +}, +{ +"conf": [ +0.727416, +0.948412, +0.018953, +0.144962, +0.216042 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.202914, +0.846326, +0.327042, +0.112445, +0.608637 +], +"most": 1, +"first": 51, +"sibs": [ +0.337097, +0.423571 +], +"k": 1, +"lens": [ +1, +1 +] +}, +{ +"conf": [ +0.558248, +0.230984, +0.034874, +0.212517, +0.187405 +], +"most": 2, +"first": 19, +"k": 1 +}, +{ +"conf": [ +0.319481, +0.024975, +0.367006, +0.935609, +0.219393 +], +"most": 1, +"first": 0, +"k": 1 +}, +{ +"conf": [ +0.317478, +0.606635, +0.033913, +0.066316, +0.068303 +], +"most": 0, +"first": 71, +"sibs": [ +0.211808, +0.467578 +], +"k": 0, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.134537, +0.332474, +0.240925, +0.157186, +0.58256 +], +"most": 0, +"first": 61, +"k": 0 +}, +{ +"conf": [ +0.208217, +0.214078, +0.91078, +0.782585, +0.932859 +], +"most": 5, +"first": 31, +"k": 0 +}, +{ +"conf": [ +0.562946, +0.384722, +0.322482, +0.099976, +0.382443 +], +"most": 0, +"first": 90, +"sibs": [ +0.426196 +], +"k": 0, +"lens": [ +0 +] +}, +{ +"conf": [ +0.895551, +0.310162, +0.362385, +0.408383, +0.627164 +], +"most": 0, +"first": 51, +"k": 0 +}, +{ +"conf": [ +0.038548, +0.94303, +0.21142, +0.231752, +0.930109 +], +"most": 4, +"first": 0, +"k": 0 +}, +{ +"conf": [ +0.263901, +0.776282, +0.710729, +0.588156, +0.638038 +], +"most": 3, +"first": null, +"sibs": [ +0.406428, +0.341648 +], +"k": 1, +"lens": [ +3, +2 +] +}, +{ +"conf": [ +0.021908, +0.285434, +0.131896, +0.982585, +0.881574 +], +"most": 5, +"first": 29, +"k": 0 +}, +{ +"conf": [ +0.026188, +0.28418, +0.288387, +0.322261, +0.429295 +], +"most": 2, +"first": 54, +"k": 0 +}, +{ +"conf": [ +0.216916, +0.304151, +0.019044, +0.300221, +0.085621 +], +"most": 2, +"first": null, +"sibs": [ +0.056584, +0.47221, +0.066942 +], +"k": 1, +"lens": [ +0, +1, +0 +] +}, +{ +"conf": [ +0.550934, +0.665393, +0.990497, +0.368626, +0.694083 +], +"most": 3, +"first": 72, +"k": 3 +}, +{ +"conf": [ +0.385568, +0.267085, +0.133727, +0.838767, +0.881831 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.162897, +0.81164, +0.596814, +0.104566, +0.351816 +], +"most": 2, +"first": 60, +"sibs": [ +0.23332 +], +"k": 0, +"lens": [ +0 +] +}, +{ +"conf": [ +0.129527, +0.201796, +0.271902, +0.512879, +0.123704 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.306335, +0.51898, +0.137642, +0.250496, +0.700066 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.437534, +0.583328, +0.037605, +0.124767, +0.323227 +], +"most": 0, +"first": 34, +"sibs": [ +0.296823 +], +"k": 0, +"lens": [ +0 +] +}, +{ +"conf": [ +0.149028, +0.523146, +0.363628, +0.66406, +0.625994 +], +"most": 4, +"first": 12, +"k": 0 +}, +{ +"conf": [ +0.430892, +0.140888, +0.296602, +0.006747, +0.081132 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.000154, +0.83354, +0.293087, +0.579052, +0.018901 +], +"most": 5, +"first": null, +"sibs": [ +0.10925, +0.147902, +0.039611 +], +"k": 1, +"lens": [ +0, +0, +0 +] +}, +{ +"conf": [ +0.763321, +0.037932, +0.298353, +0.694764, +0.257484 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.240821, +0.525912, +0.816427, +0.589207, +0.143546 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.022618, +0.451225, +0.166197, +0.30166, +0.034016 +], +"most": 2, +"first": null, +"sibs": [ +0.155252, +0.416246, +0.139555 +], +"k": 1, +"lens": [ +0, +1, +0 +] +}, +{ +"conf": [ +0.112332, +0.725274, +0.342759, +0.238385, +0.064562 +], +"most": 2, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.184002, +0.616767, +0.392208, +0.5557, +0.253695 +], +"most": 5, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.28871, +0.609414, +0.077399, +0.762776, +0.23528 +], +"most": 4, +"first": null, +"sibs": [ +0.104912, +0.386001, +0.185266 +], +"k": 1, +"lens": [ +0, +1, +0 +] +}, +{ +"conf": [ +0.238125, +0.247961, +0.130879, +0.332155, +0.732294 +], +"most": 3, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.614748, +0.088008, +0.56505, +0.52087, +0.201813 +], +"most": 0, +"first": 63, +"k": 0 +}, +{ +"conf": [ +0.186555, +0.366924, +0.285886, +0.787776, +0.220924 +], +"most": 3, +"first": null, +"sibs": [ +0.320235, +0.138439 +], +"k": 1, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.413244, +0.199166, +0.434437, +0.270541, +0.06695 +], +"most": 4, +"first": 32, +"k": 1 +}, +{ +"conf": [ +0.478324, +0.061946, +0.257278, +0.891242, +0.15209 +], +"most": 5, +"first": 47, +"k": 1 +}, +{ +"conf": [ +0.998882, +0.380304, +0.139415, +0.316445, +0.312019 +], +"most": 1, +"first": 64, +"sibs": [ +0.417531 +], +"k": 1, +"lens": [ +1 +] +}, +{ +"conf": [ +0.569355, +0.672518, +0.25694, +0.257373, +0.01854 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.059427, +0.930448, +0.364707, +0.036625, +0.512495 +], +"most": 0, +"first": 57, +"k": 0 +}, +{ +"conf": [ +0.030164, +0.344485, +0.279408, +0.234925, +0.073249 +], +"most": 1, +"first": 14, +"sibs": [ +0.207578, +0.245375, +0.016777 +], +"k": 0, +"lens": [ +0, +0, +0 +] +}, +{ +"conf": [ +0.44832, +0.426959, +0.352537, +0.394091, +0.794887 +], +"most": 5, +"first": 45, +"k": 1 +}, +{ +"conf": [ +0.737982, +0.326595, +0.492364, +0.907618, +0.696208 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.23412, +0.174842, +0.205566, +0.702013, +0.188606 +], +"most": 2, +"first": null, +"sibs": [ +0.09819, +0.08649 +], +"k": 1, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.541939, +0.247954, +0.414753, +0.574384, +0.090176 +], +"most": 5, +"first": 77, +"k": 1 +}, +{ +"conf": [ +0.021258, +0.617395, +0.781551, +0.218379, +0.189181 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.353959, +0.374094, +0.363689, +0.09767, +0.278141 +], +"most": 3, +"first": 62, +"sibs": [ +0.286633 +], +"k": 1, +"lens": [ +0 +] +}, +{ +"conf": [ +0.145674, +0.080319, +0.068872, +0.294704, +0.439104 +], +"most": 5, +"first": 45, +"k": 0 +}, +{ +"conf": [ +0.200803, +0.171654, +0.045007, +0.129769, +0.326461 +], +"most": 5, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.333115, +0.124127, +0.16837, +0.278815, +0.828524 +], +"most": 3, +"first": 58, +"sibs": [ +0.45332, +0.288574 +], +"k": 1, +"lens": [ +1, +0 +] +}, +{ +"conf": [ +0.297377, +0.22302, +0.085074, +0.285445, +0.396758 +], +"most": 0, +"first": 55, +"k": 0 +}, +{ +"conf": [ +0.698307, +0.15553, +0.447224, +0.896379, +0.092263 +], +"most": 5, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.344328, +0.195324, +0.114177, +0.310498, +0.363316 +], +"most": 3, +"first": null, +"sibs": [ +0.190941, +0.013192, +0.13457 +], +"k": 1, +"lens": [ +0, +0, +0 +] +}, +{ +"conf": [ +0.304339, +0.076587, +0.503445, +0.238569, +0.204797 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.065426, +0.903402, +0.304912, +0.704625, +0.260012 +], +"most": 4, +"first": 27, +"k": 0 +}, +{ +"conf": [ +0.536978, +0.595816, +0.198751, +0.265725, +0.326938 +], +"most": 0, +"first": 61, +"sibs": [ +0.428544, +0.396008 +], +"k": 0, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.901801, +0.120512, +0.451452, +0.09739, +0.408118 +], +"most": 2, +"first": 46, +"k": 1 +}, +{ +"conf": [ +0.526924, +0.068197, +0.451863, +0.973285, +0.302472 +], +"most": 1, +"first": 29, +"k": 1 +}, +{ +"conf": [ +0.284743, +0.521989, +0.228172, +0.562421, +0.347478 +], +"most": 3, +"first": 9, +"sibs": [ +0.18165 +], +"k": 0, +"lens": [ +0 +] +}, +{ +"conf": [ +0.345895, +0.111341, +0.637817, +0.232399, +0.277188 +], +"most": 2, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.70675, +0.644244, +0.176111, +0.699422, +0.856256 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.939758, +0.069114, +0.292452, +0.579889, +0.320458 +], +"most": 1, +"first": null, +"sibs": [ +0.089634 +], +"k": 1, +"lens": [ +0 +] +}, +{ +"conf": [ +0.758551, +0.280383, +0.544763, +0.592117, +0.955149 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.737545, +0.355479, +0.589567, +0.101993, +0.251985 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.184193, +0.243523, +0.776746, +0.430876, +0.893343 +], +"most": 4, +"first": 64, +"sibs": [ +0.382285, +0.245725, +0.449119 +], +"k": 1, +"lens": [ +1, +0, +1 +] +}, +{ +"conf": [ +0.498358, +0.486649, +0.36337, +0.719013, +0.195257 +], +"most": 2, +"first": 50, +"k": 1 +}, +{ +"conf": [ +0.299808, +0.161169, +0.339022, +0.973909, +0.278622 +], +"most": 2, +"first": 30, +"k": 1 +}, +{ +"conf": [ +0.314853, +0.290795, +0.682198, +0.318864, +0.393935 +], +"most": 5, +"first": 9, +"sibs": [ +0.2321 +], +"k": 1, +"lens": [ +0 +] +}, +{ +"conf": [ +0.292386, +0.910948, +0.255558, +0.533151, +0.857193 +], +"most": 3, +"first": 1, +"k": 0 +}, +{ +"conf": [ +0.187644, +0.466039, +0.20724, +0.302401, +0.191454 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.534174, +0.162479, +0.565178, +0.943246, +0.083033 +], +"most": 0, +"first": 82, +"sibs": [ +0.362818 +], +"k": 0, +"lens": [ +0 +] +}, +{ +"conf": [ +0.420135, +0.085742, +0.464257, +0.334254, +0.742021 +], +"most": 1, +"first": 12, +"k": 1 +}, +{ +"conf": [ +0.097649, +0.046281, +0.272922, +0.160557, +0.053157 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.240553, +0.193662, +0.768477, +0.15496, +0.382727 +], +"most": 2, +"first": 3, +"sibs": [ +0.158986, +0.220274 +], +"k": 0, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.995422, +0.170527, +0.472391, +0.194637, +0.382378 +], +"most": 5, +"first": 63, +"k": 1 +}, +{ +"conf": [ +0.262624, +0.170504, +0.215627, +0.576311, +0.014684 +], +"most": 3, +"first": 16, +"k": 0 +}, +{ +"conf": [ +0.973412, +0.17894, +0.257972, +0.134821, +0.058089 +], +"most": 0, +"first": 0, +"sibs": [ +0.182045, +0.494022, +0.140674 +], +"k": 0, +"lens": [ +0, +0, +0 +] +}, +{ +"conf": [ +0.010022, +0.29676, +0.38233, +0.748886, +0.255363 +], +"most": 2, +"first": 14, +"k": 0 +}, +{ +"conf": [ +0.223968, +0.19885, +0.391897, +0.098225, +0.317866 +], +"most": 1, +"first": 46, +"k": 0 +}, +{ +"conf": [ +0.389599, +0.059963, +0.029002, +0.238448, +0.196785 +], +"most": 2, +"first": null, +"sibs": [ +0.388119 +], +"k": 1, +"lens": [ +1 +] +}, +{ +"conf": [ +0.103858, +0.141377, +0.22394, +0.077811, +0.392239 +], +"most": 3, +"first": 67, +"k": 0 +}, +{ +"conf": [ +0.31911, +0.368145, +0.100206, +0.748914, +0.182822 +], +"most": 0, +"first": 10, +"k": 0 +}, +{ +"conf": [ +0.234148, +0.742621, +0.886843, +0.986006, +0.100506 +], +"most": 5, +"first": 56, +"sibs": [ +0.272149, +0.245141 +], +"k": 0, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.301211, +0.151076, +0.716157, +0.413492, +0.649307 +], +"most": 0, +"first": 18, +"k": 0 +}, +{ +"conf": [ +0.531626, +0.733929, +0.680763, +0.772415, +0.082978 +], +"most": 2, +"first": null, +"k": 2 +}, +{ +"conf": [ +0.006542, +0.249135, +0.244573, +0.064486, +0.282716 +], +"most": 0, +"first": null, +"sibs": [ +0.297748, +0.496938, +0.367752 +], +"k": 0, +"lens": [ +0, +0, +0 +] +}, +{ +"conf": [ +0.522406, +0.684719, +0.034869, +0.375527, +0.363108 +], +"most": 2, +"first": null, +"k": 2 +}, +{ +"conf": [ +0.311042, +0.456173, +0.525682, +0.320809, +0.510442 +], +"most": 5, +"first": 28, +"k": 1 +}, +{ +"conf": [ +0.003062, +0.423847, +0.312501, +0.369688, +0.539065 +], +"most": 2, +"first": null, +"sibs": [ +0.401852, +0.135048, +0.181339 +], +"k": 1, +"lens": [ +1, +0, +0 +] +}, +{ +"conf": [ +0.56253, +0.160089, +0.490608, +0.071821, +0.213879 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.587494, +0.746424, +0.263371, +0.390044, +0.142824 +], +"most": 0, +"first": 69, +"k": 0 +}, +{ +"conf": [ +0.377796, +0.06978, +0.050608, +0.249782, +0.412461 +], +"most": 2, +"first": 78, +"sibs": [ +0.148647 +], +"k": 1, +"lens": [ +0 +] +}, +{ +"conf": [ +0.777744, +0.485982, +0.098101, +0.136634, +0.303795 +], +"most": 3, +"first": null, +"k": 2 +}, +{ +"conf": [ +0.012496, +0.247559, +0.041835, +0.103917, +0.135736 +], +"most": 4, +"first": 44, +"k": 0 +}, +{ +"conf": [ +0.009985, +0.600778, +0.285346, +0.31149, +0.231311 +], +"most": 2, +"first": null, +"sibs": [ +0.05512, +0.158461 +], +"k": 1, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.623465, +0.592054, +0.195732, +0.330948, +0.26965 +], +"most": 5, +"first": 6, +"k": 2 +}, +{ +"conf": [ +0.787746, +0.549449, +0.121981, +0.360679, +0.285415 +], +"most": 2, +"first": 23, +"k": 2 +}, +{ +"conf": [ +0.76665, +0.656275, +0.062553, +0.234722, +0.488896 +], +"most": 0, +"first": 89, +"sibs": [ +0.369189, +0.214024 +], +"k": 0, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.350192, +0.299827, +0.245339, +0.698701, +0.462705 +], +"most": 2, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.897074, +0.081866, +0.051909, +0.368434, +0.120791 +], +"most": 1, +"first": 67, +"k": 1 +}, +{ +"conf": [ +0.027681, +0.611648, +0.352352, +0.386566, +0.077167 +], +"most": 1, +"first": null, +"sibs": [ +0.310929 +], +"k": 1, +"lens": [ +0 +] +}, +{ +"conf": [ +0.664283, +0.607628, +0.053193, +0.310689, +0.580478 +], +"most": 4, +"first": 81, +"k": 2 +}, +{ +"conf": [ +0.20231, +0.346278, +0.160288, +0.185321, +0.647887 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.208636, +0.24817, +0.100514, +0.592665, +0.110525 +], +"most": 1, +"first": null, +"sibs": [ +0.003131, +0.404468, +0.457542 +], +"k": 1, +"lens": [ +0, +1, +1 +] +}, +{ +"conf": [ +0.711484, +0.309048, +0.739593, +0.561226, +0.639151 +], +"most": 0, +"first": 77, +"k": 0 +}, +{ +"conf": [ +0.3451, +0.325997, +0.028317, +0.238178, +0.326997 +], +"most": 2, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.433753, +0.053863, +0.413541, +0.639367, +0.110002 +], +"most": 2, +"first": 74, +"sibs": [ +0.36271 +], +"k": 1, +"lens": [ +1 +] +}, +{ +"conf": [ +0.00429, +0.286123, +0.954904, +0.553602, +0.751218 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.708217, +0.822611, +0.418612, +0.22011, +0.344792 +], +"most": 5, +"first": null, +"k": 2 +}, +{ +"conf": [ +0.360512, +0.478986, +0.035378, +0.15572, +0.160616 +], +"most": 1, +"first": null, +"sibs": [ +0.051205, +0.466911 +], +"k": 1, +"lens": [ +0, +1 +] +}, +{ +"conf": [ +0.613363, +0.09981, +0.556385, +0.373048, +0.047234 +], +"most": 0, +"first": 37, +"k": 0 +}, +{ +"conf": [ +0.357579, +0.393405, +0.394118, +0.414614, +0.361582 +], +"most": 3, +"first": 62, +"k": 1 +}, +{ +"conf": [ +0.303392, +0.021606, +0.115711, +0.349601, +0.189548 +], +"most": 5, +"first": 97, +"sibs": [ +0.397521, +0.06592, +0.070017 +], +"k": 1, +"lens": [ +1, +0, +0 +] +}, +{ +"conf": [ +0.3269, +0.198741, +0.691655, +0.178558, +0.75603 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.747332, +0.368791, +0.957202, +0.589689, +0.149902 +], +"most": 3, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.202225, +0.15285, +0.45718, +0.271084, +0.893801 +], +"most": 3, +"first": 47, +"sibs": [ +0.017962 +], +"k": 0, +"lens": [ +0 +] +}, +{ +"conf": [ +0.641341, +0.222631, +0.630471, +0.384394, +0.595783 +], +"most": 2, +"first": 61, +"k": 1 +}, +{ +"conf": [ +0.628826, +0.362873, +0.307085, +0.057208, +0.709973 +], +"most": 4, +"first": 81, +"k": 1 +}, +{ +"conf": [ +0.278767, +0.413851, +0.391861, +0.285811, +0.205693 +], +"most": 3, +"first": null, +"sibs": [ +0.193789, +0.246576 +], +"k": 1, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.338875, +0.434013, +0.491728, +0.233159, +0.061755 +], +"most": 5, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.268335, +0.533869, +0.204802, +0.145522, +0.53687 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.263682, +0.066906, +0.203346, +0.293756, +0.764089 +], +"most": 1, +"first": null, +"sibs": [ +0.334997 +], +"k": 1, +"lens": [ +0 +] +}, +{ +"conf": [ +0.572218, +0.089808, +0.60531, +0.356147, +0.11614 +], +"most": 0, +"first": 54, +"k": 0 +}, +{ +"conf": [ +0.745446, +0.545786, +0.052053, +0.610623, +0.696396 +], +"most": 5, +"first": null, +"k": 2 +}, +{ +"conf": [ +0.683252, +0.137717, +0.189362, +0.072277, +0.755082 +], +"most": 3, +"first": 60, +"sibs": [ +0.298065 +], +"k": 1, +"lens": [ +0 +] +}, +{ +"conf": [ +0.931377, +0.013262, +0.184543, +0.794353, +0.116063 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.338744, +0.778208, +0.563175, +0.141444, +0.041741 +], +"most": 3, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.723608, +0.28124, +0.063705, +0.13023, +0.073463 +], +"most": 2, +"first": null, +"sibs": [ +0.209296, +0.414394, +0.393387 +], +"k": 1, +"lens": [ +0, +1, +1 +] +}, +{ +"conf": [ +0.833741, +0.062228, +0.034566, +0.237305, +0.787476 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.697269, +0.788368, +0.100231, +0.368167, +0.257605 +], +"most": 1, +"first": 43, +"k": 1 +}, +{ +"conf": [ +0.995867, +0.126371, +0.650831, +0.097915, +0.287378 +], +"most": 2, +"first": 96, +"sibs": [ +0.496589, +0.011271, +0.085801 +], +"k": 1, +"lens": [ +1, +0, +0 +] +}, +{ +"conf": [ +0.397496, +0.009465, +0.232866, +0.208451, +0.646328 +], +"most": 4, +"first": 79, +"k": 1 +}, +{ +"conf": [ +0.230206, +0.522068, +0.451872, +0.233739, +0.371216 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.535626, +0.658565, +0.37657, +0.74181, +0.539115 +], +"most": 0, +"first": null, +"sibs": [ +0.097355 +], +"k": 0, +"lens": [ +0 +] +}, +{ +"conf": [ +0.436231, +0.266838, +0.234889, +0.346583, +0.149527 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.935769, +0.315963, +0.33006, +0.415987, +0.27188 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.217759, +0.263947, +0.476955, +0.322271, +0.650305 +], +"most": 0, +"first": null, +"sibs": [ +0.203212, +0.174684 +], +"k": 0, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.089305, +0.504815, +0.4117, +0.159015, +0.133428 +], +"most": 5, +"first": 49, +"k": 0 +}, +{ +"conf": [ +0.006427, +0.316088, +0.188409, +0.470559, +0.706751 +], +"most": 0, +"first": 9, +"k": 0 +}, +{ +"conf": [ +0.256839, +0.155115, +0.272713, +0.022092, +0.09896 +], +"most": 3, +"first": null, +"sibs": [ +0.455445, +0.405974 +], +"k": 1, +"lens": [ +1, +1 +] +}, +{ +"conf": [ +0.989759, +0.93273, +0.373592, +0.054414, +0.304392 +], +"most": 5, +"first": null, +"k": 3 +}, +{ +"conf": [ +0.438939, +0.344816, +0.088759, +0.026886, +0.82077 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.461229, +0.443706, +0.757647, +0.27284, +0.108922 +], +"most": 4, +"first": null, +"sibs": [ +0.130949, +0.268129, +0.079357 +], +"k": 1, +"lens": [ +0, +0, +0 +] +}, +{ +"conf": [ +0.275901, +0.339701, +0.147988, +0.394848, +0.686588 +], +"most": 3, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.345058, +0.932509, +0.16604, +0.198228, +0.165042 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.177037, +0.230161, +0.225137, +0.226765, +0.497718 +], +"most": 0, +"first": 82, +"sibs": [ +0.308568, +0.308057 +], +"k": 0, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.237411, +0.064236, +0.024028, +0.870828, +0.184659 +], +"most": 0, +"first": 47, +"k": 0 +}, +{ +"conf": [ +0.209368, +0.207326, +0.14278, +0.122726, +0.184153 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.183808, +0.280684, +0.157196, +0.185419, +0.204731 +], +"most": 0, +"first": 34, +"sibs": [ +0.107417, +0.073466 +], +"k": 0, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.049946, +0.60214, +0.395591, +0.148256, +0.630456 +], +"most": 5, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.829158, +0.204409, +0.243157, +0.123395, +0.024835 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.24477, +0.739779, +0.338118, +0.176491, +0.669371 +], +"most": 0, +"first": 65, +"sibs": [ +0.172301, +0.386857 +], +"k": 0, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.059752, +0.930995, +0.581286, +0.406999, +0.42483 +], +"most": 3, +"first": 88, +"k": 0 +}, +{ +"conf": [ +0.253538, +0.084202, +0.901685, +0.725748, +0.072768 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.197874, +0.107647, +0.499914, +0.985106, +0.633664 +], +"most": 0, +"first": null, +"sibs": [ +0.069348, +0.289668 +], +"k": 0, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.782758, +0.672207, +0.463443, +0.783708, +0.642744 +], +"most": 5, +"first": null, +"k": 2 +}, +{ +"conf": [ +0.049853, +0.39926, +0.105489, +0.036296, +0.649392 +], +"most": 1, +"first": 33, +"k": 0 +}, +{ +"conf": [ +0.995875, +0.451928, +0.206846, +0.384326, +0.416594 +], +"most": 5, +"first": 80, +"sibs": [ +0.464123 +], +"k": 2, +"lens": [ +1 +] +}, +{ +"conf": [ +0.407372, +0.202172, +0.243052, +0.027358, +0.480934 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.015145, +0.877897, +0.921918, +0.618893, +0.340631 +], +"most": 4, +"first": 91, +"k": 0 +}, +{ +"conf": [ +0.150835, +0.397263, +0.716253, +0.663826, +0.079602 +], +"most": 1, +"first": 16, +"sibs": [ +0.279294, +0.2621 +], +"k": 0, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.392888, +0.947329, +0.627743, +0.758073, +0.092839 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.220745, +0.054896, +0.970968, +0.37671, +0.193139 +], +"most": 5, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.228302, +0.041892, +0.706098, +0.256824, +0.227155 +], +"most": 1, +"first": null, +"sibs": [ +0.330176, +0.064411 +], +"k": 1, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.188019, +0.192443, +0.581736, +0.396698, +0.401674 +], +"most": 3, +"first": 94, +"k": 0 +}, +{ +"conf": [ +0.348577, +0.692447, +0.187265, +0.034777, +0.135339 +], +"most": 2, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.762575, +0.286601, +0.685149, +0.285909, +0.793388 +], +"most": 2, +"first": null, +"sibs": [ +0.300854 +], +"k": 1, +"lens": [ +0 +] +}, +{ +"conf": [ +0.556516, +0.011686, +0.188436, +0.15077, +0.599195 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.760255, +0.141163, +0.201939, +0.796594, +0.332643 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.676767, +0.969825, +0.573249, +0.327459, +0.406809 +], +"most": 3, +"first": null, +"sibs": [ +0.446715, +0.427653, +0.261449 +], +"k": 3, +"lens": [ +3, +3, +2 +] +}, +{ +"conf": [ +0.056485, +0.198344, +0.201329, +0.260734, +0.393096 +], +"most": 0, +"first": 18, +"k": 0 +}, +{ +"conf": [ +0.301955, +0.076954, +0.840994, +0.205619, +0.799685 +], +"most": 0, +"first": 11, +"k": 0 +}, +{ +"conf": [ +0.188776, +0.905859, +0.787657, +0.324921, +0.683989 +], +"most": 2, +"first": 25, +"sibs": [ +0.373746 +], +"k": 0, +"lens": [ +0 +] +}, +{ +"conf": [ +0.564439, +0.393792, +0.170517, +0.019134, +0.434207 +], +"most": 5, +"first": 63, +"k": 1 +}, +{ +"conf": [ +0.946754, +0.25139, +0.95593, +0.319923, +0.183228 +], +"most": 2, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.58458, +0.003592, +0.276101, +0.733888, +0.481456 +], +"most": 4, +"first": null, +"sibs": [ +0.36536, +0.052765, +0.315157 +], +"k": 1, +"lens": [ +1, +0, +0 +] +}, +{ +"conf": [ +0.213186, +0.527912, +0.361235, +0.221644, +0.254705 +], +"most": 5, +"first": 36, +"k": 0 +}, +{ +"conf": [ +0.089743, +0.151601, +0.173864, +0.176429, +0.272332 +], +"most": 0, +"first": 9, +"k": 0 +}, +{ +"conf": [ +0.634099, +0.14108, +0.723781, +0.430643, +0.213166 +], +"most": 2, +"first": 7, +"sibs": [ +0.206786 +], +"k": 1, +"lens": [ +0 +] +}, +{ +"conf": [ +0.106222, +0.053667, +0.155716, +0.013083, +0.871079 +], +"most": 3, +"first": 74, +"k": 0 +}, +{ +"conf": [ +0.43135, +0.067498, +0.112257, +0.637999, +0.970361 +], +"most": 0, +"first": 36, +"k": 0 +}, +{ +"conf": [ +0.865548, +0.736312, +0.992986, +0.549519, +0.173906 +], +"most": 1, +"first": null, +"sibs": [ +0.489628 +], +"k": 1, +"lens": [ +1 +] +}, +{ +"conf": [ +0.084119, +0.364776, +0.15218, +0.776373, +0.094905 +], +"most": 1, +"first": 86, +"k": 0 +}, +{ +"conf": [ +0.68949, +0.062715, +0.277776, +0.037121, +0.730153 +], +"most": 3, +"first": 14, +"k": 1 +}, +{ +"conf": [ +0.281851, +0.234837, +0.258856, +0.784997, +0.223415 +], +"most": 1, +"first": null, +"sibs": [ +0.334874, +0.21849, +0.04578 +], +"k": 1, +"lens": [ +0, +0, +0 +] +}, +{ +"conf": [ +0.406433, +0.093432, +0.505753, +0.159845, +0.701723 +], +"most": 0, +"first": 42, +"k": 0 +}, +{ +"conf": [ +0.224213, +0.532666, +0.373504, +0.111609, +0.584998 +], +"most": 4, +"first": 34, +"k": 0 +}, +{ +"conf": [ +0.041845, +0.019437, +0.087145, +0.15911, +0.266731 +], +"most": 5, +"first": null, +"sibs": [ +0.371034, +0.426038 +], +"k": 1, +"lens": [ +1, +1 +] +}, +{ +"conf": [ +0.199179, +0.333779, +0.267521, +0.289339, +0.542916 +], +"most": 2, +"first": 52, +"k": 0 +}, +{ +"conf": [ +0.197493, +0.261165, +0.001848, +0.164159, +0.702162 +], +"most": 0, +"first": 37, +"k": 0 +}, +{ +"conf": [ +0.171926, +0.200095, +0.650493, +0.577455, +0.178518 +], +"most": 0, +"first": 58, +"sibs": [ +0.321606, +0.377082 +], +"k": 0, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.118858, +0.93481, +0.43718, +0.184746, +0.026918 +], +"most": 2, +"first": 67, +"k": 0 +}, +{ +"conf": [ +0.347265, +0.867989, +0.039865, +0.113321, +0.684188 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.100364, +0.907354, +0.146592, +0.367527, +0.560658 +], +"most": 4, +"first": 96, +"sibs": [ +0.044368 +], +"k": 0, +"lens": [ +0 +] +}, +{ +"conf": [ +0.300334, +0.808587, +0.482102, +0.386986, +0.679943 +], +"most": 0, +"first": 78, +"k": 0 +}, +{ +"conf": [ +0.81328, +0.899817, +0.075318, +0.003989, +0.185648 +], +"most": 0, +"first": 12, +"k": 0 +}, +{ +"conf": [ +0.037922, +0.381632, +0.549587, +0.174572, +0.428678 +], +"most": 2, +"first": 33, +"sibs": [ +0.098716, +0.258351 +], +"k": 0, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.345366, +0.318602, +0.934544, +0.297814, +0.015529 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.068666, +0.291474, +0.922845, +0.635101, +0.253622 +], +"most": 1, +"first": 83, +"k": 0 +}, +{ +"conf": [ +0.748847, +0.162841, +0.299231, +0.864466, +0.384401 +], +"most": 4, +"first": 47, +"sibs": [ +0.339027, +0.076949 +], +"k": 1, +"lens": [ +1, +0 +] +}, +{ +"conf": [ +0.042616, +0.681971, +0.331399, +0.618302, +0.758537 +], +"most": 4, +"first": 89, +"k": 0 +}, +{ +"conf": [ +0.346441, +0.001251, +0.250828, +0.090596, +0.256368 +], +"most": 2, +"first": 18, +"k": 1 +}, +{ +"conf": [ +0.468988, +0.116703, +0.167371, +0.085073, +0.505855 +], +"most": 1, +"first": null, +"sibs": [ +0.442392, +0.134772 +], +"k": 1, +"lens": [ +1, +0 +] +}, +{ +"conf": [ +0.575375, +0.201732, +0.524893, +0.432125, +0.01836 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.320192, +0.147039, +0.747941, +0.812383, +0.010543 +], +"most": 0, +"first": 1, +"k": 0 +}, +{ +"conf": [ +0.428841, +0.300574, +0.183083, +0.395035, +0.386929 +], +"most": 3, +"first": 81, +"sibs": [ +0.215238 +], +"k": 1, +"lens": [ +0 +] +}, +{ +"conf": [ +0.351481, +0.152208, +0.266438, +0.753467, +0.184025 +], +"most": 2, +"first": 66, +"k": 1 +}, +{ +"conf": [ +0.48632, +0.330215, +0.04088, +0.707191, +0.642267 +], +"most": 4, +"first": 96, +"k": 1 +}, +{ +"conf": [ +0.316109, +0.077972, +0.296435, +0.184219, +0.110117 +], +"most": 0, +"first": null, +"sibs": [ +0.436968, +0.23634 +], +"k": 0, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.148531, +0.268079, +0.360515, +0.396656, +0.191568 +], +"most": 4, +"first": 29, +"k": 0 +}, +{ +"conf": [ +0.080501, +0.066555, +0.279821, +0.270946, +0.244769 +], +"most": 1, +"first": 88, +"k": 0 +}, +{ +"conf": [ +0.282087, +0.529716, +0.742135, +0.923372, +0.189962 +], +"most": 1, +"first": null, +"sibs": [ +0.460531 +], +"k": 1, +"lens": [ +1 +] +}, +{ +"conf": [ +0.704246, +0.653923, +0.587173, +0.741539, +0.441169 +], +"most": 3, +"first": 66, +"k": 2 +}, +{ +"conf": [ +0.600672, +0.037479, +0.087099, +0.01198, +0.785384 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.17634, +0.931306, +0.694093, +0.551277, +0.076701 +], +"most": 0, +"first": null, +"sibs": [ +0.2273 +], +"k": 0, +"lens": [ +0 +] +}, +{ +"conf": [ +0.515888, +0.614581, +0.617821, +0.739154, +0.118179 +], +"most": 5, +"first": null, +"k": 2 +}, +{ +"conf": [ +0.685015, +0.187141, +0.590484, +0.134382, +0.234673 +], +"most": 0, +"first": 22, +"k": 0 +}, +{ +"conf": [ +0.650719, +0.931641, +0.41278, +0.148298, +0.104425 +], +"most": 5, +"first": 31, +"sibs": [ +0.395695, +0.4218 +], +"k": 3, +"lens": [ +2, +2 +] +}, +{ +"conf": [ +0.383, +0.037569, +0.43303, +0.831498, +0.425236 +], +"most": 1, +"first": 24, +"k": 1 +}, +{ +"conf": [ +0.47621, +0.604067, +0.566403, +0.374498, +0.587645 +], +"most": 3, +"first": 14, +"k": 1 +}, +{ +"conf": [ +0.019631, +0.059923, +0.223808, +0.826362, +0.216411 +], +"most": 0, +"first": 19, +"sibs": [ +0.260572 +], +"k": 0, +"lens": [ +0 +] +}, +{ +"conf": [ +0.366553, +0.02916, +0.065456, +0.552714, +0.676879 +], +"most": 4, +"first": 53, +"k": 1 +}, +{ +"conf": [ +0.082936, +0.283372, +0.261425, +0.002993, +0.122622 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.283727, +0.848373, +0.363402, +0.491041, +0.608546 +], +"most": 1, +"first": null, +"sibs": [ +0.353025 +], +"k": 1, +"lens": [ +1 +] +}, +{ +"conf": [ +0.613389, +0.374907, +0.024875, +0.144442, +0.268411 +], +"most": 3, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.201194, +0.219101, +0.074604, +0.235707, +0.127277 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.347908, +0.382794, +0.314573, +0.502905, +0.479107 +], +"most": 1, +"first": null, +"sibs": [ +0.466217, +0.153989 +], +"k": 1, +"lens": [ +1, +0 +] +}, +{ +"conf": [ +0.293915, +0.58402, +0.649949, +0.25933, +0.029182 +], +"most": 3, +"first": 20, +"k": 0 +}, +{ +"conf": [ +0.997105, +0.319623, +0.653985, +0.177495, +0.273476 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.602084, +0.721424, +0.036252, +0.347321, +0.219452 +], +"most": 4, +"first": null, +"sibs": [ +0.20771 +], +"k": 2, +"lens": [ +0 +] +}, +{ +"conf": [ +0.283156, +0.079498, +0.318771, +0.20273, +0.74242 +], +"most": 2, +"first": 1, +"k": 0 +}, +{ +"conf": [ +0.697405, +0.234041, +0.610378, +0.303349, +0.362149 +], +"most": 3, +"first": 49, +"k": 1 +}, +{ +"conf": [ +0.902872, +0.308429, +0.106236, +0.92592, +0.306128 +], +"most": 2, +"first": null, +"sibs": [ +0.395855, +0.374296, +0.291568 +], +"k": 1, +"lens": [ +1, +1, +0 +] +}, +{ +"conf": [ +0.056824, +0.768686, +0.159608, +0.108158, +0.304438 +], +"most": 3, +"first": 15, +"k": 0 +}, +{ +"conf": [ +0.187018, +0.294563, +0.364335, +0.142057, +0.387159 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.299928, +0.561802, +0.297741, +0.678461, +0.118946 +], +"most": 1, +"first": null, +"sibs": [ +0.436053, +0.19508, +0.035094 +], +"k": 1, +"lens": [ +1, +0, +0 +] +}, +{ +"conf": [ +0.106573, +0.322202, +0.691361, +0.734692, +0.008822 +], +"most": 5, +"first": 31, +"k": 0 +}, +{ +"conf": [ +0.350934, +0.350623, +0.206838, +0.267642, +0.005161 +], +"most": 2, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.050486, +0.050419, +0.844482, +0.614647, +0.08597 +], +"most": 0, +"first": 51, +"sibs": [ +0.372825 +], +"k": 0, +"lens": [ +0 +] +}, +{ +"conf": [ +0.436838, +0.242064, +0.650866, +0.632514, +0.130614 +], +"most": 5, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.215579, +0.194408, +0.371362, +0.309585, +0.982821 +], +"most": 3, +"first": 12, +"k": 0 +}, +{ +"conf": [ +0.670007, +0.149929, +0.161877, +0.35292, +0.060997 +], +"most": 5, +"first": null, +"sibs": [ +0.104176, +0.425159, +0.368323 +], +"k": 1, +"lens": [ +0, +1, +1 +] +}, +{ +"conf": [ +0.216425, +0.050937, +0.522382, +0.403611, +0.069517 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.980971, +0.108191, +0.060425, +0.133168, +0.06268 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.262935, +0.355781, +0.376664, +0.530923, +0.265275 +], +"most": 3, +"first": 13, +"sibs": [ +0.428988, +0.22949 +], +"k": 1, +"lens": [ +1, +0 +] +}, +{ +"conf": [ +0.383826, +0.012637, +0.728842, +0.187854, +0.048255 +], +"most": 2, +"first": 9, +"k": 1 +}, +{ +"conf": [ +0.084925, +0.327734, +0.376799, +0.319581, +0.841575 +], +"most": 2, +"first": 70, +"k": 0 +}, +{ +"conf": [ +0.615957, +0.815616, +0.964981, +0.266317, +0.316669 +], +"most": 4, +"first": 69, +"sibs": [ +0.235977, +0.028795, +0.100488 +], +"k": 3, +"lens": [ +0, +0, +0 +] +}, +{ +"conf": [ +0.25068, +0.002467, +0.678329, +0.523458, +0.065461 +], +"most": 3, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.641436, +0.058814, +0.320215, +0.117668, +0.234697 +], +"most": 4, +"first": 4, +"k": 1 +}, +{ +"conf": [ +0.374792, +0.483379, +0.176214, +0.916167, +0.246726 +], +"most": 0, +"first": null, +"sibs": [ +0.385273, +0.478611, +0.184032 +], +"k": 0, +"lens": [ +0, +0, +0 +] +}, +{ +"conf": [ +0.380628, +0.901627, +0.586569, +0.013933, +0.686351 +], +"most": 0, +"first": 69, +"k": 0 +}, +{ +"conf": [ +0.123062, +0.265041, +0.135336, +0.628993, +0.475482 +], +"most": 2, +"first": 0, +"k": 0 +}, +{ +"conf": [ +0.224419, +0.716274, +0.261864, +0.057668, +0.26641 +], +"most": 5, +"first": 96, +"sibs": [ +0.482859, +0.339373, +0.070637 +], +"k": 1, +"lens": [ +2, +1, +0 +] +}, +{ +"conf": [ +0.710374, +0.03045, +0.102043, +0.551143, +0.442709 +], +"most": 0, +"first": 89, +"k": 0 +}, +{ +"conf": [ +0.46471, +0.066446, +0.587735, +0.109108, +0.230758 +], +"most": 1, +"first": 24, +"k": 1 +}, +{ +"conf": [ +0.377167, +0.745356, +0.226346, +0.285507, +0.849947 +], +"most": 5, +"first": 35, +"sibs": [ +0.486193, +0.455127, +0.024643 +], +"k": 2, +"lens": [ +2, +2, +0 +] +}, +{ +"conf": [ +0.253916, +0.743935, +0.101045, +0.296338, +0.063428 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.757378, +0.715756, +0.719717, +0.366346, +0.202142 +], +"most": 2, +"first": null, +"k": 2 +}, +{ +"conf": [ +0.313157, +0.48581, +0.125444, +0.116187, +0.261201 +], +"most": 2, +"first": null, +"sibs": [ +0.01341, +0.133268, +0.387123 +], +"k": 1, +"lens": [ +0, +0, +1 +] +}, +{ +"conf": [ +0.220549, +0.31534, +0.214483, +0.304697, +0.0031 +], +"most": 5, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.02583, +0.463951, +0.733165, +0.341489, +0.397173 +], +"most": 5, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.455903, +0.703591, +0.261538, +0.150885, +0.650227 +], +"most": 0, +"first": null, +"sibs": [ +0.044326, +0.124236, +0.011106 +], +"k": 0, +"lens": [ +0, +0, +0 +] +}, +{ +"conf": [ +0.256718, +0.313845, +0.191365, +0.427457, +0.094196 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.183781, +0.251766, +0.689001, +0.243591, +0.242649 +], +"most": 4, +"first": 83, +"k": 0 +}, +{ +"conf": [ +0.685343, +0.297471, +0.199671, +0.340352, +0.561211 +], +"most": 5, +"first": null, +"sibs": [ +0.320188, +0.321429 +], +"k": 1, +"lens": [ +0, +0 +] +}, +{ +"conf": [ +0.251143, +0.970871, +0.451289, +0.352289, +0.224975 +], +"most": 4, +"first": 65, +"k": 0 +}, +{ +"conf": [ +0.754365, +0.398811, +0.066918, +0.846555, +0.072044 +], +"most": 1, +"first": 42, +"k": 1 +}, +{ +"conf": [ +0.458346, +0.434018, +0.703531, +0.028579, +0.044371 +], +"most": 1, +"first": 96, +"sibs": [ +0.061518 +], +"k": 1, +"lens": [ +0 +] +}, +{ +"conf": [ +0.005159, +0.395373, +0.214123, +0.313061, +0.228399 +], +"most": 2, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.393259, +0.330625, +0.305133, +0.609504, +0.673697 +], +"most": 3, +"first": 81, +"k": 1 +}, +{ +"conf": [ +0.738643, +0.059805, +0.211661, +0.56708, +0.328086 +], +"most": 3, +"first": 55, +"sibs": [ +0.401964, +0.127943 +], +"k": 1, +"lens": [ +1, +0 +] +}, +{ +"conf": [ +0.031891, +0.493047, +0.050023, +0.869949, +0.511447 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.647213, +0.373861, +0.889661, +0.216492, +0.643797 +], +"most": 2, +"first": 42, +"k": 1 +}, +{ +"conf": [ +0.110822, +0.878664, +0.427929, +0.352443, +0.003855 +], +"most": 2, +"first": 90, +"sibs": [ +0.457892 +], +"k": 1, +"lens": [ +2 +] +}, +{ +"conf": [ +0.017548, +0.670339, +0.313403, +0.708698, +0.378821 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.183975, +0.13228, +0.350752, +0.473373, +0.359003 +], +"most": 4, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.18694, +0.251597, +0.019876, +0.19964, +0.127515 +], +"most": 4, +"first": 61, +"sibs": [ +0.014462, +0.38372, +0.311376 +], +"k": 0, +"lens": [ +0, +0, +0 +] +}, +{ +"conf": [ +0.325107, +0.53688, +0.519368, +0.774005, +0.644386 +], +"most": 2, +"first": 95, +"k": 1 +}, +{ +"conf": [ +0.451865, +0.026058, +0.077952, +0.065114, +0.915224 +], +"most": 1, +"first": 4, +"k": 1 +}, +{ +"conf": [ +0.280124, +0.941461, +0.344146, +0.266877, +0.175559 +], +"most": 0, +"first": null, +"sibs": [ +0.331454, +0.418718, +0.239807 +], +"k": 0, +"lens": [ +0, +0, +0 +] +}, +{ +"conf": [ +0.786754, +0.619258, +0.592847, +0.075248, +0.474141 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.141966, +0.351679, +0.127044, +0.24893, +0.49377 +], +"most": 4, +"first": 15, +"k": 0 +}, +{ +"conf": [ +0.924972, +0.043281, +0.799334, +0.290756, +0.202286 +], +"most": 0, +"first": 40, +"sibs": [ +0.178192 +], +"k": 0, +"lens": [ +0 +] +}, +{ +"conf": [ +0.304613, +0.234341, +0.846127, +0.293326, +0.541511 +], +"most": 0, +"first": null, +"k": 0 +}, +{ +"conf": [ +0.4062, +0.222732, +0.936579, +0.302671, +0.3173 +], +"most": 3, +"first": 48, +"k": 1 +}, +{ +"conf": [ +0.992284, +0.392707, +0.782539, +0.8612, +0.006077 +], +"most": 1, +"first": 27, +"sibs": [ +0.213079, +0.422596 +], +"k": 1, +"lens": [ +0, +1 +] +}, +{ +"conf": [ +0.820415, +0.246399, +0.521773, +0.811829, +0.337972 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.688137, +0.025928, +0.345414, +0.314481, +0.259257 +], +"most": 0, +"first": 52, +"k": 0 +}, +{ +"conf": [ +0.482955, +0.046026, +0.730492, +0.390538, +0.289828 +], +"most": 0, +"first": null, +"sibs": [ +0.210462 +], +"k": 0, +"lens": [ +0 +] +}, +{ +"conf": [ +0.437415, +0.293865, +0.148873, +0.893924, +0.177587 +], +"most": 0, +"first": 96, +"k": 0 +}, +{ +"conf": [ +0.294207, +0.347358, +0.753126, +0.253984, +0.169252 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.633864, +0.458313, +0.08248, +0.270808, +0.227106 +], +"most": 5, +"first": null, +"sibs": [ +0.194 +], +"k": 1, +"lens": [ +0 +] +}, +{ +"conf": [ +0.501569, +0.388695, +0.314117, +0.371986, +0.746457 +], +"most": 2, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.178466, +0.069296, +0.111698, +0.034625, +0.96781 +], +"most": 1, +"first": null, +"k": 1 +}, +{ +"conf": [ +0.588933, +0.027401, +0.5111, +0.279772, +0.318275 +], +"most": 3, +"first": null, +"sibs": [ +0.20048 +], +"k": 1, +"lens": [ +0 +] +}, +{ +"conf": [ +0.857989, +0.188545, +0.026385, +0.135328, +0.354977 +], +"most": 3, +"first": 75, +"k": 1 +} +], +"costs": { +"verify": [ +22.317, +26.801, +31.5, +36.072, +40.662, +45.0, +47.801, +51.334, +57.002, +59.882, +60.79, +62.123, +68.0, +70.0, +70.5, +72.25, +75.849, +79.448, +83.046, +86.645, +90.244, +93.843, +97.442, +101.04, +102.919, +104.798, +106.677, +108.556, +110.435, +112.314, +114.193, +116.072, +118.108, +120.145, +122.182, +124.219, +126.255, +128.292, +130.329, +132.366, +134.403, +136.439, +138.476, +140.513, +142.55, +144.586, +146.623, +148.66, +151.751, +154.843, +157.934, +161.026, +164.117, +167.209, +170.3, +173.392, +176.483, +179.575, +182.666, +185.758, +188.849, +191.941, +195.032, +198.124 +], +"draft": 3.614, +"slot": 0.83 +} +} \ No newline at end of file diff --git a/zig/src/families/deepseek_v41/draft/fixtures/calib-measure-golden.json b/zig/src/families/deepseek_v41/draft/fixtures/calib-measure-golden.json new file mode 100644 index 0000000000..fe09e15651 --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/fixtures/calib-measure-golden.json @@ -0,0 +1 @@ +{"cases":[{"slots":4,"cap":64,"env":{},"draft":true,"method":{"reps":5,"warm":3,"stat":"median","recheck":4,"shape":"raw","cycle":4,"deep":5},"n":4,"wide":[24,32,48,64],"calls":[[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,5]],[[0,5]],[[0,5]],[[0,5]],[[0,5]],[[0,5]],[[0,5]],[[0,5]],[[0,6]],[[0,6]],[[0,6]],[[0,6]],[[0,6]],[[0,6]],[[0,6]],[[0,6]],[[0,7]],[[0,7]],[[0,7]],[[0,7]],[[0,7]],[[0,7]],[[0,7]],[[0,7]],[[0,8]],[[0,8]],[[0,8]],[[0,8]],[[0,8]],[[0,8]],[[0,8]],[[0,8]],[[0,9]],[[0,9]],[[0,9]],[[0,9]],[[0,9]],[[0,9]],[[0,10]],[[0,10]],[[0,10]],[[0,10]],[[0,10]],[[0,10]],[[0,11]],[[0,11]],[[0,11]],[[0,11]],[[0,11]],[[0,11]],[[0,12]],[[0,12]],[[0,12]],[[0,12]],[[0,12]],[[0,12]],[[0,13]],[[0,13]],[[0,13]],[[0,13]],[[0,13]],[[0,13]],[[0,14]],[[0,14]],[[0,14]],[[0,14]],[[0,14]],[[0,14]],[[0,15]],[[0,15]],[[0,15]],[[0,15]],[[0,15]],[[0,15]],[[0,16]],[[0,16]],[[0,16]],[[0,16]],[[0,16]],[[0,16]],[[0,4],[1,4]],[[0,4],[1,4]],[[0,4],[1,4]],[[0,4],[1,4]],[[0,12],[1,12]],[[0,12],[1,12]],[[0,12],[1,12]],[[0,12],[1,12]],[[0,16],[1,16]],[[0,16],[1,16]],[[0,16],[1,16]],[[0,16],[1,16]],[[0,16],[1,16],[2,16]],[[0,16],[1,16],[2,16]],[[0,16],[1,16],[2,16]],[[0,16],[1,16],[2,16]],[[0,16],[1,16],[2,16],[3,16]],[[0,16],[1,16],[2,16],[3,16]],[[0,16],[1,16],[2,16],[3,16]],[[0,16],[1,16],[2,16],[3,16]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],"draft","draft","draft","draft"],"samples":[[25.389948941665352,30.760286869735864,34.96020428474367,38.48811515933903,25.372628780246487,30.615379479627336,35.04908297657039,39.572299713199754,25.377427524290397,29.757369477351858,34.11614211154301,38.529655136130714,25.394191732857507,30.600656285059813,34.12323784459659,38.48807193048742,26.485295671818676,29.747330126951965,34.11135212456884,38.52892506210992,25.40480983930138,29.758334989537616,35.17158137253773,38.51895274453909,25.38115208369618,29.789246302584615,34.15030392920926,38.51699365100103,25.395881936219666,29.741449007588017,35.023825561665944,38.514626097139626,42.87236138384287,42.899401902900536,42.868231794273015,42.86134229130312,43.741312008933164,42.89501541688878,42.873973671362364,42.88998218726192,48.24817569510742,47.25911514424297,47.24390163721637,48.28674062935079,47.260041178446954,47.239791924766905,47.267339850350254,47.22850018299596,52.465345210066516,51.630325099154106,52.66795314355477,51.622863414650055,51.61743300220678,52.424272881467004,51.62248373346756,51.636681240211146,56.00358714639242,55.97055211683255,55.97464833260801,55.989321858419316,55.98095062759967,57.06300511690188,55.98290804937278,56.0052148387058,60.375886054202965,60.35659124813719,60.33093524342803,60.3391553943311,61.39975113512719,61.302047879735255,64.7278237812543,64.72591743568046,64.73921362373858,65.69808884012218,64.7138458534755,64.72538569960307,69.10799965714887,69.09216241967897,69.09527765651546,69.10463655015064,69.09666427191041,69.11707505639697,73.48382677412246,73.45297961467168,73.48716335172867,73.44685672183004,74.40263542650882,74.34219162757927,78.84084164356864,77.85485132160375,78.85483596487575,77.8171489498618,77.85837723918121,77.85762520643402,82.20436303872702,82.22162223341911,83.13945654538202,82.19695580726238,83.10555767047845,82.18097414635395,86.57202290510213,87.47944936669683,86.57561311417794,87.67552497329234,86.59858479791183,87.45966928165672,91.98369922899019,90.92647777799812,90.9655706908552,90.93293045077644,92.02575145252467,90.95502087234308,57.42025795370864,56.59426585202709,57.68450491268595,56.61308142953203,127.57986859094217,127.58183249070498,126.5099575888371,126.5393334641658,161.4594612400424,161.46492180848782,162.3314347274345,162.3435304746863,232.00125026994556,232.0004980417707,231.99489499415904,231.98690815536338,302.51976730585284,302.5465524564197,303.4914281915917,302.52431406722735,25.391608879317573,29.781730696640807,34.13533429761628,38.52912202705738,25.41161432714034,29.771798847491482,34.127377609024734,39.34894557437474,26.422266759482227,29.74816232597277,35.19238069452513,38.51352716492329,25.382110646660294,29.762972647176866,35.07374738251201,38.52808932663265,25.397353668722644,29.78828333846195,34.12782919588153,39.42448798193254,25.395138200337897,29.765236781940985,35.0192506057283,39.42985335106641,26.20674824413527,29.751640478366426,34.136459477394965,38.51287718362073,25.413954534656114,29.75630673768137,34.117473157493805,38.51216097245924,3.6087576133922994,3.767057908703464,3.7783884711188875,3.7254664248393965],[25.56586040248021,29.91872597397105,35.28522094802611,38.65732259316701,25.577276210924538,29.934564906284322,34.29904039978737,38.660875343339285,25.546409152184424,29.946416056077396,34.282130871019945,38.672621005389374,26.661335667313324,29.93612913007837,34.291033816316485,38.67728704210549,25.573363586204323,30.924950642429394,34.318164499381965,38.70008654848789,25.555820968065746,29.959840173660268,34.32807115132164,38.67681370820719,25.588852605665124,29.925791350706277,34.290787053464555,38.698929177598984,25.556599555557113,29.94165167194751,34.31521694994899,38.68740164523388,43.05124685595274,43.04237507471953,43.04744668797866,43.034442284351826,44.07093868690026,43.041986769253526,44.099799995706235,43.03558231219995,47.4162118264303,47.43541987602384,47.40906802499012,47.430508132038085,47.40349494932161,47.42082995005603,47.409397022192934,47.43067189087924,51.793734580769524,51.81065414649311,51.80031247516581,52.77408575489062,51.76535224109102,51.8044356002747,51.80726574315031,52.77312594921568,56.16363544891101,56.13293555722976,56.17395342233067,56.14931248555877,56.171740217337174,56.160986871304885,56.14582974726545,56.147339561334775,60.50669970602485,60.51840233169514,61.40747432061744,60.52256127009059,60.50598880574398,60.51565790824043,65.9258217073102,65.71626321112944,64.90780854858258,64.89897389838006,64.9150950503099,64.89494448373989,69.2878025945447,69.28756228410293,70.16905204022805,69.24944806098665,69.24404234689518,69.27027803715191,73.63174166118824,73.63991723616437,73.65713470483115,73.66016825130828,73.62932734815786,73.6373994858468,78.015006411988,77.98739448196557,78.00599726363089,78.00768306708505,77.99545700515864,78.00732531018184,82.39224362228015,82.35721327810097,82.35274752360056,82.39823744770547,82.38594709837344,82.35816132594209,86.72498557882591,87.72497507936805,87.84033908386846,86.75251415195362,86.7264020167795,86.72298013857471,91.12379632711054,91.10890680744888,91.12351002511332,91.0994871812818,91.1092592177738,91.10958721180396,56.762745737046316,56.745929060980416,57.78697638936592,56.75965637851732,127.56378482345099,126.68232121586698,126.6790866241081,126.70329593936458,161.66107171841304,161.63703995971446,161.63847907068885,161.64635652489778,232.1819621446366,232.1740512445558,232.19382802028576,232.1870230773584,302.7135015186104,302.7173314284255,302.69868683035384,303.76697024007626,26.608926159042312,29.921882772555364,34.29367098704006,38.66086516404721,26.381977759456277,29.921591556671956,34.32984841811049,38.68788021418368,25.5730298117669,30.984766140704778,34.32373246005227,38.69656412280165,26.665119320796293,30.894293283722618,34.330315601891925,38.70009532010954,25.562255926956823,30.784774658400238,34.328562659879935,38.66831140760496,25.573842328412866,29.945699544555282,35.11932967444409,39.627476586133525,25.552277194719863,29.945654120069776,34.307115752881145,38.67130245544104,25.575634899610122,29.92044879772493,34.31376314767931,38.69000945928747,3.7488686518827308,3.793148179056516,3.712898026719813,3.7807047962132856]],"ints":[[3767,25396,29758,34136,38517,42890,47260,51623,55989,60357,64726,69097,73487,77858,82222,87460,90955,56613,126539,162331,231995,302547],[3781,25573,29942,34315,38687,43042,47421,51807,56149,60518,64908,69270,73640,78006,82358,86753,91109,56760,126682,161638,232187,302717]],"costs":{"verify":[25.573,29.942,34.315,38.687,43.042,47.421,51.807,56.149,60.518,64.908,69.27,73.64,78.006,82.358,87.46,91.109,95.47925,99.84949999999999,104.21974999999999,108.59,112.96025,117.3305,121.70075,126.071,130.527125,134.98325,139.43937499999998,143.8955,148.351625,152.80775,157.26387499999998,161.72,166.0478125,170.375625,174.7034375,179.03125,183.3590625,187.68687500000001,192.0146875,196.34250000000003,200.67031250000002,204.99812500000002,209.3259375,213.65375000000003,217.98156250000002,222.30937500000005,226.63718750000004,230.96500000000003,235.33493750000002,239.70487500000002,244.07481250000004,248.44475000000003,252.81468750000002,257.18462500000004,261.55456250000003,265.9245,270.2944375,274.664375,279.0343125,283.40425000000005,287.77418750000004,292.14412500000003,296.5140625,300.884],"draft":3.781,"slot":0.6109999999999971}},{"slots":1,"cap":64,"env":{},"draft":true,"method":{"reps":5,"warm":3,"stat":"median","recheck":4,"shape":"raw","cycle":4,"deep":5},"n":1,"wide":[],"calls":[[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,5]],[[0,5]],[[0,5]],[[0,5]],[[0,5]],[[0,5]],[[0,5]],[[0,5]],[[0,6]],[[0,6]],[[0,6]],[[0,6]],[[0,6]],[[0,6]],[[0,6]],[[0,6]],[[0,7]],[[0,7]],[[0,7]],[[0,7]],[[0,7]],[[0,7]],[[0,7]],[[0,7]],[[0,8]],[[0,8]],[[0,8]],[[0,8]],[[0,8]],[[0,8]],[[0,8]],[[0,8]],[[0,9]],[[0,9]],[[0,9]],[[0,9]],[[0,9]],[[0,9]],[[0,10]],[[0,10]],[[0,10]],[[0,10]],[[0,10]],[[0,10]],[[0,11]],[[0,11]],[[0,11]],[[0,11]],[[0,11]],[[0,11]],[[0,12]],[[0,12]],[[0,12]],[[0,12]],[[0,12]],[[0,12]],[[0,13]],[[0,13]],[[0,13]],[[0,13]],[[0,13]],[[0,13]],[[0,14]],[[0,14]],[[0,14]],[[0,14]],[[0,14]],[[0,14]],[[0,15]],[[0,15]],[[0,15]],[[0,15]],[[0,15]],[[0,15]],[[0,16]],[[0,16]],[[0,16]],[[0,16]],[[0,16]],[[0,16]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],"draft","draft","draft","draft"],"samples":[[25.391932402612838,29.783629769099207,34.11557657273079,38.511686468609696,26.228401749790464,29.76568907274668,35.07578027256386,38.51991209501193,25.37512140133913,29.744476761948135,34.1113998703122,38.48569373099053,25.37464918475507,29.77426860650212,34.13275834509477,38.50185212911583,25.37474271150586,29.751813166512875,34.15695101102756,39.352989085955414,25.398572359108584,30.78146809775717,34.13115551757073,38.50847007709035,25.38317692972214,29.749435529311086,34.133462879253784,38.49939472183905,26.293734657770074,29.76930219119822,34.15680650527975,38.49257673437023,43.72386033958264,43.74901059657077,42.86322731945802,42.88019632531359,42.8645687414928,42.868436792900866,42.87979190371516,42.88786291749602,47.26206986060788,47.23920550964067,47.247297100625474,47.235722427330984,47.25063669582141,47.266681861742654,47.23697166821239,47.22516230538076,51.62461213626557,52.497614647450064,51.61723451999478,51.62925473393898,51.60843000112436,51.596443485436794,51.59732427603103,51.59732798654204,55.97110185351539,55.99282651303383,56.006654749921836,55.98786462519456,56.911959222361475,55.98349292904459,56.003197898348844,56.99403770870504,60.357122075402,61.4200343951552,60.379027471640256,60.371443835833816,60.33340625117489,60.345259139239715,65.6437425822105,64.70024365216887,64.73921308293029,64.72640743550073,64.74857146565682,64.73405761437334,69.07002881052904,69.08177308025643,69.1164151007797,69.11276279925005,69.09227070934776,69.1167999194704,74.31105727584963,73.48032297716145,73.48839465737456,73.48176328252976,73.48503478613111,74.40149609124092,77.849716783021,77.84402974630211,77.83094225021614,78.64908468968679,77.83380175897037,77.83598346952658,83.07661649155307,82.19275527028458,82.19233093882394,83.2700143917009,82.18140001486063,82.21431190008843,86.58872945534313,86.56124421656841,86.58125327451671,86.55918245494831,86.58455675697496,86.58012311252605,90.9577282391183,90.94489470373901,90.93553108243668,91.9409832920337,92.00710411255386,90.96520704645172,25.377707786560677,29.774125871654178,34.14175723230528,38.527959659518274,26.439320702820623,30.722148330482923,34.979525002654555,38.52521661690389,25.39013858688577,29.770252993444046,34.12757107048492,38.50778138280475,25.41090388365319,29.761610500713687,35.031593601956956,38.488592260023324,26.43770215775021,30.824834775785348,34.12939514066693,38.48560597646156,25.391922746280216,29.778202424267874,35.18675805389648,38.49208655014991,25.38027631555906,29.77759198915919,34.136648932303615,39.56893876397771,25.411519757426504,30.79360500976236,35.10694759040689,38.52507724195675,3.6212463068068246,3.6945982851648296,3.6941731189017446,3.677181396597007],[25.56125502133,29.960758645529495,34.32825124048122,39.523095436607036,26.652419436913988,29.946954117008318,34.30574925369001,38.65599378218576,26.38522484983241,29.953024109886428,34.30107296605911,38.65996057322718,25.55869007039746,29.92656164883556,34.29691337817076,38.66780887187815,25.544710047824992,29.95080757307278,34.28677002864333,38.669807414976276,25.587399157529944,29.92536060742168,35.379182000610854,38.65891874488625,25.565048538510382,29.913152442418323,35.36412454866422,38.67411444434765,25.570667023316673,29.923781251795845,35.11367755777428,38.68342500345534,43.038366398150174,43.05626004418173,43.0365869143543,43.061035199116304,43.06562896306332,44.05174935914147,43.04042225351168,43.02819427778104,47.44031296092999,47.43665019020682,47.40674081460838,47.42124302777029,47.408001787175635,48.27266663369301,47.42744302336632,48.471534081613754,51.777740442048525,51.76743775621162,52.694081493314116,51.78838812332742,51.80633729219153,51.800406770666996,52.825883756554504,52.787706048661676,56.16378761737906,56.15850099695763,56.14943906323333,56.158854699560834,56.13681203180931,56.15434390040264,57.17327042748366,56.147937962350625,60.550363172296784,60.5213915476952,60.507495916340304,60.550940896064276,61.60586876774232,60.540195250837314,65.89483491779902,64.90484749485859,64.90707407078844,64.91522996111598,64.87885269348226,64.87378482449913,69.26199482074935,69.25961431920769,69.26606417789571,69.26466536219777,69.2708161138853,69.26215358612353,73.65982192345655,73.65836285771366,74.54074761028551,73.61945338550413,73.66070468958696,73.63397614119549,78.94631273723007,78.0204393903432,78.02128699711375,78.01276680481806,77.98930349383681,78.00350582203919,82.39962981019744,82.37699359824546,82.36200171177188,82.37629675045355,82.38577046245155,82.36716913074815,86.74348931458553,87.81116570980885,86.72702518219921,86.75541674313081,86.74975720043676,86.72859313946901,91.10302205533571,92.20612944545792,91.12375614165558,91.11075104510746,91.12175104473863,91.1260421546558,25.57238268684614,29.951507810551448,34.31968480776959,38.670051110557324,25.55990999758251,29.915822904172273,35.17867955190468,38.66239291471629,25.54463818341901,29.946104696591647,34.29294080206091,38.67569227156764,26.441326527560705,29.957820655226897,35.26684531493629,38.69846415648226,25.56756424064588,29.950432924238157,34.31212733778466,38.653103846968406,25.55211630726717,29.93068435466739,34.310503678739224,38.666830577312794,25.573799203925773,29.93006009273813,35.36711233596179,38.693039534337004,25.56186419826645,31.01721232235377,34.28712865240868,38.65321607668193,3.7277775820712122,3.770649670855164,3.810552989648386,3.7218255984043935]],"ints":[[3694,25383,29769,34133,38492,42880,47237,51597,56003,60371,64734,69113,73485,77836,82193,86580,90965],[3771,25565,29925,34312,38667,43061,47427,51806,56154,60540,64905,69265,73658,78013,82376,86750,91124]],"costs":{"verify":[25.565,29.925,34.312,38.667,43.061,47.427,51.806,56.154,60.54,64.905,69.265,73.658,78.013,82.376,86.75,91.124],"draft":3.771,"slot":0.0}},{"slots":2,"cap":64,"env":{"TF_DSV41_CALIB_SHAPE":"fit","TF_DSV41_CALIB_STAT":"min"},"draft":false,"method":{"reps":5,"warm":2,"stat":"min","recheck":2,"shape":"fit","cycle":0,"deep":3},"n":2,"wide":[24,32],"calls":[[[0,1]],[[0,1]],[[0,1]],[[0,1]],[[0,1]],[[0,1]],[[0,1]],[[0,2]],[[0,2]],[[0,2]],[[0,2]],[[0,2]],[[0,2]],[[0,2]],[[0,3]],[[0,3]],[[0,3]],[[0,3]],[[0,3]],[[0,3]],[[0,3]],[[0,4]],[[0,4]],[[0,4]],[[0,4]],[[0,4]],[[0,4]],[[0,4]],[[0,5]],[[0,5]],[[0,5]],[[0,5]],[[0,5]],[[0,5]],[[0,5]],[[0,6]],[[0,6]],[[0,6]],[[0,6]],[[0,6]],[[0,6]],[[0,6]],[[0,7]],[[0,7]],[[0,7]],[[0,7]],[[0,7]],[[0,7]],[[0,7]],[[0,8]],[[0,8]],[[0,8]],[[0,8]],[[0,8]],[[0,8]],[[0,8]],[[0,9]],[[0,9]],[[0,9]],[[0,9]],[[0,10]],[[0,10]],[[0,10]],[[0,10]],[[0,11]],[[0,11]],[[0,11]],[[0,11]],[[0,12]],[[0,12]],[[0,12]],[[0,12]],[[0,13]],[[0,13]],[[0,13]],[[0,13]],[[0,14]],[[0,14]],[[0,14]],[[0,14]],[[0,15]],[[0,15]],[[0,15]],[[0,15]],[[0,16]],[[0,16]],[[0,16]],[[0,16]],[[0,4],[1,4]],[[0,4],[1,4]],[[0,4],[1,4]],[[0,4],[1,4]],[[0,12],[1,12]],[[0,12],[1,12]],[[0,12],[1,12]],[[0,12],[1,12]],[[0,16],[1,16]],[[0,16],[1,16]],[[0,16],[1,16]],[[0,16],[1,16]],[[0,1]],[[0,1]],[[0,1]],[[0,1]],[[0,1]],[[0,1]],[[0,1]],[[0,2]],[[0,2]],[[0,2]],[[0,2]],[[0,2]],[[0,2]],[[0,2]]],"samples":[[26.22797875960714,25.41964117040152,25.394576980261263,25.407408801015663,25.400445069521993,25.401131136050026,26.251027554963002,29.786353680378852,29.769861828640387,29.745163661459628,30.814108752565517,29.758743777733798,29.773795089113264,29.787914005851235,34.15945548147192,34.139525939735904,34.13454559301954,34.12236038946048,34.11811462831338,35.181584532892884,34.13415864622493,38.48260868142006,38.494493732969204,38.488531483835686,39.411424715581234,38.52917235303721,38.503948659808884,38.52794387694303,42.886430425028266,43.743627449202904,42.889760915272745,42.88006778904219,42.879056975607455,42.865398863796145,42.863799090355315,47.220511677664945,47.26769872195291,48.228105961470646,47.26258410369155,47.232859838345576,47.267935646686965,47.2266768888403,52.44818164862863,52.55405663842794,52.6802008487266,51.59632640209111,51.60621384413844,51.60098039618788,52.47518292753739,55.99549875978482,55.962309071105665,56.00369344927003,55.99953627461218,56.92916391330982,57.0191760285752,57.00344628019138,60.36483665457126,60.33209555744179,60.34547057913642,60.34475135083994,64.72746661654583,64.70492817038576,64.72541062782966,64.7483555665076,69.10497233752722,69.09803384110091,70.14440649595599,69.0877612927352,73.48966086465225,74.29283241970097,74.5142987706231,73.46240294418749,77.81098896009553,77.832131836999,77.82947008217889,77.8212742964115,83.14017476504887,82.21520896120182,82.2161879846135,82.19182184905094,86.57433923235658,86.59815625128431,87.52391217490185,86.58585867863167,90.95897577128653,90.9579262859097,90.96294529581428,90.95414876583163,56.618833945776714,56.60373548687403,56.62118522809578,56.57331927500309,126.508586631644,126.50027107645201,126.49697583788111,126.54017621559888,161.4761174483874,162.56980050116,161.46329092953238,161.48507886464358,25.38304346842324,25.38610559247445,25.412268572381436,26.404348967616897,26.379390724741825,25.388949764987956,25.393348283500927,29.77085461839124,29.748717333745844,29.75362008862703,30.858235968139525,30.813129948455753,29.775229950132598,30.720360610871467],[25.562587967101535,25.587475209590593,25.566599673425117,25.545828292251826,25.54956979486178,25.55845693871106,25.558860407613793,30.752092572811307,29.950795364356964,29.92107537386346,29.91360460418946,29.95240503742025,29.934028796901657,29.93592658219768,34.310227157789086,34.31818391243269,34.31736269976682,34.298688664080146,34.32198975986012,34.32813639142296,35.26100254669018,38.66802868947161,39.68862341525892,38.664449834300285,39.771140320794984,38.65690456541415,38.663747408236304,38.660032673760725,43.02586770927519,43.03138566808684,43.05774524129902,43.06270761378528,43.96432635473957,43.0226486198535,43.04549957078052,47.42851449725549,47.41422308507026,47.40253695956653,47.394260297323854,47.39888966014405,47.423297614386684,48.370627604640504,51.763112738399286,51.762492295551965,51.76120344640367,51.81027736091437,51.77266598957431,51.767992285476794,51.804193139901145,56.13145494839955,56.163011721309886,57.20178744638815,56.15856992267254,56.17342517268753,56.14041737521802,56.138956398058326,60.526493174961615,60.54186793221561,60.510524895448725,60.54471013862894,64.89033677848965,64.88074936476096,65.71618243378907,64.88279728955604,69.28947529604557,70.23830588832425,69.27092015700964,69.27938984551929,74.72556865138813,73.65133236942256,73.61592873655809,74.49450908029576,78.02791727169733,78.00159871692358,78.01799155879507,78.02952486645154,82.3873478503856,82.39104135043362,82.36706688967388,82.3629238917647,87.82875775341381,86.72219380389379,86.72615844329812,86.73761119644041,92.06402551512838,91.10870251402048,91.10633903492271,91.13489719459267,56.76273763310746,57.859140964978906,56.75487969540427,57.64570108351563,126.67093677680441,127.79232361322101,126.66966601250351,126.70406935774281,161.62746050395072,161.64303384221057,161.65314448448953,161.65553331973115,26.576857502163875,25.574536100521073,25.55529884796215,26.39560735019586,26.50698104514504,25.551856655397387,25.572794596200765,29.91202649548086,29.940263634898656,29.922965789864975,29.957638613154813,29.93090359962025,29.919747173039468,29.956879194628527]],"ints":[[0,25389,29745,34118,38489,42864,47227,51596,56000,60332,64705,69088,73462,77821,82192,86586,90954,56573,126497,161463],[0,25546,29914,34299,38657,43023,47394,51761,56139,60511,64881,69271,73616,78002,82363,86722,91106,56755,126670,161643]],"costs":{"verify":[25.546,29.916399999999996,34.285799999999995,38.655199999999994,43.02459999999999,47.394,51.763400000000004,56.1328,60.50325,64.8737,69.24415,73.6146,77.98505,82.3555,86.72595000000001,91.0964,95.465325,99.83425,104.203175,108.5721,112.941025,117.30995,121.678875,126.0478,130.419425,134.79104999999998,139.162675,143.5343,147.905925,152.27755000000002,156.649175,161.0208],"draft":0.0,"slot":0.6221999999999994}},{"slots":4,"cap":32,"env":{"TF_DSV41_CALIB_WARM":"1","TF_DSV41_CALIB_REPS":"4","TF_DSV41_CALIB_RECHECK":"2","TF_DSV41_CALIB_CYCLE":"0","TF_DSV41_CALIB_DEEP_REPS":"2"},"draft":true,"method":{"reps":4,"warm":1,"stat":"median","recheck":2,"shape":"raw","cycle":0,"deep":2},"n":2,"wide":[24,32],"calls":[[[0,1]],[[0,1]],[[0,1]],[[0,1]],[[0,1]],[[0,2]],[[0,2]],[[0,2]],[[0,2]],[[0,2]],[[0,3]],[[0,3]],[[0,3]],[[0,3]],[[0,3]],[[0,4]],[[0,4]],[[0,4]],[[0,4]],[[0,4]],[[0,5]],[[0,5]],[[0,5]],[[0,5]],[[0,5]],[[0,6]],[[0,6]],[[0,6]],[[0,6]],[[0,6]],[[0,7]],[[0,7]],[[0,7]],[[0,7]],[[0,7]],[[0,8]],[[0,8]],[[0,8]],[[0,8]],[[0,8]],[[0,9]],[[0,9]],[[0,9]],[[0,10]],[[0,10]],[[0,10]],[[0,11]],[[0,11]],[[0,11]],[[0,12]],[[0,12]],[[0,12]],[[0,13]],[[0,13]],[[0,13]],[[0,14]],[[0,14]],[[0,14]],[[0,15]],[[0,15]],[[0,15]],[[0,16]],[[0,16]],[[0,16]],[[0,4],[1,4]],[[0,4],[1,4]],[[0,4],[1,4]],[[0,4],[1,4]],[[0,12],[1,12]],[[0,12],[1,12]],[[0,12],[1,12]],[[0,12],[1,12]],[[0,16],[1,16]],[[0,16],[1,16]],[[0,16],[1,16]],[[0,16],[1,16]],[[0,1]],[[0,1]],[[0,1]],[[0,1]],[[0,1]],[[0,2]],[[0,2]],[[0,2]],[[0,2]],[[0,2]],"draft","draft","draft","draft"],"samples":[[25.413008930286196,25.396047698450275,25.392412191195035,25.403551529961987,25.413275129722024,29.751566233358062,29.740755540842656,29.767628529498324,29.781475245272304,29.765213113364553,35.23539503703432,34.12799113721121,34.11982255215662,34.979409408947504,34.124813414223354,38.51262908585795,39.53800376291383,39.551451949250804,38.50255241184186,38.50529681494663,42.898933760056934,42.870045279642,42.88485411075271,42.88946765723267,42.878792519104536,47.246266561842276,47.26451990234182,47.25220362468008,47.23959792147525,47.26247151916141,51.59455305295069,51.593093900578424,51.63137060151257,51.59109631074443,51.620828825207354,55.96154194176961,56.81133891312129,55.9804484067854,57.03389456721197,55.97711527423144,60.34035451955333,61.396484402621354,60.34753210974486,64.7434421092612,65.82956304248455,65.64184894978098,69.96040883564092,69.97680589040556,69.10183353943466,73.47696217334487,73.45107289006592,73.46922781084686,77.81192542870485,77.83550496617408,77.84232848985084,82.19858366931021,83.13839406650914,82.2075023945672,86.58924039127669,86.57928291086137,86.55828330790882,90.92803177782116,90.94471612058896,90.96886243719382,57.498444819771066,57.496294936072445,56.6059900829714,56.58594127589822,126.49887492455036,127.371590996745,127.57534146362559,126.52721444408144,161.46505213214368,161.48441570771865,161.48407109346863,162.54098321462607,25.418279990731207,25.399694840416487,25.37529503354108,25.379877096156633,25.416635830879386,29.769309010589495,29.788320642865074,29.76993745960499,29.751752843822032,29.764251311235057,3.791785678799897,3.754684799559982,3.7782538122428377,3.664640584247536],[26.648126670011152,25.547795027250686,26.64578478106705,25.58448270781355,25.57898917291368,29.91136984496734,29.955111128515455,29.936740814832774,30.793307659450875,29.922530290150462,35.16880805329947,34.287343687765315,35.16063770621258,35.346056921184754,34.291088102918366,38.69481310471201,38.66767093791168,38.689094847995875,38.690695456921276,38.666742566192625,43.060195711859706,43.947628127398275,43.06531949930559,43.05468430891324,43.03991543213215,47.435748998395866,47.41046900676338,48.324326381589344,47.423302231891284,47.407245183762825,51.806537902848504,51.772107915894594,51.80706453381845,52.785481563205394,51.769361139236025,57.15247533203183,57.12562124642773,56.154184677438934,56.13630965365246,56.147012621408976,61.54174974278703,60.53689300483711,61.40613160334851,64.87144064635686,64.9118964953459,64.88200357841833,69.25358817113647,69.2605806989377,69.27395359764432,73.64905507336061,73.61464009238716,73.66032526681465,78.01902171684105,77.98741677231646,78.0256433130262,82.36934810145158,82.38776205769227,82.35631987679426,86.75639328851048,86.75813721197301,86.75372157040329,91.13617684681685,92.12997471433937,91.13648111724615,56.75274785687634,57.64412822622944,56.78241787029492,56.75446857537736,126.68625871663153,126.69481574243946,127.74698184364297,126.6997089379629,161.63882292914877,161.63484568585318,161.66776068314448,161.6599222535342,25.57236677455421,25.556231379482597,25.574742660751326,25.562238630527645,26.452307891531746,29.94613112684874,29.930848028016044,29.914434540046386,30.982684296759544,30.927877506201185,3.7098312932357658,3.6843270710278375,3.8069156671554083,3.826726978672923]],"ints":[[3755,25390,29766,34126,39022,42882,47257,51607,56396,60872,65736,69539,73460,77839,82673,86569,90957,56606,127372,161484],[3807,25568,29946,34726,38678,43060,47417,51790,56151,60972,64897,69267,73637,78007,82372,86756,91633,56782,126700,161660]],"costs":{"verify":[25.568,29.946,34.726,39.022,43.06,47.417,51.79,56.396,60.972,65.736,69.539,73.637,78.007,82.673,86.756,91.633,96.05212499999999,100.47125,104.890375,109.3095,113.728625,118.14775,122.56687500000001,126.986,131.272,135.558,139.844,144.13,148.416,152.702,156.988,161.274],"draft":3.807,"slot":0.3859999999999957}},{"slots":8,"cap":48,"env":{"TF_DSV41_CALIB_CYCLE":"8","TF_DSV41_CALIB_RECHECK":"8"},"draft":true,"method":{"reps":5,"warm":3,"stat":"median","recheck":8,"shape":"raw","cycle":8,"deep":5},"n":3,"wide":[24,32,48],"calls":[[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,5]],[[0,6]],[[0,7]],[[0,8]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,5]],[[0,6]],[[0,7]],[[0,8]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,5]],[[0,6]],[[0,7]],[[0,8]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,5]],[[0,6]],[[0,7]],[[0,8]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,5]],[[0,6]],[[0,7]],[[0,8]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,5]],[[0,6]],[[0,7]],[[0,8]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,5]],[[0,6]],[[0,7]],[[0,8]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,5]],[[0,6]],[[0,7]],[[0,8]],[[0,9]],[[0,9]],[[0,9]],[[0,9]],[[0,9]],[[0,9]],[[0,10]],[[0,10]],[[0,10]],[[0,10]],[[0,10]],[[0,10]],[[0,11]],[[0,11]],[[0,11]],[[0,11]],[[0,11]],[[0,11]],[[0,12]],[[0,12]],[[0,12]],[[0,12]],[[0,12]],[[0,12]],[[0,13]],[[0,13]],[[0,13]],[[0,13]],[[0,13]],[[0,13]],[[0,14]],[[0,14]],[[0,14]],[[0,14]],[[0,14]],[[0,14]],[[0,15]],[[0,15]],[[0,15]],[[0,15]],[[0,15]],[[0,15]],[[0,16]],[[0,16]],[[0,16]],[[0,16]],[[0,16]],[[0,16]],[[0,4],[1,4]],[[0,4],[1,4]],[[0,4],[1,4]],[[0,4],[1,4]],[[0,12],[1,12]],[[0,12],[1,12]],[[0,12],[1,12]],[[0,12],[1,12]],[[0,16],[1,16]],[[0,16],[1,16]],[[0,16],[1,16]],[[0,16],[1,16]],[[0,16],[1,16],[2,16]],[[0,16],[1,16],[2,16]],[[0,16],[1,16],[2,16]],[[0,16],[1,16],[2,16]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,5]],[[0,6]],[[0,7]],[[0,8]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,5]],[[0,6]],[[0,7]],[[0,8]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,5]],[[0,6]],[[0,7]],[[0,8]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,5]],[[0,6]],[[0,7]],[[0,8]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,5]],[[0,6]],[[0,7]],[[0,8]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,5]],[[0,6]],[[0,7]],[[0,8]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,5]],[[0,6]],[[0,7]],[[0,8]],[[0,1]],[[0,2]],[[0,3]],[[0,4]],[[0,5]],[[0,6]],[[0,7]],[[0,8]],"draft","draft","draft","draft"],"samples":[[25.37018293537585,29.772584547799852,35.14894554177772,38.498611930208426,42.85810546502944,47.23215844705919,51.60524957875623,56.009945260939276,25.393208200057416,29.74896452349185,34.1444859392368,38.51993083083016,42.85491079235726,47.24609709660399,51.623813685750974,55.99824378577978,25.3772475855385,29.752905075270064,34.13719191416931,38.49987731064175,42.857383698560625,47.24211916152399,51.63341390993992,55.960070510423066,25.387913006511553,29.76858226350032,34.14977657826057,38.49827684200591,42.85256801711057,47.26960326036078,52.53691244968195,55.98371958046755,25.41236553724957,29.76138770372927,34.110581729805745,38.486930739736636,42.85614692287254,47.24294100208226,52.595653157936795,56.894877366516994,25.408002505059812,29.750694508948072,34.13242573856223,38.497461247516185,42.853575937442656,48.28322365187887,51.592256447747786,55.9811662278662,26.312550627380915,29.743750276793435,34.12595650002004,39.59400670623836,42.859741264010154,47.22355908859299,51.59887152171905,55.96723774590373,25.39519289450709,29.751123020901105,35.012637363365684,38.48004706071606,43.95852945299339,47.22924490533842,51.60425018709702,55.986177531508474,60.33229132754059,60.34643125758521,60.37847716447686,60.372934863494265,60.33880563927596,60.375414301120145,64.71073036220787,64.74316295725657,65.58646253188272,65.61414933344167,64.70688641013567,64.74924285976158,69.09321512353017,69.11977895128985,69.0941814659709,70.11929111627069,69.11546255992107,70.07285029567356,73.47085176252222,73.48936744108414,73.44370617397544,73.4650659678664,73.4554695237648,74.3303706711913,78.71482608288716,77.81662366721775,77.83941998718547,78.87499624644079,78.77868705543278,78.65659890512688,82.20671391052292,82.19645691485766,82.1846817276537,83.15655259116284,82.1928348301526,82.20529386119324,86.56100745599815,86.56729241704397,86.58941293197131,86.5895433150854,86.5800989085983,87.63962894943234,90.96105918627018,91.9444184933127,90.962059340427,90.96053556515926,90.9665611671926,90.92816417319227,56.586820106190316,56.58820962423761,56.60367595498883,56.58553694445345,126.53496811640252,126.5163984156743,126.50709090280543,127.55038972716193,162.28815707506783,161.47865304378684,162.38084680492193,162.44001112829665,232.0290132718128,232.9834441298999,231.9932921018335,232.03258559465212,25.406161164028163,29.789952754413207,34.123300217402175,39.42424783213028,42.89825227078836,47.25062886677733,51.59219106678847,56.79288001692839,25.411863677618385,30.82810011346737,35.062425837395494,38.52918039603992,42.8510369988544,47.263546712315474,51.63953741509886,56.82683529221322,26.352502817189816,29.74574300549193,34.15622932732276,38.492756813525375,42.88510347623742,47.2246191367276,51.591309450373046,56.00174572202832,25.405895043036253,30.793411586046204,34.11390996905084,38.507675436108,42.88718537065961,48.219214227856355,52.5288782799862,56.86601557181348,25.397231067358916,29.747783457651167,34.1178891836762,38.50974828822018,42.88438192963895,47.2530751328577,51.60611608278032,55.98596429331337,25.379413539781126,29.74922103726385,34.14746848568484,39.446339385676765,42.85088828635253,47.257477019229555,52.461843909441086,55.99731202528346,25.399833380902237,30.83578216683236,34.136262528136285,38.49809442294827,42.85295457975735,47.239011954729904,52.657057294368315,55.988087723221724,25.376344001870166,29.768653726137018,34.1100889977497,38.51111642916294,42.85798287457965,47.24462314050015,51.602722551933766,56.94232447774539,3.793424757077446,3.7822352269358817,3.7368378046949147,3.655780944654907],[25.58636110165935,30.945793945647893,34.30350630412704,38.67021056919384,43.03733709195967,47.40884088209896,51.768116841685696,56.13654228409359,25.561847843505348,29.955870137882812,35.26975748604855,38.682563991528696,43.03876098640558,47.42438439166108,52.772662343613774,56.17776240535477,25.574691087399515,29.946435152851336,34.30557951890023,38.652003020615666,43.042146627954025,47.419190742289175,51.78051199436595,56.14276125140805,25.574188763812344,29.947915008392556,35.163832718581034,38.67296858561531,43.04690918581855,47.428329531044255,51.781320156692345,56.17156581627114,25.54167522055195,29.91970250968734,34.32822914021472,38.667127240728405,43.046591023198744,47.40049629731402,52.841630432681086,56.14994803124773,26.460304136094237,29.921992041295198,34.296678878945386,38.66803312007505,43.04631513377899,48.33932389487927,51.76857959793324,57.12883071885244,25.577609627930542,29.91997560945947,34.31700709768393,38.69532679857457,43.03795932730736,48.386751151838325,52.85966843166534,56.151726400116786,25.579725862826308,29.915972120988954,34.29814979153889,38.66393940825219,43.98513170337992,47.423928367948065,51.76715792470077,56.177670523197776,60.52939568394322,61.390950873374095,60.54824840089168,60.54956163495717,61.415452142000504,60.53525288734818,64.90374680834066,65.90295240835076,64.8803071835573,65.82435043230817,64.89468442214275,64.87782010265164,69.26663403362454,69.25537438826268,69.28208293516036,70.17519355906643,70.24862878733984,69.26122235222465,73.64976725705219,73.61653501993715,73.65168001751954,73.62240122392905,74.6629236730314,73.61627704369766,78.01665940223756,77.9813534988989,78.00053917208061,77.99471834459837,77.98683022213027,77.98882966130805,82.38822284226899,82.35209493170714,82.36691969204912,83.34860510274211,82.37360902774071,83.2246510198047,87.69985891876786,87.82247706790258,86.76775635422018,86.76207231235367,86.75921437725265,86.74347757698797,91.13413161981043,91.13803536718024,91.10134512422974,91.10195671701149,92.12653938470794,91.12730381559686,57.67561806203503,56.781571540000186,57.80048014935346,56.74545052340818,126.70557419835404,126.70590516927405,126.70885436784829,126.68735504041706,161.6631023861146,161.64162556879091,161.63198431422643,161.65799683494697,232.18290216118476,232.19999827858828,232.18401711619663,233.2224201580857,25.590293515278972,29.913346637840732,34.292120357577005,38.659894363718195,43.068447322070824,47.408923749458154,51.78124242195281,56.14203377126614,25.563319438333565,30.820793788620904,34.29225202307862,38.662466154619324,43.059456346895786,47.403381582967086,51.79137560128311,56.15292905201841,25.583514100844695,29.931659568660507,34.320392802101196,39.7233326289097,43.05647972535098,47.40747088374064,51.78678352865518,56.1407861372345,26.615323740315944,29.930804704122238,34.30283140107804,39.64794920261738,43.057544761722966,47.397422788094445,51.81057201752992,56.16344953625685,25.54477975490954,29.957747137473234,34.28640078243461,38.69674740417395,43.03922274061733,47.43340374000127,52.652208079848606,56.14572102467719,25.564450532442606,29.917352792836027,35.23900678123937,38.68563090622956,44.12651875213669,47.39779240549069,51.809063877840345,56.16681110109312,25.54264444745513,30.93716034368299,35.333490946413804,38.65721374154418,43.0535149358775,47.44096446461299,51.77583908766792,56.162667197099836,25.590064012476432,29.929118570294122,34.29370956894218,38.684313461430975,43.037367951342276,47.42987579845703,51.80072174198358,56.167641178603844,3.6982619901664293,3.67010223123998,3.79704344777565,3.820961685846669]],"ints":[[3737,25397,29751,34118,38497,42856,47243,51604,55984,60373,64749,69120,73465,78657,82196,86589,90962,56588,126516,162381,232033],[3797,25564,29920,34303,38668,43047,47428,51781,56163,60550,64895,69282,73622,77989,82374,86762,91127,56782,126706,161642,232200]],"costs":{"verify":[25.564,29.92,34.303,38.668,43.047,47.428,51.781,56.163,60.55,64.895,69.282,73.622,78.657,82.374,86.762,91.127,95.497,99.86699999999999,104.237,108.607,112.977,117.34700000000001,121.717,126.087,130.546375,135.00575,139.465125,143.9245,148.383875,152.84325,157.302625,161.762,166.087,170.412,174.737,179.062,183.387,187.712,192.037,196.362,200.687,205.012,209.337,213.66199999999998,217.987,222.31199999999998,226.637,230.962],"draft":3.797,"slot":0.6189999999999998}}],"pure":{"statistic":[{"xs":[34.0,64.043,39.763],"median":39.763,"min":34.0},{"xs":[35.2,29.979,20.154112],"median":29.979,"min":20.154112},{"xs":[37.679,56.3,31.529,37.9,60.8],"median":37.9,"min":31.529},{"xs":[39.118,37.7,47.3],"median":39.118,"min":37.7},{"xs":[64.275894,52.6,52.8,69.7,67.356617,40.135,56.440971],"median":56.440971,"min":40.135},{"xs":[45.1,60.546,45.794,20.702,52.474],"median":45.794,"min":20.702},{"xs":[21.589796,51.784],"median":36.686898,"min":21.589796},{"xs":[66.907,63.842527],"median":65.3747635,"min":63.842527},{"xs":[53.016376,31.184845,38.0],"median":38.0,"min":31.184845},{"xs":[69.107],"median":69.107,"min":69.107},{"xs":[35.8,51.6,33.564,60.6,33.003837,72.668422],"median":43.7,"min":33.003837},{"xs":[60.94783,69.7,51.1,33.375,27.951203],"median":51.1,"min":27.951203}],"table":[{"times":[24.806098092930668,27.292768587054304,30.417300906393212],"raw":[24.806098092930668,27.292768587054304,30.417300906393212],"fit":[24.806098092930668,27.292768587054304,30.417300906393212]},{"times":[26.82213909949447],"raw":[26.82213909949447],"fit":[26.82213909949447]},{"times":[24.420784665999516,25.860018135998587,31.060004505796808,36.5752186389166,39.94603844420495,46.82286651346993,53.0658381187733,56.341769793198324,59.07738497390979,64.72041317992903,70.84574057769899,73.23802112051479],"raw":[24.420784665999516,25.860018135998587,31.060004505796808,36.5752186389166,39.94603844420495,46.82286651346993,53.0658381187733,56.341769793198324,59.07738497390979,64.72041317992903,70.84574057769899,73.23802112051479],"fit":[24.420784665999516,25.860018135998587,31.100730230366423,36.34144232473426,41.582154419102096,46.82286651346993,52.06357860783777,57.304290702205606,61.79379871178621,66.28330672136681,70.77281473094743,75.26232274052803]},{"times":[26.132881527618146,27.162410789166753,32.46855330683257,37.629261798148974,39.673619358095415,47.86457508851268,50.90532630494167,53.36436591893442,62.3090540697355,63.91018675168276,69.76124868959707,75.18065790564737,80.01306962991569,83.07358387117824,88.87922029361702,92.9868633944975],"raw":[26.132881527618146,27.162410789166753,32.46855330683257,37.629261798148974,39.673619358095415,47.86457508851268,50.90532630494167,53.36436591893442,62.3090540697355,63.91018675168276,69.76124868959707,75.18065790564737,80.01306962991569,83.07358387117824,88.87922029361702,92.9868633944975],"fit":[26.132881527618146,27.904971119886234,32.46855330683257,37.0321354937789,41.59571768072524,46.15929986767157,50.722882054617905,55.28646424156424,60.098094743635336,64.90972524570644,69.72135574777754,74.53298624984863,79.34461675191973,84.15624725399083,88.96787775606194,93.77950825813303]},{"times":[23.185840241756793],"raw":[23.185840241756793],"fit":[23.185840241756793]},{"times":[23.80247070760712,28.528045111252894,36.17988494159925,35.968502813379864,42.85043309191579,45.87594819962704,52.2362186233385,55.54612846985341,59.76296768113491],"raw":[23.80247070760712,28.528045111252894,36.074193877489556,36.074193877489556,42.85043309191579,45.87594819962704,52.2362186233385,55.54612846985341,59.76296768113491],"fit":[23.80247070760712,28.528045111252894,33.03105900435298,37.534072897453065,42.03708679055315,46.540100683653236,51.043114576753325,55.54612846985341,59.76296768113491]},{"times":[23.647826691187905,25.85921169452405,32.93488497868212,34.718735211345646,40.433775076649795,47.348522065784714,52.0217314485388,56.764593674831794,57.12091669628024,62.87611944383095,68.9752101310577,71.07968788163642],"raw":[23.647826691187905,25.85921169452405,32.93488497868212,34.718735211345646,40.433775076649795,47.348522065784714,52.0217314485388,56.764593674831794,57.12091669628024,62.87611944383095,68.9752101310577,71.07968788163642],"fit":[23.647826691187905,25.85921169452405,31.010108691242007,36.161005687959964,41.31190268467792,46.46279968139588,51.61369667811383,56.764593674831794,60.85058852698748,64.93658337914316,69.02257823129884,73.10857308345453]},{"times":[26.594125738547234,28.64085674112235,30.49415035155814,36.76648786876947,43.521867754495396,47.07187861099164,50.26200224891235,55.28224735872069],"raw":[26.594125738547234,28.64085674112235,30.49415035155814,36.76648786876947,43.521867754495396,47.07187861099164,50.26200224891235,55.28224735872069],"fit":[26.594125738547234,27.635714553916753,32.24347002138408,36.8512254888514,41.45898095631872,46.06673642378604,50.67449189125337,55.28224735872069]},{"times":[23.237525363760824,28.02296239098694,32.9573345613486],"raw":[23.237525363760824,28.02296239098694,32.9573345613486],"fit":[23.237525363760824,28.02296239098694,32.9573345613486]},{"times":[22.532166210765478],"raw":[22.532166210765478],"fit":[22.532166210765478]}],"extend":[{"table":[22.400698313200692,27.814642167222946,32.71975417007472,37.791361297788285,40.37114383953916,44.76745231358612,52.103872186643855,56.88136029649459,60.570952443082454,65.90476294982736,68.20751037020648,73.75512277229465,77.79108224372406,79.75675263249187,87.36788496645512,88.614143353837],"points":[[24,109.95696000852061],[32,122.902003745105],[48,152.0650779545239],[64,174.23277711237708]],"out":[22.400698313200692,27.814642167222946,32.71975417007472,37.791361297788285,40.37114383953916,44.76745231358612,52.103872186643855,56.88136029649459,60.570952443082454,65.90476294982736,68.20751037020648,73.75512277229465,77.79108224372406,79.75675263249187,87.36788496645512,88.614143353837,91.28199543567244,93.94984751750789,96.61769959934335,99.2855516811788,101.95340376301425,104.62125584484971,107.28910792668516,109.95696000852061,111.57509047559365,113.19322094266671,114.81135140973976,116.42948187681282,118.04761234388586,119.6657428109589,121.28387327803196,122.902003745105,124.72469588319369,126.54738802128237,128.37008015937104,130.19277229745973,132.0154644355484,133.8381565736371,135.66084871172578,137.48354084981446,139.30623298790314,141.12892512599183,142.95161726408048,144.77430940216917,146.59700154025785,148.41969367834653,150.24238581643522,152.0650779545239,153.45055915188973,154.83604034925554,156.22152154662137,157.6070027439872,158.99248394135302,160.37796513871885,161.76344633608466,163.1489275334505,164.53440873081632,165.91988992818213,167.30537112554796,168.69085232291377,170.0763335202796,171.46181471764544,172.84729591501124,174.23277711237708]},{"table":[22.757448631919377,30.53984621290446,31.539047937852636,36.217003191780435,41.76604727857415,46.30008530825279,49.39986240325126,53.494511041730156,60.54891586773537,64.29609000694803,67.26019946173595,74.03003976163177,78.9427498293754,80.0373172131405,87.71778113349347,91.47172820135144],"points":[[24,103.3315316766266],[32,124.5899078689737],[48,146.13922483971987],[64,182.97858128315326]],"out":[22.757448631919377,30.53984621290446,31.539047937852636,36.217003191780435,41.76604727857415,46.30008530825279,49.39986240325126,53.494511041730156,60.54891586773537,64.29609000694803,67.26019946173595,74.03003976163177,78.9427498293754,80.0373172131405,87.71778113349347,91.47172820135144,92.95420363576083,94.43667907017023,95.91915450457962,97.40162993898902,98.88410537339841,100.36658080780781,101.8490562422172,103.3315316766266,105.98882870066998,108.64612572471337,111.30342274875676,113.96071977280015,116.61801679684353,119.27531382088692,121.93261084493031,124.5899078689737,125.93674017964533,127.28357249031697,128.6304048009886,129.97723711166023,131.3240694223319,132.67090173300352,134.01773404367515,135.36456635434678,136.7113986650184,138.05823097569007,139.4050632863617,140.75189559703333,142.09872790770495,143.4455602183766,144.79239252904824,146.13922483971987,148.44168461743445,150.74414439514905,153.04660417286362,155.34906395057823,157.6515237282928,159.9539835060074,162.25644328372198,164.55890306143658,166.86136283915116,169.16382261686573,171.46628239458033,173.7687421722949,176.0712019500095,178.37366172772408,180.6761215054387,182.97858128315326]},{"table":[25.13917091513699,27.167563174980756,33.72736300982616,36.927791273180155,40.83778700969242,47.59090423791337,51.85138997852453,53.758194644382705,61.12191466806451,63.45382248524636,69.71152218168312,73.83506105522241,78.82152987765159,82.00436149975593,84.11588942460465,90.52211793501804],"points":[[24,94.66832623200565],[32,125.06645792882104],[48,136.3699615819204],[64,174.97782270027676]],"out":[25.13917091513699,27.167563174980756,33.72736300982616,36.927791273180155,40.83778700969242,47.59090423791337,51.85138997852453,53.758194644382705,61.12191466806451,63.45382248524636,69.71152218168312,73.83506105522241,78.82152987765159,82.00436149975593,84.11588942460465,90.52211793501804,91.0403939721415,91.55867000926494,92.0769460463884,92.59522208351186,93.1134981206353,93.63177415775876,94.1500501948822,94.66832623200565,98.46809269410758,102.2678591562095,106.06762561831142,109.86739208041334,113.66715854251527,117.4669250046172,121.26669146671912,125.06645792882104,125.77292690713975,126.47939588545846,127.18586486377717,127.89233384209588,128.5988028204146,129.3052717987333,130.011740777052,130.7182097553707,131.42467873368943,132.13114771200813,132.83761669032685,133.54408566864555,134.25055464696428,134.95702362528297,135.6634926036017,136.3699615819204,138.78295290181768,141.19594422171494,143.6089355416122,146.02192686150948,148.43491818140677,150.84790950130403,153.2609008212013,155.67389214109858,158.08688346099586,160.49987478089312,162.91286610079038,165.32585742068767,167.73884874058496,170.15184006048221,172.56483138037947,174.97782270027676]},{"table":[23.31833182543431,28.700134977704856,32.35365338836243,39.47069829984901,41.80805400562156,47.52761878334897,49.08332593601431,56.27800389511262,59.433865666600916,64.42800687622153,68.1547057167171,74.1688013672121,78.93035772926112,81.4500089801748,84.20036699794008,89.12033143991277],"points":[[24,104.88779343190649],[64,183.9044151801816]],"out":[23.31833182543431,28.700134977704856,32.35365338836243,39.47069829984901,41.80805400562156,47.52761878334897,49.08332593601431,56.27800389511262,59.433865666600916,64.42800687622153,68.1547057167171,74.1688013672121,78.93035772926112,81.4500089801748,84.20036699794008,89.12033143991277,91.09126418891199,93.06219693791121,95.03312968691041,97.00406243590963,98.97499518490885,100.94592793390805,102.91686068290727,104.88779343190649,106.86320897561336,108.83862451932025,110.81404006302712,112.789455606734,114.76487115044088,116.74028669414776,118.71570223785463,120.6911177815615,122.66653332526839,124.64194886897526,126.61736441268214,128.59277995638902,130.5681955000959,132.54361104380277,134.51902658750964,136.49444213121652,138.4698576749234,140.4452732186303,142.42068876233716,144.39610430604404,146.3715198497509,148.34693539345778,150.32235093716469,152.29776648087153,154.27318202457843,156.2485975682853,158.22401311199218,160.19942865569905,162.17484419940592,164.15025974311283,166.12567528681967,168.10109083052657,170.07650637423345,172.05192191794032,174.02733746164722,176.00275300535407,177.97816854906097,179.9535840927678,181.9289996364747,183.9044151801816]},{"table":[25.59586208910397,29.549055813180807,31.996863957815542,37.30686572015639,41.1843840994003,47.36645015482748,51.14200659270114,56.679878608361484,58.49581073645146,65.39710193236746,68.2715582986829,74.62753215138217,77.28257014888649,80.88844696996928,86.73265770647363,89.40117993385842],"points":[[48,143.76101459064338],[64,175.47636811101188]],"out":[25.59586208910397,29.549055813180807,31.996863957815542,37.30686572015639,41.1843840994003,47.36645015482748,51.14200659270114,56.679878608361484,58.49581073645146,65.39710193236746,68.2715582986829,74.62753215138217,77.28257014888649,80.88844696996928,86.73265770647363,89.40117993385842,91.09992476688295,92.79866959990747,94.497414432932,96.19615926595654,97.89490409898107,99.59364893200559,101.29239376503013,102.99113859805466,104.68988343107918,106.38862826410372,108.08737309712825,109.78611793015278,111.4848627631773,113.18360759620184,114.88235242922637,116.58109726225089,118.27984209527543,119.97858692829996,121.6773317613245,123.37607659434903,125.07482142737355,126.77356626039808,128.4723110934226,130.17105592644714,131.86980075947167,133.5685455924962,135.26729042552074,136.96603525854528,138.6647800915698,140.36352492459432,142.06226975761885,143.76101459064338,145.7432241856664,147.72543378068946,149.70764337571248,151.6898529707355,153.67206256575855,155.65427216078157,157.6364817558046,159.61869135082765,161.60090094585067,163.5831105408737,165.56532013589674,167.54752973091976,169.52973932594278,171.5119489209658,173.49415851598886,175.47636811101188]},{"table":[23.113997395707763,27.468031282984693,33.09196234424875,35.68315640566185,42.52914767133393,48.09490372612725,50.254228127095914,53.2208761562036,59.9480221081079,64.70234609588589,67.0686751690172,74.6147803559184,76.87842503076315,82.23606171223528,85.79662202040436,91.06516955873819],"points":[[24,97.0501812568268],[32,108.15393647508465],[48,140.58956356308371],[64,179.3166812143076]],"out":[23.113997395707763,27.468031282984693,33.09196234424875,35.68315640566185,42.52914767133393,48.09490372612725,50.254228127095914,53.2208761562036,59.9480221081079,64.70234609588589,67.0686751690172,74.6147803559184,76.87842503076315,82.23606171223528,85.79662202040436,91.06516955873819,91.81329602099927,92.56142248326034,93.30954894552141,94.0576754077825,94.80580187004358,95.55392833230465,96.30205479456572,97.0501812568268,98.43815065910903,99.82612006139126,101.2140894636735,102.60205886595573,103.99002826823795,105.37799767052019,106.76596707280243,108.15393647508465,110.1811631680846,112.20838986108453,114.23561655408447,116.26284324708442,118.29006994008435,120.3172966330843,122.34452332608424,124.37175001908417,126.39897671208412,128.42620340508407,130.453430098084,132.48065679108396,134.5078834840839,136.53511017708382,138.56233687008378,140.58956356308371,143.01000841628522,145.4304532694867,147.8508981226882,150.2713429758897,152.69178782909117,155.11223268229267,157.53267753549417,159.95312238869565,162.37356724189715,164.79401209509865,167.21445694830015,169.63490180150163,172.05534665470313,174.4757915079046,176.8962363611061,179.3166812143076]}],"split":[{"total":24,"n":2,"out":[12,12]},{"total":32,"n":2,"out":[16,16]},{"total":48,"n":3,"out":[16,16,16]},{"total":64,"n":4,"out":[16,16,16,16]},{"total":7,"n":3,"out":[3,2,2]},{"total":8,"n":2,"out":[4,4]},{"total":5,"n":4,"out":[2,1,1,1]}],"wide":[{"slots":1,"cap":16,"out":[]},{"slots":1,"cap":24,"out":[]},{"slots":1,"cap":40,"out":[]},{"slots":1,"cap":64,"out":[]},{"slots":1,"cap":100,"out":[]},{"slots":2,"cap":16,"out":[]},{"slots":2,"cap":24,"out":[24]},{"slots":2,"cap":40,"out":[24,32]},{"slots":2,"cap":64,"out":[24,32]},{"slots":2,"cap":100,"out":[24,32]},{"slots":3,"cap":16,"out":[]},{"slots":3,"cap":24,"out":[24]},{"slots":3,"cap":40,"out":[24,32]},{"slots":3,"cap":64,"out":[24,32,48]},{"slots":3,"cap":100,"out":[24,32,48]},{"slots":4,"cap":16,"out":[]},{"slots":4,"cap":24,"out":[24]},{"slots":4,"cap":40,"out":[24,32]},{"slots":4,"cap":64,"out":[24,32,48,64]},{"slots":4,"cap":100,"out":[24,32,48,64]},{"slots":8,"cap":16,"out":[]},{"slots":8,"cap":24,"out":[24]},{"slots":8,"cap":40,"out":[24,32]},{"slots":8,"cap":64,"out":[24,32,48,64]},{"slots":8,"cap":100,"out":[24,32,48,64]}],"method":[{"env":{},"out":{"reps":5,"warm":3,"stat":"median","recheck":4,"shape":"raw","cycle":4,"deep":5}},{"env":{"TF_DSV41_CALIB_SHAPE":"fit"},"out":{"reps":5,"warm":2,"stat":"median","recheck":2,"shape":"fit","cycle":0,"deep":3}},{"env":{"TF_DSV41_CALIB_SHAPE":"FIT","TF_DSV41_CALIB_WARM":"0"},"out":{"reps":5,"warm":0,"stat":"median","recheck":2,"shape":"fit","cycle":0,"deep":3}},{"env":{"TF_DSV41_CALIB_STAT":"min","TF_DSV41_CALIB_REPS":" 7 "},"out":{"reps":7,"warm":3,"stat":"min","recheck":4,"shape":"raw","cycle":4,"deep":5}},{"env":{"TF_DSV41_CALIB_REPS":"0"},"out":null},{"env":{"TF_DSV41_CALIB_RECHECK":"9"},"out":null},{"env":{"TF_DSV41_CALIB_SHAPE":"line"},"out":null},{"env":{"TF_DSV41_CALIB_STAT":"mean"},"out":null},{"env":{"TF_DSV41_CALIB_CYCLE":"8","TF_DSV41_CALIB_DEEP_REPS":"50"},"out":{"reps":5,"warm":3,"stat":"median","recheck":4,"shape":"raw","cycle":8,"deep":50}},{"env":{"TF_DSV41_CALIB_DEEP_REPS":"51"},"out":null}],"fallback":[{"vocab":129280,"out":[112649,120568,128487,7128,15047,22966,30885,38804,46723,54642,62561,70480,78399,86318,94237,102156,110075,117994,125913,4554,12473,20392,28311,36230,44149,52068,59987,67906,75825,83744,91663,99582,107501,115420,123339,1980,9899,17818,25737,33656,41575,49494,57413,65332,73251,81170,89089,97008,104927,112846,120765,128684,7325,15244,23163,31082,39001,46920,54839,62758,70677,78596,86515,94434]},{"vocab":1000,"out":[873,808,743,678,613,548,483,418,353,288,223,158,93,28,961,896,831,766,701,636,571,506,441,376,311,246,181,116,51,984,919,854,789,724,659,594,529,464,399,334,269,204,139,74,9,942,877,812,747,682,617,552,487,422,357,292,227,162,97,32,965,900,835,770]},{"vocab":3,"out":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]},{"vocab":2,"out":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]}],"text":"The committee met on Tuesday to review the quarterly results. Revenue grew by eleven percent, driven mostly\nby the new subscription plans, while operating costs stayed flat. Two questions remained open: whether to expand\nthe support team before the summer, and how to price the enterprise tier for customers with more than fifty seats.\n\nHere is a short function that answers the second question from the usage data:\n\n```python\ndef enterprise_price(seats: int, base: float = 12.0, discount: float = 0.15) -> float:\n \"\"\"Monthly price for a team: the first fifty seats at the base rate, the rest discounted.\"\"\"\n if seats <= 50:\n return seats * base\n return 50 * base + (seats - 50) * base * (1.0 - discount)\n\n\nfor team in (10, 50, 120):\n print(f\"{team} seats: ${enterprise_price(team):,.2f} a month\")\n```\n\nIn summary, the pricing keeps small teams unchanged and rewards larger ones, and the"}} diff --git a/zig/src/families/deepseek_v41/draft/fixtures/golden.json b/zig/src/families/deepseek_v41/draft/fixtures/golden.json new file mode 100644 index 0000000000..15ef4a467f --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/fixtures/golden.json @@ -0,0 +1 @@ +{"costs": {"verify": [21.954, 26.573, 31.977, 36.158, 40.852, 44.776, 47.237, 51.089, 57.951, 59.663, 60.793, 62.458, 69.962, 70.85, 70.971, 72.362, 75.84174999999999, 79.3215, 82.80125, 86.281, 89.76075, 93.2405, 96.72025, 100.2, 102.075, 103.95, 105.825, 107.7, 109.575, 111.45, 113.325, 115.2, 117.19375000000001, 119.1875, 121.18125, 123.175, 125.16875, 127.1625, 129.15625, 131.15, 133.14375, 135.1375, 137.13125, 139.125, 141.11875, 143.1125, 145.10625, 147.1, 150.17499999999998, 153.25, 156.325, 159.4, 162.475, 165.55, 168.625, 171.7, 174.775, 177.85, 180.925, 184.0, 187.07500000000002, 190.15, 193.22500000000002, 196.3], "draft": 3.61, "slot": 0.83}, "traces": [{"joint": 1, "ops": [{"op": "joint", "asks": [{"slot": 0, "conf": [0.233193, 0.625368, 0.708788, 0.859433, 0.759429], "most": 0, "first": null}, {"slot": 2, "conf": [0.024106, 0.077206, 0.172643, 0.110568, 0.065316], "most": 0, "first": 20}, {"slot": 4, "conf": [0.063641, 0.049826, 0.21935, 0.137342, 0.008774], "most": 0, "first": 91}], "others": [4], "ks": [0, 0, 0]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 34}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 20}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 63}, {"op": "reset", "slot": 5}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.036934, 0.012086, 0.123237, 0.245801, 0.272936], "most": 1, "first": 5}], "others": [], "ks": [0]}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 42}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.497145, 0.154144, 0.893636, 0.129612, 0.111742], "most": 0, "first": null}, {"slot": 3, "conf": [0.450788, 0.338278, 0.098838, 0.377515, 0.049423], "most": 0, "first": 95}], "others": [], "ks": [0, 0]}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 27}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 74}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.137513, 0.111525, 0.141617, 0.019469, 0.206884], "most": 1, "first": 41}, {"slot": 1, "conf": [0.164803, 0.022465, 0.146102, 0.150604, 0.136267], "most": 5, "first": null}, {"slot": 0, "conf": [0.533157, 0.044931, 0.311462, 0.411311, 0.355108], "most": 4, "first": 10}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 41}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 17}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": 3, "bonus": 10}, {"op": "check", "kept": [1.9409, 0.0, 0.0, 0.0, 0.0], "prob": [0.856110556282, 0.0, 0.0, 0.0, 0.0], "rates": [0.07672094673648273, 0.03313123281317298, 0.0455497859160062, 0.0455497859160062, 0.0455497859160062, 0.053814555043320715], "joint": [0.03911750899702707, 0.07298470970331716, 0.08918750185807295, 0.13988704121421952, 0.15016593335635878], "zero": 1, "reached": [3, 0, 0, 0, 0], "statkept": [2, 0, 0, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.00835, 0.634153, 0.386925, 0.021562, 0.196913], "most": 5, "first": 71}, {"slot": 2, "conf": [0.526242, 0.634179, 0.837469, 0.112014, 0.908388], "most": 3, "first": null}, {"slot": 0, "conf": [0.217015, 0.272439, 0.109698, 0.181125, 0.145367], "most": 3, "first": null}], "others": [3], "ks": [1, 3, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 80}, {"op": "record", "slot": 2, "rows": 4, "keep": 4, "tokens": null, "bonus": 60}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 62}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.021807, 0.486914, 0.112946, 0.626668, 0.101634], "most": 1, "first": null}, {"slot": 2, "conf": [0.185013, 0.167469, 0.663118, 0.17208, 0.156216], "most": 3, "first": null}, {"slot": 0, "conf": [0.077616, 0.315799, 0.107792, 0.537296, 0.220373], "most": 3, "first": null}, {"slot": 1, "conf": [0.061884, 0.070115, 0.022832, 0.158307, 0.200396], "most": 3, "first": null}], "others": [], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 1}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 4}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 78}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 67}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.960385, 0.191736, 0.871705, 0.433728, 0.147284], "most": 5, "first": 31}, {"slot": 2, "conf": [0.163523, 0.15104, 0.377471, 0.018011, 0.424208], "most": 4, "first": null}, {"slot": 3, "conf": [0.200715, 0.220867, 0.087686, 0.208794, 0.019954], "most": 1, "first": 3}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 59}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 63}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 86}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.000665, 0.262195, 0.235985, 0.278567, 0.230684], "most": 0, "first": null}, {"slot": 2, "conf": [0.076517, 0.689403, 0.582609, 0.079608, 0.865494], "most": 5, "first": 31}], "others": [2], "ks": [0, 1]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 70}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 31}, {"op": "check", "kept": [3.7712764888031476, 1.0, 1.0, 0.0, 0.0], "prob": [2.7834382431253135, 0.634179, 0.837469, 0.0, 0.0], "rates": [0.052061253783737556, 0.044174977084230634, 0.05165733964700818, 0.04444325929086335, 0.0455497859160062, 0.053814555043320715], "joint": [0.03911750899702707, 0.07298470970331716, 0.09995370565211902, 0.0874294007588872, 0.15016593335635878], "zero": 1, "reached": [14, 1, 1, 0, 0], "statkept": [5, 1, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.085457, 0.206676, 0.182525, 0.24594, 0.183123], "most": 2, "first": null}, {"slot": 5, "conf": [0.157766, 0.024151, 0.038045, 0.00987, 0.096874], "most": 0, "first": 31}], "others": [], "ks": [1, 0]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 85}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 31}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.349879, 0.542072, 0.38561, 0.615242, 0.993649], "most": 0, "first": 99}, {"slot": 4, "conf": [0.316111, 0.439984, 0.585088, 0.330467, 0.102201], "most": 2, "first": 58}], "others": [1], "ks": [0, 1]}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 87}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 58}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.023188, 0.268246, 0.225661, 0.023239, 0.431066], "most": 4, "first": null}, {"slot": 4, "conf": [0.058827, 0.171755, 0.06169, 0.037375, 0.286868], "most": 4, "first": 18}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 32}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 18}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.228612, 0.000392, 0.03056, 0.17919, 0.28904], "most": 4, "first": null}, {"slot": 4, "conf": [0.078933, 0.101823, 0.109658, 0.246013, 0.241118], "most": 5, "first": 86}, {"slot": 5, "conf": [0.007418, 0.108376, 0.456705, 0.175823, 0.669958], "most": 0, "first": 8}], "others": [4], "ks": [1, 1, 0]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 65}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 44}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 8}, {"op": "check", "kept": [5.970101763719956, 1.0, 1.0, 0.0, 0.0], "prob": [3.0489617888923, 0.634179, 0.837469, 0.0, 0.0], "rates": [0.052061253783737556, 0.0490587724093464, 0.053813940745959056, 0.04444325929086335, 0.04444325929086335, 0.04621143270845207], "joint": [0.03911750899702707, 0.06205853812650915, 0.09995370565211902, 0.0874294007588872, 0.15016593335635878], "zero": 1, "reached": [20, 1, 1, 0, 0], "statkept": [8, 1, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.35351, 0.025466, 0.419433, 0.369522, 0.522691], "most": 1, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 68}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.452312, 0.877248, 0.346947, 0.728373, 0.312913], "most": 1, "first": null}, {"slot": 1, "conf": [0.399764, 0.650239, 0.269553, 0.616529, 0.446469], "most": 1, "first": 21}, {"slot": 3, "conf": [0.415775, 0.147442, 0.084549, 0.089167, 0.232286], "most": 1, "first": null}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 18}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 64}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 51}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.173848, 0.876804, 0.591416, 0.299611, 0.140685], "most": 0, "first": 0}, {"slot": 3, "conf": [0.254999, 0.289294, 0.244209, 0.189789, 0.233529], "most": 1, "first": null}], "others": [], "ks": [0, 1]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 67}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 20}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.116849, 0.122661, 0.136949, 0.0094, 0.140568], "most": 1, "first": 24}], "others": [], "ks": [0]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 24}, {"op": "check", "kept": [7.77089344575597, 1.0, 1.0, 0.0, 0.0], "prob": [4.363953977819456, 0.634179, 0.837469, 0.0, 0.0], "rates": [0.052061253783737556, 0.052062544470090066, 0.05287528231623951, 0.040315874879682315, 0.04462393181463219, 0.04621143270845207], "joint": [0.03587637002888048, 0.06084176927865062, 0.10130792018333241, 0.0874294007588872, 0.15016593335635878], "zero": 2, "reached": [25, 1, 1, 0, 0], "statkept": [10, 1, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.050823, 0.174942, 0.113948, 0.212215, 0.02917], "most": 4, "first": 83}, {"slot": 0, "conf": [0.155326, 0.194051, 0.037477, 0.210721, 0.219605], "most": 4, "first": null}, {"slot": 1, "conf": [0.210741, 0.077701, 0.218934, 0.280339, 0.101604], "most": 1, "first": null}, {"slot": 4, "conf": [0.558295, 0.081161, 0.447502, 0.421148, 0.366245], "most": 1, "first": 46}], "others": [], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 83}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 13}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 98}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 85}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.258669, 0.267789, 0.140711, 0.27943, 0.250874], "most": 0, "first": null}], "others": [], "ks": [0]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 90}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.162585, 0.223956, 0.056299, 0.019933, 0.017486], "most": 0, "first": null}], "others": [5], "ks": [0]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 1}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.389448, 0.662605, 0.63838, 0.313114, 0.397464], "most": 3, "first": null}, {"slot": 5, "conf": [0.185999, 0.153315, 0.203462, 0.101567, 0.193403], "most": 4, "first": null}], "others": [1], "ks": [1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 73}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 17}, {"op": "check", "kept": [7.442936693600974, 1.0, 1.0, 0.0, 0.0], "prob": [5.097604870909366, 0.634179, 0.837469, 0.0, 0.0], "rates": [0.04859086491739553, 0.049248468372633615, 0.05424519506233112, 0.040315874879682315, 0.043155532539271534, 0.0420601038884566], "joint": [0.03993004256542538, 0.06084176927865062, 0.10130792018333241, 0.07868646068299848, 0.15016593335635878], "zero": 2, "reached": [31, 1, 1, 0, 0], "statkept": [11, 1, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.415438, 0.338052, 0.242526, 0.160354, 0.865391], "most": 3, "first": 8}], "others": [6], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 8}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.021882, 0.2907, 0.043637, 0.286989, 0.086588], "most": 0, "first": null}, {"slot": 2, "conf": [0.03833, 0.15388, 0.208225, 0.084079, 0.01463], "most": 1, "first": 31}, {"slot": 1, "conf": [0.204287, 0.117975, 0.494474, 0.330017, 0.089978], "most": 1, "first": 42}], "others": [], "ks": [0, 1, 1]}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 85}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 15}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 95}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.103705, 0.288168, 0.057919, 0.079494, 0.054114], "most": 5, "first": 55}], "others": [], "ks": [0]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 12}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.244653, 0.223568, 0.101956, 0.072322, 0.272383], "most": 5, "first": 55}, {"slot": 0, "conf": [0.41205, 0.313441, 0.407416, 0.399963, 0.668793], "most": 0, "first": 92}, {"slot": 2, "conf": [0.409298, 0.582598, 0.387572, 0.952444, 0.709999], "most": 3, "first": null}], "others": [], "ks": [1, 0, 2]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 55}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 92}, {"op": "record", "slot": 2, "rows": 3, "keep": 3, "tokens": null, "bonus": 71}, {"op": "check", "kept": [10.826457735928424, 1.97, 1.0, 0.0, 0.0], "prob": [5.567480842884266, 1.19775163, 0.837469, 0.0, 0.0], "rates": [0.048233062156339435, 0.05314746904159928, 0.05821576982039049, 0.04017910954164564, 0.04607696528728988, 0.04242121437796359], "joint": [0.04420719914238033, 0.06292597938387601, 0.10229317223161581, 0.07868646068299848, 0.15016593335635878], "zero": 3, "reached": [36, 2, 1, 0, 0], "statkept": [16, 2, 1, 0, 0]}, {"op": "tree", "slot": 4, "conf": [0.713765, 0.574473, 0.261474, 0.378934, 0.097497], "most": 3, "first": null, "sibs": [0.293189], "k": 3, "lens": [2]}, {"op": "record", "slot": 4, "rows": 4, "keep": 2, "tokens": null, "bonus": 84}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.675451, 0.053021, 0.048905, 0.361581, 0.209185], "most": 3, "first": 18}], "others": [7], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 79}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.661805, 0.394608, 0.848324, 0.383076, 0.051695], "most": 4, "first": null}, {"slot": 1, "conf": [0.593582, 0.569724, 0.291988, 0.032602, 0.083099], "most": 1, "first": 98}, {"slot": 5, "conf": [0.276188, 0.287419, 0.567075, 0.516607, 0.433392], "most": 2, "first": null}], "others": [], "ks": [2, 1, 1]}, {"op": "record", "slot": 2, "rows": 3, "keep": 2, "tokens": 3, "bonus": 21}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 6}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 64}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.493176, 0.965571, 0.133183, 0.315863, 0.211607], "most": 5, "first": null}, {"slot": 2, "conf": [0.574695, 0.986789, 0.141937, 0.574704, 0.156739], "most": 2, "first": 63}, {"slot": 3, "conf": [0.349217, 0.655509, 0.851222, 0.327217, 0.28311], "most": 1, "first": null}, {"slot": 4, "conf": [0.262073, 0.072426, 0.04005, 0.169075, 0.073022], "most": 0, "first": 67}], "others": [], "ks": [2, 2, 1, 0]}, {"op": "record", "slot": 5, "rows": 3, "keep": 2, "tokens": null, "bonus": 64}, {"op": "record", "slot": 2, "rows": 3, "keep": 2, "tokens": 2, "bonus": 63}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 64}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 67}, {"op": "check", "kept": [14.781046042176905, 1.7440268356999995, 1.0, 0.0, 0.0], "prob": [8.21939591586144, 3.87934643973378, 0.837469, 0.0, 0.0], "rates": [0.048233062156339435, 0.0555926173004225, 0.060198980784948404, 0.04327641828625384, 0.04523848761273269, 0.043176650067643416], "joint": [0.04782457944260453, 0.06292597938387601, 0.1057639027799812, 0.07868646068299848, 0.15016593335635878], "zero": 3, "reached": [44, 6, 1, 0, 0], "statkept": [23, 2, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.068332, 0.174522, 0.188418, 0.11823, 0.103314], "most": 3, "first": 49}, {"slot": 4, "conf": [0.060934, 0.540406, 0.176262, 0.016293, 0.001238], "most": 0, "first": 9}], "others": [], "ks": [1, 0]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 49}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 9}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.053792, 0.151735, 0.230241, 0.221944, 0.09091], "most": 5, "first": null}, {"slot": 3, "conf": [0.132846, 0.504736, 0.039881, 0.557904, 0.661156], "most": 5, "first": null}], "others": [8], "ks": [1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 49}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 38}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.834455, 0.939319, 0.7892, 0.834513, 0.983909], "most": 0, "first": null}, {"slot": 2, "conf": [0.27638, 0.075369, 0.125081, 0.123118, 0.160722], "most": 2, "first": null}, {"slot": 1, "conf": [0.256652, 0.330698, 0.147281, 0.915343, 0.165195], "most": 2, "first": 68}, {"slot": 5, "conf": [0.418619, 0.696391, 0.566795, 0.297336, 0.116251], "most": 0, "first": null}], "others": [], "ks": [0, 1, 1, 0]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 6}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 24}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 68}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 66}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.270116, 0.631479, 0.598461, 0.568053, 0.405801], "most": 2, "first": 68}, {"slot": 5, "conf": [0.278432, 0.036886, 0.119683, 0.056292, 0.229916], "most": 4, "first": null}], "others": [3], "ks": [1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 32}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 99}, {"op": "check", "kept": [15.573769444927956, 1.7440268356999995, 1.0, 0.0, 0.0], "prob": [7.8960231174086495, 3.87934643973378, 0.837469, 0.0, 0.0], "rates": [0.04795059171030171, 0.05367732020216574, 0.060595043325455976, 0.04768777857610652, 0.04339048114938832, 0.04544559404965689], "joint": [0.04782457944260453, 0.06640867901063142, 0.1057639027799812, 0.09158108196506835, 0.15016593335635878], "zero": 3, "reached": [51, 6, 1, 0, 0], "statkept": [27, 2, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.908183, 0.697551, 0.278887, 0.957661, 0.646339], "most": 1, "first": 62}, {"slot": 0, "conf": [0.057806, 0.089642, 0.131402, 0.25209, 0.194991], "most": 0, "first": null}, {"slot": 2, "conf": [0.162328, 0.182293, 0.196063, 0.062733, 0.079933], "most": 2, "first": 52}], "others": [], "ks": [1, 0, 1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 83}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 60}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 52}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.408984, 0.958688, 0.181577, 0.460042, 0.025828], "most": 0, "first": 28}, {"slot": 4, "conf": [0.011799, 0.180292, 0.174625, 0.110434, 0.283188], "most": 4, "first": 40}], "others": [], "ks": [0, 1]}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 19}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 40}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.629322, 0.939106, 0.767927, 0.193221, 0.74827], "most": 2, "first": null}, {"slot": 0, "conf": [0.023667, 0.350004, 0.166206, 0.631492, 0.366344], "most": 5, "first": null}, {"slot": 5, "conf": [0.113701, 0.210975, 0.466904, 0.019701, 0.950681], "most": 3, "first": 12}, {"slot": 2, "conf": [0.388102, 0.520892, 0.302447, 0.802797, 0.036597], "most": 1, "first": null}], "others": [], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 55}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 90}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 33}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 83}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.085839, 0.266613, 0.127241, 0.105601, 0.188028], "most": 5, "first": 92}], "others": [], "ks": [0]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 83}, {"op": "check", "kept": [15.860765219575976, 1.7440268356999995, 1.0, 0.0, 0.0], "prob": [8.215612535108596, 3.87934643973378, 0.837469, 0.0, 0.0], "rates": [0.046032568041889634, 0.05206299622543278, 0.05994053898532656, 0.04768777857610652, 0.04607617488760705, 0.047095514937127485], "joint": [0.04782457944260453, 0.06857753057210804, 0.1018598782254762, 0.09773328586288019, 0.15016593335635878], "zero": 4, "reached": [58, 6, 1, 0, 0], "statkept": [31, 2, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.532227, 0.192641, 0.079544, 0.607434, 0.3387], "most": 1, "first": 57}, {"slot": 4, "conf": [0.170553, 0.584994, 0.089164, 0.617831, 0.360084], "most": 4, "first": null}, {"slot": 5, "conf": [0.562211, 0.083199, 0.118497, 0.535822, 0.646661], "most": 2, "first": null}, {"slot": 2, "conf": [0.978108, 0.887895, 0.747204, 0.394823, 0.790179], "most": 4, "first": 75}], "others": [], "ks": [1, 1, 1, 4]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 57}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": 1, "bonus": 2}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 63}, {"op": "record", "slot": 2, "rows": 5, "keep": 2, "tokens": null, "bonus": 20}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.140963, 0.127154, 0.066445, 0.271718, 0.016282], "most": 5, "first": 20}, {"slot": 5, "conf": [0.175307, 0.232052, 0.031002, 0.243853, 0.28862], "most": 5, "first": null}, {"slot": 0, "conf": [0.019816, 0.392764, 0.45062, 0.060583, 0.162961], "most": 4, "first": null}], "others": [5], "ks": [1, 1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 20}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 23}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 15}, {"op": "tree", "slot": 3, "conf": [0.118751, 0.246217, 0.234169, 0.06966, 0.01915], "most": 1, "first": null, "sibs": [0.45795, 0.118588, 0.274228], "k": 1, "lens": [1, 0, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 9}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.270706, 0.761375, 0.660071, 0.705531, 0.961404], "most": 4, "first": 82}], "others": [], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 80}, {"op": "reset", "slot": 3}, {"op": "check", "kept": [18.27216153401644, 1.6917060306289995, 1.0, 0.0, 0.0], "prob": [8.798268574810269, 4.650861046541767, 0.837469, 0.0, 0.0], "rates": [0.04813179861655459, 0.05399130739950868, 0.05648408025413941, 0.06626246562634595, 0.04607617488760705, 0.04970987509264113], "joint": [0.04701950586116225, 0.06857753057210804, 0.1018598782254762, 0.10229531195198939, 0.15016593335635878], "zero": 4, "reached": [67, 7, 1, 0, 0], "statkept": [38, 2, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.829567, 0.024982, 0.42525, 0.547545, 0.783585], "most": 4, "first": null}, {"slot": 1, "conf": [0.662911, 0.374425, 0.063384, 0.074694, 0.665606], "most": 0, "first": 68}, {"slot": 0, "conf": [0.166126, 0.220882, 0.059883, 0.218538, 0.273025], "most": 5, "first": 19}], "others": [], "ks": [1, 0, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 0}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 68}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 79}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.351355, 0.597953, 0.734742, 0.420562, 0.298304], "most": 0, "first": null}, {"slot": 5, "conf": [0.24425, 0.836073, 0.057126, 0.72762, 0.032462], "most": 5, "first": null}, {"slot": 1, "conf": [0.342309, 0.595576, 0.64642, 0.272524, 0.071263], "most": 2, "first": 38}, {"slot": 4, "conf": [0.580074, 0.119566, 0.42679, 0.402132, 0.148505], "most": 3, "first": 16}], "others": [], "ks": [0, 1, 1, 1]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 11}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 13}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 38}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 16}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.60537, 0.231875, 0.352938, 0.297017, 0.126346], "most": 5, "first": null}, {"slot": 2, "conf": [0.140091, 0.061236, 0.230129, 0.248452, 0.262684], "most": 0, "first": 66}], "others": [4], "ks": [1, 0]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 21}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 66}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.48242, 0.593939, 0.258024, 0.795217, 0.998706], "most": 1, "first": null}, {"slot": 3, "conf": [0.012293, 0.137394, 0.174503, 0.106668, 0.020184], "most": 5, "first": null}, {"slot": 4, "conf": [0.93432, 0.086861, 0.472778, 0.807873, 0.296413], "most": 1, "first": null}, {"slot": 5, "conf": [0.865895, 0.094643, 0.147348, 0.486069, 0.425607], "most": 5, "first": null}], "others": [], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 41}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 68}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 57}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 49}, {"op": "check", "kept": [18.943429634359724, 1.6917060306289995, 1.0, 0.0, 0.0], "prob": [10.989504475778018, 4.650861046541767, 0.837469, 0.0, 0.0], "rates": [0.0484007812455514, 0.05359401462044718, 0.06136305152516875, 0.05754071005236205, 0.04607617488760705, 0.04919642118397896], "joint": [0.04891346881277229, 0.06970772226644541, 0.10053587518376252, 0.09915652288252323, 0.15016593335635878], "zero": 4, "reached": [77, 7, 1, 0, 0], "statkept": [44, 2, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.077699, 0.147045, 0.057259, 0.04504, 0.261795], "most": 4, "first": 40}], "others": [1], "ks": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 79}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.247689, 0.407094, 0.377857, 0.817526, 0.012177], "most": 0, "first": null}, {"slot": 3, "conf": [0.209734, 0.29304, 0.173243, 0.047912, 0.220141], "most": 4, "first": 45}, {"slot": 2, "conf": [0.253316, 0.632124, 0.37733, 0.610569, 0.539739], "most": 2, "first": null}], "others": [], "ks": [0, 1, 1]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 68}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 54}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 94}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.342098, 0.437051, 0.303134, 0.126637, 0.328768], "most": 2, "first": 73}], "others": [1], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 73}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.187548, 0.500661, 0.225061, 0.178178, 0.636862], "most": 1, "first": 41}, {"slot": 3, "conf": [0.66815, 0.018173, 0.43488, 0.369026, 0.008757], "most": 0, "first": 58}, {"slot": 1, "conf": [0.734511, 0.727391, 0.369936, 0.833902, 0.076991], "most": 5, "first": 50}], "others": [3], "ks": [1, 0, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 41}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 46}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 39}, {"op": "check", "kept": [18.54731237276405, 1.6917060306289995, 1.0, 0.0, 0.0], "prob": [10.875855595508439, 4.650861046541767, 0.837469, 0.0, 0.0], "rates": [0.048436872915484576, 0.0514502540356293, 0.06136305152516875, 0.052061253783737556, 0.04607617488760705, 0.04919642118397896], "joint": [0.046317739694302924, 0.06970772226644541, 0.10116920200175478, 0.10362654528288129, 0.15016593335635878], "zero": 4, "reached": [83, 7, 1, 0, 0], "statkept": [47, 2, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.189765, 0.043444, 0.120501, 0.275177, 0.110745], "most": 4, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 15}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.004523, 0.289619, 0.824803, 0.195899, 0.71529], "most": 2, "first": null}, {"slot": 2, "conf": [0.072697, 0.283058, 0.279528, 0.075445, 0.059621], "most": 1, "first": 66}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 24}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 53}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.383305, 0.084407, 0.786681, 0.428131, 0.0834], "most": 5, "first": 24}], "others": [6], "ks": [1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 24}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.376619, 0.198172, 0.33045, 0.015713, 0.021745], "most": 3, "first": null}, {"slot": 3, "conf": [0.258281, 0.693167, 0.795775, 0.568978, 0.772463], "most": 4, "first": 0}, {"slot": 2, "conf": [0.533532, 0.838226, 0.415551, 0.745025, 0.039991], "most": 0, "first": 91}, {"slot": 1, "conf": [0.229325, 0.282483, 0.024039, 0.121731, 0.179835], "most": 0, "first": null}], "others": [], "ks": [1, 1, 0, 0]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 15}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 0}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 95}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 53}, {"op": "check", "kept": [20.046991808885714, 1.6917060306289995, 1.0, 0.0, 0.0], "prob": [10.276846776056537, 4.650861046541767, 0.837469, 0.0, 0.0], "rates": [0.0475346493640543, 0.052374187381748906, 0.062379140415445074, 0.05646150608501049, 0.04607617488760705, 0.05269907073971929], "joint": [0.04913476026916489, 0.07211855466174337, 0.10009379757482416, 0.10362654528288129, 0.15016593335635878], "zero": 4, "reached": [89, 7, 1, 0, 0], "statkept": [52, 2, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.298381, 0.117334, 0.292253, 0.268289, 0.011069], "most": 2, "first": null}, {"slot": 3, "conf": [0.424262, 0.280527, 0.626986, 0.494594, 0.688774], "most": 3, "first": 86}, {"slot": 2, "conf": [0.491912, 0.093067, 0.516253, 0.458952, 0.892252], "most": 1, "first": 83}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": 4, "bonus": 89}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 86}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 20}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.323275, 0.28928, 0.042721, 0.24253, 0.308061], "most": 5, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 38}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.097667, 0.165077, 0.218378, 0.290958, 0.098699], "most": 0, "first": 10}, {"slot": 1, "conf": [0.316094, 0.48459, 0.714024, 0.711686, 0.061133], "most": 5, "first": null}, {"slot": 5, "conf": [0.219918, 0.144924, 0.560594, 0.175024, 0.177722], "most": 0, "first": 98}], "others": [], "ks": [0, 1, 0]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": 1, "bonus": 10}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 62}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 98}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.454452, 0.796391, 0.979113, 0.864106, 0.881777], "most": 3, "first": null}, {"slot": 4, "conf": [0.684189, 0.808932, 0.782606, 0.343322, 0.024879], "most": 2, "first": 64}, {"slot": 5, "conf": [0.124601, 0.28237, 0.079521, 0.172077, 0.294863], "most": 4, "first": null}], "others": [], "ks": [1, 2, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 53}, {"op": "record", "slot": 4, "rows": 3, "keep": 3, "tokens": null, "bonus": 64}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": 3, "bonus": 6}, {"op": "check", "kept": [20.06667839232619, 2.6409548497101296, 1.0, 0.0, 0.0], "prob": [10.86185879592172, 5.320267215145513, 0.837469, 0.0, 0.0], "rates": [0.0475346493640543, 0.0514502540356293, 0.062379140415445074, 0.05606716846782445, 0.055389859394972304, 0.05709065996802923], "joint": [0.05026454448302916, 0.07211855466174337, 0.10268774625717234, 0.1031508204861026, 0.15016593335635878], "zero": 4, "reached": [97, 8, 1, 0, 0], "statkept": [57, 3, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.046883, 0.592638, 0.128753, 0.336661, 0.249544], "most": 4, "first": 72}, {"slot": 0, "conf": [0.259844, 0.876343, 0.839087, 0.301834, 0.789169], "most": 2, "first": null}, {"slot": 5, "conf": [0.026317, 0.246387, 0.246214, 0.116592, 0.289239], "most": 5, "first": 72}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 87}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 85}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 38}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.20938, 0.041794, 0.827602, 0.983376, 0.691383], "most": 2, "first": null}, {"slot": 0, "conf": [0.231778, 0.053757, 0.253618, 0.207369, 0.201463], "most": 2, "first": 86}, {"slot": 3, "conf": [0.165864, 0.073451, 0.325929, 0.159579, 0.565403], "most": 3, "first": 81}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 91}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 31}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 95}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.601257, 0.147956, 0.061523, 0.719004, 0.170208], "most": 4, "first": 68}, {"slot": 5, "conf": [0.51038, 0.526203, 0.574666, 0.606942, 0.236888], "most": 2, "first": 24}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 68}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 24}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.105103, 0.235365, 0.061236, 0.240191, 0.197307], "most": 5, "first": null}, {"slot": 3, "conf": [0.214364, 0.126129, 0.231834, 0.037161, 0.299953], "most": 1, "first": null}, {"slot": 0, "conf": [0.178792, 0.013938, 0.05621, 0.292337, 0.155128], "most": 3, "first": 31}, {"slot": 1, "conf": [0.558785, 0.501184, 0.457472, 0.202793, 0.307382], "most": 5, "first": 12}], "others": [], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 21}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 86}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": 2, "bonus": 31}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 39}, {"op": "check", "kept": [17.245068775941128, 2.6409548497101296, 1.0, 0.0, 0.0], "prob": [10.26909695044126, 5.320267215145513, 0.837469, 0.0, 0.0], "rates": [0.0475346493640543, 0.047162732865993526, 0.060366910079462974, 0.052062544470090066, 0.056529230846535695, 0.05823420936276678], "joint": [0.05026454448302916, 0.07035424312144664, 0.10258922377146198, 0.1031508204861026, 0.15016593335635878], "zero": 4, "reached": [109, 8, 1, 0, 0], "statkept": [61, 3, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.21891, 0.370955, 0.481472, 0.643613, 0.345304], "most": 3, "first": null}], "others": [4], "ks": [1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": 2, "bonus": 87}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.551456, 0.6019, 0.552894, 0.161497, 0.134707], "most": 4, "first": 46}, {"slot": 5, "conf": [0.160894, 0.16148, 0.255499, 0.061438, 0.209174], "most": 5, "first": 72}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 52}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 72}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.020646, 0.606163, 0.271364, 0.273881, 0.083072], "most": 1, "first": 64}, {"slot": 1, "conf": [0.330484, 0.830137, 0.801774, 0.41908, 0.504914], "most": 2, "first": 46}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": 3, "bonus": 24}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 46}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.262245, 0.29852, 0.188221, 0.266577, 0.018214], "most": 0, "first": null}], "others": [], "ks": [0]}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 14}, {"op": "reset", "slot": 2}, {"op": "check", "kept": [16.721600333437294, 2.6409548497101296, 1.0, 0.0, 0.0], "prob": [10.017417200081114, 5.320267215145513, 0.837469, 0.0, 0.0], "rates": [0.0475346493640543, 0.04930649345081141, 0.06626246562634595, 0.052062544470090066, 0.05882019754872273, 0.05673870299689465], "joint": [0.05192579803460855, 0.0741221467091414, 0.10258922377146198, 0.10172180041229116, 0.15016593335635878], "zero": 4, "reached": [114, 8, 1, 0, 0], "statkept": [63, 3, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.179316, 0.037132, 0.493581, 0.087134, 0.444596], "most": 2, "first": null}, {"slot": 5, "conf": [0.079344, 0.187217, 0.02302, 0.142666, 0.038586], "most": 3, "first": null}], "others": [3], "ks": [1, 1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 38}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 18}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.481581, 0.13992, 0.285758, 0.512361, 0.406766], "most": 3, "first": null}], "others": [4], "ks": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 8}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.124758, 0.100037, 0.290566, 0.255161, 0.195553], "most": 2, "first": 81}, {"slot": 2, "conf": [0.480814, 0.02409, 0.66979, 0.183266, 0.604752], "most": 0, "first": null}, {"slot": 1, "conf": [0.685164, 0.262481, 0.24525, 0.333794, 0.697333], "most": 1, "first": null}, {"slot": 4, "conf": [0.234836, 0.203235, 0.550499, 0.540084, 0.292345], "most": 1, "first": 65}], "others": [], "ks": [1, 0, 1, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": 2, "bonus": 64}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 1}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 6}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 17}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.059083, 0.247546, 0.1154, 0.234463, 0.021132], "most": 5, "first": 83}, {"slot": 4, "conf": [0.275824, 0.271565, 0.239893, 0.181597, 0.004504], "most": 1, "first": null}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 63}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 69}, {"op": "check", "kept": [16.703043056202645, 2.6409548497101296, 1.0, 0.0, 0.0], "prob": [9.765479528827, 5.320267215145513, 0.837469, 0.0, 0.0], "rates": [0.04888758521994969, 0.047162732865993526, 0.0455497859160062, 0.05320266758175255, 0.05888736529515189, 0.05359401462044718], "joint": [0.054515163938000995, 0.07254542552728432, 0.10258922377146198, 0.10402869433732256, 0.15016593335635878], "zero": 4, "reached": [122, 8, 1, 0, 0], "statkept": [67, 3, 1, 0, 0]}, {"op": "tree", "slot": 5, "conf": [0.275794, 0.570046, 0.393131, 0.468383, 0.046283], "most": 4, "first": 58, "sibs": [0.118441, 0.132026], "k": 1, "lens": [0, 0]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 45}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.322873, 0.084753, 0.212956, 0.542064, 0.280197], "most": 5, "first": null}, {"slot": 0, "conf": [0.658705, 0.546704, 0.464839, 0.686753, 0.148521], "most": 2, "first": 36}], "others": [6], "ks": [1, 2]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 15}, {"op": "record", "slot": 0, "rows": 3, "keep": 2, "tokens": null, "bonus": 36}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.113034, 0.099548, 0.235048, 0.240187, 0.253238], "most": 0, "first": null}], "others": [], "ks": [0]}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 96}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.638183, 0.553894, 0.126323, 0.061113, 0.665795], "most": 3, "first": null}, {"slot": 2, "conf": [0.20267, 0.182708, 0.030923, 0.155744, 0.248607], "most": 3, "first": 28}, {"slot": 4, "conf": [0.444566, 0.559042, 0.375047, 0.411954, 0.016828], "most": 0, "first": 66}, {"slot": 3, "conf": [0.106682, 0.186258, 0.055, 0.165238, 0.138619], "most": 2, "first": null}], "others": [], "ks": [1, 1, 0, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 18}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 28}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": 2, "bonus": 66}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 46}, {"op": "check", "kept": [18.510767098640528, 2.5617262042188256, 1.0, 0.0, 0.0], "prob": [10.161962452712164, 5.707363198691148, 0.837469, 0.0, 0.0], "rates": [0.051763741655792686, 0.047162732865993526, 0.040490747864113046, 0.055009661071725724, 0.062064611400627065, 0.05477154997166626], "joint": [0.055696343535046926, 0.07262886492136347, 0.10258922377146198, 0.10402869433732256, 0.15016593335635878], "zero": 4, "reached": [128, 9, 1, 0, 0], "statkept": [72, 3, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.127766, 0.093243, 0.107878, 0.263962, 0.063164], "most": 3, "first": 89}, {"slot": 2, "conf": [0.514751, 0.16603, 0.52411, 0.609571, 0.476926], "most": 1, "first": null}, {"slot": 0, "conf": [0.181213, 0.327319, 0.389188, 0.123655, 0.636229], "most": 1, "first": 46}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 89}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 76}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 46}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.844003, 0.181882, 0.970832, 0.024303, 0.116937], "most": 3, "first": 94}], "others": [8], "ks": [2]}, {"op": "record", "slot": 4, "rows": 3, "keep": 3, "tokens": null, "bonus": 94}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.534878, 0.14563, 0.415608, 0.119551, 0.454828], "most": 0, "first": null}], "others": [4], "ks": [0]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 62}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.399582, 0.674375, 0.234673, 0.024102, 0.008703], "most": 3, "first": 55}], "others": [], "ks": [2]}, {"op": "record", "slot": 1, "rows": 3, "keep": 3, "tokens": null, "bonus": 55}, {"op": "check", "kept": [19.69201835941069, 4.380328185549493, 1.0, 0.0, 0.0], "prob": [10.698100808315221, 6.220858573648501, 0.837469, 0.0, 0.0], "rates": [0.05298024472634644, 0.04874182514823872, 0.03836047336824137, 0.055009661071725724, 0.06558639526593167, 0.05477154997166626], "joint": [0.05761708538638658, 0.07262886492136347, 0.10084085761270906, 0.10600116057680939, 0.15016593335635878], "zero": 4, "reached": [133, 11, 1, 0, 0], "statkept": [76, 5, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.377214, 0.216322, 0.367226, 0.698197, 0.426171], "most": 5, "first": null}], "others": [7], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 36}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.110776, 0.056618, 0.422048, 0.854904, 0.383338], "most": 4, "first": 12}, {"slot": 2, "conf": [0.697807, 0.015914, 0.656539, 0.266246, 0.228571], "most": 1, "first": 80}], "others": [2], "ks": [1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": 2, "bonus": 86}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": 3, "bonus": 80}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.153958, 0.166785, 0.485907, 0.525686, 0.682236], "most": 2, "first": 5}, {"slot": 0, "conf": [0.292733, 0.24738, 0.100873, 0.136712, 0.246211], "most": 0, "first": null}, {"slot": 4, "conf": [0.245693, 0.929633, 0.737797, 0.530231, 0.490519], "most": 2, "first": 40}], "others": [4], "ks": [1, 0, 2]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 5}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 36}, {"op": "record", "slot": 4, "rows": 3, "keep": 3, "tokens": null, "bonus": 16}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.400191, 0.635003, 0.85944, 0.497251, 0.434216], "most": 0, "first": 18}], "others": [5], "ks": [0]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 18}, {"op": "check", "kept": [19.73639900993505, 5.248918339983008, 1.0, 0.0, 0.0], "prob": [10.67346914704829, 6.9638658164390455, 0.837469, 0.0, 0.0], "rates": [0.05392056422478405, 0.04874182514823872, 0.052061253783737556, 0.05359401462044718, 0.06955378088029794, 0.05477154997166626], "joint": [0.06028895635510195, 0.07685816374867066, 0.10342652062841955, 0.10600116057680939, 0.15016593335635878], "zero": 4, "reached": [138, 12, 1, 0, 0], "statkept": [79, 6, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.654445, 0.985704, 0.365612, 0.688582, 0.737942], "most": 2, "first": null}, {"slot": 1, "conf": [0.332984, 0.900395, 0.385639, 0.848934, 0.96643], "most": 5, "first": 26}, {"slot": 2, "conf": [0.230827, 0.513475, 0.665262, 0.220123, 0.127462], "most": 2, "first": 64}], "others": [5], "ks": [2, 1, 1]}, {"op": "record", "slot": 3, "rows": 3, "keep": 2, "tokens": null, "bonus": 83}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 26}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 42}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.093528, 0.665027, 0.542505, 0.39207, 0.251809], "most": 5, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 29}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.354312, 0.665316, 0.759592, 0.423103, 0.027113], "most": 0, "first": 38}, {"slot": 3, "conf": [0.120267, 0.001688, 0.283853, 0.05245, 0.03719], "most": 3, "first": 7}, {"slot": 0, "conf": [0.732511, 0.452533, 0.775854, 0.738727, 0.61435], "most": 1, "first": 93}, {"slot": 5, "conf": [0.880017, 0.427747, 0.194873, 0.92334, 0.161405], "most": 3, "first": null}], "others": [6], "ks": [0, 1, 1, 3]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 38}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 68}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 93}, {"op": "record", "slot": 5, "rows": 4, "keep": 4, "tokens": null, "bonus": 97}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.541085, 0.34903, 0.636202, 0.325523, 0.176206], "most": 3, "first": 67}], "others": [], "ks": [1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 12}, {"op": "check", "kept": [21.79153353865944, 5.938707266090012, 1.97, 0.0, 0.0], "prob": [11.646111447579225, 7.936181226687498, 1.00721793, 0.0, 0.0], "rates": [0.05392056422478405, 0.05086103493729258, 0.05466472303206998, 0.054155045896401396, 0.06434675177597035, 0.0578173138076005], "joint": [0.05859120697886377, 0.07685816374867066, 0.10386199945889181, 0.10876468323223634, 0.15016593335635878], "zero": 4, "reached": [146, 14, 2, 0, 0], "statkept": [86, 7, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.275552, 0.147667, 0.153446, 0.361426, 0.241794], "most": 2, "first": null}, {"slot": 1, "conf": [0.176299, 0.266562, 0.167032, 0.279867, 0.207495], "most": 5, "first": 48}, {"slot": 0, "conf": [0.223918, 0.207458, 0.298906, 0.129454, 0.163558], "most": 3, "first": null}], "others": [6], "ks": [1, 1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": 3, "bonus": 40}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 51}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 99}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.112406, 0.007284, 0.063866, 0.238571, 0.224598], "most": 0, "first": null}, {"slot": 1, "conf": [0.619621, 0.634463, 0.662328, 0.130873, 0.510293], "most": 4, "first": 17}, {"slot": 3, "conf": [0.87444, 0.471024, 0.556085, 0.024591, 0.823777], "most": 2, "first": 24}], "others": [], "ks": [0, 3, 1]}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 75}, {"op": "record", "slot": 1, "rows": 4, "keep": 1, "tokens": null, "bonus": 61}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 95}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.265647, 0.14245, 0.197166, 0.086219, 0.227758], "most": 3, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 52}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.02561, 0.104626, 0.281769, 0.150981, 0.025781], "most": 0, "first": null}], "others": [8], "ks": [0]}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 71}, {"op": "check", "kept": [21.034410382174695, 5.938707266090012, 1.97, 0.0, 0.0], "prob": [11.994806029190519, 7.936181226687498, 1.00721793, 0.0, 0.0], "rates": [0.05607738679377541, 0.04648902225133656, 0.05646150608501049, 0.05623793227703222, 0.06434675177597035, 0.0588167333606411], "joint": [0.06293129638470553, 0.07685816374867066, 0.10301857132319088, 0.10876468323223634, 0.15016593335635878], "zero": 4, "reached": [152, 14, 2, 0, 0], "statkept": [89, 7, 2, 0, 0]}, {"op": "tree", "slot": 3, "conf": [0.878492, 0.053243, 0.902253, 0.953568, 0.958314], "most": 1, "first": 16, "sibs": [0.026573, 0.183574, 0.448241], "k": 1, "lens": [0, 0, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 69}, {"op": "tree", "slot": 0, "conf": [0.091427, 0.248251, 0.161451, 0.204797, 0.066517], "most": 1, "first": null, "sibs": [0.399923, 0.480661], "k": 1, "lens": [1, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 96}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.248379, 0.289707, 0.15137, 0.248395, 0.273608], "most": 5, "first": 92}, {"slot": 5, "conf": [0.065096, 0.262717, 0.542865, 0.152664, 0.256199], "most": 1, "first": null}, {"slot": 3, "conf": [0.806615, 0.88549, 0.485696, 0.8241, 0.361508], "most": 1, "first": 73}, {"slot": 1, "conf": [0.341775, 0.107636, 0.458482, 0.152596, 0.691051], "most": 3, "first": null}], "others": [], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 56}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 14}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 73}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 10}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.982914, 0.754378, 0.600572, 0.022472, 0.455739], "most": 2, "first": 0}, {"slot": 3, "conf": [0.299465, 0.145516, 0.288914, 0.253494, 0.169967], "most": 0, "first": null}, {"slot": 4, "conf": [0.099755, 0.094971, 0.037043, 0.007156, 0.177675], "most": 3, "first": null}, {"slot": 5, "conf": [0.245134, 0.205444, 0.104118, 0.086382, 0.233152], "most": 3, "first": 91}], "others": [], "ks": [2, 0, 1, 1]}, {"op": "record", "slot": 2, "rows": 3, "keep": 2, "tokens": null, "bonus": 82}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 97}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 16}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 98}, {"op": "check", "kept": [20.562849887944243, 5.760546048107312, 1.97, 0.0, 0.0], "prob": [12.43674011565899, 8.452473789886874, 1.00721793, 0.0, 0.0], "rates": [0.052739278104761304, 0.047771461329002056, 0.057561024279240036, 0.053508181606768374, 0.06323070989117995, 0.0588167333606411], "joint": [0.06293129638470553, 0.07685816374867066, 0.099700642430037, 0.10967458151563271, 0.15016593335635878], "zero": 4, "reached": [161, 15, 2, 0, 0], "statkept": [94, 7, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.464328, 0.412697, 0.099291, 0.539839, 0.376634], "most": 5, "first": 77}], "others": [], "ks": [1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 77}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.699779, 0.539557, 0.262864, 0.740715, 0.642543], "most": 2, "first": 63}, {"slot": 2, "conf": [0.146992, 0.564547, 0.761327, 0.260887, 0.890317], "most": 3, "first": 39}, {"slot": 1, "conf": [0.35223, 0.631044, 0.429641, 0.207105, 0.663698], "most": 0, "first": null}], "others": [], "ks": [2, 1, 0]}, {"op": "record", "slot": 0, "rows": 3, "keep": 1, "tokens": null, "bonus": 48}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 39}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 49}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.938009, 0.791652, 0.549659, 0.207009, 0.514017], "most": 4, "first": 94}, {"slot": 2, "conf": [0.342837, 0.611772, 0.378529, 0.491138, 0.367535], "most": 4, "first": null}], "others": [], "ks": [3, 2]}, {"op": "record", "slot": 0, "rows": 4, "keep": 1, "tokens": null, "bonus": 94}, {"op": "record", "slot": 2, "rows": 3, "keep": 2, "tokens": null, "bonus": 30}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.11912, 0.290374, 0.071031, 0.119694, 0.041319], "most": 0, "first": null}], "others": [], "ks": [0]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 32}, {"op": "check", "kept": [19.54331167413915, 5.587729666664092, 1.97, 0.0, 0.0], "prob": [13.120598048053203, 8.810671576190268, 1.00721793, 0.0, 0.0], "rates": [0.04907784769098951, 0.04860215962639731, 0.05515331088389309, 0.053508181606768374, 0.061827229138107107, 0.0588167333606411], "joint": [0.06293129638470553, 0.07708709454115449, 0.09616851801749242, 0.10937687991512354, 0.15016593335635878], "zero": 4, "reached": [166, 16, 2, 0, 0], "statkept": [96, 7, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.155855, 0.226706, 0.117399, 0.172654, 0.127596], "most": 5, "first": 92}, {"slot": 2, "conf": [0.246736, 0.371266, 0.701122, 0.16574, 0.843683], "most": 3, "first": 77}, {"slot": 3, "conf": [0.226211, 0.151624, 0.516275, 0.516559, 0.338138], "most": 1, "first": 92}, {"slot": 0, "conf": [0.701733, 0.094602, 0.032958, 0.190864, 0.021979], "most": 2, "first": null}], "others": [], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 92}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": 4, "bonus": 66}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 37}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 15}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.103274, 0.150528, 0.044438, 0.101234, 0.156769], "most": 4, "first": 93}, {"slot": 2, "conf": [0.748782, 0.917824, 0.406302, 0.047281, 0.558267], "most": 0, "first": null}, {"slot": 0, "conf": [0.453671, 0.073698, 0.63828, 0.199583, 0.317033], "most": 1, "first": null}, {"slot": 4, "conf": [0.435049, 0.302018, 0.255444, 0.223367, 0.680691], "most": 1, "first": 17}], "others": [], "ks": [1, 0, 1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 74}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 97}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 41}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 70}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.722182, 0.344061, 0.768356, 0.466358, 0.549934], "most": 4, "first": 31}, {"slot": 4, "conf": [0.130714, 0.177589, 0.035293, 0.177172, 0.234074], "most": 3, "first": 38}, {"slot": 3, "conf": [0.617721, 0.323623, 0.006294, 0.395301, 0.527681], "most": 0, "first": 75}, {"slot": 1, "conf": [0.447504, 0.464804, 0.02801, 0.617798, 0.043677], "most": 1, "first": 4}], "others": [1], "ks": [1, 1, 0, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 44}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": 3, "bonus": 38}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": 1, "bonus": 84}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 4}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.526755, 0.861261, 0.658139, 0.123054, 0.08854], "most": 2, "first": 52}, {"slot": 4, "conf": [0.15502, 0.500937, 0.030021, 0.575601, 0.24076], "most": 0, "first": 23}, {"slot": 0, "conf": [0.519164, 0.645683, 0.324709, 0.618785, 0.303131], "most": 1, "first": 92}, {"slot": 3, "conf": [0.472648, 0.406456, 0.164649, 0.461327, 0.316785], "most": 2, "first": null}], "others": [], "ks": [1, 0, 1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 24}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 22}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 92}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 11}, {"op": "check", "kept": [16.45185697647055, 5.587729666664092, 1.97, 0.0, 0.0], "prob": [13.241490437186563, 8.810671576190268, 1.00721793, 0.0, 0.0], "rates": [0.047032937370531616, 0.04860215962639731, 0.06117210649819053, 0.048149590309790276, 0.05859120697886377, 0.0588167333606411], "joint": [0.06186601147614513, 0.07708709454115449, 0.09616851801749242, 0.11448209633390537, 0.15016593335635878], "zero": 4, "reached": [179, 16, 2, 0, 0], "statkept": [100, 7, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.196065, 0.116326, 0.261559, 0.06995, 0.198487], "most": 5, "first": null}, {"slot": 5, "conf": [0.2206, 0.320306, 0.13158, 0.391385, 0.324858], "most": 4, "first": 3}, {"slot": 0, "conf": [0.498904, 0.330288, 0.265637, 0.415975, 0.44678], "most": 4, "first": 71}, {"slot": 3, "conf": [0.033485, 0.003301, 0.277043, 0.14614, 0.042101], "most": 0, "first": null}], "others": [], "ks": [1, 1, 1, 0]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 22}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": 2, "bonus": 64}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 71}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 99}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.46352, 0.549068, 0.54121, 0.405007, 0.561119], "most": 4, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 52}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.096312, 0.149894, 0.100808, 0.200235, 0.075083], "most": 5, "first": null}, {"slot": 4, "conf": [0.003985, 0.248798, 0.031561, 0.097187, 0.219558], "most": 4, "first": 29}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 2}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 29}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.015314, 0.187681, 0.099015, 0.208975, 0.004542], "most": 0, "first": 25}], "others": [], "ks": [0]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 25}, {"op": "check", "kept": [14.703936290495827, 5.587729666664092, 1.97, 0.0, 0.0], "prob": [12.382323936738342, 8.810671576190268, 1.00721793, 0.0, 0.0], "rates": [0.04294311672961582, 0.047771461329002056, 0.06117210649819053, 0.04986774207536533, 0.060761251681784645, 0.05988221787215766], "joint": [0.0596565110662828, 0.07812523935428724, 0.09616851801749242, 0.11243297158117493, 0.15016593335635878], "zero": 4, "reached": [185, 16, 2, 0, 0], "statkept": [101, 7, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.14543, 0.011807, 0.496388, 0.33201, 0.837872], "most": 4, "first": 9}, {"slot": 1, "conf": [0.000974, 0.576855, 0.370399, 0.545796, 0.362919], "most": 2, "first": 83}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": 3, "bonus": 9}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 62}, {"op": "reset", "slot": 5}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.997689, 0.192327, 0.504822, 0.431338, 0.079899], "most": 0, "first": null}, {"slot": 0, "conf": [0.052797, 0.667495, 0.601435, 0.012985, 0.177886], "most": 1, "first": null}], "others": [], "ks": [0, 1]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 1}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 78}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.017515, 0.207135, 0.203053, 0.181289, 0.293938], "most": 3, "first": null}, {"slot": 2, "conf": [0.62493, 0.14897, 0.184462, 0.210809, 0.697145], "most": 4, "first": null}], "others": [2], "ks": [1, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 50}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 81}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.198638, 0.446531, 0.101866, 0.231651, 0.029483], "most": 5, "first": 88}], "others": [], "ks": [0]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 56}, {"op": "check", "kept": [14.045933102242591, 5.587729666664092, 1.97, 0.0, 0.0], "prob": [11.30936351970077, 8.810671576190268, 1.00721793, 0.0, 0.0], "rates": [0.04135521023955006, 0.04984848138678475, 0.05924359599513291, 0.05077363567929606, 0.06028895635510195, 0.06626246562634595], "joint": [0.0585108537633731, 0.0796571067926066, 0.09616851801749242, 0.11243297158117493, 0.15016593335635878], "zero": 5, "reached": [190, 16, 2, 0, 0], "statkept": [103, 7, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.116038, 0.113946, 0.293373, 0.640002, 0.275709], "most": 5, "first": 65}], "others": [2], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": 2, "bonus": 16}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.684619, 0.499293, 0.255017, 0.092008, 0.121148], "most": 1, "first": 8}, {"slot": 4, "conf": [0.178866, 0.032688, 0.271373, 0.222672, 0.288124], "most": 2, "first": null}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 89}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 29}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.024215, 0.158016, 0.175213, 0.123026, 0.22088], "most": 4, "first": 27}, {"slot": 5, "conf": [0.654043, 0.395293, 0.492299, 0.553631, 0.788871], "most": 5, "first": null}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 27}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 29}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.067748, 0.156726, 0.206604, 0.095834, 0.149824], "most": 5, "first": 74}, {"slot": 5, "conf": [0.243502, 0.133296, 0.385124, 0.238936, 0.152261], "most": 2, "first": 74}], "others": [2], "ks": [1, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 63}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 65}, {"op": "check", "kept": [15.06451799048441, 5.587729666664092, 1.97, 0.0, 0.0], "prob": [10.927389523908877, 8.810671576190268, 1.00721793, 0.0, 0.0], "rates": [0.04135521023955006, 0.05192550144456745, 0.05924359599513291, 0.05077363567929606, 0.05664167171358485, 0.044174977084230634], "joint": [0.05803934174457332, 0.077628052038193, 0.09616851801749242, 0.11243297158117493, 0.15016593335635878], "zero": 5, "reached": [197, 16, 2, 0, 0], "statkept": [107, 7, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.125601, 0.278042, 0.871409, 0.617658, 0.797294], "most": 5, "first": null}, {"slot": 1, "conf": [0.015523, 0.125269, 0.278188, 0.047432, 0.249963], "most": 0, "first": null}, {"slot": 0, "conf": [0.86125, 0.739593, 0.688261, 0.14419, 0.805106], "most": 5, "first": null}], "others": [], "ks": [1, 0, 3]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 39}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 40}, {"op": "record", "slot": 0, "rows": 4, "keep": 3, "tokens": 5, "bonus": 14}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.202033, 0.168347, 0.082809, 0.20667, 0.081242], "most": 2, "first": 27}, {"slot": 2, "conf": [0.93701, 0.009096, 0.061524, 0.293635, 0.66128], "most": 2, "first": 29}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 27}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 83}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.487671, 0.625017, 0.250424, 0.117501, 0.487282], "most": 1, "first": null}, {"slot": 1, "conf": [0.213288, 0.750245, 0.000872, 0.556063, 0.467207], "most": 2, "first": null}, {"slot": 5, "conf": [0.412889, 0.221451, 0.560521, 0.691001, 0.624392], "most": 0, "first": null}], "others": [], "ks": [1, 1, 0]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 66}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 1}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 80}, {"op": "tree", "slot": 5, "conf": [0.067632, 0.031282, 0.111082, 0.115144, 0.245154], "most": 2, "first": null, "sibs": [0.288809], "k": 1, "lens": [1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 88}, {"op": "check", "kept": [17.6871511318371, 6.42009777666417, 1.9108999999999998, 0.0, 0.0], "prob": [11.440763163897355, 9.285944428904559, 1.6652623920999998, 0.0, 0.0], "rates": [0.04663422546634226, 0.05282843437651882, 0.05969525571955169, 0.05077363567929606, 0.05664167171358485, 0.04477453224605844], "joint": [0.05803934174457332, 0.07713715288281203, 0.10301857132319088, 0.11243297158117493, 0.15016593335635878], "zero": 5, "reached": [204, 17, 3, 0, 0], "statkept": [113, 8, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.008174, 0.56479, 0.778311, 0.386415, 0.096673], "most": 3, "first": null}, {"slot": 0, "conf": [0.600887, 0.03624, 0.956802, 0.900365, 0.48976], "most": 5, "first": 1}, {"slot": 1, "conf": [0.251843, 0.068396, 0.408669, 0.16761, 0.243201], "most": 1, "first": null}, {"slot": 4, "conf": [0.39652, 0.228535, 0.514907, 0.310622, 0.109606], "most": 3, "first": 31}], "others": [3], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 37}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 1}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 50}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 98}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.239052, 0.197142, 0.246722, 0.031842, 0.299438], "most": 5, "first": 59}], "others": [2], "ks": [1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 1}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.987274, 0.226748, 0.480834, 0.842264, 0.621095], "most": 0, "first": null}, {"slot": 0, "conf": [0.165373, 0.2913, 0.285345, 0.044205, 0.202983], "most": 4, "first": 52}, {"slot": 1, "conf": [0.174819, 0.01066, 0.110058, 0.005817, 0.110142], "most": 3, "first": 73}, {"slot": 4, "conf": [0.346554, 0.114824, 0.451535, 0.279167, 0.043144], "most": 5, "first": null}], "others": [4], "ks": [0, 1, 1, 1]}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 80}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 52}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 80}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 60}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.122365, 0.249186, 0.469767, 0.15265, 0.682424], "most": 5, "first": 68}, {"slot": 4, "conf": [0.468255, 0.390005, 0.261292, 0.44436, 0.458905], "most": 2, "first": 49}, {"slot": 1, "conf": [0.663366, 0.115165, 0.510609, 0.257515, 0.445353], "most": 2, "first": null}, {"slot": 0, "conf": [0.137902, 0.775975, 0.206178, 0.072109, 0.264336], "most": 4, "first": 44}], "others": [], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 85}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 49}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 61}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 44}, {"op": "check", "kept": [17.30076888536113, 6.42009777666417, 1.9108999999999998, 0.0, 0.0], "prob": [11.010723521265835, 9.285944428904559, 1.6652623920999998, 0.0, 0.0], "rates": [0.04512830172390112, 0.04702603104573067, 0.06285644844304578, 0.052981185056656756, 0.054130011627126495, 0.04477453224605844], "joint": [0.05918443843831995, 0.07713715288281203, 0.10301857132319088, 0.11076487668365437, 0.15016593335635878], "zero": 5, "reached": [216, 17, 3, 0, 0], "statkept": [119, 8, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.670939, 0.732999, 0.919388, 0.267303, 0.094113], "most": 4, "first": 17}, {"slot": 4, "conf": [0.058647, 0.105656, 0.369792, 0.015603, 0.178486], "most": 5, "first": null}], "others": [], "ks": [3, 1]}, {"op": "record", "slot": 5, "rows": 4, "keep": 1, "tokens": null, "bonus": 17}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 51}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.018839, 0.147232, 0.064252, 0.366313, 0.469364], "most": 3, "first": null}, {"slot": 0, "conf": [0.752617, 0.840792, 0.333592, 0.271929, 0.645239], "most": 2, "first": null}, {"slot": 1, "conf": [0.36084, 0.263414, 0.384138, 0.683917, 0.539963], "most": 5, "first": 1}], "others": [], "ks": [1, 2, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 23}, {"op": "record", "slot": 0, "rows": 3, "keep": 2, "tokens": null, "bonus": 47}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": 3, "bonus": 1}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.167439, 0.262006, 0.013723, 0.01585, 0.102505], "most": 2, "first": 90}], "others": [6], "ks": [1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 90}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.360894, 0.144377, 0.170479, 0.005247, 0.203079], "most": 4, "first": 57}], "others": [], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 30}, {"op": "check", "kept": [17.605424296594943, 6.227494843364244, 1.9108999999999998, 0.0, 0.0], "prob": [11.072106724719745, 9.848158096037421, 1.6652623920999998, 0.0, 0.0], "rates": [0.04557370357674314, 0.049163577911445704, 0.06285644844304578, 0.0536208603020195, 0.05530973451327434, 0.04424351631379111], "joint": [0.05767562783248335, 0.07763766501736397, 0.10471190978122781, 0.11253543459496809, 0.15016593335635878], "zero": 5, "reached": [223, 18, 3, 0, 0], "statkept": [123, 8, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.287702, 0.057561, 0.164607, 0.019872, 0.234375], "most": 2, "first": 67}], "others": [3], "ks": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 30}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.139003, 0.257929, 0.136308, 0.208287, 0.006412], "most": 5, "first": 36}], "others": [5], "ks": [1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 36}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.282541, 0.193721, 0.116118, 0.201841, 0.212179], "most": 3, "first": 1}, {"slot": 4, "conf": [0.240163, 0.241679, 0.499146, 0.371873, 0.238504], "most": 2, "first": 65}, {"slot": 1, "conf": [0.233139, 0.257325, 0.240789, 0.109549, 0.285125], "most": 4, "first": 92}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 1}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": 1, "bonus": 65}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 92}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.003165, 0.250842, 0.101691, 0.256775, 0.241999], "most": 1, "first": null}, {"slot": 2, "conf": [0.407557, 0.431798, 0.452616, 0.279122, 0.698083], "most": 2, "first": null}, {"slot": 4, "conf": [0.167313, 0.574332, 0.372133, 0.219058, 0.049248], "most": 4, "first": 42}, {"slot": 1, "conf": [0.040837, 0.036669, 0.029187, 0.136551, 0.046668], "most": 2, "first": null}], "others": [8], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 74}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 47}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 22}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 80}, {"op": "check", "kept": [20.517754370210998, 6.227494843364244, 1.9108999999999998, 0.0, 0.0], "prob": [9.992626753074498, 9.848158096037421, 1.6652623920999998, 0.0, 0.0], "rates": [0.04557370357674314, 0.054556445189321776, 0.06285644844304578, 0.0536208603020195, 0.05530973451327434, 0.046627236762348145], "joint": [0.05511196546904691, 0.07763766501736397, 0.10545146408059505, 0.11253543459496809, 0.15016593335635878], "zero": 5, "reached": [232, 18, 3, 0, 0], "statkept": [131, 8, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.119032, 0.102158, 0.227445, 0.172047, 0.145857], "most": 4, "first": null}, {"slot": 2, "conf": [0.203691, 0.169684, 0.167385, 0.116535, 0.13399], "most": 4, "first": 36}, {"slot": 5, "conf": [0.093851, 0.216747, 0.28495, 0.173079, 0.286613], "most": 3, "first": 62}, {"slot": 1, "conf": [0.088819, 0.59687, 0.29068, 0.431327, 0.609177], "most": 2, "first": 14}], "others": [], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 62}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 24}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": 2, "bonus": 62}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 61}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.405754, 0.134559, 0.958099, 0.474014, 0.30199], "most": 5, "first": 66}, {"slot": 3, "conf": [0.672809, 0.34632, 0.245229, 0.631078, 0.567382], "most": 0, "first": null}, {"slot": 2, "conf": [0.165792, 0.129717, 0.251496, 0.506319, 0.033388], "most": 4, "first": null}], "others": [4], "ks": [1, 0, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 26}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": 1, "bonus": 42}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 59}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.646123, 0.144296, 0.080427, 0.198313, 0.213699], "most": 3, "first": 88}], "others": [2], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": 4, "bonus": 88}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.184563, 0.004883, 0.032361, 0.106985, 0.018099], "most": 1, "first": 58}, {"slot": 2, "conf": [0.088689, 0.390783, 0.276806, 0.313261, 0.129218], "most": 3, "first": 60}, {"slot": 4, "conf": [0.137848, 0.471792, 0.095332, 0.261988, 0.676312], "most": 1, "first": null}, {"slot": 5, "conf": [0.057806, 0.182715, 0.181067, 0.170193, 0.095692], "most": 5, "first": null}], "others": [], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 79}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 52}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 97}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 78}, {"op": "check", "kept": [19.280554144009646, 6.227494843364244, 1.9108999999999998, 0.0, 0.0], "prob": [9.03146126431929, 9.848158096037421, 1.6652623920999998, 0.0, 0.0], "rates": [0.05309503193870383, 0.05359401462044718, 0.05866601854684272, 0.05535642618912524, 0.05530973451327434, 0.05012191418538622], "joint": [0.060625613022381675, 0.07763766501736397, 0.09966555268333731, 0.11285899776740319, 0.15016593335635878], "zero": 5, "reached": [243, 18, 3, 0, 0], "statkept": [136, 8, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.29998, 0.476712, 0.328529, 0.6053, 0.286778], "most": 3, "first": 27}, {"slot": 5, "conf": [0.344023, 0.95903, 0.132343, 0.879996, 0.389971], "most": 0, "first": null}, {"slot": 3, "conf": [0.081908, 0.292424, 0.224347, 0.297708, 0.178198], "most": 2, "first": 1}], "others": [], "ks": [1, 0, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": 3, "bonus": 49}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 87}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 89}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.34851, 0.432563, 0.54747, 0.213052, 0.344177], "most": 2, "first": 96}], "others": [], "ks": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 4}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.060318, 0.686433, 0.506598, 0.469922, 0.660179], "most": 4, "first": null}, {"slot": 3, "conf": [0.29032, 0.012852, 0.133247, 0.018037, 0.100372], "most": 2, "first": 78}, {"slot": 5, "conf": [0.527577, 0.064752, 0.331948, 0.259691, 0.57273], "most": 2, "first": null}], "others": [8], "ks": [2, 1, 1]}, {"op": "record", "slot": 2, "rows": 3, "keep": 1, "tokens": null, "bonus": 11}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 78}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": 3, "bonus": 30}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.083131, 0.103101, 0.1674, 0.071659, 0.066455], "most": 0, "first": null}], "others": [], "ks": [0]}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 59}, {"op": "check", "kept": [18.942834841477847, 6.227494843364244, 1.9108999999999998, 0.0, 0.0], "prob": [9.037086266719605, 9.848158096037421, 1.6652623920999998, 0.0, 0.0], "rates": [0.05623427197705641, 0.05359401462044718, 0.05657080359874119, 0.05658886829054083, 0.05530973451327434, 0.05323473614928956], "joint": [0.06279081348746673, 0.07763766501736397, 0.10033298007763265, 0.11509382940636168, 0.15016593335635878], "zero": 5, "reached": [249, 18, 3, 0, 0], "statkept": [139, 8, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.336954, 0.148605, 0.003546, 0.268218, 0.355473], "most": 2, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 27}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.027238, 0.063305, 0.274817, 0.094904, 0.166768], "most": 3, "first": null}, {"slot": 4, "conf": [0.296258, 0.552209, 0.582762, 0.639402, 0.403324], "most": 4, "first": 40}], "others": [7], "ks": [1, 2]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 82}, {"op": "record", "slot": 4, "rows": 3, "keep": 3, "tokens": 5, "bonus": 40}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.52444, 0.133569, 0.159854, 0.379764, 0.413107], "most": 2, "first": null}, {"slot": 5, "conf": [0.230353, 0.670745, 0.368719, 0.515183, 0.009955], "most": 0, "first": null}], "others": [], "ks": [1, 0]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 36}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 16}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.004185, 0.457177, 0.448133, 0.137234, 0.583219], "most": 0, "first": 23}, {"slot": 0, "conf": [0.527734, 0.143818, 0.075765, 0.079056, 0.113787], "most": 3, "first": 0}], "others": [], "ks": [0, 1]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 0}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 0}, {"op": "check", "kept": [18.20775682159249, 7.040669998063317, 1.9108999999999998, 0.0, 0.0], "prob": [9.39880576329305, 10.104922353156297, 1.6652623920999998, 0.0, 0.0], "rates": [0.05824263883337986, 0.05359401462044718, 0.05657080359874119, 0.05658886829054083, 0.05888736529515189, 0.052847078935154335], "joint": [0.05952092270657067, 0.07757824586634761, 0.10033298007763265, 0.11509382940636168, 0.15016593335635878], "zero": 5, "reached": [254, 19, 3, 0, 0], "statkept": [141, 9, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.628433, 0.334207, 0.600124, 0.138404, 0.096745], "most": 3, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 95}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.100439, 0.544601, 0.633829, 0.02171, 0.590029], "most": 1, "first": null}, {"slot": 1, "conf": [0.401792, 0.368816, 0.174365, 0.382633, 0.019851], "most": 3, "first": null}, {"slot": 3, "conf": [0.101288, 0.006604, 0.115308, 0.276527, 0.289244], "most": 2, "first": null}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 38}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 22}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 70}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.14314, 0.690671, 0.012061, 0.198125, 0.47592], "most": 4, "first": null}], "others": [1], "ks": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 84}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.04095, 0.304125, 0.318719, 0.19304, 0.620055], "most": 4, "first": 44}], "others": [4], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 44}, {"op": "check", "kept": [16.13655170494157, 7.040669998063317, 1.9108999999999998, 0.0, 0.0], "prob": [9.099319194320769, 10.104922353156297, 1.6652623920999998, 0.0, 0.0], "rates": [0.05824263883337986, 0.05266495189583294, 0.05657080359874119, 0.05673870299689465, 0.05788556437391593, 0.052847078935154335], "joint": [0.058460412557296615, 0.07956643797570971, 0.09907881782666225, 0.11509382940636168, 0.15016593335635878], "zero": 5, "reached": [260, 19, 3, 0, 0], "statkept": [142, 9, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.009159, 0.216222, 0.600322, 0.306012, 0.663248], "most": 3, "first": null}, {"slot": 4, "conf": [0.042886, 0.452893, 0.081268, 0.684887, 0.084501], "most": 4, "first": 9}, {"slot": 0, "conf": [0.384762, 0.365621, 0.325945, 0.61332, 0.645391], "most": 5, "first": 88}, {"slot": 1, "conf": [0.126165, 0.230265, 0.222961, 0.02745, 0.067113], "most": 5, "first": 94}], "others": [], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 2}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 9}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 88}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 94}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.722326, 0.294036, 0.606498, 0.605223, 0.440947], "most": 1, "first": 61}, {"slot": 2, "conf": [0.150894, 0.176983, 0.400248, 0.596852, 0.438516], "most": 0, "first": 95}, {"slot": 4, "conf": [0.060363, 0.245049, 0.284994, 0.260693, 0.119046], "most": 0, "first": 22}], "others": [4], "ks": [1, 0, 0]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 59}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 57}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 4}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.989971, 0.934459, 0.135907, 0.440096, 0.175296], "most": 4, "first": 40}, {"slot": 1, "conf": [0.105156, 0.287527, 0.1265, 0.175743, 0.091606], "most": 4, "first": 88}, {"slot": 0, "conf": [0.997027, 0.920125, 0.828198, 0.870201, 0.004044], "most": 1, "first": 90}], "others": [], "ks": [2, 1, 1]}, {"op": "record", "slot": 3, "rows": 3, "keep": 1, "tokens": null, "bonus": 91}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 94}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 90}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.576634, 0.075966, 0.467961, 0.529223, 0.375083], "most": 4, "first": 18}], "others": [], "ks": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 18}, {"op": "reset", "slot": 0}, {"op": "check", "kept": [16.81450662631086, 7.040669998063317, 1.9108999999999998, 0.0, 0.0], "prob": [10.573933961916648, 10.104922353156297, 1.6652623920999998, 0.0, 0.0], "rates": [0.06626246562634595, 0.05477154997166626, 0.05607738679377541, 0.0550994545154003, 0.05257800472781419, 0.05371711706678756], "joint": [0.058460412557296615, 0.07956643797570971, 0.09732200185038196, 0.11348792911232412, 0.15016593335635878], "zero": 5, "reached": [269, 19, 3, 0, 0], "statkept": [147, 9, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.175008, 0.099385, 0.009618, 0.107335, 0.141627], "most": 1, "first": 35}], "others": [4], "ks": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 35}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.100513, 0.055837, 0.053589, 0.013541, 0.020767], "most": 1, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": 3, "bonus": 29}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.200516, 0.06678, 0.274825, 0.299341, 0.176374], "most": 4, "first": null}], "others": [7], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 55}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.272118, 0.214748, 0.087766, 0.225469, 0.225531], "most": 4, "first": null}, {"slot": 3, "conf": [0.154479, 0.224512, 0.23812, 0.096944, 0.219153], "most": 5, "first": null}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 45}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 40}, {"op": "check", "kept": [16.294481775371246, 7.040669998063317, 1.9108999999999998, 0.0, 0.0], "prob": [9.933964668344114, 10.104922353156297, 1.6652623920999998, 0.0, 0.0], "rates": [0.03313123281317298, 0.05477154997166626, 0.05607738679377541, 0.05298024472634644, 0.054155045896401396, 0.05801448643213057], "joint": [0.06109134419916621, 0.07956643797570971, 0.09732200185038196, 0.11348792911232412, 0.15016593335635878], "zero": 5, "reached": [274, 19, 3, 0, 0], "statkept": [149, 9, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.343809, 0.02992, 0.419743, 0.476403, 0.100359], "most": 2, "first": null}, {"slot": 0, "conf": [0.054881, 0.172771, 0.170564, 0.119563, 0.008211], "most": 5, "first": null}, {"slot": 1, "conf": [0.818242, 0.947954, 0.979212, 0.367949, 0.196284], "most": 1, "first": 18}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 57}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 37}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 18}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.204886, 0.767205, 0.737251, 0.437665, 0.26683], "most": 5, "first": 3}, {"slot": 3, "conf": [0.277914, 0.067346, 0.167451, 0.229276, 0.245026], "most": 0, "first": 64}], "others": [], "ks": [1, 0]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 3}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 14}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.421263, 0.64917, 0.498026, 0.040442, 0.512656], "most": 5, "first": 39}, {"slot": 4, "conf": [0.248141, 0.179315, 0.237882, 0.32208, 0.45071], "most": 0, "first": 10}, {"slot": 0, "conf": [0.028195, 0.189217, 0.164681, 0.083434, 0.288966], "most": 5, "first": null}], "others": [], "ks": [1, 0, 1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 85}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 12}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 82}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.054429, 0.51105, 0.385154, 0.180499, 0.662762], "most": 3, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": 4, "bonus": 93}, {"op": "check", "kept": [16.742660579727737, 7.040669998063317, 1.9108999999999998, 0.0, 0.0], "prob": [9.749506673209007, 10.104922353156297, 1.6652623920999998, 0.0, 0.0], "rates": [0.044174977084230634, 0.05266495189583294, 0.05607738679377541, 0.051763741655792686, 0.05933787409350801, 0.05801448643213057], "joint": [0.06109134419916621, 0.07911934087502948, 0.09556538392437337, 0.11348792911232412, 0.15016593335635878], "zero": 5, "reached": [281, 19, 3, 0, 0], "statkept": [153, 9, 2, 0, 0]}, {"op": "tree", "slot": 3, "conf": [0.211682, 0.25314, 0.264592, 0.492361, 0.545298], "most": 0, "first": 88, "sibs": [0.49296, 0.287559], "k": 0, "lens": [0, 0]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 88}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.804272, 0.188276, 0.600991, 0.20185, 0.091444], "most": 1, "first": 6}, {"slot": 3, "conf": [0.164422, 0.179027, 0.161254, 0.286132, 0.104547], "most": 0, "first": 44}], "others": [1], "ks": [1, 0]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 6}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": 1, "bonus": 46}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.251152, 0.378826, 0.526485, 0.676802, 0.295368], "most": 0, "first": null}], "others": [1], "ks": [0]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 70}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.075119, 0.250605, 0.001361, 0.273101, 0.22281], "most": 0, "first": 59}, {"slot": 1, "conf": [0.219112, 0.487651, 0.038351, 0.016049, 0.483605], "most": 5, "first": null}], "others": [], "ks": [0, 1]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": 1, "bonus": 59}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 31}, {"op": "check", "kept": [15.753169339465826, 7.040669998063317, 1.9108999999999998, 0.0, 0.0], "prob": [10.172566668822354, 10.104922353156297, 1.6652623920999998, 0.0, 0.0], "rates": [0.044174977084230634, 0.05176755127058278, 0.04960691908680133, 0.04919642118397896, 0.0592864545821842, 0.05801448643213057], "joint": [0.060025296374900845, 0.07996312288920875, 0.09556538392437337, 0.11348792911232412, 0.15016593335635878], "zero": 5, "reached": [283, 19, 3, 0, 0], "statkept": [153, 9, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.572071, 0.403492, 0.87024, 0.829156, 0.956967], "most": 4, "first": 17}, {"slot": 4, "conf": [0.399045, 0.431597, 0.339505, 0.674787, 0.354613], "most": 1, "first": 27}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 17}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 27}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.178528, 0.01132, 0.242946, 0.105031, 0.295768], "most": 0, "first": 71}], "others": [6], "ks": [0]}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": 3, "bonus": 71}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.237746, 0.075771, 0.682825, 0.75273, 0.331624], "most": 0, "first": 73}, {"slot": 5, "conf": [0.688575, 0.678867, 0.163261, 0.370456, 0.454939], "most": 4, "first": 60}, {"slot": 3, "conf": [0.153328, 0.170078, 0.245735, 0.263223, 0.13223], "most": 0, "first": null}, {"slot": 4, "conf": [0.438816, 0.252884, 0.548308, 0.611175, 0.000886], "most": 0, "first": null}], "others": [], "ks": [0, 2, 0, 0]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 73}, {"op": "record", "slot": 5, "rows": 3, "keep": 2, "tokens": null, "bonus": 60}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 30}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 80}, {"op": "reset", "slot": 5}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.58546, 0.215437, 0.642956, 0.919615, 0.541289], "most": 5, "first": 43}], "others": [], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 13}, {"op": "check", "kept": [14.916167550941545, 6.829449898121417, 1.9108999999999998, 0.0, 0.0], "prob": [11.15665307743708, 10.480641682561608, 1.6652623920999998, 0.0, 0.0], "rates": [0.042050376350868335, 0.05176755127058278, 0.04960691908680133, 0.04784056789032208, 0.0581412250356115, 0.06626246562634595], "joint": [0.06328547641961327, 0.07417777097376474, 0.09556538392437337, 0.111568168708922, 0.15016593335635878], "zero": 5, "reached": [287, 20, 3, 0, 0], "statkept": [154, 9, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.23917, 0.050451, 0.201425, 0.214088, 0.111798], "most": 0, "first": 42}, {"slot": 0, "conf": [0.059755, 0.840809, 0.536138, 0.482891, 0.197451], "most": 4, "first": null}], "others": [7], "ks": [0, 1]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 71}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 16}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.057437, 0.285209, 0.22246, 0.132205, 0.010016], "most": 3, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 17}, {"op": "tree", "slot": 1, "conf": [0.874052, 0.07545, 0.448942, 0.255521, 0.533261], "most": 0, "first": null, "sibs": [0.443881, 0.139495, 0.409195], "k": 0, "lens": [0, 0, 0]}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 41}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.497361, 0.550591, 0.419044, 0.668408, 0.441856], "most": 1, "first": null}, {"slot": 2, "conf": [0.204422, 0.189505, 0.250663, 0.013312, 0.179995], "most": 3, "first": null}, {"slot": 5, "conf": [0.021045, 0.297769, 0.112752, 0.283056, 0.052147], "most": 5, "first": 87}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": 2, "bonus": 36}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 84}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 39}, {"op": "check", "kept": [13.80902060903574, 6.829449898121417, 1.9108999999999998, 0.0, 0.0], "prob": [10.373220786388387, 10.480641682561608, 1.6652623920999998, 0.0, 0.0], "rates": [0.04049309014340339, 0.05055835381999962, 0.04874182514823872, 0.04919642118397896, 0.05923117929277972, 0.04969684921975947], "joint": [0.06328547641961327, 0.07308176260501531, 0.09556538392437337, 0.111568168708922, 0.15016593335635878], "zero": 5, "reached": [292, 20, 3, 0, 0], "statkept": [155, 9, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.454142, 0.84991, 0.456596, 0.968771, 0.477884], "most": 0, "first": 86}, {"slot": 4, "conf": [0.613714, 0.29326, 0.416429, 0.208378, 0.230913], "most": 2, "first": 5}, {"slot": 2, "conf": [0.079396, 0.250983, 0.492089, 0.787432, 0.262074], "most": 1, "first": null}], "others": [], "ks": [0, 1, 1]}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 17}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 29}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": 3, "bonus": 16}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.263784, 0.286979, 0.253379, 0.023307, 0.240707], "most": 5, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 4}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.248564, 0.119785, 0.107288, 0.011219, 0.044513], "most": 4, "first": null}, {"slot": 5, "conf": [0.119544, 0.754968, 0.89401, 0.121331, 0.7559], "most": 2, "first": null}], "others": [5], "ks": [1, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 1}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 93}, {"op": "tree", "slot": 1, "conf": [0.194162, 0.297103, 0.029832, 0.289164, 0.066465], "most": 5, "first": null, "sibs": [0.471803], "k": 1, "lens": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 69}, {"op": "check", "kept": [13.24655441851438, 6.829449898121417, 1.9108999999999998, 0.0, 0.0], "prob": [10.022650400032319, 10.480641682561608, 1.6652623920999998, 0.0, 0.0], "rates": [0.04049309014340339, 0.050817957372203425, 0.05086103493729258, 0.04919642118397896, 0.05923117929277972, 0.044174977084230634], "joint": [0.06110321861204039, 0.07308176260501531, 0.09763391905120386, 0.111568168708922, 0.15016593335635878], "zero": 5, "reached": [298, 20, 3, 0, 0], "statkept": [157, 9, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.045749, 0.177648, 0.172768, 0.130716, 0.132196], "most": 5, "first": null}, {"slot": 0, "conf": [0.145876, 0.215721, 0.152673, 0.12357, 0.03298], "most": 3, "first": null}], "others": [2], "ks": [1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": 3, "bonus": 70}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 24}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.555614, 0.375891, 0.510991, 0.641826, 0.695285], "most": 0, "first": 63}, {"slot": 1, "conf": [0.698746, 0.88898, 0.081516, 0.40284, 0.112974], "most": 5, "first": 24}, {"slot": 4, "conf": [0.136956, 0.160161, 0.147311, 0.274658, 0.006969], "most": 4, "first": null}], "others": [], "ks": [0, 2, 1]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 63}, {"op": "record", "slot": 1, "rows": 3, "keep": 2, "tokens": null, "bonus": 66}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 24}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.619951, 0.128413, 0.180258, 0.049744, 0.506577], "most": 4, "first": 51}, {"slot": 4, "conf": [0.217505, 0.144586, 0.048493, 0.047208, 0.104535], "most": 1, "first": null}, {"slot": 3, "conf": [0.928409, 0.880499, 0.864416, 0.4936, 0.168443], "most": 1, "first": null}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 51}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 52}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 11}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.764616, 0.200593, 0.137654, 0.83073, 0.470003], "most": 0, "first": 14}, {"slot": 0, "conf": [0.123423, 0.073291, 0.221634, 0.150632, 0.151241], "most": 2, "first": null}], "others": [7], "ks": [0, 1]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 14}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 65}, {"op": "reset", "slot": 2}, {"op": "check", "kept": [15.757481741421843, 6.624566401177774, 1.9108999999999998, 0.0, 0.0], "prob": [10.529386577478942, 11.055202432084759, 1.6652623920999998, 0.0, 0.0], "rates": [0.03918664205745546, 0.048149590309790276, 0.06626246562634595, 0.04919642118397896, 0.05877858108505261, 0.044174977084230634], "joint": [0.06110321861204039, 0.0762592305443638, 0.09745076492439231, 0.111568168708922, 0.15016593335635878], "zero": 5, "reached": [306, 21, 3, 0, 0], "statkept": [163, 9, 2, 0, 0]}, {"op": "tree", "slot": 1, "conf": [0.029076, 0.452966, 0.395032, 0.654481, 0.604373], "most": 4, "first": 1, "sibs": [0.297086, 0.317519], "k": 0, "lens": [0, 0]}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 1}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.114739, 0.037824, 0.174792, 0.276795, 0.131345], "most": 1, "first": 6}, {"slot": 0, "conf": [0.032713, 0.447935, 0.691826, 0.541055, 0.499076], "most": 2, "first": null}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 37}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 18}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.049281, 0.005691, 0.042225, 0.172053, 0.192642], "most": 2, "first": null}, {"slot": 5, "conf": [0.101701, 0.346378, 0.202509, 0.524897, 0.2271], "most": 4, "first": null}, {"slot": 3, "conf": [0.451714, 0.497736, 0.699869, 0.529484, 0.399579], "most": 2, "first": null}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 80}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 50}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 35}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.227228, 0.240085, 0.070268, 0.128379, 0.167837], "most": 4, "first": null}, {"slot": 0, "conf": [0.304165, 0.519606, 0.163625, 0.177541, 0.239048], "most": 5, "first": null}, {"slot": 2, "conf": [0.293788, 0.28104, 0.227033, 0.068126, 0.159397], "most": 0, "first": null}, {"slot": 4, "conf": [0.236319, 0.022044, 0.657308, 0.670926, 0.116375], "most": 4, "first": null}], "others": [], "ks": [1, 1, 0, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 90}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 45}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 5}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 51}, {"op": "check", "kept": [17.334908200377804, 6.624566401177774, 1.9108999999999998, 0.0, 0.0], "prob": [9.43729046583591, 11.055202432084759, 1.6652623920999998, 0.0, 0.0], "rates": [0.041197255628971176, 0.04439192574287774, 0.0455497859160062, 0.04919642118397896, 0.06112972432845471, 0.053009972501076766], "joint": [0.06110321861204039, 0.0762592305443638, 0.0931766240556161, 0.111568168708922, 0.15016593335635878], "zero": 6, "reached": [314, 21, 3, 0, 0], "statkept": [168, 9, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.245282, 0.12238, 0.317335, 0.409462, 0.596115], "most": 5, "first": null}, {"slot": 0, "conf": [0.65784, 0.349707, 0.289258, 0.127518, 0.384012], "most": 4, "first": null}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 19}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": 1, "bonus": 45}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.03193, 0.470571, 0.130918, 0.489852, 0.637954], "most": 2, "first": 98}], "others": [], "ks": [0]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 92}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.177247, 0.264739, 0.094939, 0.092525, 0.19457], "most": 1, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 46}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.65508, 0.310129, 0.086563, 0.142087, 0.545555], "most": 1, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 7}, {"op": "check", "kept": [15.771368314050372, 6.624566401177774, 1.9108999999999998, 0.0, 0.0], "prob": [9.778714157769915, 11.055202432084759, 1.6652623920999998, 0.0, 0.0], "rates": [0.03985204926709591, 0.04439192574287774, 0.03836047336824137, 0.05050327612556432, 0.06112972432845471, 0.053009972501076766], "joint": [0.05290748685028504, 0.07467049657468956, 0.0931766240556161, 0.10953038564858361, 0.15016593335635878], "zero": 7, "reached": [318, 21, 3, 0, 0], "statkept": [169, 9, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.693885, 0.136059, 0.572322, 0.75938, 0.51304], "most": 3, "first": null}, {"slot": 2, "conf": [0.984288, 0.140201, 0.556732, 0.83905, 0.051786], "most": 0, "first": null}, {"slot": 0, "conf": [0.507619, 0.115661, 0.504926, 0.558592, 0.619377], "most": 1, "first": null}], "others": [], "ks": [1, 0, 1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 2}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 29}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 96}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.369808, 0.644311, 0.174028, 0.494786, 0.219971], "most": 5, "first": null}, {"slot": 1, "conf": [0.632959, 0.778258, 0.537555, 0.334882, 0.663741], "most": 2, "first": 18}], "others": [7], "ks": [1, 2]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 89}, {"op": "record", "slot": 1, "rows": 3, "keep": 1, "tokens": 1, "bonus": 18}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.193626, 0.047197, 0.473374, 0.325588, 0.490532], "most": 2, "first": null}], "others": [5], "ks": [1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 37}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.079457, 0.066059, 0.257443, 0.096919, 0.142243], "most": 2, "first": 48}], "others": [2], "ks": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 39}, {"op": "check", "kept": [15.851135120728243, 6.624566401177774, 1.9108999999999998, 0.0, 0.0], "prob": [10.390988367851437, 11.055202432084759, 1.6652623920999998, 0.0, 0.0], "rates": [0.04169871289972849, 0.04389054117037263, 0.040490747864113046, 0.04830748151140935, 0.06227597946276589, 0.053009972501076766], "joint": [0.051964811162041435, 0.07254078842375175, 0.09365220173724835, 0.10953038564858361, 0.15016593335635878], "zero": 7, "reached": [324, 21, 3, 0, 0], "statkept": [172, 9, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.038152, 0.210689, 0.15371, 0.173824, 0.107555], "most": 1, "first": 53}], "others": [], "ks": [0]}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 53}, {"op": "tree", "slot": 3, "conf": [0.232161, 0.161175, 0.201573, 0.023911, 0.043528], "most": 1, "first": 96, "sibs": [0.123029], "k": 1, "lens": [0]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 24}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.020436, 0.927639, 0.561736, 0.26819, 0.00563], "most": 5, "first": null}, {"slot": 4, "conf": [0.222224, 0.643908, 0.581898, 0.793902, 0.535978], "most": 0, "first": null}], "others": [], "ks": [1, 0]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 59}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 50}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.065043, 0.286901, 0.268985, 0.200671, 0.071833], "most": 3, "first": 67}], "others": [], "ks": [0]}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 67}, {"op": "check", "kept": [17.856468952719194, 6.624566401177774, 1.9108999999999998, 0.0, 0.0], "prob": [9.53719379604851, 11.055202432084759, 1.6652623920999998, 0.0, 0.0], "rates": [0.04169871289972849, 0.04642878246849174, 0.0401384777482314, 0.051109748184492855, 0.06112972432845471, 0.053009972501076766], "joint": [0.051964811162041435, 0.07020958377146291, 0.09365220173724835, 0.10953038564858361, 0.15016593335635878], "zero": 9, "reached": [326, 21, 3, 0, 0], "statkept": [174, 9, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.649744, 0.127422, 0.097222, 0.048927, 0.418522], "most": 5, "first": null}, {"slot": 0, "conf": [0.513074, 0.384795, 0.648714, 0.249361, 0.581779], "most": 5, "first": 57}, {"slot": 5, "conf": [0.628391, 0.499554, 0.276405, 0.201122, 0.956123], "most": 5, "first": 64}, {"slot": 2, "conf": [0.06607, 0.203821, 0.083673, 0.061554, 0.117359], "most": 0, "first": null}], "others": [], "ks": [1, 1, 1, 0]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 98}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 38}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 34}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 85}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.539977, 0.177819, 0.324471, 0.392757, 0.674915], "most": 4, "first": 34}], "others": [1], "ks": [1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 34}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.835579, 0.344936, 0.935526, 0.091014, 0.416457], "most": 4, "first": null}], "others": [8], "ks": [3]}, {"op": "record", "slot": 0, "rows": 4, "keep": 1, "tokens": null, "bonus": 65}, {"op": "tree", "slot": 2, "conf": [0.025522, 0.051489, 0.137534, 0.127869, 0.187564], "most": 1, "first": null, "sibs": [0.091332, 0.398192, 0.033698], "k": 1, "lens": [0, 1, 0]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": 2, "bonus": 78}, {"op": "reset", "slot": 4}, {"op": "check", "kept": [16.814838744498942, 6.624566401177774, 1.9108999999999998, 0.0, 0.0], "prob": [10.874006930126306, 11.055202432084759, 1.6652623920999998, 0.0, 0.0], "rates": [0.040302395234984176, 0.04431838326537848, 0.04441737099926394, 0.051109748184492855, 0.06626246562634595, 0.05206336584927182], "joint": [0.0524897644959233, 0.07020958377146291, 0.09365220173724835, 0.10660654360037515, 0.15016593335635878], "zero": 9, "reached": [332, 21, 3, 0, 0], "statkept": [176, 9, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.684354, 0.476925, 0.088085, 0.202927, 0.57903], "most": 5, "first": null}, {"slot": 5, "conf": [0.313122, 0.282924, 0.054986, 0.04052, 0.304746], "most": 2, "first": null}, {"slot": 4, "conf": [0.169504, 0.285012, 0.115425, 0.078166, 0.132954], "most": 3, "first": null}], "others": [], "ks": [2, 1, 1]}, {"op": "record", "slot": 1, "rows": 3, "keep": 3, "tokens": null, "bonus": 4}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 88}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 7}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.035107, 0.100848, 0.180857, 0.107536, 0.228349], "most": 3, "first": 76}], "others": [1], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 76}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.131136, 0.104381, 0.086181, 0.182954, 0.12045], "most": 4, "first": 71}, {"slot": 3, "conf": [0.048897, 0.227007, 0.118347, 0.064975, 0.27441], "most": 4, "first": 9}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 4}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 69}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.031445, 0.1628, 0.212289, 0.025681, 0.036823], "most": 4, "first": null}, {"slot": 2, "conf": [0.467435, 0.098221, 0.363587, 0.525924, 0.611854], "most": 1, "first": null}, {"slot": 5, "conf": [0.072267, 0.432694, 0.402092, 0.202799, 0.548331], "most": 2, "first": null}, {"slot": 1, "conf": [0.009908, 0.229891, 0.272632, 0.133809, 0.243813], "most": 0, "first": null}], "others": [], "ks": [1, 1, 1, 0]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 75}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 81}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 22}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 82}, {"op": "check", "kept": [18.196070834202928, 7.425829409142441, 1.9108999999999998, 0.0, 0.0], "prob": [9.943438849912075, 11.200471359122215, 1.6652623920999998, 0.0, 0.0], "rates": [0.040302395234984176, 0.04670724535528291, 0.04792868212100395, 0.04888758521994969, 0.06626246562634595, 0.053009972501076766], "joint": [0.0524897644959233, 0.06990429466566682, 0.0967508485049414, 0.10660654360037515, 0.15016593335635878], "zero": 9, "reached": [341, 22, 3, 0, 0], "statkept": [182, 10, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.142662, 0.380803, 0.149438, 0.247872, 0.3693], "most": 4, "first": null}, {"slot": 1, "conf": [0.105403, 0.077972, 0.116965, 0.259664, 0.012012], "most": 4, "first": null}, {"slot": 0, "conf": [0.379072, 0.594042, 0.89227, 0.82035, 0.088823], "most": 4, "first": 0}], "others": [8], "ks": [1, 1, 3]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 77}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 56}, {"op": "record", "slot": 0, "rows": 4, "keep": 3, "tokens": 4, "bonus": 38}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.326745, 0.425992, 0.712314, 0.792179, 0.401128], "most": 2, "first": null}, {"slot": 3, "conf": [0.674509, 0.841284, 0.064069, 0.005434, 0.309378], "most": 5, "first": 87}, {"slot": 4, "conf": [0.085782, 0.01617, 0.59221, 0.603053, 0.482279], "most": 5, "first": 10}, {"slot": 5, "conf": [0.694305, 0.39629, 0.229935, 0.979206, 0.919827], "most": 0, "first": 96}], "others": [], "ks": [1, 2, 1, 0]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": 2, "bonus": 22}, {"op": "record", "slot": 3, "rows": 3, "keep": 2, "tokens": 4, "bonus": 87}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 88}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": 3, "bonus": 91}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.835583, 0.501347, 0.527563, 0.79448, 0.994964], "most": 3, "first": 96}, {"slot": 5, "conf": [0.481372, 0.366978, 0.596002, 0.128339, 0.12715], "most": 4, "first": 5}], "others": [5], "ks": [2, 2]}, {"op": "record", "slot": 1, "rows": 3, "keep": 1, "tokens": null, "bonus": 58}, {"op": "record", "slot": 5, "rows": 3, "keep": 3, "tokens": 5, "bonus": 15}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.121524, 0.001296, 0.230874, 0.275674, 0.254557], "most": 5, "first": 77}], "others": [], "ks": [0]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 62}, {"op": "check", "kept": [18.29289003426855, 8.718254004330259, 1.8535729999999997, 0.0, 0.0], "prob": [10.402712435514239, 11.964325394544147, 2.5075745203369997, 0.0, 0.0], "rates": [0.047458788232696635, 0.046177451550407936, 0.04792868212100395, 0.05269907073971929, 0.06626246562634595, 0.0641759550665633], "joint": [0.0524897644959233, 0.0736619387195361, 0.0967630222656807, 0.11276975036877987, 0.15016593335635878], "zero": 10, "reached": [349, 25, 4, 0, 0], "statkept": [187, 12, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.48659, 0.252426, 0.043986, 0.617453, 0.104133], "most": 0, "first": null}, {"slot": 1, "conf": [0.169737, 0.142324, 0.178969, 0.06906, 0.06704], "most": 0, "first": 81}, {"slot": 0, "conf": [0.11512, 0.966089, 0.482789, 0.20964, 0.015481], "most": 0, "first": null}, {"slot": 3, "conf": [0.237232, 0.232387, 0.099773, 0.269632, 0.066785], "most": 0, "first": null}], "others": [], "ks": [0, 0, 0, 0]}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 31}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": 3, "bonus": 46}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 87}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 43}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.341774, 0.583648, 0.356213, 0.150721, 0.599926], "most": 1, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 44}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.219903, 0.258782, 0.656159, 0.179942, 0.659096], "most": 1, "first": 5}, {"slot": 1, "conf": [0.172945, 0.279965, 0.061272, 0.260102, 0.037452], "most": 5, "first": 95}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 11}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 95}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.09243, 0.310743, 0.55745, 0.665311, 0.029047], "most": 1, "first": null}, {"slot": 1, "conf": [0.192648, 0.013398, 0.057783, 0.269001, 0.102809], "most": 3, "first": null}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 80}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 58}, {"op": "check", "kept": [16.67872710081484, 8.718254004330259, 1.8535729999999997, 0.0, 0.0], "prob": [9.881461774012534, 11.964325394544147, 2.5075745203369997, 0.0, 0.0], "rates": [0.047458788232696635, 0.05012028869286287, 0.04767966891237907, 0.05366882310979523, 0.06626246562634595, 0.06223328591749644], "joint": [0.04860664629606308, 0.07318239075971107, 0.0967630222656807, 0.11584720217422029, 0.15016593335635878], "zero": 10, "reached": [354, 25, 4, 0, 0], "statkept": [188, 12, 2, 0, 0]}, {"op": "tree", "slot": 0, "conf": [0.02878, 0.077466, 0.165974, 0.107358, 0.01131], "most": 5, "first": 80, "sibs": [0.412351, 0.139483, 0.44664], "k": 1, "lens": [1, 0, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 80}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.233872, 0.223772, 0.090052, 0.290843, 0.249782], "most": 0, "first": 1}, {"slot": 2, "conf": [0.066186, 0.078395, 0.040077, 0.235989, 0.254578], "most": 5, "first": null}], "others": [3], "ks": [0, 1]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 1}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 28}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.187837, 0.497998, 0.708742, 0.792917, 0.600271], "most": 4, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 51}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.120018, 0.26991, 0.077983, 0.293456, 0.19422], "most": 4, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 60}, {"op": "reset", "slot": 2}, {"op": "check", "kept": [17.619130182303522, 8.718254004330259, 1.8535729999999997, 0.0, 0.0], "prob": [9.13874808716314, 11.964325394544147, 2.5075745203369997, 0.0, 0.0], "rates": [0.04848328134848166, 0.04858019111024746, 0.06626246562634595, 0.05366882310979523, 0.06626246562634595, 0.06248659141892469], "joint": [0.044187860269148255, 0.07006824647206379, 0.09578996708656731, 0.11584720217422029, 0.15016593335635878], "zero": 10, "reached": [358, 25, 4, 0, 0], "statkept": [191, 12, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.977818, 0.768271, 0.447477, 0.387646, 0.836421], "most": 0, "first": 61}], "others": [7], "ks": [0]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 61}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.320195, 0.439941, 0.752986, 0.201057, 0.282121], "most": 4, "first": null}, {"slot": 0, "conf": [0.195037, 0.496565, 0.37848, 0.090112, 0.161797], "most": 3, "first": 5}, {"slot": 2, "conf": [0.063549, 0.204177, 0.570324, 0.552968, 0.024613], "most": 1, "first": null}, {"slot": 4, "conf": [0.273347, 0.539503, 0.586189, 0.637523, 0.973061], "most": 0, "first": 57}], "others": [], "ks": [1, 1, 1, 0]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 38}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 7}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 4}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 57}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.274095, 0.146322, 0.426875, 0.302987, 0.245605], "most": 4, "first": null}], "others": [3], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 85}, {"op": "tree", "slot": 4, "conf": [0.601232, 0.169884, 0.079468, 0.235506, 0.999498], "most": 4, "first": null, "sibs": [0.265455], "k": 1, "lens": [0]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 55}, {"op": "check", "kept": [18.041046590781875, 8.718254004330259, 1.8535729999999997, 0.0, 0.0], "prob": [9.236122674046728, 11.964325394544147, 2.5075745203369997, 0.0, 0.0], "rates": [0.04848328134848166, 0.04858019111024746, 0.06626246562634595, 0.05366882310979523, 0.0622205630072087, 0.06248659141892469], "joint": [0.04639725328260567, 0.07006824647206379, 0.09578996708656731, 0.11353025813073588, 0.15016593335635878], "zero": 10, "reached": [363, 25, 4, 0, 0], "statkept": [194, 12, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.109804, 0.269475, 0.254757, 0.039213, 0.065885], "most": 5, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 28}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.188174, 0.548659, 0.605755, 0.319829, 0.558766], "most": 5, "first": null}], "others": [3], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": 4, "bonus": 68}, {"op": "reset", "slot": 1}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.482854, 0.116059, 0.375192, 0.67181, 0.285049], "most": 0, "first": 69}, {"slot": 0, "conf": [0.346506, 0.629613, 0.363071, 0.09097, 0.094344], "most": 0, "first": 37}], "others": [3], "ks": [0, 0]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 69}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 37}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.581252, 0.602465, 0.225539, 0.483865, 0.479624], "most": 5, "first": null}, {"slot": 3, "conf": [0.074227, 0.279558, 0.078477, 0.285806, 0.242017], "most": 5, "first": null}, {"slot": 1, "conf": [0.870511, 0.681365, 0.906256, 0.035475, 0.915082], "most": 3, "first": null}, {"slot": 4, "conf": [0.191465, 0.19376, 0.455518, 0.270904, 0.646168], "most": 2, "first": null}], "others": [], "ks": [2, 1, 3, 1]}, {"op": "record", "slot": 2, "rows": 3, "keep": 2, "tokens": null, "bonus": 44}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 33}, {"op": "record", "slot": 1, "rows": 4, "keep": 3, "tokens": null, "bonus": 84}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 82}, {"op": "check", "kept": [18.736552559741074, 9.20300519267434, 1.7979658099999996, 0.0, 0.0], "prob": [9.590507002653801, 12.522989813726587, 3.3386032847268896, 0.0, 0.0], "rates": [0.04883945250973736, 0.07543753771876886, 0.06081800212863007, 0.05489486535387426, 0.05606716846782445, 0.06248659141892469], "joint": [0.047738704371563356, 0.07373728816218438, 0.09578996708656731, 0.11353025813073588, 0.15016593335635878], "zero": 10, "reached": [369, 27, 5, 0, 0], "statkept": [198, 13, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.246471, 0.078814, 0.232225, 0.204769, 0.267429], "most": 4, "first": null}], "others": [1], "ks": [1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 88}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.189006, 0.431053, 0.09013, 0.192635, 0.886849], "most": 1, "first": 61}, {"slot": 5, "conf": [0.501821, 0.383336, 0.477788, 0.324407, 0.495299], "most": 0, "first": 98}], "others": [1], "ks": [1, 0]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 61}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 98}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.268112, 0.262264, 0.055261, 0.02849, 0.238058], "most": 1, "first": 50}, {"slot": 4, "conf": [0.197551, 0.151319, 0.023569, 0.104589, 0.110407], "most": 2, "first": null}, {"slot": 5, "conf": [0.073169, 0.319576, 0.474358, 0.897642, 0.346888], "most": 1, "first": null}, {"slot": 3, "conf": [0.250985, 0.065433, 0.023016, 0.639723, 0.385239], "most": 1, "first": 35}], "others": [], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 50}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 44}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 31}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 9}, {"op": "reset", "slot": 3}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.071106, 0.028117, 0.086404, 0.07727, 0.107324], "most": 1, "first": null}, {"slot": 1, "conf": [0.055538, 0.239488, 0.260735, 0.196845, 0.020242], "most": 5, "first": 8}], "others": [5], "ks": [1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 82}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 54}, {"op": "check", "kept": [18.26059730223356, 9.20300519267434, 1.7979658099999996, 0.0, 0.0], "prob": [8.705646577714088, 12.522989813726587, 3.3386032847268896, 0.0, 0.0], "rates": [0.04883945250973736, 0.05718288516247088, 0.055495655482970765, 0.06626246562634595, 0.054173315209896974, 0.06357629367161573], "joint": [0.04990864547936169, 0.07422416012204347, 0.09578996708656731, 0.11582827436311482, 0.15016593335635878], "zero": 10, "reached": [377, 27, 5, 0, 0], "statkept": [202, 13, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.519319, 0.224395, 0.2115, 0.663642, 0.111869], "most": 2, "first": 68}], "others": [], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 31}, {"op": "tree", "slot": 2, "conf": [0.005265, 0.253658, 0.179395, 0.014083, 0.173963], "most": 5, "first": 32, "sibs": [0.370408, 0.111049, 0.352899], "k": 0, "lens": [0, 0, 0]}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 5}, {"op": "reset", "slot": 2}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.273921, 0.095776, 0.270493, 0.119365, 0.121551], "most": 3, "first": null}, {"slot": 2, "conf": [0.087209, 0.061796, 0.144027, 0.101044, 0.156726], "most": 5, "first": null}, {"slot": 1, "conf": [0.068072, 0.116796, 0.248126, 0.239263, 0.115887], "most": 4, "first": null}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 51}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 17}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 96}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.941859, 0.711961, 0.253075, 0.599701, 0.242777], "most": 3, "first": 23}, {"slot": 1, "conf": [0.359062, 0.613655, 0.120384, 0.470726, 0.368307], "most": 1, "first": 61}, {"slot": 5, "conf": [0.106604, 0.287159, 0.270306, 0.237561, 0.109373], "most": 4, "first": 63}, {"slot": 0, "conf": [0.226825, 0.219587, 0.224127, 0.166626, 0.001869], "most": 4, "first": null}], "others": [8], "ks": [2, 1, 1, 1]}, {"op": "record", "slot": 3, "rows": 3, "keep": 1, "tokens": null, "bonus": 23}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 99}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 46}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 83}, {"op": "check", "kept": [17.543979249370715, 9.20300519267434, 1.7979658099999996, 0.0, 0.0], "prob": [8.920572868684415, 12.522989813726587, 3.3386032847268896, 0.0, 0.0], "rates": [0.05087442969764309, 0.04361370716510903, 0.06626246562634595, 0.02810014893078933, 0.054173315209896974, 0.06569550346066959], "joint": [0.04990864547936169, 0.07530616663372021, 0.09834436620887578, 0.11582827436311482, 0.15016593335635878], "zero": 11, "reached": [385, 27, 5, 0, 0], "statkept": [206, 13, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.007026, 0.301901, 0.448805, 0.577579, 0.471924], "most": 3, "first": null}, {"slot": 2, "conf": [0.741741, 0.135642, 0.52138, 0.732664, 0.601129], "most": 1, "first": null}, {"slot": 3, "conf": [0.819197, 0.874318, 0.09167, 0.734397, 0.985866], "most": 2, "first": 24}, {"slot": 5, "conf": [0.916327, 0.073063, 0.755128, 0.797334, 0.749851], "most": 5, "first": null}], "others": [7], "ks": [1, 1, 2, 1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 14}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": 3, "bonus": 44}, {"op": "record", "slot": 3, "rows": 3, "keep": 3, "tokens": null, "bonus": 76}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 60}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.891887, 0.34994, 0.752803, 0.258765, 0.428285], "most": 5, "first": 99}], "others": [8], "ks": [3]}, {"op": "record", "slot": 4, "rows": 4, "keep": 3, "tokens": null, "bonus": 42}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.311766, 0.126259, 0.959543, 0.232832, 0.553042], "most": 4, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 66}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.725422, 0.881241, 0.072349, 0.212564, 0.444525], "most": 3, "first": null}, {"slot": 2, "conf": [0.826396, 0.13091, 0.694656, 0.763902, 0.98508], "most": 4, "first": 89}], "others": [], "ks": [2, 1]}, {"op": "record", "slot": 4, "rows": 3, "keep": 3, "tokens": 4, "bonus": 34}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 89}, {"op": "reset", "slot": 3}, {"op": "check", "kept": [19.958531920217162, 11.310234358213668, 1.7440268356999995, 0.0, 0.0], "prob": [11.767053319298196, 13.472723288463285, 3.991248186185083, 0.0, 0.0], "rates": [0.05087442969764309, 0.04719875395289565, 0.06626246562634595, 0.06626246562634595, 0.06465354678582076, 0.06569550346066959], "joint": [0.05473154178753215, 0.07530616663372021, 0.09834436620887578, 0.11912846947750476, 0.15016593335635878], "zero": 11, "reached": [393, 30, 6, 0, 0], "statkept": [213, 16, 2, 0, 0]}, {"op": "tree", "slot": 0, "conf": [0.300555, 0.202148, 0.632864, 0.256062, 0.140812], "most": 4, "first": null, "sibs": [0.459338, 0.414112, 0.442929], "k": 1, "lens": [1, 1, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 10}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.22082, 0.116497, 0.203454, 0.226404, 0.150315], "most": 0, "first": null}, {"slot": 3, "conf": [0.341156, 0.438027, 0.441547, 0.135412, 0.285011], "most": 3, "first": null}, {"slot": 0, "conf": [0.291023, 0.032478, 0.20114, 0.372787, 0.336941], "most": 5, "first": null}], "others": [], "ks": [0, 1, 1]}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 12}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 67}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 91}, {"op": "tree", "slot": 5, "conf": [0.210524, 0.385423, 0.931188, 0.910183, 0.274781], "most": 5, "first": 1, "sibs": [0.300393, 0.446651, 0.447545], "k": 1, "lens": [0, 1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 1}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.781039, 0.932404, 0.394174, 0.340729, 0.269331], "most": 4, "first": 74}], "others": [4], "ks": [2]}, {"op": "record", "slot": 0, "rows": 3, "keep": 3, "tokens": null, "bonus": 99}, {"op": "reset", "slot": 0}, {"op": "check", "kept": [19.937036272910035, 11.970927327467258, 1.7440268356999995, 0.0, 0.0], "prob": [11.941282938705035, 14.000945589809387, 3.991248186185083, 0.0, 0.0], "rates": [0.06626246562634595, 0.04702850397626001, 0.06626246562634595, 0.06626246562634595, 0.06465354678582076, 0.06428081176297383], "joint": [0.05473154178753215, 0.07530616663372021, 0.1007503381433224, 0.11852342179355008, 0.15016593335635878], "zero": 11, "reached": [398, 31, 6, 0, 0], "statkept": [216, 17, 2, 0, 0]}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.246326, 0.232239, 0.047138, 0.283263, 0.422025], "most": 4, "first": null}, {"slot": 5, "conf": [0.972045, 0.19684, 0.162468, 0.49138, 0.287737], "most": 3, "first": null}, {"slot": 1, "conf": [0.418129, 0.344734, 0.243694, 0.188615, 0.317624], "most": 2, "first": null}, {"slot": 3, "conf": [0.18997, 0.12761, 0.127469, 0.000656, 0.127522], "most": 0, "first": null}], "others": [], "ks": [1, 1, 1, 0]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 63}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 51}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 32}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 47}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.297161, 0.264277, 0.093738, 0.130469, 0.210664], "most": 0, "first": 9}, {"slot": 3, "conf": [0.21252, 0.143337, 0.028789, 0.269157, 0.2062], "most": 5, "first": 95}, {"slot": 0, "conf": [0.107455, 0.355377, 0.69404, 0.035166, 0.689723], "most": 4, "first": null}], "others": [], "ks": [0, 1, 1]}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 8}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 3}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 79}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.809931, 0.422223, 0.743916, 0.314327, 0.980069], "most": 1, "first": 53}], "others": [], "ks": [1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 62}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.590624, 0.569432, 0.618833, 0.572282, 0.664734], "most": 2, "first": 84}], "others": [3], "ks": [2]}, {"op": "record", "slot": 2, "rows": 3, "keep": 1, "tokens": 2, "bonus": 84}, {"op": "check", "kept": [18.821090309990424, 11.970927327467258, 1.7440268356999995, 0.0, 0.0], "prob": [12.729751880061452, 14.000945589809387, 3.991248186185083, 0.0, 0.0], "rates": [0.04969684921975947, 0.04532167055677672, 0.0639717500751668, 0.060738581146744415, 0.06157036845004698, 0.06428081176297383], "joint": [0.0561973150171714, 0.07530616663372021, 0.10283664082208373, 0.1156671750033128, 0.15016593335635878], "zero": 11, "reached": [405, 31, 6, 0, 0], "statkept": [219, 17, 2, 0, 0]}]}, {"joint": 2, "ops": [{"op": "tree", "slot": 0, "conf": [0.679612, 0.079488, 0.294857, 0.572505, 0.174782], "most": 3, "first": null, "sibs": [0.497693, 0.008892], "k": 1, "lens": [1, 0]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": 3, "bonus": 89}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.122588, 0.093343, 0.008029, 0.071843, 0.127533], "most": 0, "first": null}, {"slot": 2, "conf": [0.222464, 0.241552, 0.202747, 0.045798, 0.189493], "most": 4, "first": null}, {"slot": 0, "conf": [0.566128, 0.335905, 0.2014, 0.369589, 0.163714], "most": 3, "first": 51}], "others": [], "ks": [0, 1, 0]}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 94}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 85}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 51}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.135695, 0.099336, 0.042561, 0.490262, 0.477374], "most": 4, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 24}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.026615, 0.047223, 0.303446, 0.034628, 0.281032], "most": 1, "first": null}, {"slot": 5, "conf": [0.248943, 0.022431, 0.656113, 0.709871, 0.401207], "most": 4, "first": 57}], "others": [7], "ks": [1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 32}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 57}, {"op": "check", "kept": [2.7388658099999996, 0.0, 0.0, 0.0, 0.0], "prob": [1.6996764440018681, 0.0, 0.0, 0.0, 0.0], "rates": [0.06540650140623977, 0.0455497859160062, 0.06626246562634595, 0.06626246562634595, 0.03313123281317298, 0.03313123281317298], "joint": [0.07533978241870838, 0.0917473278590761, 0.09003331232556046, 0.13291465217896958, 0.14369261276277787], "zero": 1, "reached": [5, 0, 0, 0, 0], "statkept": [2, 0, 0, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.741498, 0.549862, 0.466451, 0.285555, 0.944619], "most": 2, "first": null}, {"slot": 4, "conf": [0.042919, 0.604512, 0.622671, 0.663696, 0.327641], "most": 0, "first": null}, {"slot": 3, "conf": [0.719925, 0.674768, 0.878293, 0.155905, 0.574186], "most": 4, "first": null}, {"slot": 2, "conf": [0.284859, 0.249034, 0.130541, 0.159388, 0.239401], "most": 2, "first": 49}], "others": [], "ks": [2, 0, 2, 0]}, {"op": "record", "slot": 5, "rows": 3, "keep": 3, "tokens": 5, "bonus": 61}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 6}, {"op": "record", "slot": 3, "rows": 3, "keep": 1, "tokens": null, "bonus": 16}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": 1, "bonus": 49}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.18057, 0.402359, 0.439727, 0.087063, 0.304095], "most": 5, "first": 68}], "others": [5], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 65}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.009212, 0.206421, 0.275073, 0.0666, 0.152351], "most": 0, "first": 62}], "others": [6], "ks": [0]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 54}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.985133, 0.434111, 0.672808, 0.259912, 0.789064], "most": 4, "first": 71}], "others": [], "ks": [2]}, {"op": "record", "slot": 5, "rows": 3, "keep": 3, "tokens": null, "bonus": 71}, {"op": "check", "kept": [6.148150072873391, 1.97, 0.0, 0.0, 0.0], "prob": [4.201378685799565, 0.96747714, 0.0, 0.0, 0.0], "rates": [0.06540650140623977, 0.05754071005236205, 0.053814555043320715, 0.02810014893078933, 0.040490747864113046, 0.08879505115581558], "joint": [0.06425547978763564, 0.04587366392953805, 0.09003331232556046, 0.13291465217896958, 0.14369261276277787], "zero": 2, "reached": [9, 2, 0, 0, 0], "statkept": [5, 2, 0, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.455513, 0.796633, 0.149406, 0.389878, 0.003636], "most": 3, "first": 27}], "others": [7], "ks": [3]}, {"op": "record", "slot": 3, "rows": 4, "keep": 2, "tokens": null, "bonus": 47}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.943662, 0.242947, 0.865447, 0.87209, 0.390526], "most": 0, "first": null}, {"slot": 2, "conf": [0.250212, 0.865251, 0.841884, 0.587651, 0.994206], "most": 0, "first": null}], "others": [3], "ks": [0, 0]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 13}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 54}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.988894, 0.314642, 0.133718, 0.230462, 0.986932], "most": 0, "first": 9}, {"slot": 3, "conf": [0.876008, 0.076908, 0.498026, 0.880331, 0.878934], "most": 5, "first": 91}], "others": [2], "ks": [0, 1]}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 92}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 91}, {"op": "reset", "slot": 4}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.076106, 0.151085, 0.093392, 0.4272, 0.291542], "most": 0, "first": null}], "others": [], "ks": [0]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": 2, "bonus": 7}, {"op": "check", "kept": [7.7547944035665735, 1.9108999999999998, 0.0, 0.0, 0.0], "prob": [5.270932815468809, 1.7350858258, 0.0, 0.0, 0.0], "rates": [0.06540650140623977, 0.05754071005236205, 0.05017309718528925, 0.04706177642518746, 0.0910995718320124, 0.08879505115581558], "joint": [0.06315032692438477, 0.06171859845879842, 0.09003331232556046, 0.13291465217896958, 0.14369261276277787], "zero": 2, "reached": [11, 3, 0, 0, 0], "statkept": [7, 2, 0, 0, 0]}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.204975, 0.192558, 0.005152, 0.223843, 0.165251], "most": 5, "first": 12}, {"slot": 4, "conf": [0.229851, 0.027375, 0.051138, 0.176013, 0.063088], "most": 3, "first": 87}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 12}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 87}, {"op": "tree", "slot": 4, "conf": [0.160801, 0.183577, 0.271827, 0.233434, 0.247123], "most": 4, "first": null, "sibs": [0.290478, 0.193031, 0.4575], "k": 1, "lens": [0, 0, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 21}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.475433, 0.675274, 0.112163, 0.580574, 0.631413], "most": 2, "first": 93}, {"slot": 0, "conf": [0.047926, 0.270974, 0.038214, 0.217935, 0.076536], "most": 4, "first": 14}], "others": [3], "ks": [2, 0]}, {"op": "record", "slot": 3, "rows": 3, "keep": 3, "tokens": 5, "bonus": 93}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 14}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.22473, 0.002152, 0.186552, 0.284054, 0.125155], "most": 4, "first": 12}], "others": [], "ks": [1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 57}, {"op": "check", "kept": [9.255719452151038, 2.853573, 0.0, 0.0, 0.0], "prob": [5.63535578050928, 2.358307251026, 0.0, 0.0, 0.0], "rates": [0.05967706184248666, 0.05754071005236205, 0.04621143270845207, 0.06745197113055636, 0.05071551133914307, 0.08879505115581558], "joint": [0.060607208469857385, 0.0797551516843291, 0.09003331232556046, 0.13291465217896958, 0.14369261276277787], "zero": 3, "reached": [16, 4, 0, 0, 0], "statkept": [9, 3, 0, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.421294, 0.584032, 0.469546, 0.146662, 0.452464], "most": 2, "first": 92}], "others": [2], "ks": [2]}, {"op": "record", "slot": 1, "rows": 3, "keep": 3, "tokens": null, "bonus": 92}, {"op": "tree", "slot": 0, "conf": [0.430892, 0.581657, 0.063559, 0.411386, 0.132067], "most": 0, "first": null, "sibs": [0.058923, 0.250791], "k": 0, "lens": [0, 0]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 5}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.659905, 0.806966, 0.652072, 0.735189, 0.743258], "most": 3, "first": null}, {"slot": 1, "conf": [0.086726, 0.030533, 0.149031, 0.330866, 0.012942], "most": 0, "first": 23}, {"slot": 4, "conf": [0.252858, 0.146814, 0.250776, 0.11955, 0.199633], "most": 3, "first": 21}, {"slot": 2, "conf": [0.41715, 0.208237, 0.426052, 0.308943, 0.326335], "most": 4, "first": 29}], "others": [], "ks": [3, 0, 0, 1]}, {"op": "record", "slot": 0, "rows": 4, "keep": 3, "tokens": null, "bonus": 93}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 23}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 21}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 29}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.2037, 0.115373, 0.060688, 0.16932, 0.06434], "most": 2, "first": null}], "others": [4], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 58}, {"op": "check", "kept": [10.687067035895456, 4.6549268356999995, 0.0, 0.0, 0.0], "prob": [6.660767373009639, 3.592408332490363, 0.652072, 0.0, 0.0], "rates": [0.057360554102952634, 0.06382319152428016, 0.0437442585660632, 0.06745197113055636, 0.04865469761105434, 0.08879505115581558], "joint": [0.0572047728970516, 0.08189605752379081, 0.09003331232556046, 0.11630032065659839, 0.14369261276277787], "zero": 4, "reached": [20, 6, 1, 0, 0], "statkept": [11, 5, 0, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.218547, 0.024984, 0.148512, 0.062168, 0.273077], "most": 2, "first": 46}], "others": [], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 42}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.139708, 0.80327, 0.361557, 0.614715, 0.190973], "most": 2, "first": null}, {"slot": 2, "conf": [0.512772, 0.127256, 0.453739, 0.678963, 0.012402], "most": 2, "first": 17}, {"slot": 4, "conf": [0.156055, 0.270238, 0.721741, 0.423372, 0.185479], "most": 0, "first": null}], "others": [], "ks": [1, 1, 0]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 37}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 67}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 57}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.196128, 0.039062, 0.535134, 0.525482, 0.26893], "most": 5, "first": 31}], "others": [], "ks": [0]}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 57}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.256928, 0.721264, 0.596591, 0.082999, 0.640488], "most": 2, "first": null}], "others": [2], "ks": [1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 15}, {"op": "check", "kept": [9.17734809866027, 4.6549268356999995, 0.0, 0.0, 0.0], "prob": [6.970452722506851, 3.592408332490363, 0.652072, 0.0, 0.0], "rates": [0.057360554102952634, 0.05440531963125283, 0.04083725446600752, 0.06209187527811986, 0.048243630333182574, 0.08879505115581558], "joint": [0.05100635539188183, 0.08189605752379081, 0.07482629609834314, 0.11630032065659839, 0.14369261276277787], "zero": 5, "reached": [24, 6, 1, 0, 0], "statkept": [11, 5, 0, 0, 0]}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.28538, 0.154335, 0.167955, 0.172736, 0.151235], "most": 4, "first": 56}, {"slot": 0, "conf": [0.963312, 0.567432, 0.40088, 0.316926, 0.123194], "most": 5, "first": null}], "others": [], "ks": [0, 2]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 93}, {"op": "record", "slot": 0, "rows": 3, "keep": 3, "tokens": null, "bonus": 16}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.196731, 0.150033, 0.219798, 0.184338, 0.094443], "most": 4, "first": 57}, {"slot": 1, "conf": [0.023659, 0.008429, 0.073541, 0.003942, 0.189223], "most": 0, "first": 44}], "others": [], "ks": [0, 0]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 19}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 98}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.246161, 0.404031, 0.670547, 0.261832, 0.544801], "most": 1, "first": 45}, {"slot": 2, "conf": [0.575104, 0.100772, 0.41383, 0.684744, 0.332766], "most": 2, "first": null}, {"slot": 1, "conf": [0.100556, 0.222724, 0.260557, 0.156122, 0.28572], "most": 4, "first": 52}, {"slot": 4, "conf": [0.172516, 0.169525, 0.131943, 0.27383, 0.122106], "most": 1, "first": 52}], "others": [4], "ks": [1, 1, 0, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 73}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 13}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 37}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": 3, "bonus": 50}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.183889, 0.07009, 0.061202, 0.298368, 0.126698], "most": 5, "first": 29}], "others": [], "ks": [0]}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 29}, {"op": "check", "kept": [9.02565763450215, 5.515279030628999, 0.0, 0.0, 0.0], "prob": [7.853628826199351, 4.052068082515652, 0.652072, 0.0, 0.0], "rates": [0.05818753113942096, 0.05165701619682262, 0.0399090074629844, 0.0594079314158281, 0.054158467676421206, 0.0787891680651744], "joint": [0.049377955329409744, 0.08078975088791045, 0.07482629609834314, 0.11317658376481905, 0.14369261276277787], "zero": 9, "reached": [28, 7, 1, 0, 0], "statkept": [12, 6, 0, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.457682, 0.05277, 0.298934, 0.095193, 0.196631], "most": 4, "first": 72}, {"slot": 5, "conf": [0.136039, 0.356737, 0.081688, 0.069193, 0.27469], "most": 0, "first": 44}, {"slot": 1, "conf": [0.478519, 0.016025, 0.673465, 0.209833, 0.032516], "most": 3, "first": null}, {"slot": 4, "conf": [0.149418, 0.276568, 0.104428, 0.150603, 0.032863], "most": 0, "first": 59}], "others": [5], "ks": [1, 0, 1, 0]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 72}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 44}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 80}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 83}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.454393, 0.044138, 0.317706, 0.602217, 0.056888], "most": 0, "first": 39}, {"slot": 1, "conf": [0.069878, 0.216923, 0.126466, 0.286773, 0.27514], "most": 1, "first": 29}, {"slot": 2, "conf": [0.201317, 0.212065, 0.074898, 0.250386, 0.002417], "most": 5, "first": null}, {"slot": 0, "conf": [0.580937, 0.596112, 0.737071, 0.788837, 0.321171], "most": 2, "first": null}], "others": [], "ks": [0, 0, 1, 2]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 39}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": 3, "bonus": 29}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 41}, {"op": "record", "slot": 0, "rows": 3, "keep": 2, "tokens": null, "bonus": 90}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.516162, 0.539496, 0.303043, 0.525087, 0.515108], "most": 1, "first": null}, {"slot": 3, "conf": [0.289318, 0.71926, 0.020566, 0.953003, 0.821979], "most": 3, "first": 57}], "others": [1], "ks": [1, 2]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 56}, {"op": "record", "slot": 3, "rows": 3, "keep": 2, "tokens": null, "bonus": 8}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.080116, 0.495262, 0.706222, 0.561859, 0.797292], "most": 1, "first": null}, {"slot": 0, "conf": [0.523446, 0.705518, 0.048436, 0.227302, 0.705732], "most": 0, "first": null}], "others": [], "ks": [1, 0]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 78}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 93}, {"op": "check", "kept": [13.474371076228202, 5.189326039918825, 0.0, 0.0, 0.0], "prob": [8.53846948449358, 5.110079498838977, 0.652072, 0.0, 0.0], "rates": [0.05789956906177884, 0.059548178197923254, 0.04274219687768252, 0.05622004525713643, 0.05411566547048498, 0.07388748950461797], "joint": [0.048473631446166725, 0.08105588803479999, 0.07482629609834314, 0.11501143143924608, 0.14369261276277787], "zero": 10, "reached": [35, 9, 1, 0, 0], "statkept": [18, 6, 0, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.182764, 0.64717, 0.510716, 0.215216, 0.883112], "most": 1, "first": null}, {"slot": 0, "conf": [0.107844, 0.277786, 0.122089, 0.240845, 0.171583], "most": 4, "first": null}, {"slot": 2, "conf": [0.536796, 0.696555, 0.250783, 0.442931, 0.167082], "most": 5, "first": 79}, {"slot": 5, "conf": [0.608207, 0.43435, 0.562228, 0.570914, 0.29458], "most": 5, "first": null}], "others": [8], "ks": [1, 1, 2, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 69}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": 1, "bonus": 57}, {"op": "record", "slot": 2, "rows": 3, "keep": 2, "tokens": null, "bonus": 79}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 25}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.506119, 0.489814, 0.635221, 0.615726, 0.20729], "most": 3, "first": null}, {"slot": 0, "conf": [0.583622, 0.53236, 0.899829, 0.801618, 0.963005], "most": 4, "first": null}, {"slot": 5, "conf": [0.206003, 0.341864, 0.285556, 0.312207, 0.244896], "most": 3, "first": 22}], "others": [6], "ks": [2, 4, 1]}, {"op": "record", "slot": 1, "rows": 3, "keep": 1, "tokens": null, "bonus": 49}, {"op": "record", "slot": 0, "rows": 5, "keep": 2, "tokens": null, "bonus": 16}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 22}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.862982, 0.694199, 0.26442, 0.581411, 0.015197], "most": 1, "first": null}, {"slot": 0, "conf": [0.041123, 0.674849, 0.219599, 0.227103, 0.358411], "most": 2, "first": null}], "others": [6], "ks": [1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": 3, "bonus": 48}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 57}, {"op": "tree", "slot": 3, "conf": [0.220436, 0.114494, 0.229223, 0.208125, 0.102155], "most": 3, "first": 31, "sibs": [0.499096], "k": 1, "lens": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 23}, {"op": "check", "kept": [13.462573986055984, 4.882636870959622, 0.0, 0.0, 0.0], "prob": [9.671896072559871, 6.0160921504575935, 0.652072, 0.0, 0.0], "rates": [0.05347307630607989, 0.055870144637945034, 0.044256180059429724, 0.054167165574770644, 0.05830975368732469, 0.06690849307736055], "joint": [0.048473631446166725, 0.08057978375316215, 0.06811862548998966, 0.1101760273914554, 0.14369261276277787], "zero": 10, "reached": [45, 11, 1, 0, 0], "statkept": [22, 6, 0, 0, 0]}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.904993, 0.446262, 0.745699, 0.672576, 0.453722], "most": 3, "first": 14}, {"slot": 0, "conf": [0.364162, 0.671222, 0.431068, 0.206793, 0.622175], "most": 2, "first": 97}, {"slot": 5, "conf": [0.556627, 0.719066, 0.372813, 0.069773, 0.18049], "most": 2, "first": 39}, {"slot": 4, "conf": [0.093781, 0.188932, 0.167379, 0.007125, 0.244637], "most": 1, "first": null}], "others": [1], "ks": [1, 0, 1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 14}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 97}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 61}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": 2, "bonus": 76}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.517311, 0.212579, 0.350827, 0.647996, 0.05585], "most": 0, "first": 25}, {"slot": 4, "conf": [0.913328, 0.052509, 0.505486, 0.592883, 0.902631], "most": 1, "first": null}, {"slot": 5, "conf": [0.052377, 0.051729, 0.208528, 0.531527, 0.673178], "most": 5, "first": null}], "others": [], "ks": [0, 1, 1]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 63}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 71}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 71}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.171714, 0.070011, 0.239963, 0.168587, 0.238052], "most": 1, "first": null}], "others": [3], "ks": [1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 95}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.493841, 0.448189, 0.195916, 0.424614, 0.573901], "most": 3, "first": 5}, {"slot": 5, "conf": [0.109039, 0.19058, 0.034006, 0.254925, 0.069581], "most": 3, "first": null}, {"slot": 0, "conf": [0.543424, 0.789443, 0.297471, 0.001957, 0.511231], "most": 4, "first": 75}, {"slot": 3, "conf": [0.019492, 0.470218, 0.622746, 0.295121, 0.118285], "most": 1, "first": null}], "others": [], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 5}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 2}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 18}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": 1, "bonus": 59}, {"op": "check", "kept": [13.0467289557575, 4.882636870959622, 0.0, 0.0, 0.0], "prob": [10.43225308002832, 6.0160921504575935, 0.652072, 0.0, 0.0], "rates": [0.049252490842114985, 0.056807988997189494, 0.043287169971401605, 0.05212172656942241, 0.05763757970196568, 0.06004202942059441], "joint": [0.04927746910918655, 0.08057978375316215, 0.07965075042385579, 0.10723442419988496, 0.14369261276277787], "zero": 11, "reached": [55, 11, 1, 0, 0], "statkept": [25, 6, 0, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.749194, 0.281954, 0.822374, 0.81135, 0.353939], "most": 3, "first": 20}, {"slot": 3, "conf": [0.134233, 0.040473, 0.088635, 0.21828, 0.215864], "most": 5, "first": null}, {"slot": 1, "conf": [0.01737, 0.24551, 0.155983, 0.505289, 0.316631], "most": 0, "first": 55}, {"slot": 4, "conf": [0.173407, 0.605289, 0.993411, 0.972515, 0.691294], "most": 1, "first": null}], "others": [], "ks": [1, 1, 0, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 17}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 68}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 51}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 2}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.145047, 0.32703, 0.41626, 0.395039, 0.364918], "most": 1, "first": 53}, {"slot": 5, "conf": [0.307277, 0.437333, 0.985396, 0.036719, 0.250073], "most": 2, "first": 47}, {"slot": 0, "conf": [0.563502, 0.691579, 0.556085, 0.455889, 0.613249], "most": 0, "first": null}], "others": [], "ks": [0, 1, 0]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 78}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 76}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 61}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.756824, 0.851683, 0.533686, 0.34395, 0.595041], "most": 2, "first": 75}], "others": [5], "ks": [2]}, {"op": "record", "slot": 2, "rows": 3, "keep": 3, "tokens": null, "bonus": 75}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.339786, 0.176692, 0.147952, 0.376939, 0.57452], "most": 3, "first": null}, {"slot": 2, "conf": [0.532096, 0.341386, 0.373573, 0.522547, 0.594303], "most": 1, "first": null}, {"slot": 1, "conf": [0.078928, 0.024081, 0.194544, 0.567981, 0.681682], "most": 2, "first": 65}, {"slot": 0, "conf": [0.32051, 0.425283, 0.094392, 0.438998, 0.267023], "most": 1, "first": null}], "others": [], "ks": [1, 1, 0, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": 2, "bonus": 72}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": 1, "bonus": 42}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 56}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 74}, {"op": "check", "kept": [15.62962980760764, 5.736157764830834, 0.0, 0.0, 0.0], "prob": [10.77040271600421, 6.687292385943865, 0.652072, 0.0, 0.0], "rates": [0.04962009613893627, 0.0549769882035091, 0.04608373171506671, 0.05310723505262234, 0.05553235537153367, 0.06108464109996779], "joint": [0.05136741973840666, 0.08057978375316215, 0.08114801722934703, 0.1062937275312143, 0.14369261276277787], "zero": 13, "reached": [63, 12, 1, 0, 0], "statkept": [32, 7, 0, 0, 0]}, {"op": "tree", "slot": 3, "conf": [0.338305, 0.633411, 0.040784, 0.189922, 0.412712], "most": 5, "first": 76, "sibs": [0.023557, 0.389432, 0.180927], "k": 2, "lens": [0, 1, 0]}, {"op": "record", "slot": 3, "rows": 3, "keep": 3, "tokens": null, "bonus": 85}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.535236, 0.188707, 0.473562, 0.547943, 0.067111], "most": 3, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 15}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.259619, 0.299523, 0.006602, 0.215149, 0.17786], "most": 1, "first": 54}, {"slot": 5, "conf": [0.511764, 0.453383, 0.239723, 0.954157, 0.824714], "most": 5, "first": 25}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 10}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 25}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.356076, 0.264542, 0.587705, 0.24619, 0.628618], "most": 4, "first": 20}, {"slot": 4, "conf": [0.117744, 0.233421, 0.165335, 0.681001, 0.162694], "most": 3, "first": 97}, {"slot": 5, "conf": [0.00338, 0.552185, 0.492171, 0.117436, 0.552138], "most": 0, "first": 11}], "others": [], "ks": [1, 0, 0]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 49}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 97}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 34}, {"op": "check", "kept": [16.760451102840996, 6.564073031885909, 0.0, 0.0, 0.0], "prob": [10.917402260447371, 7.120084614365548, 0.652072, 0.0, 0.0], "rates": [0.04962009613893627, 0.0549769882035091, 0.04866087390698157, 0.05493878551475529, 0.05684854410878538, 0.05650023956101574], "joint": [0.04756242568370987, 0.07944739920111227, 0.08114801722934703, 0.1062937275312143, 0.12932335148650007], "zero": 14, "reached": [68, 13, 1, 0, 0], "statkept": [35, 8, 0, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.704345, 0.988538, 0.818957, 0.120948, 0.607421], "most": 1, "first": null}, {"slot": 3, "conf": [0.481227, 0.666229, 0.969887, 0.067997, 0.306018], "most": 4, "first": 69}, {"slot": 2, "conf": [0.355815, 0.214713, 0.118483, 0.666481, 0.810938], "most": 5, "first": 69}], "others": [4], "ks": [1, 3, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 90}, {"op": "record", "slot": 3, "rows": 4, "keep": 2, "tokens": null, "bonus": 84}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 69}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.192204, 0.027359, 0.205287, 0.177201, 0.052644], "most": 5, "first": 62}, {"slot": 1, "conf": [0.113633, 0.30982, 0.650606, 0.301511, 0.386055], "most": 1, "first": 30}, {"slot": 3, "conf": [0.353034, 0.191211, 0.521593, 0.538032, 0.448754], "most": 3, "first": null}, {"slot": 2, "conf": [0.350359, 0.297413, 0.227705, 0.448219, 0.152164], "most": 5, "first": null}], "others": [], "ks": [1, 0, 1, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 65}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 61}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 10}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 28}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.040518, 0.043211, 0.173547, 0.130621, 0.155992], "most": 5, "first": 22}, {"slot": 2, "conf": [0.996159, 0.909564, 0.972995, 0.96402, 0.740302], "most": 4, "first": null}, {"slot": 3, "conf": [0.752953, 0.142585, 0.862668, 0.004992, 0.659272], "most": 4, "first": 18}, {"slot": 1, "conf": [0.907755, 0.107584, 0.428015, 0.936771, 0.874309], "most": 2, "first": null}], "others": [], "ks": [0, 4, 1, 1]}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 22}, {"op": "record", "slot": 2, "rows": 5, "keep": 4, "tokens": null, "bonus": 91}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": 1, "bonus": 18}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 97}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.142772, 0.444626, 0.252301, 0.192061, 0.013611], "most": 5, "first": null}, {"slot": 5, "conf": [0.735314, 0.1206, 0.623353, 0.655042, 0.189707], "most": 2, "first": 11}], "others": [8], "ks": [1, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 40}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 97}, {"op": "check", "kept": [18.14812105994504, 7.176136315701451, 1.0, 0.0, 0.0], "prob": [12.58707997958539, 8.255093743656543, 1.60550484, 0.96402, 0.0], "rates": [0.05236414023116753, 0.0548289451139414, 0.052022265529646686, 0.05421327502394419, 0.05684854410878538, 0.04971511079985128], "joint": [0.04756242568370987, 0.07944739920111227, 0.08255112099566364, 0.10583763586331299, 0.12932335148650007], "zero": 16, "reached": [79, 15, 2, 1, 0], "statkept": [42, 9, 1, 0, 0]}, {"op": "tree", "slot": 4, "conf": [0.039452, 0.175, 0.013066, 0.005538, 0.235263], "most": 1, "first": 83, "sibs": [0.084014, 0.077157], "k": 0, "lens": [0, 0]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": 2, "bonus": 83}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.352794, 0.385026, 0.562663, 0.403714, 0.444108], "most": 5, "first": null}, {"slot": 1, "conf": [0.71184, 0.971241, 0.413355, 0.259172, 0.316032], "most": 5, "first": 68}, {"slot": 0, "conf": [0.950336, 0.652259, 0.269527, 0.151792, 0.738813], "most": 0, "first": 54}, {"slot": 3, "conf": [0.216032, 0.096989, 0.050718, 0.124035, 0.046709], "most": 0, "first": null}], "others": [3], "ks": [1, 3, 0, 0]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 6}, {"op": "record", "slot": 1, "rows": 4, "keep": 3, "tokens": null, "bonus": 22}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 54}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": 3, "bonus": 63}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.144786, 0.182449, 0.006042, 0.00396, 0.173733], "most": 4, "first": null}, {"slot": 2, "conf": [0.205727, 0.507967, 0.507366, 0.693248, 0.098315], "most": 3, "first": null}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 87}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 60}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.202681, 0.126251, 0.122691, 0.054938, 0.25938], "most": 0, "first": null}, {"slot": 5, "conf": [0.195026, 0.50408, 0.699435, 0.08, 0.149082], "most": 5, "first": 16}], "others": [6], "ks": [0, 0]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 56}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 33}, {"op": "check", "kept": [18.799183810696626, 7.960852226230408, 0.97, 0.0, 0.0], "prob": [12.011377498909974, 8.978681931346845, 1.9706946948, 0.96402, 0.0], "rates": [0.051011732698520655, 0.05707524492963281, 0.05314803696741238, 0.05822907317386599, 0.05975006089910053, 0.04639725328260567], "joint": [0.04756242568370987, 0.08052696848174454, 0.08677015453764524, 0.10812617341472285, 0.12932335148650007], "zero": 18, "reached": [83, 16, 3, 1, 0], "statkept": [45, 10, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.152098, 0.887119, 0.864998, 0.379821, 0.242954], "most": 4, "first": null}], "others": [6], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 87}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.22965, 0.16471, 0.178487, 0.172423, 0.296466], "most": 5, "first": 63}], "others": [], "ks": [1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 65}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.03274, 0.076536, 0.09689, 0.643727, 0.287589], "most": 1, "first": null}, {"slot": 5, "conf": [0.108239, 0.018201, 0.185309, 0.24764, 0.274962], "most": 2, "first": 7}], "others": [], "ks": [1, 0]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 86}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 7}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.435639, 0.118395, 0.897699, 0.359528, 0.669368], "most": 2, "first": 33}, {"slot": 2, "conf": [0.096644, 0.134439, 0.279535, 0.223405, 0.228679], "most": 4, "first": 84}], "others": [], "ks": [1, 0]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 33}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": 3, "bonus": 82}, {"op": "check", "kept": [18.482766829824765, 7.960852226230408, 0.97, 0.0, 0.0], "prob": [10.989997238003536, 8.978681931346845, 1.9706946948, 0.96402, 0.0], "rates": [0.04730409653475991, 0.05707524492963281, 0.05760872156314313, 0.05822907317386599, 0.061396427637488205, 0.04639725328260567], "joint": [0.04375743162901308, 0.07892549082885797, 0.08677015453764524, 0.10812617341472285, 0.12932335148650007], "zero": 20, "reached": [87, 16, 3, 1, 0], "statkept": [47, 10, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.547756, 0.449545, 0.056399, 0.257778, 0.018761], "most": 4, "first": null}, {"slot": 3, "conf": [0.780564, 0.342227, 0.992572, 0.459885, 0.252546], "most": 4, "first": null}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 35}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 58}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.060804, 0.645954, 0.644457, 0.548324, 0.429306], "most": 4, "first": 47}], "others": [], "ks": [0]}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 47}, {"op": "reset", "slot": 3}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.402877, 0.673647, 0.677624, 0.59607, 0.403985], "most": 0, "first": null}, {"slot": 5, "conf": [0.81451, 0.90305, 0.705765, 0.364331, 0.967093], "most": 4, "first": null}, {"slot": 4, "conf": [0.072105, 0.575132, 0.620022, 0.604034, 0.101564], "most": 0, "first": 37}, {"slot": 2, "conf": [0.62257, 0.552466, 0.713527, 0.002456, 0.73476], "most": 4, "first": 52}], "others": [], "ks": [0, 3, 0, 1]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 43}, {"op": "record", "slot": 5, "rows": 4, "keep": 3, "tokens": null, "bonus": 44}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 37}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 62}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.440345, 0.067243, 0.307644, 0.211968, 0.486519], "most": 4, "first": 79}], "others": [], "ks": [1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 79}, {"op": "check", "kept": [18.10793436857435, 8.722026659443495, 0.9409, 0.0, 0.0], "prob": [12.181868973522144, 9.612371473406439, 2.617338853956, 0.96402, 0.0], "rates": [0.049454282740885355, 0.05707524492963281, 0.05760872156314313, 0.0455497859160062, 0.061396427637488205, 0.04722975990107512], "joint": [0.04345895490661048, 0.08114236197760777, 0.08677015453764524, 0.10746840687797804, 0.12932335148650007], "zero": 21, "reached": [92, 17, 4, 1, 0], "statkept": [49, 11, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.247692, 0.25475, 0.079994, 0.139524, 0.207254], "most": 4, "first": null}, {"slot": 4, "conf": [0.12189, 0.293665, 0.281324, 0.125145, 0.192814], "most": 2, "first": null}, {"slot": 0, "conf": [0.287418, 0.11456, 0.125582, 0.228519, 0.112221], "most": 1, "first": 53}, {"slot": 3, "conf": [0.217428, 0.124983, 0.119627, 0.252761, 0.290831], "most": 4, "first": 53}], "others": [], "ks": [1, 1, 1, 0]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 69}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 67}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 53}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 82}, {"op": "reset", "slot": 4}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.156702, 0.191859, 0.274021, 0.239692, 0.15512], "most": 4, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 27}, {"op": "tree", "slot": 2, "conf": [0.564345, 0.311066, 0.323258, 0.590959, 0.427169], "most": 2, "first": 60, "sibs": [0.063565, 0.360969, 0.456895], "k": 1, "lens": [0, 1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 60}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.10657, 0.225915, 0.043898, 0.127285, 0.074463], "most": 5, "first": null}, {"slot": 4, "conf": [0.591713, 0.310261, 0.225027, 0.624965, 0.645591], "most": 2, "first": 99}, {"slot": 3, "conf": [0.298788, 0.378935, 0.941316, 0.123353, 0.303049], "most": 3, "first": 50}], "others": [], "ks": [1, 2, 1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 39}, {"op": "record", "slot": 4, "rows": 3, "keep": 2, "tokens": null, "bonus": 99}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": 1, "bonus": 72}, {"op": "check", "kept": [17.23822228943536, 8.46036585966019, 0.9409, 0.0, 0.0], "prob": [11.606582025818355, 9.634261329204246, 2.617338853956, 0.96402, 0.0], "rates": [0.05003567761360274, 0.053317338131893766, 0.05507723598825281, 0.038609541704739965, 0.05620029786157866, 0.04937656716930581], "joint": [0.038279343509258816, 0.08114236197760777, 0.08677015453764524, 0.10740270099691428, 0.12932335148650007], "zero": 22, "reached": [100, 18, 4, 1, 0], "statkept": [53, 11, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.002167, 0.090724, 0.216284, 0.209383, 0.068225], "most": 0, "first": null}, {"slot": 0, "conf": [0.441367, 0.345232, 0.356494, 0.944012, 0.249484], "most": 4, "first": 97}, {"slot": 3, "conf": [0.588803, 0.224533, 0.545088, 0.504854, 0.598349], "most": 3, "first": 38}, {"slot": 1, "conf": [0.138011, 0.607283, 0.169217, 0.482123, 0.247002], "most": 4, "first": 58}], "others": [], "ks": [0, 1, 1, 0]}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 99}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 97}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 38}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 60}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.186896, 0.168499, 0.224568, 0.248278, 0.156982], "most": 1, "first": null}], "others": [5], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 58}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.26165, 0.023303, 0.195518, 0.199836, 0.045524], "most": 2, "first": 85}], "others": [1], "ks": [0]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 25}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.150863, 0.213082, 0.482513, 0.551883, 0.209796], "most": 5, "first": null}, {"slot": 0, "conf": [0.137906, 0.251818, 0.040103, 0.228634, 0.195476], "most": 1, "first": 7}, {"slot": 4, "conf": [0.524563, 0.695943, 0.354326, 0.5876, 0.41414], "most": 5, "first": 11}, {"slot": 5, "conf": [0.24445, 0.25065, 0.217838, 0.173529, 0.136451], "most": 0, "first": 90}], "others": [], "ks": [1, 0, 2, 0]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": 3, "bonus": 84}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": 3, "bonus": 7}, {"op": "record", "slot": 4, "rows": 3, "keep": 1, "tokens": null, "bonus": 11}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 90}, {"op": "reset", "slot": 5}, {"op": "check", "kept": [15.288325092636969, 8.46036585966019, 0.9409, 0.0, 0.0], "prob": [11.266707101963423, 9.634261329204246, 2.617338853956, 0.96402, 0.0], "rates": [0.05396281314620469, 0.05289314482804218, 0.05507723598825281, 0.05070002245286708, 0.04134879778370444, 0.06626246562634595], "joint": [0.03862077488722734, 0.08114236197760777, 0.08518959525261618, 0.10651159786659535, 0.12932335148650007], "zero": 25, "reached": [105, 18, 4, 1, 0], "statkept": [54, 11, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.807974, 0.659649, 0.379003, 0.690441, 0.55849], "most": 1, "first": 3}, {"slot": 4, "conf": [0.576146, 0.267885, 0.299134, 0.477672, 0.088874], "most": 4, "first": 21}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 3}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 77}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.857948, 0.295427, 0.868953, 0.241731, 0.711069], "most": 2, "first": 25}, {"slot": 1, "conf": [0.800759, 0.281096, 0.737394, 0.197303, 0.959785], "most": 3, "first": null}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 46}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 33}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.067023, 0.28486, 0.112449, 0.242152, 0.068708], "most": 0, "first": null}, {"slot": 4, "conf": [0.568588, 0.264078, 0.421573, 0.205439, 0.675836], "most": 0, "first": null}, {"slot": 1, "conf": [0.149618, 0.292022, 0.078168, 0.210133, 0.131552], "most": 2, "first": 0}], "others": [], "ks": [0, 0, 0]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 23}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 91}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 61}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.169233, 0.2739, 0.230604, 0.081812, 0.233747], "most": 2, "first": null}, {"slot": 2, "conf": [0.225085, 0.058053, 0.109628, 0.045279, 0.139032], "most": 1, "first": null}, {"slot": 3, "conf": [0.072967, 0.165039, 0.263278, 0.124446, 0.218208], "most": 4, "first": null}, {"slot": 1, "conf": [0.193721, 0.051328, 0.131139, 0.096306, 0.230047], "most": 4, "first": 5}], "others": [1], "ks": [1, 1, 1, 0]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 3}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": 3, "bonus": 24}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 11}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": 2, "bonus": 57}, {"op": "check", "kept": [12.592659570230554, 8.46036585966019, 0.9409, 0.0, 0.0], "prob": [11.829538270190053, 9.634261329204246, 2.617338853956, 0.96402, 0.0], "rates": [0.05388499594739926, 0.054662133353740525, 0.05901132427312801, 0.05349182763744428, 0.03909347809089792, 0.06626246562634595], "joint": [0.03862077488722734, 0.07953563196311948, 0.08441971326036118, 0.10864123893616795, 0.12932335148650007], "zero": 27, "reached": [112, 18, 4, 1, 0], "statkept": [55, 11, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.06782, 0.06362, 0.201047, 0.216182, 0.285611], "most": 4, "first": null}], "others": [5], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 96}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.473152, 0.287514, 0.570695, 0.532983, 0.353834], "most": 3, "first": null}, {"slot": 4, "conf": [0.696552, 0.064142, 0.129159, 0.115418, 0.54783], "most": 4, "first": null}, {"slot": 3, "conf": [0.008757, 0.004467, 0.453844, 0.496348, 0.557839], "most": 0, "first": 77}], "others": [4], "ks": [1, 1, 0]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 22}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": 3, "bonus": 46}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": 2, "bonus": 77}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.031214, 0.126552, 0.12328, 0.193637, 0.065095], "most": 0, "first": null}, {"slot": 0, "conf": [0.442933, 0.465224, 0.166321, 0.238752, 0.337014], "most": 0, "first": 72}], "others": [], "ks": [0, 0]}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 87}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": 3, "bonus": 72}, {"op": "reset", "slot": 1}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.313656, 0.598218, 0.593768, 0.402806, 0.508905], "most": 5, "first": 81}, {"slot": 5, "conf": [0.595232, 0.196078, 0.396677, 0.132254, 0.257182], "most": 4, "first": 21}, {"slot": 3, "conf": [0.618853, 0.448737, 0.26435, 0.066217, 0.494764], "most": 5, "first": 36}, {"slot": 1, "conf": [0.290407, 0.86348, 0.038977, 0.773047, 0.46645], "most": 2, "first": null}], "others": [], "ks": [0, 1, 1, 1]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 81}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 21}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 36}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 88}, {"op": "check", "kept": [15.575224743544203, 8.46036585966019, 0.9409, 0.0, 0.0], "prob": [12.374533905776815, 9.634261329204246, 2.617338853956, 0.96402, 0.0], "rates": [0.05947424765076722, 0.03313123281317298, 0.05901132427312801, 0.058987145339788646, 0.046847383999488935, 0.060738581146744415], "joint": [0.040551813631588704, 0.0821475186885605, 0.0891289134195133, 0.10908880898549546, 0.12932335148650007], "zero": 28, "reached": [118, 18, 4, 1, 0], "statkept": [60, 11, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.272832, 0.076227, 0.284136, 0.242778, 0.039244], "most": 0, "first": null}], "others": [], "ks": [0]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 34}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.326974, 0.812204, 0.122677, 0.4118, 0.119192], "most": 1, "first": 12}], "others": [8], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 73}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.641806, 0.86026, 0.269822, 0.452885, 0.840829], "most": 3, "first": 92}, {"slot": 4, "conf": [0.24332, 0.710594, 0.498925, 0.973678, 0.100732], "most": 1, "first": 44}], "others": [], "ks": [2, 0]}, {"op": "record", "slot": 5, "rows": 3, "keep": 2, "tokens": null, "bonus": 92}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": 1, "bonus": 44}, {"op": "tree", "slot": 2, "conf": [0.625002, 0.629104, 0.897077, 0.129106, 0.916327], "most": 3, "first": null, "sibs": [0.391654], "k": 2, "lens": [1]}, {"op": "record", "slot": 2, "rows": 3, "keep": 1, "tokens": null, "bonus": 18}, {"op": "check", "kept": [16.612207479593774, 8.206554883870384, 0.9409, 0.0, 0.0], "prob": [12.718403900787433, 10.20549348932812, 2.617338853956, 0.96402, 0.0], "rates": [0.05947424765076722, 0.04969684921975947, 0.054585365663465626, 0.058987145339788646, 0.04608843602733044, 0.05936882458208587], "joint": [0.04278108142795472, 0.0821475186885605, 0.0891289134195133, 0.10954008200471237, 0.12932335148650007], "zero": 29, "reached": [121, 19, 4, 1, 0], "statkept": [62, 11, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.395816, 0.529655, 0.725175, 0.370967, 0.647027], "most": 2, "first": 21}, {"slot": 2, "conf": [0.182099, 0.085016, 0.007192, 0.289716, 0.122858], "most": 1, "first": null}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 50}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": 2, "bonus": 20}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.056905, 0.043535, 0.344438, 0.582385, 0.230419], "most": 1, "first": null}, {"slot": 1, "conf": [0.137995, 0.155271, 0.75113, 0.95967, 0.251515], "most": 0, "first": 71}, {"slot": 5, "conf": [0.175385, 0.021258, 0.097576, 0.034502, 0.148982], "most": 1, "first": null}, {"slot": 0, "conf": [0.094465, 0.158728, 0.241325, 0.201637, 0.290716], "most": 0, "first": null}], "others": [], "ks": [1, 0, 1, 0]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": 3, "bonus": 67}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 97}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 54}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 88}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.413124, 0.857952, 0.438789, 0.606004, 0.892457], "most": 4, "first": null}, {"slot": 3, "conf": [0.514066, 0.79126, 0.170932, 0.714742, 0.169168], "most": 2, "first": null}, {"slot": 2, "conf": [0.102415, 0.436609, 0.545897, 0.580641, 0.442625], "most": 0, "first": null}], "others": [], "ks": [2, 2, 0]}, {"op": "record", "slot": 1, "rows": 3, "keep": 3, "tokens": null, "bonus": 21}, {"op": "record", "slot": 3, "rows": 3, "keep": 3, "tokens": null, "bonus": 43}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 92}, {"op": "tree", "slot": 5, "conf": [0.205133, 0.629382, 0.546931, 0.143083, 0.188218], "most": 5, "first": null, "sibs": [0.123444, 0.09782], "k": 1, "lens": [0, 0]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 48}, {"op": "check", "kept": [17.13124446745654, 9.691547490233642, 0.9409, 0.0, 0.0], "prob": [12.06526255234062, 11.225822264108826, 2.617338853956, 0.96402, 0.0], "rates": [0.058283889167356356, 0.05936882458208587, 0.05745657570696354, 0.06250634830099933, 0.04608843602733044, 0.052768929654219594], "joint": [0.04278108142795472, 0.0821475186885605, 0.08769661808576427, 0.11090712335994744, 0.12932335148650007], "zero": 29, "reached": [128, 21, 4, 1, 0], "statkept": [66, 13, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.027286, 0.224001, 0.28378, 0.078468, 0.192021], "most": 4, "first": null}, {"slot": 0, "conf": [0.171198, 0.38468, 0.099744, 0.01495, 0.636554], "most": 5, "first": null}, {"slot": 4, "conf": [0.23747, 0.518251, 0.603718, 0.149554, 0.955948], "most": 4, "first": null}], "others": [3], "ks": [1, 1, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 81}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 50}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 61}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.68005, 0.792038, 0.697781, 0.296931, 0.639652], "most": 4, "first": null}, {"slot": 0, "conf": [0.480547, 0.176376, 0.44283, 0.444825, 0.207306], "most": 3, "first": 67}, {"slot": 1, "conf": [0.094575, 0.991927, 0.28886, 0.155166, 0.769077], "most": 0, "first": null}, {"slot": 2, "conf": [0.114303, 0.153188, 0.076808, 0.021566, 0.036259], "most": 3, "first": 72}], "others": [1], "ks": [2, 1, 0, 0]}, {"op": "record", "slot": 5, "rows": 3, "keep": 3, "tokens": null, "bonus": 29}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 36}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 53}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 26}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.374031, 0.138692, 0.597914, 0.161695, 0.957227], "most": 0, "first": null}, {"slot": 0, "conf": [0.238228, 0.183075, 0.268812, 0.219342, 0.145901], "most": 0, "first": 15}, {"slot": 5, "conf": [0.811979, 0.126027, 0.627783, 0.884909, 0.48866], "most": 2, "first": 39}], "others": [], "ks": [0, 0, 1]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 72}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 15}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 64}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.047593, 0.240786, 0.053323, 0.02022, 0.007945], "most": 3, "first": null}, {"slot": 0, "conf": [0.174132, 0.176077, 0.247202, 0.002011, 0.227259], "most": 1, "first": 65}, {"slot": 4, "conf": [0.397509, 0.048656, 0.004385, 0.100349, 0.606465], "most": 1, "first": 98}], "others": [6], "ks": [1, 1, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 63}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 65}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 94}, {"op": "check", "kept": [15.186620843747836, 10.400801065526633, 0.9409, 0.0, 0.0], "prob": [11.676591249311945, 11.68108559618556, 2.617338853956, 0.96402, 0.0], "rates": [0.05261183446029408, 0.05719964822216344, 0.055987682709803845, 0.056164164897988145, 0.04676758071304971, 0.05834603575153341], "joint": [0.04278108142795472, 0.0821475186885605, 0.08697428328578301, 0.11041746294644753, 0.12932335148650007], "zero": 30, "reached": [137, 22, 4, 1, 0], "statkept": [69, 14, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.106862, 0.042508, 0.147064, 0.085983, 0.196846], "most": 0, "first": null}, {"slot": 0, "conf": [0.002237, 0.053522, 0.274176, 0.123005, 0.009632], "most": 4, "first": 87}, {"slot": 1, "conf": [0.343748, 0.188356, 0.039771, 0.6402, 0.567998], "most": 4, "first": null}], "others": [], "ks": [0, 0, 1]}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 55}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 87}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 7}, {"op": "tree", "slot": 4, "conf": [0.04602, 0.200225, 0.070744, 0.29352, 0.175982], "most": 0, "first": 47, "sibs": [0.295798, 0.269812, 0.358699], "k": 0, "lens": [0, 0, 0]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 47}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.275195, 0.004372, 0.219945, 0.257209, 0.22306], "most": 5, "first": null}, {"slot": 4, "conf": [0.923565, 0.874683, 0.914905, 0.715915, 0.728384], "most": 5, "first": 41}, {"slot": 3, "conf": [0.523768, 0.649641, 0.016318, 0.511061, 0.204266], "most": 5, "first": 31}], "others": [], "ks": [1, 5, 2]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 42}, {"op": "record", "slot": 4, "rows": 6, "keep": 3, "tokens": null, "bonus": 92}, {"op": "record", "slot": 3, "rows": 3, "keep": 2, "tokens": 2, "bonus": 95}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.254822, 0.106487, 0.122224, 0.193714, 0.25405], "most": 4, "first": null}, {"slot": 2, "conf": [0.267147, 0.092619, 0.278107, 0.007842, 0.2458], "most": 0, "first": 10}, {"slot": 3, "conf": [0.003719, 0.156766, 0.164122, 0.278414, 0.034431], "most": 5, "first": null}, {"slot": 4, "conf": [0.055395, 0.231019, 0.019805, 0.014388, 0.260347], "most": 0, "first": 37}], "others": [], "ks": [1, 0, 1, 0]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 85}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 10}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 44}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 37}, {"op": "check", "kept": [16.70110095257278, 10.756113722554007, 0.912673, 0.0, 0.0], "prob": [11.561787522499815, 12.488816947450994, 3.4537236883373197, 0.96402, 0.0], "rates": [0.053173658232091456, 0.054937645772048724, 0.0544386330040494, 0.054446820305694144, 0.04842274997639391, 0.057397528529948005], "joint": [0.04278108142795472, 0.0821475186885605, 0.08607024766713767, 0.11137772077929801, 0.12932335148650007], "zero": 31, "reached": [143, 24, 5, 1, 0], "statkept": [73, 15, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.215107, 0.340477, 0.395869, 0.594255, 0.468855], "most": 1, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 79}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.047069, 0.142792, 0.106813, 0.152606, 0.061953], "most": 1, "first": 2}, {"slot": 3, "conf": [0.130122, 0.249099, 0.220534, 0.272895, 0.203125], "most": 5, "first": 89}, {"slot": 0, "conf": [0.230972, 0.244631, 0.284217, 0.262494, 0.288258], "most": 0, "first": 49}, {"slot": 2, "conf": [0.20455, 0.531616, 0.328561, 0.582634, 0.05102], "most": 1, "first": 97}], "others": [], "ks": [0, 0, 0, 0]}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 2}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 89}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 49}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 74}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.646201, 0.14925, 0.477329, 0.589162, 0.96328], "most": 4, "first": 57}], "others": [6], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": 2, "bonus": 57}, {"op": "tree", "slot": 4, "conf": [0.087364, 0.162238, 0.083718, 0.392547, 0.537615], "most": 3, "first": 51, "sibs": [0.191911, 0.212016], "k": 0, "lens": [0, 0]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 51}, {"op": "check", "kept": [17.568249380686176, 10.756113722554007, 0.912673, 0.0, 0.0], "prob": [10.882433781625906, 12.488816947450994, 3.4537236883373197, 0.96402, 0.0], "rates": [0.051848150906399944, 0.05591336224520106, 0.05497026320184869, 0.05353468582122208, 0.04788038112783378, 0.057397528529948005], "joint": [0.044725676038316294, 0.0821475186885605, 0.08607024766713767, 0.11130838041898238, 0.12932335148650007], "zero": 35, "reached": [145, 24, 5, 1, 0], "statkept": [74, 15, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.529989, 0.195358, 0.165336, 0.236186, 0.205925], "most": 2, "first": null}, {"slot": 4, "conf": [0.27202, 0.206482, 0.065416, 0.030109, 0.15724], "most": 2, "first": 80}, {"slot": 5, "conf": [0.033486, 0.274897, 0.063067, 0.183475, 0.201323], "most": 1, "first": null}, {"slot": 2, "conf": [0.550906, 0.2518, 0.300584, 0.551955, 0.686593], "most": 4, "first": 53}], "others": [], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 57}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 54}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 39}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 56}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.283366, 0.066561, 0.036031, 0.138068, 0.020369], "most": 3, "first": null}, {"slot": 3, "conf": [0.211614, 0.264106, 0.261401, 0.289731, 0.205078], "most": 2, "first": 38}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 14}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 49}, {"op": "tree", "slot": 5, "conf": [0.220437, 0.143076, 0.274349, 0.15876, 0.007857], "most": 4, "first": null, "sibs": [0.328172], "k": 1, "lens": [1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 82}, {"op": "tree", "slot": 4, "conf": [0.102605, 0.043627, 0.051543, 0.021957, 0.358359], "most": 1, "first": 19, "sibs": [0.419711, 0.20533], "k": 1, "lens": [1, 0]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 19}, {"op": "check", "kept": [16.401604819687208, 10.756113722554007, 0.912673, 0.0, 0.0], "prob": [10.4744679638617, 12.488816947450994, 3.4537236883373197, 0.96402, 0.0], "rates": [0.051848150906399944, 0.05591336224520106, 0.0528560223094699, 0.05341423809930775, 0.046890719047862635, 0.05349868673461849], "joint": [0.044725676038316294, 0.0765493980920828, 0.08607024766713767, 0.10974045285495275, 0.12932335148650007], "zero": 35, "reached": [153, 24, 5, 1, 0], "statkept": [77, 15, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.174935, 0.640518, 0.030138, 0.536909, 0.560617], "most": 3, "first": null}, {"slot": 4, "conf": [0.261222, 0.341349, 0.677854, 0.046249, 0.147427], "most": 3, "first": null}, {"slot": 3, "conf": [0.804262, 0.742436, 0.895757, 0.331868, 0.799929], "most": 1, "first": 20}, {"slot": 0, "conf": [0.265066, 0.090355, 0.375254, 0.149226, 0.327961], "most": 2, "first": 91}], "others": [], "ks": [1, 1, 1, 0]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 11}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 21}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 20}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 2}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.178227, 0.035844, 0.278509, 0.440366, 0.486178], "most": 1, "first": null}, {"slot": 0, "conf": [0.561348, 0.883976, 0.23075, 0.065949, 0.390583], "most": 2, "first": 82}, {"slot": 3, "conf": [0.793739, 0.123846, 0.304896, 0.178476, 0.825605], "most": 3, "first": null}], "others": [], "ks": [1, 2, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 2}, {"op": "record", "slot": 0, "rows": 3, "keep": 3, "tokens": null, "bonus": 67}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 96}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.714487, 0.011112, 0.445852, 0.781766, 0.154913], "most": 5, "first": 16}, {"slot": 4, "conf": [0.087718, 0.266205, 0.079492, 0.052979, 0.04508], "most": 4, "first": null}, {"slot": 0, "conf": [0.03857, 0.292832, 0.229863, 0.214309, 0.284063], "most": 3, "first": 91}], "others": [], "ks": [1, 1, 0]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 75}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 67}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 9}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.247993, 0.348784, 0.215247, 0.529792, 0.980252], "most": 4, "first": null}, {"slot": 5, "conf": [0.815788, 0.409352, 0.977567, 0.56363, 0.084757], "most": 2, "first": 88}, {"slot": 3, "conf": [0.123184, 0.253608, 0.66388, 0.081011, 0.543843], "most": 1, "first": null}, {"slot": 1, "conf": [0.204509, 0.049342, 0.033846, 0.222433, 0.152751], "most": 4, "first": 46}], "others": [], "ks": [1, 1, 1, 0]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 73}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 39}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 16}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 46}, {"op": "check", "kept": [17.37846531594439, 11.433430310877386, 0.912673, 0.0, 0.0], "prob": [11.147154089600825, 12.998128439027465, 3.4537236883373197, 0.96402, 0.0], "rates": [0.05232090791044563, 0.05557115447611025, 0.0528560223094699, 0.05500794441658786, 0.0524526876757165, 0.052991947431988144], "joint": [0.044725676038316294, 0.07348742216839949, 0.09100837277029487, 0.11083785738350228, 0.12932335148650007], "zero": 38, "reached": [164, 25, 5, 1, 0], "statkept": [84, 16, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.036047, 0.391975, 0.101876, 0.117581, 0.190727], "most": 1, "first": 11}], "others": [3], "ks": [0]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 52}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.287267, 0.03774, 0.649003, 0.517123, 0.275704], "most": 3, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 41}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.270636, 0.472562, 0.000157, 0.535346, 0.085586], "most": 2, "first": null}], "others": [2], "ks": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 33}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.002583, 0.217894, 0.879401, 0.559414, 0.584341], "most": 4, "first": 75}, {"slot": 4, "conf": [0.363531, 0.057487, 0.474556, 0.275041, 0.536683], "most": 3, "first": null}, {"slot": 0, "conf": [0.127602, 0.282174, 0.000873, 0.212685, 0.206033], "most": 5, "first": null}, {"slot": 2, "conf": [0.241076, 0.160747, 0.046097, 0.573442, 0.336916], "most": 2, "first": 1}], "others": [1], "ks": [0, 1, 1, 0]}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 18}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 26}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 16}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 60}, {"op": "check", "kept": [15.785528679606944, 11.433430310877386, 0.912673, 0.0, 0.0], "prob": [10.232267158937878, 12.998128439027465, 3.4537236883373197, 0.96402, 0.0], "rates": [0.04704764279547684, 0.05557115447611025, 0.0507417814170911, 0.052892254246719096, 0.04872871032052899, 0.0522506442504436], "joint": [0.04323542474874323, 0.07348742216839949, 0.09100837277029487, 0.11014502428197126, 0.12932335148650007], "zero": 41, "reached": [168, 25, 5, 1, 0], "statkept": [86, 16, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.328617, 0.456396, 0.289989, 0.433154, 0.25693], "most": 3, "first": 83}, {"slot": 1, "conf": [0.056482, 0.177023, 0.49427, 0.676772, 0.956136], "most": 2, "first": null}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 83}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 13}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.479969, 0.046502, 0.288914, 0.178431, 0.444379], "most": 4, "first": null}, {"slot": 5, "conf": [0.162309, 0.007077, 0.242024, 0.604404, 0.469211], "most": 3, "first": 98}], "others": [], "ks": [1, 0]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 8}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 38}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.673368, 0.838029, 0.368597, 0.300311, 0.159951], "most": 1, "first": 67}], "others": [], "ks": [1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 67}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.163322, 0.054628, 0.008312, 0.191005, 0.233665], "most": 3, "first": 24}, {"slot": 5, "conf": [0.233753, 0.677443, 0.622984, 0.495916, 0.504708], "most": 4, "first": 91}, {"slot": 3, "conf": [0.384026, 0.60435, 0.373311, 0.212442, 0.430806], "most": 2, "first": null}], "others": [], "ks": [0, 0, 1]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 53}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 91}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 23}, {"op": "check", "kept": [15.174775282784243, 11.433430310877386, 0.912673, 0.0, 0.0], "prob": [10.266893835839316, 12.998128439027465, 3.4537236883373197, 0.96402, 0.0], "rates": [0.04704764279547684, 0.0564384135968201, 0.0507417814170911, 0.052892254246719096, 0.04872871032052899, 0.05026662254374243], "joint": [0.04323542474874323, 0.07187830238743016, 0.09100837277029487, 0.10644233850456357, 0.12932335148650007], "zero": 44, "reached": [173, 25, 5, 1, 0], "statkept": [88, 16, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.171974, 0.008055, 0.246625, 0.285767, 0.07266], "most": 2, "first": 23}], "others": [], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 33}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.363534, 0.328317, 0.376939, 0.101066, 0.038045], "most": 4, "first": 17}, {"slot": 3, "conf": [0.199432, 0.425021, 0.04878, 0.077356, 0.386528], "most": 0, "first": 70}, {"slot": 0, "conf": [0.251516, 0.110607, 0.147165, 0.268256, 0.140811], "most": 1, "first": 87}, {"slot": 1, "conf": [0.087102, 0.269293, 0.265216, 0.007011, 0.114904], "most": 4, "first": null}], "others": [1], "ks": [1, 0, 0, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 44}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 21}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 63}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 81}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.862459, 0.164132, 0.79667, 0.8909, 0.355842], "most": 0, "first": 4}, {"slot": 3, "conf": [0.520951, 0.460133, 0.261789, 0.137805, 0.113929], "most": 3, "first": 61}, {"slot": 5, "conf": [0.971315, 0.172695, 0.676731, 0.321061, 0.603359], "most": 2, "first": 90}, {"slot": 4, "conf": [0.263268, 0.191306, 0.219844, 0.284069, 0.05296], "most": 5, "first": null}], "others": [], "ks": [0, 1, 1, 1]}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 49}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": 3, "bonus": 97}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": 3, "bonus": 56}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 64}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.966685, 0.307471, 0.46691, 0.830275, 0.030461], "most": 3, "first": null}, {"slot": 4, "conf": [0.088337, 0.004488, 0.679618, 0.426813, 0.257562], "most": 4, "first": null}, {"slot": 3, "conf": [0.048864, 0.255361, 0.126477, 0.090448, 0.19706], "most": 0, "first": 87}], "others": [], "ks": [1, 1, 0]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 45}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 25}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 87}, {"op": "check", "kept": [13.477235478076194, 11.433430310877386, 0.912673, 0.0, 0.0], "prob": [11.139444984660306, 12.998128439027465, 3.4537236883373197, 0.96402, 0.0], "rates": [0.046651473836687184, 0.054689636313918516, 0.0494885466289693, 0.05010609420817121, 0.050354580168687844, 0.05084207181442644], "joint": [0.04323542474874323, 0.07187830238743016, 0.08951761044388472, 0.10832903590543326, 0.12932335148650007], "zero": 45, "reached": [181, 25, 5, 1, 0], "statkept": [90, 16, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.31101, 0.514695, 0.624032, 0.000543, 0.122469], "most": 4, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 22}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.34288, 0.506347, 0.090903, 0.134402, 0.400342], "most": 1, "first": 32}, {"slot": 0, "conf": [0.832204, 0.9367, 0.944026, 0.429575, 0.438246], "most": 0, "first": null}, {"slot": 3, "conf": [0.065213, 0.097405, 0.106932, 0.116736, 0.191456], "most": 2, "first": null}], "others": [], "ks": [0, 0, 1]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": 3, "bonus": 84}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 4}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 32}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.621621, 0.692254, 0.636801, 0.063289, 0.297869], "most": 2, "first": 84}, {"slot": 5, "conf": [0.090324, 0.088841, 0.222008, 0.117763, 0.129127], "most": 1, "first": 92}], "others": [4], "ks": [2, 0]}, {"op": "record", "slot": 0, "rows": 3, "keep": 2, "tokens": null, "bonus": 8}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 59}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.220098, 0.095321, 0.354834, 0.302084, 0.113748], "most": 5, "first": 44}], "others": [5], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 45}, {"op": "check", "kept": [12.167059857073374, 11.090427401551064, 0.912673, 0.0, 0.0], "prob": [10.801583513943545, 13.300438585856641, 3.4537236883373197, 0.96402, 0.0], "rates": [0.04295299585842693, 0.053184970389845834, 0.050619650548342866, 0.05010609420817121, 0.05508462905798929, 0.04920512332301452], "joint": [0.04323542474874323, 0.07142162464441149, 0.09074387908010233, 0.10832903590543326, 0.12932335148650007], "zero": 47, "reached": [185, 26, 5, 1, 0], "statkept": [91, 16, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.115693, 0.030042, 0.073082, 0.163435, 0.058366], "most": 1, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 19}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.112266, 0.430243, 0.605724, 0.521424, 0.238561], "most": 4, "first": 54}], "others": [2], "ks": [0]}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 54}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.699608, 0.55322, 0.598259, 0.079482, 0.480443], "most": 1, "first": 1}, {"slot": 5, "conf": [0.282161, 0.28446, 0.438141, 0.662048, 0.672732], "most": 3, "first": null}, {"slot": 1, "conf": [0.013662, 0.377849, 0.485785, 0.41094, 0.51672], "most": 2, "first": 88}], "others": [8], "ks": [1, 1, 0]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 1}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 81}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": 1, "bonus": 51}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.278998, 0.003367, 0.28098, 0.100015, 0.240296], "most": 2, "first": null}, {"slot": 4, "conf": [0.479489, 0.088098, 0.632062, 0.061533, 0.254126], "most": 5, "first": 38}, {"slot": 0, "conf": [0.404189, 0.019135, 0.114389, 0.206244, 0.083906], "most": 3, "first": 70}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 46}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": 2, "bonus": 52}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 70}, {"op": "check", "kept": [13.89083405227235, 11.090427401551064, 0.912673, 0.0, 0.0], "prob": [10.647505530371904, 13.300438585856641, 3.4537236883373197, 0.96402, 0.0], "rates": [0.04216852763037288, 0.05293362564457717, 0.048418796176675785, 0.05136381633155344, 0.05622305978385357, 0.0476162648503226], "joint": [0.04323542474874323, 0.07142162464441149, 0.09144378767484053, 0.10832903590543326, 0.12932335148650007], "zero": 49, "reached": [191, 26, 5, 1, 0], "statkept": [95, 16, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.378783, 0.509874, 0.181243, 0.075197, 0.220583], "most": 2, "first": 54}], "others": [2], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 54}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.282558, 0.085446, 0.23998, 0.06974, 0.135768], "most": 0, "first": null}], "others": [], "ks": [0]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 69}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.20523, 0.149783, 0.185509, 0.189708, 0.037461], "most": 4, "first": 11}], "others": [8], "ks": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 91}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.015273, 0.04431, 0.137584, 0.245815, 0.069017], "most": 3, "first": null}, {"slot": 1, "conf": [0.273608, 0.291791, 0.097568, 0.037514, 0.036612], "most": 2, "first": null}], "others": [2], "ks": [1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 59}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 25}, {"op": "check", "kept": [14.210128511379875, 11.090427401551064, 0.912673, 0.0, 0.0], "prob": [10.253388824432482, 13.300438585856641, 3.4537236883373197, 0.96402, 0.0], "rates": [0.04295299585842693, 0.05415627768739697, 0.048418796176675785, 0.05136381633155344, 0.05622305978385357, 0.04545188917530794], "joint": [0.04552364062450518, 0.07142162464441149, 0.09092439390904858, 0.10832903590543326, 0.12932335148650007], "zero": 49, "reached": [195, 26, 5, 1, 0], "statkept": [97, 16, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.011852, 0.055776, 0.145157, 0.008655, 0.005536], "most": 4, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": 2, "bonus": 48}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.124416, 0.536872, 0.350202, 0.865996, 0.780697], "most": 3, "first": 85}], "others": [6], "ks": [0]}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 85}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.329269, 0.171583, 0.138901, 0.284443, 0.126691], "most": 5, "first": null}, {"slot": 5, "conf": [0.107111, 0.067772, 0.229605, 0.222859, 0.097798], "most": 1, "first": null}, {"slot": 4, "conf": [0.039296, 0.147764, 0.101941, 0.198103, 0.161133], "most": 3, "first": null}, {"slot": 2, "conf": [0.832126, 0.443583, 0.31721, 0.56736, 0.471412], "most": 2, "first": null}], "others": [], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 47}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 35}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 72}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 98}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.258224, 0.477774, 0.43099, 0.694185, 0.128008], "most": 1, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": 3, "bonus": 43}, {"op": "check", "kept": [15.222947085030151, 11.090427401551064, 0.912673, 0.0, 0.0], "prob": [9.892891530114802, 13.300438585856641, 3.4537236883373197, 0.96402, 0.0], "rates": [0.04295299585842693, 0.05001815876633474, 0.05175703895729819, 0.054130011627126495, 0.05410939187536659, 0.04678302881834576], "joint": [0.04520067132823156, 0.07142162464441149, 0.09092439390904858, 0.10868322984150175, 0.12932335148650007], "zero": 50, "reached": [201, 26, 5, 1, 0], "statkept": [100, 16, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.491958, 0.634324, 0.340134, 0.415412, 0.009847], "most": 5, "first": 10}, {"slot": 3, "conf": [0.002816, 0.082486, 0.146911, 0.601351, 0.106442], "most": 1, "first": 19}, {"slot": 0, "conf": [0.590889, 0.880352, 0.039701, 0.310715, 0.062524], "most": 2, "first": 87}], "others": [], "ks": [2, 0, 2]}, {"op": "record", "slot": 5, "rows": 3, "keep": 3, "tokens": null, "bonus": 28}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 27}, {"op": "record", "slot": 0, "rows": 3, "keep": 1, "tokens": null, "bonus": 87}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.126751, 0.143523, 0.197235, 0.116279, 0.05218], "most": 0, "first": null}, {"slot": 1, "conf": [0.22729, 0.016557, 0.265714, 0.093951, 0.038121], "most": 2, "first": 99}], "others": [], "ks": [0, 0]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 50}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 73}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.042811, 0.251349, 0.059651, 0.231985, 0.297388], "most": 3, "first": null}, {"slot": 2, "conf": [0.153456, 0.293828, 0.127661, 0.111807, 0.122466], "most": 4, "first": 36}, {"slot": 1, "conf": [0.652005, 0.073481, 0.502948, 0.674306, 0.4418], "most": 3, "first": null}], "others": [], "ks": [1, 0, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 77}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 11}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 8}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.08344, 0.226305, 0.026137, 0.009577, 0.181048], "most": 4, "first": null}], "others": [5], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 29}, {"op": "check", "kept": [14.65153953374494, 11.757714579504531, 0.912673, 0.0, 0.0], "prob": [9.761306052583365, 13.53574942828094, 3.4537236883373197, 0.96402, 0.0], "rates": [0.04386186145353822, 0.04832891782765908, 0.05002671881573113, 0.05731644408780879, 0.05410939187536659, 0.05045620821595257], "joint": [0.04716591790771988, 0.06939314919989699, 0.08969568588325062, 0.10868322984150175, 0.12932335148650007], "zero": 53, "reached": [206, 27, 5, 1, 0], "statkept": [103, 17, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.254768, 0.713797, 0.42705, 0.370549, 0.893434], "most": 1, "first": 14}, {"slot": 0, "conf": [0.450826, 0.619409, 0.398536, 0.898344, 0.226311], "most": 4, "first": 31}, {"slot": 1, "conf": [0.026885, 0.717797, 0.558146, 0.988965, 0.099638], "most": 4, "first": null}, {"slot": 4, "conf": [0.394539, 0.805912, 0.591638, 0.836426, 0.818031], "most": 2, "first": null}], "others": [], "ks": [0, 1, 1, 1]}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 85}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 6}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 53}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 57}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.453414, 0.091111, 0.021071, 0.048731, 0.31695], "most": 4, "first": 74}, {"slot": 3, "conf": [0.406346, 0.639991, 0.51302, 0.181401, 0.864507], "most": 4, "first": 45}, {"slot": 1, "conf": [0.242655, 0.018599, 0.018729, 0.22605, 0.136006], "most": 4, "first": null}, {"slot": 4, "conf": [0.614844, 0.504454, 0.479762, 0.591674, 0.557451], "most": 0, "first": 47}], "others": [], "ks": [1, 1, 1, 0]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 74}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 83}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": 1, "bonus": 87}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 47}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.171031, 0.211827, 0.00936, 0.192792, 0.013195], "most": 2, "first": null}, {"slot": 1, "conf": [0.846459, 0.869031, 0.897166, 0.917231, 0.007405], "most": 3, "first": 1}, {"slot": 0, "conf": [0.81417, 0.791555, 0.972986, 0.712027, 0.329058], "most": 2, "first": null}], "others": [1], "ks": [1, 3, 2]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 54}, {"op": "record", "slot": 1, "rows": 4, "keep": 2, "tokens": null, "bonus": 18}, {"op": "record", "slot": 0, "rows": 3, "keep": 3, "tokens": null, "bonus": 89}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.270551, 0.246496, 0.256658, 0.067082, 0.234398], "most": 4, "first": null}, {"slot": 4, "conf": [0.315127, 0.427885, 0.187068, 0.624076, 0.077203], "most": 4, "first": null}, {"slot": 5, "conf": [0.029175, 0.514396, 0.453567, 0.56379, 0.250541], "most": 2, "first": 42}], "others": [], "ks": [1, 1, 0]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 91}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 86}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 88}, {"op": "check", "kept": [15.69495174396294, 12.062833647855813, 0.912673, 0.0, 0.0], "prob": [10.503155597659397, 14.370301707069537, 3.4537236883373197, 0.96402, 0.0], "rates": [0.04935622317596566, 0.04686654538172801, 0.049107800550900235, 0.05731644408780879, 0.05143084908045928, 0.05210391256963471], "joint": [0.04716591790771988, 0.06939314919989699, 0.0916844578174098, 0.10308100149915629, 0.12932335148650007], "zero": 55, "reached": [217, 29, 5, 1, 0], "statkept": [110, 18, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.196314, 0.266677, 0.150526, 0.161416, 0.264429], "most": 5, "first": 40}, {"slot": 1, "conf": [0.887395, 0.159303, 0.677521, 0.162638, 0.787288], "most": 2, "first": null}], "others": [], "ks": [0, 1]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": 1, "bonus": 40}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 13}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.258104, 0.412785, 0.287337, 0.331728, 0.131815], "most": 1, "first": null}, {"slot": 3, "conf": [0.563119, 0.50072, 0.1544, 0.170939, 0.551101], "most": 2, "first": null}], "others": [], "ks": [1, 2]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 23}, {"op": "record", "slot": 3, "rows": 3, "keep": 2, "tokens": null, "bonus": 56}, {"op": "reset", "slot": 1}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.234901, 0.116943, 0.183734, 0.148339, 0.108127], "most": 5, "first": null}, {"slot": 1, "conf": [0.989253, 0.422797, 0.216894, 0.776282, 0.461817], "most": 5, "first": null}, {"slot": 3, "conf": [0.084955, 0.038051, 0.136949, 0.201178, 0.096359], "most": 4, "first": 78}], "others": [], "ks": [1, 1, 0]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": 3, "bonus": 61}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": 2, "bonus": 75}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 43}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.40143, 0.39168, 0.009904, 0.506259, 0.173223], "most": 0, "first": 2}, {"slot": 5, "conf": [0.001683, 0.089596, 0.129855, 0.038897, 0.089491], "most": 3, "first": 8}, {"slot": 1, "conf": [0.51602, 0.522765, 0.151686, 0.116345, 0.363741], "most": 4, "first": 74}, {"slot": 2, "conf": [0.845735, 0.558816, 0.885089, 0.657578, 0.40575], "most": 4, "first": null}], "others": [], "ks": [0, 0, 1, 2]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 13}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 8}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": 1, "bonus": 75}, {"op": "record", "slot": 2, "rows": 3, "keep": 3, "tokens": null, "bonus": 14}, {"op": "check", "kept": [15.107939150033465, 12.349920179267533, 0.912673, 0.0, 0.0], "prob": [11.77051037031237, 14.565531276181726, 3.4537236883373197, 0.96402, 0.0], "rates": [0.04935622317596566, 0.04969684921975947, 0.05239980175408336, 0.05558174073118891, 0.05143084908045928, 0.053736052807493814], "joint": [0.04716591790771988, 0.06789808648103833, 0.0911870149690684, 0.10308100149915629, 0.12932335148650007], "zero": 58, "reached": [224, 31, 5, 1, 0], "statkept": [112, 19, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.328742, 0.808071, 0.131342, 0.66051, 0.797345], "most": 2, "first": null}, {"slot": 1, "conf": [0.324448, 0.016402, 0.391808, 0.519508, 0.643871], "most": 5, "first": null}, {"slot": 2, "conf": [0.132142, 0.064818, 0.009875, 0.036347, 0.176217], "most": 1, "first": 33}, {"slot": 5, "conf": [0.61835, 0.930832, 0.641296, 0.987121, 0.575795], "most": 5, "first": null}], "others": [6], "ks": [2, 1, 0, 3]}, {"op": "record", "slot": 4, "rows": 3, "keep": 1, "tokens": 2, "bonus": 36}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 41}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 33}, {"op": "record", "slot": 5, "rows": 4, "keep": 3, "tokens": null, "bonus": 11}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.060815, 0.440954, 0.637304, 0.122397, 0.378974], "most": 1, "first": null}, {"slot": 1, "conf": [0.585535, 0.57587, 0.234579, 0.333211, 0.533172], "most": 5, "first": 8}], "others": [], "ks": [1, 2]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 62}, {"op": "record", "slot": 1, "rows": 3, "keep": 3, "tokens": null, "bonus": 8}, {"op": "tree", "slot": 2, "conf": [0.280444, 0.27365, 0.265232, 0.229753, 0.07628], "most": 5, "first": null, "sibs": [0.047931, 0.458933, 0.312091], "k": 1, "lens": [0, 1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 84}, {"op": "tree", "slot": 0, "conf": [0.985195, 0.851586, 0.55721, 0.022283, 0.082642], "most": 1, "first": null, "sibs": [0.021932], "k": 1, "lens": [0]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 73}, {"op": "check", "kept": [16.408346819327516, 13.59003989667282, 0.8852928099999999, 0.0, 0.0], "prob": [12.285488748021645, 15.183485417759387, 3.9914079776872002, 0.96402, 0.0], "rates": [0.04887180500574775, 0.055495655482970765, 0.05084616492801031, 0.05558174073118891, 0.05361976295775192, 0.05840134656819087], "joint": [0.04716591790771988, 0.06789808648103833, 0.0911870149690684, 0.10138981186679583, 0.12932335148650007], "zero": 59, "reached": [231, 33, 6, 1, 0], "statkept": [116, 21, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.106997, 0.992076, 0.684825, 0.010261, 0.534941], "most": 3, "first": null}, {"slot": 5, "conf": [0.027437, 0.405802, 0.578951, 0.261366, 0.727493], "most": 0, "first": null}, {"slot": 4, "conf": [0.414793, 0.451414, 0.434801, 0.141973, 0.665057], "most": 0, "first": 30}], "others": [6], "ks": [2, 0, 0]}, {"op": "record", "slot": 0, "rows": 3, "keep": 2, "tokens": null, "bonus": 28}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 69}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 6}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.32467, 0.09732, 0.616672, 0.666485, 0.092662], "most": 0, "first": null}, {"slot": 1, "conf": [0.109771, 0.284948, 0.685876, 0.349029, 0.044045], "most": 2, "first": 88}, {"slot": 3, "conf": [0.14916, 0.583113, 0.652933, 0.37206, 0.380131], "most": 3, "first": null}], "others": [4], "ks": [0, 0, 1]}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 86}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": 3, "bonus": 88}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 25}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.213381, 0.276299, 0.060135, 0.03218, 0.102095], "most": 3, "first": 62}, {"slot": 4, "conf": [0.040516, 0.262081, 0.165088, 0.000434, 0.441254], "most": 4, "first": 63}, {"slot": 3, "conf": [0.048491, 0.121306, 0.250833, 0.159007, 0.149053], "most": 1, "first": 39}, {"slot": 0, "conf": [0.259507, 0.247739, 0.292726, 0.039999, 0.061146], "most": 4, "first": 53}], "others": [6], "ks": [1, 0, 0, 1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 23}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 19}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 39}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 85}, {"op": "tree", "slot": 0, "conf": [0.617368, 0.087401, 0.454669, 0.019153, 0.388385], "most": 3, "first": 14, "sibs": [0.487229, 0.393057], "k": 1, "lens": [1, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 32}, {"op": "check", "kept": [17.297080518708707, 13.182338699772636, 0.8852928099999999, 0.0, 0.0], "prob": [11.075244397636629, 15.720056855226606, 3.9914079776872002, 0.96402, 0.0], "rates": [0.047361842416854226, 0.06597647938509921, 0.05084616492801031, 0.05293362564457717, 0.051665771875400134, 0.05517686306800336], "joint": [0.04716591790771988, 0.06789808648103833, 0.09282387092418823, 0.10166554687223735, 0.12932335148650007], "zero": 62, "reached": [236, 34, 6, 1, 0], "statkept": [119, 21, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.363578, 0.488543, 0.077784, 0.553274, 0.396997], "most": 1, "first": null}, {"slot": 1, "conf": [0.640288, 0.051639, 0.337837, 0.595949, 0.435411], "most": 5, "first": null}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 76}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 7}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.613561, 0.635451, 0.141947, 0.533901, 0.862046], "most": 2, "first": 11}, {"slot": 1, "conf": [0.077512, 0.295376, 0.001042, 0.226814, 0.15139], "most": 5, "first": 19}], "others": [], "ks": [2, 0]}, {"op": "record", "slot": 0, "rows": 3, "keep": 1, "tokens": null, "bonus": 9}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 79}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.115432, 0.175923, 0.820465, 0.250628, 0.977467], "most": 3, "first": 97}, {"slot": 5, "conf": [0.216661, 0.653606, 0.638201, 0.640252, 0.798793], "most": 2, "first": 8}], "others": [], "ks": [0, 0]}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 97}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 53}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.236362, 0.158007, 0.001042, 0.060053, 0.099728], "most": 2, "first": 76}, {"slot": 1, "conf": [0.600138, 0.259502, 0.178776, 0.04163, 0.513234], "most": 1, "first": null}, {"slot": 0, "conf": [0.023307, 0.033692, 0.241757, 0.269235, 0.096836], "most": 0, "first": 79}, {"slot": 3, "conf": [0.375141, 0.283066, 0.128237, 0.353039, 0.041065], "most": 0, "first": null}], "others": [], "ks": [0, 1, 0, 0]}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 76}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 19}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 32}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 41}, {"op": "check", "kept": [16.439144994197086, 13.182338699772636, 0.8852928099999999, 0.0, 0.0], "prob": [11.241379685096303, 15.720056855226606, 3.9914079776872002, 0.96402, 0.0], "rates": [0.04763897116392363, 0.05974317900923406, 0.05084616492801031, 0.04941943396777404, 0.051665771875400134, 0.05357988653923226], "joint": [0.04674162205113923, 0.0623712641894626, 0.09282387092418823, 0.10166554687223735, 0.12932335148650007], "zero": 66, "reached": [240, 34, 6, 1, 0], "statkept": [120, 21, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.008051, 0.209136, 0.071354, 0.243479, 0.288998], "most": 4, "first": null}, {"slot": 0, "conf": [0.55273, 0.374815, 0.422044, 0.016018, 0.027199], "most": 1, "first": 62}, {"slot": 1, "conf": [0.972277, 0.468806, 0.583257, 0.738403, 0.001282], "most": 5, "first": null}, {"slot": 4, "conf": [0.334641, 0.464112, 0.55453, 0.59829, 0.168098], "most": 2, "first": null}], "others": [], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 23}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 25}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 39}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 65}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.197199, 0.203682, 0.705558, 0.642496, 0.554263], "most": 2, "first": null}, {"slot": 2, "conf": [0.788604, 0.922486, 0.102261, 0.56636, 0.001264], "most": 1, "first": 90}, {"slot": 3, "conf": [0.064992, 0.12433, 0.000187, 0.197078, 0.065543], "most": 3, "first": null}, {"slot": 1, "conf": [0.406275, 0.109271, 0.996441, 0.469784, 0.635716], "most": 1, "first": null}], "others": [], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 15}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 9}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 57}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 43}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.427851, 0.22235, 0.844458, 0.969369, 0.030731], "most": 3, "first": null}, {"slot": 3, "conf": [0.009374, 0.278317, 0.185331, 0.240152, 0.016294], "most": 5, "first": null}, {"slot": 1, "conf": [0.255953, 0.237805, 0.517706, 0.098637, 0.263588], "most": 3, "first": 84}, {"slot": 0, "conf": [0.014491, 0.172642, 0.047562, 0.206075, 0.194464], "most": 2, "first": null}], "others": [], "ks": [1, 1, 0, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 36}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 87}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 94}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": 3, "bonus": 79}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.247497, 0.1819, 0.066501, 0.196465, 0.04279], "most": 5, "first": null}, {"slot": 2, "conf": [0.315513, 0.140658, 0.197879, 0.450389, 0.850946], "most": 0, "first": 67}], "others": [], "ks": [1, 0]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 19}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 80}, {"op": "check", "kept": [17.497036529597572, 13.182338699772636, 0.8852928099999999, 0.0, 0.0], "prob": [11.022551579160712, 15.720056855226606, 3.9914079776872002, 0.96402, 0.0], "rates": [0.052456793760062626, 0.056538221251021224, 0.05174833920173874, 0.05293362564457717, 0.053358471101941356, 0.05357988653923226], "joint": [0.04674162205113923, 0.0623712641894626, 0.09282387092418823, 0.10733801717408274, 0.12932335148650007], "zero": 67, "reached": [252, 34, 6, 1, 0], "statkept": [128, 21, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.025421, 0.138911, 0.099428, 0.144599, 0.024522], "most": 5, "first": 27}, {"slot": 5, "conf": [0.079347, 0.270944, 0.039582, 0.045266, 0.056487], "most": 4, "first": 84}], "others": [], "ks": [0, 0]}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 27}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 8}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.208442, 0.111795, 0.194599, 0.211901, 0.094282], "most": 4, "first": 16}, {"slot": 2, "conf": [0.091866, 0.158647, 0.212924, 0.194517, 0.265461], "most": 0, "first": null}, {"slot": 5, "conf": [0.491453, 0.087084, 0.775022, 0.600593, 0.936694], "most": 4, "first": null}], "others": [], "ks": [0, 0, 1]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 33}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 28}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 53}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.236116, 0.074517, 0.600846, 0.269156, 0.03609], "most": 4, "first": null}, {"slot": 4, "conf": [0.560679, 0.553015, 0.36167, 0.778777, 0.831346], "most": 0, "first": null}, {"slot": 1, "conf": [0.547653, 0.518567, 0.551368, 0.133484, 0.228516], "most": 4, "first": null}], "others": [], "ks": [1, 0, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": 3, "bonus": 80}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 1}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 9}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.57491, 0.610186, 0.671502, 0.09862, 0.629837], "most": 4, "first": 55}, {"slot": 4, "conf": [0.312364, 0.683418, 0.437585, 0.006397, 0.464669], "most": 0, "first": 46}], "others": [], "ks": [2, 0]}, {"op": "record", "slot": 5, "rows": 3, "keep": 2, "tokens": 3, "bonus": 55}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 46}, {"op": "check", "kept": [17.852950355352593, 12.786868538779457, 0.8852928099999999, 0.0, 0.0], "prob": [10.956708573922548, 15.858641149569808, 3.9914079776872002, 0.96402, 0.0], "rates": [0.055598684576307136, 0.056167427776014064, 0.052154298492088844, 0.05293362564457717, 0.052517530580044706, 0.05776908626422297], "joint": [0.04674162205113923, 0.06090291618388417, 0.09613355822759682, 0.10733801717408274, 0.12932335148650007], "zero": 70, "reached": [256, 35, 6, 1, 0], "statkept": [131, 21, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.422143, 0.62222, 0.12465, 0.375497, 0.131578], "most": 1, "first": 1}], "others": [], "ks": [1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 1}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.591054, 0.432881, 0.008583, 0.427154, 0.299894], "most": 2, "first": 76}], "others": [], "ks": [2]}, {"op": "record", "slot": 0, "rows": 3, "keep": 2, "tokens": null, "bonus": 76}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.190995, 0.690787, 0.123123, 0.901021, 0.295611], "most": 1, "first": null}, {"slot": 5, "conf": [0.054204, 0.025811, 0.068868, 0.055852, 0.199453], "most": 1, "first": null}], "others": [6], "ks": [1, 1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 0}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 27}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.255799, 0.950579, 0.514509, 0.570162, 0.629782], "most": 0, "first": null}, {"slot": 0, "conf": [0.624775, 0.614334, 0.126421, 0.090771, 0.575299], "most": 5, "first": 86}, {"slot": 3, "conf": [0.13586, 0.277715, 0.072358, 0.004263, 0.25488], "most": 1, "first": 83}], "others": [], "ks": [0, 1, 0]}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 61}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 75}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 57}, {"op": "check", "kept": [15.756300661395953, 12.403262482616073, 0.8852928099999999, 0.0, 0.0], "prob": [10.979604195810841, 15.815762915082713, 3.9914079776872002, 0.96402, 0.0], "rates": [0.05669714813344889, 0.05415627768739697, 0.052154298492088844, 0.04902190164869568, 0.052517530580044706, 0.05562949047665915], "joint": [0.04432752257330905, 0.060517668133211494, 0.09613355822759682, 0.10733801717408274, 0.12932335148650007], "zero": 71, "reached": [261, 36, 6, 1, 0], "statkept": [132, 21, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.146643, 0.237372, 0.294962, 0.267961, 0.015986], "most": 4, "first": null}, {"slot": 0, "conf": [0.467987, 0.013483, 0.999891, 0.308061, 0.915498], "most": 3, "first": null}], "others": [4], "ks": [1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 3}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 31}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.225429, 0.173505, 0.079464, 0.199817, 0.013871], "most": 2, "first": 80}, {"slot": 2, "conf": [0.596306, 0.261627, 0.045276, 0.099503, 0.208852], "most": 2, "first": 64}, {"slot": 1, "conf": [0.461049, 0.088128, 0.687459, 0.300085, 0.572919], "most": 1, "first": null}], "others": [], "ks": [0, 1, 1]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 78}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 64}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 20}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.394607, 0.075049, 0.109415, 0.12228, 0.047033], "most": 5, "first": 63}, {"slot": 1, "conf": [0.747005, 0.582998, 0.148566, 0.814659, 0.105358], "most": 0, "first": 71}, {"slot": 3, "conf": [0.200719, 0.004143, 0.272506, 0.035507, 0.115604], "most": 0, "first": null}, {"slot": 0, "conf": [0.074922, 0.239652, 0.094863, 0.012667, 0.087152], "most": 3, "first": 84}], "others": [8], "ks": [0, 0, 0, 0]}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 63}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": 1, "bonus": 71}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 10}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 84}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.289069, 0.248154, 0.293657, 0.184551, 0.147474], "most": 5, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": 3, "bonus": 82}, {"op": "check", "kept": [15.953071667453983, 12.403262482616073, 0.8852928099999999, 0.0, 0.0], "prob": [10.988801983015344, 15.815762915082713, 3.9914079776872002, 0.96402, 0.0], "rates": [0.050082427328311176, 0.05293362564457717, 0.055507875713596444, 0.05293362564457717, 0.052517530580044706, 0.052475648019590905], "joint": [0.04432752257330905, 0.060517668133211494, 0.09368393067389132, 0.10644823926930418, 0.12932335148650007], "zero": 74, "reached": [266, 36, 6, 1, 0], "statkept": [134, 21, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.3526, 0.679401, 0.120636, 0.173566, 0.183382], "most": 1, "first": null}, {"slot": 4, "conf": [0.097586, 0.070741, 0.203928, 0.128814, 0.138899], "most": 3, "first": 52}, {"slot": 5, "conf": [0.670638, 0.363939, 0.411682, 0.069734, 0.26907], "most": 2, "first": null}], "others": [], "ks": [1, 0, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 5}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 8}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 5}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.188998, 0.606815, 0.281119, 0.045647, 0.157841], "most": 3, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 56}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.264097, 0.217201, 0.168904, 0.292453, 0.076868], "most": 5, "first": 60}], "others": [8], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 60}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.230189, 0.141655, 0.100437, 0.074698, 0.138314], "most": 0, "first": null}], "others": [8], "ks": [0]}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 99}, {"op": "check", "kept": [15.554738265273368, 12.403262482616073, 0.8852928099999999, 0.0, 0.0], "prob": [10.916104870878907, 15.815762915082713, 3.9914079776872002, 0.96402, 0.0], "rates": [0.050082427328311176, 0.05293362564457717, 0.05650049003309625, 0.05293362564457717, 0.05076962130477927, 0.05357988653923226], "joint": [0.05010937334374067, 0.060517668133211494, 0.09313049344059282, 0.10644823926930418, 0.12932335148650007], "zero": 75, "reached": [270, 36, 6, 1, 0], "statkept": [136, 21, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.25045, 0.258743, 0.0316, 0.010862, 0.145031], "most": 3, "first": 92}], "others": [], "ks": [1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 95}, {"op": "reset", "slot": 4}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.062698, 0.064759, 0.145025, 0.112431, 0.291514], "most": 3, "first": null}], "others": [4], "ks": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 53}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.241905, 0.343247, 0.409186, 0.055375, 0.057629], "most": 4, "first": 26}], "others": [], "ks": [1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 27}, {"op": "reset", "slot": 4}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.188814, 0.093431, 0.625746, 0.134022, 0.329622], "most": 2, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 68}, {"op": "check", "kept": [16.653170947678383, 12.403262482616073, 0.8852928099999999, 0.0, 0.0], "prob": [10.374982506445074, 15.815762915082713, 3.9914079776872002, 0.96402, 0.0], "rates": [0.050082427328311176, 0.05293362564457717, 0.05650049003309625, 0.05293362564457717, 0.06626246562634595, 0.05572308200080155], "joint": [0.050460451621042006, 0.060517668133211494, 0.09313049344059282, 0.10644823926930418, 0.12932335148650007], "zero": 75, "reached": [274, 36, 6, 1, 0], "statkept": [139, 21, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.547601, 0.037907, 0.146519, 0.524689, 0.747199], "most": 3, "first": 83}, {"slot": 4, "conf": [0.61392, 0.582079, 0.547557, 0.423248, 0.036175], "most": 4, "first": 80}], "others": [], "ks": [1, 2]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 12}, {"op": "record", "slot": 4, "rows": 3, "keep": 3, "tokens": null, "bonus": 80}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.912826, 0.270306, 0.38422, 0.253603, 0.832807], "most": 3, "first": 47}, {"slot": 5, "conf": [0.217437, 0.015517, 0.456944, 0.30025, 0.641405], "most": 5, "first": null}, {"slot": 3, "conf": [0.291657, 0.340857, 0.590372, 0.496668, 0.632066], "most": 3, "first": null}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 47}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 81}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 8}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.159138, 0.077201, 0.23293, 0.185247, 0.21739], "most": 2, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 99}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.280518, 0.29315, 0.270747, 0.128808, 0.175972], "most": 2, "first": null}], "others": [8], "ks": [2]}, {"op": "record", "slot": 1, "rows": 3, "keep": 3, "tokens": 4, "bonus": 73}, {"op": "check", "kept": [17.14040327263164, 13.640229669893463, 0.8852928099999999, 0.0, 0.0], "prob": [11.082007281022879, 15.738817956801325, 3.9914079776872002, 0.96402, 0.0], "rates": [0.050082427328311176, 0.05021647668099656, 0.054327394262592546, 0.05293362564457717, 0.07295238293747981, 0.05143669107766297], "joint": [0.05157750762200946, 0.06309546343617893, 0.09392517044287597, 0.10644823926930418, 0.12932335148650007], "zero": 75, "reached": [281, 38, 6, 1, 0], "statkept": [143, 23, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.514919, 0.413271, 0.555386, 0.204964, 0.390961], "most": 5, "first": 20}, {"slot": 5, "conf": [0.301277, 0.738856, 0.381693, 0.706894, 0.566361], "most": 0, "first": null}], "others": [], "ks": [2, 0]}, {"op": "record", "slot": 1, "rows": 3, "keep": 3, "tokens": null, "bonus": 20}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 97}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.086437, 0.247454, 0.281497, 0.002942, 0.210623], "most": 3, "first": 15}, {"slot": 2, "conf": [0.147075, 0.091046, 0.366192, 0.492937, 0.495027], "most": 0, "first": 64}, {"slot": 5, "conf": [0.507808, 0.778337, 0.44913, 0.21253, 0.624183], "most": 4, "first": 55}, {"slot": 1, "conf": [0.785091, 0.980738, 0.77885, 0.349272, 0.636624], "most": 2, "first": 24}], "others": [3], "ks": [0, 0, 2, 2]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 15}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 63}, {"op": "record", "slot": 5, "rows": 3, "keep": 2, "tokens": null, "bonus": 55}, {"op": "record", "slot": 1, "rows": 3, "keep": 3, "tokens": null, "bonus": 24}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.77932, 0.389583, 0.102369, 0.444683, 0.319407], "most": 1, "first": 3}, {"slot": 0, "conf": [0.888742, 0.600695, 0.345444, 0.22771, 0.400265], "most": 1, "first": 96}, {"slot": 3, "conf": [0.213216, 0.045728, 0.059799, 0.087968, 0.161464], "most": 4, "first": null}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 17}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 96}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 37}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.622114, 0.499805, 0.622779, 0.404923, 0.638918], "most": 4, "first": 31}], "others": [3], "ks": [2]}, {"op": "record", "slot": 1, "rows": 3, "keep": 1, "tokens": null, "bonus": 69}, {"op": "check", "kept": [17.78865892841933, 14.389969333510676, 0.8852928099999999, 0.0, 0.0], "prob": [12.681017551162157, 16.488965774987733, 3.9914079776872002, 0.96402, 0.0], "rates": [0.049236930159466105, 0.05429697941726811, 0.054756955776092235, 0.05357214605552753, 0.0634236062662523, 0.050130557408206586], "joint": [0.054832024927772983, 0.06459773637513558, 0.09168333073150908, 0.1070929402803868, 0.12932335148650007], "zero": 76, "reached": [288, 41, 6, 1, 0], "statkept": [147, 25, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.260208, 0.010483, 0.189121, 0.186395, 0.115513], "most": 2, "first": 71}], "others": [], "ks": [1]}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 71}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.794784, 0.387765, 0.543876, 0.094424, 0.889223], "most": 3, "first": 80}, {"slot": 3, "conf": [0.243327, 0.451471, 0.286623, 0.182544, 0.311385], "most": 1, "first": null}, {"slot": 1, "conf": [0.497359, 0.149525, 0.657558, 0.557262, 0.340242], "most": 5, "first": null}], "others": [], "ks": [2, 1, 1]}, {"op": "record", "slot": 5, "rows": 3, "keep": 1, "tokens": null, "bonus": 95}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 3}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 95}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.209417, 0.145785, 0.059374, 0.109086, 0.043907], "most": 0, "first": 14}, {"slot": 4, "conf": [0.270313, 0.120703, 0.25021, 0.211994, 0.139919], "most": 1, "first": 0}, {"slot": 0, "conf": [0.665758, 0.467714, 0.370822, 0.26188, 0.189757], "most": 1, "first": null}], "others": [], "ks": [0, 0, 1]}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 14}, {"op": "record", "slot": 4, "rows": 1, "keep": 1, "tokens": null, "bonus": 53}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 79}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.433446, 0.504956, 0.659545, 0.36557, 0.010277], "most": 3, "first": 2}], "others": [], "ks": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 2}, {"op": "check", "kept": [17.140897055825494, 14.389969333510676, 0.8852928099999999, 0.0, 0.0], "prob": [13.14820921493382, 16.488965774987733, 3.9914079776872002, 0.96402, 0.0], "rates": [0.051288468916110524, 0.05220863405506549, 0.054756955776092235, 0.05523482533643532, 0.059327620303230064, 0.04739275235563521], "joint": [0.050101037091467786, 0.06459773637513558, 0.09193768612722437, 0.1070929402803868, 0.12932335148650007], "zero": 77, "reached": [294, 41, 6, 1, 0], "statkept": [150, 25, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.539335, 0.963159, 0.175143, 0.247145, 0.370709], "most": 5, "first": 31}, {"slot": 0, "conf": [0.006693, 0.202055, 0.091543, 0.162349, 0.114957], "most": 2, "first": null}], "others": [2], "ks": [2, 1]}, {"op": "record", "slot": 4, "rows": 3, "keep": 2, "tokens": null, "bonus": 3}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 52}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.207738, 0.147988, 0.176855, 0.239517, 0.117974], "most": 1, "first": null}, {"slot": 0, "conf": [0.973769, 0.066826, 0.56877, 0.852697, 0.636175], "most": 2, "first": null}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": 2, "bonus": 9}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 32}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.563286, 0.553648, 0.250036, 0.177793, 0.008596], "most": 4, "first": 26}], "others": [], "ks": [2]}, {"op": "record", "slot": 2, "rows": 3, "keep": 2, "tokens": 2, "bonus": 55}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.352625, 0.259954, 0.585612, 0.189131, 0.194969], "most": 5, "first": 31}, {"slot": 3, "conf": [0.205755, 0.228242, 0.530226, 0.590571, 0.551976], "most": 3, "first": 91}, {"slot": 0, "conf": [0.533028, 0.462885, 0.533705, 0.513614, 0.590886], "most": 5, "first": null}], "others": [1], "ks": [1, 0, 2]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 66}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 91}, {"op": "record", "slot": 0, "rows": 3, "keep": 1, "tokens": null, "bonus": 97}, {"op": "check", "kept": [17.010012897089517, 13.539522145900193, 0.8852928099999999, 0.0, 0.0], "prob": [13.36512368923068, 17.002380127685957, 3.9914079776872002, 0.96402, 0.0], "rates": [0.05030877007634355, 0.05220863405506549, 0.054116419571094906, 0.05523482533643532, 0.05653071030837502, 0.04739275235563521], "joint": [0.04960363025086576, 0.06671282205614848, 0.09193768612722437, 0.1070929402803868, 0.12932335148650007], "zero": 78, "reached": [301, 43, 6, 1, 0], "statkept": [153, 25, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.263051, 0.398628, 0.497415, 0.001637, 0.300145], "most": 0, "first": 22}, {"slot": 5, "conf": [0.589219, 0.672026, 0.374239, 0.090966, 0.176324], "most": 2, "first": 77}], "others": [], "ks": [0, 2]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 22}, {"op": "record", "slot": 5, "rows": 3, "keep": 3, "tokens": null, "bonus": 2}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.73794, 0.778508, 0.130442, 0.487687, 0.128825], "most": 1, "first": null}, {"slot": 1, "conf": [0.803784, 0.41196, 0.788876, 0.04669, 0.306807], "most": 2, "first": 68}, {"slot": 4, "conf": [0.800671, 0.052206, 0.227055, 0.904703, 0.543145], "most": 2, "first": null}, {"slot": 3, "conf": [0.083543, 0.088282, 0.297414, 0.292587, 0.205495], "most": 2, "first": null}], "others": [], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 41}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 10}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 7}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 79}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.628629, 0.281351, 0.417273, 0.090786, 0.65629], "most": 4, "first": null}, {"slot": 4, "conf": [0.0245, 0.168503, 0.786678, 0.522531, 0.301836], "most": 1, "first": null}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": 2, "bonus": 33}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 22}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.079432, 0.17937, 0.088085, 0.178198, 0.064374], "most": 5, "first": 51}, {"slot": 1, "conf": [0.534192, 0.528118, 0.468229, 0.420342, 0.688296], "most": 4, "first": 87}, {"slot": 0, "conf": [0.257761, 0.322585, 0.187909, 0.157643, 0.139501], "most": 4, "first": 14}, {"slot": 4, "conf": [0.171752, 0.218118, 0.035564, 0.245329, 0.016847], "most": 3, "first": null}], "others": [], "ks": [0, 1, 1, 1]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 37}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 97}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 16}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 8}, {"op": "check", "kept": [19.744566322231787, 14.133336481523187, 0.8852928099999999, 0.0, 0.0], "prob": [13.460490376567465, 17.164334723855376, 3.9914079776872002, 0.96402, 0.0], "rates": [0.05300540655146825, 0.05584662448522857, 0.054116419571094906, 0.054797763374490105, 0.056179443054742435, 0.052717147201035784], "joint": [0.04960363025086576, 0.07016333439552826, 0.0897272292231616, 0.11070038614899402, 0.12932335148650007], "zero": 79, "reached": [311, 44, 6, 1, 0], "statkept": [162, 26, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.407409, 0.344336, 0.811306, 0.662892, 0.385295], "most": 2, "first": null}, {"slot": 2, "conf": [0.132544, 0.240796, 0.574836, 0.579636, 0.472261], "most": 2, "first": 9}, {"slot": 1, "conf": [0.40039, 0.135385, 0.613094, 0.472388, 0.574448], "most": 5, "first": null}, {"slot": 5, "conf": [0.182872, 0.021451, 0.05586, 0.037106, 0.09542], "most": 5, "first": 26}], "others": [7], "ks": [1, 1, 1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 37}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 9}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 18}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 44}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.204902, 0.255044, 0.109823, 0.040773, 0.105111], "most": 1, "first": null}, {"slot": 3, "conf": [0.093657, 0.036737, 0.405532, 0.355843, 0.61455], "most": 2, "first": null}, {"slot": 2, "conf": [0.284333, 0.269424, 0.256962, 0.117671, 0.190926], "most": 5, "first": 78}, {"slot": 1, "conf": [0.032991, 0.283964, 0.284595, 0.124139, 0.035267], "most": 1, "first": null}], "others": [6], "ks": [1, 1, 2, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 72}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": 1, "bonus": 40}, {"op": "record", "slot": 2, "rows": 3, "keep": 2, "tokens": null, "bonus": 78}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 19}, {"op": "joint", "asks": [{"slot": 1, "conf": [0.257716, 0.086196, 0.174282, 0.231934, 0.052849], "most": 0, "first": null}, {"slot": 5, "conf": [0.210341, 0.287735, 0.12584, 0.084144, 0.158984], "most": 5, "first": 43}, {"slot": 3, "conf": [0.465621, 0.632283, 0.593286, 0.468852, 0.001531], "most": 0, "first": 3}, {"slot": 0, "conf": [0.528858, 0.184784, 0.534484, 0.302675, 0.212836], "most": 5, "first": null}], "others": [], "ks": [0, 0, 0, 1]}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 54}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 82}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": 2, "bonus": 33}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 34}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.195364, 0.210259, 0.190759, 0.296575, 0.117249], "most": 4, "first": null}, {"slot": 3, "conf": [0.120683, 0.663847, 0.104647, 0.213755, 0.514489], "most": 1, "first": null}, {"slot": 5, "conf": [0.255301, 0.243474, 0.054845, 0.153247, 0.147999], "most": 2, "first": null}, {"slot": 1, "conf": [0.592079, 0.542, 0.393298, 0.322172, 0.957732], "most": 0, "first": 2}], "others": [], "ks": [1, 1, 1, 0]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 61}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": null, "bonus": 4}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 91}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 49}, {"op": "reset", "slot": 1}, {"op": "check", "kept": [18.600431175956793, 13.709336387077492, 0.8852928099999999, 0.0, 0.0], "prob": [11.597548466721081, 16.918828682139715, 3.9914079776872002, 0.96402, 0.0], "rates": [0.05039169041025135, 0.06626246562634595, 0.04978710600540731, 0.055804318807841174, 0.05268862527849702, 0.05220863405506549], "joint": [0.04960363025086576, 0.07016333439552826, 0.0897272292231616, 0.10773586613172217, 0.12932335148650007], "zero": 80, "reached": [323, 45, 6, 1, 0], "statkept": [168, 26, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.578048, 0.573894, 0.605222, 0.215033, 0.37282], "most": 4, "first": null}, {"slot": 2, "conf": [0.090431, 0.105067, 0.206035, 0.003927, 0.273155], "most": 0, "first": 94}, {"slot": 4, "conf": [0.325512, 0.614158, 0.364201, 0.029527, 0.683313], "most": 5, "first": 67}], "others": [], "ks": [2, 0, 2]}, {"op": "record", "slot": 0, "rows": 3, "keep": 1, "tokens": null, "bonus": 84}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 94}, {"op": "record", "slot": 4, "rows": 3, "keep": 2, "tokens": null, "bonus": 67}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.051837, 0.288164, 0.140578, 0.172695, 0.940708], "most": 3, "first": 79}, {"slot": 0, "conf": [0.47508, 0.065202, 0.608441, 0.101111, 0.989113], "most": 5, "first": 19}, {"slot": 5, "conf": [0.117238, 0.373967, 0.236841, 0.654993, 0.168114], "most": 4, "first": 89}], "others": [], "ks": [0, 1, 0]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 79}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 19}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 44}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.047733, 0.180469, 0.278618, 0.176008, 0.098892], "most": 1, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 62}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.039476, 0.644817, 0.019468, 0.107931, 0.073025], "most": 3, "first": null}], "others": [6], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": 3, "bonus": 64}, {"op": "check", "kept": [16.772856131405184, 13.298056295465168, 0.8852928099999999, 0.0, 0.0], "prob": [10.807216090739157, 17.025421821675522, 3.9914079776872002, 0.96402, 0.0], "rates": [0.048292036643157545, 0.06626246562634595, 0.050179227109000184, 0.055804318807841174, 0.05297647153666317, 0.049058817255905725], "joint": [0.04960363025086576, 0.07016333439552826, 0.08640143121429589, 0.1088132247930394, 0.12932335148650007], "zero": 82, "reached": [328, 46, 6, 1, 0], "statkept": [169, 26, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.41539, 0.28558, 0.419502, 0.32786, 0.421292], "most": 0, "first": null}, {"slot": 1, "conf": [0.103278, 0.081821, 0.219668, 0.03691, 0.051514], "most": 3, "first": 28}, {"slot": 0, "conf": [0.265758, 0.176365, 0.257774, 0.165854, 0.113588], "most": 3, "first": null}, {"slot": 5, "conf": [0.126376, 0.109395, 0.131068, 0.026749, 0.043388], "most": 1, "first": 5}], "others": [], "ks": [0, 0, 1, 0]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 10}, {"op": "record", "slot": 1, "rows": 1, "keep": 1, "tokens": null, "bonus": 87}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": 4, "bonus": 11}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": 1, "bonus": 18}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.926923, 0.129476, 0.815321, 0.969966, 0.386996], "most": 5, "first": 34}, {"slot": 1, "conf": [0.643755, 0.133259, 0.067051, 0.609093, 0.28894], "most": 5, "first": 70}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 34}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 70}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.094942, 0.383381, 0.451061, 0.225234, 0.28419], "most": 3, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 67}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.53423, 0.653419, 0.189861, 0.151095, 0.306596], "most": 5, "first": 87}, {"slot": 0, "conf": [0.205111, 0.901603, 0.319142, 0.853353, 0.22187], "most": 2, "first": null}], "others": [], "ks": [2, 1]}, {"op": "record", "slot": 5, "rows": 3, "keep": 2, "tokens": null, "bonus": 87}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 1}, {"op": "check", "kept": [15.948586616721656, 12.899114606601213, 0.8852928099999999, 0.0, 0.0], "prob": [11.104204144924752, 17.168078167025257, 3.9914079776872002, 0.96402, 0.0], "rates": [0.055611968519506415, 0.03587637002888048, 0.04860900901697117, 0.055804318807841174, 0.05297647153666317, 0.05060846131299435], "joint": [0.04824544312511713, 0.06973713458831118, 0.08640143121429589, 0.11065174965367086, 0.12932335148650007], "zero": 84, "reached": [334, 47, 6, 1, 0], "statkept": [172, 26, 1, 0, 0]}, {"op": "tree", "slot": 2, "conf": [0.46912, 0.242693, 0.330301, 0.203022, 0.040638], "most": 1, "first": null, "sibs": [0.371542, 0.169728, 0.483351], "k": 1, "lens": [1, 0, 1]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 83}, {"op": "tree", "slot": 0, "conf": [0.265158, 0.136254, 0.254185, 0.045592, 0.265874], "most": 5, "first": 72, "sibs": [0.376828, 0.404151, 0.319523], "k": 1, "lens": [1, 1, 1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 72}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.019688, 0.266042, 0.246735, 0.042127, 0.113726], "most": 4, "first": 97}, {"slot": 2, "conf": [0.265244, 0.68469, 0.691954, 0.18696, 0.399214], "most": 3, "first": null}, {"slot": 1, "conf": [0.473453, 0.548982, 0.101215, 0.598111, 0.047831], "most": 5, "first": 74}], "others": [], "ks": [0, 1, 1]}, {"op": "record", "slot": 0, "rows": 1, "keep": 1, "tokens": null, "bonus": 97}, {"op": "record", "slot": 2, "rows": 2, "keep": 1, "tokens": null, "bonus": 22}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": 3, "bonus": 24}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.038033, 0.540036, 0.018407, 0.467644, 0.08826], "most": 5, "first": null}, {"slot": 0, "conf": [0.676535, 0.635228, 0.217199, 0.177425, 0.37901], "most": 1, "first": null}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 19}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 11}, {"op": "check", "kept": [17.40408322544602, 12.899114606601213, 0.8852928099999999, 0.0, 0.0], "prob": [11.008880097540848, 17.168078167025257, 3.9914079776872002, 0.96402, 0.0], "rates": [0.0564098741455415, 0.05818689631095077, 0.046688808480385396, 0.055804318807841174, 0.05297647153666317, 0.05060846131299435], "joint": [0.04824544312511713, 0.06973713458831118, 0.08696649521243142, 0.11065174965367086, 0.12932335148650007], "zero": 85, "reached": [340, 47, 6, 1, 0], "statkept": [176, 26, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.50634, 0.09494, 0.530117, 0.613397, 0.223583], "most": 3, "first": 68}, {"slot": 4, "conf": [0.262154, 0.866356, 0.899146, 0.004672, 0.75022], "most": 4, "first": null}], "others": [], "ks": [1, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 92}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 68}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.251394, 0.166929, 0.446, 0.032048, 0.605277], "most": 3, "first": null}, {"slot": 2, "conf": [0.177832, 0.01618, 0.198518, 0.245125, 0.099416], "most": 1, "first": null}, {"slot": 1, "conf": [0.058952, 0.210858, 0.160958, 0.111609, 0.022741], "most": 2, "first": null}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": 2, "bonus": 96}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 56}, {"op": "record", "slot": 1, "rows": 2, "keep": 1, "tokens": null, "bonus": 72}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.593999, 0.204262, 0.430981, 0.976398, 0.111988], "most": 1, "first": 15}, {"slot": 4, "conf": [0.279738, 0.03822, 0.043172, 0.08506, 0.248847], "most": 1, "first": 81}, {"slot": 2, "conf": [0.764857, 0.224407, 0.535407, 0.371162, 0.928585], "most": 4, "first": null}], "others": [1], "ks": [1, 1, 1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 97}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": 3, "bonus": 81}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 93}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.314453, 0.517926, 0.676521, 0.200252, 0.085903], "most": 3, "first": null}], "others": [2], "ks": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 57}, {"op": "check", "kept": [16.833730646021984, 12.899114606601213, 0.8852928099999999, 0.0, 0.0], "prob": [11.253950077190607, 17.168078167025257, 3.9914079776872002, 0.96402, 0.0], "rates": [0.0564098741455415, 0.051673800521905386, 0.050059027937109174, 0.049107800550900235, 0.05459828804034611, 0.05060846131299435], "joint": [0.04824544312511713, 0.07021598730260896, 0.08809468565706849, 0.11065174965367086, 0.12932335148650007], "zero": 85, "reached": [349, 47, 6, 1, 0], "statkept": [180, 26, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.081423, 0.169245, 0.009089, 0.197502, 0.208494], "most": 3, "first": null}], "others": [3], "ks": [1]}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 60}, {"op": "joint", "asks": [{"slot": 0, "conf": [0.278647, 0.192127, 0.053608, 0.24056, 0.018092], "most": 1, "first": null}, {"slot": 4, "conf": [0.189242, 0.04043, 0.203158, 0.073573, 0.10391], "most": 1, "first": 87}, {"slot": 2, "conf": [0.956646, 0.188795, 0.65046, 0.28882, 0.312365], "most": 5, "first": 7}, {"slot": 5, "conf": [0.001282, 0.004512, 0.111212, 0.121232, 0.144366], "most": 1, "first": 3}], "others": [], "ks": [1, 1, 1, 0]}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 29}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 87}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 7}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 3}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.030177, 0.400626, 0.58323, 0.235373, 0.032247], "most": 3, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 5, "rows": 2, "keep": 2, "tokens": null, "bonus": 63}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.157326, 0.300506, 0.195008, 0.316992, 0.922784], "most": 0, "first": 1}, {"slot": 4, "conf": [0.221608, 0.16059, 0.097753, 0.25946, 0.174105], "most": 5, "first": null}], "others": [], "ks": [0, 1]}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 1}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 61}, {"op": "check", "kept": [17.25791058058113, 12.899114606601213, 0.8852928099999999, 0.0, 0.0], "prob": [10.69283081851786, 17.168078167025257, 3.9914079776872002, 0.96402, 0.0], "rates": [0.05703077421312808, 0.051673800521905386, 0.050059027937109174, 0.049107800550900235, 0.049068014401462225, 0.04985034060787071], "joint": [0.049347516175019195, 0.07021598730260896, 0.08809468565706849, 0.10996442650802465, 0.12932335148650007], "zero": 86, "reached": [355, 47, 6, 1, 0], "statkept": [183, 26, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.948328, 0.016267, 0.17564, 0.943569, 0.184615], "most": 4, "first": 8}, {"slot": 2, "conf": [0.255426, 0.299522, 0.050886, 0.178347, 0.166297], "most": 0, "first": null}], "others": [], "ks": [1, 0]}, {"op": "record", "slot": 3, "rows": 2, "keep": 1, "tokens": null, "bonus": 8}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 16}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.28964, 0.265199, 0.292543, 0.238669, 0.018253], "most": 4, "first": 28}, {"slot": 5, "conf": [0.073399, 0.093814, 0.014022, 0.262239, 0.21291], "most": 0, "first": 59}], "others": [], "ks": [1, 0]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": 2, "bonus": 28}, {"op": "record", "slot": 5, "rows": 1, "keep": 1, "tokens": null, "bonus": 40}, {"op": "reset", "slot": 3}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.148144, 0.680981, 0.305312, 0.698549, 0.132904], "most": 4, "first": 77}], "others": [], "ks": [0]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 77}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.192057, 0.076373, 0.287631, 0.243768, 0.197257], "most": 3, "first": 74}, {"slot": 1, "conf": [0.167144, 0.947245, 0.65369, 0.821915, 0.241724], "most": 4, "first": null}], "others": [], "ks": [0, 1]}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 74}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 69}, {"op": "check", "kept": [18.643528028033053, 12.899114606601213, 0.8852928099999999, 0.0, 0.0], "prob": [10.779020202156548, 17.168078167025257, 3.9914079776872002, 0.96402, 0.0], "rates": [0.05703077421312808, 0.05468365505550391, 0.04843991870517991, 0.03911750899702707, 0.049068014401462225, 0.04985034060787071], "joint": [0.049767658763439575, 0.07009059209027668, 0.08809468565706849, 0.10996442650802465, 0.12932335148650007], "zero": 88, "reached": [358, 47, 6, 1, 0], "statkept": [185, 26, 1, 0, 0]}, {"op": "joint", "asks": [{"slot": 5, "conf": [0.992787, 0.629745, 0.569035, 0.880863, 0.322188], "most": 3, "first": null}, {"slot": 3, "conf": [0.617604, 0.66839, 0.002155, 0.467506, 0.068152], "most": 2, "first": 16}, {"slot": 0, "conf": [0.320369, 0.129219, 0.607781, 0.65939, 0.081188], "most": 3, "first": null}, {"slot": 2, "conf": [0.503945, 0.628528, 0.826038, 0.562292, 0.351961], "most": 3, "first": 31}], "others": [], "ks": [2, 2, 1, 2]}, {"op": "record", "slot": 5, "rows": 3, "keep": 2, "tokens": 3, "bonus": 89}, {"op": "record", "slot": 3, "rows": 3, "keep": 2, "tokens": null, "bonus": 69}, {"op": "record", "slot": 0, "rows": 2, "keep": 2, "tokens": null, "bonus": 69}, {"op": "record", "slot": 2, "rows": 3, "keep": 2, "tokens": null, "bonus": 16}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.024737, 0.843054, 0.963577, 0.131699, 0.769439], "most": 4, "first": 55}], "others": [], "ks": [0]}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": 2, "bonus": 0}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.070404, 0.494761, 0.597324, 0.380821, 0.396402], "most": 4, "first": 32}, {"slot": 1, "conf": [0.270383, 0.102821, 0.532247, 0.639869, 0.531692], "most": 2, "first": null}, {"slot": 3, "conf": [0.092156, 0.431204, 0.794179, 0.802464, 0.77875], "most": 1, "first": 48}, {"slot": 4, "conf": [0.243331, 0.294195, 0.158504, 0.099008, 0.158357], "most": 3, "first": null}], "others": [], "ks": [0, 1, 0, 1]}, {"op": "record", "slot": 2, "rows": 1, "keep": 1, "tokens": null, "bonus": 32}, {"op": "record", "slot": 1, "rows": 2, "keep": 2, "tokens": null, "bonus": 46}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 88}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": 2, "bonus": 61}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.094933, 0.33537, 0.061027, 0.423906, 0.110148], "most": 1, "first": null}, {"slot": 3, "conf": [0.161023, 0.443203, 0.759936, 0.66359, 0.117133], "most": 0, "first": null}], "others": [8], "ks": [1, 0]}, {"op": "record", "slot": 2, "rows": 2, "keep": 2, "tokens": null, "bonus": 46}, {"op": "record", "slot": 3, "rows": 1, "keep": 1, "tokens": null, "bonus": 31}, {"op": "check", "kept": [20.701082456115785, 11.772673625350546, 0.8852928099999999, 0.0, 0.0], "prob": [10.614140412920342, 17.538234775433438, 3.9914079776872002, 0.96402, 0.0], "rates": [0.05809387970961073, 0.05666396568430239, 0.04960430234649019, 0.05214813048952195, 0.051112515001523154, 0.0520177467212564], "joint": [0.050195016936170525, 0.07099062344255265, 0.08809468565706849, 0.11071805592248421, 0.12932335148650007], "zero": 91, "reached": [365, 50, 6, 1, 0], "statkept": [192, 26, 1, 0, 0]}, {"op": "tree", "slot": 4, "conf": [0.008481, 0.212385, 0.06497, 0.197974, 0.106964], "most": 4, "first": null, "sibs": [0.30329, 0.419318], "k": 1, "lens": [1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 1, "tokens": null, "bonus": 98}, {"op": "joint", "asks": [{"slot": 2, "conf": [0.475496, 0.463059, 0.290668, 0.671727, 0.253552], "most": 2, "first": 70}], "others": [], "ks": [2]}, {"op": "record", "slot": 2, "rows": 3, "keep": 3, "tokens": null, "bonus": 51}, {"op": "joint", "asks": [{"slot": 4, "conf": [0.077715, 0.135065, 0.1777, 0.295114, 0.002888], "most": 5, "first": null}, {"slot": 0, "conf": [0.696963, 0.087202, 0.750758, 0.716815, 0.28386], "most": 4, "first": null}, {"slot": 5, "conf": [0.225953, 0.202514, 0.554515, 0.272427, 0.20634], "most": 5, "first": null}], "others": [], "ks": [1, 1, 1]}, {"op": "record", "slot": 4, "rows": 2, "keep": 2, "tokens": null, "bonus": 10}, {"op": "record", "slot": 0, "rows": 2, "keep": 1, "tokens": null, "bonus": 51}, {"op": "record", "slot": 5, "rows": 2, "keep": 1, "tokens": null, "bonus": 41}, {"op": "joint", "asks": [{"slot": 3, "conf": [0.203072, 0.264284, 0.04783, 0.118011, 0.223733], "most": 4, "first": null}], "others": [], "ks": [1]}, {"op": "record", "slot": 3, "rows": 2, "keep": 2, "tokens": 3, "bonus": 66}, {"op": "check", "kept": [20.041387967671312, 12.41949341659003, 0.8852928099999999, 0.0, 0.0], "prob": [10.4184652124989, 17.475146732170437, 3.9914079776872002, 0.96402, 0.0], "rates": [0.05609064247824484, 0.05666396568430239, 0.05167114827759394, 0.060821331257298564, 0.05012863006474614, 0.05263423374424587], "joint": [0.05153633495672788, 0.07099062344255265, 0.08470737285607481, 0.11071805592248421, 0.12932335148650007], "zero": 91, "reached": [371, 51, 6, 1, 0], "statkept": [195, 27, 1, 0, 0]}]}], "plans": [{"qm": [0.3619895886477361, 0.584284172714277, 0.46134346620877853, 0.0777838497675416, 0.7952538343731717], "sibs": [0.21614559781898332, 0.0937932710277386, 0.4117919430136082], "cont": [0.26536095138212656, 0.14657284324238928, 0.6927257647754964, 0.47126317261940254], "rate": 0.028764726650874522, "least": 1, "dup": 0.0, "rows": 3, "k": 3, "lens": [1, 0, 2], "ms": 47.237, "e": 2.408281770292658}, {"qm": [0.6038364954070143, 0.7459136535149893], "sibs": [0.42767136554698615, 0.050905561549830655], "cont": [], "rate": 0.03344478574840824, "least": 1, "dup": 0.4, "rows": 3, "k": 2, "lens": [1, 0], "ms": 36.558, "e": 2.4819177473687337}, {"qm": [0.3730606720301667, 0.8521481930870586, 0.1170394672794679, 0.7387889863284639, 0.6115030048696143], "sibs": [0.030013457286270227, 0.19222713125212929, 0.09608709093086076], "cont": [0.8698883988544895, 0.34401533089245884, 0.37942435305408995, 0.7516004792719678], "rate": 0.04658714343604445, "least": 0, "dup": 0.4, "rows": 5, "k": 2, "lens": [0, 0, 0], "ms": 31.977, "e": 1.690963649612517}, {"qm": [0.5012001083931369, 0.34410748034381966, 0.6948187335074153, 0.9141897628446571, 0.42606984547925564], "sibs": [0.46236716841827974], "cont": [0.9910063558698518], "rate": 0.03542792490738285, "least": 1, "dup": 0.4, "rows": 5, "k": 4, "lens": [2], "ms": 47.637, "e": 2.8236260763931043}, {"qm": [0.9540447052403334, 0.38952621956080946, 0.656539846097613, 0.47963576951765496], "sibs": [0.5805567555692873, 0.4763228519460333], "cont": [], "rate": 0.00652127411945763, "least": 0, "dup": 0.4, "rows": 5, "k": 4, "lens": [1, 1], "ms": 48.037, "e": 3.74356148588827}, {"qm": [0.9416048330604755, 0.7898109286989042, 0.8171522366685441, 0.30878323501148575], "sibs": [0.1583353596026499, 0.45305174136752774, 0.39925327856167214], "cont": [0.7428180870706791], "rate": 0.05558114152575793, "least": 1, "dup": 0.4, "rows": 2, "k": 3, "lens": [0, 2, 2], "ms": 51.888999999999996, "e": 4.7784149985406295}, {"qm": [0.11008178951751613], "sibs": [0.03493276425418237, 0.33352423865985664], "cont": [0.9955649829270513, 0.5786018557440481, 0.7643972266282469], "rate": 0.056888490232388636, "least": 0, "dup": 0.0, "rows": 4, "k": 0, "lens": [0, 0], "ms": 21.954, "e": 1.0}, {"qm": [0.29415230479673193], "sibs": [], "cont": [0.29842076351228464, 0.019403333949133406, 0.37392323765444435, 0.24502515969304528], "rate": 0.012953540279758091, "least": 1, "dup": 0.0, "rows": 5, "k": 1, "lens": [], "ms": 26.573, "e": 1.2941523047967318}, {"qm": [0.28978134976063585, 0.18245531889775524], "sibs": [0.578783924480423, 0.2283687383624168], "cont": [0.09467076883183245], "rate": 0.02212256512786449, "least": 0, "dup": 0.4, "rows": 5, "k": 1, "lens": [1, 1], "ms": 36.958, "e": 2.0969340126034757}, {"qm": [0.8968273492348134, 0.9213207716757534, 0.3037543924381926], "sibs": [], "cont": [0.18984402356207908, 0.33902353048251843, 0.8189341891037178], "rate": 0.04487247569430966, "least": 1, "dup": 0.0, "rows": 4, "k": 3, "lens": [], "ms": 36.158, "e": 2.9740748398951635}, {"qm": [0.7703646667297368, 0.24740935011091547, 0.3304900043678688, 0.26142549970309037, 0.8556776068042907], "sibs": [], "cont": [0.2618445211211756, 0.8901135123537158], "rate": 0.07953639487867709, "least": 1, "dup": 0.4, "rows": 1, "k": 1, "lens": [], "ms": 26.573, "e": 1.7703646667297368}, {"qm": [0.6607763075896768, 0.6719057126511094, 0.7554841853090902, 0.03714610380838801], "sibs": [0.0509620434923326, 0.5633128170020755, 0.050267662381515275], "cont": [0.8357403078223191, 0.11410638367499693, 0.38513932987889143], "rate": 0.024476658985735658, "least": 1, "dup": 0.4, "rows": 1, "k": 3, "lens": [0, 1, 0], "ms": 41.251999999999995, "e": 3.0034878975068677}, {"qm": [0.3522619117801574, 0.34792779457823064, 0.1364972225539377, 0.8657773790976483], "sibs": [0.38720008740494927], "cont": [0.16965200105410116], "rate": 0.02876705432500026, "least": 0, "dup": 0.4, "rows": 3, "k": 2, "lens": [1], "ms": 36.558, "e": 1.8620237092646883}, {"qm": [0.5002493287123001, 0.631012287198922, 0.21511391399603141, 0.6106223802024978], "sibs": [0.16744127783706955, 0.3594120212826968, 0.34376446962689283], "cont": [0.5140767701711986, 0.486936488088589, 0.7236898289846803], "rate": 0.018853235594904046, "least": 0, "dup": 0.0, "rows": 2, "k": 4, "lens": [2, 2, 2], "ms": 60.793, "e": 3.2434620073817997}, {"qm": [0.034194922769808844, 0.5813375713159403, 0.5807189641415045], "sibs": [0.43108388131175174], "cont": [0.7918744861918335, 0.6886809652399496, 0.10719466997428562], "rate": 0.032474770212595566, "least": 0, "dup": 0.4, "rows": 5, "k": 1, "lens": [3], "ms": 41.251999999999995, "e": 2.0417342453310394}, {"qm": [0.15536364167859606, 0.9692912697248144, 0.5375367865727112, 0.7091305668227874], "sibs": [0.4594582025243709, 0.0045379948006473375, 0.3117905134613134], "cont": [0.8304336140216517], "rate": 0.07120751194431969, "least": 0, "dup": 0.4, "rows": 1, "k": 0, "lens": [0, 0, 0], "ms": 21.954, "e": 1.0}, {"qm": [0.23216407734100197, 0.9190289786392082, 0.39932210874486673, 0.9377780429710219, 0.5102122309316223], "sibs": [], "cont": [], "rate": 0.05868363193543824, "least": 0, "dup": 0.4, "rows": 3, "k": 0, "lens": [], "ms": 21.954, "e": 1.0}, {"qm": [0.8297268187261622], "sibs": [0.379407607414136, 0.2372197057380784, 0.22755307846368542], "cont": [], "rate": 0.01289836598199904, "least": 0, "dup": 0.4, "rows": 1, "k": 1, "lens": [1, 1, 1], "ms": 42.052, "e": 2.673907210342062}, {"qm": [0.34906894184500004, 0.4194100353646427, 0.10076849650069142, 0.9950646879873664], "sibs": [], "cont": [0.08262132626032859, 0.14792831195127754, 0.6515135398077343], "rate": 0.0482065169370982, "least": 0, "dup": 0.4, "rows": 4, "k": 1, "lens": [], "ms": 26.573, "e": 1.3490689418450001}, {"qm": [0.5133319945549907, 0.4397847408648241, 0.3598580605692925], "sibs": [0.09692239420536228, 0.015890094184422242], "cont": [0.3400025016529016, 0.08450683146361226, 0.4939265293870836], "rate": 0.07587492116199816, "least": 1, "dup": 0.0, "rows": 2, "k": 1, "lens": [0, 0], "ms": 26.573, "e": 1.5133319945549908}, {"qm": [0.3814702385278267, 0.40870168259716444, 0.3604790667974278, 0.2577434437530959, 0.5784911402692497], "sibs": [0.23073021442333538, 0.5060904438449942, 0.5830536097692137], "cont": [0.1640194514475528, 0.9559008298590902, 0.7405457277907412, 0.09925122346072324], "rate": 0.07605244110753054, "least": 0, "dup": 0.0, "rows": 2, "k": 1, "lens": [0, 1, 1], "ms": 36.158, "e": 2.4706142921420344}, {"qm": [0.9552255269136738], "sibs": [0.3828400383229566, 0.5763341844640085, 0.4494930521653586], "cont": [0.8523634577928696], "rate": 0.057306321885138206, "least": 1, "dup": 0.0, "rows": 1, "k": 1, "lens": [1, 1, 1], "ms": 40.852, "e": 3.3638928018659975}, {"qm": [0.8449434351463533, 0.14608797715532185], "sibs": [0.3309459056748343, 0.2800065709020008], "cont": [0.025446989051955193, 0.8424326219574335, 0.026859076243780367, 0.7347373463610899], "rate": 0.031907166356572654, "least": 0, "dup": 0.0, "rows": 3, "k": 1, "lens": [1, 1], "ms": 36.158, "e": 2.455895911723189}, {"qm": [0.3906885310378235, 0.946072505965806, 0.20268247349638369, 0.05086073501745081], "sibs": [0.11103806033255996], "cont": [], "rate": 0.07023629630905276, "least": 1, "dup": 0.0, "rows": 2, "k": 1, "lens": [0], "ms": 26.573, "e": 1.3906885310378234}, {"qm": [0.3173069687752431, 0.2718555647309928, 0.7085792791202419, 0.5909132319763901, 0.5702746879651637], "sibs": [0.2392548270575207], "cont": [0.5454911115104165], "rate": 0.05903084712164601, "least": 1, "dup": 0.0, "rows": 2, "k": 1, "lens": [0], "ms": 26.573, "e": 1.317306968775243}, {"qm": [0.5647568034069126, 0.40509886930326, 0.9345685254463926, 0.19071914447834604, 0.41577430096872736], "sibs": [0.335472250274314], "cont": [0.6906462080551251, 0.7175462455084872], "rate": 0.034562966927787014, "least": 0, "dup": 0.4, "rows": 1, "k": 3, "lens": [1], "ms": 41.251999999999995, "e": 2.3428241726431094}, {"qm": [0.4973235332069401, 0.8431316860254485, 0.8036519647502529], "sibs": [], "cont": [0.9968501691846356, 0.48873031379523735], "rate": 0.013530893212472302, "least": 1, "dup": 0.0, "rows": 4, "k": 3, "lens": [], "ms": 36.158, "e": 2.2536114480261182}, {"qm": [0.21746871656605915], "sibs": [], "cont": [0.8381457967367197, 0.35026632984736716, 0.605755616587224], "rate": 0.04553157399285047, "least": 0, "dup": 0.0, "rows": 5, "k": 1, "lens": [], "ms": 26.573, "e": 1.2174687165660592}, {"qm": [0.04170556554347349, 0.2028846533443951, 0.5600728296448659], "sibs": [0.31072332298573024], "cont": [0.3334907544742536], "rate": 0.04849573475430201, "least": 0, "dup": 0.4, "rows": 1, "k": 0, "lens": [0], "ms": 21.954, "e": 1.0}, {"qm": [0.6874817375207889, 0.9461391803214141, 0.2972092983682908, 0.5536636259396968, 0.4554601109075669], "sibs": [0.45211158108731997, 0.5660519265964268, 0.4647003796510557], "cont": [0.2348341001660752, 0.3969328986657571], "rate": 0.02517149852538444, "least": 1, "dup": 0.4, "rows": 3, "k": 4, "lens": [2, 3, 2], "ms": 63.658, "e": 4.522145149009526}, {"qm": [0.12406883204852404, 0.10488826504523341, 0.8052444544265396, 0.5520305194036133], "sibs": [0.11914963929146855, 0.10666381427203693], "cont": [0.9837317112353612, 0.11286139185533861], "rate": 0.03892718493488788, "least": 0, "dup": 0.4, "rows": 1, "k": 0, "lens": [0, 0], "ms": 21.954, "e": 1.0}, {"qm": [0.7833424770694549, 0.30097657050713544, 0.9112443872761743, 0.70591278521193, 0.8074869595904233], "sibs": [], "cont": [0.5365449160774239], "rate": 0.027314839396216928, "least": 0, "dup": 0.4, "rows": 4, "k": 5, "lens": [], "ms": 44.776, "e": 2.508075217544762}, {"qm": [0.37413693318225405, 0.03513410060256328, 0.8161870517069689], "sibs": [0.018741782889346692], "cont": [0.31469521414409984, 0.30858547865129005, 0.3557817871018436], "rate": 0.05868305252034291, "least": 1, "dup": 0.4, "rows": 2, "k": 1, "lens": [0], "ms": 26.573, "e": 1.3741369331822542}, {"qm": [0.4244330492563253, 0.20448663943540868, 0.10313506305956166, 0.33893982056743366, 0.5188326124996868], "sibs": [], "cont": [0.7357088873876331], "rate": 0.07264345180254833, "least": 0, "dup": 0.4, "rows": 4, "k": 1, "lens": [], "ms": 26.573, "e": 1.4244330492563253}, {"qm": [0.06649143397577872], "sibs": [], "cont": [], "rate": 0.06451322783897981, "least": 0, "dup": 0.0, "rows": 1, "k": 0, "lens": [], "ms": 21.954, "e": 1.0}, {"qm": [0.329530255770506], "sibs": [0.46138666114779425], "cont": [0.7479907794749217], "rate": 0.031711130625296124, "least": 1, "dup": 0.0, "rows": 1, "k": 1, "lens": [1], "ms": 31.977, "e": 1.7909169169183001}, {"qm": [0.19254052776610753, 0.018909045142972203, 0.12073489221175893, 0.05283861598298811, 0.9385654080023147], "sibs": [], "cont": [0.8162937440816909], "rate": 0.04566895188803918, "least": 1, "dup": 0.4, "rows": 5, "k": 1, "lens": [], "ms": 26.573, "e": 1.1925405277661074}, {"qm": [0.9143383372936209, 0.09460744218021244, 0.4718990945358065, 0.9446343936155023], "sibs": [], "cont": [0.5108197103856482, 0.2328814590314825, 0.3145203743028663, 0.6479910693524085], "rate": 0.03484355810761134, "least": 1, "dup": 0.4, "rows": 3, "k": 1, "lens": [], "ms": 26.573, "e": 1.914338337293621}, {"qm": [0.27082196204275466, 0.854905584265504, 0.2994605541115407, 0.07662874597728886, 0.5710129506685174], "sibs": [0.05423853682966013, 0.0572340181461894, 0.09485860923632672], "cont": [0.569619991392271, 0.08289271022803313, 0.7028418108776979, 0.3989849461038738], "rate": 0.03966898584256013, "least": 0, "dup": 0.4, "rows": 2, "k": 2, "lens": [0, 0, 0], "ms": 31.977, "e": 1.502349169734846}, {"qm": [0.19579833587363904], "sibs": [0.24972867295872406, 0.49198969779746016, 0.21316248833994414], "cont": [0.13594421561521874], "rate": 0.03992477483138919, "least": 1, "dup": 0.4, "rows": 4, "k": 1, "lens": [1, 1, 1], "ms": 42.052, "e": 2.1506791949697672}, {"qm": [0.4985144131326755, 0.021309000355816954, 0.1251081683305546, 0.044355904910336474, 0.0723030100340758], "sibs": [0.4356224455003334], "cont": [0.7511697396821374, 0.3114791286990275, 0.27816914936688253], "rate": 0.042992746033827564, "least": 0, "dup": 0.4, "rows": 4, "k": 1, "lens": [2], "ms": 36.558, "e": 2.2613632576191907}, {"qm": [0.5620127967882583, 0.37289677509585006, 0.5291985314092401, 0.5529976025234237, 0.21175776161336168], "sibs": [0.5931802720682231, 0.08569823411216405], "cont": [], "rate": 0.03944094984794961, "least": 1, "dup": 0.0, "rows": 4, "k": 2, "lens": [1, 0], "ms": 36.158, "e": 2.364765828341422}, {"qm": [0.19541858144710178, 0.8754739077732142], "sibs": [0.2658502262568181, 0.32605745908514955, 0.5853806666675667], "cont": [], "rate": 0.03475426240248557, "least": 0, "dup": 0.4, "rows": 2, "k": 2, "lens": [1, 1, 1], "ms": 45.976000000000006, "e": 2.5437908026076284}, {"qm": [0.08040067718988642], "sibs": [], "cont": [0.6449410469740284], "rate": 0.06467792685766331, "least": 1, "dup": 0.0, "rows": 1, "k": 1, "lens": [], "ms": 26.573, "e": 1.0804006771898864}, {"qm": [0.44073420518661066, 0.018559974784891686, 0.5775757574648626], "sibs": [0.2709483923738729, 0.4591780513437531], "cont": [0.5550637160324436, 0.8454091750046187, 0.4362489822566782], "rate": 0.06818540202878741, "least": 0, "dup": 0.0, "rows": 4, "k": 1, "lens": [0, 1], "ms": 31.977, "e": 1.8999122565303639}, {"qm": [], "sibs": [0.28545724946726564, 0.3904204658624865], "cont": [0.4822120956040524, 0.5792379194136661, 0.6108763854841418, 0.8266078541874587], "rate": 0.015067947389465305, "least": 0, "dup": 0.4, "rows": 4, "k": 0, "lens": [3, 4], "ms": 51.888999999999996, "e": 2.257193642849179}, {"qm": [0.07328967028409294, 0.6401303325611755, 0.0394282808707217, 0.9068192152974545, 0.15779891712875183], "sibs": [0.05623972320156383, 0.05434921791668377, 0.09874550267705719], "cont": [0.5201380967961485], "rate": 0.03895853880134364, "least": 1, "dup": 0.4, "rows": 4, "k": 1, "lens": [0, 0, 0], "ms": 26.573, "e": 1.073289670284093}, {"qm": [0.37940575513050867, 0.7096868254928838, 0.8552905622974519, 0.702143854067469, 0.4413152666625343], "sibs": [], "cont": [], "rate": 0.06308011217849976, "least": 0, "dup": 0.4, "rows": 1, "k": 1, "lens": [], "ms": 26.573, "e": 1.3794057551305086}, {"qm": [0.5867483734798713, 0.15795262073788097, 0.0452966262574539, 0.3147623998294172], "sibs": [], "cont": [], "rate": 0.047292804084779726, "least": 0, "dup": 0.0, "rows": 4, "k": 1, "lens": [], "ms": 26.573, "e": 1.5867483734798713}, {"qm": [0.43713448311347836, 0.11613754609598759, 0.9008264069834141, 0.5866056521649684], "sibs": [0.2705130578227038], "cont": [0.05407683464966173, 0.5261071700923535, 0.3229774794124666, 0.38092041379453967], "rate": 0.07077917311607783, "least": 0, "dup": 0.0, "rows": 4, "k": 1, "lens": [0], "ms": 26.573, "e": 1.4371344831134785}, {"qm": [0.22856669231098126, 0.11864827314707449, 0.6083060926752277, 0.47727190205375314, 0.11042243448876166], "sibs": [0.0445911037389799, 0.5554693427893733], "cont": [0.2206857909253903, 0.9456391341154494, 0.019360675046276588], "rate": 0.014468051666163616, "least": 0, "dup": 0.0, "rows": 2, "k": 1, "lens": [0, 2], "ms": 36.158, "e": 1.9066202263486343}, {"qm": [0.6614073052716681], "sibs": [0.5594132296820412], "cont": [0.29001088578642076], "rate": 0.036301058331436674, "least": 0, "dup": 0.0, "rows": 5, "k": 1, "lens": [2], "ms": 36.158, "e": 2.3830564612144403}, {"qm": [0.5429971275864639, 0.01381422096883922, 0.8310099452054799, 0.21471189695372928, 0.1378310610897916], "sibs": [], "cont": [0.3558481146604294, 0.8689758308719906], "rate": 0.06812731033408256, "least": 1, "dup": 0.0, "rows": 5, "k": 1, "lens": [], "ms": 26.573, "e": 1.5429971275864638}, {"qm": [], "sibs": [0.11521942684181265, 0.31570360609721176], "cont": [], "rate": 0.0753730318187228, "least": 1, "dup": 0.0, "rows": 5, "k": 0, "lens": [0, 1], "ms": 26.573, "e": 1.3157036060972118}, {"qm": [0.4633067933383799, 0.8566956944106059, 0.8016832755897845, 0.2979595394586282], "sibs": [0.24246889420233675, 0.4131521413491566], "cont": [0.575155611669572, 0.852440297034485, 0.09724263346930806, 0.7826476252755277], "rate": 0.011242394474584399, "least": 0, "dup": 0.0, "rows": 1, "k": 4, "lens": [1, 1], "ms": 47.237, "e": 2.928849492985455}, {"qm": [0.5715958456601014], "sibs": [0.4479296149744946], "cont": [0.14171501254357322], "rate": 0.04247775971555853, "least": 1, "dup": 0.0, "rows": 4, "k": 1, "lens": [1], "ms": 31.977, "e": 2.019525460634596}, {"qm": [0.8275358526907465, 0.6605116378556654, 0.37111146647163784, 0.9562936912162088, 0.07218797558753598], "sibs": [0.38398537247809755], "cont": [0.6668344765898749], "rate": 0.05522964960489905, "least": 0, "dup": 0.0, "rows": 3, "k": 4, "lens": [2], "ms": 47.237, "e": 3.4110040891467497}, {"qm": [0.5060149680536606, 0.7338182427534091, 0.46389594146601787, 0.2636128082649909], "sibs": [0.16577860299829061, 0.0015981230550865977, 0.34246249817423796], "cont": [0.2752960827757033, 0.8732576816350535, 0.30566502998870304, 0.8909829548117074], "rate": 0.06431305168513639, "least": 1, "dup": 0.0, "rows": 3, "k": 2, "lens": [0, 0, 1], "ms": 36.158, "e": 2.2198004808919585}, {"qm": [0.012438199011745255, 0.7946774667122825, 0.9652883988640416], "sibs": [0.1727856110303968, 0.57521428531758], "cont": [], "rate": 0.06959569442587042, "least": 0, "dup": 0.0, "rows": 1, "k": 0, "lens": [0, 0], "ms": 21.954, "e": 1.0}, {"qm": [0.15109935210823922, 0.10842404163381425, 0.08043894133000706, 0.29018857081820737, 0.7793973950636574], "sibs": [], "cont": [], "rate": 0.019833839346177346, "least": 0, "dup": 0.4, "rows": 5, "k": 1, "lens": [], "ms": 26.573, "e": 1.1510993521082393}, {"qm": [0.6247099944087781, 0.016182957914473328, 0.41660171586521444, 0.5725067922721059, 0.23421956493074303], "sibs": [0.05222014751577923, 0.43305717619420747, 0.25458299385160216], "cont": [0.3928469940784862, 0.10798603785537397, 0.8446227139534992], "rate": 0.020736710736354302, "least": 1, "dup": 0.4, "rows": 3, "k": 1, "lens": [0, 2, 2], "ms": 45.576, "e": 2.582487538264703}, {"qm": [0.9206886836094786, 0.6486263957028627, 0.9002555603860385, 0.5007227438516276], "sibs": [0.4344259447591791], "cont": [0.8887103703532475, 0.7359181423910831, 0.15181429944176172, 0.21309498202158694], "rate": 0.019153591298254453, "least": 1, "dup": 0.0, "rows": 5, "k": 4, "lens": [3], "ms": 51.089, "e": 4.429313387779353}, {"qm": [0.8589321578316395, 0.8258213722759763, 0.17160910465472878], "sibs": [0.17475558208221306, 0.5538607395825329, 0.10851350272799902], "cont": [0.19719581559506727], "rate": 0.07534838578961678, "least": 1, "dup": 0.4, "rows": 4, "k": 2, "lens": [0, 1, 0], "ms": 36.558, "e": 3.122117430686662}, {"qm": [0.7027799553369646], "sibs": [0.2869589402535776, 0.5380588689038724, 0.2642863847032459], "cont": [0.10597130920289333, 0.6272740832627641], "rate": 0.04609027100561128, "least": 0, "dup": 0.0, "rows": 2, "k": 1, "lens": [1, 1, 1], "ms": 40.852, "e": 2.7920841491976605}, {"qm": [0.4992648992347719, 0.8972205745255978, 0.6040449577382069], "sibs": [0.4563611659051056, 0.22823427100483623], "cont": [], "rate": 0.024449925864543517, "least": 0, "dup": 0.4, "rows": 4, "k": 3, "lens": [1, 1], "ms": 45.576, "e": 2.902393461526747}, {"qm": [], "sibs": [], "cont": [0.4693742234210203], "rate": 0.07737715290758652, "least": 1, "dup": 0.4, "rows": 5, "k": 0, "lens": [], "ms": 0.0, "e": 0.0}, {"qm": [], "sibs": [0.5480789505832951, 0.3613617412378937], "cont": [0.8441050629016086, 0.3740485380228159], "rate": 0.07053238681851272, "least": 1, "dup": 0.4, "rows": 1, "k": 0, "lens": [1, 0], "ms": 26.973, "e": 1.548078950583295}, {"qm": [0.9840145327708886], "sibs": [], "cont": [0.11655218189446492], "rate": 0.03053967678823948, "least": 0, "dup": 0.0, "rows": 4, "k": 1, "lens": [], "ms": 26.573, "e": 1.9840145327708885}, {"qm": [0.056410380896117274, 0.8464961188066883], "sibs": [0.20556767500541753, 0.0417160410138381], "cont": [0.19915330010487908], "rate": 0.05954025656454521, "least": 1, "dup": 0.4, "rows": 3, "k": 1, "lens": [0, 0], "ms": 26.573, "e": 1.0564103808961174}, {"qm": [0.8262795483070248, 0.09944054948247372], "sibs": [0.4575939107351479, 0.5618617297009858], "cont": [0.776330755277639], "rate": 0.006732980056769603, "least": 1, "dup": 0.0, "rows": 5, "k": 2, "lens": [2, 2], "ms": 47.237, "e": 3.719335648364772}, {"qm": [0.6697749384840261, 0.082701478770693, 0.056625813371149025, 0.0941812326262148], "sibs": [0.14672110810584477, 0.514448142015834], "cont": [0.8163953622712767, 0.6362896005203524], "rate": 0.04126209870565795, "least": 1, "dup": 0.4, "rows": 4, "k": 1, "lens": [0, 3], "ms": 41.251999999999995, "e": 2.8714533851286146}, {"qm": [0.3879303351623218, 0.30333074395506143, 0.23317614604723091, 0.07407536677025506], "sibs": [0.5162761754868949], "cont": [], "rate": 0.046910179641255614, "least": 0, "dup": 0.4, "rows": 3, "k": 1, "lens": [1], "ms": 32.377, "e": 1.9042065106492165}, {"qm": [0.13778143697395806, 0.6718079577766403, 0.4947746514545087, 0.5456570705573686, 0.19184538518821348], "sibs": [0.3057914691274301, 0.3586692149334974, 0.2883531823213654], "cont": [0.8555320025851414, 0.7380373305849443, 0.12828856558219282], "rate": 0.013973409193668504, "least": 0, "dup": 0.0, "rows": 1, "k": 3, "lens": [1, 1, 1], "ms": 47.237, "e": 2.228955629854691}, {"qm": [], "sibs": [0.27997562337657705, 0.48228257378890926], "cont": [0.4063293525819056, 0.7669359058933333, 0.10879270545741193, 0.5039751021291715], "rate": 0.005980483768901688, "least": 0, "dup": 0.0, "rows": 5, "k": 0, "lens": [3, 3], "ms": 47.237, "e": 2.30952750895993}, {"qm": [0.4509032704384539, 0.18030467071308676, 0.8153981224198633, 0.6239856247650148, 0.20283861711482232], "sibs": [0.27829100820676006, 0.08932686190517343, 0.22867037552365363], "cont": [0.20390389455908997], "rate": 0.07027401327333148, "least": 1, "dup": 0.0, "rows": 2, "k": 1, "lens": [0, 0, 0], "ms": 26.573, "e": 1.450903270438454}, {"qm": [0.3103748769815724, 0.48406745853723865], "sibs": [], "cont": [0.37721733314086847], "rate": 0.037530204465672186, "least": 1, "dup": 0.0, "rows": 3, "k": 1, "lens": [], "ms": 26.573, "e": 1.3103748769815724}, {"qm": [0.7030606037709847, 0.11205200100723911], "sibs": [0.18997634768417374, 0.15213726161651006], "cont": [0.5509006002561609, 0.7656217062875705], "rate": 0.027151292498880274, "least": 1, "dup": 0.0, "rows": 1, "k": 1, "lens": [1, 1], "ms": 36.158, "e": 2.0451742130716686}, {"qm": [0.8016208316754593, 0.27592634666170646, 0.1018891915402087], "sibs": [0.22092083323936695, 0.3240372170004347], "cont": [0.17343270287969814, 0.280727701913444], "rate": 0.04574966709434661, "least": 0, "dup": 0.0, "rows": 1, "k": 2, "lens": [1, 1], "ms": 40.852, "e": 2.5677671894073892}, {"qm": [0.7469323953639586, 0.665602863593131, 0.1915300308253297, 0.09904447884522394], "sibs": [], "cont": [0.5359086731191236, 0.7932877242965982, 0.16448579917211392], "rate": 0.030598583651269138, "least": 0, "dup": 0.0, "rows": 5, "k": 2, "lens": [], "ms": 31.977, "e": 2.2440927366286862}, {"qm": [0.8422143205432072, 0.270111316613306, 0.60684401627368, 0.15865499528188198], "sibs": [0.11123105931419038], "cont": [0.7137293584763155], "rate": 0.03455981912220561, "least": 0, "dup": 0.0, "rows": 1, "k": 2, "lens": [0], "ms": 31.977, "e": 2.069705939535714}, {"qm": [0.9966562894418802, 0.7451447014828528, 0.7074166049304939], "sibs": [0.37850823914680803], "cont": [0.6267282459656383, 0.5823664484432782, 0.13857127493491495, 0.23742054120971523], "rate": 0.0562238163894808, "least": 0, "dup": 0.4, "rows": 5, "k": 3, "lens": [2], "ms": 45.176, "e": 3.8804046590021537}, {"qm": [0.034356560610255826, 0.10258588842847172, 0.35719205371403284, 0.2695775663915002, 0.9701522116543946], "sibs": [0.4822937671749293, 0.35299805153954195, 0.5659369725438168], "cont": [], "rate": 0.0330322781399435, "least": 1, "dup": 0.0, "rows": 1, "k": 1, "lens": [1, 1, 1], "ms": 40.852, "e": 2.4355853518685437}, {"qm": [0.7189963024687093, 0.45950817375554254, 0.5885498530286531, 0.5485892885549425, 0.7442996308417177], "sibs": [0.08492586695285897, 0.47842961518928834, 0.5117533962327478], "cont": [0.47500382024107834, 0.6617541760689662, 0.4214213254665461, 0.3714633342381951], "rate": 0.03309407405618103, "least": 1, "dup": 0.4, "rows": 2, "k": 3, "lens": [0, 2, 2], "ms": 51.888999999999996, "e": 3.704352558550185}, {"qm": [0.5416390228271754, 0.9276346748466936], "sibs": [0.06760873638172306, 0.13666213899215587], "cont": [0.15470430540924007, 0.4562680857682476], "rate": 0.04641831741092714, "least": 0, "dup": 0.4, "rows": 3, "k": 2, "lens": [0, 0], "ms": 31.977, "e": 2.044082161651743}, {"qm": [0.5904230364849213, 0.6934978167043986, 0.8657517770941104, 0.9248394345422678], "sibs": [0.35943079292536867, 0.1794562940367181, 0.18167033411841263], "cont": [], "rate": 0.05557910162893784, "least": 1, "dup": 0.0, "rows": 1, "k": 4, "lens": [1, 0, 1], "ms": 47.237, "e": 3.2233141176344584}, {"qm": [0.07562000610478237, 0.7947752001371903], "sibs": [0.10486367901026128, 0.4767838591284526], "cont": [0.2829419127360885], "rate": 0.06844082984147822, "least": 0, "dup": 0.4, "rows": 5, "k": 0, "lens": [0, 0], "ms": 21.954, "e": 1.0}, {"qm": [0.5664303517148891, 0.715796648695767, 0.6386930490965733], "sibs": [0.3966162545486457, 0.3036913019639838], "cont": [0.9386970942894572, 0.19635400167787131, 0.11257941379321779, 0.9082451103057465], "rate": 0.07805901945384065, "least": 1, "dup": 0.4, "rows": 4, "k": 2, "lens": [2, 2], "ms": 48.037, "e": 3.329563524111956}, {"qm": [0.16912262875225514], "sibs": [], "cont": [0.7473424279781994, 0.4988117425474057], "rate": 0.03387730136543765, "least": 1, "dup": 0.0, "rows": 4, "k": 1, "lens": [], "ms": 26.573, "e": 1.1691226287522551}, {"qm": [0.009361555141391364, 0.8567046112972846], "sibs": [], "cont": [0.8415679344081114, 0.9378256876920368, 0.2962154330863801, 0.07662292095775303], "rate": 0.031159107584050463, "least": 1, "dup": 0.4, "rows": 4, "k": 1, "lens": [], "ms": 26.573, "e": 1.0093615551413913}, {"qm": [0.9105291559627261, 0.3972236756881036, 0.11716359198187931], "sibs": [0.018931016183739734], "cont": [0.025950014404991784, 0.4049902743928767, 0.04785074203110862], "rate": 0.07067847467492591, "least": 1, "dup": 0.4, "rows": 1, "k": 1, "lens": [0], "ms": 26.573, "e": 1.9105291559627262}, {"qm": [0.7892949521899859, 0.5410498443590365, 0.45699702696735056, 0.33541121418128994, 0.2317385335835639], "sibs": [0.5148600371442597], "cont": [0.8984108937664197, 0.03145876406927772, 0.6714681430327223, 0.13324022618194198], "rate": 0.00566024950540837, "least": 0, "dup": 0.4, "rows": 3, "k": 4, "lens": [2], "ms": 47.637, "e": 3.454377119241916}, {"qm": [0.7611774043569456, 0.29953001507516996, 0.7509583671272998, 0.38308903958494445], "sibs": [0.28025455403928806, 0.06575842618388225, 0.589634723969811], "cont": [0.25215099014199627, 0.5155803764381676, 0.06955532031209388], "rate": 0.049053691678329625, "least": 1, "dup": 0.0, "rows": 2, "k": 3, "lens": [1, 0, 2], "ms": 47.237, "e": 3.1789542541631173}, {"qm": [0.6780752700163685, 0.16808730079746292, 0.8493119845445488], "sibs": [], "cont": [0.07555119779205721, 0.9946515862033753, 0.5425675814492404, 0.7041012648149055], "rate": 0.05031151617848762, "least": 0, "dup": 0.0, "rows": 2, "k": 1, "lens": [], "ms": 26.573, "e": 1.6780752700163686}, {"qm": [], "sibs": [0.4295162383366774, 0.39938996572130353, 0.43874534097147905], "cont": [0.03713902395593305, 0.49528688131287346, 0.9546895069701703, 0.9756198711103709], "rate": 0.07070774245080833, "least": 1, "dup": 0.4, "rows": 2, "k": 0, "lens": [1, 1, 1], "ms": 37.358000000000004, "e": 2.26765154502946}, {"qm": [0.4658463263915483, 0.5847658096974335, 0.8261120769207391, 0.755302417730204], "sibs": [0.05514122548771243, 0.4548158035841044, 0.09182404133447682], "cont": [0.1271148490098053, 0.49413598146993953], "rate": 0.012248298073411133, "least": 1, "dup": 0.0, "rows": 3, "k": 4, "lens": [0, 2, 1], "ms": 51.089, "e": 2.7377278204218007}, {"qm": [0.2027834157787819, 0.6648591901747432, 0.9957743795012632, 0.9654902698904252, 0.09686806055685071], "sibs": [0.15878014821848319, 0.40178543638839803, 0.24966424535684067], "cont": [0.893336081969981, 0.10860901834349523, 0.9526845800707462], "rate": 0.030130348674708693, "least": 1, "dup": 0.0, "rows": 1, "k": 4, "lens": [1, 1, 1], "ms": 51.089, "e": 2.411708057024387}, {"qm": [0.7008373083621089, 0.23684089543007703, 0.5142010779766045, 0.03925765435798789, 0.1959617018066231], "sibs": [0.43282770730722286, 0.24013729512015788, 0.191927883968774], "cont": [], "rate": 0.041311971108010506, "least": 1, "dup": 0.0, "rows": 4, "k": 2, "lens": [1, 1, 1], "ms": 44.776, "e": 2.7317171304215506}, {"qm": [0.7275014254889673, 0.22380245838213808, 0.18455860444622973, 0.8325900099780716], "sibs": [0.0492619844767437, 0.2753446049967104], "cont": [0.12898227273477691, 0.5030058784625575, 0.7642290705502255, 0.9242340368021741], "rate": 0.06172657000789898, "least": 1, "dup": 0.0, "rows": 5, "k": 1, "lens": [0, 0], "ms": 26.573, "e": 1.7275014254889673}, {"qm": [0.038768054528402485], "sibs": [0.39067553177676245, 0.03244292529756374], "cont": [0.5958453030471399, 0.10269531019246803, 0.9755167718995926, 0.9746631218085162], "rate": 0.04530547034932178, "least": 0, "dup": 0.0, "rows": 3, "k": 1, "lens": [2, 0], "ms": 36.158, "e": 1.6622257669297924}, {"qm": [0.5776637787211231, 0.38878949126763407, 0.2264362581856041, 0.14945919137722685], "sibs": [], "cont": [], "rate": 0.06794312031135774, "least": 1, "dup": 0.0, "rows": 2, "k": 1, "lens": [], "ms": 26.573, "e": 1.577663778721123}, {"qm": [0.9630140430706595, 0.09136075234016894, 0.1703964822817483, 0.5974656183862215], "sibs": [], "cont": [0.4738769008099095, 0.9848246869319254, 0.9075395375719447], "rate": 0.017465075923181184, "least": 0, "dup": 0.0, "rows": 4, "k": 1, "lens": [], "ms": 26.573, "e": 1.9630140430706595}, {"qm": [0.3078992106636558, 0.743955043779938, 0.9150886409392597], "sibs": [0.1648832799685596], "cont": [0.7560494640996197, 0.65033399527033, 0.0022861133196768746, 0.1105915018290845], "rate": 0.04720641564798081, "least": 1, "dup": 0.0, "rows": 1, "k": 1, "lens": [0], "ms": 26.573, "e": 1.3078992106636558}, {"qm": [0.5013244542520848, 0.6553589196092443], "sibs": [0.4018432591957702, 0.5150238583066136], "cont": [0.6045897035175305, 0.5113583669829352, 0.9371031708818022], "rate": 0.04679474670805486, "least": 1, "dup": 0.4, "rows": 2, "k": 2, "lens": [2, 2], "ms": 48.037, "e": 3.3010674432025477}, {"qm": [0.9320407164229518], "sibs": [0.18739829372586508, 0.301506193834223, 0.20736231131233365], "cont": [0.38999210047326216, 0.9134410205664705], "rate": 0.06592362432244639, "least": 1, "dup": 0.4, "rows": 2, "k": 1, "lens": [0, 0, 0], "ms": 26.573, "e": 1.9320407164229518}, {"qm": [], "sibs": [0.34125571745375793, 0.27089978586921537], "cont": [0.35322152803454243, 0.13995395057870397, 0.9485822753655996], "rate": 0.04130493583094223, "least": 0, "dup": 0.0, "rows": 4, "k": 0, "lens": [1, 1], "ms": 31.977, "e": 1.6121555033229735}, {"qm": [0.31267479398767006, 0.9002433304215458, 0.14266315285693976, 0.7491472440031745], "sibs": [], "cont": [0.8024792864984183, 0.6916686702363956, 0.7458908729989049], "rate": 0.07154481200078398, "least": 1, "dup": 0.4, "rows": 3, "k": 1, "lens": [], "ms": 26.573, "e": 1.3126747939876702}, {"qm": [0.35870732699043273, 0.6595843757245577, 0.669625009915801, 0.18432481084242247], "sibs": [0.09203469064436165], "cont": [0.5567374987791941], "rate": 0.010809780287349486, "least": 1, "dup": 0.0, "rows": 4, "k": 4, "lens": [2], "ms": 47.237, "e": 1.9262136049836704}, {"qm": [0.4821403595277429, 0.04258818405638898, 0.545482831873801, 0.12144415727594104, 0.3737873294618721], "sibs": [0.4497267174221269, 0.4786333869131546], "cont": [0.4956232491053941], "rate": 0.008743338346055157, "least": 0, "dup": 0.0, "rows": 4, "k": 1, "lens": [2, 2], "ms": 44.776, "e": 2.8706173151134995}, {"qm": [0.0563274905161284, 0.9302679636605006, 0.44922912816058425, 0.5154409703089878], "sibs": [0.14707077988296094], "cont": [0.3306198453370358, 0.35792711263463595, 0.8426812143325365, 0.8231958925219973], "rate": 0.021548603257900097, "least": 0, "dup": 0.4, "rows": 1, "k": 0, "lens": [0], "ms": 21.954, "e": 1.0}, {"qm": [0.25939006961173394, 0.21661949618112797], "sibs": [0.34588797797522736, 0.4213602466085846], "cont": [], "rate": 0.01104443561858115, "least": 1, "dup": 0.0, "rows": 5, "k": 2, "lens": [1, 1], "ms": 40.852, "e": 2.082827240389227}, {"qm": [0.9681825865212061], "sibs": [], "cont": [], "rate": 0.016094601496550346, "least": 0, "dup": 0.0, "rows": 4, "k": 1, "lens": [], "ms": 26.573, "e": 1.9681825865212061}, {"qm": [], "sibs": [0.5297836992609835, 0.14385500259170156, 0.5434854561331812], "cont": [0.6302527855749692], "rate": 0.0736239956477661, "least": 1, "dup": 0.0, "rows": 5, "k": 0, "lens": [1, 0, 2], "ms": 36.158, "e": 2.415802378041585}, {"qm": [0.8224314398169836, 0.08721052686207109, 0.27057382821218956], "sibs": [0.4742940074951565, 0.0115714383349931], "cont": [0.7081684020240013, 0.3906693797543557, 0.048582034813873465, 0.5368277729194111], "rate": 0.04916302809133483, "least": 1, "dup": 0.0, "rows": 5, "k": 1, "lens": [2, 0], "ms": 36.158, "e": 2.6326054766895446}, {"qm": [0.4844234009967179, 0.30578125014244273, 0.13476064988458114, 0.08428601916772804, 0.19025895865325215], "sibs": [0.16395042295887752, 0.2646568830811262], "cont": [0.2530875550904267, 0.957430820424956, 0.8381713390374367], "rate": 0.021939863383129685, "least": 0, "dup": 0.0, "rows": 3, "k": 2, "lens": [1, 1], "ms": 40.852, "e": 2.0611583001917517}, {"qm": [0.32040278509582243], "sibs": [0.5548174013563127, 0.21003521202714107], "cont": [0.26073019079235105, 0.8551489815852465, 0.9713105175055873, 0.4232595135805155], "rate": 0.01350641223162935, "least": 1, "dup": 0.4, "rows": 5, "k": 1, "lens": [5, 4], "ms": 61.592999999999996, "e": 2.671707621512466}, {"qm": [0.8606132590442777, 0.558958674063923, 0.8549670189074047], "sibs": [0.30906312285569865, 0.36508621265509167], "cont": [0.4741263813191061, 0.12909780345794553, 0.9940158264486069, 0.7764573373577752], "rate": 0.020772184635531706, "least": 1, "dup": 0.4, "rows": 4, "k": 3, "lens": [2, 2], "ms": 51.888999999999996, "e": 3.746721355627355}, {"qm": [0.5442019167772189], "sibs": [], "cont": [0.13299284561336056, 0.049800041573224485], "rate": 0.04813329684139177, "least": 1, "dup": 0.4, "rows": 5, "k": 1, "lens": [], "ms": 26.573, "e": 1.5442019167772187}, {"qm": [0.06758308393363366, 0.5256097169170384, 0.39096717593563945], "sibs": [0.42465983285377606, 0.5901549664401584, 0.5019280965146974], "cont": [], "rate": 0.03362326008877884, "least": 1, "dup": 0.4, "rows": 5, "k": 1, "lens": [1, 1, 1], "ms": 42.052, "e": 2.5843259797422657}, {"qm": [0.5931739342609468], "sibs": [0.26990257169867665, 0.12600691792176474], "cont": [0.23535321933986408], "rate": 0.018919440370732798, "least": 0, "dup": 0.0, "rows": 3, "k": 1, "lens": [1, 1], "ms": 36.158, "e": 1.9890834238813881}, {"qm": [0.49839106936651356], "sibs": [0.03468557182733543], "cont": [0.15498335586738987, 0.537306992567299, 0.7630256726644487], "rate": 0.027801790798703024, "least": 1, "dup": 0.0, "rows": 4, "k": 1, "lens": [0], "ms": 26.573, "e": 1.4983910693665137}, {"qm": [0.08860535095123667, 0.6524795103062052, 0.37709680209197294, 0.6994216671526516, 0.1437416820892815], "sibs": [0.5201264311919653], "cont": [0.5833631727370454], "rate": 0.010151631828801376, "least": 1, "dup": 0.0, "rows": 5, "k": 2, "lens": [2], "ms": 40.852, "e": 1.969967563266916}, {"qm": [], "sibs": [], "cont": [0.8802712782955726, 0.28865901736548494, 0.7801632401572044], "rate": 0.05173613403085834, "least": 0, "dup": 0.0, "rows": 1, "k": 0, "lens": [], "ms": 21.954, "e": 1.0}, {"qm": [0.21064408599554962, 0.7520029603362196, 0.7763088141414896, 0.31259511678199314], "sibs": [0.07659841286415393, 0.4014898625678308, 0.2867428955510437], "cont": [0.4185407592600733, 0.24568745936695902, 0.7697211142066246], "rate": 0.02821681043669427, "least": 1, "dup": 0.4, "rows": 2, "k": 3, "lens": [0, 2, 2], "ms": 51.888999999999996, "e": 2.468306460754743}, {"qm": [0.023451679594399977], "sibs": [0.41090206188512185], "cont": [0.634658739996125, 0.6886222779310694], "rate": 0.033960168973151975, "least": 0, "dup": 0.0, "rows": 2, "k": 1, "lens": [2], "ms": 36.158, "e": 1.695136326337343}, {"qm": [], "sibs": [0.23348726686003518, 0.5234263683574595, 0.36265622072919623], "cont": [0.8076742267779563, 0.6159105441931487, 0.45256304416316273], "rate": 0.07993484980622444, "least": 0, "dup": 0.4, "rows": 2, "k": 0, "lens": [0, 1, 0], "ms": 26.973, "e": 1.5234263683574594}, {"qm": [], "sibs": [], "cont": [0.7979073592256813], "rate": 0.013639517119458554, "least": 1, "dup": 0.0, "rows": 3, "k": 0, "lens": [], "ms": 0.0, "e": 0.0}, {"qm": [], "sibs": [0.30297993186003114, 0.3902767699028499, 0.2116419485579097], "cont": [], "rate": 0.07410938391691983, "least": 0, "dup": 0.4, "rows": 5, "k": 0, "lens": [0, 1, 0], "ms": 26.973, "e": 1.39027676990285}, {"qm": [0.9993987045647057, 0.6351097014587493, 0.2776269070165057, 0.4227408584065181], "sibs": [], "cont": [0.7718792331531729], "rate": 0.012981283593064007, "least": 1, "dup": 0.4, "rows": 4, "k": 4, "lens": [], "ms": 40.852, "e": 2.8848383824062482}, {"qm": [0.30901138476582735, 0.5087919180671353, 0.9450037156936423, 0.4847294635533872], "sibs": [0.2305949838127989, 0.244373006832461, 0.05830998387302781], "cont": [0.35162562634532524, 0.4094903492134565], "rate": 0.0403755719580736, "least": 0, "dup": 0.0, "rows": 1, "k": 1, "lens": [1, 1, 0], "ms": 36.158, "e": 1.7839793754110873}, {"qm": [0.21953199233189824, 0.2523278461329269, 0.5093186876548798, 0.6199219122188883], "sibs": [0.0366302768677369], "cont": [], "rate": 0.029756603297120904, "least": 0, "dup": 0.0, "rows": 2, "k": 1, "lens": [0], "ms": 26.573, "e": 1.2195319923318984}, {"qm": [0.9247495182063008], "sibs": [0.5280224699661259, 0.35154105464413216], "cont": [0.5995624780970902, 0.41916394630209863, 0.2856316220964116, 0.7141518388134086], "rate": 0.01693706548247507, "least": 1, "dup": 0.0, "rows": 4, "k": 1, "lens": [3, 3], "ms": 51.089, "e": 3.552713813923289}, {"qm": [0.7127560227571847, 0.9279593203560826, 0.622213306885134, 0.9380300400466227, 0.7881881124788559], "sibs": [], "cont": [0.04981783892820413], "rate": 0.018524587934619027, "least": 1, "dup": 0.0, "rows": 4, "k": 5, "lens": [], "ms": 44.776, "e": 3.476003762155437}, {"qm": [0.5460276324574425], "sibs": [0.5765051712579091, 0.3645523499117831, 0.3126252638797567], "cont": [0.5171063608861496, 0.6269961816389835], "rate": 0.053346126955679335, "least": 0, "dup": 0.4, "rows": 2, "k": 1, "lens": [2, 2, 1], "ms": 48.437000000000005, "e": 3.2863372476634916}, {"qm": [0.9349027552728855, 0.24535018862173796, 0.9676018866697789], "sibs": [0.5121495965305727, 0.3636477573548365, 0.5358739034031939], "cont": [0.14253681428827036, 0.8059564219409543, 0.4770497374364322], "rate": 0.07122919267062804, "least": 0, "dup": 0.4, "rows": 5, "k": 1, "lens": [1, 1, 1], "ms": 42.052, "e": 3.3465740125614887}, {"qm": [0.8258769436391581, 0.8213173338588764, 0.21881926415534503, 0.26497258338742313], "sibs": [0.4428368073049376, 0.47131092555177717], "cont": [], "rate": 0.028419847412070445, "least": 1, "dup": 0.4, "rows": 5, "k": 3, "lens": [1, 1], "ms": 45.576, "e": 3.5667583753720926}, {"qm": [0.5916132339561712, 0.7982495197764067], "sibs": [0.5775622220022396, 0.3058381554337904, 0.010890523012607022], "cont": [0.6354455559770743, 0.4678534148247968], "rate": 0.06342486653971766, "least": 1, "dup": 0.0, "rows": 3, "k": 2, "lens": [2, 2, 0], "ms": 47.237, "e": 3.508621435281277}, {"qm": [0.04426181424459941, 0.8863240107184073, 0.0914164838081426, 0.37588990007501544, 0.6791662961507273], "sibs": [], "cont": [0.7306356037450303, 0.07686513164793607], "rate": 0.04881393595247282, "least": 1, "dup": 0.0, "rows": 4, "k": 1, "lens": [], "ms": 26.573, "e": 1.0442618142445994}, {"qm": [0.5127342941104006, 0.946515301896633, 0.25757610615217486], "sibs": [0.1218352621702128, 0.427714556021104], "cont": [0.17120469770508995, 0.2023925249069316, 0.8951098771807354, 0.9180234760263246], "rate": 0.0349064654087258, "least": 1, "dup": 0.0, "rows": 1, "k": 2, "lens": [0, 1], "ms": 36.158, "e": 2.4257597053141673}, {"qm": [0.33514907301514807, 0.2531845107443196, 0.49873328281470974], "sibs": [0.21007730726747442, 0.42491487648665516], "cont": [0.6547958207678312, 0.966865428747278, 0.09437612600715917, 0.5117544688076108], "rate": 0.03196527416372154, "least": 0, "dup": 0.0, "rows": 4, "k": 1, "lens": [3, 3], "ms": 51.089, "e": 2.787944682113592}, {"qm": [0.25050488580747665, 0.8329463528165393, 0.8009227304718188], "sibs": [0.09159426359138531, 0.0328752026879882, 0.2627416992465415], "cont": [0.5389560358249557, 0.6146589097353558, 0.9554664246133023, 0.9586334552943246], "rate": 0.05170925622026653, "least": 0, "dup": 0.4, "rows": 1, "k": 1, "lens": [0, 0, 0], "ms": 26.573, "e": 1.2505048858074765}, {"qm": [0.2975847052448416, 0.05006129566813966], "sibs": [0.16558398842897204, 0.13954759559903068], "cont": [0.5643975831837416, 0.5889519617636327], "rate": 0.016120376048886755, "least": 0, "dup": 0.4, "rows": 1, "k": 1, "lens": [1, 1], "ms": 36.958, "e": 1.6027162892728444}, {"qm": [0.6951022411687692, 0.11469882567965761, 0.49244055564465916, 0.7908154881924548], "sibs": [0.15702766768995619, 0.3338886938125862], "cont": [0.16308865617583967, 0.48237221509524064, 0.5161975473730667], "rate": 0.009243312853316223, "least": 1, "dup": 0.4, "rows": 3, "k": 3, "lens": [1, 2], "ms": 48.037, "e": 2.3594604823160887}, {"qm": [0.033954347225010784, 0.3211998875025053, 0.12801423056238037, 0.8252677373971878], "sibs": [], "cont": [0.27492284413798285], "rate": 0.02443463594969359, "least": 1, "dup": 0.0, "rows": 4, "k": 1, "lens": [], "ms": 26.573, "e": 1.0339543472250108}, {"qm": [0.9170350813766102, 0.738582046194745], "sibs": [0.041822403007871344, 0.01782876301823941], "cont": [0.4377650617659098, 0.03485044408687976, 0.404616985701828], "rate": 0.05891640437742883, "least": 1, "dup": 0.0, "rows": 5, "k": 2, "lens": [0, 0], "ms": 31.977, "e": 2.5943407282121114}, {"qm": [0.09588844189129042, 0.18223569536024908, 0.39905481381042684, 0.05428800235620368, 0.7544335433406656], "sibs": [0.552147087860511, 0.39883658912171865], "cont": [0.18444727509391845, 0.9789969056825295], "rate": 0.06667557758059649, "least": 0, "dup": 0.4, "rows": 2, "k": 1, "lens": [1, 1], "ms": 36.958, "e": 2.04687211887352}, {"qm": [0.3702097449419448, 0.30644694915603987], "sibs": [0.3147307559780675, 0.3156020203268208, 0.4667428721553938], "cont": [0.5242063983333531, 0.21421549795902006], "rate": 0.05959353746878254, "least": 0, "dup": 0.0, "rows": 1, "k": 1, "lens": [1, 1, 1], "ms": 40.852, "e": 2.467285393402227}, {"qm": [0.054253557756714144, 0.6229115691497116, 0.2976869035803621, 0.3720706010075072], "sibs": [0.554829352270046], "cont": [0.6613776415003834], "rate": 0.02151632058889271, "least": 0, "dup": 0.4, "rows": 2, "k": 1, "lens": [2], "ms": 36.558, "e": 1.9760346384663088}, {"qm": [0.2516236129711822, 0.34804790048515266], "sibs": [], "cont": [0.39038776222232685, 0.7437269336605159], "rate": 0.06630655933282957, "least": 0, "dup": 0.4, "rows": 4, "k": 0, "lens": [], "ms": 21.954, "e": 1.0}, {"qm": [0.39409829231978055, 0.05003335327205083], "sibs": [], "cont": [], "rate": 0.05328552496982385, "least": 1, "dup": 0.4, "rows": 3, "k": 1, "lens": [], "ms": 26.573, "e": 1.3940982923197804}, {"qm": [0.08664030867009287, 0.0932353847958487, 0.2535385445360342, 0.4845139546911822], "sibs": [0.13367257406637126, 0.5349066565405984, 0.07998068115878103], "cont": [0.8527987816598385, 0.0776196536246242], "rate": 0.026653853666593823, "least": 0, "dup": 0.4, "rows": 5, "k": 1, "lens": [2, 2, 1], "ms": 48.437000000000005, "e": 2.405363773740539}], "allocs": [{"qs": [[0.631374966960312, 0.47410491385740705, 0.4677806764913871, 0.020084141438373004, 0.8790331930265274], [0.8381328618348686, 0.799824026789575, 0.07054621079111911, 0.914386127685892, 0.3883692410340617], [0.8244385184865882, 0.5791280549607256, 0.540480004074087, 0.33259664984088577, 0.7940821150875388]], "lo": [0, 1, 0], "top": [3, 2, 5], "w": [1.3198114197984454, 1.58957046533846, 0.5867342410571361], "shared": 3, "rate": 0.0671726625162401, "max": 64, "ks": [2, 2, 2], "best": -1.1376562518819888}, {"qs": [[0.01101993849366012, 0.8855409802044746, 0.31997297717081263, 0.6723030661804951, 0.555759762078569], [0.6095560017390529, 0.852381310255372, 0.40161264912326844, 0.7794550682758423, 0.9210175053537624], [0.8913827755178002, 0.6361233065617453, 0.21921776530750292, 0.676891440968298, 0.9496390389181292], [0.4052450190306457, 0.9677391972045304, 0.6098103434306082, 0.644058905055785, 0.3856946441436675]], "lo": [1, 1, 0, 0], "top": [5, 3, 0, 3], "w": [1.8387786583746357, 1.315194099332682, 0.5627913928218855, 0.7092517518204485], "shared": 6, "rate": 0.02042317688072881, "max": 16, "ks": [1, 3, 0, 2], "best": 0.04548630576052992}, {"qs": [[0.6310447497560524, 0.4210445785358259, 0.20593735476421182, 0.6914540724799337, 0.18987862846726045]], "lo": [0], "top": [0], "w": [1.6380201033732615], "shared": 10, "rate": 0.0462490714523788, "max": 64, "ks": [0], "best": -2.811619800804464}, {"qs": [[0.12454389571289237, 0.1883720936624108, 0.6230993645548608, 0.62001777908998, 0.28424834086186923], [0.6422996064110209, 0.0003887242507912836, 0.05271981535797188, 0.9842446611961614, 0.7188098941945115], [0.9967926390924038, 0.6554831224778276, 0.7465624917766815, 0.9274171096773086, 0.7659894106141135], [0.4877278325651301, 0.7426774367884875, 0.8567003343877062, 0.20040145670809428, 0.9255619241088037], [0.3861459277620797, 0.35935838006928633, 0.8839020112811717, 0.15663376390427552, 0.816522126962488]], "lo": [0, 0, 1, 1, 0], "top": [1, 4, 2, 1, 1], "w": null, "shared": 9, "rate": 0.04910194880225301, "max": 32, "ks": [0, 1, 2, 1, 1], "best": -2.3838764525542486}, {"qs": [[0.008206073115937795, 0.07145663511153466, 0.7287801104600246, 0.2047141686145557, 0.670313281049598], [0.9580646565453154, 0.7728591910220349, 0.13167113124187957, 0.3250818041996706, 0.92622561651105], [0.7547636707337781, 0.3595119605141671, 0.848072389697549, 0.2901913124640978, 0.2159709965350498]], "lo": [0, 0, 1], "top": [3, 3, 5], "w": [0.5304210747172791, 1.5114479794298283, 1.6717506709085432], "shared": 1, "rate": 0.061093429540592555, "max": 64, "ks": [0, 3, 4], "best": -0.049510014980428174}, {"qs": [[0.1924203965312471, 0.31355212474073935, 0.4332051271842132, 0.5705615254688456, 0.39253159655435177], [0.441070919103396, 0.2705245263826238, 0.2554226746118545, 0.5649324928297382, 0.5370213861156324], [0.3824297704595966, 0.5836100736625757, 0.5265106286747872, 0.6300568527509055, 0.24677660796655487]], "lo": [0, 1, 0], "top": [4, 5, 1], "w": [1.8167017721960266, 1.1003646563685885, 1.9428532418010198], "shared": 7, "rate": 0.013774865952338858, "max": 16, "ks": [3, 2, 1], "best": 0.3841861650384192}, {"qs": [[0.011197904037222983, 0.6830901763706402, 0.1678515905979423, 0.9219359711894449, 0.1712704082725265], [0.3336882287743187, 0.06217515462435552, 0.9892548948774837, 0.2636384516238187, 0.34077621913741274]], "lo": [0, 0], "top": [3, 1], "w": null, "shared": 3, "rate": 0.017720036826658694, "max": 32, "ks": [0, 1], "best": -0.45974414017615106}, {"qs": [[0.4317037571920046, 0.2230857525807609, 0.8650346616733073, 0.37996317254401357, 0.9896636863944619], [0.8384536553966739, 0.672271949582261, 0.4331535007502587, 0.6260644229134683, 0.16444612683089654], [0.2878513570469726, 0.37376068684639374, 0.23711720714632123, 0.6302441098469224, 0.6427208909282506], [0.2562297341802582, 0.26754844674605416, 0.9360428826453547, 0.8006704202639804, 0.6625779123278244]], "lo": [0, 1, 1, 1], "top": [3, 3, 5, 0], "w": [0.7692385128848335, 1.3157299190572325, 1.1299173241806086, 1.1090646927336738], "shared": 1, "rate": 0.009877812668008953, "max": 64, "ks": [3, 3, 4, 0], "best": 1.0006475882024088}, {"qs": [[0.3430938423878922, 0.02282509408631328, 0.7487770283558971, 0.0695578736974074, 0.41880086990652976]], "lo": [0], "top": [0], "w": [0.4051188102530397], "shared": 2, "rate": 0.0869376067224722, "max": 64, "ks": [0], "best": -2.7800038501644933}, {"qs": [[0.06834630649757067, 0.44234323798496944, 0.19632336216475565, 0.5131334132398675, 0.28386309364163], [0.396577538381811, 0.31830839379471143, 0.34350591040577994, 0.9181394157729951, 0.4684629249843505]], "lo": [1, 0], "top": [4, 1], "w": null, "shared": 7, "rate": 0.05635331497157336, "max": 16, "ks": [1, 1], "best": -3.029309538685048}, {"qs": [[0.4817727520037054, 0.757321704212255, 0.5310472465046511, 0.40208129870470133, 0.11264068813633377], [0.9054264880566311, 0.7023118711885877, 0.7641534817622165, 0.455108823223191, 0.9624066448475233], [0.5903769336036139, 0.9879428735575783, 0.8184087980097521, 0.07737282699038217, 0.03824589900555975], [0.02386672324773831, 0.883814913832192, 0.4859706206468839, 0.9392698149252479, 0.7401071228301705], [0.20111350691704788, 0.41448582121241195, 0.7971932594593772, 0.8616085828244141, 0.7164070680516993]], "lo": [0, 0, 0, 0, 1], "top": [2, 5, 1, 5, 4], "w": null, "shared": 10, "rate": 0.0941639469624996, "max": 16, "ks": [0, 0, 0, 0, 1], "best": -6.813891530100396}, {"qs": [[0.37958590651809254, 0.22556638393864503, 0.5373592073237411, 0.4004629369238262, 0.10034834356678468]], "lo": [1], "top": [1], "w": null, "shared": 0, "rate": 0.029734414935342096, "max": 16, "ks": [1], "best": -0.7901326080768455}, {"qs": [[0.8270382161168985, 0.5307590187730247, 0.4199964642980647, 0.7155104040308454, 0.3686927875782946], [0.45470256199504033, 0.5640777586121616, 0.4541273661840616, 0.33757220755160955, 0.1566653907626786]], "lo": [0, 0], "top": [5, 2], "w": [0.4385554958112656, 1.386740820416525], "shared": 7, "rate": 0.05106792073335158, "max": 16, "ks": [1, 2], "best": -1.8406616067415698}, {"qs": [[0.1670290444511321, 0.534993197875146, 0.0465937240505927, 0.7756284930592245, 0.8594334785978572], [0.4523853441703264, 0.9437457131132151, 0.21214914404897667, 0.754796136961873, 0.17841336699503496], [0.1639376494273046, 0.5765502905599722, 0.6943740421370984, 0.8205268926813654, 0.03830103216666603], [0.7396093905258708, 0.09661193220927, 0.01246575689545637, 0.07550684882443381, 0.47704178085304005]], "lo": [1, 0, 0, 0], "top": [4, 5, 2, 2], "w": [0.533333107543237, 0.9022939180141314, 0.5827200311907701, 1.5993825043873877], "shared": 10, "rate": 0.03326010820330153, "max": 16, "ks": [1, 0, 0, 1], "best": -1.2238496305196085}, {"qs": [[0.16422695157696376, 0.15258192910748214, 0.8547515048117049, 0.3670466963262671, 0.7028036778104981], [0.3826605258539826, 0.16886561942524636, 0.41072837309960886, 0.6574744890803997, 0.1446177466152908]], "lo": [0, 0], "top": [5, 0], "w": [0.9269450731945155, 1.2727712460790674], "shared": 6, "rate": 0.052324630283928655, "max": 16, "ks": [0, 0], "best": -2.673213036575631}, {"qs": [[0.23929217397851787, 0.8798358389500006, 0.4161899071047107, 0.9151808271229673, 0.581313767392042], [0.10394155592104692, 0.555805894660466, 0.6069889383263044, 0.3390707668040672, 0.22631704727384572], [0.7016484568123476, 0.7434778003002327, 0.3375241231008749, 0.10979089153404142, 0.7625515272950467]], "lo": [0, 1, 0], "top": [1, 3, 4], "w": null, "shared": 5, "rate": 0.08971716280157661, "max": 64, "ks": [1, 1, 2], "best": -4.14095387221511}, {"qs": [[0.08512866871505853, 0.5875340463240534, 0.27667724614725486, 0.32190733375120206, 0.3206122848531231]], "lo": [0], "top": [0], "w": [1.9023267343331922], "shared": 5, "rate": 0.06719016389502289, "max": 32, "ks": [0], "best": -3.008506778563545}, {"qs": [[0.3564259661751814, 0.06078965047889773, 0.704491913837042, 0.8724067543121534, 0.5667478753266606], [0.9212076476948068, 0.7382427748880983, 0.20020231952174228, 0.7660298051926128, 0.5995532725008739], [0.3286329817434648, 0.5937954664530185, 0.17525273990889711, 0.5061434279787712, 0.010981792072411345], [0.6648142236944493, 0.7709071174418965, 0.42291405457003206, 0.08970539694820312, 0.7573622981291346]], "lo": [0, 1, 0, 0], "top": [4, 4, 2, 3], "w": [1.8371502625950464, 0.6321948307188717, 1.5214757821177443, 0.48741454892441294], "shared": 8, "rate": 0.03638456613555272, "max": 64, "ks": [1, 2, 2, 2], "best": -0.5571856200192316}, {"qs": [[0.5137281654240535, 0.27816583694577746, 0.39371803323048826, 0.21217984685746472, 0.5284695348023407], [0.48509917472617825, 0.5444771399618067, 0.9302861457313583, 0.27080237432977816, 0.9801890739796807], [0.6761233223515757, 0.8070805145397284, 0.8083952840903145, 0.739809512508953, 0.42257055907021723], [0.9921130104260686, 0.15794320025912334, 0.13463742206238205, 0.9818959987509889, 0.9672452279597725]], "lo": [1, 0, 0, 1], "top": [5, 4, 2, 4], "w": [1.7944127977865407, 0.4647542756712469, 0.46608869837474864, 0.46128801757156623], "shared": 0, "rate": 0.023800490400323296, "max": 64, "ks": [3, 4, 2, 2], "best": -0.19668134480719268}, {"qs": [[0.25440789626188687, 0.7898462386995171, 0.5028055033507164, 0.46289683808235604, 0.3881931923812869], [0.6478332918753839, 0.21805614142925955, 0.17264560948221497, 0.39549127755238, 0.5478335838774111], [0.2110278284775412, 0.8874501072974261, 0.7296316581150244, 0.7935853020504676, 0.4666403610510953], [0.0011051138774942348, 0.48712185956765897, 0.9817959760622198, 0.9968613869810666, 0.06478639760546512], [0.590423494950806, 0.5684314229749621, 0.427408557447287, 0.07776908116303871, 0.9057704269352185]], "lo": [1, 0, 1, 1, 1], "top": [1, 0, 1, 2, 0], "w": [1.1955875279580075, 0.993481627987912, 1.0813139261689475, 1.9457468707379018, 0.589422001236626], "shared": 0, "rate": 0.0721458263930345, "max": 16, "ks": [1, 0, 1, 1, 0], "best": -3.68585812459374}, {"qs": [[0.5473410789220665, 0.417159655294572, 0.10287459430613155, 0.3759625452857166, 0.7803175084709949], [0.38250958708840266, 0.08560947033381272, 0.12778829212094567, 0.925165038127223, 0.32956207107786195], [0.3961499598908418, 0.418258236338359, 0.0454846709324529, 0.717937491489167, 0.619986021635734], [0.03875637408300492, 0.36455866672622506, 0.7883120056407164, 0.3117779573394942, 0.182815348829254], [0.27084149118658996, 0.39563583847306405, 0.15570348923019917, 0.22793464461209467, 0.25896056926100774]], "lo": [1, 1, 1, 0, 0], "top": [4, 5, 3, 3, 3], "w": [0.43919258827661767, 1.4867629224869219, 1.132594213450861, 1.2936636576748417, 1.791006336513022], "shared": 7, "rate": 0.06336408195859329, "max": 64, "ks": [1, 1, 1, 0, 1], "best": -4.100072871781909}, {"qs": [[0.43735458810260663, 0.5190366979370639, 0.8730758805915213, 0.5760689969744328, 0.2876320544724318], [0.8225504506923887, 0.25698442741390026, 0.3363870775602674, 0.5622906689211631, 0.9490440134822772], [0.11804671132790467, 0.3333750352128697, 0.4869549745043199, 0.21399709217475416, 0.5452342438734605], [0.16507060571830434, 0.3245307301469582, 0.5277889322714202, 0.45854555506607597, 0.6738627186130519], [0.3126110477168449, 0.5408907965462362, 0.46164235421299826, 0.22832259656167242, 0.5502904111793959]], "lo": [0, 1, 0, 1, 1], "top": [5, 0, 1, 1, 4], "w": null, "shared": 9, "rate": 0.041182591439527266, "max": 64, "ks": [3, 0, 0, 1, 2], "best": -2.521638148998666}, {"qs": [[0.7143777912118775, 0.2292025955615733, 0.8011929314708794, 0.7262159759662994, 0.46077605571769886]], "lo": [0], "top": [5], "w": null, "shared": 5, "rate": 0.027804330643704234, "max": 64, "ks": [5], "best": -0.5418422329745298}, {"qs": [[0.049163064064102424, 0.6240703296586972, 0.8004404023048067, 0.00423819488354793, 0.40756281682238316], [0.5866652610494213, 0.22404151547751328, 0.9464643333969553, 0.8654724760518206, 0.2664623059494423]], "lo": [1, 1], "top": [3, 3], "w": null, "shared": 1, "rate": 0.043955775865463724, "max": 64, "ks": [1, 1], "best": -1.795681355655924}, {"qs": [[0.15741482137795781, 0.07053474000995097, 0.7819844830887897, 0.41608536529090145, 0.2986283074058341], [0.5930799003647597, 0.6240700751136369, 0.4662152940876009, 0.8635168603366455, 0.5703394273725217]], "lo": [1, 0], "top": [2, 0], "w": null, "shared": 2, "rate": 0.08931009557718768, "max": 16, "ks": [1, 0], "best": -3.648496024519271}, {"qs": [[0.4348663285135892, 0.3969244518129146, 0.569694529169954, 0.6646570975321139, 0.18590613462574646]], "lo": [1], "top": [0], "w": null, "shared": 5, "rate": 0.02333796108091255, "max": 64, "ks": [0], "best": -1.0449805453589402}, {"qs": [[0.7749053983249554, 0.12310719223860178, 0.19478083772852284, 0.4322540071657488, 0.13688494404835128], [0.41657647907435225, 0.22649103430198247, 0.6478886820054296, 0.05628864442024761, 0.36355436636160465], [0.12390369490713171, 0.2065723611771383, 0.9270032945587117, 0.6012719931101361, 0.7528514995571461], [0.6201538011538574, 0.9863387417969309, 0.2514164767311349, 0.781280183391337, 0.2067039380439003]], "lo": [0, 0, 1, 1], "top": [3, 3, 0, 5], "w": null, "shared": 0, "rate": 0.08410405913711068, "max": 32, "ks": [1, 1, 0, 2], "best": -2.49362867990586}, {"qs": [[0.921356854786056, 0.19062630087176013, 0.2854567793072359, 0.39415890156527245, 0.7119883435244264]], "lo": [1], "top": [5], "w": [0.9271176335896218], "shared": 0, "rate": 0.07667208876926318, "max": 16, "ks": [1], "best": -2.0374074148656307}, {"qs": [[0.9907361524875961, 0.40011051712352785, 0.3497023261235066, 0.3775622084187198, 0.26103519776387796], [0.10889627736956142, 0.6047961527465017, 0.7171451722940864, 0.9167806078515804, 0.558290089901258], [0.224422536643746, 0.8773191652946338, 0.8919700199207049, 0.5917538191713543, 0.08191092301029768]], "lo": [1, 1, 1], "top": [1, 1, 3], "w": null, "shared": 4, "rate": 0.08722211661983, "max": 32, "ks": [1, 1, 3], "best": -5.075208618374067}, {"qs": [[0.6707878269063302, 0.6879710863208821, 0.26596828126809635, 0.5157905049927435, 0.856756977001874], [0.7219955919556286, 0.6530291752398609, 0.6625462678027243, 0.5397749163029939, 0.9034906798552365], [0.3051820584040912, 0.30850890822885524, 0.544927161430819, 0.3632597958405419, 0.3235751844409944]], "lo": [0, 1, 0], "top": [1, 3, 1], "w": [1.9106375321201126, 0.6314752108707269, 0.5668869871401065], "shared": 4, "rate": 0.06421549006859342, "max": 64, "ks": [1, 3, 1], "best": -2.0611440854030034}, {"qs": [[0.9514272759662353, 0.42143315057297226, 0.3950603909865472, 0.32823799492386596, 0.26684432771739997]], "lo": [1], "top": [5], "w": null, "shared": 8, "rate": 0.07569869358528188, "max": 64, "ks": [3], "best": -4.168621412138873}, {"qs": [[0.31962524557506244, 0.42885791188019584, 0.13581919254006314, 0.42641384013439476, 0.13979708395397916], [0.18806704343837788, 0.9049805266946533, 0.5096854003440071, 0.34802255678880545, 0.7744344061894589], [0.5362173665553834, 0.3380177113893835, 0.5705739711910635, 0.7115670717604566, 0.8605151807621199], [0.8087991457084417, 0.01933435202249978, 0.13949209488452707, 0.8067017443196762, 0.2975325958176347]], "lo": [1, 0, 1, 0], "top": [4, 5, 0, 1], "w": [1.198740548087823, 1.7927324031904501, 1.01303917044744, 0.4655302838039773], "shared": 1, "rate": 0.02236451401881537, "max": 32, "ks": [2, 4, 0, 1], "best": -0.0040983702495167584}, {"qs": [[0.2976166244761942, 0.7894122367662554, 0.22002736863486072, 0.4923457726074698, 0.49841000991239437], [0.6116162294622173, 0.20950279806339223, 0.09460966271177706, 0.9980351401445948, 0.34933162226510717], [0.41801904257692535, 0.758598844849734, 0.1621723469793036, 0.6522230724613941, 0.5647004561578999], [0.6819864007929843, 0.6791144554191838, 0.06319519075211932, 0.39846410168039237, 0.6719774575982055]], "lo": [1, 1, 1, 1], "top": [5, 0, 3, 2], "w": null, "shared": 10, "rate": 0.04143200388392469, "max": 16, "ks": [1, 0, 1, 1], "best": -3.142275680563645}, {"qs": [[0.9612231172782244, 0.6460392101522543, 0.8893149191111067, 0.5208103787848751, 0.3252380682007123], [0.9845414138566503, 0.6423217870860319, 0.31025371617089526, 0.6665585872486207, 0.14269964829366422], [0.5123816774381722, 0.5515237694504962, 0.24385349275009083, 0.9671185150150106, 0.4001983539945162]], "lo": [1, 0, 0], "top": [3, 5, 5], "w": [1.3476704422641865, 1.0830248880052147, 0.5732045647861568], "shared": 3, "rate": 0.09402195649479586, "max": 16, "ks": [3, 2, 1], "best": -2.2464013079027723}, {"qs": [[0.5792076179517441, 0.728283765093881, 0.992391023820124, 0.43529102021444155, 0.9124123128943398], [0.6319213617709141, 0.2873422520870762, 0.7986255185624239, 0.9053917887362001, 0.7858426853380298], [0.27290769808585147, 0.5876844496768336, 0.32326425881528387, 0.13445082622517057, 0.19601690963645446], [0.07049713056515772, 0.7057792355054122, 0.5484720625287476, 0.5968900293122879, 0.6362172207715476]], "lo": [0, 1, 0, 1], "top": [0, 0, 0, 1], "w": [1.3741706518645556, 1.506314386562983, 1.1988835265167346, 1.689617709575856], "shared": 10, "rate": 0.08607308326253266, "max": 16, "ks": [0, 0, 0, 1], "best": -6.108692792225206}, {"qs": [[0.1738965652525969, 0.4167932315314393, 0.644714146083494, 0.909545891763616, 0.13104009006176365], [0.9539963354228405, 0.6449411917560258, 0.2168171237636055, 0.6132653864743869, 0.48166516242967605], [0.09058321576773731, 0.32608904911024816, 0.5138067027550781, 0.8193211018660167, 0.2519719177720692], [0.5637127269577262, 0.4731043854528618, 0.1320955605670342, 0.5508504387950525, 0.5798140000537845]], "lo": [1, 1, 0, 0], "top": [3, 0, 4, 3], "w": null, "shared": 6, "rate": 0.011938113621789915, "max": 32, "ks": [3, 0, 1, 2], "best": 0.17633221893053996}, {"qs": [[0.35496507180844517, 0.01712565734067295, 0.5877987551041959, 0.8839441375704469, 0.7372417751831021]], "lo": [1], "top": [3], "w": [0.9220345832969683], "shared": 4, "rate": 0.0677637888225635, "max": 16, "ks": [1], "best": -3.0341914083191033}, {"qs": [[0.00789046160312834, 0.9638328080387963, 0.5357628908018796, 0.13229316890303278, 0.9146984980809453]], "lo": [1], "top": [2], "w": [1.9661398467625417], "shared": 1, "rate": 0.018847673235027583, "max": 64, "ks": [1], "best": -0.602692047036477}, {"qs": [[0.14892245781290614, 0.7020174501783654, 0.4219488187295257, 0.6051422020545508, 0.6137743772356914], [0.09078932255598837, 0.0985923944466901, 0.11695190081282203, 0.9316143108641198, 0.23235480936703956], [0.1961187341959758, 0.1865282445069112, 0.9256081291321783, 0.426220567221961, 0.06712406707544272], [0.5468334408030127, 0.3099892622114403, 0.6462266969082553, 0.653688722250826, 0.3718238025481745], [0.06311732089259359, 0.20676358058416267, 0.32120070974365666, 0.6943880953198733, 0.6727512165198467]], "lo": [0, 0, 1, 1, 1], "top": [1, 2, 4, 0, 2], "w": null, "shared": 1, "rate": 0.04941894246764178, "max": 32, "ks": [0, 0, 1, 0, 1], "best": -2.5247643517293508}, {"qs": [[0.11870683721494546, 0.07370458650785916, 0.02448570924542226, 0.02890240566880975, 0.23219646910547664], [0.17364026845970904, 0.8557655667345329, 0.9528248599793782, 0.6790320894984169, 0.2800282168657141], [0.6285783743137727, 0.21193714785899054, 0.6279327766697428, 0.1803166451367445, 0.7082198296445019], [0.8360463533752391, 0.0011847506226548843, 0.8035334424549961, 0.7375294322907526, 0.30252333786032537]], "lo": [1, 1, 1, 1], "top": [3, 4, 4, 2], "w": [1.944573541850742, 0.42696988808716296, 0.4024813045887519, 1.6529196144863012], "shared": 1, "rate": 0.0680033554137652, "max": 64, "ks": [1, 1, 1, 1], "best": -3.9408624495831073}, {"qs": [[0.8827819249880071, 0.6874590438696442, 0.9100318644643456, 0.41346212045813147, 0.6575112278689473]], "lo": [0], "top": [3], "w": [0.9876190523700923], "shared": 4, "rate": 0.04371966358452788, "max": 64, "ks": [3], "best": -0.21693976456257724}, {"qs": [[0.2835016479915494, 0.40487111220007277, 0.6675803502066819, 0.690906666544721, 0.390096034562312], [0.006207422109100746, 0.5529327572838044, 0.7520263126677248, 0.4188034497013975, 0.31132873862003607], [0.5267510223146594, 0.7772381801450916, 0.8475547220015155, 0.9710886418020447, 0.7488741459381618], [0.1442297662922566, 0.9497218081212088, 0.5152230859856075, 0.1543435009204832, 0.6397508381367533], [0.20280792952636506, 0.09616472849664359, 0.39022005598575216, 0.5726661210587088, 0.42243538615458853]], "lo": [1, 1, 0, 0, 0], "top": [5, 3, 1, 1, 4], "w": [0.9701775228954845, 1.0684737638117072, 1.1579813596038235, 1.807581562213698, 1.7520922415508369], "shared": 7, "rate": 0.0409035820257954, "max": 16, "ks": [1, 1, 1, 0, 1], "best": -1.99455893770984}, {"qs": [[0.5568135677903013, 0.5171744915355522, 0.8547079757748876, 0.5919249907875466, 0.4270398815643397], [0.7400156687369392, 0.18118693912579753, 0.4958293620080789, 0.23442700608348455, 0.6588061965859497]], "lo": [1, 1], "top": [4, 3], "w": null, "shared": 6, "rate": 0.011470120717250357, "max": 16, "ks": [4, 3], "best": 0.0663069906248348}, {"qs": [[0.7614689706623097, 0.4983915773837986, 0.8647218891633776, 0.03273002670171554, 0.301612577356827], [0.8817513647407391, 0.8471438067581163, 0.7780537794543957, 0.974767240403997, 0.6840279319996763], [0.31448912212430524, 0.6178143202618576, 0.7712018143044475, 0.9604501655783169, 0.036118992816174034], [0.8818586942341973, 0.6558253939398749, 0.19950414379100523, 0.8329036828358115, 0.33921872598263403]], "lo": [1, 1, 0, 1], "top": [5, 5, 1, 1], "w": null, "shared": 5, "rate": 0.01825995635253043, "max": 32, "ks": [3, 5, 1, 1], "best": 1.7924075728708384}, {"qs": [[0.4848897592450524, 0.5696660467414444, 0.5838656877563239, 0.3534991507740639, 0.9745863019728381], [0.132380508265887, 0.14360726404449642, 0.061629411737461615, 0.38944023479909773, 0.04730774165435914]], "lo": [0, 1], "top": [3, 0], "w": null, "shared": 3, "rate": 0.08968723103323481, "max": 32, "ks": [2, 0], "best": -3.475440740817321}, {"qs": [[0.9587933285912631, 0.7364442724589404, 0.4294447644859527, 0.3295148929864429, 0.004452500892712541], [0.979554315976016, 0.35192128776884435, 0.22587579913896816, 0.4487396425728596, 0.5287379218204227], [0.9829860495913418, 0.006630067806125628, 0.048882459996474004, 0.8426155779160984, 0.18967496589767963], [0.16241519952648542, 0.6173765189692703, 0.8844191278360917, 0.05510795217162412, 0.4702542468933927], [0.7828331083372794, 0.12850426189472675, 0.5113049779661272, 0.5801474771019187, 0.9075990111414627]], "lo": [1, 0, 1, 1, 1], "top": [5, 2, 1, 1, 2], "w": null, "shared": 4, "rate": 0.00673555466113856, "max": 32, "ks": [4, 2, 1, 1, 2], "best": 1.9764120701297396}, {"qs": [[0.16468759829174306, 0.8416545263496572, 0.3932285639447467, 0.15384902997248429, 0.8345343495737263], [0.6697470900379076, 0.6624258023864349, 0.744947347911162, 0.16700396752233704, 0.7973851005074405], [0.9111138939685517, 0.5712393378677588, 0.3859901329806801, 0.86777346848882, 0.9627831295927642], [0.11790004528360098, 0.5096873269077372, 0.9767227068107315, 0.9932673886110718, 0.0413757311644074], [0.7630196009408876, 0.041525099875445615, 0.8613031359420967, 0.5847247537888357, 0.6541025929361581]], "lo": [0, 1, 1, 0, 0], "top": [4, 2, 5, 3, 1], "w": null, "shared": 4, "rate": 0.04687886700254439, "max": 32, "ks": [1, 2, 5, 0, 1], "best": -1.2836060505371316}, {"qs": [[0.2637554825789259, 0.46436257060782193, 0.5897026160109238, 0.4581490311096754, 0.015509898014217538], [0.88481839916405, 0.36050360128466297, 0.2549885302267111, 0.03545578777484082, 0.08653554117340423], [0.4895077518154193, 0.3704885402872742, 0.8923658936452188, 0.5613019719216326, 0.6572325236317595], [0.5857082672661272, 0.16738853505982254, 0.13085693291331202, 0.3806328726260444, 0.4482880551105316]], "lo": [1, 0, 1, 1], "top": [4, 2, 2, 1], "w": [1.0983228329192813, 1.9081424690748094, 1.405643332863394, 0.6408886800110791], "shared": 9, "rate": 0.03772852369365471, "max": 64, "ks": [2, 2, 2, 1], "best": -0.5687916342312209}, {"qs": [[0.5355683731442968, 0.12203506306526579, 0.07853544795576017, 0.18113809736757414, 0.5484775594348931], [0.3998339192126499, 0.17427565934935707, 0.9870672803762385, 0.4289466889074084, 0.7935542180246117], [0.6647271585720101, 0.2508731126776407, 0.14028861461034015, 0.722929821572572, 0.6765393532625736], [0.560249696434337, 0.45457732370563886, 0.8111405410835718, 0.9341869920067682, 0.8998759128483564], [0.8777353100225525, 0.9935303080449954, 0.20547141992193274, 0.05025808867047521, 0.5888808508515514]], "lo": [1, 1, 0, 1, 0], "top": [1, 4, 1, 3, 2], "w": [1.068354405394694, 1.476354459479024, 1.1939382083542585, 0.36262076232060625, 1.3223127851949392], "shared": 3, "rate": 0.023099875231322904, "max": 64, "ks": [1, 3, 1, 2, 2], "best": 1.652249756092348}, {"qs": [[0.9115613424287778, 0.26527372909395264, 0.0488561618322878, 0.24126205391158773, 0.05040791701116354]], "lo": [1], "top": [5], "w": null, "shared": 8, "rate": 0.030315924099223703, "max": 32, "ks": [2], "best": -1.6011826971601353}, {"qs": [[0.7399033938254477, 0.7418285596330666, 0.09643718084176789, 0.427177859710989, 0.6632220913463472]], "lo": [1], "top": [2], "w": null, "shared": 8, "rate": 0.032736383351380544, "max": 32, "ks": [2], "best": -1.4412614841713278}, {"qs": [[0.09953366757441895, 0.36156311841754873, 0.9141407903374623, 0.6912455646188583, 0.1274092045831231], [0.3422466479458478, 0.12279283371520988, 0.1760257852069158, 0.060368450525736916, 0.6307876389635012], [0.11052632317667, 0.42310205275453805, 0.1259498802832062, 0.24272372690757316, 0.1788554110002436], [0.41513245862481996, 0.7703709289589773, 0.03888218184131209, 0.922709893703495, 0.6258152247548165], [0.7138910689029451, 0.21898484897991943, 0.8799715596968447, 0.7707771207248802, 0.5880350702033491]], "lo": [0, 1, 1, 0, 0], "top": [5, 4, 1, 4, 1], "w": null, "shared": 3, "rate": 0.02929066140315694, "max": 32, "ks": [1, 2, 1, 2, 1], "best": -0.4883989218186382}, {"qs": [[0.9708496304282628, 0.10970378819028581, 0.46221487905389336, 0.722857422995401, 0.2668675081517302], [0.11159872428444106, 0.7152076858223614, 0.7209635071135546, 0.47359344273085147, 0.8168616376735311], [0.8827852837903296, 0.3514327394773544, 0.41550697692456373, 0.4767764284591609, 0.7620622576255326], [0.4416414467026162, 0.2995914396758248, 0.9131328393421727, 0.2093040311658647, 0.4141876715455788], [0.6069710586851681, 0.9209690979331844, 0.715305121118033, 0.10688255021066295, 0.21432535351621973]], "lo": [0, 1, 0, 1, 0], "top": [5, 1, 3, 2, 4], "w": [1.4549465727296875, 0.9214324338338522, 1.405403516687196, 1.0201669752781626, 1.516856917888662], "shared": 7, "rate": 0.09750469976827843, "max": 64, "ks": [1, 1, 2, 1, 3], "best": -2.9484481442625654}, {"qs": [[0.3979382270523253, 0.9518740726948436, 0.2023011748265462, 0.11414254369895305, 0.5363300972226493]], "lo": [1], "top": [3], "w": [1.729036070316645], "shared": 4, "rate": 0.051764043004803265, "max": 16, "ks": [2], "best": -1.7902415736319057}, {"qs": [[0.6536095127891495, 0.6489242667358781, 0.4401461680587876, 0.7671015394697028, 0.33532067020791523], [0.13267215598749582, 0.4237455730601889, 0.6184457484941625, 0.9336875397165876, 0.003135983113521812]], "lo": [0, 0], "top": [4, 2], "w": null, "shared": 4, "rate": 0.0320977843198513, "max": 32, "ks": [4, 2], "best": -0.40822817153140245}, {"qs": [[0.8599251305922586, 0.13085860596606613, 0.07810655595660276, 0.8977350293212403, 0.3366396842736754], [0.44626165688117025, 0.9914288430258172, 0.4654139053818349, 0.5660115803164487, 0.7543893875294212]], "lo": [1, 0], "top": [3, 5], "w": null, "shared": 5, "rate": 0.08305308476454012, "max": 32, "ks": [1, 3], "best": -3.954431664770475}, {"qs": [[0.07608167175685676, 0.8330189735047224, 0.6309989932568548, 0.11304183786038458, 0.3224693479418306], [0.6125172302567601, 0.60414785515019, 0.4690539675197828, 0.71431789046209, 0.6965951686171715]], "lo": [0, 0], "top": [2, 2], "w": null, "shared": 0, "rate": 0.058814508489605205, "max": 64, "ks": [0, 2], "best": -1.1440467968082282}, {"qs": [[0.11032501320427868, 0.7371726576022355, 0.10540617648326911, 0.019380214173043697, 0.3120022338731264]], "lo": [1], "top": [2], "w": null, "shared": 7, "rate": 0.0761906294395508, "max": 16, "ks": [1], "best": -4.415323166651408}, {"qs": [[0.47324919099469853, 0.5799744425429956, 0.8205695485013597, 0.44179224268845196, 0.07292081768016967], [0.5472781327832225, 0.7673538516721541, 0.5448282496210778, 0.7424753519153691, 0.39047081953213947]], "lo": [1, 0], "top": [1, 1], "w": null, "shared": 5, "rate": 0.04680991668881776, "max": 64, "ks": [1, 1], "best": -2.1654033492504556}, {"qs": [[0.8486739548881856, 0.7732258166770138, 0.9509315489811657, 0.5736795119017375, 0.07680654699829037], [0.49356166075502805, 0.11328184273728303, 0.3713061905802483, 0.21144225102759717, 0.6109768851765042]], "lo": [0, 0], "top": [5, 1], "w": null, "shared": 2, "rate": 0.07861536020177906, "max": 32, "ks": [3, 1], "best": -1.393910830660463}, {"qs": [[0.6129111340689868, 0.8551666061477758, 0.47114068730732295, 0.9508559935112721, 0.21177911018240825], [0.9997176614833324, 0.2017367625450559, 0.979388772190218, 0.1142609412201816, 0.6875828668642497]], "lo": [1, 1], "top": [2, 5], "w": [0.4990094918903247, 0.9289263842628321], "shared": 10, "rate": 0.07752533206904855, "max": 64, "ks": [2, 2], "best": -5.1609909864680175}, {"qs": [[0.6157736673956951, 0.30466628282087216, 0.4818894428539483, 0.5666661563111095, 0.9379534049074336], [0.32356903931123593, 0.37274100347580286, 0.2005337406516402, 0.34050300354749197, 0.5175869060568091], [0.768612515691463, 0.0783919166804592, 0.8115273793667742, 0.32017188372584604, 0.27612631252583575]], "lo": [1, 0, 0], "top": [5, 3, 5], "w": [0.3747039907517956, 1.7049130987246877, 0.6170722467707517], "shared": 4, "rate": 0.0088736409353827, "max": 32, "ks": [3, 3, 3], "best": 0.802217243440249}, {"qs": [[0.7086082132797376, 0.7330197378189013, 0.09841494798498363, 0.5061359449430511, 0.7256034467325335], [0.31916930970806967, 0.03549040054634689, 0.5572987769274232, 0.23988466903658034, 0.47643774951971496], [0.3999666094259934, 0.044708121392161515, 0.9838191334076483, 0.7510495493318694, 0.7134592047186862], [0.9077508013663913, 0.489924566561168, 0.16730673874646795, 0.023812519768875107, 0.6247230927545105]], "lo": [1, 0, 1, 0], "top": [5, 3, 4, 5], "w": null, "shared": 7, "rate": 0.008969837187528123, "max": 32, "ks": [3, 1, 1, 3], "best": 1.5738848997245438}, {"qs": [[0.30507294629992066, 0.37160133056045197, 0.058763298772998684, 0.5833137614115723, 0.5951564135054348], [0.19088715020906621, 0.8511147410380124, 0.4328394958624239, 0.9070522676880176, 0.6388645421618386], [0.12950606823157973, 0.6005448684166347, 0.23846175835101935, 0.7059135763137654, 0.3975190913207689], [0.848782875735199, 0.9551893274089224, 0.8176562734195748, 0.13522064793119648, 0.8498015965304996]], "lo": [1, 0, 1, 0], "top": [3, 4, 1, 4], "w": null, "shared": 6, "rate": 0.012582061496028642, "max": 16, "ks": [1, 1, 1, 3], "best": 1.6028687059496098}, {"qs": [[0.6932809545255177, 0.38876969024635855, 0.7456926943798542, 0.88642395104741, 0.0040789807636802955]], "lo": [0], "top": [5], "w": [1.522670915823935], "shared": 9, "rate": 0.02666004194642798, "max": 32, "ks": [4], "best": 0.15448223836900188}, {"qs": [[0.7374895087948814, 0.2743810213634845, 0.43432988879567, 0.9103968662154522, 0.9456354948170705], [0.6699581864685125, 0.09569545030992876, 0.7704494879532447, 0.04264856755279112, 0.26403062205563144], [0.06419683884671357, 0.06355420817950175, 0.1253634454257303, 0.9086167894569931, 0.15447478698548034], [0.6988112239951316, 0.4758918421578574, 0.7916395297918083, 0.8057772507092946, 0.6914401150248414]], "lo": [1, 1, 0, 1], "top": [3, 1, 3, 4], "w": [0.6672404283323412, 1.3425157128795278, 0.5882859331590397, 0.9071672032244502], "shared": 3, "rate": 0.0393064095535466, "max": 16, "ks": [3, 1, 0, 4], "best": -1.8630004655556878}, {"qs": [[0.6849432544018415, 0.7383195043989621, 0.9830752916930713, 0.691204294467487, 0.9819688154032584], [0.48863304269419217, 0.7969571052071196, 0.013177450382415912, 0.22373133148876245, 0.15843106438488297], [0.9465485222846522, 0.32229545390053127, 0.6810183231280984, 0.5082576650201274, 0.3415555744908628]], "lo": [0, 1, 0], "top": [3, 0, 1], "w": null, "shared": 6, "rate": 0.011809083537570339, "max": 64, "ks": [3, 0, 1], "best": 1.808159659637051}, {"qs": [[0.1950567860815864, 0.6079195147703546, 0.11329483887449143, 0.08365482089821341, 0.15542612627925356], [0.9900010765707483, 0.6139329431553171, 0.386337217348237, 0.6418994982891063, 0.9200794141045197]], "lo": [1, 0], "top": [2, 1], "w": [1.6763866081681047, 1.4305414081561052], "shared": 10, "rate": 0.0742625009141507, "max": 16, "ks": [2, 1], "best": -3.655462461053012}, {"qs": [[0.2391705936950762, 0.12997573426354803, 0.6099815664426205, 0.5221758573994477, 0.031299450004791174], [0.9250237190956595, 0.8687335572103685, 0.8566758610787932, 0.31394040413173396, 0.6630078398427139]], "lo": [0, 0], "top": [4, 0], "w": null, "shared": 2, "rate": 0.0382971220214644, "max": 16, "ks": [1, 0], "best": -1.3253434351257876}, {"qs": [[0.23758077468013072, 0.20794916633077942, 0.03581455351789775, 0.8300064555835445, 0.17349226315053967], [0.14308847993723728, 0.010091422061709876, 0.1910086201551896, 0.6386061413601422, 0.049132526030917334], [0.2208128392581855, 0.8711733532488947, 0.49483521562938393, 0.17836926462641722, 0.9719885881966729], [0.42955318668388087, 0.5884963269347756, 0.855864185705954, 0.9500882119302619, 0.19108917845152495], [0.9846247377424677, 0.14627465080468272, 0.541567605778746, 0.4750493983986903, 0.519551745816592]], "lo": [0, 0, 0, 0, 0], "top": [5, 2, 2, 5, 1], "w": [1.4790788695365236, 1.2666868905989626, 0.306559556038591, 0.7829843212360782, 1.8387363778650792], "shared": 6, "rate": 0.0482384171052866, "max": 16, "ks": [1, 1, 0, 2, 1], "best": -0.6132496224664465}, {"qs": [[0.4861475250261589, 0.73576585005856, 0.5415452627633085, 0.5482317701377896, 0.9762701317245597], [0.6128490789470321, 0.25909728867319537, 0.8977857815831043, 0.7167537390140174, 0.6506528227380614], [0.003376620790056628, 0.2119965385398649, 0.4392077884621197, 0.15427415037889136, 0.6553665702417707], [0.4367782114177817, 0.5505829968630422, 0.4897257460700405, 0.40847240335830715, 0.5374568157740068], [0.09877309299534964, 0.3763339510209607, 0.20138337532737005, 0.48044030903490176, 0.397211788637437]], "lo": [1, 1, 0, 0, 0], "top": [3, 1, 2, 4, 2], "w": null, "shared": 2, "rate": 0.09984844783516159, "max": 64, "ks": [2, 1, 0, 2, 0], "best": -5.201382739859122}, {"qs": [[0.8209100650548016, 0.8282925138560913, 0.39023365003844146, 0.7937138422064103, 0.7387310313366513], [0.3549736418760693, 0.8059793065537626, 0.682895452361549, 0.24827043524020398, 0.9854266302012552]], "lo": [0, 1], "top": [3, 4], "w": [0.6218924455696687, 1.9050466275787914], "shared": 5, "rate": 0.02127543137649575, "max": 64, "ks": [3, 4], "best": 0.6006714214142466}, {"qs": [[0.21124736869171334, 0.725744315564507, 0.5484361254061667, 0.2830505833848642, 0.6927347134582476], [0.25572020868201406, 0.97985540485336, 0.7979071777667731, 0.9220349201288273, 0.8253588773960798]], "lo": [0, 1], "top": [4, 5], "w": [0.5503790705606182, 1.3656243313044152], "shared": 8, "rate": 0.0630328784622488, "max": 16, "ks": [1, 5], "best": -3.370183965725081}, {"qs": [[0.02817958388086761, 0.9102306612304292, 0.0453099540011922, 0.25649103054726785, 0.8672626796849724]], "lo": [1], "top": [2], "w": [1.6724482659277082], "shared": 8, "rate": 0.036740282849264706, "max": 16, "ks": [2], "best": -2.1906538489076937}, {"qs": [[0.7165607322448824, 0.19953325681132006, 0.23347478957264511, 0.5395117720401951, 0.8594011630205132]], "lo": [0], "top": [2], "w": [1.6158865010076484], "shared": 4, "rate": 0.07645884520114041, "max": 32, "ks": [2], "best": -2.222769926485694}, {"qs": [[0.44269604773930626, 0.8596998201496331, 0.6113254257758403, 0.7849530473836638, 0.7562907069348083], [0.8857104560481247, 0.6109790752542383, 0.8178323422049836, 0.5257586210100271, 0.2082347151384465], [0.7517665528388403, 0.0949064944825051, 0.33008275556565647, 0.2641493497942201, 0.288670520790243]], "lo": [1, 1, 0], "top": [4, 0, 2], "w": [0.32854083128815037, 1.2651971984540151, 1.4141609965119497], "shared": 10, "rate": 0.036967792194888084, "max": 16, "ks": [2, 0, 1], "best": -1.4869064948981956}, {"qs": [[0.18766514913977983, 0.19869057760615783, 0.2025521001641064, 0.0971373190839423, 0.9205927689058634]], "lo": [0], "top": [4], "w": null, "shared": 7, "rate": 0.05438283987499113, "max": 16, "ks": [0], "best": -2.778364906373422}, {"qs": [[0.9993902948897906, 0.12858832606112502, 0.18558899633168946, 0.6407683033951536, 0.9868016905237236], [0.3056712572089395, 0.05913509897007496, 0.8406656699185077, 0.34213956784029387, 0.22772766044023596], [0.9223545571128001, 0.736552109711541, 0.38666195869259623, 0.6966957407365382, 0.9029792382468369]], "lo": [1, 0, 1], "top": [3, 0, 5], "w": [1.2027522513781819, 1.424871769885918, 0.8205252168856485], "shared": 1, "rate": 0.008418445961660729, "max": 32, "ks": [3, 0, 5], "best": 0.7161845851003293}, {"qs": [[0.48241314754789677, 0.8722999862744611, 0.7572023828391017, 0.8087727928050252, 0.014000488178996351], [0.2425051723081696, 0.27237691027747246, 0.362533199004034, 0.6824493579864074, 0.5980162099431274], [0.2639552023497598, 0.5748886099394174, 0.26011073274052354, 0.3344238769128769, 0.06061967040548], [0.6286458641833796, 0.9749041851130418, 0.36674097917756876, 0.5311035260612585, 0.24260788337382821]], "lo": [0, 0, 1, 1], "top": [1, 1, 0, 0], "w": null, "shared": 8, "rate": 0.06248177542706922, "max": 64, "ks": [1, 1, 0, 0], "best": -3.7019154691517877}, {"qs": [[0.9415306512230066, 0.6433814512170077, 0.2503906102508473, 0.3533502333287585, 0.2117194311898467]], "lo": [0], "top": [2], "w": [0.3966592428511538], "shared": 3, "rate": 0.05579965245441256, "max": 32, "ks": [2], "best": -1.8847367686284136}, {"qs": [[0.42285138943131206, 0.525286607678635, 0.7717642059146592, 0.2941010572246944, 0.5822697527648855], [0.7844535358452932, 0.9249120150591681, 0.941351134315995, 0.8132303602367452, 0.38707359820510767], [0.018189630238301335, 0.9612634614644563, 0.39891045602574027, 0.02319896047379455, 0.8162712891668729], [0.4830921303304787, 0.26441589518160813, 0.01999542015288769, 0.15153208227121118, 0.3667999486369895]], "lo": [1, 0, 0, 0], "top": [1, 3, 1, 2], "w": null, "shared": 9, "rate": 0.0357840116130637, "max": 64, "ks": [1, 3, 0, 2], "best": -0.15912970004022053}, {"qs": [[0.7491439549893154, 0.15790349069074583, 0.9377666802145626, 0.3426589776623037, 0.09460325593379093], [0.2364499038353719, 0.4640559720796855, 0.6088038983590903, 0.1356014239831299, 0.3506087790267406], [0.09664975042679469, 0.03541417520128831, 0.42796188518399836, 0.7615892513610089, 0.14632842363114096], [0.03369977000991742, 0.5341507369182467, 0.35317806035578414, 0.6430944385311913, 0.6307817730798346], [0.44822636034102037, 0.6093753063999742, 0.2511586690605382, 0.020628955401908455, 0.5770648492947817]], "lo": [0, 0, 1, 0, 0], "top": [4, 1, 3, 3, 3], "w": null, "shared": 2, "rate": 0.022417816476062818, "max": 16, "ks": [4, 1, 1, 0, 3], "best": 0.42059581903141074}, {"qs": [[0.4103166692917173, 0.3373677533720254, 0.16764620169347721, 0.8774508142190426, 0.28195525249278985], [0.9173584507036638, 0.9735513309589978, 0.35124295943122197, 0.7564684562364645, 0.05723523639337624], [0.3650922196044535, 0.46302744554172015, 0.33637363618608485, 0.23334581467570525, 0.8151112525270805], [0.692777581527257, 0.20185016483917995, 0.4536029046818103, 0.16472818038948756, 0.15047130415197674], [0.32428542343807965, 0.6610322467968882, 0.7854374741063388, 0.3403716298051881, 0.6676900853907125]], "lo": [1, 1, 1, 0, 0], "top": [5, 3, 4, 4, 3], "w": [1.7276169555557657, 0.9940599221297146, 1.207114228385572, 0.9857986100393434, 1.8773449343070114], "shared": 6, "rate": 0.03324772762853009, "max": 16, "ks": [1, 2, 1, 1, 0], "best": -0.8351424061277299}, {"qs": [[0.5835282387080671, 0.8704781725190043, 0.8733223689649854, 0.22967586489437464, 0.5658335747252543], [0.8478483580770865, 0.08670681903617516, 0.24938740401562876, 0.45247709456878504, 0.8457892368395966]], "lo": [0, 0], "top": [1, 5], "w": null, "shared": 6, "rate": 0.09547248931333066, "max": 16, "ks": [1, 1], "best": -4.264798533116094}, {"qs": [[0.5653021175523057, 0.1559643899195361, 0.6343227113348457, 0.4631285127433834, 0.9815128717074381], [0.6978695872918603, 0.2865911949602682, 0.10983573702135208, 0.6242860354014194, 0.1826651726387054], [0.39178112716676605, 0.6971519199874511, 0.07014997876327611, 0.12238921884024456, 0.014515466439141034], [0.6333478933612365, 0.834165046224255, 0.510901502309535, 0.8305967020804976, 0.7656944724253426], [0.6055672776042221, 0.6077250570300136, 0.22900535181999404, 0.5103414442693711, 0.17781625063078588]], "lo": [1, 1, 0, 0, 1], "top": [1, 4, 3, 1, 2], "w": [0.30512031980947557, 1.4504984161815222, 1.249565297303905, 0.5937732442224197, 0.8087983345607321], "shared": 2, "rate": 0.09861177004031796, "max": 32, "ks": [1, 2, 2, 1, 2], "best": -5.203902706934919}, {"qs": [[0.16087912131321147, 0.18947523406769506, 0.9197911989271704, 0.0730800554474591, 0.6779279995726685], [0.34844227501136815, 0.7505122797287168, 0.7673640755195843, 0.5452618694966235, 0.5632477072570804]], "lo": [1, 1], "top": [0, 2], "w": [1.94728249475842, 1.8641442356395603], "shared": 6, "rate": 0.018708190469620887, "max": 16, "ks": [0, 2], "best": -0.6286940245913466}, {"qs": [[0.38069226691270097, 0.8439519453386205, 0.48952588834443966, 0.6543645231707582, 0.7090224649986103], [0.8715632000481208, 0.1333692317856704, 0.13655455942362504, 0.5000406787691222, 0.24517361377740388], [0.746053654927767, 0.2362887981267594, 0.10389941411635895, 0.6308309737959624, 0.6343235438504091]], "lo": [1, 1, 0], "top": [4, 4, 1], "w": [0.8423476795777645, 0.8530148786504641, 0.6195277726840555], "shared": 0, "rate": 0.09986314767308488, "max": 16, "ks": [2, 1, 1], "best": -3.984400048402709}, {"qs": [[0.26597051461807286, 0.752841882770242, 0.8609952267068479, 0.555279225167973, 0.0723755365850548], [0.4270090729082118, 0.750096840196369, 0.8051267530280177, 0.9374239455797012, 0.41204787543963317], [0.26606378561214183, 0.8157639339303193, 0.802901783489792, 0.7301617257676115, 0.14753606328527025], [0.913627133037448, 0.7261765614714527, 0.16453339754941587, 0.1884591590877781, 0.43556791429222874], [0.914292837745268, 0.5070493115866123, 0.4314299669853857, 0.11831344016517209, 0.31254957410126394]], "lo": [0, 1, 1, 1, 1], "top": [0, 4, 1, 5, 4], "w": [1.782155498969699, 0.4106918890881898, 0.6836508940409227, 1.7934374993871283, 1.6327252055215609], "shared": 2, "rate": 0.0748540321601547, "max": 16, "ks": [0, 2, 1, 3, 3], "best": -2.8159322581968143}, {"qs": [[0.9069393978893461, 0.44956046053048004, 0.7135372425343838, 0.1797413167978662, 0.15190904721142884]], "lo": [0], "top": [2], "w": null, "shared": 6, "rate": 0.08252304907922797, "max": 16, "ks": [1], "best": -3.309080656519332}, {"qs": [[0.3158459679295136, 0.483371671539525, 0.5442769850055402, 0.8758496123085601, 0.02902590200458377], [0.4985876344502219, 0.7264102094263273, 0.6913248662308066, 0.979883956786233, 0.47512222033861373], [0.14213892324641542, 0.7971498346789615, 0.15497567684738667, 0.4523101322050014, 0.23749998059939703], [0.9731118170018751, 0.1097043685462975, 0.5899664731000194, 0.9126129237676531, 0.41562403064328135], [0.7677479266243369, 0.3205190735113397, 0.5727105427044865, 0.2903404187174027, 0.23373440293748682]], "lo": [0, 1, 0, 0, 1], "top": [4, 5, 4, 2, 0], "w": [0.5003537008599914, 1.1158844805306618, 1.5492213544697118, 0.7801318469787528, 1.4637123262229703], "shared": 8, "rate": 0.0805790631358831, "max": 16, "ks": [0, 2, 0, 1, 0], "best": -4.667556557145694}, {"qs": [[0.2725865762468791, 0.8340586300675257, 0.811491572904669, 0.045387138880097155, 0.855418688030773]], "lo": [0], "top": [0], "w": null, "shared": 2, "rate": 0.040017954868874495, "max": 16, "ks": [0], "best": -1.2796541428419996}, {"qs": [[0.7908200243365661, 0.4196113033869484, 0.25637390034180774, 0.4893290355552439, 0.7472934355068014], [0.5129804453370759, 0.4742830813248984, 0.2503482320998529, 0.8762957600796731, 0.7652290033149358], [0.28582171371740395, 0.7444823896940839, 0.7342619519479514, 0.8748093962455479, 0.5099339561747546], [0.7807661063318253, 0.8662386310876005, 0.6608538366494588, 0.3400528861882457, 0.005056044791033609], [0.823691748963627, 0.12366189130361926, 0.46840528776505885, 0.3633967343452845, 0.6127675139031019]], "lo": [1, 0, 1, 0, 0], "top": [3, 3, 4, 5, 1], "w": null, "shared": 8, "rate": 0.046073034484655224, "max": 16, "ks": [1, 0, 1, 0, 1], "best": -2.510245172414994}, {"qs": [[0.4523751817646905, 0.8848715433026405, 0.5631171678093283, 0.10078946299591729, 0.9613027908334406], [0.326072645150457, 0.833624743827412, 0.001906402097249904, 0.20277565418637122, 0.7102007628360506], [0.3061727601339945, 0.02915575942391968, 0.6749017236275079, 0.23474103717103834, 0.7664930931544306]], "lo": [0, 1, 1], "top": [0, 5, 3], "w": [1.413768855744317, 0.38528807351160876, 1.129984633992084], "shared": 3, "rate": 0.08638189624486274, "max": 16, "ks": [0, 1, 1], "best": -4.413164697253793}, {"qs": [[0.2963718544751729, 0.5299273651875726, 0.08317887827575421, 0.6995234316344582, 0.5833626848730853], [0.2776972229744019, 0.44099196303367416, 0.7545205994163737, 0.8318484038102928, 0.18617126748531276]], "lo": [1, 0], "top": [2, 0], "w": null, "shared": 7, "rate": 0.0676618302845594, "max": 64, "ks": [2, 0], "best": -3.9563100925314365}, {"qs": [[0.5325750284806506, 0.5969865792662776, 0.3641581108490841, 0.2933202998466099, 0.21441566132098266]], "lo": [0], "top": [0], "w": [0.967539941024979], "shared": 0, "rate": 0.09018446425366929, "max": 16, "ks": [0], "best": -1.9799097282250555}, {"qs": [[0.894870003248086, 0.8706924824120498, 0.9660150724859726, 0.023618838041823853, 0.6653284693657493]], "lo": [1], "top": [1], "w": [0.3182872199229691], "shared": 6, "rate": 0.08777839832139012, "max": 32, "ks": [1], "best": -4.4845105918415}, {"qs": [[0.02807414905492478, 0.43139803955077227, 0.7126764078588681, 0.9578639493085674, 0.776465846376326], [0.3522159473264217, 0.3816693023042458, 0.9047009715277312, 0.951587666349307, 0.972251690034052]], "lo": [1, 0], "top": [2, 5], "w": null, "shared": 0, "rate": 0.0746223457534632, "max": 64, "ks": [1, 1], "best": -2.345978830427301}, {"qs": [[0.7752440983793271, 0.9262978238042155, 0.774131948549245, 0.725084145100553, 0.5991988999789728]], "lo": [0], "top": [5], "w": [0.39795007363150486], "shared": 5, "rate": 0.05932506161240495, "max": 16, "ks": [2], "best": -2.4365789244986416}, {"qs": [[0.7061217974147269, 0.6880769623786478, 0.8036875207120957, 0.08039226764071339, 0.8436232696081355], [0.7568123935621099, 0.4433229570580992, 0.9059952450682021, 0.5036160043892409, 0.7948387842840927]], "lo": [1, 1], "top": [4, 5], "w": [0.9406406421854636, 0.8090792767840278], "shared": 8, "rate": 0.09393570589577054, "max": 16, "ks": [3, 3], "best": -5.455650517091633}, {"qs": [[0.058006146393665525, 0.9921929235478378, 0.36161970564980483, 0.49533355253309364, 0.09862388671994671], [0.014779660118123927, 0.3300720897078202, 0.36483537006333366, 0.525455309311183, 0.588753028966147], [0.07256534014282012, 0.5336413622613135, 0.5191513452764234, 0.650715824649714, 0.15140461512034653], [0.32122015000958504, 0.3398194371076678, 0.7772043827909284, 0.1320453201217221, 0.328906974889699]], "lo": [0, 1, 0, 0], "top": [1, 3, 3, 1], "w": null, "shared": 3, "rate": 0.03840617354748979, "max": 16, "ks": [1, 1, 1, 1], "best": -1.8830348719264762}, {"qs": [[0.8945446870091907, 0.18492407337520544, 0.3676739957064884, 0.15487150363376367, 0.9867147536682389], [0.8334256313961257, 0.9282381954598226, 0.8750526602082888, 0.3625456541438088, 0.5833685302214034], [0.2525931118932204, 0.7841524075989723, 0.7854013140937997, 0.38997574316156514, 0.8004600290649502]], "lo": [0, 0, 0], "top": [2, 3, 3], "w": null, "shared": 6, "rate": 0.09709218957813513, "max": 32, "ks": [2, 3, 2], "best": -3.2311536886666317}, {"qs": [[0.8595563358043545, 0.5908483167439359, 0.9965729726486411, 0.844216880900252, 0.7298049539459595], [0.31497305181939883, 0.5824501576034028, 0.0031325368310677337, 0.8018413163858045, 0.5547680606426881], [0.9225294287787169, 0.41891459321601987, 0.6893630983546346, 0.9284124665058984, 0.5951556360204432], [0.3223376417996625, 0.6610104525904867, 0.2752205884975899, 0.009317588793563925, 0.1751349888439928]], "lo": [0, 1, 0, 0], "top": [1, 2, 0, 3], "w": null, "shared": 7, "rate": 0.07940274306880525, "max": 32, "ks": [1, 2, 0, 2], "best": -4.16732266217489}, {"qs": [[0.23868905133235963, 0.1967476979279884, 0.3088530500609018, 0.2771782286686896, 0.24512838121317337], [0.7333594773774779, 0.7320295740710047, 0.03378038624488455, 0.13286883298857355, 0.6479201302744421], [0.653682152716433, 0.8451528481400583, 0.4876721725061419, 0.11692012846511068, 0.15451191908352224]], "lo": [0, 1, 0], "top": [3, 5, 5], "w": [0.9922921854262612, 1.5639938636199324, 1.1157718069853302], "shared": 6, "rate": 0.03248077639361892, "max": 32, "ks": [2, 2, 3], "best": 0.4190828047078079}, {"qs": [[0.9665794697992897, 0.24492643463522434, 0.6163896100467943, 0.9094575399535646, 0.69878624791037], [0.07553024905114358, 0.4926686453926815, 0.36291228997868197, 0.7347187300264484, 0.6738638790460445], [0.37907908049468486, 0.5838329309947289, 0.8952607783266888, 0.5430671873681111, 0.8863136109372467], [0.7890178770320406, 0.9086970642623893, 0.6852101448789253, 0.8045122476597412, 0.41090075519334235]], "lo": [1, 0, 1, 1], "top": [5, 3, 5, 4], "w": null, "shared": 0, "rate": 0.05360881524278935, "max": 32, "ks": [4, 0, 4, 4], "best": -1.2333037815216672}, {"qs": [[0.06891409451267794, 0.9884383458326974, 0.5616425803044776, 0.12434868206731287, 0.18895091183296242], [0.5754540765011833, 0.6460678453855536, 0.47051903653846416, 0.9550915165566793, 0.4797420274464338]], "lo": [1, 0], "top": [2, 4], "w": null, "shared": 10, "rate": 0.01132545720453795, "max": 64, "ks": [2, 4], "best": 0.45900703006508414}, {"qs": [[0.137946465586266, 0.6391857477038764, 0.5721610039374884, 0.08462969752710414, 0.5043499277950014], [0.6833075150248155, 0.6384560886576598, 0.7075118822265415, 0.962882166227296, 0.6508735886547153]], "lo": [1, 0], "top": [0, 1], "w": null, "shared": 0, "rate": 0.09147219100701971, "max": 64, "ks": [0, 1], "best": -2.241698736806654}, {"qs": [[0.9913997259067989, 0.7704950366384347, 0.31887467796957514, 0.06512803255661959, 0.3727435719464982], [0.83980966821586, 0.15432133217315325, 0.9405517240526708, 0.7892260699200285, 0.31319594258721006], [0.5182828270502452, 0.09579870148903402, 0.6651554654304633, 0.39608911749230313, 0.21822085911846534], [0.4345970319734139, 0.9825240335914941, 0.0743004060857394, 0.7857654057597258, 0.656354827834713], [0.385893688117663, 0.27855185791809467, 0.14442761217067046, 0.9096204304481398, 0.5191660117222964]], "lo": [1, 1, 0, 0, 1], "top": [5, 1, 1, 4, 4], "w": null, "shared": 2, "rate": 0.049626055792532875, "max": 32, "ks": [3, 1, 1, 2, 2], "best": -1.0962204457988474}, {"qs": [[0.48674682264266966, 0.9563660012308971, 0.3318286152614812, 0.34310024851609155, 0.4947320631068366]], "lo": [1], "top": [5], "w": [1.367881448253209], "shared": 0, "rate": 0.037097295926688506, "max": 64, "ks": [3], "best": -0.4933089556765601}, {"qs": [[0.18220794874309587, 0.06525125747685201, 0.02462611322240238, 0.5219801627830524, 0.9640723785484057]], "lo": [0], "top": [5], "w": null, "shared": 10, "rate": 0.06053809009048147, "max": 64, "ks": [1], "best": -3.598880082128196}, {"qs": [[0.09918165898022491, 0.01828946072952542, 0.4351162957760901, 0.09736600573427501, 0.06386467619200453], [0.2045036358462572, 0.6224140633429593, 0.47953038364550515, 0.6530789775931312, 0.37223046010403094]], "lo": [1, 1], "top": [1, 0], "w": null, "shared": 6, "rate": 0.05116164111904143, "max": 32, "ks": [1, 0], "best": -2.96486826448957}, {"qs": [[0.05873638763199418, 0.5108895621064005, 0.6382683110413714, 0.48679488937843396, 0.10566931755421927], [0.9870056781372479, 0.3462466307540927, 0.2383328588831254, 0.22668106874730576, 0.6763231165289783], [0.7332961167877701, 0.1504935834165415, 0.2584716978012823, 0.7970813335616197, 0.3934156388247032], [0.7913364404837084, 0.17018102342236752, 0.3341739745719061, 0.05116087624501209, 0.7241891496377604], [0.3051458210188991, 0.7965549240980897, 0.17178383687735777, 0.6703180015626594, 0.8157224609158686]], "lo": [0, 0, 1, 1, 1], "top": [5, 5, 3, 3, 0], "w": null, "shared": 8, "rate": 0.08272549333359978, "max": 16, "ks": [0, 1, 1, 1, 0], "best": -4.9991764704687}, {"qs": [[0.4845370296035276, 0.07663883222713963, 0.509886890571026, 0.0007463115486026073, 0.17700420603985778], [0.04517085698304302, 0.7715386440508101, 0.5980056660426363, 0.842321534420923, 0.9581332456306701], [0.36904899510442624, 0.5059945025332401, 0.300450087342215, 0.6822450800450635, 0.41081161415553147], [0.7428042921777033, 0.8436730350118071, 0.48498548883640324, 0.4776438874309815, 0.31297575130095656]], "lo": [0, 1, 0, 1], "top": [1, 1, 3, 0], "w": null, "shared": 2, "rate": 0.04764428109461772, "max": 16, "ks": [1, 1, 3, 0], "best": -1.8000109165291958}, {"qs": [[0.5690889071362634, 0.36709113102284807, 0.3091866167462245, 0.35762054618257066, 0.028886676687442825], [0.33343104138664703, 0.18866032024802748, 0.9864260855928421, 0.28349111670564253, 0.9839954071760034], [0.9522049969362499, 0.31058073515850015, 0.3429701730789849, 0.4342036392117551, 0.05072338809673571], [0.29594759957762573, 0.8402033398435623, 0.6253857296569825, 0.936549454181938, 0.7259077027694804], [0.6835545099291009, 0.40102413204220455, 0.6080214622065377, 0.27219774744618186, 0.1141147788938619]], "lo": [0, 1, 1, 1, 0], "top": [4, 2, 0, 0, 4], "w": [1.8294592357729953, 1.851447061800931, 1.2118919491467501, 1.0322236327982461, 1.7999553223504712], "shared": 6, "rate": 0.021454460559517094, "max": 64, "ks": [3, 2, 0, 0, 4], "best": 1.9122700429245791}, {"qs": [[0.7141474446847441, 0.010803085588685146, 0.5067278938447469, 0.7959564054219401, 0.9882423780242356], [0.3059477267938516, 0.47996015141863824, 0.9347436426093515, 0.5246994571115184, 0.18197558053632323]], "lo": [0, 1], "top": [0, 0], "w": null, "shared": 9, "rate": 0.04921089015783236, "max": 64, "ks": [0, 0], "best": -2.9916776453651024}, {"qs": [[0.47665868098624875, 0.11089312772078008, 0.7453927213319383, 0.7942115224615529, 0.28894859098349157], [0.4069807837212742, 0.6496415915763964, 0.5757991125563281, 0.0891908514696037, 0.9398468343299917], [0.37294461432634174, 0.2956102022829681, 0.24587720453718098, 0.3444365423081769, 0.14541726512142383], [0.5799793387719451, 0.5406067976871519, 0.82248215167369, 0.36013767186669887, 0.8148619441496596], [0.8333804058362584, 0.21783261481501892, 0.7544926735496752, 0.9668971909319871, 0.6584134627190291]], "lo": [1, 0, 0, 0, 1], "top": [2, 2, 3, 0, 2], "w": [0.3721514725019418, 1.5544395593473628, 1.4561686841079309, 1.0944313725124888, 1.6266070399795696], "shared": 3, "rate": 0.0537539230845213, "max": 32, "ks": [1, 2, 2, 0, 2], "best": -1.772464367816076}, {"qs": [[0.9501577379244219, 0.15412375008351775, 0.023594584016344067, 0.690259063345023, 0.06562973038962272]], "lo": [0], "top": [3], "w": null, "shared": 10, "rate": 0.08361064362248057, "max": 32, "ks": [1], "best": -4.271995841448469}, {"qs": [[0.8191409196969148, 0.3277426420219929, 0.8501329503333687, 0.5418094541721977, 0.47731483781187767], [0.18949786453984074, 0.41333780321705127, 0.69805087068609, 0.21636070492717585, 0.22511893587681653], [0.428287466407936, 0.35440452378224774, 0.7268062090940091, 0.4941536677045677, 0.5998438914796127]], "lo": [0, 0, 1], "top": [4, 0, 5], "w": null, "shared": 1, "rate": 0.020130977631010363, "max": 16, "ks": [4, 0, 4], "best": 0.49878117853520654}, {"qs": [[0.7366864815510485, 0.03324319447586732, 0.6409697741461052, 0.6611145360536356, 0.27591575750223585]], "lo": [0], "top": [3], "w": null, "shared": 5, "rate": 0.020670057526373516, "max": 16, "ks": [1], "best": -0.23970502582225728}, {"qs": [[0.5388968711185858, 0.049249776977398296, 0.7553511823337588, 0.16114853581930622, 0.5646638692339603], [0.7129368487046138, 0.8124419675124692, 0.404098767410521, 0.09947999259379903, 0.13889950319890287], [0.07604195256932311, 0.16460848752240187, 0.38761236585680314, 0.8784173374105645, 0.4518843566655931], [0.791041034296873, 0.11859164518886522, 0.2637587311249282, 0.6979350191572024, 0.10669891167189638]], "lo": [0, 0, 1, 1], "top": [2, 3, 0, 1], "w": [1.0452767586231178, 1.2771434912176358, 1.5834359932148327, 1.6087892016431125], "shared": 9, "rate": 0.05848883929881247, "max": 32, "ks": [1, 3, 0, 1], "best": -2.1269258403275217}, {"qs": [[0.873529332284541, 0.578572225616296, 0.9837486031463136, 0.21171070516968082, 0.3973755667588923], [0.37102383071558676, 0.27969402952376976, 0.9067943693128923, 0.11692918828809118, 0.12351936280332021], [0.7932697973435449, 0.3919087148011542, 0.6503525014425087, 0.8770821572777775, 0.5512459236314095], [0.2785064127772472, 0.6093116970461623, 0.740776165503768, 0.5266555670848206, 0.990856705893186], [0.046649688506021136, 0.6586864191700007, 0.06676576345192542, 0.01411927501921606, 0.02156971323268042]], "lo": [1, 0, 0, 0, 1], "top": [2, 1, 0, 1, 0], "w": [1.617081970596867, 1.227704388424825, 0.7861795900781163, 0.47934867068459786, 1.7938853603350484], "shared": 6, "rate": 0.06217213142937727, "max": 32, "ks": [2, 1, 0, 1, 0], "best": -3.0061361551864034}, {"qs": [[0.22077987524834963, 0.9531017136986237, 0.9990689824621788, 0.35462872425768155, 0.33818956710288905], [0.12377676903869073, 0.33888499720515, 0.36133687570143314, 0.9657887554542588, 0.6346254602570118], [0.8758253704717075, 0.7728445724839679, 0.22514353367757334, 0.7246361926317575, 0.16832699406878016]], "lo": [0, 0, 0], "top": [2, 1, 4], "w": [0.5835806353463937, 0.32543511581465295, 1.9169840789253263], "shared": 6, "rate": 0.047667116962828444, "max": 32, "ks": [2, 0, 4], "best": 0.34899707519454637}, {"qs": [[0.7225853899029528, 0.23262109448071344, 0.6642041505106373, 0.3770315333644745, 0.44890377180372587], [0.15347082698416525, 0.6733411658531211, 0.7082611241253268, 0.8393734792865598, 0.6643634498088177], [0.604318686923649, 0.2016795845556938, 0.8390409986530518, 0.8352816598433551, 0.1694978347375854]], "lo": [1, 0, 0], "top": [1, 2, 5], "w": null, "shared": 0, "rate": 0.0768621394926054, "max": 16, "ks": [1, 0, 1], "best": -2.5356534356282667}, {"qs": [[0.4091018310392025, 0.2361535891156331, 0.1453993050641078, 0.9480790840893147, 0.9238506745445526], [0.8111909343679735, 0.2697332057789661, 0.18428921803462273, 0.7499755826918204, 0.17275815470797218], [0.21876798231353733, 0.057539956382767876, 0.34292306733605415, 0.9951725528331035, 0.974194528109328], [0.3056163415631822, 0.18908795531523748, 0.5849999659358707, 0.5938446270181221, 0.6874748270045578]], "lo": [0, 1, 0, 0], "top": [0, 1, 5, 5], "w": null, "shared": 1, "rate": 0.026896143035230663, "max": 64, "ks": [0, 1, 1, 1], "best": -0.8497127276501798}, {"qs": [[0.1359762768991335, 0.10134448767005377, 0.6014196691440381, 0.8351991543339954, 0.06314558280626992], [0.4219648756715527, 0.5820368623062406, 0.5318927580371952, 0.8803340677455983, 0.4485352053934891], [0.2983021014779551, 0.3763551114881737, 0.36844687262405973, 0.4780708909108312, 0.39779790449184893]], "lo": [0, 1, 1], "top": [4, 0, 2], "w": [0.8252481793355402, 0.3087731224195365, 1.8115858208006872], "shared": 2, "rate": 0.012196561355818373, "max": 64, "ks": [1, 0, 2], "best": -0.3075136996014722}, {"qs": [[0.8184693274859725, 0.38473195065942434, 0.09361033242077654, 0.601768169659621, 0.6890797520999341], [0.11909920929685924, 0.04871044654233592, 0.15978266946396746, 0.6199613785390237, 0.1386633319907068], [0.28593498884102075, 0.8925855776624949, 0.9401517055192454, 0.05874900214688483, 0.9574082193750308], [0.7723982078513797, 0.1411686585137244, 0.39669705178856784, 0.5522867999660338, 0.2031334607766383]], "lo": [1, 1, 1, 0], "top": [5, 4, 4, 2], "w": null, "shared": 1, "rate": 0.022137563715028223, "max": 32, "ks": [2, 1, 3, 1], "best": 0.19978988030474865}, {"qs": [[0.39861177910955736, 0.1822018051558122, 0.5444510089015141, 0.8333364659132503, 0.3109093110555413], [0.6369958153744766, 0.5845328647751675, 0.11690412680357243, 0.5181105954741232, 0.6528881422229218]], "lo": [1, 0], "top": [2, 3], "w": [1.4456653741350485, 1.8721875870590008], "shared": 2, "rate": 0.026697414512739936, "max": 16, "ks": [2, 2], "best": 0.6307265896673846}, {"qs": [[0.495271516831184, 0.7350234046622979, 0.7463797287074356, 0.43762512921633157, 0.08542430492072228], [0.522319559206061, 0.8532603784108881, 0.052220636915122465, 0.19890655701653237, 0.3692127808368365], [0.44820211069354343, 0.198544423706576, 0.19213853296687589, 0.8680959387127235, 0.316952658884458]], "lo": [0, 1, 0], "top": [4, 1, 2], "w": [0.6036706720800334, 1.286456566586245, 0.7596298006703661], "shared": 7, "rate": 0.023171193568614745, "max": 32, "ks": [4, 1, 1], "best": -0.5817039741692569}, {"qs": [[0.511256011040827, 0.001025841078596601, 0.17068003214637284, 0.9304534059181802, 0.24457057541785743]], "lo": [1], "top": [4], "w": null, "shared": 9, "rate": 0.011376014900250711, "max": 32, "ks": [1], "best": -0.6915820738309415}, {"qs": [[0.21053731522876484, 0.3828635991346214, 0.9277941740220748, 0.27432249016125476, 0.38418759752034426], [0.28147361928287373, 0.09965379335148505, 0.6247328364353206, 0.7177567616321014, 0.37635410116964485], [0.7239906696114918, 0.4104660675413154, 0.6442122311746497, 0.6977687133361635, 0.43618739803299], [0.9852822494433512, 0.14066626470642674, 0.09898856020094526, 0.18483438972596866, 0.7839873646497597], [0.8743700009189989, 0.006623008943165809, 0.4702071360469895, 0.2308319948831743, 0.2854821139135083]], "lo": [0, 1, 1, 0, 0], "top": [4, 0, 5, 5, 3], "w": null, "shared": 1, "rate": 0.047750772057819184, "max": 16, "ks": [1, 0, 3, 1, 1], "best": -0.42361168260999493}, {"qs": [[0.1687084357586448, 0.7641479357055869, 0.7858737133310131, 0.03465534172000817, 0.41575207966176553], [0.425716007325992, 0.10163396950069858, 0.2959038163630061, 0.21251944350556928, 0.07764563548460102], [0.7011490821741231, 0.8618578923052495, 0.42841247195280585, 0.45760286159196883, 0.2698719450719348]], "lo": [1, 0, 0], "top": [2, 2, 5], "w": null, "shared": 9, "rate": 0.09211572642750142, "max": 32, "ks": [1, 1, 2], "best": -4.934522236092391}, {"qs": [[0.23558284896416604, 0.9319701280238671, 0.15078277792657935, 0.12460447229607952, 0.26904391283449347]], "lo": [0], "top": [0], "w": null, "shared": 4, "rate": 0.037776196830880675, "max": 64, "ks": [0], "best": -1.5432331929351373}, {"qs": [[0.14531221387863957, 0.05908649599419635, 0.43830105288955534, 0.9624113606974122, 0.9264830805206185], [0.6387145314808066, 0.14887931838047963, 0.1568166041536979, 0.5431649472695466, 0.33399240544494446], [0.3055746702358374, 0.297648056104452, 0.286022782928786, 0.5825698140710346, 0.4724938530828495]], "lo": [0, 1, 1], "top": [2, 5, 1], "w": [0.9064524367708235, 1.2103876842820946, 0.48157676222827983], "shared": 6, "rate": 0.010237083459026873, "max": 16, "ks": [1, 4, 1], "best": -0.45186712273202734}, {"qs": [[0.733521850859835, 0.7118285275446282, 0.6273834509812148, 0.12358516303665268, 0.9235770187397125], [0.8518718037739984, 0.0765232457848597, 0.05418315834399656, 0.6522868371704014, 0.45669957572386266]], "lo": [1, 1], "top": [0, 5], "w": [1.5599275411505154, 1.9535409404491606], "shared": 3, "rate": 0.028816115691216334, "max": 16, "ks": [0, 2], "best": -1.2338394390323033}, {"qs": [[0.6518157144755663, 0.23262947956239477, 0.09411857544211133, 0.6097401336113859, 0.6831375709655957], [0.5912944310237496, 0.09509286371529513, 0.7221684201327956, 0.6858442598484884, 0.3469850046804994], [0.07922350698105363, 0.1313808213556228, 0.3784441572981093, 0.8472916497894885, 0.21920529613354411]], "lo": [0, 1, 1], "top": [3, 3, 4], "w": null, "shared": 3, "rate": 0.04135225123348825, "max": 64, "ks": [2, 2, 1], "best": -1.654252263587889}, {"qs": [[0.8594115491230586, 0.8084764111870001, 0.49798594193258283, 0.5378596605214861, 0.37017136135000306], [0.5540939193772411, 0.42135608039417416, 0.10980103642792582, 0.6018913065349921, 0.03466463181844215], [0.1393005365319545, 0.9971176880200856, 0.685841961719907, 0.13919524827624263, 0.9731400549282659]], "lo": [1, 1, 0], "top": [4, 5, 2], "w": [1.1213311099105727, 1.3716202982688122, 1.5534340848963608], "shared": 7, "rate": 0.04104472437662647, "max": 32, "ks": [4, 2, 2], "best": -1.1275418151762362}, {"qs": [[0.5752026479024932, 0.1042498140459327, 0.35339966019122915, 0.9510109292136186, 0.0985559417790588]], "lo": [1], "top": [1], "w": null, "shared": 3, "rate": 0.029280112113631677, "max": 16, "ks": [1], "best": -1.1961511400660811}, {"qs": [[0.3128172453908915, 0.7037230193335298, 0.25973975228451385, 0.1481750662312602, 0.3634625505015904], [0.26589522590756665, 0.28443406508624636, 0.7095499412105991, 0.3707737659923628, 0.2866889379818929]], "lo": [0, 0], "top": [5, 5], "w": [1.6080949688955906, 1.0297478868624368], "shared": 1, "rate": 0.06899037811238022, "max": 32, "ks": [2, 1], "best": -1.9582675708658717}, {"qs": [[0.3028592071349977, 0.8549860067873339, 0.014678996832637625, 0.3210584168023063, 0.3381081974923009]], "lo": [0], "top": [3], "w": null, "shared": 4, "rate": 0.09801799496240342, "max": 64, "ks": [0], "best": -4.004231130204104}, {"qs": [[0.148562246711739, 0.3405487118063585, 0.23234788177483057, 0.7020581843385351, 0.9723784185837716]], "lo": [1], "top": [0], "w": [0.5277176625537275], "shared": 1, "rate": 0.027794399146477224, "max": 64, "ks": [0], "best": -0.7385805685193393}, {"qs": [[0.9356158969532352, 0.195390310918087, 0.017001253647517922, 0.5331889124482294, 0.5570500244662016]], "lo": [1], "top": [3], "w": null, "shared": 4, "rate": 0.028026432325604644, "max": 16, "ks": [2], "best": -1.1410743027589891}, {"qs": [[0.2005744799132423, 0.9979239083688641, 0.6729520751358626, 0.7037517872560675, 0.35187230271252534], [0.7513778467429555, 0.17228548639488528, 0.25316947991146066, 0.913928055859494, 0.1743473339944004], [0.09813887416196487, 0.871449246317853, 0.9493938383911156, 0.023863056165947305, 0.577525702896639]], "lo": [1, 1, 1], "top": [0, 4, 2], "w": [0.5662607521939402, 1.4552375988340531, 1.521586732636045], "shared": 6, "rate": 0.09392150868691483, "max": 64, "ks": [0, 2, 1], "best": -5.677766902754367}, {"qs": [[0.02083132975433044, 0.666551153941061, 0.5897620195654848, 0.9833659423134629, 0.977004810886288], [0.12278502643066613, 0.9109712160393753, 0.20997842906853914, 0.3578523246479104, 0.6615454982949117], [0.8599728381487993, 0.7483044853366907, 0.2529060075417865, 0.6963465075789242, 0.14238773848584896], [0.13234212036544202, 0.04553449107748564, 0.5352879254842313, 0.024379633876620233, 0.8574426567409645], [0.38470153045038014, 0.6119036416567118, 0.800510681022862, 0.7954431731713206, 0.8401621855518078]], "lo": [0, 1, 0, 1, 0], "top": [1, 2, 5, 5, 3], "w": [1.3936335127353758, 1.6879621494491357, 0.5850101634523455, 1.7929528592035173, 1.8424969586035478], "shared": 2, "rate": 0.05570435098086881, "max": 16, "ks": [0, 2, 3, 1, 3], "best": -1.3775668065365512}, {"qs": [[0.09133977522741243, 0.6484042641336549, 0.024047014551384138, 0.7622901514941204, 0.6309937190339869], [0.9545139792972049, 0.2807927811791422, 0.4324111219506963, 0.7696344782192838, 0.26653764350068143]], "lo": [1, 0], "top": [4, 5], "w": [0.949326358716615, 1.2335693454579302], "shared": 8, "rate": 0.013595330277809275, "max": 32, "ks": [2, 4], "best": 0.8335150509150898}, {"qs": [[0.16307037366649058, 0.7992678443836534, 0.3326750761525128, 0.46532323575943957, 0.6354468499953975], [0.03938089998309424, 0.07452691718036142, 0.6483116699437437, 0.9484222203845984, 0.9888161337286325]], "lo": [1, 0], "top": [5, 1], "w": [0.5014891609607857, 0.9001054799899317], "shared": 1, "rate": 0.07906669928548113, "max": 16, "ks": [1, 0], "best": -2.8588937127644267}, {"qs": [[0.2129661890377652, 0.46715743584181046, 0.21917998870386157, 0.9449818327887658, 0.23488505622958022], [0.33453381941476523, 0.7027395705934901, 0.11920555005332645, 0.9881864008765072, 0.25605519742733573]], "lo": [1, 1], "top": [4, 1], "w": [0.9253773755965418, 0.7435355346476212], "shared": 10, "rate": 0.09203540551424873, "max": 16, "ks": [2, 1], "best": -6.439780136747102}, {"qs": [[0.09721554835874247, 0.8134289940612096, 0.036420974453720834, 0.24306345250614347, 0.9990305181608442], [0.3838944002854272, 0.24708811044736279, 0.14555508117990446, 0.24613084068713353, 0.17934438069049696], [0.9717111816694262, 0.22928367968476138, 0.42995432158624447, 0.6915093797898533, 0.3393343728675775], [0.19854623746904287, 0.6899045648102132, 0.06974390758168258, 0.6711701770984744, 0.050692703782559856], [0.6965821755007949, 0.4136869275668775, 0.7776995619014462, 0.3157710036861233, 0.6589162030725078]], "lo": [0, 0, 0, 1, 1], "top": [2, 0, 4, 2, 0], "w": [1.6380519695603033, 1.5514351014224081, 0.6121229792846904, 0.7932439258697163, 1.0052299495682044], "shared": 0, "rate": 0.011348327137890503, "max": 32, "ks": [2, 0, 3, 2, 0], "best": 0.47846418871741025}, {"qs": [[0.25329945736751636, 0.2918589336232673, 0.12806666419361556, 0.8992372675965217, 0.5184435445277933], [0.081133852193503, 0.20855039177880152, 0.42511670345968144, 0.07368365762027274, 0.2971543902542554], [0.3110234572399899, 0.3578335423475425, 0.7678041891608862, 0.40317972970599136, 0.06358472930392112]], "lo": [0, 1, 1], "top": [0, 0, 4], "w": null, "shared": 5, "rate": 0.05711916193197749, "max": 64, "ks": [0, 0, 3], "best": -3.275698106216086}, {"qs": [[0.10517781013896244, 0.682608754842657, 0.08012120412995982, 0.8892934125007683, 0.9683430700940457], [0.04291559356411745, 0.5349992660384738, 0.18390560135546852, 0.6603497498500512, 0.09024598664095873], [0.0007309562546924386, 0.01977569677290325, 0.2978516527815761, 0.4330942234406041, 0.36726039698573687]], "lo": [1, 1, 1], "top": [5, 5, 2], "w": null, "shared": 0, "rate": 0.025799103407299306, "max": 32, "ks": [2, 1, 1], "best": -1.1468769536345629}, {"qs": [[0.6378764996203935, 0.38607713288322865, 0.03639277677021302, 0.2084777253750827, 0.7631094955307385], [0.3199406207710088, 0.3344031000346901, 0.8002171312746647, 0.2432231207027492, 0.1946522587744186], [0.4329378938927503, 0.34060709488529295, 0.5791207245457637, 0.06162360289623581, 0.34050704960267997], [0.2901253211381599, 0.0712739074869998, 0.2524701756707177, 0.6259290372821544, 0.244931896765056]], "lo": [1, 1, 1, 1], "top": [0, 2, 5, 3], "w": null, "shared": 10, "rate": 0.09724003133515804, "max": 16, "ks": [0, 1, 1, 1], "best": -7.374854146513222}, {"qs": [[0.645172324287819, 0.7306229767834371, 0.04632713003804667, 0.002546363432442078, 0.26401128340553137], [0.3832190295987087, 0.2234051421218387, 0.49211727764827107, 0.3827119881023662, 0.7568124878061792], [0.11768376783822998, 0.7591119127420575, 0.31362110109055585, 0.3951996084302899, 0.6614662414366869], [0.04686104102780453, 0.4578264344538615, 0.7835956550271712, 0.07891376689897345, 0.40503207531061736], [0.8239932362294458, 0.8704417847298708, 0.10655048555025637, 0.713138686540165, 0.5802953069435555]], "lo": [0, 0, 1, 0, 1], "top": [5, 4, 5, 2, 4], "w": [1.7404021882848693, 1.1033968172762545, 0.9244896987762605, 1.1841942788736197, 1.0907277160289706], "shared": 6, "rate": 0.033720873981716644, "max": 64, "ks": [2, 1, 1, 0, 2], "best": 0.5909502325619056}], "weights": [{"tpr": [null, 4.794658937001821, 3.685054470516473, null, null], "alpha": 2.0, "w": [1.8283096009343416, 0.31812243070013313, 0.5385448878363431, 1.8283096009343416, 1.8283096009343416]}, {"tpr": [4.02619619157546, null, 2.9368265401726057], "alpha": 1.0, "w": [0.7420587824725297, 1.4938371219580109, 1.0173138260118106]}, {"tpr": [null], "alpha": 0.5, "w": [1.0]}, {"tpr": [null, 3.5458853353326867, 0.8913138136814351, null], "alpha": 2.0, "w": [0.877428499765013, 0.2791401791340076, 4.4178447835298815, 0.877428499765013]}, {"tpr": [4.131322373346725, 2.5315278445704466, 5.318928907586978, 2.3591463922495057], "alpha": 0.0, "w": [1.0, 1.0, 1.0, 1.0]}, {"tpr": [5.324567486411354, null, 0.9559033471855839], "alpha": 0.5, "w": [0.7636048649895534, 1.2459363508007015, 1.802203643787605]}, {"tpr": [null, null, null, null], "alpha": 2.0, "w": [1.0, 1.0, 1.0, 1.0]}, {"tpr": [null, null], "alpha": 0.0, "w": [1.0, 1.0]}, {"tpr": [5.7501035098559266, 5.606395739661705, 4.2490718918249835], "alpha": 2.0, "w": [0.8032106398418911, 0.8449155010171742, 1.470932080199021]}, {"tpr": [5.872493450085568, 5.548387159466291, 4.103539915629618, null], "alpha": 0.5, "w": [0.8798856443172808, 0.9052199643281424, 1.0525874109153313, 1.5077262800764148]}, {"tpr": [1.8790123599948967, null, null, null], "alpha": 0.0, "w": [1.0, 1.0, 1.0, 1.0]}, {"tpr": [5.307072837447161, 2.3223077396443417], "alpha": 0.0, "w": [1.0, 1.0]}, {"tpr": [5.735118936773338, null], "alpha": 0.5, "w": [0.8479730053977719, 1.4359464478219885]}, {"tpr": [0.7625600879574669, null], "alpha": 0.5, "w": [1.3829965859228446, 0.8539710448756329]}, {"tpr": [2.970995962396632, null, 0.5316169270913367], "alpha": 0.0, "w": [1.0, 1.0, 1.0]}, {"tpr": [null, null, 3.076915228377856, null, 4.916151367572323], "alpha": 0.5, "w": [1.2046018287364328, 1.2046018287364328, 0.9711822813700894, 1.2046018287364328, 0.7683266421307231]}, {"tpr": [1.016764315314104, null, 0.956934880175708], "alpha": 0.0, "w": [1.0, 1.0, 1.0]}, {"tpr": [null, 4.791577068132863, 4.476712030102529], "alpha": 0.5, "w": [1.393229031558052, 0.9001155708063164, 0.9312320916203636]}, {"tpr": [null, 4.728905211305002, 1.609418612519658], "alpha": 2.0, "w": [1.5640521634592506, 0.27976282638741223, 2.415311702644937]}, {"tpr": [null, null, 0.5636670889145418, null, null], "alpha": 2.0, "w": [0.5672659881965132, 0.5672659881965132, 7.141696335499115, 0.5672659881965132, 0.5672659881965132]}, {"tpr": [null, null], "alpha": 2.0, "w": [1.0, 1.0]}, {"tpr": [null, 0.8071135453965481, null], "alpha": 0.5, "w": [0.9120753520434044, 1.4357485930354283, 0.9120753520434044]}, {"tpr": [null], "alpha": 0.0, "w": [1.0]}, {"tpr": [1.4429239763798436, null, null, null], "alpha": 2.0, "w": [1.6300866232323543, 0.8484721506903037, 0.8484721506903037, 0.8484721506903037]}, {"tpr": [3.2771222139220706, null], "alpha": 1.0, "w": [0.8051457756905552, 1.3192805534805176]}, {"tpr": [4.490511464210821, 3.542347532582484, null], "alpha": 1.0, "w": [0.7447450828081056, 0.9440875864109087, 1.6721431661322175]}, {"tpr": [5.077715457379488], "alpha": 1.0, "w": [1.0]}, {"tpr": [null, 3.000572736363415, null], "alpha": 0.0, "w": [1.0, 1.0, 1.0]}, {"tpr": [null, null, null], "alpha": 0.0, "w": [1.0, 1.0, 1.0]}, {"tpr": [null, 4.7505535066851285, null], "alpha": 1.0, "w": [1.4584255844475216, 0.6140023819940894, 1.4584255844475216]}, {"tpr": [2.865435450765925], "alpha": 1.0, "w": [1.0]}, {"tpr": [null, 1.0886086748498944, null], "alpha": 1.0, "w": [0.8481014458083157, 1.5581383198609149, 0.8481014458083157]}, {"tpr": [null, 3.8546818347181486, null, null], "alpha": 0.5, "w": [1.1228392462986607, 0.8087947308248188, 1.1228392462986607, 1.1228392462986607]}, {"tpr": [1.1857752932063927, null, 3.7925991284954548], "alpha": 2.0, "w": [3.08839744742096, 1.0856203805283946, 0.30190068422702443]}, {"tpr": [null, null, null], "alpha": 2.0, "w": [1.0, 1.0, 1.0]}, {"tpr": [4.700937155700164, null, 4.434871265732102, null, null], "alpha": 1.0, "w": [0.6439485540911436, 1.5135808421432266, 0.6825816360617029, 1.5135808421432266, 1.5135808421432266]}, {"tpr": [null, null, null], "alpha": 0.0, "w": [1.0, 1.0, 1.0]}, {"tpr": [null, null, null, 1.3128852690192887], "alpha": 0.5, "w": [0.9596429642633167, 0.9596429642633167, 0.9596429642633167, 1.184435167439252]}, {"tpr": [null], "alpha": 0.5, "w": [1.0]}, {"tpr": [3.6813013470065887, null], "alpha": 0.5, "w": [0.8884372706216526, 1.2053480129681204]}, {"tpr": [null, 4.947682851693272, 5.141843772519053, null, null], "alpha": 1.0, "w": [1.6089526624212325, 0.6503863366547805, 0.6258271287900241, 1.6089526624212325, 1.6089526624212325]}, {"tpr": [4.791604336422606, null, null], "alpha": 1.0, "w": [0.6115978225772001, 1.4652673894037676, 1.4652673894037676]}, {"tpr": [4.402891075430584, 4.824226030369299, 1.3982651878500556, null, 5.750681488331065], "alpha": 1.0, "w": [0.8347271584584409, 0.7618243285576026, 2.628408965860856, 1.8376063781981002, 0.6390916909332781]}, {"tpr": [null, null, 2.5383958806847597, null, null], "alpha": 0.0, "w": [1.0, 1.0, 1.0, 1.0, 1.0]}, {"tpr": [null, null], "alpha": 0.0, "w": [1.0, 1.0]}, {"tpr": [2.952015250435555, null, null], "alpha": 1.0, "w": [0.7850021833288091, 1.1586692084059258, 1.1586692084059258]}, {"tpr": [2.6714505332975707, null, null], "alpha": 2.0, "w": [0.6801981590413443, 1.2135836996446365, 1.2135836996446365]}, {"tpr": [3.944065010092161, 5.788417647687585, 5.516806681490848, null], "alpha": 0.5, "w": [1.0650781964323934, 0.8791714918272053, 0.900553759723298, 1.4956794571949563]}, {"tpr": [1.5989559251817256, 5.332540694637602, 5.213199633415875], "alpha": 2.0, "w": [4.727730776924676, 0.42506697664477605, 0.4447510793641785]}, {"tpr": [1.5311240796660521], "alpha": 0.0, "w": [1.0]}, {"tpr": [2.751153192531856, null, 3.4679695318846493], "alpha": 1.0, "w": [0.9958397017812661, 1.369853787402751, 0.7900033577620916]}, {"tpr": [1.3380187183617744], "alpha": 0.0, "w": [1.0]}, {"tpr": [1.4815623695874554, null, null, null, 4.077159762614736], "alpha": 0.5, "w": [1.269709611910865, 1.0928218728490244, 1.0928218728490244, 1.0928218728490244, 0.7653948080258964]}, {"tpr": [0.5480853436025876], "alpha": 0.0, "w": [1.0]}, {"tpr": [null, 4.149152793009108, null, 4.518827029452558, null], "alpha": 1.0, "w": [1.4667979822461665, 0.7070349323927376, 1.4667979822461665, 0.6491941261243029, 1.4667979822461665]}, {"tpr": [null, null], "alpha": 0.5, "w": [1.0, 1.0]}, {"tpr": [5.235428999631989, 1.6534712616899243, null], "alpha": 0.0, "w": [1.0, 1.0, 1.0]}, {"tpr": [null], "alpha": 0.0, "w": [1.0]}, {"tpr": [3.117278942363494, 2.0005309761198458, 3.7834068043418543, null, null], "alpha": 0.5, "w": [0.9187219632030387, 1.1468306507443256, 0.8339315629479325, 1.1469828755637912, 1.1469828755637912]}, {"tpr": [null, null, null], "alpha": 0.0, "w": [1.0, 1.0, 1.0]}]} \ No newline at end of file diff --git a/zig/src/families/deepseek_v41/draft/fixtures/lookup-golden.json b/zig/src/families/deepseek_v41/draft/fixtures/lookup-golden.json new file mode 100644 index 0000000000..fd9b5a5fc6 --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/fixtures/lookup-golden.json @@ -0,0 +1 @@ +{"generator": "tools/lookup_golden.py on the reference 8474f31 lookup.py", "scenarios": [{"costs": {"verify": [42.0, 47.0, 52.0, 57.0, 62.0, 67.0, 72.0, 77.0, 82.0, 87.0, 92.0, 97.0, 102.0, 107.0, 112.0, 117.0], "draft": 5.6}, "min_match": 4, "max_rows": 16, "eos": [2], "prompt": [17, 33, 13, 19, 36, 29, 3, 5, 20, 17, 16, 20, 33, 20, 35, 24, 25, 24, 21, 33, 15, 28, 7, 17, 18, 22, 22, 29, 23, 3, 38, 13, 9, 34, 29, 32, 27, 28, 36, 19, 21, 36, 38, 10, 15, 2, 5, 37, 34, 20, 17, 16, 20, 4, 4, 18, 15, 18, 24, 9, 19, 23, 28, 30, 16, 14, 12, 21, 30, 21, 10, 28, 13, 33, 17, 19, 19, 37, 7, 22, 6, 5, 15, 34, 29, 32, 27, 28, 36, 21, 27, 23, 17], "rounds": [{"left": 341, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [3, 22, 7, 8]}, {"left": 337, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [28]}, {"left": 336, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [36, 37, 9]}, {"left": 333, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [12, 26]}, {"left": 331, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [13, 8, 34]}, {"left": 328, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [20, 17, 16]}, {"left": 325, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [20, 4]}, {"left": 323, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [4, 18, 25]}, {"left": 320, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [7, 32, 13]}, {"left": 317, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [4, 5, 17]}, {"left": 314, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [34]}, {"left": 313, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [21, 22]}, {"left": 311, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [18, 13, 9, 34, 29, 32]}, {"left": 305, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [27]}, {"left": 304, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [28, 36]}, {"left": 302, "kind": "l", "drafts": [19, 21, 36, 38], "rows": 5, "kept": 0, "emitted": [16]}, {"left": 301, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [12, 32]}, {"left": 299, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [29, 15]}, {"left": 297, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [4, 37, 32]}, {"left": 294, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [16, 37, 14]}, {"left": 291, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [9]}, {"left": 290, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [19, 11]}, {"left": 288, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [17, 18]}, {"left": 286, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [26, 34]}, {"left": 284, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [15]}, {"left": 283, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [26]}, {"left": 282, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [17]}, {"left": 281, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [32]}, {"left": 280, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [14, 5]}, {"left": 278, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [34, 12]}, {"left": 276, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [32]}, {"left": 275, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [29, 15, 24, 25]}, {"left": 271, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [24]}, {"left": 270, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [21, 33]}, {"left": 268, "kind": "l", "drafts": [15, 28], "rows": 3, "kept": 2, "emitted": [15, 28, 7]}, {"left": 265, "kind": "l", "drafts": [17, 18], "rows": 3, "kept": 2, "emitted": [17, 18, 22]}, {"left": 262, "kind": "l", "drafts": [22, 29, 23, 3, 38], "rows": 6, "kept": 3, "emitted": [22, 29, 23, 24]}, {"left": 258, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [21, 33, 15]}, {"left": 255, "kind": "l", "drafts": [28, 7, 17, 18, 22], "rows": 6, "kept": 5, "emitted": [28, 7, 17, 18, 22, 22]}, {"left": 249, "kind": "l", "drafts": [29, 23, 24, 21, 33], "rows": 6, "kept": 2, "emitted": [29, 23, 3]}, {"left": 246, "kind": "l", "drafts": [38, 13, 9, 34, 29], "rows": 6, "kept": 5, "emitted": [38, 13, 9, 34, 29, 32]}, {"left": 240, "kind": "l", "drafts": [27, 28, 36, 19, 21, 36, 38], "rows": 8, "kept": 3, "emitted": [27, 28, 36, 37]}, {"left": 236, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [14, 9, 19, 16]}, {"left": 232, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [11, 35, 24]}, {"left": 229, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [25, 24, 21, 33]}, {"left": 225, "kind": "l", "drafts": [15, 28, 7, 17, 18, 22, 22, 29, 23, 3, 38], "rows": 12, "kept": 0, "emitted": [38]}, {"left": 224, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [10]}, {"left": 223, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [15]}, {"left": 222, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [21, 27, 23]}, {"left": 219, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [17]}, {"left": 218, "kind": "l", "drafts": [3, 22, 7, 8, 28], "rows": 6, "kept": 0, "emitted": [10]}, {"left": 217, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [26, 26, 16, 6, 13]}, {"left": 212, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [12, 26, 13]}, {"left": 209, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [8, 34]}, {"left": 207, "kind": "l", "drafts": [20, 17, 16], "rows": 4, "kept": 3, "emitted": [20, 17, 16, 20]}, {"left": 203, "kind": "l", "drafts": [4, 4, 18, 25, 7, 32, 13], "rows": 8, "kept": 1, "emitted": [4, 38]}, {"left": 201, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [32, 32, 35, 25]}, {"left": 197, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [39]}, {"left": 196, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [5]}, {"left": 195, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [23]}, {"left": 194, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [35, 12]}, {"left": 192, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [25]}, {"left": 191, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [16, 39]}, {"left": 189, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [18]}, {"left": 188, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [25, 13]}, {"left": 186, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [33]}, {"left": 185, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [23]}, {"left": 184, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [4, 19, 21, 36]}, {"left": 180, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [38]}, {"left": 179, "kind": "l", "drafts": [10, 15], "rows": 3, "kept": 1, "emitted": [10, 4]}, {"left": 177, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [18, 25, 20, 16, 27, 8]}, {"left": 171, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [12]}, {"left": 170, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [39]}, {"left": 169, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [23]}, {"left": 168, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [26, 36]}, {"left": 166, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [15]}, {"left": 165, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [4, 29, 22, 35, 9, 9]}, {"left": 159, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [24]}, {"left": 158, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [34, 14]}, {"left": 156, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [33]}, {"left": 155, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [35, 21]}, {"left": 153, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [4, 15, 9]}, {"left": 150, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [24]}, {"left": 149, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [10, 15, 6]}, {"left": 146, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [13]}, {"left": 145, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [12]}, {"left": 144, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [26]}, {"left": 143, "kind": "l", "drafts": [13, 8, 34, 20, 17], "rows": 6, "kept": 5, "emitted": [13, 8, 34, 20, 17, 16]}, {"left": 137, "kind": "l", "drafts": [20, 4, 38, 32, 32, 35, 25], "rows": 8, "kept": 2, "emitted": [20, 4, 4]}, {"left": 134, "kind": "l", "drafts": [18, 25, 7, 32, 13, 4], "rows": 7, "kept": 1, "emitted": [18, 30]}, {"left": 132, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [11, 17, 18]}, {"left": 129, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [26]}, {"left": 128, "kind": "l", "drafts": [34, 15, 26, 17, 32, 14, 5, 34], "rows": 9, "kept": 5, "emitted": [34, 15, 26, 17, 32, 37]}, {"left": 122, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [32, 16]}, {"left": 120, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [37, 14]}, {"left": 118, "kind": "l", "drafts": [9, 19, 11, 17, 18, 26, 34, 15], "rows": 9, "kept": 2, "emitted": [9, 19, 29]}, {"left": 115, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [32, 27]}, {"left": 113, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [28, 36]}, {"left": 111, "kind": "l", "drafts": [37, 14, 9, 19, 16, 11, 35], "rows": 8, "kept": 0, "emitted": [38]}, {"left": 110, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [24, 5]}, {"left": 108, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [4]}, {"left": 107, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [32, 35]}, {"left": 105, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [2]}, {"left": 104, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [7, 17, 21]}, {"left": 101, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [4, 12, 19, 15]}, {"left": 97, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [28, 21]}, {"left": 95, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [20, 14]}, {"left": 93, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [13, 39]}, {"left": 91, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [11, 28]}, {"left": 89, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [35]}, {"left": 88, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [21, 10, 15]}, {"left": 85, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [21, 27, 23, 17]}, {"left": 81, "kind": "l", "drafts": [10, 26, 26, 16, 6], "rows": 6, "kept": 0, "emitted": [34]}, {"left": 80, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [9, 34]}, {"left": 78, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [29, 32, 27]}, {"left": 75, "kind": "l", "drafts": [28, 36, 37, 14], "rows": 5, "kept": 2, "emitted": [28, 36, 9]}, {"left": 72, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [19]}, {"left": 71, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [13, 37, 30, 11]}, {"left": 67, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [17]}, {"left": 66, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [18, 14]}, {"left": 64, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [9, 19, 26]}, {"left": 61, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [34, 20, 33]}, {"left": 58, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [24, 3, 6, 12, 21]}, {"left": 53, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [4, 15, 4]}, {"left": 50, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [37, 32, 16, 37, 14]}, {"left": 45, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [9, 19, 25, 3]}, {"left": 41, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [39, 20, 35, 20]}, {"left": 37, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [6, 30]}, {"left": 35, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [21, 36]}, {"left": 33, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [38]}, {"left": 32, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [10]}, {"left": 31, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [15]}, {"left": 30, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [13]}, {"left": 29, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [29, 23, 21]}, {"left": 26, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [2, 32, 10, 3, 4]}, {"left": 21, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [29, 34]}, {"left": 19, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [36, 33]}, {"left": 17, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [27]}, {"left": 16, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [23, 17, 15, 9]}, {"left": 12, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [31, 4]}, {"left": 10, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [4]}, {"left": 9, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [9, 24, 6, 17, 10]}, {"left": 4, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [15, 21, 27, 23]}]}, {"costs": {"verify": [21.291, 24.948500000000003, 28.631999999999998, 32.341499999999996, 36.077, 39.8385, 43.626000000000005, 47.439499999999995, 51.279, 55.144499999999994, 59.036, 62.9535, 66.89699999999999, 70.8665, 74.862, 78.8835, 82.931, 87.00450000000001, 91.104, 95.22949999999999, 99.381, 103.5585, 107.762, 111.99149999999999, 116.24699999999999, 120.5285, 124.83599999999998, 129.1695, 133.529, 137.9145, 142.326, 146.7635, 151.227, 155.7165, 160.232, 164.7735, 169.341, 173.93449999999999, 178.55399999999997, 183.1995, 187.871, 192.5685, 197.29199999999997, 202.04149999999998, 206.817, 211.61849999999998, 216.446, 221.29949999999997, 226.17899999999997, 231.0845, 236.016, 240.97349999999997, 245.95699999999997, 250.9665, 256.002, 261.0635, 266.15099999999995, 271.2645, 276.404, 281.5695, 286.76099999999997, 291.9785, 297.222, 302.4915], "draft": 3.677}, "min_match": 4, "max_rows": 16, "eos": [2], "prompt": [44, 11, 35, 14, 7, 16, 5, 12, 9, 41, 27, 13, 29, 26, 39, 14, 10, 25, 36, 6, 16, 24, 36, 35, 5, 45, 7, 24, 20, 42, 22, 31, 43, 7, 41, 16, 3, 30, 40, 24, 15, 11, 37, 39, 9, 43, 29, 46, 11, 9, 15, 38, 36, 26, 9, 7, 38, 36, 33, 33, 13, 35, 28, 5, 12, 9, 41, 16, 3, 10, 22, 41, 19, 26, 8, 38, 3, 22, 14, 28, 21, 41, 13, 17, 4, 36, 25, 32, 10, 42, 6, 42, 29, 25, 32, 10, 42, 32, 19, 24, 17, 10, 34, 15, 38, 36, 26, 9, 7, 42, 24, 36, 23, 33, 4, 32, 19, 24, 29, 26, 39, 9, 19, 30, 40, 7, 37, 38, 28, 8, 22, 25, 5, 43, 25, 13, 23, 21, 41, 9, 33, 46, 27, 19, 15, 43, 4, 14, 33, 29, 26, 39, 35, 4, 18, 7, 46, 44, 38, 7, 40, 31, 42, 40, 31, 6, 44, 11, 12, 10, 33, 44, 26, 33, 4, 45, 31, 6, 44, 11, 12, 13, 29, 26, 39, 12, 9, 41, 16, 45, 11, 25, 21, 21, 14, 10, 25, 36, 16, 39, 8, 38, 27, 13, 28, 31, 37, 17, 43, 33, 29, 5, 33, 29, 38, 34, 21, 17, 27, 23, 33, 4, 32, 19, 24, 40, 10, 34, 15, 38, 36, 26, 9, 7, 42, 24, 40, 26, 33, 4, 45, 31, 6, 44, 11, 12, 10, 33, 44, 22, 25, 7, 16, 37, 31, 16, 16, 3, 30, 40, 26, 33, 4, 45, 31, 6, 44, 11, 12, 10], "rounds": [{"left": 500, "kind": "l", "drafts": [33, 44, 22], "rows": 4, "kept": 0, "emitted": [32]}, {"left": 499, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [32, 41]}, {"left": 497, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [10, 18, 20]}, {"left": 494, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [21]}, {"left": 493, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [28]}, {"left": 492, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [8, 22]}, {"left": 490, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [25, 6]}, {"left": 488, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [26]}, {"left": 487, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [30, 14, 10]}, {"left": 484, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [25]}, {"left": 483, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [36]}, {"left": 482, "kind": "l", "drafts": [16, 39], "rows": 3, "kept": 0, "emitted": [11]}, {"left": 481, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [38, 9]}, {"left": 479, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [29, 23, 45, 36]}, {"left": 475, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [4, 40, 3, 43, 36, 23]}, {"left": 469, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [33]}, {"left": 468, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [4, 32, 19, 24, 9, 4]}, {"left": 462, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [13, 28, 21, 35]}, {"left": 458, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [7]}, {"left": 457, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [38, 44]}, {"left": 455, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [14, 26, 9]}, {"left": 452, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [7]}, {"left": 451, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [42]}, {"left": 450, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [24, 36, 23]}, {"left": 447, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [33, 4]}, {"left": 445, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [32]}, {"left": 444, "kind": "l", "drafts": [19, 24], "rows": 3, "kept": 1, "emitted": [19, 37]}, {"left": 442, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [38, 28]}, {"left": 440, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [8, 22, 25]}, {"left": 437, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [16, 3, 30, 40]}, {"left": 433, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [24]}, {"left": 432, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [15]}, {"left": 431, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [11, 37]}, {"left": 429, "kind": "l", "drafts": [39, 9], "rows": 3, "kept": 0, "emitted": [31]}, {"left": 428, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [16, 40]}, {"left": 426, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [26, 33, 31]}, {"left": 423, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [22, 43, 16, 31]}, {"left": 419, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [5]}, {"left": 418, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [37]}, {"left": 417, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [36, 44, 42, 20, 10]}, {"left": 412, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [33, 8, 19, 28, 36, 14]}, {"left": 406, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [10]}, {"left": 405, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [25]}, {"left": 404, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [36]}, {"left": 403, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [33, 4]}, {"left": 401, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [45, 31, 6]}, {"left": 398, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [44, 11, 12]}, {"left": 395, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [10, 33]}, {"left": 393, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [44, 44, 42, 15]}, {"left": 389, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [35, 42]}, {"left": 387, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [12, 4, 39, 9]}, {"left": 383, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [43]}, {"left": 382, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [29]}, {"left": 381, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [46, 11]}, {"left": 379, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [9]}, {"left": 378, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [19]}, {"left": 377, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [30]}, {"left": 376, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [40]}, {"left": 375, "kind": "l", "drafts": [7], "rows": 2, "kept": 1, "emitted": [7, 37]}, {"left": 373, "kind": "l", "drafts": [38, 28], "rows": 3, "kept": 2, "emitted": [38, 28, 8]}, {"left": 370, "kind": "l", "drafts": [22], "rows": 2, "kept": 1, "emitted": [22, 25]}, {"left": 368, "kind": "l", "drafts": [5, 43], "rows": 3, "kept": 0, "emitted": [3]}, {"left": 367, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [30]}, {"left": 366, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [40, 24]}, {"left": 364, "kind": "l", "drafts": [15, 11, 37, 31], "rows": 5, "kept": 0, "emitted": [35]}, {"left": 363, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [42, 25]}, {"left": 361, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [16]}, {"left": 360, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [29, 25, 31, 6]}, {"left": 356, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [44]}, {"left": 355, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [7, 42]}, {"left": 353, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [24, 36, 23]}, {"left": 350, "kind": "l", "drafts": [33, 4], "rows": 3, "kept": 2, "emitted": [33, 4, 32]}, {"left": 347, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [19, 24]}, {"left": 345, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [38]}, {"left": 344, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [40, 41]}, {"left": 342, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [19]}, {"left": 341, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [26, 26]}, {"left": 339, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [5, 27, 9, 43]}, {"left": 335, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [29, 46, 11, 9, 19]}, {"left": 330, "kind": "l", "drafts": [30, 40, 7], "rows": 4, "kept": 1, "emitted": [30, 17]}, {"left": 328, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [33, 43, 30, 14]}, {"left": 324, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [5]}, {"left": 323, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [15]}, {"left": 322, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [19]}, {"left": 321, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [35, 32, 21, 6]}, {"left": 317, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [23]}, {"left": 316, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [33]}, {"left": 315, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [4, 32, 19, 24, 40]}, {"left": 310, "kind": "l", "drafts": [10, 34], "rows": 3, "kept": 0, "emitted": [39]}, {"left": 309, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [46, 17]}, {"left": 307, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [30]}, {"left": 306, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [38]}, {"left": 305, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [38, 27, 13, 29]}, {"left": 301, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [26, 39, 15]}, {"left": 298, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [8, 38, 27, 13]}, {"left": 294, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [29]}, {"left": 293, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [31]}, {"left": 292, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [6, 44, 11]}, {"left": 289, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [12]}, {"left": 288, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [10]}, {"left": 287, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [33, 44, 10, 5]}, {"left": 283, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [35]}, {"left": 282, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [20]}, {"left": 281, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [21]}, {"left": 280, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [17]}, {"left": 279, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [36, 40, 26, 22]}, {"left": 275, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [5]}, {"left": 274, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [32]}, {"left": 273, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [30]}, {"left": 272, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [40, 24, 15]}, {"left": 269, "kind": "l", "drafts": [11, 37], "rows": 3, "kept": 2, "emitted": [11, 37, 31]}, {"left": 266, "kind": "l", "drafts": [16, 40, 26], "rows": 4, "kept": 1, "emitted": [16, 3]}, {"left": 264, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [30]}, {"left": 263, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [40]}, {"left": 262, "kind": "l", "drafts": [24, 15, 11], "rows": 4, "kept": 3, "emitted": [24, 15, 11, 37]}, {"left": 258, "kind": "l", "drafts": [31, 16], "rows": 3, "kept": 2, "emitted": [31, 16, 25]}, {"left": 255, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [36, 40, 24, 15]}, {"left": 251, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [11]}, {"left": 250, "kind": "l", "drafts": [37, 31, 16, 25], "rows": 5, "kept": 0, "emitted": [36]}, {"left": 249, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [40, 26, 33]}, {"left": 246, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [4, 45, 31, 6, 44]}, {"left": 241, "kind": "l", "drafts": [11, 12], "rows": 3, "kept": 2, "emitted": [11, 12, 10]}, {"left": 238, "kind": "l", "drafts": [32, 32, 41], "rows": 4, "kept": 0, "emitted": [33]}, {"left": 237, "kind": "l", "drafts": [44, 22], "rows": 3, "kept": 1, "emitted": [44, 39]}, {"left": 235, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [18, 23]}, {"left": 233, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [36, 34]}, {"left": 231, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [39, 16, 35, 9]}, {"left": 227, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [4, 17, 10]}, {"left": 224, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [34, 15, 37]}, {"left": 221, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [31]}, {"left": 220, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [16, 41]}, {"left": 218, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [10]}, {"left": 217, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [28, 26, 40]}, {"left": 214, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [40]}, {"left": 213, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [16]}, {"left": 212, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [13]}, {"left": 211, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [13]}, {"left": 210, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [23, 46, 28, 8]}, {"left": 206, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [22, 25]}, {"left": 204, "kind": "l", "drafts": [3, 30, 40], "rows": 4, "kept": 0, "emitted": [9]}, {"left": 203, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [7, 42, 24, 36, 23]}, {"left": 198, "kind": "l", "drafts": [33, 4], "rows": 3, "kept": 0, "emitted": [6]}, {"left": 197, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [26, 30]}, {"left": 195, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [14, 10, 25]}, {"left": 192, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [2, 8, 3, 7]}, {"left": 188, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [17, 45]}, {"left": 186, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [39, 20]}, {"left": 184, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [40, 8, 37, 31, 16]}, {"left": 179, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [32, 38]}, {"left": 177, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [9, 36, 46]}, {"left": 174, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [10, 10]}, {"left": 172, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [46]}, {"left": 171, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [28, 40, 36, 10, 33]}, {"left": 166, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [44, 16, 17]}, {"left": 163, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [21, 20]}, {"left": 161, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [20, 35, 12, 32]}, {"left": 157, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [36, 13]}, {"left": 155, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [15, 35, 21, 16, 38]}, {"left": 150, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [7, 31, 43, 42]}, {"left": 146, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [27]}, {"left": 145, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [45]}, {"left": 144, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [4]}, {"left": 143, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [7]}, {"left": 142, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [36]}, {"left": 141, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [44, 26]}, {"left": 139, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [9]}, {"left": 138, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [7]}, {"left": 137, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [42, 24, 36]}, {"left": 134, "kind": "l", "drafts": [23, 33], "rows": 3, "kept": 2, "emitted": [23, 33, 4]}, {"left": 131, "kind": "l", "drafts": [32, 19, 37], "rows": 4, "kept": 2, "emitted": [32, 19, 24]}, {"left": 128, "kind": "l", "drafts": [29, 26, 39], "rows": 4, "kept": 0, "emitted": [17]}, {"left": 127, "kind": "l", "drafts": [10, 34, 15], "rows": 4, "kept": 0, "emitted": [36]}, {"left": 126, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [40, 26, 33, 4]}, {"left": 122, "kind": "l", "drafts": [45, 31], "rows": 3, "kept": 2, "emitted": [45, 31, 6]}, {"left": 119, "kind": "l", "drafts": [44, 11], "rows": 3, "kept": 2, "emitted": [44, 11, 11]}, {"left": 116, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [3]}, {"left": 115, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [12]}, {"left": 114, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [15, 37]}, {"left": 112, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [11, 40]}, {"left": 110, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [9]}, {"left": 109, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [7, 42]}, {"left": 107, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [24, 36]}, {"left": 105, "kind": "l", "drafts": [23, 33, 4], "rows": 4, "kept": 3, "emitted": [23, 33, 4, 32]}, {"left": 101, "kind": "l", "drafts": [19, 24, 17], "rows": 4, "kept": 2, "emitted": [19, 24, 4]}, {"left": 98, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [34]}, {"left": 97, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [44]}, {"left": 96, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [42, 15, 33, 4]}, {"left": 92, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [45, 31]}, {"left": 90, "kind": "l", "drafts": [6, 44, 11, 11], "rows": 5, "kept": 3, "emitted": [6, 44, 11, 12]}, {"left": 86, "kind": "l", "drafts": [10, 33, 44], "rows": 4, "kept": 3, "emitted": [10, 33, 44, 13]}, {"left": 82, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [5]}, {"left": 81, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [19]}, {"left": 80, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [36]}, {"left": 79, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [7, 31]}, {"left": 77, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [16]}, {"left": 76, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [19, 22]}, {"left": 74, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [44, 42, 15, 35]}, {"left": 70, "kind": "l", "drafts": [42, 12, 4, 39], "rows": 5, "kept": 2, "emitted": [42, 12, 5]}, {"left": 67, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [12, 9, 41, 16, 3]}, {"left": 62, "kind": "l", "drafts": [10, 22, 41, 19], "rows": 5, "kept": 0, "emitted": [30]}, {"left": 61, "kind": "l", "drafts": [40, 24, 15], "rows": 4, "kept": 1, "emitted": [40, 35]}, {"left": 59, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [42, 12, 4]}, {"left": 56, "kind": "l", "drafts": [39, 9], "rows": 3, "kept": 2, "emitted": [39, 9, 43]}, {"left": 53, "kind": "l", "drafts": [29, 46, 11], "rows": 4, "kept": 3, "emitted": [29, 46, 11, 9]}, {"left": 49, "kind": "l", "drafts": [19, 30, 40, 7], "rows": 5, "kept": 4, "emitted": [19, 30, 40, 7, 37]}, {"left": 44, "kind": "l", "drafts": [38, 28, 8, 22, 25, 3], "rows": 7, "kept": 0, "emitted": [21]}, {"left": 43, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [14, 36]}, {"left": 41, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [14]}, {"left": 40, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [28, 30]}, {"left": 38, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [14, 10, 26, 6, 14, 38]}, {"left": 32, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [40, 22, 17]}, {"left": 29, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [15, 11]}, {"left": 27, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [37]}, {"left": 26, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [31]}, {"left": 25, "kind": "l", "drafts": [16, 25, 36, 40], "rows": 5, "kept": 1, "emitted": [16, 38]}, {"left": 23, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [27, 13, 29]}, {"left": 20, "kind": "l", "drafts": [31, 6, 44], "rows": 4, "kept": 0, "emitted": [26]}, {"left": 19, "kind": "l", "drafts": [39, 15], "rows": 3, "kept": 1, "emitted": [39, 9]}, {"left": 17, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [8, 37]}, {"left": 15, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [6, 7, 26, 30]}, {"left": 11, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [14]}, {"left": 10, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [10, 25]}, {"left": 8, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [22, 25, 45]}, {"left": 5, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [43, 26]}, {"left": 3, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [12]}, {"left": 2, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [24]}, {"left": 1, "kind": "s", "drafts": [], "rows": 1, "kept": 0, "emitted": [13]}]}, {"costs": {"verify": [42.0, 47.0, 52.0, 57.0, 62.0, 67.0, 72.0, 77.0, 82.0, 87.0, 92.0, 97.0, 102.0, 107.0, 112.0, 117.0], "draft": 5.6}, "min_match": 3, "max_rows": 8, "eos": [2], "prompt": [50, 18, 45, 17, 19, 11, 4, 28, 30, 21, 47, 19, 48, 43, 27, 46, 52, 31, 42, 2, 51, 10, 40, 8, 8, 17, 13, 8, 8, 5, 8, 36, 5, 9, 28, 43, 39, 20, 29, 18, 24, 22, 8, 5, 15, 39, 27, 30, 21, 47, 19, 48, 43, 41, 11, 38, 40, 17, 30, 16, 31, 32, 38, 51, 30, 7, 33, 6, 34, 20, 4, 2, 32, 25, 33, 5, 31, 15, 7, 46, 53, 44, 20, 8, 36, 29, 5, 49, 5, 10, 25, 35, 30, 8, 30, 11, 41, 44, 6, 17, 34, 23, 43, 28, 33, 53, 13, 21, 35, 34, 11, 18, 20, 39, 49, 24, 29, 20, 39, 49, 53, 44, 20, 8, 36, 42, 19, 30, 4, 47, 7, 9, 30, 48, 8, 44, 7, 28, 23, 41, 12, 41, 29, 7, 34, 8, 36, 38, 14, 5, 9, 8, 51, 30, 7, 6, 38, 14, 5, 9, 8, 51, 30, 7, 33, 6, 34, 20, 4, 42, 10, 35, 12, 5, 6], "rounds": [{"left": 303, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [51]}, {"left": 302, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [10, 40]}, {"left": 300, "kind": "l", "drafts": [8, 8], "rows": 3, "kept": 2, "emitted": [8, 8, 28]}, {"left": 297, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [30, 45]}, {"left": 295, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [21, 22, 35, 45]}, {"left": 291, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [48]}, {"left": 290, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [11, 43, 40, 42]}, {"left": 286, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [8, 48, 30, 35]}, {"left": 282, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [6, 18, 17, 53]}, {"left": 278, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [5, 41, 30, 12]}, {"left": 274, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [34, 20, 4]}, {"left": 271, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [8]}, {"left": 270, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [48, 30]}, {"left": 268, "kind": "l", "drafts": [35, 6, 18, 17], "rows": 5, "kept": 4, "emitted": [35, 6, 18, 17, 53]}, {"left": 263, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [5, 29]}, {"left": 261, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [4]}, {"left": 260, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [39, 38, 46]}, {"left": 257, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [11]}, {"left": 256, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [51, 40]}, {"left": 254, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [13]}, {"left": 253, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [22, 32]}, {"left": 251, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [51, 44, 53, 44]}, {"left": 247, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [20]}, {"left": 246, "kind": "l", "drafts": [8, 36, 42, 19, 30, 4, 47], "rows": 8, "kept": 2, "emitted": [8, 36, 49]}, {"left": 243, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [7]}, {"left": 242, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [48, 20, 33, 11]}, {"left": 238, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [43, 40, 42, 8, 48]}, {"left": 233, "kind": "l", "drafts": [30, 35, 6, 18, 17, 53], "rows": 7, "kept": 6, "emitted": [30, 35, 6, 18, 17, 53, 5]}, {"left": 226, "kind": "l", "drafts": [41, 30, 12, 34], "rows": 5, "kept": 0, "emitted": [29]}, {"left": 225, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [4]}, {"left": 224, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [39, 38, 46, 11, 51]}, {"left": 219, "kind": "l", "drafts": [40, 13, 22, 32, 51, 44], "rows": 7, "kept": 6, "emitted": [40, 13, 22, 32, 51, 44, 53]}, {"left": 212, "kind": "l", "drafts": [44, 20, 8, 36, 49, 7, 48], "rows": 8, "kept": 4, "emitted": [44, 20, 8, 36, 26]}, {"left": 207, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [17, 32]}, {"left": 205, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [33, 23, 29]}, {"left": 202, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [14, 50, 24]}, {"left": 199, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [11, 3]}, {"left": 197, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [5]}, {"left": 196, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [47, 5, 48]}, {"left": 193, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [37, 40]}, {"left": 191, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [27, 23]}, {"left": 189, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [43, 28, 33, 53, 13]}, {"left": 184, "kind": "l", "drafts": [21, 35, 34, 11, 18, 20, 39], "rows": 8, "kept": 1, "emitted": [21, 15]}, {"left": 182, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [28, 37, 4]}, {"left": 179, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [41]}, {"left": 178, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [48, 49, 5, 10, 25]}, {"left": 173, "kind": "l", "drafts": [35, 30, 8, 30, 11, 41, 44], "rows": 8, "kept": 0, "emitted": [42]}, {"left": 172, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [22, 34, 20]}, {"left": 169, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [48, 34]}, {"left": 167, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [32]}, {"left": 166, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [11, 4]}, {"left": 164, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [40, 17]}, {"left": 162, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [34, 23]}, {"left": 160, "kind": "l", "drafts": [43, 28, 33, 53, 13], "rows": 6, "kept": 5, "emitted": [43, 28, 33, 53, 13, 7]}, {"left": 154, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [35, 14]}, {"left": 152, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [5, 52]}, {"left": 150, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [34]}, {"left": 149, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [22, 28]}, {"left": 147, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [5, 32]}, {"left": 145, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [24, 23, 26, 19, 38]}, {"left": 140, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [12]}, {"left": 139, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [21]}, {"left": 138, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [6]}, {"left": 137, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [41, 35, 10, 40]}, {"left": 133, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [8, 8, 46]}, {"left": 130, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [14, 48, 51, 10]}, {"left": 126, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [40]}, {"left": 125, "kind": "l", "drafts": [8, 8, 28, 30, 45, 21, 22], "rows": 8, "kept": 2, "emitted": [8, 8, 51]}, {"left": 122, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [27, 10, 28]}, {"left": 119, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [6]}, {"left": 118, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [35, 24]}, {"left": 116, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [34, 27, 33]}, {"left": 113, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [48]}, {"left": 112, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [44]}, {"left": 111, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [27, 52, 40, 20, 31]}, {"left": 106, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [27, 12, 15, 38, 22]}, {"left": 101, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [32, 45, 12]}, {"left": 98, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [13, 14]}, {"left": 96, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [48, 51, 10]}, {"left": 93, "kind": "l", "drafts": [40, 8, 8, 51, 27, 10], "rows": 7, "kept": 0, "emitted": [38]}, {"left": 92, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [14, 5, 9]}, {"left": 89, "kind": "l", "drafts": [8, 51, 30, 7], "rows": 5, "kept": 4, "emitted": [8, 51, 30, 7, 33]}, {"left": 84, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [6, 34]}, {"left": 82, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [20]}, {"left": 81, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [4]}, {"left": 80, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [8]}, {"left": 79, "kind": "l", "drafts": [48, 30, 35, 6, 18, 17, 53], "rows": 8, "kept": 0, "emitted": [8]}, {"left": 78, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [11]}, {"left": 77, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [37]}, {"left": 76, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [26]}, {"left": 75, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [33, 46]}, {"left": 73, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [12]}, {"left": 72, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [3, 51]}, {"left": 70, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [14]}, {"left": 69, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [18, 39]}, {"left": 67, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [14, 19, 19]}, {"left": 64, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [41, 29, 17, 12, 11]}, {"left": 59, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [20]}, {"left": 58, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [33, 11, 43]}, {"left": 55, "kind": "l", "drafts": [40, 42, 8, 48, 30], "rows": 6, "kept": 5, "emitted": [40, 42, 8, 48, 30, 35]}, {"left": 49, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [6, 18, 17, 53, 5]}, {"left": 44, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [29, 8]}, {"left": 42, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [8, 21]}, {"left": 40, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [4, 14, 33]}, {"left": 37, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [46]}, {"left": 36, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [50, 36]}, {"left": 34, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [23]}, {"left": 33, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [13, 45, 14]}, {"left": 30, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [22, 46]}, {"left": 28, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [20, 16]}, {"left": 26, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [43, 45, 11, 46]}, {"left": 22, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [19]}, {"left": 21, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [23, 19]}, {"left": 19, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [50, 29, 6]}, {"left": 16, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [31]}, {"left": 15, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [7, 47]}, {"left": 13, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [7]}, {"left": 12, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [9, 30, 48]}, {"left": 9, "kind": "l", "drafts": [8, 44, 7, 28, 23, 41, 12], "rows": 8, "kept": 7, "emitted": [8, 44, 7, 28, 23, 41, 12, 41]}, {"left": 1, "kind": "s", "drafts": [], "rows": 1, "kept": 0, "emitted": [29]}]}, {"costs": {"verify": [21.291, 24.948500000000003, 28.631999999999998, 32.341499999999996, 36.077, 39.8385, 43.626000000000005, 47.439499999999995, 51.279, 55.144499999999994, 59.036, 62.9535, 66.89699999999999, 70.8665, 74.862, 78.8835, 82.931, 87.00450000000001, 91.104, 95.22949999999999, 99.381, 103.5585, 107.762, 111.99149999999999, 116.24699999999999, 120.5285, 124.83599999999998, 129.1695, 133.529, 137.9145, 142.326, 146.7635, 151.227, 155.7165, 160.232, 164.7735, 169.341, 173.93449999999999, 178.55399999999997, 183.1995, 187.871, 192.5685, 197.29199999999997, 202.04149999999998, 206.817, 211.61849999999998, 216.446, 221.29949999999997, 226.17899999999997, 231.0845, 236.016, 240.97349999999997, 245.95699999999997, 250.9665, 256.002, 261.0635, 266.15099999999995, 271.2645, 276.404, 281.5695, 286.76099999999997, 291.9785, 297.222, 302.4915], "draft": 3.677}, "min_match": 6, "max_rows": 12, "eos": [2], "prompt": [24, 43, 13, 18, 46, 29, 30, 58, 29, 14, 37, 23, 6, 5, 26, 3, 48, 35, 32, 49, 48, 35, 32, 31, 37, 32, 28, 4, 47, 24, 7, 33, 35, 31, 35, 8, 60, 21, 58, 24, 40, 60, 35, 48, 45, 60, 47, 5, 44, 17, 46, 14, 35, 20, 35, 18, 7, 31, 30, 30, 43, 48, 23, 50, 47, 59, 28, 41, 39, 60, 11, 48, 35, 32, 3, 35, 32, 3, 31, 35, 8, 60, 21, 58, 24, 40, 60, 35, 48, 45, 60, 47, 5, 44, 17, 46, 37, 37, 6, 13, 18, 27, 35, 28, 54, 18, 53, 32, 3, 60, 47, 5, 44, 17, 46, 37, 37, 6, 6, 47, 30, 14, 35, 20, 35, 18, 7, 31, 29, 55, 44, 23, 14, 11, 30, 24, 7, 33, 35, 31, 46, 4, 18, 22, 45, 25, 16, 26, 38, 3, 11, 28, 24, 5, 37, 18, 25, 29, 31, 14, 44, 36, 23, 38, 57, 22, 33, 51, 43, 13, 18, 27, 35, 28, 54, 59, 47, 59, 28, 41, 39, 60, 11, 48, 53, 37, 55, 37, 28, 34, 28, 18, 7, 31, 30, 30, 43, 48, 23, 50, 47, 59, 28, 41, 39, 60, 11, 28, 54, 18, 27, 35, 28, 54, 51, 46, 11, 57, 11, 40, 50, 42, 58, 19, 14, 4, 57, 43, 13, 18, 27, 35, 40, 31, 46, 11, 57, 11, 34, 33, 2, 36, 50, 46, 54, 40, 60, 35, 48, 45, 60, 47, 5, 44, 17, 46, 37, 37, 6, 51, 42, 32, 3, 43, 15, 42, 58, 19, 14, 4, 57, 27, 15, 49, 24, 60, 52, 27, 6, 20, 28, 11, 57, 19, 27, 30, 15, 15, 3, 32, 3, 57, 57, 18, 16, 34, 57, 52, 36, 37, 58, 16, 3, 18, 5, 31, 35, 51, 40, 22, 42, 58, 19, 59, 28, 41, 39, 60, 11, 45, 55, 51, 5, 31, 40, 5, 37, 18, 47, 60, 52, 27, 6, 20, 28, 11, 57, 19, 27, 30, 15, 28, 23, 19, 56, 22, 3, 4, 10, 34, 36, 42, 35, 31, 35, 57, 16, 4, 50, 28, 13, 48, 13, 8, 3, 25, 3, 20, 33, 25, 11, 42, 35, 28, 54, 35, 28, 54, 32, 3, 28, 54], "rounds": [{"left": 173, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [54]}, {"left": 172, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [11]}, {"left": 171, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [44, 23, 9]}, {"left": 168, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [32]}, {"left": 167, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [13]}, {"left": 166, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [59, 22, 57]}, {"left": 163, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [23, 30]}, {"left": 161, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [13]}, {"left": 160, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [48, 7, 31]}, {"left": 157, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [30, 30]}, {"left": 155, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [43, 48, 23]}, {"left": 152, "kind": "l", "drafts": [50, 47], "rows": 3, "kept": 2, "emitted": [50, 47, 59]}, {"left": 149, "kind": "l", "drafts": [28, 41, 39, 60], "rows": 5, "kept": 1, "emitted": [28, 57]}, {"left": 147, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [19, 27]}, {"left": 145, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [30]}, {"left": 144, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [15]}, {"left": 143, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [15, 43, 13]}, {"left": 140, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [18, 27, 52, 44, 26]}, {"left": 135, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [58]}, {"left": 134, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [7]}, {"left": 133, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [30]}, {"left": 132, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [40]}, {"left": 131, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [55, 50, 42, 58]}, {"left": 127, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [19, 14]}, {"left": 125, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [4, 57, 27, 15]}, {"left": 121, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [49]}, {"left": 120, "kind": "l", "drafts": [24, 60], "rows": 3, "kept": 1, "emitted": [24, 41]}, {"left": 118, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [57]}, {"left": 117, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [59]}, {"left": 116, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [40, 16, 26, 52, 54, 3]}, {"left": 110, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [45, 37, 23, 54]}, {"left": 106, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [11, 20, 60, 40, 58]}, {"left": 101, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [52]}, {"left": 100, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [55, 49, 53]}, {"left": 97, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [15]}, {"left": 96, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [15, 19, 55, 13]}, {"left": 92, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [7, 47, 21, 59]}, {"left": 88, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [13, 18]}, {"left": 86, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [27, 35]}, {"left": 84, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [27, 30]}, {"left": 82, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [15, 15, 35, 18, 7]}, {"left": 77, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [31]}, {"left": 76, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [30, 30]}, {"left": 74, "kind": "l", "drafts": [43, 48, 23], "rows": 4, "kept": 3, "emitted": [43, 48, 23, 50]}, {"left": 70, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [47, 59]}, {"left": 68, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [28, 41]}, {"left": 66, "kind": "l", "drafts": [39, 60], "rows": 3, "kept": 2, "emitted": [39, 60, 11]}, {"left": 63, "kind": "l", "drafts": [48, 35, 32, 3, 35, 32], "rows": 7, "kept": 0, "emitted": [3]}, {"left": 62, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [32]}, {"left": 61, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [22, 11]}, {"left": 59, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [52, 32]}, {"left": 57, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [59, 13, 7, 12, 15]}, {"left": 52, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [15, 18]}, {"left": 50, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [13]}, {"left": 49, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [56]}, {"left": 48, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [55]}, {"left": 47, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [58, 15, 49]}, {"left": 44, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [48, 35, 32, 3, 47]}, {"left": 39, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [60]}, {"left": 38, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [52]}, {"left": 37, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [27, 6, 20]}, {"left": 34, "kind": "l", "drafts": [28, 11, 57, 19, 27, 30, 15], "rows": 8, "kept": 4, "emitted": [28, 11, 57, 19, 37]}, {"left": 29, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [54]}, {"left": 28, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [17, 54, 7, 57]}, {"left": 24, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [44]}, {"left": 23, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [17, 57]}, {"left": 21, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [50]}, {"left": 20, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [28, 54, 46, 27]}, {"left": 16, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [35]}, {"left": 15, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [28, 54]}, {"left": 13, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [40]}, {"left": 12, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [50, 42, 58, 19]}, {"left": 8, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [14, 4, 57, 27]}, {"left": 4, "kind": "l", "drafts": [15, 49, 24], "rows": 4, "kept": 3, "emitted": [15, 49, 24, 46]}]}, {"costs": {"verify": [42.0, 47.0, 52.0, 57.0, 62.0, 67.0, 72.0, 77.0, 82.0, 87.0, 92.0, 97.0, 102.0, 107.0, 112.0, 117.0], "draft": 5.6}, "min_match": 4, "max_rows": 16, "eos": [2], "prompt": [53, 49, 63, 29, 62, 7, 4, 9, 52, 44, 25, 8, 29, 28, 38, 19, 46, 28, 55, 7, 55, 60, 13, 67, 15, 42, 15, 19, 54, 58, 59, 46, 6, 7, 11, 53, 12, 55, 53, 59, 24, 33, 62, 19, 25, 63, 25, 23, 5, 62, 22, 48, 6, 44, 21, 42, 59, 52, 65, 57, 31, 65, 58, 15, 59, 63, 27, 65, 57, 31, 65, 58, 15, 59, 63, 2, 22, 33, 10, 34, 12, 63, 18, 20, 18, 35, 52, 37, 34, 46, 25, 35, 32, 7, 4, 46, 27, 14, 43, 62, 12, 20, 52, 37, 34, 46, 25, 35, 32, 52, 37, 34, 46, 25, 35, 32, 7, 4, 46, 58, 61, 21, 38, 58, 47, 10, 67, 65, 7, 3, 2, 50, 11, 67, 33, 3, 21, 6, 19, 22, 14, 44, 58, 41, 6, 18, 57, 18, 64, 9, 40, 6, 34, 5, 52, 39, 2, 3, 25, 34, 41, 52, 64, 22, 27, 30, 33, 42, 53, 26, 58, 62, 9, 10, 66, 33, 27, 30, 33, 42, 53, 26, 58, 40, 58, 40, 6, 48, 30, 5, 9, 65, 33, 32, 65, 55, 62, 6, 52, 14, 16, 50, 47, 4, 67, 25, 15, 52, 42, 15, 19, 54, 14, 22, 58, 23, 15, 11, 17, 6, 50, 58, 13, 23, 61, 64, 37, 29, 31, 44, 33, 62, 19, 25, 63, 25, 23, 5, 62, 22, 48, 6, 44, 21, 42, 59, 52, 65, 57, 31, 65, 58, 56, 19, 41, 26, 50, 36, 30, 45, 10, 66, 33, 27, 30, 33, 42, 53, 26, 58, 40, 58, 40, 6, 48, 30, 5, 9, 47, 26, 6, 17, 55, 19, 25, 47, 20, 40, 58, 40, 6, 48, 30, 5, 9, 65, 40, 14, 19, 17, 61, 62, 46, 67, 23, 2, 24, 45, 16], "rounds": [{"left": 417, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [17, 25]}, {"left": 415, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [60]}, {"left": 414, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [16]}, {"left": 413, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [66, 31]}, {"left": 411, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [37]}, {"left": 410, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [33]}, {"left": 409, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [55, 12, 50, 41, 6]}, {"left": 404, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [48, 59, 30, 63, 64]}, {"left": 399, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [28, 60, 66]}, {"left": 396, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [16]}, {"left": 395, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [41, 63]}, {"left": 393, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [22, 38]}, {"left": 391, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [57, 19, 54, 22]}, {"left": 387, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [40, 66, 59, 56]}, {"left": 383, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [7]}, {"left": 382, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [4]}, {"left": 381, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [44, 52, 39]}, {"left": 378, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [45, 48, 34]}, {"left": 375, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [22, 42]}, {"left": 373, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [46, 26, 32, 30, 41, 64]}, {"left": 367, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [32, 58]}, {"left": 365, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [12, 63]}, {"left": 363, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [49, 40, 6]}, {"left": 360, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [48]}, {"left": 359, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [30, 5, 9, 65]}, {"left": 355, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [40, 14, 19, 17]}, {"left": 351, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [41, 6, 48]}, {"left": 348, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [59, 30, 33, 32]}, {"left": 344, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [65]}, {"left": 343, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [55, 62, 6, 52, 14]}, {"left": 338, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [16]}, {"left": 337, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [50, 47, 4, 67, 25]}, {"left": 332, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [15, 52, 42]}, {"left": 329, "kind": "l", "drafts": [15, 19, 54, 14, 22, 58], "rows": 7, "kept": 4, "emitted": [15, 19, 54, 14, 53]}, {"left": 324, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [22, 40]}, {"left": 322, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [43, 6]}, {"left": 320, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [28]}, {"left": 319, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [5, 62, 22]}, {"left": 316, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [48, 2]}, {"left": 314, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [6, 48, 59]}, {"left": 311, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [30]}, {"left": 310, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [33]}, {"left": 309, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [32, 65, 55, 62, 6, 52]}, {"left": 303, "kind": "l", "drafts": [14, 16, 50, 47], "rows": 5, "kept": 4, "emitted": [14, 16, 50, 47, 4]}, {"left": 298, "kind": "l", "drafts": [67, 25, 15, 52, 42, 15, 19], "rows": 8, "kept": 2, "emitted": [67, 25, 51]}, {"left": 295, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [46]}, {"left": 294, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [28, 10, 27]}, {"left": 291, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [66]}, {"left": 290, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [32, 34, 25, 65]}, {"left": 286, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [41, 32]}, {"left": 284, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [7]}, {"left": 283, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [4, 46]}, {"left": 281, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [27, 14, 43]}, {"left": 278, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [62, 12]}, {"left": 276, "kind": "l", "drafts": [20, 52, 37, 34, 46, 25, 35, 32, 52, 37, 34, 46, 25, 35, 32], "rows": 16, "kept": 1, "emitted": [20, 51]}, {"left": 274, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [13]}, {"left": 273, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [46, 34, 53, 26]}, {"left": 269, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [58, 40, 58]}, {"left": 266, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [40]}, {"left": 265, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [6]}, {"left": 264, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [48]}, {"left": 263, "kind": "l", "drafts": [30, 5, 9, 47, 26, 6, 17], "rows": 8, "kept": 3, "emitted": [30, 5, 9, 65]}, {"left": 259, "kind": "l", "drafts": [33, 32, 65, 55, 62, 6], "rows": 7, "kept": 0, "emitted": [40]}, {"left": 258, "kind": "l", "drafts": [14, 19, 17, 61], "rows": 5, "kept": 3, "emitted": [14, 19, 17, 40]}, {"left": 254, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [65, 3, 63]}, {"left": 251, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [14]}, {"left": 250, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [2]}, {"left": 249, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [19]}, {"left": 248, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [22, 18]}, {"left": 246, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [42, 53]}, {"left": 244, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [26]}, {"left": 243, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [58]}, {"left": 242, "kind": "l", "drafts": [40, 58], "rows": 3, "kept": 2, "emitted": [40, 58, 40]}, {"left": 239, "kind": "l", "drafts": [6, 48, 30, 5, 9, 47], "rows": 7, "kept": 5, "emitted": [6, 48, 30, 5, 9, 65]}, {"left": 233, "kind": "l", "drafts": [33, 32, 65, 55, 62, 6], "rows": 7, "kept": 0, "emitted": [40]}, {"left": 232, "kind": "l", "drafts": [14, 19, 17, 40], "rows": 5, "kept": 3, "emitted": [14, 19, 17, 28]}, {"left": 228, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [24, 46]}, {"left": 226, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [27, 14, 43]}, {"left": 223, "kind": "l", "drafts": [62, 12, 20, 51, 13, 46, 34, 53], "rows": 9, "kept": 0, "emitted": [33]}, {"left": 222, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [27]}, {"left": 221, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [30]}, {"left": 220, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [33, 42, 53]}, {"left": 217, "kind": "l", "drafts": [26, 58, 40, 58, 40], "rows": 6, "kept": 5, "emitted": [26, 58, 40, 58, 40, 6]}, {"left": 211, "kind": "l", "drafts": [48, 30, 5, 9, 47], "rows": 6, "kept": 4, "emitted": [48, 30, 5, 9, 65]}, {"left": 206, "kind": "l", "drafts": [33, 32, 65, 55, 62, 6, 52], "rows": 8, "kept": 0, "emitted": [40]}, {"left": 205, "kind": "l", "drafts": [14, 19, 17, 28, 24], "rows": 6, "kept": 3, "emitted": [14, 19, 17, 41]}, {"left": 201, "kind": "l", "drafts": [6, 48, 59, 30, 33, 32], "rows": 7, "kept": 0, "emitted": [26]}, {"left": 200, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [50, 36]}, {"left": 198, "kind": "l", "drafts": [30, 45, 10, 66, 33, 27, 30, 33], "rows": 9, "kept": 4, "emitted": [30, 45, 10, 66, 66]}, {"left": 193, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [33, 27, 30]}, {"left": 190, "kind": "l", "drafts": [33, 42, 53, 26, 58, 40, 58, 40], "rows": 9, "kept": 8, "emitted": [33, 42, 53, 26, 58, 40, 58, 40, 6]}, {"left": 181, "kind": "l", "drafts": [48, 30, 5, 9], "rows": 5, "kept": 4, "emitted": [48, 30, 5, 9, 65]}, {"left": 176, "kind": "l", "drafts": [33, 32, 65, 55], "rows": 5, "kept": 0, "emitted": [40]}, {"left": 175, "kind": "l", "drafts": [14, 19, 17], "rows": 4, "kept": 3, "emitted": [14, 19, 17, 4]}, {"left": 171, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [54]}, {"left": 170, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [3, 49, 17, 62]}, {"left": 166, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [29, 39]}, {"left": 164, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [61]}, {"left": 163, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [26]}, {"left": 162, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [18]}, {"left": 161, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [31]}, {"left": 160, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [36]}, {"left": 159, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [31, 21, 58, 48, 66]}, {"left": 154, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [9, 16]}, {"left": 152, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [30]}, {"left": 151, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [38, 17, 17]}, {"left": 148, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [13]}, {"left": 147, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [51, 66, 46, 59, 19]}, {"left": 142, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [49, 4]}, {"left": 140, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [15]}, {"left": 139, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [44]}, {"left": 138, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [29, 38, 6]}, {"left": 135, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [28]}, {"left": 134, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [44]}, {"left": 133, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [65]}, {"left": 132, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [7, 3, 36]}, {"left": 129, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [40]}, {"left": 128, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [40]}, {"left": 127, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [30, 40, 26]}, {"left": 124, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [5, 62, 19, 36, 41]}, {"left": 119, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [52]}, {"left": 118, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [19]}, {"left": 117, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [67]}, {"left": 116, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [28]}, {"left": 115, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [51]}, {"left": 114, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [17]}, {"left": 113, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [47]}, {"left": 112, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [42]}, {"left": 111, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [42, 25, 41, 30, 61]}, {"left": 106, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [18, 8, 67, 33]}, {"left": 102, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [35, 52]}, {"left": 100, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [37, 34]}, {"left": 98, "kind": "l", "drafts": [46, 25, 35, 32, 7, 4, 46, 27, 14, 43, 62, 12], "rows": 13, "kept": 6, "emitted": [46, 25, 35, 32, 7, 4, 33]}, {"left": 91, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [39, 21]}, {"left": 89, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [4]}, {"left": 88, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [23, 19]}, {"left": 86, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [42, 66]}, {"left": 84, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [34]}, {"left": 83, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [36, 31, 21]}, {"left": 80, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [58]}, {"left": 79, "kind": "l", "drafts": [48, 66, 9, 16, 30, 38, 17, 17, 13, 51, 66], "rows": 12, "kept": 7, "emitted": [48, 66, 9, 16, 30, 38, 17, 34]}, {"left": 71, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [4]}, {"left": 70, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [3, 61, 25]}, {"left": 67, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [39]}, {"left": 66, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [34]}, {"left": 65, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [52]}, {"left": 64, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [14, 48]}, {"left": 62, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [30]}, {"left": 61, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [5]}, {"left": 60, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [9, 65]}, {"left": 58, "kind": "l", "drafts": [40, 14, 19, 17, 4, 54, 3, 49, 17, 62, 29, 39, 61], "rows": 14, "kept": 4, "emitted": [40, 14, 19, 17, 65]}, {"left": 53, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [4, 4]}, {"left": 51, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [40]}, {"left": 50, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [59, 12, 25, 50, 24]}, {"left": 45, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [64, 55, 62, 6]}, {"left": 41, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [52, 14, 16, 50]}, {"left": 37, "kind": "l", "drafts": [47, 4, 67, 25, 51, 46, 28, 10, 27], "rows": 10, "kept": 4, "emitted": [47, 4, 67, 25, 15]}, {"left": 32, "kind": "l", "drafts": [52, 42, 15, 19, 54], "rows": 6, "kept": 5, "emitted": [52, 42, 15, 19, 54, 50]}, {"left": 26, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [36, 30]}, {"left": 24, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [45, 10, 66]}, {"left": 21, "kind": "l", "drafts": [66, 33, 27, 30, 33, 42, 53], "rows": 8, "kept": 0, "emitted": [33]}, {"left": 20, "kind": "l", "drafts": [27, 30, 33, 42, 53, 26], "rows": 7, "kept": 6, "emitted": [27, 30, 33, 42, 53, 26, 58]}, {"left": 13, "kind": "l", "drafts": [40, 58, 40, 6, 48, 30], "rows": 7, "kept": 6, "emitted": [40, 58, 40, 6, 48, 30, 5]}, {"left": 6, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [9]}, {"left": 5, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [65]}, {"left": 4, "kind": "l", "drafts": [33, 32, 65], "rows": 4, "kept": 0, "emitted": [40]}, {"left": 3, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [14]}, {"left": 2, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [21]}, {"left": 1, "kind": "s", "drafts": [], "rows": 1, "kept": 0, "emitted": [58]}]}, {"costs": {"verify": [21.291, 24.948500000000003, 28.631999999999998, 32.341499999999996, 36.077, 39.8385, 43.626000000000005, 47.439499999999995, 51.279, 55.144499999999994, 59.036, 62.9535, 66.89699999999999, 70.8665, 74.862, 78.8835, 82.931, 87.00450000000001, 91.104, 95.22949999999999, 99.381, 103.5585, 107.762, 111.99149999999999, 116.24699999999999, 120.5285, 124.83599999999998, 129.1695, 133.529, 137.9145, 142.326, 146.7635, 151.227, 155.7165, 160.232, 164.7735, 169.341, 173.93449999999999, 178.55399999999997, 183.1995, 187.871, 192.5685, 197.29199999999997, 202.04149999999998, 206.817, 211.61849999999998, 216.446, 221.29949999999997, 226.17899999999997, 231.0845, 236.016, 240.97349999999997, 245.95699999999997, 250.9665, 256.002, 261.0635, 266.15099999999995, 271.2645, 276.404, 281.5695, 286.76099999999997, 291.9785, 297.222, 302.4915], "draft": 3.677}, "min_match": 4, "max_rows": 16, "eos": [2], "prompt": [56, 44, 50, 38, 58, 30, 19, 59, 11, 33, 62, 63, 27, 46, 23, 31, 24, 40, 6, 56, 53, 46, 59, 30, 73, 23, 27, 34, 5, 69, 25, 55, 46, 28, 4, 41, 31, 58, 48, 73, 25, 65, 18, 44, 50, 29, 7, 8, 41, 10, 46, 65, 4, 44, 60, 25, 42, 68, 73, 55, 41, 48, 73, 25, 65], "rounds": [{"left": 368, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [58, 24, 18]}, {"left": 365, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [9]}, {"left": 364, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [70]}, {"left": 363, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [58, 58, 60, 64, 40]}, {"left": 358, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [37, 61]}, {"left": 356, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [32]}, {"left": 355, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [13, 22, 36, 62]}, {"left": 351, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [32, 14, 40]}, {"left": 348, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [18, 63, 29]}, {"left": 345, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [49, 36]}, {"left": 343, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [71, 63, 57, 13]}, {"left": 339, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [52, 60]}, {"left": 337, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [25, 42, 68]}, {"left": 334, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [73]}, {"left": 333, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [43]}, {"left": 332, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [57, 44, 34, 55]}, {"left": 328, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [18]}, {"left": 327, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [30]}, {"left": 326, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [58, 71]}, {"left": 324, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [34]}, {"left": 323, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [12, 62]}, {"left": 321, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [48]}, {"left": 320, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [45, 31, 69]}, {"left": 317, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [57, 66, 46]}, {"left": 314, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [27]}, {"left": 313, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [47, 12]}, {"left": 311, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [7]}, {"left": 310, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [45]}, {"left": 309, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [14]}, {"left": 308, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [48]}, {"left": 307, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [72, 9]}, {"left": 305, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [37]}, {"left": 304, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [42]}, {"left": 303, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [38, 56, 63]}, {"left": 300, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [26, 30]}, {"left": 298, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [10, 30]}, {"left": 296, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [63, 48]}, {"left": 294, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [58]}, {"left": 293, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [44, 60, 25, 42, 18]}, {"left": 288, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [44]}, {"left": 287, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [50]}, {"left": 286, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [29]}, {"left": 285, "kind": "l", "drafts": [7, 8], "rows": 3, "kept": 2, "emitted": [7, 8, 41]}, {"left": 282, "kind": "l", "drafts": [10, 46, 65, 4, 44], "rows": 6, "kept": 3, "emitted": [10, 46, 65, 15]}, {"left": 278, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [36]}, {"left": 277, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [32, 14]}, {"left": 275, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [40, 18]}, {"left": 273, "kind": "l", "drafts": [63, 29, 49, 36, 71], "rows": 6, "kept": 1, "emitted": [63, 27]}, {"left": 271, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [49]}, {"left": 270, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [40, 18]}, {"left": 268, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [31, 18]}, {"left": 266, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [64, 70]}, {"left": 264, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [56, 11, 47, 53, 48]}, {"left": 259, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [53]}, {"left": 258, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [52]}, {"left": 257, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [5]}, {"left": 256, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [12, 56]}, {"left": 254, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [21]}, {"left": 253, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [32, 14, 3]}, {"left": 250, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [72]}, {"left": 249, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [3, 52]}, {"left": 247, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [13, 51]}, {"left": 245, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [8, 45, 64]}, {"left": 242, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [48]}, {"left": 241, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [61, 43, 67, 41, 16]}, {"left": 236, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [42, 18]}, {"left": 234, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [39, 3, 72]}, {"left": 231, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [3]}, {"left": 230, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [52, 8, 72, 11, 30]}, {"left": 225, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [28]}, {"left": 224, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [63]}, {"left": 223, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [29]}, {"left": 222, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [53, 48]}, {"left": 220, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [72]}, {"left": 219, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [9]}, {"left": 218, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [37, 42]}, {"left": 216, "kind": "l", "drafts": [38, 56, 63, 26], "rows": 5, "kept": 3, "emitted": [38, 56, 63, 45]}, {"left": 212, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [11]}, {"left": 211, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [68, 25]}, {"left": 209, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [42]}, {"left": 208, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [68, 73]}, {"left": 206, "kind": "l", "drafts": [43, 57, 44, 34, 55], "rows": 6, "kept": 0, "emitted": [11]}, {"left": 205, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [69, 5, 52, 4, 6]}, {"left": 200, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [38, 13]}, {"left": 198, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [44]}, {"left": 197, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [37, 17]}, {"left": 195, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [8]}, {"left": 194, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [37, 61, 32, 13]}, {"left": 190, "kind": "l", "drafts": [22, 36], "rows": 3, "kept": 2, "emitted": [22, 36, 62]}, {"left": 187, "kind": "l", "drafts": [32, 14, 40], "rows": 4, "kept": 0, "emitted": [71]}, {"left": 186, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [64, 48]}, {"left": 184, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [61, 43, 67]}, {"left": 181, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [41, 16]}, {"left": 179, "kind": "l", "drafts": [42, 18], "rows": 3, "kept": 2, "emitted": [42, 18, 49]}, {"left": 176, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [9]}, {"left": 175, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [58, 35]}, {"left": 173, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [17]}, {"left": 172, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [43, 56, 73]}, {"left": 169, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [56, 27]}, {"left": 167, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [23]}, {"left": 166, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [51, 9, 52]}, {"left": 163, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [28]}, {"left": 162, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [49]}, {"left": 161, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [9]}, {"left": 160, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [58, 35]}, {"left": 158, "kind": "l", "drafts": [17, 43, 56, 73], "rows": 5, "kept": 2, "emitted": [17, 43, 6]}, {"left": 155, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [8, 15, 70]}, {"left": 152, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [61]}, {"left": 151, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [11]}, {"left": 150, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [55]}, {"left": 149, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [33]}, {"left": 148, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [14, 22]}, {"left": 146, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [15, 50, 39, 35]}, {"left": 142, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [17]}, {"left": 141, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [43]}, {"left": 140, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [6, 8]}, {"left": 138, "kind": "l", "drafts": [15, 70, 61, 11], "rows": 5, "kept": 1, "emitted": [15, 74]}, {"left": 136, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [59, 33]}, {"left": 134, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [55, 17, 42, 4]}, {"left": 130, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [58, 64]}, {"left": 128, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [30, 63, 48]}, {"left": 125, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [58, 44]}, {"left": 123, "kind": "l", "drafts": [60, 25, 42], "rows": 4, "kept": 3, "emitted": [60, 25, 42, 68]}, {"left": 119, "kind": "l", "drafts": [73, 55, 41, 48], "rows": 5, "kept": 1, "emitted": [73, 62]}, {"left": 117, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [23, 31]}, {"left": 115, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [24, 40]}, {"left": 113, "kind": "l", "drafts": [6, 56, 53], "rows": 4, "kept": 0, "emitted": [54]}, {"left": 112, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [27, 58]}, {"left": 110, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [71]}, {"left": 109, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [34, 12]}, {"left": 107, "kind": "l", "drafts": [62, 48, 45], "rows": 4, "kept": 3, "emitted": [62, 48, 45, 31]}, {"left": 103, "kind": "l", "drafts": [69, 57, 66, 46], "rows": 5, "kept": 1, "emitted": [69, 29]}, {"left": 101, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [53, 32, 52, 28, 49, 9]}, {"left": 95, "kind": "l", "drafts": [58, 35, 17], "rows": 4, "kept": 0, "emitted": [67]}, {"left": 94, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [20, 26]}, {"left": 92, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [13, 44, 28]}, {"left": 89, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [18, 42, 19]}, {"left": 86, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [2, 9, 20]}, {"left": 83, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [23]}, {"left": 82, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [68, 61, 10, 27, 30]}, {"left": 77, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [73]}, {"left": 76, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [69, 28]}, {"left": 74, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [8]}, {"left": 73, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [24]}, {"left": 72, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [12]}, {"left": 71, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [68, 5]}, {"left": 69, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [38]}, {"left": 68, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [25, 16]}, {"left": 66, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [42, 52, 7, 29]}, {"left": 62, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [64, 5, 48, 49, 18]}, {"left": 57, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [23, 62]}, {"left": 55, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [39, 68, 31, 36]}, {"left": 51, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [68, 61, 32, 40]}, {"left": 47, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [45, 36]}, {"left": 45, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [50]}, {"left": 44, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [50]}, {"left": 43, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [44, 20, 6, 16, 40, 22]}, {"left": 37, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [15, 49, 50]}, {"left": 34, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [43]}, {"left": 33, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [59, 36]}, {"left": 31, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [21]}, {"left": 30, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [30, 22]}, {"left": 28, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [28]}, {"left": 27, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [67]}, {"left": 26, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [61, 32, 13]}, {"left": 23, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [72, 59, 62, 26, 2]}, {"left": 18, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [27]}, {"left": 17, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [62]}, {"left": 16, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [71]}, {"left": 15, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [22, 7, 70]}, {"left": 12, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [4, 61, 6]}, {"left": 9, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [8]}, {"left": 8, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [30, 19]}, {"left": 6, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [59, 11, 33, 62, 63, 27]}]}, {"costs": {"verify": [42.0, 47.0, 52.0, 57.0, 62.0, 67.0, 72.0, 77.0, 82.0, 87.0, 92.0, 97.0, 102.0, 107.0, 112.0, 117.0], "draft": 5.6}, "min_match": 3, "max_rows": 8, "eos": [2], "prompt": [14, 14, 2, 25, 10, 63, 59, 53, 44, 9, 66, 14, 15, 27, 39, 27, 41, 4, 8, 36, 39, 14, 14, 10, 68, 64, 67, 10, 68, 64, 55, 62, 25, 3, 52, 62, 80, 31, 75, 41, 4, 8, 36, 39, 14, 14, 34, 43, 55, 62, 25, 3, 52, 62, 80, 31, 75, 7, 59, 52, 69, 42, 21, 37, 62, 80, 31, 75, 10, 68, 64, 54, 6, 42, 3, 37, 9, 41, 4, 8, 36, 39, 14, 14, 17, 14, 21, 43, 15, 9, 41, 4, 8, 36, 39, 14, 14, 51, 58, 3, 24, 51, 52, 72, 7, 59, 13, 43, 51, 9, 34, 43, 55, 62, 25, 3, 52, 62], "rounds": [{"left": 417, "kind": "l", "drafts": [80, 31, 75, 7], "rows": 5, "kept": 0, "emitted": [65]}, {"left": 416, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [39]}, {"left": 415, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [74]}, {"left": 414, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [74]}, {"left": 413, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [10]}, {"left": 412, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [32, 59, 4, 29, 76]}, {"left": 407, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [44]}, {"left": 406, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [3]}, {"left": 405, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [29]}, {"left": 404, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [69, 42, 22]}, {"left": 401, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [78, 12]}, {"left": 399, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [38, 3, 9, 70, 51, 71]}, {"left": 393, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [21, 25]}, {"left": 391, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [72, 48]}, {"left": 389, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [35, 13]}, {"left": 387, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [21]}, {"left": 386, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [64, 50, 6]}, {"left": 383, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [71]}, {"left": 382, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [6, 26, 54, 6]}, {"left": 378, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [43, 35]}, {"left": 376, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [51, 48]}, {"left": 374, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [56, 62, 80]}, {"left": 371, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [31]}, {"left": 370, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [75]}, {"left": 369, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [53, 13, 8, 65]}, {"left": 365, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [61]}, {"left": 364, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [59, 13]}, {"left": 362, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [43]}, {"left": 361, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [51, 9, 34, 43]}, {"left": 357, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [55]}, {"left": 356, "kind": "l", "drafts": [62, 25], "rows": 3, "kept": 2, "emitted": [62, 25, 3]}, {"left": 353, "kind": "l", "drafts": [52, 62, 65, 39, 74], "rows": 6, "kept": 2, "emitted": [52, 62, 80]}, {"left": 350, "kind": "l", "drafts": [31, 75, 7, 59], "rows": 5, "kept": 0, "emitted": [75]}, {"left": 349, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [72, 20]}, {"left": 347, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [68]}, {"left": 346, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [70]}, {"left": 345, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [51, 28, 24, 19]}, {"left": 341, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [49, 13, 54, 52]}, {"left": 337, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [53, 24]}, {"left": 335, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [31, 41, 19, 41, 36]}, {"left": 330, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [24]}, {"left": 329, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [13, 70]}, {"left": 327, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [18, 26]}, {"left": 325, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [53, 11]}, {"left": 323, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [38, 3]}, {"left": 321, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [9]}, {"left": 320, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [70]}, {"left": 319, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [51, 71]}, {"left": 317, "kind": "l", "drafts": [21, 25], "rows": 3, "kept": 2, "emitted": [21, 25, 72]}, {"left": 314, "kind": "l", "drafts": [48, 35, 13], "rows": 4, "kept": 3, "emitted": [48, 35, 13, 21]}, {"left": 310, "kind": "l", "drafts": [64, 50, 6, 71, 6, 26], "rows": 7, "kept": 6, "emitted": [64, 50, 6, 71, 6, 26, 62]}, {"left": 303, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [78]}, {"left": 302, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [64, 31, 49]}, {"left": 299, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [69]}, {"left": 298, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [66, 28]}, {"left": 296, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [67]}, {"left": 295, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [5, 80, 34, 46]}, {"left": 291, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [22, 65]}, {"left": 289, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [11, 77]}, {"left": 287, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [46, 24, 55]}, {"left": 284, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [70, 16, 48]}, {"left": 281, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [62, 4]}, {"left": 279, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [30]}, {"left": 278, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [37, 71, 50]}, {"left": 275, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [75, 51, 62]}, {"left": 272, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [53, 43, 67]}, {"left": 269, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [48, 14]}, {"left": 267, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [10]}, {"left": 266, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [64]}, {"left": 265, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [79]}, {"left": 264, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [52, 34, 44]}, {"left": 261, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [61]}, {"left": 260, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [24, 43]}, {"left": 258, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [30, 68, 58, 81]}, {"left": 254, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [12, 60]}, {"left": 252, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [53]}, {"left": 251, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [51, 6]}, {"left": 249, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [5, 79]}, {"left": 247, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [49]}, {"left": 246, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [53, 32]}, {"left": 244, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [3, 75]}, {"left": 242, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [54, 59, 38, 59]}, {"left": 238, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [21]}, {"left": 237, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [3, 42, 75]}, {"left": 234, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [26]}, {"left": 233, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [61]}, {"left": 232, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [35, 73]}, {"left": 230, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [79]}, {"left": 229, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [69, 57]}, {"left": 227, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [15]}, {"left": 226, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [27]}, {"left": 225, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [78]}, {"left": 224, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [11, 70]}, {"left": 222, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [51]}, {"left": 221, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [28, 24, 19]}, {"left": 218, "kind": "l", "drafts": [49, 13, 54, 52, 53, 24], "rows": 7, "kept": 2, "emitted": [49, 13, 59]}, {"left": 215, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [13, 43]}, {"left": 213, "kind": "l", "drafts": [51, 9, 34, 43, 55], "rows": 6, "kept": 5, "emitted": [51, 9, 34, 43, 55, 62]}, {"left": 207, "kind": "l", "drafts": [25, 3, 52, 62, 80, 75, 72], "rows": 8, "kept": 5, "emitted": [25, 3, 52, 62, 80, 31]}, {"left": 201, "kind": "l", "drafts": [75, 7, 59, 52, 69, 42, 21], "rows": 8, "kept": 1, "emitted": [75, 53]}, {"left": 199, "kind": "l", "drafts": [13, 8, 65, 61, 59, 13, 43], "rows": 8, "kept": 0, "emitted": [19]}, {"left": 198, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [68, 52, 67]}, {"left": 195, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [75]}, {"left": 194, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [73, 13, 67]}, {"left": 191, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [10]}, {"left": 190, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [68, 64, 65, 61]}, {"left": 186, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [59, 13, 43]}, {"left": 183, "kind": "l", "drafts": [51, 9, 34, 43, 55], "rows": 6, "kept": 5, "emitted": [51, 9, 34, 43, 55, 62]}, {"left": 177, "kind": "l", "drafts": [25, 3, 52, 62, 80, 75], "rows": 7, "kept": 5, "emitted": [25, 3, 52, 62, 80, 31]}, {"left": 171, "kind": "l", "drafts": [75, 53, 19, 68, 52, 67, 75], "rows": 8, "kept": 1, "emitted": [75, 63]}, {"left": 169, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [45]}, {"left": 168, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [18]}, {"left": 167, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [66]}, {"left": 166, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [70]}, {"left": 165, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [43, 69, 45]}, {"left": 162, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [39, 65, 27]}, {"left": 159, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [18, 3]}, {"left": 157, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [51, 71, 6, 26]}, {"left": 153, "kind": "l", "drafts": [62, 78, 64, 31, 49, 69, 66], "rows": 8, "kept": 1, "emitted": [62, 4]}, {"left": 151, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [30, 37]}, {"left": 149, "kind": "l", "drafts": [71, 50, 75, 51, 62, 53], "rows": 7, "kept": 1, "emitted": [71, 77]}, {"left": 147, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [64, 8]}, {"left": 145, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [69, 43, 33]}, {"left": 142, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [21, 33, 7, 53, 13]}, {"left": 137, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [8, 65]}, {"left": 135, "kind": "l", "drafts": [61, 59, 13, 43], "rows": 5, "kept": 4, "emitted": [61, 59, 13, 43, 51]}, {"left": 130, "kind": "l", "drafts": [9, 34, 43, 55, 62], "rows": 6, "kept": 5, "emitted": [9, 34, 43, 55, 62, 25]}, {"left": 124, "kind": "l", "drafts": [3, 52, 62, 80, 75, 72, 20], "rows": 8, "kept": 1, "emitted": [3, 73]}, {"left": 122, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [43, 16]}, {"left": 120, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [49, 42, 3, 37]}, {"left": 116, "kind": "l", "drafts": [9, 41, 4, 8, 36], "rows": 6, "kept": 5, "emitted": [9, 41, 4, 8, 36, 39]}, {"left": 110, "kind": "l", "drafts": [14, 14, 17, 14, 21, 43], "rows": 7, "kept": 2, "emitted": [14, 14, 24]}, {"left": 107, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [54, 68]}, {"left": 105, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [27, 34, 49, 74, 15, 10]}, {"left": 99, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [68]}, {"left": 98, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [64, 16]}, {"left": 96, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [48, 62, 4, 28]}, {"left": 92, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [24, 19]}, {"left": 90, "kind": "l", "drafts": [49, 13, 59, 13, 43, 51, 9], "rows": 8, "kept": 2, "emitted": [49, 13, 53]}, {"left": 87, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [11]}, {"left": 86, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [38]}, {"left": 85, "kind": "l", "drafts": [3, 9, 70, 51, 71, 21, 25], "rows": 8, "kept": 7, "emitted": [3, 9, 70, 51, 71, 21, 25, 72]}, {"left": 77, "kind": "l", "drafts": [48, 35, 13, 21, 64, 50], "rows": 7, "kept": 6, "emitted": [48, 35, 13, 21, 64, 50, 6]}, {"left": 70, "kind": "l", "drafts": [71, 6, 26, 62, 78, 64, 31], "rows": 8, "kept": 3, "emitted": [71, 6, 26, 54]}, {"left": 66, "kind": "l", "drafts": [6, 43, 35, 51, 48, 56], "rows": 7, "kept": 6, "emitted": [6, 43, 35, 51, 48, 56, 53]}, {"left": 59, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [10, 44]}, {"left": 57, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [26, 72, 55, 21]}, {"left": 53, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [72, 34, 54, 73]}, {"left": 49, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [7, 54, 6, 43, 35]}, {"left": 44, "kind": "l", "drafts": [51, 48, 56, 53, 10, 44, 26], "rows": 8, "kept": 3, "emitted": [51, 48, 56, 44]}, {"left": 40, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [3, 29, 69, 42, 22, 78]}, {"left": 34, "kind": "l", "drafts": [12, 38, 3, 9, 70, 51, 71], "rows": 8, "kept": 1, "emitted": [12, 54]}, {"left": 32, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [6]}, {"left": 31, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [42, 3, 37, 9, 41]}, {"left": 26, "kind": "l", "drafts": [4, 8, 36, 39, 14], "rows": 6, "kept": 0, "emitted": [39]}, {"left": 25, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [40, 25, 32, 22, 55, 52]}, {"left": 19, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [76]}, {"left": 18, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [30]}, {"left": 17, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [54, 70]}, {"left": 15, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [46]}, {"left": 14, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [28, 62]}, {"left": 12, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [48]}, {"left": 11, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [56, 7, 53, 13]}, {"left": 7, "kind": "l", "drafts": [8, 65, 61, 59, 13], "rows": 6, "kept": 5, "emitted": [8, 65, 61, 59, 13, 43]}, {"left": 1, "kind": "s", "drafts": [], "rows": 1, "kept": 0, "emitted": [65]}]}, {"costs": {"verify": [21.291, 24.948500000000003, 28.631999999999998, 32.341499999999996, 36.077, 39.8385, 43.626000000000005, 47.439499999999995, 51.279, 55.144499999999994, 59.036, 62.9535, 66.89699999999999, 70.8665, 74.862, 78.8835, 82.931, 87.00450000000001, 91.104, 95.22949999999999, 99.381, 103.5585, 107.762, 111.99149999999999, 116.24699999999999, 120.5285, 124.83599999999998, 129.1695, 133.529, 137.9145, 142.326, 146.7635, 151.227, 155.7165, 160.232, 164.7735, 169.341, 173.93449999999999, 178.55399999999997, 183.1995, 187.871, 192.5685, 197.29199999999997, 202.04149999999998, 206.817, 211.61849999999998, 216.446, 221.29949999999997, 226.17899999999997, 231.0845, 236.016, 240.97349999999997, 245.95699999999997, 250.9665, 256.002, 261.0635, 266.15099999999995, 271.2645, 276.404, 281.5695, 286.76099999999997, 291.9785, 297.222, 302.4915], "draft": 3.677}, "min_match": 6, "max_rows": 12, "eos": [2], "prompt": [77, 66, 39, 51, 61, 53, 39, 57, 37, 7, 51, 37, 55, 26, 28, 5, 69, 40, 40, 32, 84, 6, 45, 76, 44, 14, 34, 27, 41, 61, 8, 4, 29, 81, 19, 61, 10, 41, 58, 13, 28, 11, 41, 17, 26, 66, 61, 80, 26, 84, 6, 45, 76, 44, 14, 34, 4, 9, 78, 59, 10, 86, 5, 11, 28, 64, 68, 18, 81, 70, 85, 34, 50, 10, 71, 18, 62, 61, 54, 43, 77, 82, 12, 39, 84, 78, 27, 5, 72, 66, 37, 60, 36, 45, 69, 63, 26, 19, 39, 5, 32, 42, 15, 59, 63, 65, 36, 9, 54, 53, 5, 27, 51, 82, 63, 15, 36, 9, 31, 25, 43, 80, 85, 34, 50, 10, 71, 18, 62, 52, 65, 81, 82, 64, 37, 77, 39, 82, 7, 7, 58, 27, 33, 12, 18, 88, 84, 10, 38, 42, 25, 31, 69, 75, 71, 67, 27, 33, 12, 18, 88, 84, 10, 38, 50, 3, 25, 69, 21, 83, 35, 81, 65, 60, 24, 50, 32, 76, 52, 10, 85, 34, 50, 10, 71, 18, 62, 61, 54, 43, 5, 4, 59, 71, 67, 39, 49, 67, 15, 59, 63, 65, 36, 9, 54, 53, 40, 57, 20, 73, 16, 5, 86, 80, 77, 75, 71, 67, 28, 80, 59, 45, 64, 33, 51, 77, 52, 88, 11, 26, 47, 80, 77, 75, 71, 67, 50, 10, 71, 18, 62, 61, 79, 84, 58, 77, 54, 19, 85, 21, 40, 17, 27, 33, 85, 29, 6, 10, 7, 63, 73, 77, 39, 82, 7, 7, 58], "rounds": [{"left": 416, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [3]}, {"left": 415, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [25, 69, 21]}, {"left": 412, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [83, 35, 81]}, {"left": 409, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [52, 30, 74]}, {"left": 406, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [39, 15]}, {"left": 404, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [36]}, {"left": 403, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [9]}, {"left": 402, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [31]}, {"left": 401, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [25, 43, 80, 85]}, {"left": 397, "kind": "l", "drafts": [34, 50, 10], "rows": 4, "kept": 3, "emitted": [34, 50, 10, 71]}, {"left": 393, "kind": "l", "drafts": [18, 62, 52, 65, 81, 82, 64, 37, 77, 39], "rows": 11, "kept": 2, "emitted": [18, 62, 61]}, {"left": 390, "kind": "l", "drafts": [54, 43, 5, 4, 59], "rows": 6, "kept": 2, "emitted": [54, 43, 20]}, {"left": 387, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [40]}, {"left": 386, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [37]}, {"left": 385, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [78, 35]}, {"left": 383, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [58, 27, 33]}, {"left": 380, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [12, 18, 88, 84]}, {"left": 376, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [10, 38, 47, 43, 74]}, {"left": 371, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [61, 30]}, {"left": 369, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [36]}, {"left": 368, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [87, 74, 10, 46, 72, 22]}, {"left": 362, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [56, 79, 25, 69]}, {"left": 358, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [21, 83, 35, 81, 76]}, {"left": 353, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [71, 80]}, {"left": 351, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [61]}, {"left": 350, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [30, 57, 12, 43]}, {"left": 346, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [27]}, {"left": 345, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [41, 9]}, {"left": 343, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [60, 77]}, {"left": 341, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [39]}, {"left": 340, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [82, 7]}, {"left": 338, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [7]}, {"left": 337, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [58]}, {"left": 336, "kind": "l", "drafts": [3, 25], "rows": 3, "kept": 0, "emitted": [27]}, {"left": 335, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [33]}, {"left": 334, "kind": "l", "drafts": [12, 18, 88, 84, 10, 38], "rows": 7, "kept": 5, "emitted": [12, 18, 88, 84, 10, 26]}, {"left": 328, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [28]}, {"left": 327, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [5]}, {"left": 326, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [69, 66, 61]}, {"left": 323, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [80]}, {"left": 322, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [26]}, {"left": 321, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [32]}, {"left": 320, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [24]}, {"left": 319, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [37, 59]}, {"left": 317, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [9, 35]}, {"left": 315, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [60, 45, 56]}, {"left": 312, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [56]}, {"left": 311, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [50, 58, 6, 64, 81]}, {"left": 306, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [18, 18, 72]}, {"left": 303, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [61, 30, 77, 82, 12, 39]}, {"left": 297, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [84, 78, 27, 5, 72]}, {"left": 292, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [66, 37]}, {"left": 290, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [60, 36]}, {"left": 288, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [45]}, {"left": 287, "kind": "l", "drafts": [69, 63, 26, 19], "rows": 5, "kept": 2, "emitted": [69, 63, 19]}, {"left": 284, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [75, 34]}, {"left": 282, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [4, 9, 78, 59, 18]}, {"left": 277, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [61]}, {"left": 276, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [53]}, {"left": 275, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [39, 57, 37, 27]}, {"left": 271, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [33, 12, 18, 88, 84]}, {"left": 266, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [10, 38, 65]}, {"left": 263, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [22]}, {"left": 262, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [18]}, {"left": 261, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [73]}, {"left": 260, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [16]}, {"left": 259, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [5]}, {"left": 258, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [5, 11, 79, 25, 19]}, {"left": 253, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [36, 30, 39, 69]}, {"left": 249, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [73, 63, 40, 72]}, {"left": 245, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [59]}, {"left": 244, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [63, 65, 36, 9]}, {"left": 240, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [54, 53, 40, 57, 20, 46]}, {"left": 234, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [14, 80, 36, 71]}, {"left": 230, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [67]}, {"left": 229, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [10]}, {"left": 228, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [53, 84]}, {"left": 226, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [74, 10, 86]}, {"left": 223, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [11, 32, 32, 26, 88]}, {"left": 218, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [79, 55, 48]}, {"left": 215, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [55, 86]}, {"left": 213, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [53]}, {"left": 212, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [41]}, {"left": 211, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [51]}, {"left": 210, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [34, 68, 50, 3, 25]}, {"left": 205, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [69]}, {"left": 204, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [21]}, {"left": 203, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [83]}, {"left": 202, "kind": "l", "drafts": [35], "rows": 2, "kept": 1, "emitted": [35, 81]}, {"left": 200, "kind": "l", "drafts": [65, 60, 24, 50, 32], "rows": 6, "kept": 0, "emitted": [52]}, {"left": 199, "kind": "l", "drafts": [30, 74, 39, 15], "rows": 5, "kept": 2, "emitted": [30, 74, 9]}, {"left": 196, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [11]}, {"left": 195, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [35]}, {"left": 194, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [65]}, {"left": 193, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [67, 43, 12, 58]}, {"left": 189, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [24, 58]}, {"left": 187, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [57, 43]}, {"left": 185, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [8]}, {"left": 184, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [64]}, {"left": 183, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [60]}, {"left": 182, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [2]}, {"left": 181, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [73, 3, 41]}, {"left": 178, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [71]}, {"left": 177, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [87, 80]}, {"left": 175, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [87]}, {"left": 174, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [16, 74, 46, 48]}, {"left": 170, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [64, 79, 18]}, {"left": 167, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [68, 14]}, {"left": 165, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [72]}, {"left": 164, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [86, 83, 42, 74, 42]}, {"left": 159, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [62, 70]}, {"left": 157, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [79, 59]}, {"left": 155, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [87]}, {"left": 154, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [26, 4, 4]}, {"left": 151, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [49, 55, 6, 86]}, {"left": 147, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [9]}, {"left": 146, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [39, 66, 86, 81]}, {"left": 142, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [59]}, {"left": 141, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [65]}, {"left": 140, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [45]}, {"left": 139, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [7]}, {"left": 138, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [7]}, {"left": 137, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [58, 27, 33]}, {"left": 134, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [12, 12, 74, 59]}, {"left": 130, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [45, 67]}, {"left": 128, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [53]}, {"left": 127, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [73, 85, 82]}, {"left": 124, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [2, 45]}, {"left": 122, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [59, 18]}, {"left": 120, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [17]}, {"left": 119, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [10, 71, 18]}, {"left": 116, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [62, 61, 54, 43, 60, 60]}, {"left": 110, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [48, 87]}, {"left": 108, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [51]}, {"left": 107, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [12]}, {"left": 106, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [45, 25, 9, 31]}, {"left": 102, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [25]}, {"left": 101, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [43, 80, 85, 34]}, {"left": 97, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [50, 10]}, {"left": 95, "kind": "l", "drafts": [71, 18, 62], "rows": 4, "kept": 3, "emitted": [71, 18, 62, 61]}, {"left": 91, "kind": "l", "drafts": [54, 43, 20, 40], "rows": 5, "kept": 0, "emitted": [55]}, {"left": 90, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [13, 65, 22, 18, 73, 16]}, {"left": 84, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [5, 86]}, {"left": 82, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [80]}, {"left": 81, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [77]}, {"left": 80, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [75, 71, 67]}, {"left": 77, "kind": "l", "drafts": [28, 80, 59], "rows": 4, "kept": 0, "emitted": [64]}, {"left": 76, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [46]}, {"left": 75, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [6, 84]}, {"left": 73, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [18]}, {"left": 72, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [72, 6, 16]}, {"left": 69, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [18, 73, 16, 5]}, {"left": 65, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [86, 80, 77, 51]}, {"left": 61, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [9, 14]}, {"left": 59, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [23, 84]}, {"left": 57, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [41, 34]}, {"left": 55, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [15, 12, 18]}, {"left": 52, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [88, 84, 10]}, {"left": 49, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [38]}, {"left": 48, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [2, 30, 13, 45]}, {"left": 44, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [34, 52]}, {"left": 42, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [20, 12]}, {"left": 40, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [46]}, {"left": 39, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [35, 73, 4, 73, 3]}, {"left": 34, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [25]}, {"left": 33, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [69, 38, 34]}, {"left": 30, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [4, 9, 78]}, {"left": 27, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [59, 18]}, {"left": 25, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [61, 53, 39]}, {"left": 22, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [57, 37, 7]}, {"left": 19, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [51, 37, 55]}, {"left": 16, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [26, 28]}, {"left": 14, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [5, 69, 66, 61]}, {"left": 10, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [80]}, {"left": 9, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [26, 50]}, {"left": 7, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [13]}, {"left": 6, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [42, 81]}, {"left": 4, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [55, 78]}, {"left": 2, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [16]}, {"left": 1, "kind": "s", "drafts": [], "rows": 1, "kept": 0, "emitted": [15]}]}, {"costs": {"verify": [42.0, 47.0, 52.0, 57.0, 62.0, 67.0, 72.0, 77.0, 82.0, 87.0, 92.0, 97.0, 102.0, 107.0, 112.0, 117.0], "draft": 5.6}, "min_match": 4, "max_rows": 16, "eos": [2], "prompt": [16, 9, 60, 89, 57, 39, 59, 25, 38, 46, 34, 44, 6, 8, 45, 55, 74, 16, 41, 21, 21, 31, 50, 34, 32, 16, 3, 69, 45, 91, 4, 93, 16, 9, 87, 94, 21, 89, 78, 17, 78, 47, 65, 76, 11, 27, 9, 66, 67, 69, 41, 87, 46, 13, 93, 16, 9, 87, 15, 24, 24, 43, 30, 55, 13, 5, 47, 83, 88, 43, 60, 88, 65, 3, 69, 45, 91, 4, 68, 59, 17, 21, 10, 87, 35, 2, 49, 76, 47, 56, 48, 9, 72, 67, 33], "rounds": [{"left": 191, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [24, 8]}, {"left": 189, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [90, 54]}, {"left": 187, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [73]}, {"left": 186, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [17, 43]}, {"left": 184, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [70, 78, 43]}, {"left": 181, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [30]}, {"left": 180, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [55, 13, 5, 47]}, {"left": 176, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [83, 88, 68]}, {"left": 173, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [89, 78]}, {"left": 171, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [17, 78]}, {"left": 169, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [47, 88, 4, 72]}, {"left": 165, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [44]}, {"left": 164, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [36]}, {"left": 163, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [31, 65]}, {"left": 161, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [21, 71]}, {"left": 159, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [8, 30]}, {"left": 157, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [16, 32, 51, 47]}, {"left": 153, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [16, 15]}, {"left": 151, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [32]}, {"left": 150, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [36, 24]}, {"left": 148, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [86]}, {"left": 147, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [4]}, {"left": 146, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [50]}, {"left": 145, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [66, 64]}, {"left": 143, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [55]}, {"left": 142, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [87]}, {"left": 141, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [49]}, {"left": 140, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [76]}, {"left": 139, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [47]}, {"left": 138, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [56, 48]}, {"left": 136, "kind": "l", "drafts": [9, 72], "rows": 3, "kept": 2, "emitted": [9, 72, 67]}, {"left": 133, "kind": "l", "drafts": [33, 24, 8, 90, 54, 73], "rows": 7, "kept": 1, "emitted": [33, 66]}, {"left": 131, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [21, 41]}, {"left": 129, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [10, 31, 66]}, {"left": 126, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [38]}, {"left": 125, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [43]}, {"left": 124, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [60, 88]}, {"left": 122, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [65, 69]}, {"left": 120, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [45, 91, 4]}, {"left": 117, "kind": "l", "drafts": [68, 59, 17, 21, 10], "rows": 6, "kept": 0, "emitted": [58]}, {"left": 116, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [84]}, {"left": 115, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [47]}, {"left": 114, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [34, 69, 40]}, {"left": 111, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [90, 15, 24, 24]}, {"left": 107, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [43, 30, 55, 13]}, {"left": 103, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [5, 2]}, {"left": 101, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [47, 34, 69]}, {"left": 98, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [40]}, {"left": 97, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [90, 15]}, {"left": 95, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [24, 24]}, {"left": 93, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [43, 30, 55]}, {"left": 90, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [13, 5, 47, 83, 88]}, {"left": 85, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [67, 89]}, {"left": 83, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [12]}, {"left": 82, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [91, 21, 45]}, {"left": 79, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [91, 4]}, {"left": 77, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [68, 51]}, {"left": 75, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [95, 29]}, {"left": 73, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [21, 45, 58]}, {"left": 70, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [71, 74]}, {"left": 68, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [77, 36]}, {"left": 66, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [52, 93]}, {"left": 64, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [56]}, {"left": 63, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [72]}, {"left": 62, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [67, 29, 52, 90]}, {"left": 58, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [39]}, {"left": 57, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [42]}, {"left": 56, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [74, 79]}, {"left": 54, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [14, 26, 68, 94, 90]}, {"left": 49, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [91, 53, 25, 40]}, {"left": 45, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [67, 94, 21]}, {"left": 42, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [67, 7, 31]}, {"left": 39, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [71]}, {"left": 38, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [86, 8]}, {"left": 36, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [91, 17, 16]}, {"left": 33, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [73, 64]}, {"left": 31, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [36]}, {"left": 30, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [8, 13]}, {"left": 28, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [21, 9, 68]}, {"left": 25, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [23, 66, 58, 84]}, {"left": 21, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [47, 34]}, {"left": 19, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [69, 40, 90, 15]}, {"left": 15, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [24]}, {"left": 14, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [24]}, {"left": 13, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [95]}, {"left": 12, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [60, 41, 46]}, {"left": 9, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [19, 59, 49]}, {"left": 6, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [61]}, {"left": 5, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [75, 44]}, {"left": 3, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [89, 55, 87]}]}, {"costs": {"verify": [21.291, 24.948500000000003, 28.631999999999998, 32.341499999999996, 36.077, 39.8385, 43.626000000000005, 47.439499999999995, 51.279, 55.144499999999994, 59.036, 62.9535, 66.89699999999999, 70.8665, 74.862, 78.8835, 82.931, 87.00450000000001, 91.104, 95.22949999999999, 99.381, 103.5585, 107.762, 111.99149999999999, 116.24699999999999, 120.5285, 124.83599999999998, 129.1695, 133.529, 137.9145, 142.326, 146.7635, 151.227, 155.7165, 160.232, 164.7735, 169.341, 173.93449999999999, 178.55399999999997, 183.1995, 187.871, 192.5685, 197.29199999999997, 202.04149999999998, 206.817, 211.61849999999998, 216.446, 221.29949999999997, 226.17899999999997, 231.0845, 236.016, 240.97349999999997, 245.95699999999997, 250.9665, 256.002, 261.0635, 266.15099999999995, 271.2645, 276.404, 281.5695, 286.76099999999997, 291.9785, 297.222, 302.4915], "draft": 3.677}, "min_match": 4, "max_rows": 16, "eos": [2], "prompt": [22, 94, 26, 41, 102, 30, 57, 5, 10, 10, 64, 13, 61, 72, 72, 9, 11, 74, 100, 51, 72, 99, 64, 8, 30, 100, 31, 32, 57, 51, 69, 64, 24, 67, 101, 101, 33, 30, 38, 14, 7, 29, 73, 27, 72, 95, 38, 2, 60, 57, 41, 61, 61, 41, 41, 43, 27, 90, 20, 19, 102, 72, 46, 91, 43, 98, 71, 5, 87, 30, 8, 51, 79, 57, 83, 78, 6, 36, 51, 52, 7, 29, 46, 62, 4, 68, 20, 79, 48, 83, 3, 55, 87, 65, 32, 2, 57, 41, 83, 30, 86, 52, 46, 90, 58, 61, 41, 41, 30, 38, 14, 7, 29, 53, 72, 6, 56, 40, 44, 100, 80, 102, 13, 87, 44, 3, 77, 77, 78, 47, 19, 58, 49, 99, 79, 45, 73, 60, 42, 65, 12, 97, 69, 64, 24, 67, 101, 101, 33, 30, 38, 14, 7, 29, 74, 40, 41, 13, 33, 101, 101, 33, 30, 38, 14, 7, 29, 21, 53, 72, 6, 56, 40, 44, 100, 50, 37, 21, 53, 72, 6, 56, 40, 44, 100, 50, 20, 60, 89, 40, 9, 25, 29, 4, 96, 50, 84, 29, 15, 64, 90, 20, 12, 41, 38, 14, 7, 29, 22, 69, 62, 102, 78, 45, 7, 54, 60, 57, 41, 60, 57, 41, 33, 81, 78, 12, 51, 54, 49, 23, 39, 27, 40, 61, 46, 62, 33, 7, 59, 71, 37, 43, 27, 90, 20, 101, 33, 30, 38, 14, 7, 29, 84, 28, 42, 101], "rounds": [{"left": 424, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [5]}, {"left": 423, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [24]}, {"left": 422, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [61]}, {"left": 421, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [61, 41]}, {"left": 419, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [41, 100, 34, 60]}, {"left": 415, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [57]}, {"left": 414, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [96, 86, 41, 20, 51]}, {"left": 409, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [80, 54, 13, 90]}, {"left": 405, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [62, 48]}, {"left": 403, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [30]}, {"left": 402, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [13, 51]}, {"left": 400, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [25, 50, 94, 15, 76]}, {"left": 395, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [46, 62]}, {"left": 393, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [4]}, {"left": 392, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [68, 73, 97]}, {"left": 389, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [39, 102, 76]}, {"left": 386, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [99]}, {"left": 385, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [5]}, {"left": 384, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [36]}, {"left": 383, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [82]}, {"left": 382, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [36]}, {"left": 381, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [15]}, {"left": 380, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [30, 75, 33]}, {"left": 377, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [98]}, {"left": 376, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [7, 76]}, {"left": 374, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [9]}, {"left": 373, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [9]}, {"left": 372, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [35, 57, 41]}, {"left": 369, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [11]}, {"left": 368, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [10]}, {"left": 367, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [73, 19, 51, 30]}, {"left": 363, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [20]}, {"left": 362, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [5]}, {"left": 361, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [24]}, {"left": 360, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [61, 61, 41, 41]}, {"left": 356, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [33, 93]}, {"left": 354, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [77]}, {"left": 353, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [101, 99, 8]}, {"left": 350, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [28, 23]}, {"left": 348, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [23, 68, 98, 15]}, {"left": 344, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [76, 46]}, {"left": 342, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [62, 4]}, {"left": 340, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [68, 20, 79, 48]}, {"left": 336, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [83, 30, 55, 50]}, {"left": 332, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [46]}, {"left": 331, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [46]}, {"left": 330, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [7, 59, 71, 37]}, {"left": 326, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [43, 27]}, {"left": 324, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [90, 20]}, {"left": 322, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [19, 102, 72]}, {"left": 319, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [46, 91]}, {"left": 317, "kind": "l", "drafts": [43, 98, 71], "rows": 4, "kept": 0, "emitted": [21]}, {"left": 316, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [53, 72, 6]}, {"left": 313, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [56, 40]}, {"left": 311, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [44, 100]}, {"left": 309, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [70]}, {"left": 308, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [44]}, {"left": 307, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [28]}, {"left": 306, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [102, 79, 68]}, {"left": 303, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [61, 41]}, {"left": 301, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [41, 38]}, {"left": 299, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [14, 7, 29, 84, 56]}, {"left": 294, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [40, 44]}, {"left": 292, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [100, 100]}, {"left": 290, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [91, 84, 61, 102, 30]}, {"left": 285, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [57, 2]}, {"left": 283, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [62]}, {"left": 282, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [4]}, {"left": 281, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [68]}, {"left": 280, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [20, 79, 48]}, {"left": 277, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [83]}, {"left": 276, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [7, 4, 65]}, {"left": 273, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [12]}, {"left": 272, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [97, 69, 52]}, {"left": 269, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [36]}, {"left": 268, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [29]}, {"left": 267, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [88]}, {"left": 266, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [12]}, {"left": 265, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [31, 54]}, {"left": 263, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [31, 24, 61, 61, 41]}, {"left": 258, "kind": "l", "drafts": [41], "rows": 2, "kept": 1, "emitted": [41, 2]}, {"left": 256, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [6, 7]}, {"left": 254, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [17]}, {"left": 253, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [52, 90]}, {"left": 251, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [94]}, {"left": 250, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [83, 46]}, {"left": 248, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [101]}, {"left": 247, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [79]}, {"left": 246, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [59, 71]}, {"left": 244, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [37, 43]}, {"left": 242, "kind": "l", "drafts": [27, 90, 20], "rows": 4, "kept": 3, "emitted": [27, 90, 20, 19]}, {"left": 238, "kind": "l", "drafts": [102, 72], "rows": 3, "kept": 2, "emitted": [102, 72, 46]}, {"left": 235, "kind": "l", "drafts": [91, 21, 53, 72, 6], "rows": 6, "kept": 1, "emitted": [91, 43]}, {"left": 233, "kind": "l", "drafts": [98, 71, 5], "rows": 4, "kept": 1, "emitted": [98, 91]}, {"left": 231, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [11]}, {"left": 230, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [12, 89, 66]}, {"left": 227, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [23, 66]}, {"left": 225, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [57, 41, 47]}, {"left": 222, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [83]}, {"left": 221, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [6]}, {"left": 220, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [89]}, {"left": 219, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [89]}, {"left": 218, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [22]}, {"left": 217, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [101]}, {"left": 216, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [86]}, {"left": 215, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [72, 6]}, {"left": 213, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [56]}, {"left": 212, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [40, 44]}, {"left": 210, "kind": "l", "drafts": [100, 70, 44, 28, 102, 79, 68, 61], "rows": 9, "kept": 0, "emitted": [91]}, {"left": 209, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [93]}, {"left": 208, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [59]}, {"left": 207, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [72]}, {"left": 206, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [97]}, {"left": 205, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [24]}, {"left": 204, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [78]}, {"left": 203, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [46]}, {"left": 202, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [49, 102]}, {"left": 200, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [41, 35]}, {"left": 198, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [86]}, {"left": 197, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [21, 70, 80, 47]}, {"left": 193, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [21]}, {"left": 192, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [19]}, {"left": 191, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [100, 87]}, {"left": 189, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [9, 38, 9, 93]}, {"left": 185, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [57, 41, 13]}, {"left": 182, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [68]}, {"left": 181, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [72, 9, 37, 21]}, {"left": 177, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [53]}, {"left": 176, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [72, 6]}, {"left": 174, "kind": "l", "drafts": [56, 40, 44], "rows": 4, "kept": 3, "emitted": [56, 40, 44, 100]}, {"left": 170, "kind": "l", "drafts": [50, 20], "rows": 3, "kept": 0, "emitted": [81]}, {"left": 169, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [85]}, {"left": 168, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [40]}, {"left": 167, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [79]}, {"left": 166, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [32, 2]}, {"left": 164, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [12]}, {"left": 163, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [53]}, {"left": 162, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [34]}, {"left": 161, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [36, 77, 59]}, {"left": 158, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [73, 11, 31]}, {"left": 155, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [71, 96]}, {"left": 153, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [57]}, {"left": 152, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [87, 46, 98, 8, 79, 48]}, {"left": 146, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [83]}, {"left": 145, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [16]}, {"left": 144, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [70, 70]}, {"left": 142, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [50, 37]}, {"left": 140, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [21, 53]}, {"left": 138, "kind": "l", "drafts": [72, 6, 56, 40, 44], "rows": 6, "kept": 4, "emitted": [72, 6, 56, 40, 60]}, {"left": 133, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [57, 41, 84, 14]}, {"left": 129, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [59]}, {"left": 128, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [39]}, {"left": 127, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [102]}, {"left": 126, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [43]}, {"left": 125, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [68]}, {"left": 124, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [46, 30, 55]}, {"left": 121, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [50, 46, 46, 7, 59]}, {"left": 116, "kind": "l", "drafts": [71, 37, 43, 27, 90], "rows": 6, "kept": 5, "emitted": [71, 37, 43, 27, 90, 20]}, {"left": 110, "kind": "l", "drafts": [19, 102], "rows": 3, "kept": 2, "emitted": [19, 102, 72]}, {"left": 107, "kind": "l", "drafts": [46, 91, 21, 53, 72, 6], "rows": 7, "kept": 1, "emitted": [46, 7]}, {"left": 105, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [59, 71, 37]}, {"left": 102, "kind": "l", "drafts": [43, 27, 90, 20, 19, 102, 72, 46], "rows": 9, "kept": 1, "emitted": [43, 5]}, {"left": 100, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [24]}, {"left": 99, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [61]}, {"left": 98, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [91, 11, 24, 81, 33]}, {"left": 93, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [60, 51]}, {"left": 91, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [96]}, {"left": 90, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [38, 15, 76, 46, 62, 4]}, {"left": 84, "kind": "l", "drafts": [68, 20, 79, 48, 83], "rows": 6, "kept": 5, "emitted": [68, 20, 79, 48, 83, 34]}, {"left": 78, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [60]}, {"left": 77, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [57, 41, 68]}, {"left": 74, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [20, 79, 48, 83, 60]}, {"left": 69, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [53, 41, 87]}, {"left": 66, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [29, 52]}, {"left": 64, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [16, 59]}, {"left": 62, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [50, 42, 30]}, {"left": 59, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [60, 16, 24]}, {"left": 56, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [2]}, {"left": 55, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [83]}, {"left": 54, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [41]}, {"left": 53, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [34]}, {"left": 52, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [60]}, {"left": 51, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [57]}, {"left": 50, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [41, 59]}, {"left": 48, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [74]}, {"left": 47, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [98, 12, 8, 28]}, {"left": 43, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [26, 70]}, {"left": 41, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [21, 53, 72, 6, 56]}, {"left": 36, "kind": "l", "drafts": [40, 60, 57, 41, 84, 14, 59, 39], "rows": 9, "kept": 1, "emitted": [40, 44]}, {"left": 34, "kind": "l", "drafts": [100, 81, 85, 40, 79, 32], "rows": 7, "kept": 1, "emitted": [100, 50]}, {"left": 32, "kind": "l", "drafts": [20, 60, 89], "rows": 4, "kept": 0, "emitted": [37]}, {"left": 31, "kind": "l", "drafts": [21, 53], "rows": 3, "kept": 2, "emitted": [21, 53, 72]}, {"left": 28, "kind": "l", "drafts": [6, 56, 40], "rows": 4, "kept": 3, "emitted": [6, 56, 40, 56]}, {"left": 24, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [40]}, {"left": 23, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [44, 100]}, {"left": 21, "kind": "l", "drafts": [50, 37, 21, 53, 72], "rows": 6, "kept": 0, "emitted": [41]}, {"left": 20, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [21, 86, 71]}, {"left": 17, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [22]}, {"left": 16, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [101, 99, 74]}, {"left": 13, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [21, 53]}, {"left": 11, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [72]}, {"left": 10, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [6]}, {"left": 9, "kind": "l", "drafts": [56, 40, 56, 40], "rows": 5, "kept": 2, "emitted": [56, 40, 44]}, {"left": 6, "kind": "l", "drafts": [100, 50, 37, 21], "rows": 5, "kept": 1, "emitted": [100, 5]}, {"left": 4, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [24, 61, 14]}, {"left": 1, "kind": "s", "drafts": [], "rows": 1, "kept": 0, "emitted": [7]}]}, {"costs": {"verify": [42.0, 47.0, 52.0, 57.0, 62.0, 67.0, 72.0, 77.0, 82.0, 87.0, 92.0, 97.0, 102.0, 107.0, 112.0, 117.0], "draft": 5.6}, "min_match": 3, "max_rows": 8, "eos": [2], "prompt": [100, 75, 101, 96, 50, 48, 7, 41, 96, 16, 67, 3, 26, 39, 28, 32, 11, 32, 90, 39, 75, 98, 39, 16, 92, 100, 5, 83, 53, 64, 80, 101], "rounds": [{"left": 154, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [58]}, {"left": 153, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [98]}, {"left": 152, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [24, 5, 39, 14, 33, 92]}, {"left": 146, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [92, 32]}, {"left": 144, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [90, 39, 75, 4, 70, 69]}, {"left": 138, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [73, 98, 39, 16, 109]}, {"left": 133, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [96, 68]}, {"left": 131, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [85]}, {"left": 130, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [67, 48, 37]}, {"left": 127, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [76, 91, 12, 82, 31, 96]}, {"left": 121, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [87, 59, 38]}, {"left": 118, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [5]}, {"left": 117, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [83, 75, 100, 75]}, {"left": 113, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [87]}, {"left": 112, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [59, 38, 65]}, {"left": 109, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [32, 28, 32]}, {"left": 106, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [11]}, {"left": 105, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [32, 90, 39, 57, 54, 90]}, {"left": 99, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [80, 15]}, {"left": 97, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [23]}, {"left": 96, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [98]}, {"left": 95, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [98, 71, 90, 12]}, {"left": 91, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [66]}, {"left": 90, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [11, 32]}, {"left": 88, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [90]}, {"left": 87, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [39, 75]}, {"left": 85, "kind": "l", "drafts": [98, 39], "rows": 3, "kept": 0, "emitted": [27]}, {"left": 84, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [89, 101, 66, 20]}, {"left": 80, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [2]}, {"left": 79, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [69, 12]}, {"left": 77, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [4, 70, 69]}, {"left": 74, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [73]}, {"left": 73, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [98, 39]}, {"left": 71, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [59, 38, 32]}, {"left": 68, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [90, 39, 75, 39]}, {"left": 64, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [75, 80]}, {"left": 62, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [31]}, {"left": 61, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [93]}, {"left": 60, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [75]}, {"left": 59, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [78, 22]}, {"left": 57, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [27, 40, 89, 15, 106]}, {"left": 52, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [109, 27, 42, 19]}, {"left": 48, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [84, 70, 69, 73, 98]}, {"left": 43, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [39, 83]}, {"left": 41, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [96, 50]}, {"left": 39, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [69]}, {"left": 38, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [12, 4]}, {"left": 36, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [70, 69]}, {"left": 34, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [73]}, {"left": 33, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [98]}, {"left": 32, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [39, 32, 90, 39, 75]}, {"left": 27, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [49]}, {"left": 26, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [32, 92]}, {"left": 24, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [42]}, {"left": 23, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [9, 82]}, {"left": 21, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [31, 96, 87, 59]}, {"left": 17, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [38, 45, 33]}, {"left": 14, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [13]}, {"left": 13, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [103, 5, 96, 29]}, {"left": 9, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [16, 109]}, {"left": 7, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [96, 68, 85, 67]}, {"left": 3, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [48]}, {"left": 2, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [37]}, {"left": 1, "kind": "s", "drafts": [], "rows": 1, "kept": 0, "emitted": [76]}]}, {"costs": {"verify": [21.291, 24.948500000000003, 28.631999999999998, 32.341499999999996, 36.077, 39.8385, 43.626000000000005, 47.439499999999995, 51.279, 55.144499999999994, 59.036, 62.9535, 66.89699999999999, 70.8665, 74.862, 78.8835, 82.931, 87.00450000000001, 91.104, 95.22949999999999, 99.381, 103.5585, 107.762, 111.99149999999999, 116.24699999999999, 120.5285, 124.83599999999998, 129.1695, 133.529, 137.9145, 142.326, 146.7635, 151.227, 155.7165, 160.232, 164.7735, 169.341, 173.93449999999999, 178.55399999999997, 183.1995, 187.871, 192.5685, 197.29199999999997, 202.04149999999998, 206.817, 211.61849999999998, 216.446, 221.29949999999997, 226.17899999999997, 231.0845, 236.016, 240.97349999999997, 245.95699999999997, 250.9665, 256.002, 261.0635, 266.15099999999995, 271.2645, 276.404, 281.5695, 286.76099999999997, 291.9785, 297.222, 302.4915], "draft": 3.677}, "min_match": 6, "max_rows": 12, "eos": [2], "prompt": [98, 51, 82, 17, 47, 30, 13, 58, 108, 109, 9, 11, 105, 26, 93, 10, 25, 24, 49, 20, 19, 21, 49, 22, 8, 27, 38, 70, 116, 36, 75, 27, 36, 95, 7, 36, 60, 11, 32, 28, 99, 42, 24, 20, 25, 25, 24, 49, 20, 19, 21, 49, 22, 8, 27, 38, 70, 116, 36, 71, 55, 43, 70, 74, 104, 30, 25, 100, 61, 64, 105, 24, 8, 47, 30, 13, 58, 108, 101, 103, 66, 37, 72, 42, 110, 15, 9, 94, 38, 109, 57, 52, 58, 25, 113, 50, 21, 10, 5, 63, 110, 110, 108, 23, 71, 3, 90, 37, 70, 27, 36, 95, 7, 36, 60, 11, 32, 28, 99, 42, 24, 20, 25, 65, 59, 98, 41, 26, 15, 68, 100, 112, 61, 59, 94, 108, 97, 11, 58, 38, 96, 20, 108, 60, 69, 110, 51, 100, 40, 3, 32, 112, 75, 13, 44, 30, 70, 116, 36, 75, 27, 36, 95, 7], "rounds": [{"left": 222, "kind": "l", "drafts": [36, 60, 11], "rows": 4, "kept": 0, "emitted": [22]}, {"left": 221, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [8, 27, 38]}, {"left": 218, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [70]}, {"left": 217, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [116, 36]}, {"left": 215, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [75]}, {"left": 214, "kind": "l", "drafts": [27, 36], "rows": 3, "kept": 2, "emitted": [27, 36, 95]}, {"left": 211, "kind": "l", "drafts": [7, 36, 60, 11], "rows": 5, "kept": 4, "emitted": [7, 36, 60, 11, 32]}, {"left": 206, "kind": "l", "drafts": [28, 99, 42, 24, 20, 25, 25], "rows": 8, "kept": 0, "emitted": [35]}, {"left": 205, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [42, 26]}, {"left": 203, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [15, 68]}, {"left": 201, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [100, 112]}, {"left": 199, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [105]}, {"left": 198, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [15, 93, 22, 86]}, {"left": 194, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [116, 84, 25, 18, 62]}, {"left": 189, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [87, 40, 107, 104, 98]}, {"left": 184, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [51, 82]}, {"left": 182, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [17, 47]}, {"left": 180, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [30, 13, 58, 108, 2]}, {"left": 175, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [15, 111, 33, 80, 58]}, {"left": 170, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [92, 75, 56]}, {"left": 167, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [10, 2]}, {"left": 165, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [94, 28, 60, 30]}, {"left": 161, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [86]}, {"left": 160, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [56, 11, 66, 94, 44]}, {"left": 155, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [24, 56, 34]}, {"left": 152, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [13, 49]}, {"left": 150, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [78, 86]}, {"left": 148, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [32]}, {"left": 147, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [113]}, {"left": 146, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [7]}, {"left": 145, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [25, 92, 78]}, {"left": 142, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [78, 64, 105, 24]}, {"left": 138, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [8]}, {"left": 137, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [22]}, {"left": 136, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [87, 116, 114, 44, 24]}, {"left": 131, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [56, 34, 13]}, {"left": 128, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [49, 78]}, {"left": 126, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [68]}, {"left": 125, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [100, 112]}, {"left": 123, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [99, 36, 17, 28, 105]}, {"left": 118, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [92]}, {"left": 117, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [88, 2, 56]}, {"left": 114, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [25]}, {"left": 113, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [87, 59, 54]}, {"left": 110, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [103, 90, 59, 67]}, {"left": 106, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [36]}, {"left": 105, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [25]}, {"left": 104, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [67, 36, 25]}, {"left": 101, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [18]}, {"left": 100, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [62, 87, 40, 107]}, {"left": 96, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [104]}, {"left": 95, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [98, 51]}, {"left": 93, "kind": "l", "drafts": [82, 17, 47, 30, 13, 58, 108], "rows": 8, "kept": 6, "emitted": [82, 17, 47, 30, 13, 58, 75]}, {"left": 86, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [13, 44]}, {"left": 84, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [115, 4]}, {"left": 82, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [64]}, {"left": 81, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [97]}, {"left": 80, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [54, 100, 112, 2, 43]}, {"left": 75, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [49, 20, 19, 107, 30]}, {"left": 70, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [97, 5, 29]}, {"left": 67, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [112, 44]}, {"left": 65, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [20, 108, 60, 55, 28, 35]}, {"left": 59, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [42, 26, 15]}, {"left": 56, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [100, 55]}, {"left": 54, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [7, 49]}, {"left": 52, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [25, 24]}, {"left": 50, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [49, 20]}, {"left": 48, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [19, 21]}, {"left": 46, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [49]}, {"left": 45, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [22, 8]}, {"left": 43, "kind": "l", "drafts": [27, 38, 70, 116, 36, 71, 55, 43, 70], "rows": 10, "kept": 5, "emitted": [27, 38, 70, 116, 36, 75]}, {"left": 37, "kind": "l", "drafts": [27, 36, 95, 7, 36, 60, 11, 32], "rows": 9, "kept": 5, "emitted": [27, 36, 95, 7, 36, 70]}, {"left": 31, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [72]}, {"left": 30, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [24, 94, 97, 15]}, {"left": 26, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [36, 60, 32]}, {"left": 23, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [35]}, {"left": 22, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [114]}, {"left": 21, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [63, 101]}, {"left": 19, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [19, 66]}, {"left": 17, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [108, 31, 110, 22, 37]}, {"left": 12, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [29, 96, 7, 68, 76]}, {"left": 7, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [76, 2, 103]}, {"left": 4, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [90, 59]}, {"left": 2, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [67]}, {"left": 1, "kind": "s", "drafts": [], "rows": 1, "kept": 0, "emitted": [36]}]}, {"costs": {"verify": [42.0, 47.0, 52.0, 57.0, 62.0, 67.0, 72.0, 77.0, 82.0, 87.0, 92.0, 97.0, 102.0, 107.0, 112.0, 117.0], "draft": 5.6}, "min_match": 4, "max_rows": 16, "eos": [2], "prompt": [92, 59, 86, 31, 116, 100, 26, 103, 86, 67, 107, 74, 119, 19, 64, 84, 108, 65, 23, 108, 41, 33, 70, 68, 85, 39, 87, 43, 53, 46, 19, 98, 39, 65, 23, 108, 41, 4, 53, 46, 19, 98, 39, 65, 23, 108, 41, 104, 13, 77, 35, 37, 49, 110, 36, 66, 26, 120, 16, 64, 81, 117, 117, 86, 111, 58, 71, 78, 28, 85, 22, 59, 56, 3, 54, 61, 120, 16, 64, 81, 3, 6, 4, 99, 104, 15, 77, 52, 85, 39, 87, 43, 53, 87, 82, 70, 94, 34, 119, 21, 32, 9, 92, 18, 110, 101, 7, 36, 118, 37, 72, 69, 100, 118, 94, 119], "rounds": [{"left": 359, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [19, 64]}, {"left": 357, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [84, 108, 72, 39]}, {"left": 353, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [104, 9, 93, 26]}, {"left": 349, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [6]}, {"left": 348, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [27]}, {"left": 347, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [94]}, {"left": 346, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [34, 119, 21]}, {"left": 343, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [32]}, {"left": 342, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [9, 92]}, {"left": 340, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [18, 110, 101, 7]}, {"left": 336, "kind": "l", "drafts": [36, 118, 37, 72, 69], "rows": 6, "kept": 5, "emitted": [36, 118, 37, 72, 69, 100]}, {"left": 330, "kind": "l", "drafts": [118, 94, 119, 19, 64, 84, 108], "rows": 8, "kept": 0, "emitted": [68]}, {"left": 329, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [54, 12]}, {"left": 327, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [15, 104, 34, 119]}, {"left": 323, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [73]}, {"left": 322, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [96, 105]}, {"left": 320, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [101]}, {"left": 319, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [81, 95, 111, 80]}, {"left": 315, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [33]}, {"left": 314, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [70, 68]}, {"left": 312, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [52]}, {"left": 311, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [85, 39, 87]}, {"left": 308, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [43, 53, 46, 19]}, {"left": 304, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [98, 39, 65]}, {"left": 301, "kind": "l", "drafts": [23, 108, 41, 4, 53, 46, 19, 98, 39, 65, 23, 108, 41, 104, 13], "rows": 16, "kept": 3, "emitted": [23, 108, 41, 104]}, {"left": 297, "kind": "l", "drafts": [13, 77, 35, 37, 49, 110, 36, 66, 26], "rows": 10, "kept": 0, "emitted": [35]}, {"left": 296, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [27, 94]}, {"left": 294, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [34, 119, 21, 32, 8]}, {"left": 289, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [120]}, {"left": 288, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [19, 65, 52, 85, 39]}, {"left": 283, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [87, 43, 53, 46, 19]}, {"left": 278, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [98, 39]}, {"left": 276, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [65]}, {"left": 275, "kind": "l", "drafts": [23, 108, 41, 104, 35], "rows": 6, "kept": 3, "emitted": [23, 108, 41, 87]}, {"left": 271, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [82, 70]}, {"left": 269, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [22, 49, 104, 9]}, {"left": 265, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [120, 16, 64, 81]}, {"left": 261, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [117]}, {"left": 260, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [87]}, {"left": 259, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [68]}, {"left": 258, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [54]}, {"left": 257, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [12]}, {"left": 256, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [15, 77, 52, 85]}, {"left": 252, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [114, 111]}, {"left": 250, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [80]}, {"left": 249, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [33, 70, 68]}, {"left": 246, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [104]}, {"left": 245, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [62, 84]}, {"left": 243, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [108, 72, 39]}, {"left": 240, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [54, 12, 15, 8]}, {"left": 236, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [65]}, {"left": 235, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [23]}, {"left": 234, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [108]}, {"left": 233, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [41, 64, 95, 49, 108]}, {"left": 228, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [24, 86]}, {"left": 226, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [67, 120, 114]}, {"left": 223, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [111, 80, 33, 70, 68]}, {"left": 218, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [15]}, {"left": 217, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [77, 52, 85]}, {"left": 214, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [39]}, {"left": 213, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [87, 43]}, {"left": 211, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [53, 46, 19]}, {"left": 208, "kind": "l", "drafts": [98, 39, 65, 23, 108], "rows": 6, "kept": 5, "emitted": [98, 39, 65, 23, 108, 41]}, {"left": 202, "kind": "l", "drafts": [87, 82, 70, 22, 49, 104, 9, 120], "rows": 9, "kept": 0, "emitted": [72]}, {"left": 201, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [71]}, {"left": 200, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [8]}, {"left": 199, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [42]}, {"left": 198, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [79, 54, 100, 93]}, {"left": 194, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [4]}, {"left": 193, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [87, 68, 54, 12]}, {"left": 189, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [15, 77, 52, 85, 102]}, {"left": 184, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [116]}, {"left": 183, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [78]}, {"left": 182, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [102, 115]}, {"left": 180, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [57, 60]}, {"left": 178, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [67]}, {"left": 177, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [25, 49, 102]}, {"left": 174, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [118]}, {"left": 173, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [10, 100, 26]}, {"left": 170, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [103]}, {"left": 169, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [86, 67]}, {"left": 167, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [120]}, {"left": 166, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [114]}, {"left": 165, "kind": "l", "drafts": [111, 80], "rows": 3, "kept": 2, "emitted": [111, 80, 33]}, {"left": 162, "kind": "l", "drafts": [70, 68, 15, 77, 52], "rows": 6, "kept": 1, "emitted": [70, 29]}, {"left": 160, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [81]}, {"left": 159, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [52, 116]}, {"left": 157, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [107]}, {"left": 156, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [74, 119, 19, 64, 84]}, {"left": 151, "kind": "l", "drafts": [108, 65, 23, 108], "rows": 5, "kept": 1, "emitted": [108, 72]}, {"left": 149, "kind": "l", "drafts": [39, 104, 9], "rows": 4, "kept": 1, "emitted": [39, 2]}, {"left": 147, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [14, 23, 28, 107, 74, 119]}, {"left": 141, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [19, 64]}, {"left": 139, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [84, 108]}, {"left": 137, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [72, 39, 60, 36]}, {"left": 133, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [7, 123, 56, 10, 54, 18]}, {"left": 127, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [66]}, {"left": 126, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [25, 3, 111, 116]}, {"left": 122, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [37]}, {"left": 121, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [118, 94, 119, 12, 26, 71]}, {"left": 115, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [24, 98, 49, 118]}, {"left": 111, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [10]}, {"left": 110, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [100, 26, 103]}, {"left": 107, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [86, 67, 120]}, {"left": 104, "kind": "l", "drafts": [114, 111, 80, 33, 70], "rows": 6, "kept": 3, "emitted": [114, 111, 80, 14]}, {"left": 100, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [108, 102]}, {"left": 98, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [43, 81, 41, 42]}, {"left": 94, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [40, 74, 81, 52]}, {"left": 90, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [64]}, {"left": 89, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [95]}, {"left": 88, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [49]}, {"left": 87, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [108]}, {"left": 86, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [24]}, {"left": 85, "kind": "l", "drafts": [86, 67, 120], "rows": 4, "kept": 0, "emitted": [29]}, {"left": 84, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [81, 52]}, {"left": 82, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [116, 107]}, {"left": 80, "kind": "l", "drafts": [74, 119], "rows": 3, "kept": 1, "emitted": [74, 77]}, {"left": 78, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [104]}, {"left": 77, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [106]}, {"left": 76, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [60, 35, 29, 103]}, {"left": 72, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [68]}, {"left": 71, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [43, 59]}, {"left": 69, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [111, 101, 41]}, {"left": 66, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [94, 104]}, {"left": 64, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [81, 55, 14, 104, 27]}, {"left": 59, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [93]}, {"left": 58, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [121]}, {"left": 57, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [74, 121, 63, 69]}, {"left": 53, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [75]}, {"left": 52, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [30, 6]}, {"left": 50, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [92]}, {"left": 49, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [32]}, {"left": 48, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [62, 119, 4, 64, 71]}, {"left": 43, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [54, 85]}, {"left": 41, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [15, 48, 104]}, {"left": 38, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [49, 108, 24]}, {"left": 35, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [29]}, {"left": 34, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [81]}, {"left": 33, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [52]}, {"left": 32, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [116]}, {"left": 31, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [107, 74, 119]}, {"left": 28, "kind": "l", "drafts": [19, 64], "rows": 3, "kept": 2, "emitted": [19, 64, 108]}, {"left": 25, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [41]}, {"left": 24, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [118]}, {"left": 23, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [10]}, {"left": 22, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [100, 26, 103, 23]}, {"left": 18, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [61]}, {"left": 17, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [99]}, {"left": 16, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [17, 89]}, {"left": 14, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [26]}, {"left": 13, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [79]}, {"left": 12, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [42, 107]}, {"left": 10, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [42, 56, 107, 51, 42]}, {"left": 5, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [97, 43, 111, 86, 119]}]}, {"costs": {"verify": [21.291, 24.948500000000003, 28.631999999999998, 32.341499999999996, 36.077, 39.8385, 43.626000000000005, 47.439499999999995, 51.279, 55.144499999999994, 59.036, 62.9535, 66.89699999999999, 70.8665, 74.862, 78.8835, 82.931, 87.00450000000001, 91.104, 95.22949999999999, 99.381, 103.5585, 107.762, 111.99149999999999, 116.24699999999999, 120.5285, 124.83599999999998, 129.1695, 133.529, 137.9145, 142.326, 146.7635, 151.227, 155.7165, 160.232, 164.7735, 169.341, 173.93449999999999, 178.55399999999997, 183.1995, 187.871, 192.5685, 197.29199999999997, 202.04149999999998, 206.817, 211.61849999999998, 216.446, 221.29949999999997, 226.17899999999997, 231.0845, 236.016, 240.97349999999997, 245.95699999999997, 250.9665, 256.002, 261.0635, 266.15099999999995, 271.2645, 276.404, 281.5695, 286.76099999999997, 291.9785, 297.222, 302.4915], "draft": 3.677}, "min_match": 4, "max_rows": 16, "eos": [2], "prompt": [58, 70, 18, 98, 10, 96, 109, 82, 80, 103, 83, 111, 13, 126, 28, 93, 9, 126, 119, 71, 102, 98, 4, 72, 14, 123, 117, 126, 37, 67, 76, 89, 77, 63, 17, 25, 29, 88, 44, 118, 110, 23, 125, 70, 16, 119, 48, 9, 106, 48, 109, 39, 50, 80, 22, 60, 106, 78, 38, 98, 10, 96, 109, 82, 80, 103, 83, 111, 13, 126, 28, 93, 9, 126, 119, 71, 85, 80, 103, 83, 111, 13, 126, 28, 93, 9, 82, 80, 103, 83, 111, 13, 126, 28, 93, 9, 126, 119, 71, 88, 77, 125, 70, 16, 119, 48, 9, 104, 50, 57, 119, 96, 33, 36, 126, 9, 68, 69, 3, 87, 22, 117, 87, 20, 56, 67, 78, 44, 103, 93, 86, 54, 34, 40, 103, 29, 108, 33, 99, 126, 49, 21, 78, 84, 76, 54, 60, 106, 78, 38, 98, 10, 96, 109, 82, 80, 103, 83, 111, 13, 126, 28, 93, 9, 126, 119, 71, 79, 32, 72, 14, 55, 59, 114, 122, 44, 84, 109, 11, 31, 73, 56, 28, 70, 106, 77, 46, 86, 90, 21, 109, 119, 90, 29, 114, 24, 130, 52, 105, 31, 80, 3, 95, 129, 117, 19, 9, 130, 81, 28, 112, 49, 64, 85, 83, 111, 13, 126, 28, 93, 9, 126, 119, 71, 26, 13, 15, 92, 71, 22, 109, 119, 15, 60, 43, 12, 13, 102, 98, 4, 72, 14, 123, 117, 126, 37, 67, 76, 89, 77, 63, 17, 25, 29, 88, 44, 118, 110, 23, 125, 70, 16, 119, 121, 39, 54, 117, 67, 126], "rounds": [{"left": 419, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [109, 39]}, {"left": 417, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [50, 80]}, {"left": 415, "kind": "l", "drafts": [22], "rows": 2, "kept": 1, "emitted": [22, 60]}, {"left": 413, "kind": "l", "drafts": [106, 78, 38], "rows": 4, "kept": 3, "emitted": [106, 78, 38, 98]}, {"left": 409, "kind": "l", "drafts": [10, 96, 109, 82], "rows": 5, "kept": 1, "emitted": [10, 109]}, {"left": 407, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [11, 31, 73, 56, 28]}, {"left": 402, "kind": "l", "drafts": [70, 106, 77, 46, 86], "rows": 6, "kept": 1, "emitted": [70, 75]}, {"left": 400, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [62]}, {"left": 399, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [97, 107, 49]}, {"left": 396, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [98, 10, 96]}, {"left": 393, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [109]}, {"left": 392, "kind": "l", "drafts": [82, 80, 103], "rows": 4, "kept": 3, "emitted": [82, 80, 103, 83]}, {"left": 388, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [111, 13, 126]}, {"left": 385, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [28]}, {"left": 384, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [93]}, {"left": 383, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [9]}, {"left": 382, "kind": "l", "drafts": [126, 119, 71], "rows": 4, "kept": 3, "emitted": [126, 119, 71, 118]}, {"left": 378, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [9]}, {"left": 377, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [53, 108, 36, 35]}, {"left": 373, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [51]}, {"left": 372, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [95]}, {"left": 371, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [29, 90, 115, 115]}, {"left": 367, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [122, 2, 9, 103, 90]}, {"left": 362, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [107]}, {"left": 361, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [39]}, {"left": 360, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [37, 117]}, {"left": 358, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [60, 69, 35, 95, 10]}, {"left": 353, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [63, 111, 35, 113, 21]}, {"left": 348, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [48, 88, 40, 97]}, {"left": 344, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [40, 11, 124, 17]}, {"left": 340, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [64, 34, 22]}, {"left": 337, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [85, 121, 128, 35, 59, 87]}, {"left": 331, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [91, 42, 4, 70]}, {"left": 327, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [106]}, {"left": 326, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [48, 109, 39]}, {"left": 323, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [50]}, {"left": 322, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [80, 22, 60]}, {"left": 319, "kind": "l", "drafts": [106, 78, 38, 98], "rows": 5, "kept": 4, "emitted": [106, 78, 38, 98, 10]}, {"left": 314, "kind": "l", "drafts": [96, 109, 82, 80, 103, 83, 111], "rows": 8, "kept": 7, "emitted": [96, 109, 82, 80, 103, 83, 111, 13]}, {"left": 306, "kind": "l", "drafts": [126, 28, 93, 9], "rows": 5, "kept": 4, "emitted": [126, 28, 93, 9, 126]}, {"left": 301, "kind": "l", "drafts": [119, 71, 85, 80, 103, 83, 111, 13, 126, 28, 93, 9, 82, 80], "rows": 15, "kept": 2, "emitted": [119, 71, 124]}, {"left": 298, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [2, 125, 50]}, {"left": 295, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [65]}, {"left": 294, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [28, 122, 77]}, {"left": 291, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [19, 85, 73, 108, 130, 124]}, {"left": 285, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [114, 21]}, {"left": 283, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [77, 80, 45, 85]}, {"left": 279, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [69]}, {"left": 278, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [35, 95, 10, 63, 91]}, {"left": 273, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [42, 4]}, {"left": 271, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [70, 106, 48, 109]}, {"left": 267, "kind": "l", "drafts": [39, 50, 80, 22], "rows": 5, "kept": 4, "emitted": [39, 50, 80, 22, 60]}, {"left": 262, "kind": "l", "drafts": [106, 78, 38, 98, 10, 96, 109, 82, 80, 103, 83, 111, 13], "rows": 14, "kept": 13, "emitted": [106, 78, 38, 98, 10, 96, 109, 82, 80, 103, 83, 111, 13, 126]}, {"left": 248, "kind": "l", "drafts": [28, 93, 9, 126, 119, 71], "rows": 7, "kept": 2, "emitted": [28, 93, 22]}, {"left": 245, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [60]}, {"left": 244, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [106]}, {"left": 243, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [78]}, {"left": 242, "kind": "l", "drafts": [38, 98, 10, 96, 109, 82, 80, 103], "rows": 9, "kept": 8, "emitted": [38, 98, 10, 96, 109, 82, 80, 103, 83]}, {"left": 233, "kind": "l", "drafts": [111, 13, 126, 28, 93, 22, 60, 106, 78, 38, 98, 10, 96, 109, 82], "rows": 16, "kept": 5, "emitted": [111, 13, 126, 28, 93, 9]}, {"left": 227, "kind": "l", "drafts": [126, 119, 71, 124], "rows": 5, "kept": 3, "emitted": [126, 119, 71, 94]}, {"left": 223, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [32]}, {"left": 222, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [104]}, {"left": 221, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [35]}, {"left": 220, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [39]}, {"left": 219, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [80, 65]}, {"left": 217, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [7, 2]}, {"left": 215, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [72, 9, 74]}, {"left": 212, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [72, 14, 55, 59]}, {"left": 208, "kind": "l", "drafts": [114, 122, 44, 84, 109, 11, 31, 73, 56, 28, 70, 106, 77, 46, 86], "rows": 16, "kept": 13, "emitted": [114, 122, 44, 84, 109, 11, 31, 73, 56, 28, 70, 106, 77, 40]}, {"left": 194, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [81]}, {"left": 193, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [5, 91, 80, 45, 85, 69]}, {"left": 187, "kind": "l", "drafts": [35, 95, 10, 63, 91, 42, 4, 70, 106, 48, 109, 39, 50, 80], "rows": 15, "kept": 4, "emitted": [35, 95, 10, 63, 111]}, {"left": 182, "kind": "l", "drafts": [35, 113, 21, 48, 88, 40, 97, 40, 11, 124, 17], "rows": 12, "kept": 5, "emitted": [35, 113, 21, 48, 88, 22]}, {"left": 176, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [117]}, {"left": 175, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [87]}, {"left": 174, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [20]}, {"left": 173, "kind": "l", "drafts": [56, 67, 78, 44, 103, 93, 86, 54, 34, 40], "rows": 11, "kept": 10, "emitted": [56, 67, 78, 44, 103, 93, 86, 54, 34, 40, 103]}, {"left": 162, "kind": "l", "drafts": [29, 108, 33, 99, 126, 49, 21, 78, 84, 76, 54, 60, 106, 78, 38], "rows": 16, "kept": 11, "emitted": [29, 108, 33, 99, 126, 49, 21, 78, 84, 76, 54, 10]}, {"left": 150, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [63, 111, 35]}, {"left": 147, "kind": "l", "drafts": [113, 21, 48, 88, 22, 117, 87, 20, 56, 67, 78, 44, 103], "rows": 14, "kept": 4, "emitted": [113, 21, 48, 88, 69]}, {"left": 142, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [28]}, {"left": 141, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [111]}, {"left": 140, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [125]}, {"left": 139, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [6, 7, 89]}, {"left": 136, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [112, 78, 108]}, {"left": 133, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [96]}, {"left": 132, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [32, 66, 106]}, {"left": 129, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [119]}, {"left": 128, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [15, 60]}, {"left": 126, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [43, 12, 13]}, {"left": 123, "kind": "l", "drafts": [102, 98, 4, 72, 14, 123, 117, 126, 37, 67, 76], "rows": 12, "kept": 2, "emitted": [102, 98, 15]}, {"left": 120, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [15, 77, 63, 17]}, {"left": 116, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [25, 29, 88]}, {"left": 113, "kind": "l", "drafts": [44, 118, 110, 23, 125, 70, 16, 119, 121], "rows": 10, "kept": 8, "emitted": [44, 118, 110, 23, 125, 70, 16, 119, 48]}, {"left": 104, "kind": "l", "drafts": [9, 106, 48, 109, 39, 50, 80, 22, 60, 106, 78, 38, 98, 10, 96], "rows": 16, "kept": 1, "emitted": [9, 58]}, {"left": 102, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [3, 97, 48]}, {"left": 99, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [129]}, {"left": 98, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [117]}, {"left": 97, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [19]}, {"left": 96, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [85, 16, 119, 48]}, {"left": 92, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [9]}, {"left": 91, "kind": "l", "drafts": [58, 3, 97, 48, 129, 117, 19, 85, 16], "rows": 10, "kept": 0, "emitted": [2]}, {"left": 90, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [100, 79]}, {"left": 88, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [34, 12]}, {"left": 86, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [72]}, {"left": 85, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [93]}, {"left": 84, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [86]}, {"left": 83, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [54, 34, 40]}, {"left": 80, "kind": "l", "drafts": [103, 29, 108, 33, 99, 126, 49, 21], "rows": 9, "kept": 3, "emitted": [103, 29, 108, 130]}, {"left": 76, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [19]}, {"left": 75, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [71]}, {"left": 74, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [109, 82]}, {"left": 72, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [80]}, {"left": 71, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [103, 83]}, {"left": 69, "kind": "l", "drafts": [111, 13, 126, 28, 93, 9, 126, 119], "rows": 9, "kept": 8, "emitted": [111, 13, 126, 28, 93, 9, 126, 119, 71]}, {"left": 60, "kind": "l", "drafts": [94, 32, 104, 35, 39, 80, 65, 7], "rows": 9, "kept": 0, "emitted": [67]}, {"left": 59, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [15]}, {"left": 58, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [83, 111, 13, 126]}, {"left": 54, "kind": "l", "drafts": [28, 93, 9, 126, 119, 71, 67, 15, 83], "rows": 10, "kept": 6, "emitted": [28, 93, 9, 126, 119, 71, 19]}, {"left": 47, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [85, 123, 28]}, {"left": 44, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [118, 74, 124, 118]}, {"left": 40, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [33, 12]}, {"left": 38, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [126]}, {"left": 37, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [25]}, {"left": 36, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [21]}, {"left": 35, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [48]}, {"left": 34, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [88]}, {"left": 33, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [88]}, {"left": 32, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [67, 76, 89, 77, 63, 17]}, {"left": 26, "kind": "l", "drafts": [25, 29, 88, 44, 118, 110, 23, 125, 70], "rows": 10, "kept": 9, "emitted": [25, 29, 88, 44, 118, 110, 23, 125, 70, 16]}, {"left": 16, "kind": "l", "drafts": [119, 121, 39, 54, 117, 67], "rows": 7, "kept": 1, "emitted": [119, 48]}, {"left": 14, "kind": "l", "drafts": [9, 106, 48, 109, 39], "rows": 6, "kept": 1, "emitted": [9, 51]}, {"left": 12, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [119, 54, 49]}, {"left": 9, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [88, 124, 125, 38]}, {"left": 5, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [72]}, {"left": 4, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [14]}, {"left": 3, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [123, 117]}, {"left": 1, "kind": "s", "drafts": [], "rows": 1, "kept": 0, "emitted": [126]}]}, {"costs": {"verify": [42.0, 47.0, 52.0, 57.0, 62.0, 67.0, 72.0, 77.0, 82.0, 87.0, 92.0, 97.0, 102.0, 107.0, 112.0, 117.0], "draft": 5.6}, "min_match": 3, "max_rows": 8, "eos": [2], "prompt": [91, 64, 34, 7, 34, 13, 78, 34, 126, 110, 126, 37, 18, 90, 7, 47, 137, 103, 41, 31, 89, 109, 128, 61, 44, 73, 97, 74, 121, 128, 94, 133, 111, 25, 11, 88, 48, 65, 111, 99, 27, 51, 125, 52, 64, 101, 54, 121, 128, 94, 133, 111], "rounds": [{"left": 434, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [110, 126]}, {"left": 432, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [37]}, {"left": 431, "kind": "l", "drafts": [18, 90], "rows": 3, "kept": 1, "emitted": [18, 54]}, {"left": 429, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [55]}, {"left": 428, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [91]}, {"left": 427, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [64]}, {"left": 426, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [34, 72, 19]}, {"left": 423, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [91]}, {"left": 422, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [122, 34, 92, 4]}, {"left": 418, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [64]}, {"left": 417, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [68, 11]}, {"left": 415, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [111, 112, 47, 122, 134]}, {"left": 410, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [128, 67, 109]}, {"left": 407, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [123, 29, 4]}, {"left": 404, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [58]}, {"left": 403, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [40, 66]}, {"left": 401, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [135, 39, 97, 87]}, {"left": 397, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [137]}, {"left": 396, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [65, 50, 65]}, {"left": 393, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [17, 74]}, {"left": 391, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [121]}, {"left": 390, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [128, 22]}, {"left": 388, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [127, 9, 94]}, {"left": 385, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [53, 3, 92, 22, 115]}, {"left": 380, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [77]}, {"left": 379, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [59, 101, 54, 86]}, {"left": 375, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [65, 4]}, {"left": 373, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [102, 105, 6]}, {"left": 370, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [22]}, {"left": 369, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [127, 68, 96]}, {"left": 366, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [92]}, {"left": 365, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [55]}, {"left": 364, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [90]}, {"left": 363, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [133]}, {"left": 362, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [44, 38]}, {"left": 360, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [41, 31]}, {"left": 358, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [8]}, {"left": 357, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [46, 17]}, {"left": 355, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [70]}, {"left": 354, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [8]}, {"left": 353, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [39]}, {"left": 352, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [10, 59, 54, 15]}, {"left": 348, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [76, 55]}, {"left": 346, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [44]}, {"left": 345, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [76]}, {"left": 344, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [53, 110]}, {"left": 342, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [126, 37, 18, 103]}, {"left": 338, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [34, 128]}, {"left": 336, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [57]}, {"left": 335, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [96, 77]}, {"left": 333, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [65, 111, 99]}, {"left": 330, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [27, 51]}, {"left": 328, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [125, 52]}, {"left": 326, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [64, 13, 56, 3]}, {"left": 322, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [93, 86]}, {"left": 320, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [106]}, {"left": 319, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [73]}, {"left": 318, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [5, 22]}, {"left": 316, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [4, 47, 137]}, {"left": 313, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [93, 18, 27]}, {"left": 310, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [15, 45, 47, 65, 104, 21]}, {"left": 304, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [57]}, {"left": 303, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [15, 116, 58, 8]}, {"left": 299, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [76, 21, 104, 108, 35, 77]}, {"left": 293, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [65]}, {"left": 292, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [111, 99, 27]}, {"left": 289, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [51, 125]}, {"left": 287, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [52]}, {"left": 286, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [64]}, {"left": 285, "kind": "l", "drafts": [13, 56, 3, 93], "rows": 5, "kept": 0, "emitted": [82]}, {"left": 284, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [90, 101]}, {"left": 282, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [124]}, {"left": 281, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [110, 126]}, {"left": 279, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [37]}, {"left": 278, "kind": "l", "drafts": [18, 103], "rows": 3, "kept": 0, "emitted": [41]}, {"left": 277, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [6, 42]}, {"left": 275, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [86]}, {"left": 274, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [65]}, {"left": 273, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [4, 102, 105]}, {"left": 270, "kind": "l", "drafts": [6, 22], "rows": 3, "kept": 2, "emitted": [6, 22, 127]}, {"left": 267, "kind": "l", "drafts": [68, 96], "rows": 3, "kept": 0, "emitted": [9]}, {"left": 266, "kind": "l", "drafts": [94, 53, 3], "rows": 4, "kept": 0, "emitted": [69]}, {"left": 265, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [83]}, {"left": 264, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [47]}, {"left": 263, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [42, 83, 47, 42, 87]}, {"left": 258, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [79]}, {"left": 257, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [61]}, {"left": 256, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [55]}, {"left": 255, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [82, 79]}, {"left": 253, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [87]}, {"left": 252, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [137, 27, 116, 11]}, {"left": 248, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [71, 5]}, {"left": 246, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [90]}, {"left": 245, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [7, 31]}, {"left": 243, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [55, 136, 59]}, {"left": 240, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [19, 20]}, {"left": 238, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [125, 103]}, {"left": 236, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [34, 128, 57, 83, 47, 42]}, {"left": 230, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [48, 86]}, {"left": 228, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [57, 70, 7, 100]}, {"left": 224, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [105, 16, 125]}, {"left": 221, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [34, 128]}, {"left": 219, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [61]}, {"left": 218, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [80, 130]}, {"left": 216, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [41, 6]}, {"left": 214, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [42]}, {"left": 213, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [86, 65, 4, 102, 105, 6]}, {"left": 207, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [22, 120, 31]}, {"left": 204, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [55]}, {"left": 203, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [136]}, {"left": 202, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [59, 19, 20]}, {"left": 199, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [125]}, {"left": 198, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [103]}, {"left": 197, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [6]}, {"left": 196, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [22]}, {"left": 195, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [127]}, {"left": 194, "kind": "l", "drafts": [9, 69, 83], "rows": 4, "kept": 3, "emitted": [9, 69, 83, 47]}, {"left": 190, "kind": "l", "drafts": [42, 83, 47, 42, 87], "rows": 6, "kept": 0, "emitted": [64]}, {"left": 189, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [34, 7, 34]}, {"left": 186, "kind": "l", "drafts": [13, 78, 34], "rows": 4, "kept": 3, "emitted": [13, 78, 34, 126]}, {"left": 182, "kind": "l", "drafts": [110, 126], "rows": 3, "kept": 2, "emitted": [110, 126, 37]}, {"left": 179, "kind": "l", "drafts": [18, 90, 7, 47], "rows": 5, "kept": 1, "emitted": [18, 137]}, {"left": 177, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [85]}, {"left": 176, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [83]}, {"left": 175, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [71, 98]}, {"left": 173, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [48, 29, 105, 94]}, {"left": 169, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [18, 36, 27, 116]}, {"left": 165, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [11]}, {"left": 164, "kind": "l", "drafts": [71, 5, 90, 7], "rows": 5, "kept": 4, "emitted": [71, 5, 90, 7, 47]}, {"left": 159, "kind": "l", "drafts": [137, 103, 41, 31, 89, 109], "rows": 7, "kept": 1, "emitted": [137, 16]}, {"left": 157, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [108]}, {"left": 156, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [137]}, {"left": 155, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [31, 85, 48]}, {"left": 152, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [75, 92, 137, 39, 3]}, {"left": 147, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [84, 124]}, {"left": 145, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [111, 99, 27]}, {"left": 142, "kind": "l", "drafts": [51, 125, 52, 64, 82], "rows": 6, "kept": 4, "emitted": [51, 125, 52, 64, 101]}, {"left": 137, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [54, 65, 50, 65]}, {"left": 133, "kind": "l", "drafts": [17, 74, 121, 128, 22], "rows": 6, "kept": 4, "emitted": [17, 74, 121, 128, 94]}, {"left": 128, "kind": "l", "drafts": [133, 111, 25, 11, 88], "rows": 6, "kept": 5, "emitted": [133, 111, 25, 11, 88, 48]}, {"left": 122, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [96, 90]}, {"left": 120, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [101, 54, 69, 24]}, {"left": 116, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [36, 72]}, {"left": 114, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [26, 94, 3]}, {"left": 111, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [113]}, {"left": 110, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [111]}, {"left": 109, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [13, 92, 119, 15, 73]}, {"left": 104, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [77]}, {"left": 103, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [5, 90]}, {"left": 101, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [7, 47]}, {"left": 99, "kind": "l", "drafts": [137, 16, 108, 137, 31, 85, 48], "rows": 8, "kept": 1, "emitted": [137, 93]}, {"left": 97, "kind": "l", "drafts": [18, 27, 15, 45, 47, 65], "rows": 7, "kept": 1, "emitted": [18, 65]}, {"left": 95, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [4, 102]}, {"left": 93, "kind": "l", "drafts": [105, 6, 22, 120, 31], "rows": 6, "kept": 3, "emitted": [105, 6, 22, 127]}, {"left": 89, "kind": "l", "drafts": [9, 69, 83, 47, 42], "rows": 6, "kept": 5, "emitted": [9, 69, 83, 47, 42, 55]}, {"left": 83, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [91, 64]}, {"left": 81, "kind": "l", "drafts": [34, 72, 19, 91, 122, 34, 92], "rows": 8, "kept": 1, "emitted": [34, 7]}, {"left": 79, "kind": "l", "drafts": [34, 13, 78, 34, 126, 110], "rows": 7, "kept": 6, "emitted": [34, 13, 78, 34, 126, 110, 126]}, {"left": 72, "kind": "l", "drafts": [37, 18, 90], "rows": 4, "kept": 1, "emitted": [37, 2]}, {"left": 70, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [99]}, {"left": 69, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [27]}, {"left": 68, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [51, 125]}, {"left": 66, "kind": "l", "drafts": [52, 64, 101, 54, 65, 50, 65], "rows": 8, "kept": 4, "emitted": [52, 64, 101, 54, 6]}, {"left": 61, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [22, 127, 42, 91, 64]}, {"left": 56, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [34]}, {"left": 55, "kind": "l", "drafts": [7, 34, 13, 78, 34, 126, 110], "rows": 8, "kept": 7, "emitted": [7, 34, 13, 78, 34, 126, 110, 126]}, {"left": 47, "kind": "l", "drafts": [37], "rows": 2, "kept": 1, "emitted": [37, 18]}, {"left": 45, "kind": "l", "drafts": [90, 7, 47], "rows": 4, "kept": 0, "emitted": [101]}, {"left": 44, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [54, 2, 122]}, {"left": 41, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [134, 128, 67]}, {"left": 38, "kind": "l", "drafts": [109, 123, 29, 4, 58, 40, 66], "rows": 8, "kept": 1, "emitted": [109, 2]}, {"left": 36, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [137]}, {"left": 35, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [93, 18, 2, 31, 55]}, {"left": 30, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [136, 59]}, {"left": 28, "kind": "l", "drafts": [19, 20, 125, 103, 6, 22, 127], "rows": 8, "kept": 4, "emitted": [19, 20, 125, 103, 34]}, {"left": 23, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [128, 57]}, {"left": 21, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [15]}, {"left": 20, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [116, 58, 8]}, {"left": 17, "kind": "l", "drafts": [76, 21, 104, 108, 35, 77, 65], "rows": 8, "kept": 2, "emitted": [76, 21, 126]}, {"left": 14, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [37, 18]}, {"left": 12, "kind": "l", "drafts": [101, 54], "rows": 3, "kept": 0, "emitted": [20]}, {"left": 11, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [48, 30, 49]}, {"left": 8, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [62, 11, 88, 48, 22]}, {"left": 3, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [63]}, {"left": 2, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [129]}, {"left": 1, "kind": "s", "drafts": [], "rows": 1, "kept": 0, "emitted": [21]}]}, {"costs": {"verify": [21.291, 24.948500000000003, 28.631999999999998, 32.341499999999996, 36.077, 39.8385, 43.626000000000005, 47.439499999999995, 51.279, 55.144499999999994, 59.036, 62.9535, 66.89699999999999, 70.8665, 74.862, 78.8835, 82.931, 87.00450000000001, 91.104, 95.22949999999999, 99.381, 103.5585, 107.762, 111.99149999999999, 116.24699999999999, 120.5285, 124.83599999999998, 129.1695, 133.529, 137.9145, 142.326, 146.7635, 151.227, 155.7165, 160.232, 164.7735, 169.341, 173.93449999999999, 178.55399999999997, 183.1995, 187.871, 192.5685, 197.29199999999997, 202.04149999999998, 206.817, 211.61849999999998, 216.446, 221.29949999999997, 226.17899999999997, 231.0845, 236.016, 240.97349999999997, 245.95699999999997, 250.9665, 256.002, 261.0635, 266.15099999999995, 271.2645, 276.404, 281.5695, 286.76099999999997, 291.9785, 297.222, 302.4915], "draft": 3.677}, "min_match": 6, "max_rows": 12, "eos": [2], "prompt": [11, 141, 114, 45, 80, 127, 137, 105, 74, 114, 88, 55, 51, 14, 42, 123, 94, 81, 141, 64, 12, 26, 61, 100, 32, 20, 68, 111, 75, 116, 144, 68, 90, 55, 109, 36, 30, 3, 49, 137, 138, 4, 103, 105, 5, 121, 40, 56, 71, 144, 118, 60, 59, 5, 93, 81, 14, 115, 94, 12, 114, 19, 123, 55, 64, 22, 5, 88, 55, 112, 44, 2, 5, 88, 55, 74, 114, 81, 125, 5, 53, 121, 86, 20, 71, 19, 64, 102, 144, 56, 56, 31, 86, 115, 128, 7, 74, 35, 9, 82, 77, 140, 127, 9, 41, 21, 129, 77, 62, 92, 83, 11, 80, 52, 106, 80, 101, 72, 67, 5, 88, 55, 51, 140, 127, 9, 41, 21, 129, 112, 76, 115, 95, 22, 80, 120, 71, 19, 64, 65, 19, 61, 85, 124, 101, 90, 35, 111, 135, 108, 144, 23, 110, 81, 125, 143, 9, 82, 77, 140, 127, 21, 129, 72, 67, 5, 88, 55, 51, 14, 64, 37, 144, 50, 97, 108, 86, 20, 71, 19, 64, 102, 144, 56, 56, 31, 142, 62, 77, 70, 26, 61, 60, 106, 61, 13, 125, 39, 121, 38, 14, 38, 59, 121, 137, 138, 4, 103, 105, 5, 23], "rounds": [{"left": 299, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [24, 25]}, {"left": 297, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [37, 144, 50, 97]}, {"left": 293, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [108, 86, 20, 71]}, {"left": 289, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [19, 64, 102]}, {"left": 286, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [144, 56, 56]}, {"left": 283, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [31, 142]}, {"left": 281, "kind": "l", "drafts": [62, 77, 70, 26, 61], "rows": 6, "kept": 2, "emitted": [62, 77, 81]}, {"left": 278, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [125, 5, 31]}, {"left": 275, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [108]}, {"left": 274, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [55]}, {"left": 273, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [51]}, {"left": 272, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [14]}, {"left": 271, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [129, 68, 111]}, {"left": 268, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [75, 116]}, {"left": 266, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [144, 68]}, {"left": 264, "kind": "l", "drafts": [90, 55], "rows": 3, "kept": 2, "emitted": [90, 55, 109]}, {"left": 261, "kind": "l", "drafts": [36, 30, 3, 49], "rows": 5, "kept": 4, "emitted": [36, 30, 3, 49, 137]}, {"left": 256, "kind": "l", "drafts": [138, 4, 103, 105, 5, 121, 40, 56, 71, 144, 118], "rows": 12, "kept": 5, "emitted": [138, 4, 103, 105, 5, 23]}, {"left": 250, "kind": "l", "drafts": [24, 25, 37, 144], "rows": 5, "kept": 0, "emitted": [59]}, {"left": 249, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [60, 66]}, {"left": 247, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [11]}, {"left": 246, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [123]}, {"left": 245, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [71, 114]}, {"left": 243, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [18, 96, 143]}, {"left": 240, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [19]}, {"left": 239, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [50]}, {"left": 238, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [83, 48, 11]}, {"left": 235, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [6, 67, 128]}, {"left": 232, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [125]}, {"left": 231, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [47]}, {"left": 230, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [32, 45, 50]}, {"left": 227, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [110, 44]}, {"left": 225, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [44]}, {"left": 224, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [76]}, {"left": 223, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [38]}, {"left": 222, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [86]}, {"left": 221, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [142]}, {"left": 220, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [56]}, {"left": 219, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [132, 37]}, {"left": 217, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [117, 144, 37, 129, 50]}, {"left": 212, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [37, 122]}, {"left": 210, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [12]}, {"left": 209, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [44, 11, 24]}, {"left": 206, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [61, 110, 94, 137]}, {"left": 202, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [138]}, {"left": 201, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [4]}, {"left": 200, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [103]}, {"left": 199, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [105, 5]}, {"left": 197, "kind": "l", "drafts": [23, 59], "rows": 3, "kept": 2, "emitted": [23, 59, 60]}, {"left": 194, "kind": "l", "drafts": [66, 11, 123, 71, 114, 18, 96, 143, 19], "rows": 10, "kept": 3, "emitted": [66, 11, 123, 55]}, {"left": 190, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [100]}, {"left": 189, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [124]}, {"left": 188, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [101]}, {"left": 187, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [90]}, {"left": 186, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [35, 111, 135]}, {"left": 183, "kind": "l", "drafts": [108, 144, 23, 110], "rows": 5, "kept": 0, "emitted": [93]}, {"left": 182, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [80, 71]}, {"left": 180, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [35, 114, 39, 106]}, {"left": 176, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [29, 133, 48, 64]}, {"left": 172, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [125, 5, 31, 108, 101]}, {"left": 167, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [90, 35, 111, 135, 11, 141]}, {"left": 161, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [114, 82, 64]}, {"left": 158, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [56, 102]}, {"left": 156, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [56, 11]}, {"left": 154, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [10, 80, 54, 32]}, {"left": 150, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [3]}, {"left": 149, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [127, 137]}, {"left": 147, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [105]}, {"left": 146, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [74]}, {"left": 145, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [114, 81]}, {"left": 143, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [125, 5]}, {"left": 141, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [31, 108, 103, 85]}, {"left": 137, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [110, 17, 12]}, {"left": 134, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [74]}, {"left": 133, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [71, 31]}, {"left": 131, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [108, 131]}, {"left": 129, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [87]}, {"left": 128, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [62, 62]}, {"left": 126, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [80]}, {"left": 125, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [14]}, {"left": 124, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [61]}, {"left": 123, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [100, 32, 20, 68]}, {"left": 119, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [111, 75, 116, 144]}, {"left": 115, "kind": "l", "drafts": [68, 90, 55, 109, 36, 30, 3], "rows": 8, "kept": 7, "emitted": [68, 90, 55, 109, 36, 30, 3, 49]}, {"left": 107, "kind": "l", "drafts": [137, 138, 4, 103], "rows": 5, "kept": 4, "emitted": [137, 138, 4, 103, 105]}, {"left": 102, "kind": "l", "drafts": [5, 121, 40, 56, 71, 144, 118, 60, 59], "rows": 10, "kept": 1, "emitted": [5, 23]}, {"left": 100, "kind": "l", "drafts": [59, 60, 66, 11, 123], "rows": 6, "kept": 5, "emitted": [59, 60, 66, 11, 123, 10]}, {"left": 94, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [54, 71]}, {"left": 92, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [75, 116, 144, 68]}, {"left": 88, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [90, 55, 109]}, {"left": 85, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [36]}, {"left": 84, "kind": "l", "drafts": [30, 3, 49, 137, 138, 4, 103, 105, 5, 23], "rows": 11, "kept": 4, "emitted": [30, 3, 49, 137, 131]}, {"left": 79, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [87]}, {"left": 78, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [62]}, {"left": 77, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [62]}, {"left": 76, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [80, 14, 61, 100]}, {"left": 72, "kind": "l", "drafts": [32, 20, 68, 111, 75, 116, 144, 68, 90], "rows": 10, "kept": 9, "emitted": [32, 20, 68, 111, 75, 116, 144, 68, 90, 55]}, {"left": 62, "kind": "l", "drafts": [109, 36, 30, 3, 49, 137, 138, 4, 103], "rows": 10, "kept": 9, "emitted": [109, 36, 30, 3, 49, 137, 138, 4, 103, 105]}, {"left": 52, "kind": "l", "drafts": [5, 23, 59, 60, 66, 11, 123, 10, 54, 71, 75], "rows": 12, "kept": 1, "emitted": [5, 46]}, {"left": 50, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [15, 95, 82, 77]}, {"left": 46, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [140, 72, 124]}, {"left": 43, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [101, 90]}, {"left": 41, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [35]}, {"left": 40, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [111]}, {"left": 39, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [135]}, {"left": 38, "kind": "l", "drafts": [93, 80], "rows": 3, "kept": 0, "emitted": [11]}, {"left": 37, "kind": "l", "drafts": [141, 114], "rows": 3, "kept": 2, "emitted": [141, 114, 74]}, {"left": 34, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [35, 9]}, {"left": 32, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [82]}, {"left": 31, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [77, 140]}, {"left": 29, "kind": "l", "drafts": [127, 9, 41], "rows": 4, "kept": 3, "emitted": [127, 9, 41, 21]}, {"left": 25, "kind": "l", "drafts": [129, 77, 62, 92, 83, 11, 80, 52, 106, 80, 101], "rows": 12, "kept": 1, "emitted": [129, 20]}, {"left": 23, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [71]}, {"left": 22, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [125, 136]}, {"left": 20, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [67, 34, 80, 14]}, {"left": 16, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [61]}, {"left": 15, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [100]}, {"left": 14, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [32]}, {"left": 13, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [20, 68]}, {"left": 11, "kind": "l", "drafts": [111, 75, 116, 144], "rows": 5, "kept": 1, "emitted": [111, 136]}, {"left": 9, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [67]}, {"left": 8, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [34, 9, 127, 24, 25]}, {"left": 3, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [37]}, {"left": 2, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [144]}, {"left": 1, "kind": "s", "drafts": [], "rows": 1, "kept": 0, "emitted": [50]}]}, {"costs": {"verify": [42.0, 47.0, 52.0, 57.0, 62.0, 67.0, 72.0, 77.0, 82.0, 87.0, 92.0, 97.0, 102.0, 107.0, 112.0, 117.0], "draft": 5.6}, "min_match": 4, "max_rows": 16, "eos": [2], "prompt": [15, 51, 61, 7, 114, 135, 50, 18, 54, 102, 142, 133, 106, 143, 6, 21, 125, 96, 125, 54, 8, 42, 144, 9, 116, 119, 49, 55, 62, 69, 151, 90, 91, 98, 17, 23, 28, 88, 98, 115, 146, 46, 19, 79, 102, 96, 135, 116, 114, 59, 44, 46, 19, 92, 129, 11, 122, 137, 125, 133, 104, 46, 150, 2, 44, 46, 14, 128, 95, 115, 86, 118, 18, 119, 44, 117, 3, 61, 16, 21, 95, 14, 128, 95, 115, 104, 91, 64, 6, 3, 5, 72, 131, 42, 7, 15, 82, 83, 148, 114, 59, 44, 46, 132, 57, 83, 61, 72, 14, 128, 122, 48, 37, 35, 96, 58, 95, 81, 69, 64, 41, 63, 92, 77, 64, 72, 14, 128, 122, 48, 37, 135, 134, 151, 2, 105, 26, 89, 28, 126, 56, 56, 135, 139, 31, 23, 82, 116, 65, 109, 80, 128, 114, 48, 13, 66, 124, 132, 57, 83, 61, 72, 14, 128, 122, 48, 37, 135, 134, 151, 50, 18, 54, 126, 94, 77, 130, 35, 81, 120, 73, 85, 141, 134, 91, 53, 43, 25, 115, 65, 49, 53, 82, 59, 44, 46, 20, 147, 36, 108, 6, 24, 122, 104, 124, 88, 14, 109, 80, 128, 114, 48, 13, 66, 124, 132, 57, 83, 61, 72, 14, 128, 122, 48, 37, 135, 134, 151, 105, 70, 118, 120, 6, 19, 151, 55, 67, 54, 126, 94, 77, 130, 35, 81, 120, 73, 85, 141, 134, 91, 53, 43, 25, 65, 17, 106, 27, 100, 49, 55, 62, 69, 151, 90, 91, 98, 134, 38, 81, 88, 93, 99, 26, 51, 141, 115, 65, 49, 53, 82, 18, 79, 82, 145, 68, 108, 31, 83, 17, 119, 25, 122, 79, 74, 19, 57, 54, 41, 74, 46, 88, 92, 77], "rounds": [{"left": 413, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [104, 46, 150]}, {"left": 410, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [130, 49, 29]}, {"left": 407, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [71, 86]}, {"left": 405, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [89]}, {"left": 404, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [95]}, {"left": 403, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [96, 88, 92, 77, 64]}, {"left": 398, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [17]}, {"left": 397, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [112]}, {"left": 396, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [75, 5]}, {"left": 394, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [58, 70, 144, 61]}, {"left": 390, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [16, 21]}, {"left": 388, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [95, 14, 128, 95]}, {"left": 384, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [115]}, {"left": 383, "kind": "l", "drafts": [104, 91, 64, 6], "rows": 5, "kept": 4, "emitted": [104, 91, 64, 6, 3]}, {"left": 378, "kind": "l", "drafts": [5, 72, 131, 42, 7, 15, 82, 83, 148, 114, 59, 44, 46, 132], "rows": 15, "kept": 4, "emitted": [5, 72, 131, 42, 122]}, {"left": 373, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [48]}, {"left": 372, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [37, 135, 134, 151]}, {"left": 368, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [91, 124, 26, 77, 151]}, {"left": 363, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [34]}, {"left": 362, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [147]}, {"left": 361, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [42]}, {"left": 360, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [64, 6, 3, 5, 72, 131]}, {"left": 354, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [42, 7, 11]}, {"left": 351, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [122]}, {"left": 350, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [137]}, {"left": 349, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [125]}, {"left": 348, "kind": "l", "drafts": [133, 104], "rows": 3, "kept": 2, "emitted": [133, 104, 46]}, {"left": 345, "kind": "l", "drafts": [150], "rows": 2, "kept": 1, "emitted": [150, 130]}, {"left": 343, "kind": "l", "drafts": [49, 29, 71, 86, 89, 95], "rows": 7, "kept": 6, "emitted": [49, 29, 71, 86, 89, 95, 96]}, {"left": 336, "kind": "l", "drafts": [88, 92, 77, 64, 17, 112, 75, 5, 58, 70, 144], "rows": 12, "kept": 4, "emitted": [88, 92, 77, 64, 105]}, {"left": 331, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [122]}, {"left": 330, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [101, 143, 27]}, {"left": 327, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [100]}, {"left": 326, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [104, 56, 135, 139, 31]}, {"left": 321, "kind": "l", "drafts": [23, 82, 116, 65, 109, 80, 128, 114, 48, 13, 66, 124, 132, 57, 83], "rows": 16, "kept": 0, "emitted": [81]}, {"left": 320, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [120, 73, 85]}, {"left": 317, "kind": "l", "drafts": [141, 134, 91, 53, 43, 25, 65], "rows": 8, "kept": 7, "emitted": [141, 134, 91, 53, 43, 25, 65, 17]}, {"left": 309, "kind": "l", "drafts": [106, 27, 100, 49, 55, 62, 69, 151, 90], "rows": 10, "kept": 3, "emitted": [106, 27, 100, 104]}, {"left": 305, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [56, 135, 139, 31]}, {"left": 301, "kind": "l", "drafts": [81, 120, 73, 85, 141, 134, 91, 53, 43, 25, 65, 17], "rows": 13, "kept": 0, "emitted": [14]}, {"left": 300, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [128, 95, 115]}, {"left": 297, "kind": "l", "drafts": [104, 91, 64, 6, 3, 5, 72], "rows": 8, "kept": 1, "emitted": [104, 99]}, {"left": 295, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [26, 51, 141]}, {"left": 292, "kind": "l", "drafts": [115, 65, 49, 53, 82], "rows": 6, "kept": 0, "emitted": [96]}, {"left": 291, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [107, 76, 151]}, {"left": 288, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [77, 10, 140]}, {"left": 285, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [84]}, {"left": 284, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [8]}, {"left": 283, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [117, 141, 41]}, {"left": 280, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [92, 24, 95, 93]}, {"left": 276, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [99, 26]}, {"left": 274, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [51, 141, 115]}, {"left": 271, "kind": "l", "drafts": [65, 49, 53, 82], "rows": 5, "kept": 4, "emitted": [65, 49, 53, 82, 150]}, {"left": 266, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [12, 106, 82]}, {"left": 263, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [85]}, {"left": 262, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [44, 78]}, {"left": 260, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [111, 49]}, {"left": 258, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [124, 53]}, {"left": 256, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [82, 150, 79, 9, 97]}, {"left": 251, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [13, 23, 28, 111]}, {"left": 247, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [114, 135, 50, 18, 54]}, {"left": 242, "kind": "l", "drafts": [102, 142, 133, 106, 143], "rows": 6, "kept": 0, "emitted": [126]}, {"left": 241, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [94]}, {"left": 240, "kind": "l", "drafts": [77, 130, 35, 81], "rows": 5, "kept": 4, "emitted": [77, 130, 35, 81, 120]}, {"left": 235, "kind": "l", "drafts": [73, 85, 141, 134, 91, 53], "rows": 7, "kept": 6, "emitted": [73, 85, 141, 134, 91, 53, 43]}, {"left": 228, "kind": "l", "drafts": [25, 115, 65, 49, 53, 82], "rows": 7, "kept": 1, "emitted": [25, 65]}, {"left": 226, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [17, 106]}, {"left": 224, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [27, 100]}, {"left": 222, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [104, 56, 135]}, {"left": 219, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [112, 75]}, {"left": 217, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [12]}, {"left": 216, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [15, 113]}, {"left": 214, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [83]}, {"left": 213, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [110, 117, 9, 18, 36]}, {"left": 208, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [44, 50]}, {"left": 206, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [18]}, {"left": 205, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [54, 126]}, {"left": 203, "kind": "l", "drafts": [94, 77, 130, 35, 81, 120], "rows": 7, "kept": 6, "emitted": [94, 77, 130, 35, 81, 120, 73]}, {"left": 196, "kind": "l", "drafts": [85, 141, 134, 91, 53, 43, 25, 65, 17, 106, 27], "rows": 12, "kept": 11, "emitted": [85, 141, 134, 91, 53, 43, 25, 65, 17, 106, 27, 100]}, {"left": 184, "kind": "l", "drafts": [104, 56, 135, 112], "rows": 5, "kept": 3, "emitted": [104, 56, 135, 80]}, {"left": 180, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [128]}, {"left": 179, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [114]}, {"left": 178, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [48, 13]}, {"left": 176, "kind": "l", "drafts": [66, 124, 132, 57, 83, 61, 72, 14, 128], "rows": 10, "kept": 9, "emitted": [66, 124, 132, 57, 83, 61, 72, 14, 128, 122]}, {"left": 166, "kind": "l", "drafts": [48, 37, 135, 134, 151, 105, 70, 118, 120, 6, 19, 151, 55, 67, 54], "rows": 16, "kept": 5, "emitted": [48, 37, 135, 134, 151, 15]}, {"left": 160, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [97]}, {"left": 159, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [54, 59, 125, 6, 24]}, {"left": 154, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [122]}, {"left": 153, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [104]}, {"left": 152, "kind": "l", "drafts": [124, 88, 14, 109, 80, 128, 114, 48, 13, 66, 124, 132, 57, 83, 61], "rows": 16, "kept": 15, "emitted": [124, 88, 14, 109, 80, 128, 114, 48, 13, 66, 124, 132, 57, 83, 61, 72]}, {"left": 136, "kind": "l", "drafts": [14, 128, 122, 48, 37], "rows": 6, "kept": 5, "emitted": [14, 128, 122, 48, 37, 135]}, {"left": 130, "kind": "l", "drafts": [134, 151, 105, 70, 118, 120, 6, 19, 151, 55], "rows": 11, "kept": 2, "emitted": [134, 151, 146]}, {"left": 127, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [7]}, {"left": 126, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [26, 15]}, {"left": 124, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [137]}, {"left": 123, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [94]}, {"left": 122, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [5, 72, 131, 42, 7, 117]}, {"left": 116, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [148, 81]}, {"left": 114, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [73, 86]}, {"left": 112, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [88, 145]}, {"left": 110, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [120, 95]}, {"left": 108, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [96, 88, 92]}, {"left": 105, "kind": "l", "drafts": [77, 64, 105, 122, 101, 143, 27, 100, 104, 56, 135, 139, 31, 81, 120], "rows": 16, "kept": 2, "emitted": [77, 64, 32]}, {"left": 102, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [93, 12, 28, 137, 144]}, {"left": 97, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [131, 41]}, {"left": 95, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [113, 26]}, {"left": 93, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [51, 141, 115]}, {"left": 90, "kind": "l", "drafts": [65, 49, 53, 82, 150, 12, 106, 82, 85, 44, 78, 111, 49, 124], "rows": 15, "kept": 5, "emitted": [65, 49, 53, 82, 150, 79]}, {"left": 84, "kind": "l", "drafts": [9, 97, 13, 23, 28, 111, 114, 135, 50, 18, 54, 126], "rows": 13, "kept": 0, "emitted": [119]}, {"left": 83, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [14]}, {"left": 82, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [79, 50, 5]}, {"left": 79, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [32, 122, 52]}, {"left": 76, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [49, 47, 18]}, {"left": 73, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [101]}, {"left": 72, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [142]}, {"left": 71, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [85]}, {"left": 70, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [65]}, {"left": 69, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [87, 101, 89, 147]}, {"left": 65, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [10]}, {"left": 64, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [144, 56]}, {"left": 62, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [56, 116]}, {"left": 60, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [57, 115]}, {"left": 58, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [104]}, {"left": 57, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [91]}, {"left": 56, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [38]}, {"left": 55, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [112, 150, 4]}, {"left": 52, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [50, 104, 137]}, {"left": 49, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [107]}, {"left": 48, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [17, 147, 135]}, {"left": 45, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [88, 14]}, {"left": 43, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [109]}, {"left": 42, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [80, 128, 114]}, {"left": 39, "kind": "l", "drafts": [48, 13, 66, 124, 132, 57, 83, 61, 72, 14, 128], "rows": 12, "kept": 5, "emitted": [48, 13, 66, 124, 132, 96]}, {"left": 33, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [135]}, {"left": 32, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [116]}, {"left": 31, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [114]}, {"left": 30, "kind": "l", "drafts": [59, 44, 46, 19, 92, 129, 11, 122, 137, 125, 133], "rows": 12, "kept": 0, "emitted": [12]}, {"left": 29, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [78, 59, 42, 18, 7, 31]}, {"left": 23, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [73, 4, 63]}, {"left": 20, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [134, 132]}, {"left": 18, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [48, 47]}, {"left": 16, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [46, 150, 130, 49]}, {"left": 12, "kind": "l", "drafts": [29, 71, 86, 89, 95, 96, 88], "rows": 8, "kept": 7, "emitted": [29, 71, 86, 89, 95, 96, 88, 92]}, {"left": 4, "kind": "l", "drafts": [77, 64, 105], "rows": 4, "kept": 2, "emitted": [77, 64, 7]}, {"left": 1, "kind": "s", "drafts": [], "rows": 1, "kept": 0, "emitted": [91]}]}, {"costs": {"verify": [21.291, 24.948500000000003, 28.631999999999998, 32.341499999999996, 36.077, 39.8385, 43.626000000000005, 47.439499999999995, 51.279, 55.144499999999994, 59.036, 62.9535, 66.89699999999999, 70.8665, 74.862, 78.8835, 82.931, 87.00450000000001, 91.104, 95.22949999999999, 99.381, 103.5585, 107.762, 111.99149999999999, 116.24699999999999, 120.5285, 124.83599999999998, 129.1695, 133.529, 137.9145, 142.326, 146.7635, 151.227, 155.7165, 160.232, 164.7735, 169.341, 173.93449999999999, 178.55399999999997, 183.1995, 187.871, 192.5685, 197.29199999999997, 202.04149999999998, 206.817, 211.61849999999998, 216.446, 221.29949999999997, 226.17899999999997, 231.0845, 236.016, 240.97349999999997, 245.95699999999997, 250.9665, 256.002, 261.0635, 266.15099999999995, 271.2645, 276.404, 281.5695, 286.76099999999997, 291.9785, 297.222, 302.4915], "draft": 3.677}, "min_match": 4, "max_rows": 16, "eos": [2], "prompt": [12, 128, 11, 125, 33, 75, 48, 34, 116, 99, 105, 130, 12, 22, 127, 21, 122, 67, 118, 37, 131, 33, 42, 115, 54, 74, 39, 148, 121, 75, 90, 136, 113, 29, 114, 23, 25, 131, 77, 127, 102, 9, 146, 64, 130, 12, 36, 14, 151, 54, 58, 143, 55, 105, 107, 37, 98, 45, 119, 28, 30, 18, 40, 61, 19, 127, 82, 9, 146, 14, 151, 54, 77, 149, 58, 143, 55, 151, 51, 27, 93, 82, 29, 77, 72, 5, 75, 125, 137, 113, 125, 151, 33, 42, 58, 154, 11, 105, 101, 103, 77, 30, 19, 127, 82, 9, 144, 108, 63, 19, 127, 82, 9, 146, 14, 127, 82, 9, 146, 14, 151, 54, 49, 86, 116, 99, 105, 130, 12, 132, 58, 110, 100, 44, 119, 32, 98, 45, 119, 28, 30, 18, 40, 61, 70, 144, 115, 54, 74, 39, 148, 69, 94, 23, 38, 115, 57, 105, 101, 103, 77, 30, 19, 127, 82, 9, 146, 14, 86, 133, 149, 124, 22, 127, 21, 122, 67, 118, 37, 131, 33, 130, 11, 132, 60, 64, 39, 68, 100, 39, 155, 19, 99, 151, 5, 57, 42, 75, 74, 113, 83, 110, 21, 141, 82, 120, 15, 113, 58, 7, 109, 76, 157, 59, 143, 88, 5, 110, 141, 81, 91, 33, 42, 58, 154, 11, 101, 103, 77, 30, 19, 127, 82, 9, 146, 14, 151, 14, 145, 102, 27, 87, 101, 34, 63, 151, 133, 86, 4, 26, 49, 116, 72, 87, 103, 107, 6, 137, 40, 139, 40, 105, 101, 33, 12, 77, 149, 58, 143, 55, 14, 19, 73, 118, 22, 99, 64, 116, 41, 150, 100, 116, 99, 105, 101, 103, 77, 30, 19, 127, 82, 72, 84, 153, 127, 82, 9, 146, 14, 151, 54, 51, 121, 53, 78, 43, 113, 155, 66, 84, 6, 145, 144, 66, 81, 72, 43, 12, 150, 60, 16, 117, 24, 144, 32, 35, 124, 75, 134, 129, 43, 135, 124, 148, 11, 13, 42, 92, 84, 76, 157, 59, 143], "rounds": [{"left": 333, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [20]}, {"left": 332, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [117, 103]}, {"left": 330, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [67, 37]}, {"left": 328, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [98]}, {"left": 327, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [45, 119]}, {"left": 325, "kind": "l", "drafts": [28, 30], "rows": 3, "kept": 2, "emitted": [28, 30, 18]}, {"left": 322, "kind": "l", "drafts": [40, 61, 19, 127, 82], "rows": 6, "kept": 2, "emitted": [40, 61, 70]}, {"left": 319, "kind": "l", "drafts": [144, 115, 54, 74], "rows": 5, "kept": 1, "emitted": [144, 133]}, {"left": 317, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [50, 131, 57, 55]}, {"left": 313, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [132]}, {"left": 312, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [42]}, {"left": 311, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [34, 107, 157]}, {"left": 308, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [125, 95, 85, 145]}, {"left": 304, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [98, 137]}, {"left": 302, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [79]}, {"left": 301, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [144, 35]}, {"left": 299, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [120, 58]}, {"left": 297, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [124, 90]}, {"left": 295, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [69]}, {"left": 294, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [145, 146]}, {"left": 292, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [122, 22]}, {"left": 290, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [127]}, {"left": 289, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [21, 122, 67, 118, 37]}, {"left": 284, "kind": "l", "drafts": [131, 33, 130], "rows": 4, "kept": 2, "emitted": [131, 33, 7]}, {"left": 281, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [136]}, {"left": 280, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [40, 65, 83, 26]}, {"left": 276, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [19, 20]}, {"left": 274, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [64, 144]}, {"left": 272, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [4]}, {"left": 271, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [28]}, {"left": 270, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [75, 57, 22, 18, 121, 10]}, {"left": 264, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [150]}, {"left": 263, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [52]}, {"left": 262, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [135, 144, 85]}, {"left": 259, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [93, 141, 142, 44]}, {"left": 255, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [19, 95, 106]}, {"left": 252, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [78, 69, 37]}, {"left": 249, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [101, 103, 77]}, {"left": 246, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [30]}, {"left": 245, "kind": "l", "drafts": [19, 127, 82], "rows": 4, "kept": 3, "emitted": [19, 127, 82, 9]}, {"left": 241, "kind": "l", "drafts": [146, 14], "rows": 3, "kept": 1, "emitted": [146, 64]}, {"left": 239, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [80, 39]}, {"left": 237, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [27, 75, 74]}, {"left": 234, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [151, 22, 85, 138]}, {"left": 230, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [71, 106]}, {"left": 228, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [104, 15]}, {"left": 226, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [29]}, {"left": 225, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [32, 35, 124, 75, 134]}, {"left": 220, "kind": "l", "drafts": [129, 43, 135, 124], "rows": 5, "kept": 4, "emitted": [129, 43, 135, 124, 148]}, {"left": 215, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [11]}, {"left": 214, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [13, 42]}, {"left": 212, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [92, 84, 76]}, {"left": 209, "kind": "l", "drafts": [157, 59, 143, 20, 117], "rows": 6, "kept": 3, "emitted": [157, 59, 143, 88]}, {"left": 205, "kind": "l", "drafts": [5, 110, 141, 81, 91, 33], "rows": 7, "kept": 1, "emitted": [5, 75]}, {"left": 203, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [155]}, {"left": 202, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [127]}, {"left": 201, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [69, 145]}, {"left": 199, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [146, 122, 22]}, {"left": 196, "kind": "l", "drafts": [127, 21, 122, 67, 118], "rows": 6, "kept": 5, "emitted": [127, 21, 122, 67, 118, 37]}, {"left": 190, "kind": "l", "drafts": [131, 33], "rows": 3, "kept": 2, "emitted": [131, 33, 42]}, {"left": 187, "kind": "l", "drafts": [115, 54, 74, 39], "rows": 5, "kept": 4, "emitted": [115, 54, 74, 39, 148]}, {"left": 182, "kind": "l", "drafts": [121, 75, 90, 136, 113, 29, 114], "rows": 8, "kept": 0, "emitted": [78]}, {"left": 181, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [87]}, {"left": 180, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [135, 53, 64]}, {"left": 177, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [18, 16, 69, 145]}, {"left": 173, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [146, 122, 22, 127]}, {"left": 169, "kind": "l", "drafts": [21, 122, 67, 118, 37, 131, 33], "rows": 8, "kept": 7, "emitted": [21, 122, 67, 118, 37, 131, 33, 42]}, {"left": 161, "kind": "l", "drafts": [115, 54, 74, 39], "rows": 5, "kept": 4, "emitted": [115, 54, 74, 39, 148]}, {"left": 156, "kind": "l", "drafts": [78, 87, 135, 53, 64], "rows": 6, "kept": 0, "emitted": [12]}, {"left": 155, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [77, 149, 58]}, {"left": 152, "kind": "l", "drafts": [143, 55, 14, 19, 73, 118, 22, 99, 64], "rows": 10, "kept": 2, "emitted": [143, 55, 67]}, {"left": 149, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [118, 37]}, {"left": 147, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [131]}, {"left": 146, "kind": "l", "drafts": [33, 42, 115, 54, 74, 39, 148], "rows": 8, "kept": 7, "emitted": [33, 42, 115, 54, 74, 39, 148, 119]}, {"left": 138, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [50, 49, 96, 14]}, {"left": 134, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [145, 102]}, {"left": 132, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [27, 87, 78, 88, 34, 111]}, {"left": 126, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [104]}, {"left": 125, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [149]}, {"left": 124, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [31, 113]}, {"left": 122, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [22, 127]}, {"left": 120, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [21]}, {"left": 119, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [122, 67, 118, 37, 131, 33]}, {"left": 113, "kind": "l", "drafts": [42, 115, 54, 74, 39, 148], "rows": 7, "kept": 6, "emitted": [42, 115, 54, 74, 39, 148, 145]}, {"left": 106, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [98, 137]}, {"left": 104, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [79, 144, 35, 120, 58, 124]}, {"left": 98, "kind": "l", "drafts": [90, 69, 145, 146, 122, 22, 127, 21, 122], "rows": 10, "kept": 0, "emitted": [143]}, {"left": 97, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [18]}, {"left": 96, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [27, 130, 16]}, {"left": 93, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [19, 104]}, {"left": 91, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [22]}, {"left": 90, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [67]}, {"left": 89, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [37]}, {"left": 88, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [137]}, {"left": 87, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [113, 125]}, {"left": 85, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [151]}, {"left": 84, "kind": "l", "drafts": [33, 42, 58, 154, 11, 105, 101, 103, 77, 30, 19, 127, 82, 9], "rows": 15, "kept": 0, "emitted": [40]}, {"left": 83, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [153, 67]}, {"left": 81, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [38, 75, 31]}, {"left": 78, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [20, 103, 17]}, {"left": 75, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [144, 146, 124]}, {"left": 72, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [68, 49, 140]}, {"left": 69, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [31]}, {"left": 68, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [97, 5]}, {"left": 66, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [110]}, {"left": 65, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [141]}, {"left": 64, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [81, 91, 33, 42, 58]}, {"left": 59, "kind": "l", "drafts": [154, 11, 101, 103, 77, 30], "rows": 7, "kept": 2, "emitted": [154, 11, 27]}, {"left": 56, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [93, 82]}, {"left": 54, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [29, 77, 72, 5, 75, 125]}, {"left": 48, "kind": "l", "drafts": [137, 113, 125, 151, 33], "rows": 6, "kept": 4, "emitted": [137, 113, 125, 151, 90]}, {"left": 43, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [69]}, {"left": 42, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [145]}, {"left": 41, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [146, 122, 22, 127]}, {"left": 37, "kind": "l", "drafts": [21, 122, 67, 118, 37, 131, 33], "rows": 8, "kept": 5, "emitted": [21, 122, 67, 118, 37, 2]}, {"left": 31, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [12, 77]}, {"left": 29, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [149, 58, 143]}, {"left": 26, "kind": "l", "drafts": [55, 67, 118, 37, 131, 33], "rows": 7, "kept": 1, "emitted": [55, 2]}, {"left": 24, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [101]}, {"left": 23, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [103, 77]}, {"left": 21, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [30, 19, 127]}, {"left": 18, "kind": "l", "drafts": [82, 9, 146, 64, 80, 39], "rows": 7, "kept": 3, "emitted": [82, 9, 146, 14]}, {"left": 14, "kind": "l", "drafts": [151, 14, 145, 102, 27, 87], "rows": 7, "kept": 0, "emitted": [119]}, {"left": 13, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [28]}, {"left": 12, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [30, 18, 118]}, {"left": 9, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [37, 131, 33, 42, 115, 54]}, {"left": 3, "kind": "l", "drafts": [74, 39], "rows": 3, "kept": 2, "emitted": [74, 39, 148]}]}, {"costs": {"verify": [42.0, 47.0, 52.0, 57.0, 62.0, 67.0, 72.0, 77.0, 82.0, 87.0, 92.0, 97.0, 102.0, 107.0, 112.0, 117.0], "draft": 5.6}, "min_match": 3, "max_rows": 8, "eos": [2], "prompt": [41, 142, 118, 26, 65, 4, 100, 27, 2, 123, 98, 113, 107, 24, 143, 10, 138, 124, 9, 41, 142, 118, 26, 65, 20, 57, 129, 126, 41, 70, 32, 106, 92, 57, 69, 94, 159, 148, 141, 122, 93, 64, 18, 17, 125, 77, 53, 35, 147, 55, 34, 41, 49, 152, 112, 51, 4, 20, 136, 55, 45, 105, 44, 9, 36, 79, 64, 105, 44, 9, 36, 79, 64, 2, 113, 78, 60, 8, 133, 121, 120, 111, 81, 141, 113, 107, 24, 143, 10, 138, 124, 9, 41, 142, 118, 26, 65, 4, 100, 27, 29, 116, 11, 141, 46, 123, 4, 162, 144, 92, 101, 164, 74, 153, 58, 147, 56, 163, 126, 77, 125, 38, 7, 165, 118, 32, 17, 34, 41, 49, 152, 112, 51, 4, 20, 136, 55, 45, 105, 44, 9, 36, 79, 64, 25, 65, 36, 55, 149, 9, 36, 79, 156, 107, 90, 65, 50, 53, 109, 79, 161, 62, 101, 46, 59, 48, 31, 136, 70, 92, 42, 70, 64, 92, 12, 127, 54, 12, 147, 56, 163, 126, 77, 125, 42, 135, 70, 58, 65, 95, 7, 6, 89, 51, 33, 80, 24, 18], "rounds": [{"left": 198, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [12]}, {"left": 197, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [23]}, {"left": 196, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [87]}, {"left": 195, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [49, 71, 144, 20]}, {"left": 191, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [27, 22, 76]}, {"left": 188, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [4, 137, 100, 68, 131]}, {"left": 183, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [11, 116, 155, 73, 58]}, {"left": 178, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [65, 67, 113]}, {"left": 175, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [100, 84]}, {"left": 173, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [162, 80]}, {"left": 171, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [115, 17, 16, 67, 161]}, {"left": 166, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [114, 41, 123, 75]}, {"left": 162, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [124]}, {"left": 161, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [29, 56, 163, 126]}, {"left": 157, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [77, 125, 38]}, {"left": 154, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [7, 165, 118, 32, 17]}, {"left": 149, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [53, 11, 13]}, {"left": 146, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [30, 7]}, {"left": 144, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [163]}, {"left": 143, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [126]}, {"left": 142, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [77]}, {"left": 141, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [125, 20, 136]}, {"left": 138, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [55, 45, 105]}, {"left": 135, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [44, 9]}, {"left": 133, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [36, 79]}, {"left": 131, "kind": "l", "drafts": [64, 25, 65, 36, 55], "rows": 6, "kept": 1, "emitted": [64, 65]}, {"left": 129, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [67]}, {"left": 128, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [113]}, {"left": 127, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [100]}, {"left": 126, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [31]}, {"left": 125, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [17]}, {"left": 124, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [40]}, {"left": 123, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [10, 165, 118]}, {"left": 120, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [32, 17]}, {"left": 118, "kind": "l", "drafts": [53, 11], "rows": 3, "kept": 0, "emitted": [45]}, {"left": 117, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [105]}, {"left": 116, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [44, 35]}, {"left": 114, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [55, 155, 99, 121]}, {"left": 110, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [142, 118, 26]}, {"left": 107, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [65, 4]}, {"left": 105, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [100, 27, 4]}, {"left": 102, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [57]}, {"left": 101, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [126, 129]}, {"left": 99, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [40]}, {"left": 98, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [152, 101]}, {"left": 96, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [16, 152]}, {"left": 94, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [112, 42]}, {"left": 92, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [155, 155, 67]}, {"left": 89, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [36, 165]}, {"left": 87, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [118]}, {"left": 86, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [32, 17]}, {"left": 84, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [50]}, {"left": 83, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [23, 160, 30]}, {"left": 80, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [18, 159]}, {"left": 78, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [81, 96]}, {"left": 76, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [30, 54, 16]}, {"left": 73, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [84]}, {"left": 72, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [162, 28]}, {"left": 70, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [96]}, {"left": 69, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [124]}, {"left": 68, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [9]}, {"left": 67, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [41]}, {"left": 66, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [142, 118]}, {"left": 64, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [26]}, {"left": 63, "kind": "l", "drafts": [65], "rows": 2, "kept": 1, "emitted": [65, 4]}, {"left": 61, "kind": "l", "drafts": [100, 27, 29, 116], "rows": 5, "kept": 2, "emitted": [100, 27, 123]}, {"left": 58, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [98, 113, 107, 24]}, {"left": 54, "kind": "l", "drafts": [143, 10], "rows": 3, "kept": 2, "emitted": [143, 10, 138]}, {"left": 51, "kind": "l", "drafts": [124, 9, 41, 142], "rows": 5, "kept": 3, "emitted": [124, 9, 41, 49]}, {"left": 47, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [152]}, {"left": 46, "kind": "l", "drafts": [112, 51, 4, 20], "rows": 5, "kept": 4, "emitted": [112, 51, 4, 20, 136]}, {"left": 41, "kind": "l", "drafts": [55, 45, 105, 44, 9], "rows": 6, "kept": 5, "emitted": [55, 45, 105, 44, 9, 36]}, {"left": 35, "kind": "l", "drafts": [79, 64, 25, 65, 36, 55, 149], "rows": 8, "kept": 2, "emitted": [79, 64, 77]}, {"left": 32, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [125, 38, 7]}, {"left": 29, "kind": "l", "drafts": [165, 118, 32, 17, 53, 11, 13], "rows": 8, "kept": 4, "emitted": [165, 118, 32, 17, 105]}, {"left": 24, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [44]}, {"left": 23, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [9, 36, 79, 64, 155]}, {"left": 18, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [155, 102, 96]}, {"left": 15, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [30, 54, 16, 84]}, {"left": 11, "kind": "l", "drafts": [162, 28, 96, 124, 9, 41], "rows": 7, "kept": 1, "emitted": [162, 126]}, {"left": 9, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [77]}, {"left": 8, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [125, 38]}, {"left": 6, "kind": "l", "drafts": [7, 165, 118, 32, 17], "rows": 6, "kept": 5, "emitted": [7, 165, 118, 32, 17, 127]}]}, {"costs": {"verify": [21.291, 24.948500000000003, 28.631999999999998, 32.341499999999996, 36.077, 39.8385, 43.626000000000005, 47.439499999999995, 51.279, 55.144499999999994, 59.036, 62.9535, 66.89699999999999, 70.8665, 74.862, 78.8835, 82.931, 87.00450000000001, 91.104, 95.22949999999999, 99.381, 103.5585, 107.762, 111.99149999999999, 116.24699999999999, 120.5285, 124.83599999999998, 129.1695, 133.529, 137.9145, 142.326, 146.7635, 151.227, 155.7165, 160.232, 164.7735, 169.341, 173.93449999999999, 178.55399999999997, 183.1995, 187.871, 192.5685, 197.29199999999997, 202.04149999999998, 206.817, 211.61849999999998, 216.446, 221.29949999999997, 226.17899999999997, 231.0845, 236.016, 240.97349999999997, 245.95699999999997, 250.9665, 256.002, 261.0635, 266.15099999999995, 271.2645, 276.404, 281.5695, 286.76099999999997, 291.9785, 297.222, 302.4915], "draft": 3.677}, "min_match": 6, "max_rows": 12, "eos": [2], "prompt": [129, 73, 50, 16, 29, 77, 64, 148, 86, 26, 6, 117, 105, 107, 130, 46, 114, 16, 29, 124, 107, 90, 170, 74, 52, 159, 16, 24, 115, 113, 3, 116, 76, 123, 170, 124, 14, 24, 78, 161, 116, 36, 125, 166, 9, 171, 117, 112, 122, 65, 156, 67, 87, 128, 8, 147, 172, 156, 4, 69, 61, 124, 107, 172, 167, 44, 144, 161, 102, 164, 68, 41, 105, 57, 28, 64, 41, 130, 46, 3, 74, 116, 76, 123, 170, 124, 14, 162, 168, 62, 49, 69, 60, 69, 29, 170, 92, 107, 15, 159, 16, 24, 102, 109, 69, 92, 78, 161, 116, 36, 159, 16, 24, 24, 32, 53, 36, 27, 165, 2, 86, 74, 66, 137, 115, 41, 65, 129, 144, 60, 89, 103, 149, 52, 60, 131, 34, 148, 91, 110, 37, 157, 51, 138, 105, 107, 130, 46, 114, 16, 29, 50, 37, 57, 3, 38, 119, 117, 77, 171, 32, 119, 100, 48, 38, 5, 169, 16, 29, 95, 152, 60, 106, 141, 100, 150, 132, 30, 78, 31, 8, 168, 104, 132, 69, 60, 69, 29, 170, 92, 72, 8, 24, 16, 24, 146, 64, 171, 156, 54, 52, 78, 99, 102, 76, 69, 60, 69, 102, 76, 69, 60, 69, 29, 170, 2, 24, 75, 115, 81, 128, 17, 116, 80, 163, 76, 110, 148, 29, 12, 66, 47, 104, 84, 8, 41, 64, 117, 159, 26, 63, 146, 74, 27, 110, 101, 17, 94, 12, 148, 13, 167, 127, 118, 57, 151, 147, 76, 123, 170, 124, 20, 121, 27, 111, 110, 24, 107, 22, 129, 43, 157, 51, 170, 92, 107, 15, 50, 116, 76, 123, 65, 156, 67, 87, 128, 14, 88, 57, 28, 64, 117, 159, 64, 117, 159, 141, 51, 138, 105, 107, 130, 46, 114, 16, 29, 92, 78, 161, 116, 36, 125, 166, 9, 171, 117, 112, 122, 65, 156, 67, 39, 107, 13, 130, 148, 168, 141, 69, 56, 115, 53, 149, 52, 60, 131, 34, 148, 91, 156, 4, 69, 61, 124, 107, 90, 101, 111, 106, 35, 69, 121, 164, 19, 76, 69, 60, 69, 29, 170, 92, 107, 15, 50, 116, 76, 123, 122, 56, 115, 53, 149, 52, 60, 131, 34, 148, 91, 167, 67, 124, 168, 3, 90, 93, 2, 133, 104, 14, 32, 158, 7, 43], "rounds": [{"left": 338, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [28, 64, 117]}, {"left": 335, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [159]}, {"left": 334, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [141, 45]}, {"left": 332, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [29]}, {"left": 331, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [79]}, {"left": 330, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [133, 133, 140, 77, 95]}, {"left": 325, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [55]}, {"left": 324, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [60]}, {"left": 323, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [36, 171, 74]}, {"left": 320, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [120]}, {"left": 319, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [94]}, {"left": 318, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [172]}, {"left": 317, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [157, 81, 45, 26, 52]}, {"left": 312, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [159, 16]}, {"left": 310, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [24]}, {"left": 309, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [130, 46, 114]}, {"left": 306, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [16, 29, 120, 154]}, {"left": 302, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [2, 125, 159, 16]}, {"left": 298, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [24, 16, 24, 153]}, {"left": 294, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [117, 159, 141]}, {"left": 291, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [2]}, {"left": 290, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [74, 166]}, {"left": 288, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [115, 53]}, {"left": 286, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [149, 52, 60, 131]}, {"left": 282, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [34, 148, 91, 60]}, {"left": 278, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [69, 29, 170, 100, 150]}, {"left": 273, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [132, 30]}, {"left": 271, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [78]}, {"left": 270, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [48, 110]}, {"left": 268, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [56]}, {"left": 267, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [76, 167]}, {"left": 265, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [122, 56]}, {"left": 263, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [115]}, {"left": 262, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [53, 149]}, {"left": 260, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [52, 60, 131, 34, 148]}, {"left": 255, "kind": "l", "drafts": [91, 167, 67, 124], "rows": 5, "kept": 0, "emitted": [48]}, {"left": 254, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [110, 56, 76, 16, 29, 24]}, {"left": 248, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [87]}, {"left": 247, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [128]}, {"left": 246, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [8, 147, 172]}, {"left": 243, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [156, 4]}, {"left": 241, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [69]}, {"left": 240, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [61, 124, 107, 90]}, {"left": 236, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [170, 74]}, {"left": 234, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [52, 30, 78]}, {"left": 231, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [48]}, {"left": 230, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [64]}, {"left": 229, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [117]}, {"left": 228, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [159]}, {"left": 227, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [141, 29, 116, 76, 123]}, {"left": 222, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [122]}, {"left": 221, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [166, 169]}, {"left": 219, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [29, 86, 113]}, {"left": 216, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [147]}, {"left": 215, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [24]}, {"left": 214, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [46, 88, 123, 110, 112, 58]}, {"left": 208, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [159, 141, 105, 107, 130]}, {"left": 203, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [46, 114]}, {"left": 201, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [16]}, {"left": 200, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [29]}, {"left": 199, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [24]}, {"left": 198, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [124]}, {"left": 197, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [14, 17]}, {"left": 195, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [101]}, {"left": 194, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [140, 169]}, {"left": 192, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [26, 2, 64, 117, 159]}, {"left": 187, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [141, 29]}, {"left": 185, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [159]}, {"left": 184, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [141, 122]}, {"left": 182, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [65]}, {"left": 181, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [156, 67]}, {"left": 179, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [87, 128, 8]}, {"left": 176, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [147, 172, 156, 4, 69, 61]}, {"left": 170, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [124, 107]}, {"left": 168, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [159]}, {"left": 167, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [16]}, {"left": 166, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [24, 107, 90]}, {"left": 163, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [170, 74]}, {"left": 161, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [52, 99, 102]}, {"left": 158, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [76, 69, 60, 69]}, {"left": 154, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [29]}, {"left": 153, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [170]}, {"left": 152, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [92, 107]}, {"left": 150, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [15]}, {"left": 149, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [102, 109]}, {"left": 147, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [69]}, {"left": 146, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [92, 78]}, {"left": 144, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [161, 116, 110, 56]}, {"left": 140, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [76]}, {"left": 139, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [116, 36, 125, 166]}, {"left": 135, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [9, 171, 117]}, {"left": 132, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [112]}, {"left": 131, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [122, 43]}, {"left": 129, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [157, 51, 138]}, {"left": 126, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [105, 107, 130]}, {"left": 123, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [165, 109, 165, 88]}, {"left": 119, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [57, 28, 64, 117, 159]}, {"left": 114, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [100]}, {"left": 113, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [150, 132]}, {"left": 111, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [30, 78, 48, 110, 56, 76]}, {"left": 105, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [167, 122, 56, 115]}, {"left": 101, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [53]}, {"left": 100, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [149]}, {"left": 99, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [52, 50, 116, 76, 123, 170]}, {"left": 93, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [119]}, {"left": 92, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [4, 15, 50]}, {"left": 89, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [116, 76, 123, 170]}, {"left": 85, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [124, 14, 97]}, {"left": 82, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [4, 6, 30, 103, 30]}, {"left": 77, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [30, 167, 47]}, {"left": 74, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [22, 150]}, {"left": 72, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [132]}, {"left": 71, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [30, 78]}, {"left": 69, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [48, 110]}, {"left": 67, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [56, 76, 167]}, {"left": 64, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [122]}, {"left": 63, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [56, 115]}, {"left": 61, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [53, 27]}, {"left": 59, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [73, 79, 125, 13, 102, 109]}, {"left": 53, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [69]}, {"left": 52, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [92, 171]}, {"left": 50, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [16]}, {"left": 49, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [24, 117, 159, 141, 139]}, {"left": 44, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [84, 134, 110, 56, 76]}, {"left": 39, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [59]}, {"left": 38, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [121, 116, 114, 61, 154]}, {"left": 33, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [103, 61]}, {"left": 31, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [45]}, {"left": 30, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [80, 5]}, {"left": 28, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [172]}, {"left": 27, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [125, 23]}, {"left": 25, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [14, 123]}, {"left": 23, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [56, 115, 53]}, {"left": 20, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [149, 52, 60, 131]}, {"left": 16, "kind": "l", "drafts": [34], "rows": 2, "kept": 1, "emitted": [34, 148]}, {"left": 14, "kind": "l", "drafts": [48, 110], "rows": 3, "kept": 0, "emitted": [91]}, {"left": 13, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [2]}, {"left": 12, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [90, 151, 130, 90]}, {"left": 8, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [166]}, {"left": 7, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [31, 146, 64]}, {"left": 4, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [3, 144]}, {"left": 2, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [16]}, {"left": 1, "kind": "s", "drafts": [], "rows": 1, "kept": 0, "emitted": [118]}]}, {"costs": {"verify": [42.0, 47.0, 52.0, 57.0, 62.0, 67.0, 72.0, 77.0, 82.0, 87.0, 92.0, 97.0, 102.0, 107.0, 112.0, 117.0], "draft": 5.6}, "min_match": 4, "max_rows": 16, "eos": [2], "prompt": [32, 63, 143, 37, 94, 72, 54, 29, 140, 89, 104, 60, 47, 76, 31, 35, 50, 88, 62, 68, 98, 15, 142, 84, 156, 114, 60, 49, 46, 17, 67, 31, 139, 157, 107, 142, 71, 127, 92, 14, 58, 6, 70, 149, 113, 55, 164, 157, 18, 99, 69, 48, 156, 71, 96, 109, 136, 38, 155, 103, 48, 178, 25, 54, 134, 60, 109, 88, 49, 109, 73, 51, 49, 11, 165, 153, 62, 51, 94, 124, 16, 47, 133, 179, 120, 149, 55, 164, 48, 156, 71, 133, 74, 75, 109, 136, 70, 43, 78, 149, 151, 25, 106, 26, 156, 114, 60, 49, 46, 17, 67, 31, 139, 157, 107, 142, 71, 127, 92, 14, 58, 54, 31, 160, 31, 145, 44, 87, 70, 159, 73, 170, 34, 26, 42, 102, 106, 137, 26, 163, 127, 3, 102, 104, 60, 47, 76, 31, 35, 50, 88, 62, 68, 98, 15, 142, 84, 75, 75, 43, 78, 136, 24, 171, 16, 19, 174, 170, 48, 94, 68, 98, 15, 142], "rounds": [{"left": 358, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [120]}, {"left": 357, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [149]}, {"left": 356, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [2]}, {"left": 355, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [131]}, {"left": 354, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [48, 135]}, {"left": 352, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [37, 170]}, {"left": 350, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [120]}, {"left": 349, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [149, 114]}, {"left": 347, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [60, 49]}, {"left": 345, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [46, 17]}, {"left": 343, "kind": "l", "drafts": [67, 31], "rows": 3, "kept": 2, "emitted": [67, 31, 139]}, {"left": 340, "kind": "l", "drafts": [157, 107, 142, 71, 127], "rows": 6, "kept": 5, "emitted": [157, 107, 142, 71, 127, 92]}, {"left": 334, "kind": "l", "drafts": [14, 58, 54, 31, 160, 31, 145, 44, 87, 70, 159, 73, 170, 34, 26], "rows": 16, "kept": 2, "emitted": [14, 58, 6]}, {"left": 331, "kind": "l", "drafts": [70, 149, 113, 55, 164, 157, 18, 99], "rows": 9, "kept": 1, "emitted": [70, 78]}, {"left": 329, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [48, 156, 71, 96, 109]}, {"left": 324, "kind": "l", "drafts": [136, 38, 155, 103, 48], "rows": 6, "kept": 1, "emitted": [136, 26]}, {"left": 322, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [42]}, {"left": 321, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [102]}, {"left": 320, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [106]}, {"left": 319, "kind": "l", "drafts": [137, 26, 163], "rows": 4, "kept": 3, "emitted": [137, 26, 163, 127]}, {"left": 315, "kind": "l", "drafts": [3, 102, 104, 60, 47, 76, 31, 35, 50], "rows": 10, "kept": 5, "emitted": [3, 102, 104, 60, 47, 132]}, {"left": 309, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [67]}, {"left": 308, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [39, 100]}, {"left": 306, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [8, 130, 67, 153, 37, 55]}, {"left": 300, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [96]}, {"left": 299, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [115, 8, 40]}, {"left": 296, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [127, 25, 106]}, {"left": 293, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [26]}, {"left": 292, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [156, 114]}, {"left": 290, "kind": "l", "drafts": [60, 49, 46, 17, 67], "rows": 6, "kept": 5, "emitted": [60, 49, 46, 17, 67, 31]}, {"left": 284, "kind": "l", "drafts": [139, 157, 107, 142, 71, 127, 92, 14], "rows": 9, "kept": 8, "emitted": [139, 157, 107, 142, 71, 127, 92, 14, 58]}, {"left": 275, "kind": "l", "drafts": [54, 31, 160], "rows": 4, "kept": 0, "emitted": [6]}, {"left": 274, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [138]}, {"left": 273, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [42]}, {"left": 272, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [132, 139]}, {"left": 270, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [136, 38, 155, 103]}, {"left": 266, "kind": "l", "drafts": [48, 178, 25, 54, 134, 60, 109, 88, 49, 109], "rows": 11, "kept": 5, "emitted": [48, 178, 25, 54, 134, 71]}, {"left": 260, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [108]}, {"left": 259, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [143]}, {"left": 258, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [4]}, {"left": 257, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [43, 78, 46]}, {"left": 254, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [164]}, {"left": 253, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [92]}, {"left": 252, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [131, 48, 135]}, {"left": 249, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [68, 6, 16]}, {"left": 246, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [131]}, {"left": 245, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [108, 65, 110, 76, 134, 129]}, {"left": 239, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [17, 159, 111, 106]}, {"left": 235, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [174]}, {"left": 234, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [27, 17, 159, 111]}, {"left": 230, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [106, 174, 27]}, {"left": 227, "kind": "l", "drafts": [17, 159, 111, 106, 174, 27, 17, 159], "rows": 9, "kept": 0, "emitted": [156]}, {"left": 226, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [10, 90]}, {"left": 224, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [136]}, {"left": 223, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [38, 155, 103, 60, 47, 76]}, {"left": 217, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [5, 35, 14]}, {"left": 214, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [157]}, {"left": 213, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [129, 50, 110, 153]}, {"left": 209, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [170, 34, 26]}, {"left": 206, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [42]}, {"left": 205, "kind": "l", "drafts": [102, 106, 137, 26, 163], "rows": 6, "kept": 5, "emitted": [102, 106, 137, 26, 163, 127]}, {"left": 199, "kind": "l", "drafts": [3, 102, 104, 60, 47, 76, 31, 35, 50, 88, 62, 68, 98], "rows": 14, "kept": 6, "emitted": [3, 102, 104, 60, 47, 76, 131]}, {"left": 192, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [108, 65, 110, 76, 134]}, {"left": 187, "kind": "l", "drafts": [129, 17, 159, 111, 106, 174, 27], "rows": 8, "kept": 7, "emitted": [129, 17, 159, 111, 106, 174, 27, 156]}, {"left": 179, "kind": "l", "drafts": [10, 90, 136, 38, 155, 103, 60, 47, 76, 5], "rows": 11, "kept": 1, "emitted": [10, 100]}, {"left": 177, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [143]}, {"left": 176, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [11, 152]}, {"left": 174, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [93, 11]}, {"left": 172, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [87, 117]}, {"left": 170, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [22, 59, 169]}, {"left": 167, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [65, 110, 76]}, {"left": 164, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [134]}, {"left": 163, "kind": "l", "drafts": [129, 17, 159, 111, 106, 174, 27, 156, 10], "rows": 10, "kept": 9, "emitted": [129, 17, 159, 111, 106, 174, 27, 156, 10, 100]}, {"left": 153, "kind": "l", "drafts": [143, 11, 152, 93, 11, 87, 117, 22, 59, 169, 65, 110, 76], "rows": 14, "kept": 0, "emitted": [92]}, {"left": 152, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [14, 58, 6, 70, 149]}, {"left": 147, "kind": "l", "drafts": [113, 55, 164, 157, 18, 99, 69, 48, 156, 71, 96, 109, 136], "rows": 14, "kept": 3, "emitted": [113, 55, 164, 76]}, {"left": 143, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [138, 23]}, {"left": 141, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [87, 167]}, {"left": 139, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [74, 160, 8, 151]}, {"left": 135, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [115, 8]}, {"left": 133, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [40]}, {"left": 132, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [127]}, {"left": 131, "kind": "l", "drafts": [25, 106, 26, 156, 114, 60, 49, 46, 17, 67, 31], "rows": 12, "kept": 1, "emitted": [25, 167]}, {"left": 129, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [87, 54, 31, 160]}, {"left": 125, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [31, 145, 44, 87, 70]}, {"left": 120, "kind": "l", "drafts": [159, 73, 170, 34, 26, 42, 102, 106], "rows": 9, "kept": 2, "emitted": [159, 73, 46]}, {"left": 117, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [164]}, {"left": 116, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [92, 131]}, {"left": 114, "kind": "l", "drafts": [48, 135, 68, 6, 16, 131, 108, 65], "rows": 9, "kept": 2, "emitted": [48, 135, 37]}, {"left": 111, "kind": "l", "drafts": [170, 120, 149, 114, 60, 49, 46], "rows": 8, "kept": 2, "emitted": [170, 120, 104]}, {"left": 108, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [60, 47]}, {"left": 106, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [76, 31, 35]}, {"left": 103, "kind": "l", "drafts": [50, 88, 62, 68, 98, 15], "rows": 7, "kept": 6, "emitted": [50, 88, 62, 68, 98, 15, 142]}, {"left": 96, "kind": "l", "drafts": [84, 75, 75, 43, 78, 136, 24], "rows": 8, "kept": 1, "emitted": [84, 93]}, {"left": 94, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [167, 87, 54]}, {"left": 91, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [31, 160]}, {"left": 89, "kind": "l", "drafts": [31, 145, 44, 87, 70, 159, 73], "rows": 8, "kept": 0, "emitted": [135]}, {"left": 88, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [37, 170]}, {"left": 86, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [120, 149, 139, 127, 3]}, {"left": 81, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [102, 104, 60]}, {"left": 78, "kind": "l", "drafts": [47, 76, 131, 108, 65, 110], "rows": 7, "kept": 2, "emitted": [47, 76, 31]}, {"left": 75, "kind": "l", "drafts": [35, 50, 88, 62, 68, 98], "rows": 7, "kept": 6, "emitted": [35, 50, 88, 62, 68, 98, 15]}, {"left": 68, "kind": "l", "drafts": [142, 84, 75, 75, 43, 78, 136, 24], "rows": 9, "kept": 2, "emitted": [142, 84, 87]}, {"left": 65, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [117]}, {"left": 64, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [22, 59, 169, 140]}, {"left": 60, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [130]}, {"left": 59, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [170]}, {"left": 58, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [34]}, {"left": 57, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [26]}, {"left": 56, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [42, 102, 48]}, {"left": 53, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [78]}, {"left": 52, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [48]}, {"left": 51, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [156]}, {"left": 50, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [71]}, {"left": 49, "kind": "l", "drafts": [96, 109, 136, 26, 42, 102, 106, 137], "rows": 9, "kept": 3, "emitted": [96, 109, 136, 106]}, {"left": 45, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [174]}, {"left": 44, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [27, 156, 10]}, {"left": 41, "kind": "l", "drafts": [100, 92, 14, 58, 6, 70, 149], "rows": 8, "kept": 1, "emitted": [100, 143]}, {"left": 39, "kind": "l", "drafts": [11, 152, 93, 11, 87, 117], "rows": 7, "kept": 6, "emitted": [11, 152, 93, 11, 87, 117, 22]}, {"left": 32, "kind": "l", "drafts": [59, 169, 65, 110, 76, 134, 129, 17, 159], "rows": 10, "kept": 2, "emitted": [59, 169, 140]}, {"left": 29, "kind": "l", "drafts": [130, 170, 34, 26, 42, 102, 48, 78], "rows": 9, "kept": 0, "emitted": [172]}, {"left": 28, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [82, 132]}, {"left": 26, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [115, 92, 119]}, {"left": 23, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [135, 35, 14, 157]}, {"left": 19, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [129, 50, 110]}, {"left": 16, "kind": "l", "drafts": [153, 170, 34, 26, 42, 102], "rows": 7, "kept": 6, "emitted": [153, 170, 34, 26, 42, 102, 106]}, {"left": 9, "kind": "l", "drafts": [137, 26, 163, 127, 3, 102, 104], "rows": 8, "kept": 0, "emitted": [15]}, {"left": 8, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [104, 139, 60]}, {"left": 5, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [170]}, {"left": 4, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [77]}, {"left": 3, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [86]}, {"left": 2, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [55, 178]}]}, {"costs": {"verify": [21.291, 24.948500000000003, 28.631999999999998, 32.341499999999996, 36.077, 39.8385, 43.626000000000005, 47.439499999999995, 51.279, 55.144499999999994, 59.036, 62.9535, 66.89699999999999, 70.8665, 74.862, 78.8835, 82.931, 87.00450000000001, 91.104, 95.22949999999999, 99.381, 103.5585, 107.762, 111.99149999999999, 116.24699999999999, 120.5285, 124.83599999999998, 129.1695, 133.529, 137.9145, 142.326, 146.7635, 151.227, 155.7165, 160.232, 164.7735, 169.341, 173.93449999999999, 178.55399999999997, 183.1995, 187.871, 192.5685, 197.29199999999997, 202.04149999999998, 206.817, 211.61849999999998, 216.446, 221.29949999999997, 226.17899999999997, 231.0845, 236.016, 240.97349999999997, 245.95699999999997, 250.9665, 256.002, 261.0635, 266.15099999999995, 271.2645, 276.404, 281.5695, 286.76099999999997, 291.9785, 297.222, 302.4915], "draft": 3.677}, "min_match": 4, "max_rows": 16, "eos": [2], "prompt": [59, 41, 25, 137, 7, 134, 76, 73, 86, 70, 34, 29, 185, 60, 183, 130, 146, 104, 88, 181, 71, 174, 54, 22, 173, 133, 108, 74, 8, 53, 33, 90, 159, 106, 158, 137, 32, 102, 44, 141, 92, 128, 27, 63, 93, 98, 102, 119, 117, 103, 2, 168, 18, 60, 106, 15, 34, 22, 30, 51, 36, 166, 147, 34, 91, 2, 69, 30, 17, 163, 15, 40, 99, 10, 40, 120, 169, 5, 24, 129, 140, 110, 104, 27, 170, 54, 82, 119, 37, 74, 27, 58, 44, 164, 183, 59, 41, 102, 44, 141, 92, 63, 88, 37, 141, 182, 182, 18, 137, 157, 70, 166, 127, 118, 59, 43, 17, 114, 163, 173, 129, 68, 138, 95, 45, 81, 125, 11, 63, 59, 83, 68, 83, 62, 139, 114, 51, 40, 100, 50, 73, 86, 58, 44], "rounds": [{"left": 286, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [157, 84]}, {"left": 284, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [136]}, {"left": 283, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [97]}, {"left": 282, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [59, 41, 153, 8]}, {"left": 278, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [85]}, {"left": 277, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [168, 4]}, {"left": 275, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [61, 155, 123, 91, 128, 27]}, {"left": 269, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [63, 93]}, {"left": 267, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [98, 16]}, {"left": 265, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [47]}, {"left": 264, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [95, 158, 15, 146]}, {"left": 260, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [15, 9]}, {"left": 258, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [128]}, {"left": 257, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [55, 141]}, {"left": 255, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [157, 84]}, {"left": 253, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [136]}, {"left": 252, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [97]}, {"left": 251, "kind": "l", "drafts": [59, 41], "rows": 3, "kept": 2, "emitted": [59, 41, 128]}, {"left": 248, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [55, 141]}, {"left": 246, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [157]}, {"left": 245, "kind": "l", "drafts": [84, 136, 97, 59, 41], "rows": 6, "kept": 0, "emitted": [33]}, {"left": 244, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [90]}, {"left": 243, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [159]}, {"left": 242, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [106, 158, 137]}, {"left": 239, "kind": "l", "drafts": [32, 102, 44], "rows": 4, "kept": 3, "emitted": [32, 102, 44, 141]}, {"left": 235, "kind": "l", "drafts": [92, 128, 27, 63, 93], "rows": 6, "kept": 1, "emitted": [92, 58]}, {"left": 233, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [44]}, {"left": 232, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [164, 183]}, {"left": 230, "kind": "l", "drafts": [59, 41, 102, 44, 141], "rows": 6, "kept": 0, "emitted": [24]}, {"left": 229, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [76, 73, 86]}, {"left": 226, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [58, 44, 164, 30]}, {"left": 222, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [20]}, {"left": 221, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [28, 29, 144]}, {"left": 218, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [90, 46]}, {"left": 216, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [68, 30, 153]}, {"left": 213, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [60, 85, 9]}, {"left": 210, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [117]}, {"left": 209, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [90, 90, 179, 70, 164]}, {"left": 204, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [142]}, {"left": 203, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [50]}, {"left": 202, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [102]}, {"left": 201, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [59]}, {"left": 200, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [119, 68, 49, 111]}, {"left": 196, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [186]}, {"left": 195, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [173]}, {"left": 194, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [153]}, {"left": 193, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [8]}, {"left": 192, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [85, 168, 25, 170]}, {"left": 188, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [141, 28, 9, 128]}, {"left": 184, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [55]}, {"left": 183, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [141]}, {"left": 182, "kind": "l", "drafts": [157, 84, 136], "rows": 4, "kept": 3, "emitted": [157, 84, 136, 97]}, {"left": 178, "kind": "l", "drafts": [59, 41, 128], "rows": 4, "kept": 0, "emitted": [35]}, {"left": 177, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [143]}, {"left": 176, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [123, 150]}, {"left": 174, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [166]}, {"left": 173, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [82, 44, 125, 125, 146, 186]}, {"left": 167, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [86]}, {"left": 166, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [174, 23, 26, 20, 83]}, {"left": 161, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [105]}, {"left": 160, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [54, 22]}, {"left": 158, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [173]}, {"left": 157, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [133, 108]}, {"left": 155, "kind": "l", "drafts": [74, 8, 53, 33], "rows": 5, "kept": 4, "emitted": [74, 8, 53, 33, 90]}, {"left": 150, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [159, 106, 158]}, {"left": 147, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [137, 32, 102, 44]}, {"left": 143, "kind": "l", "drafts": [141, 92, 128, 27, 63], "rows": 6, "kept": 2, "emitted": [141, 92, 7]}, {"left": 140, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [42, 115, 134, 76, 73]}, {"left": 135, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [86, 58]}, {"left": 133, "kind": "l", "drafts": [44, 164, 30, 20, 28, 29], "rows": 7, "kept": 0, "emitted": [94]}, {"left": 132, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [138]}, {"left": 131, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [113, 31]}, {"left": 129, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [44]}, {"left": 128, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [164, 183]}, {"left": 126, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [15]}, {"left": 125, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [15, 141, 157, 84]}, {"left": 121, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [24]}, {"left": 120, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [104]}, {"left": 119, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [130, 16]}, {"left": 117, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [93, 56]}, {"left": 115, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [88]}, {"left": 114, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [18, 89]}, {"left": 112, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [73]}, {"left": 111, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [73, 160]}, {"left": 109, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [113, 48, 126, 54]}, {"left": 105, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [71]}, {"left": 104, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [130]}, {"left": 103, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [78]}, {"left": 102, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [163]}, {"left": 101, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [79, 54, 22]}, {"left": 98, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [173]}, {"left": 97, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [133, 108]}, {"left": 95, "kind": "l", "drafts": [74, 8, 53, 33], "rows": 5, "kept": 4, "emitted": [74, 8, 53, 33, 90]}, {"left": 90, "kind": "l", "drafts": [159, 106], "rows": 3, "kept": 2, "emitted": [159, 106, 158]}, {"left": 87, "kind": "l", "drafts": [137, 32, 102], "rows": 4, "kept": 3, "emitted": [137, 32, 102, 44]}, {"left": 83, "kind": "l", "drafts": [141, 92, 7, 42], "rows": 5, "kept": 2, "emitted": [141, 92, 17]}, {"left": 80, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [28, 121]}, {"left": 78, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [140]}, {"left": 77, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [150]}, {"left": 76, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [28, 176, 107, 113]}, {"left": 72, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [177]}, {"left": 71, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [107, 88]}, {"left": 69, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [30, 29, 119]}, {"left": 66, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [68, 49]}, {"left": 64, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [111]}, {"left": 63, "kind": "l", "drafts": [186, 173, 153, 8, 85, 168, 25], "rows": 8, "kept": 6, "emitted": [186, 173, 153, 8, 85, 168, 4]}, {"left": 56, "kind": "l", "drafts": [61, 155, 123, 91, 128, 27, 63], "rows": 8, "kept": 7, "emitted": [61, 155, 123, 91, 128, 27, 63, 93]}, {"left": 48, "kind": "l", "drafts": [98, 16, 47, 95, 158, 15], "rows": 7, "kept": 0, "emitted": [133]}, {"left": 47, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [48]}, {"left": 46, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [147, 65, 67]}, {"left": 43, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [139, 107, 156]}, {"left": 40, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [28, 9]}, {"left": 38, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [128]}, {"left": 37, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [55, 141, 157]}, {"left": 34, "kind": "l", "drafts": [84, 136, 97, 35, 143, 123, 150, 166, 82, 44], "rows": 11, "kept": 3, "emitted": [84, 136, 97, 46]}, {"left": 30, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [32]}, {"left": 29, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [9, 155, 52, 21, 22]}, {"left": 24, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [162, 30]}, {"left": 22, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [175, 78, 49]}, {"left": 19, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [57]}, {"left": 18, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [124]}, {"left": 17, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [30, 94, 101]}, {"left": 14, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [20]}, {"left": 13, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [109, 179, 82]}, {"left": 10, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [172, 100]}, {"left": 8, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [136, 97, 59, 41]}, {"left": 4, "kind": "l", "drafts": [128, 55, 141], "rows": 4, "kept": 0, "emitted": [162]}, {"left": 3, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [30, 28, 176]}]}, {"costs": {"verify": [42.0, 47.0, 52.0, 57.0, 62.0, 67.0, 72.0, 77.0, 82.0, 87.0, 92.0, 97.0, 102.0, 107.0, 112.0, 117.0], "draft": 5.6}, "min_match": 3, "max_rows": 8, "eos": [2], "prompt": [37, 124, 5, 60, 72, 78, 72, 34, 12, 5, 119, 70, 76, 56, 2, 155, 42, 128, 18, 177, 26, 123, 45, 77, 34, 119], "rounds": [{"left": 245, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [168, 57, 155, 42]}, {"left": 241, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [128, 18, 70, 76]}, {"left": 237, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [56, 104]}, {"left": 235, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [177, 171]}, {"left": 233, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [119]}, {"left": 232, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [18, 42, 151, 45, 187, 99]}, {"left": 226, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [84, 34, 131]}, {"left": 223, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [24, 89]}, {"left": 221, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [154, 57, 27, 14, 147, 47]}, {"left": 215, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [77, 76]}, {"left": 213, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [56, 65, 177]}, {"left": 210, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [137, 134, 37]}, {"left": 207, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [55, 114, 32, 25]}, {"left": 203, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [150, 191, 165, 92, 150, 165]}, {"left": 197, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [114, 172, 165]}, {"left": 194, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [87]}, {"left": 193, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [98]}, {"left": 192, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [88, 57, 175]}, {"left": 189, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [47, 77]}, {"left": 187, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [189, 5, 36, 152]}, {"left": 183, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [57]}, {"left": 182, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [110, 182, 152]}, {"left": 179, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [141]}, {"left": 178, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [99, 84, 34, 26]}, {"left": 174, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [123]}, {"left": 173, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [187, 99]}, {"left": 171, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [84, 14, 130, 71, 108]}, {"left": 166, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [12, 5, 119, 70, 76]}, {"left": 161, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [56, 25, 22, 164]}, {"left": 157, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [94, 138, 3, 29]}, {"left": 153, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [56, 91, 63]}, {"left": 150, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [113]}, {"left": 149, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [101]}, {"left": 148, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [177]}, {"left": 147, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [28, 65]}, {"left": 145, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [177, 137]}, {"left": 143, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [134]}, {"left": 142, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [37]}, {"left": 141, "kind": "l", "drafts": [55, 114], "rows": 3, "kept": 2, "emitted": [55, 114, 32]}, {"left": 138, "kind": "l", "drafts": [25, 150, 191, 165, 92], "rows": 6, "kept": 5, "emitted": [25, 150, 191, 165, 92, 150]}, {"left": 132, "kind": "l", "drafts": [165, 114, 172, 165, 87, 98, 88], "rows": 8, "kept": 5, "emitted": [165, 114, 172, 165, 87, 119]}, {"left": 126, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [70]}, {"left": 125, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [76, 56]}, {"left": 123, "kind": "l", "drafts": [25, 22, 164, 94, 138, 3], "rows": 7, "kept": 0, "emitted": [107]}, {"left": 122, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [116]}, {"left": 121, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [92, 160]}, {"left": 119, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [76, 158]}, {"left": 117, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [135]}, {"left": 116, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [24]}, {"left": 115, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [191]}, {"left": 114, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [165, 92, 150, 165]}, {"left": 110, "kind": "l", "drafts": [114, 172, 165], "rows": 4, "kept": 3, "emitted": [114, 172, 165, 87]}, {"left": 106, "kind": "l", "drafts": [119, 70, 76, 56, 107, 116, 92], "rows": 8, "kept": 0, "emitted": [98]}, {"left": 105, "kind": "l", "drafts": [88, 57, 175, 47, 77, 189, 5], "rows": 8, "kept": 5, "emitted": [88, 57, 175, 47, 77, 119]}, {"left": 99, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [18, 42]}, {"left": 97, "kind": "l", "drafts": [151, 45, 187, 99, 84, 34], "rows": 7, "kept": 2, "emitted": [151, 45, 11]}, {"left": 94, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [83, 113, 171]}, {"left": 91, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [119, 18]}, {"left": 89, "kind": "l", "drafts": [42, 151, 45, 187, 99], "rows": 6, "kept": 3, "emitted": [42, 151, 45, 11]}, {"left": 85, "kind": "l", "drafts": [83, 113, 171, 119, 18], "rows": 6, "kept": 1, "emitted": [83, 83]}, {"left": 83, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [139]}, {"left": 82, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [50]}, {"left": 81, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [89, 56, 32]}, {"left": 78, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [25, 150]}, {"left": 76, "kind": "l", "drafts": [191, 165, 92, 150], "rows": 5, "kept": 4, "emitted": [191, 165, 92, 150, 165]}, {"left": 71, "kind": "l", "drafts": [114, 172, 165, 87, 119, 70, 76], "rows": 8, "kept": 4, "emitted": [114, 172, 165, 87, 98]}, {"left": 66, "kind": "l", "drafts": [88, 57, 175, 47, 77, 189, 5], "rows": 8, "kept": 5, "emitted": [88, 57, 175, 47, 77, 83]}, {"left": 60, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [152, 165]}, {"left": 58, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [35, 95, 25, 103]}, {"left": 54, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [142]}, {"left": 53, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [174, 172]}, {"left": 51, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [20, 187]}, {"left": 49, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [26, 123]}, {"left": 47, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [189]}, {"left": 46, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [74]}, {"left": 45, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [187, 99]}, {"left": 43, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [84]}, {"left": 42, "kind": "l", "drafts": [14, 130, 71, 108, 12, 5, 119], "rows": 8, "kept": 0, "emitted": [34]}, {"left": 41, "kind": "l", "drafts": [131, 24, 89, 154, 57], "rows": 6, "kept": 0, "emitted": [18]}, {"left": 40, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [42]}, {"left": 39, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [151, 45, 11, 83]}, {"left": 35, "kind": "l", "drafts": [83, 139, 50, 89], "rows": 5, "kept": 1, "emitted": [83, 152]}, {"left": 33, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [165]}, {"left": 32, "kind": "l", "drafts": [35, 95, 25], "rows": 4, "kept": 1, "emitted": [35, 98]}, {"left": 30, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [140, 10, 106]}, {"left": 27, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [190]}, {"left": 26, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [109, 157]}, {"left": 24, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [31, 24]}, {"left": 22, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [187, 99, 84, 34, 70, 76]}, {"left": 16, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [56]}, {"left": 15, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [189]}, {"left": 14, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [74, 187, 99, 84, 24]}, {"left": 9, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [61]}, {"left": 8, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [29]}, {"left": 7, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [100]}, {"left": 6, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [10, 26]}, {"left": 4, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [130, 120, 18]}, {"left": 1, "kind": "s", "drafts": [], "rows": 1, "kept": 0, "emitted": [42]}]}, {"costs": {"verify": [21.291, 24.948500000000003, 28.631999999999998, 32.341499999999996, 36.077, 39.8385, 43.626000000000005, 47.439499999999995, 51.279, 55.144499999999994, 59.036, 62.9535, 66.89699999999999, 70.8665, 74.862, 78.8835, 82.931, 87.00450000000001, 91.104, 95.22949999999999, 99.381, 103.5585, 107.762, 111.99149999999999, 116.24699999999999, 120.5285, 124.83599999999998, 129.1695, 133.529, 137.9145, 142.326, 146.7635, 151.227, 155.7165, 160.232, 164.7735, 169.341, 173.93449999999999, 178.55399999999997, 183.1995, 187.871, 192.5685, 197.29199999999997, 202.04149999999998, 206.817, 211.61849999999998, 216.446, 221.29949999999997, 226.17899999999997, 231.0845, 236.016, 240.97349999999997, 245.95699999999997, 250.9665, 256.002, 261.0635, 266.15099999999995, 271.2645, 276.404, 281.5695, 286.76099999999997, 291.9785, 297.222, 302.4915], "draft": 3.677}, "min_match": 6, "max_rows": 12, "eos": [2], "prompt": [169, 110, 91, 95, 97, 103, 109, 73, 19, 142, 71, 121, 29, 163, 133, 46, 52, 131, 178, 55, 29, 90, 70, 84, 121, 100, 194, 88, 123, 59, 41, 104, 39, 137, 111, 97, 86, 71, 183, 115, 89, 83, 71, 191, 141, 193, 129, 197, 190, 95, 50, 193, 34, 145], "rounds": [{"left": 268, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [4, 140, 64]}, {"left": 265, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [12]}, {"left": 264, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [44]}, {"left": 263, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [178]}, {"left": 262, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [190]}, {"left": 261, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [117]}, {"left": 260, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [120, 7]}, {"left": 258, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [132, 33, 163]}, {"left": 255, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [172]}, {"left": 254, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [67, 15]}, {"left": 252, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [71]}, {"left": 251, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [76, 135]}, {"left": 249, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [90, 149, 163, 159, 9]}, {"left": 244, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [134, 87, 128, 29, 115, 182]}, {"left": 238, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [120, 159, 86, 115, 173]}, {"left": 233, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [55, 33]}, {"left": 231, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [182]}, {"left": 230, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [193]}, {"left": 229, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [153, 68]}, {"left": 227, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [52, 131]}, {"left": 225, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [178, 55]}, {"left": 223, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [29, 90, 70]}, {"left": 220, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [84]}, {"left": 219, "kind": "l", "drafts": [121, 100, 194, 88], "rows": 5, "kept": 4, "emitted": [121, 100, 194, 88, 123]}, {"left": 214, "kind": "l", "drafts": [59, 41, 104, 39, 137, 111, 97, 86, 71, 183, 115], "rows": 12, "kept": 0, "emitted": [109]}, {"left": 213, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [14]}, {"left": 212, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [88]}, {"left": 211, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [125, 149, 108, 51, 88, 164]}, {"left": 205, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [170]}, {"left": 204, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [195]}, {"left": 203, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [61, 18]}, {"left": 201, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [59]}, {"left": 200, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [179, 7, 99]}, {"left": 197, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [161, 143]}, {"left": 195, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [161]}, {"left": 194, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [143, 123, 59, 41, 104, 39]}, {"left": 188, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [137, 111, 97]}, {"left": 185, "kind": "l", "drafts": [86, 71, 183, 115], "rows": 5, "kept": 4, "emitted": [86, 71, 183, 115, 172]}, {"left": 180, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [100, 155, 197]}, {"left": 177, "kind": "d", "drafts": [], "rows": 5, "kept": 4, "emitted": [98, 143, 178, 93, 15]}, {"left": 172, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [108, 119, 42, 79]}, {"left": 168, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [151, 138, 128, 91, 119, 42]}, {"left": 162, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [95, 2]}, {"left": 160, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [146]}, {"left": 159, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [166]}, {"left": 158, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [109]}, {"left": 157, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [14]}, {"left": 156, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [168]}, {"left": 155, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [55]}, {"left": 154, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [145]}, {"left": 153, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [61, 144, 105, 89, 83, 87]}, {"left": 147, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [154, 94]}, {"left": 145, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [96, 53, 178]}, {"left": 142, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [185]}, {"left": 141, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [18, 200, 60, 82]}, {"left": 137, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [83]}, {"left": 136, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [190]}, {"left": 135, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [67]}, {"left": 134, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [167, 91, 132]}, {"left": 131, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [143, 115, 78, 92]}, {"left": 127, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [91, 102, 111, 13]}, {"left": 123, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [154, 108]}, {"left": 121, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [39, 14, 77]}, {"left": 118, "kind": "d", "drafts": [], "rows": 5, "kept": 1, "emitted": [21, 187]}, {"left": 116, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [31]}, {"left": 115, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [172]}, {"left": 114, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [135, 161]}, {"left": 112, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [95]}, {"left": 111, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [24]}, {"left": 110, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [12]}, {"left": 109, "kind": "d", "drafts": [], "rows": 6, "kept": 2, "emitted": [167, 8, 193]}, {"left": 106, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [18]}, {"left": 105, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [59, 179]}, {"left": 103, "kind": "d", "drafts": [], "rows": 4, "kept": 3, "emitted": [7, 99, 161, 179]}, {"left": 99, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [162, 41, 14, 129]}, {"left": 95, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [74]}, {"left": 94, "kind": "d", "drafts": [], "rows": 4, "kept": 1, "emitted": [101, 19]}, {"left": 92, "kind": "d", "drafts": [], "rows": 4, "kept": 2, "emitted": [125, 177, 53]}, {"left": 89, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [189, 51, 105]}, {"left": 86, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [89]}, {"left": 85, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [83, 164]}, {"left": 83, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [144, 91, 39]}, {"left": 80, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [185, 143, 82]}, {"left": 77, "kind": "d", "drafts": [], "rows": 5, "kept": 0, "emitted": [61]}, {"left": 76, "kind": "d", "drafts": [], "rows": 6, "kept": 3, "emitted": [193, 115, 78, 92]}, {"left": 72, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [91, 102]}, {"left": 70, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [111, 13]}, {"left": 68, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [154]}, {"left": 67, "kind": "l", "drafts": [108, 39, 14, 77, 21, 187, 31, 172], "rows": 9, "kept": 1, "emitted": [108, 188]}, {"left": 65, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [38, 125, 133]}, {"left": 62, "kind": "d", "drafts": [], "rows": 6, "kept": 5, "emitted": [176, 141, 19, 106, 131, 36]}, {"left": 56, "kind": "d", "drafts": [], "rows": 5, "kept": 2, "emitted": [43, 98, 4]}, {"left": 53, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [140, 64]}, {"left": 51, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [12, 44]}, {"left": 49, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [178]}, {"left": 48, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [190]}, {"left": 47, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [117]}, {"left": 46, "kind": "l", "drafts": [120, 7, 132, 33, 163], "rows": 6, "kept": 0, "emitted": [67]}, {"left": 45, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [72]}, {"left": 44, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [44, 116]}, {"left": 42, "kind": "d", "drafts": [], "rows": 3, "kept": 1, "emitted": [52, 128]}, {"left": 40, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [105, 89, 83]}, {"left": 37, "kind": "d", "drafts": [], "rows": 4, "kept": 0, "emitted": [98]}, {"left": 36, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [68]}, {"left": 35, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [78]}, {"left": 34, "kind": "d", "drafts": [], "rows": 3, "kept": 2, "emitted": [85, 169, 146]}, {"left": 31, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [113, 116, 118, 63]}, {"left": 27, "kind": "d", "drafts": [], "rows": 6, "kept": 1, "emitted": [29, 4]}, {"left": 25, "kind": "d", "drafts": [], "rows": 6, "kept": 4, "emitted": [14, 163, 61, 139, 183]}, {"left": 20, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [169, 59, 179, 7]}, {"left": 16, "kind": "d", "drafts": [], "rows": 2, "kept": 0, "emitted": [99]}, {"left": 15, "kind": "d", "drafts": [], "rows": 3, "kept": 0, "emitted": [161]}, {"left": 14, "kind": "d", "drafts": [], "rows": 2, "kept": 1, "emitted": [143, 123]}, {"left": 12, "kind": "d", "drafts": [], "rows": 5, "kept": 3, "emitted": [59, 41, 104, 39]}, {"left": 8, "kind": "d", "drafts": [], "rows": 6, "kept": 0, "emitted": [137]}, {"left": 7, "kind": "l", "drafts": [111, 97, 86], "rows": 4, "kept": 3, "emitted": [111, 97, 86, 71]}, {"left": 3, "kind": "l", "drafts": [183, 115], "rows": 3, "kept": 0, "emitted": [193]}, {"left": 2, "kind": "d", "drafts": [], "rows": 1, "kept": 0, "emitted": [71]}, {"left": 1, "kind": "s", "drafts": [], "rows": 1, "kept": 0, "emitted": [183]}]}]} \ No newline at end of file diff --git a/zig/src/families/deepseek_v41/draft/fixtures/spec-golden.json b/zig/src/families/deepseek_v41/draft/fixtures/spec-golden.json new file mode 100644 index 0000000000..b8f40e3b14 --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/fixtures/spec-golden.json @@ -0,0 +1 @@ +{"decay": 0.8, "min_rate": 0.3, "probe": 8, "ops": [{"op": "launch", "cands": [[3, 400, 0, 74, [5]]], "got": true, "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 1, "hit": 0, "differs": 0, "miss": {}}}, {"op": "ingested", "commits": [[3, 400, 0, true]], "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 1, "hit": 0, "differs": 0, "miss": {}}}, {"op": "take", "asks": [[3, 74, 401, [5]]], "got": true, "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 1, "hit": 1, "differs": 0, "miss": {}}}, {"op": "launch", "cands": [[3, 401, 3, 71, [5]]], "got": true, "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 2, "hit": 1, "differs": 0, "miss": {}}}, {"op": "ingested", "commits": [[3, 401, 3, true]], "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 2, "hit": 1, "differs": 0, "miss": {}}}, {"op": "take", "asks": [[3, 71, 405, [5]]], "got": true, "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 2, "hit": 2, "differs": 0, "miss": {}}}, {"op": "launch", "cands": [[0, 100, 0, 999, []], [1, 200, 1, 47, [11]], [2, 300, 4, 879, []]], "got": true, "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 3, "hit": 2, "differs": 0, "miss": {}}}, {"op": "ingested", "commits": [[0, 100, 0, true], [1, 200, 1, true], [2, 300, 4, true]], "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 3, "hit": 2, "differs": 0, "miss": {}}}, {"op": "take", "asks": [[0, 999, 101, []], [1, 47, 202, [11]], [2, 879, 305, []]], "got": true, "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 3, "hit": 3, "differs": 0, "miss": {}}}, {"op": "launch", "cands": [[1, 202, 3, 599, [11]], [3, 405, 3, 370, [5]]], "got": true, "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 4, "hit": 3, "differs": 0, "miss": {}}}, {"op": "ingested", "commits": [[1, 202, 3, true], [3, 405, 3, true]], "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 4, "hit": 3, "differs": 0, "miss": {}}}, {"op": "take", "asks": [[1, 599, 206, [11]], [3, 370, 409, [5]]], "got": true, "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 4, "hit": 4, "differs": 0, "miss": {}}}, {"op": "launch", "cands": [[0, 101, 4, 428, []]], "got": true, "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 5, "hit": 4, "differs": 0, "miss": {}}}, {"op": "ingested", "commits": [[0, 101, 4, true]], "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 5, "hit": 4, "differs": 0, "miss": {}}}, {"op": "take", "asks": [[0, 428, 106, []]], "got": true, "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 5, "hit": 5, "differs": 0, "miss": {}}}, {"op": "launch", "cands": [[2, 305, 2, 711, []]], "got": true, "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 6, "hit": 5, "differs": 0, "miss": {}}}, {"op": "ingested", "commits": [[2, 305, 2, true]], "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 6, "hit": 5, "differs": 0, "miss": {}}}, {"op": "take", "asks": [[2, 711, 308, []]], "got": true, "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 6, "hit": 6, "differs": 0, "miss": {}}}, {"op": "launch", "cands": [[2, 308, 0, 62, []], [3, 409, 2, 662, [5]]], "got": true, "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 7, "hit": 6, "differs": 0, "miss": {}}}, {"op": "ingested", "commits": [[2, 308, 0, true], [3, 409, 2, true]], "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 7, "hit": 6, "differs": 0, "miss": {}}}, {"op": "take", "asks": [[2, 62, 309, []], [3, 662, 412, [5]]], "got": true, "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 7, "hit": 7, "differs": 0, "miss": {}}}, {"op": "launch", "cands": [[0, 106, 2, 132, []], [3, 412, 1, 407, [5]]], "got": true, "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 8, "hit": 7, "differs": 0, "miss": {}}}, {"op": "ingested", "commits": [[0, 106, 2, true], [3, 412, 0, false]], "state": {"rates": [1.0, 1.0, 1.0, 1.0], "skipped": [0, 0, 0, 0], "launched": 8, "hit": 7, "differs": 1, "miss": {}}}, {"op": "take", "asks": [[0, 132, 109, []], [3, 440, 413, [5]]], "got": false, "state": {"rates": [0.8, 1.0, 1.0, 0.8], "skipped": [0, 0, 0, 0], "launched": 8, "hit": 7, "differs": 1, "miss": {"commit": 1}}}, {"op": "launch", "cands": [[2, 309, 3, 980, []], [3, 413, 1, 154, [5]]], "got": true, "state": {"rates": [0.8, 1.0, 1.0, 0.8], "skipped": [0, 0, 0, 0], "launched": 9, "hit": 7, "differs": 1, "miss": {"commit": 1}}}, {"op": "ingested", "commits": [[2, 309, 3, true], [3, 413, 1, true]], "state": {"rates": [0.8, 1.0, 1.0, 0.8], "skipped": [0, 0, 0, 0], "launched": 9, "hit": 7, "differs": 1, "miss": {"commit": 1}}}, {"op": "take", "asks": [[2, 980, 313, []], [3, 154, 415, [5]]], "got": true, "state": {"rates": [0.8, 1.0, 1.0, 0.8400000000000001], "skipped": [0, 0, 0, 0], "launched": 9, "hit": 8, "differs": 1, "miss": {"commit": 1}}}, {"op": "launch", "cands": [[0, 109, 3, 921, []], [2, 313, 4, 401, []], [3, 415, 3, 408, [5]]], "got": true, "state": {"rates": [0.8, 1.0, 1.0, 0.8400000000000001], "skipped": [0, 0, 0, 0], "launched": 10, "hit": 8, "differs": 1, "miss": {"commit": 1}}}, {"op": "ingested", "commits": [[0, 109, 3, true], [2, 313, 4, true], [3, 415, 3, true]], "state": {"rates": [0.8, 1.0, 1.0, 0.8400000000000001], "skipped": [0, 0, 0, 0], "launched": 10, "hit": 8, "differs": 1, "miss": {"commit": 1}}}, {"op": "take", "asks": [[0, 451, 113, []], [2, 401, 318, []], [3, 408, 419, [5]]], "got": false, "state": {"rates": [0.6400000000000001, 1.0, 0.8, 0.6720000000000002], "skipped": [0, 0, 0, 0], "launched": 10, "hit": 8, "differs": 1, "miss": {"commit": 1, "pending": 1}}}, {"op": "forget", "slot": 1, "state": {"rates": [0.6400000000000001, 1.0, 0.8, 0.6720000000000002], "skipped": [0, 0, 0, 0], "launched": 10, "hit": 8, "differs": 1, "miss": {"commit": 1, "pending": 1}}}, {"op": "launch", "cands": [[2, 318, 2, 616, []]], "got": true, "state": {"rates": [0.6400000000000001, 1.0, 0.8, 0.6720000000000002], "skipped": [0, 0, 0, 0], "launched": 11, "hit": 8, "differs": 1, "miss": {"commit": 1, "pending": 1}}}, {"op": "ingested", "commits": [[2, 318, 2, true]], "state": {"rates": [0.6400000000000001, 1.0, 0.8, 0.6720000000000002], "skipped": [0, 0, 0, 0], "launched": 11, "hit": 8, "differs": 1, "miss": {"commit": 1, "pending": 1}}}, {"op": "take", "asks": [[2, 616, 321, [99]]], "got": false, "state": {"rates": [0.6400000000000001, 1.0, 0.6400000000000001, 0.6720000000000002], "skipped": [0, 0, 0, 0], "launched": 11, "hit": 8, "differs": 1, "miss": {"commit": 1, "pending": 1, "inputs": 1}}}, {"op": "launch", "cands": [[0, 113, 1, 104, []], [2, 321, 2, 758, []]], "got": true, "state": {"rates": [0.6400000000000001, 1.0, 0.6400000000000001, 0.6720000000000002], "skipped": [0, 0, 0, 0], "launched": 12, "hit": 8, "differs": 1, "miss": {"commit": 1, "pending": 1, "inputs": 1}}}, {"op": "ingested", "commits": [[0, 113, 1, true], [2, 321, 2, true]], "state": {"rates": [0.6400000000000001, 1.0, 0.6400000000000001, 0.6720000000000002], "skipped": [0, 0, 0, 0], "launched": 12, "hit": 8, "differs": 1, "miss": {"commit": 1, "pending": 1, "inputs": 1}}}, {"op": "take", "asks": [[0, 104, 115, []], [2, 758, 324, []]], "got": true, "state": {"rates": [0.7120000000000001, 1.0, 0.7120000000000001, 0.6720000000000002], "skipped": [0, 0, 0, 0], "launched": 12, "hit": 9, "differs": 1, "miss": {"commit": 1, "pending": 1, "inputs": 1}}}, {"op": "forget", "slot": 2, "state": {"rates": [0.7120000000000001, 1.0, 1.0, 0.6720000000000002], "skipped": [0, 0, 0, 0], "launched": 12, "hit": 9, "differs": 1, "miss": {"commit": 1, "pending": 1, "inputs": 1}}}, {"op": "launch", "cands": [[2, 324, 4, 375, []]], "got": true, "state": {"rates": [0.7120000000000001, 1.0, 1.0, 0.6720000000000002], "skipped": [0, 0, 0, 0], "launched": 13, "hit": 9, "differs": 1, "miss": {"commit": 1, "pending": 1, "inputs": 1}}}, {"op": "ingested", "commits": [[2, 324, 2, false]], "state": {"rates": [0.7120000000000001, 1.0, 1.0, 0.6720000000000002], "skipped": [0, 0, 0, 0], "launched": 13, "hit": 9, "differs": 2, "miss": {"commit": 1, "pending": 1, "inputs": 1}}}, {"op": "take", "asks": [[2, 545, 327, []]], "got": false, "state": {"rates": [0.7120000000000001, 1.0, 0.8, 0.6720000000000002], "skipped": [0, 0, 0, 0], "launched": 13, "hit": 9, "differs": 2, "miss": {"commit": 2, "pending": 1, "inputs": 1}}}, {"op": "launch", "cands": [[0, 115, 1, 837, []], [1, 206, 3, 757, [11]], [2, 327, 1, 204, []]], "got": true, "state": {"rates": [0.7120000000000001, 1.0, 0.8, 0.6720000000000002], "skipped": [0, 0, 0, 0], "launched": 14, "hit": 9, "differs": 2, "miss": {"commit": 2, "pending": 1, "inputs": 1}}}, {"op": "ingested", "commits": [[0, 115, 1, true], [1, 206, 3, true], [2, 327, 1, true]], "state": {"rates": [0.7120000000000001, 1.0, 0.8, 0.6720000000000002], "skipped": [0, 0, 0, 0], "launched": 14, "hit": 9, "differs": 2, "miss": {"commit": 2, "pending": 1, "inputs": 1}}}, {"op": "take", "asks": [[1, 757, 210, [11]], [2, 204, 329, []]], "got": false, "state": {"rates": [0.5696000000000001, 0.8, 0.6400000000000001, 0.6720000000000002], "skipped": [0, 0, 0, 0], "launched": 14, "hit": 9, "differs": 2, "miss": {"commit": 2, "pending": 1, "inputs": 1, "slots": 1}}}, {"op": "launch", "cands": [[0, 117, 0, 232, []], [3, 419, 3, 201, [5]]], "got": true, "state": {"rates": [0.5696000000000001, 0.8, 0.6400000000000001, 0.6720000000000002], "skipped": [0, 0, 0, 0], "launched": 15, "hit": 9, "differs": 2, "miss": {"commit": 2, "pending": 1, "inputs": 1, "slots": 1}}}, {"op": "ingested", "commits": [[0, 117, 0, true], [3, 419, 3, true]], "state": {"rates": [0.5696000000000001, 0.8, 0.6400000000000001, 0.6720000000000002], "skipped": [0, 0, 0, 0], "launched": 15, "hit": 9, "differs": 2, "miss": {"commit": 2, "pending": 1, "inputs": 1, "slots": 1}}}, {"op": "take", "asks": [[0, 232, 118, []], [3, 201, 423, [5]]], "got": true, "state": {"rates": [0.65568, 0.8, 0.6400000000000001, 0.7376000000000001], "skipped": [0, 0, 0, 0], "launched": 15, "hit": 10, "differs": 2, "miss": {"commit": 2, "pending": 1, "inputs": 1, "slots": 1}}}, {"op": "launch", "cands": [[3, 423, 1, 489, [5]]], "got": true, "state": {"rates": [0.65568, 0.8, 0.6400000000000001, 0.7376000000000001], "skipped": [0, 0, 0, 0], "launched": 16, "hit": 10, "differs": 2, "miss": {"commit": 2, "pending": 1, "inputs": 1, "slots": 1}}}, {"op": "ingested", "commits": [[3, 423, 1, true]], "state": {"rates": [0.65568, 0.8, 0.6400000000000001, 0.7376000000000001], "skipped": [0, 0, 0, 0], "launched": 16, "hit": 10, "differs": 2, "miss": {"commit": 2, "pending": 1, "inputs": 1, "slots": 1}}}, {"op": "take", "asks": [[3, 489, 425, [5]]], "got": true, "state": {"rates": [0.65568, 0.8, 0.6400000000000001, 0.7900800000000001], "skipped": [0, 0, 0, 0], "launched": 16, "hit": 11, "differs": 2, "miss": {"commit": 2, "pending": 1, "inputs": 1, "slots": 1}}}, {"op": "launch", "cands": [[1, 210, 0, 742, [11]], [3, 425, 1, 174, [5]]], "got": true, "state": {"rates": [0.65568, 0.8, 0.6400000000000001, 0.7900800000000001], "skipped": [0, 0, 0, 0], "launched": 17, "hit": 11, "differs": 2, "miss": {"commit": 2, "pending": 1, "inputs": 1, "slots": 1}}}, {"op": "ingested", "commits": [[1, 210, 0, true], [3, 425, 1, true]], "state": {"rates": [0.65568, 0.8, 0.6400000000000001, 0.7900800000000001], "skipped": [0, 0, 0, 0], "launched": 17, "hit": 11, "differs": 2, "miss": {"commit": 2, "pending": 1, "inputs": 1, "slots": 1}}}, {"op": "take", "asks": [[1, 742, 211, [11]], [3, 673, 427, [5]]], "got": false, "state": {"rates": [0.65568, 0.6400000000000001, 0.6400000000000001, 0.6320640000000002], "skipped": [0, 0, 0, 0], "launched": 17, "hit": 11, "differs": 2, "miss": {"commit": 2, "pending": 2, "inputs": 1, "slots": 1}}}, {"op": "launch", "cands": [[0, 118, 0, 539, []], [1, 211, 1, 444, [11]], [2, 329, 1, 845, []]], "got": true, "state": {"rates": [0.65568, 0.6400000000000001, 0.6400000000000001, 0.6320640000000002], "skipped": [0, 0, 0, 0], "launched": 18, "hit": 11, "differs": 2, "miss": {"commit": 2, "pending": 2, "inputs": 1, "slots": 1}}}, {"op": "ingested", "commits": [[0, 118, 0, true], [1, 211, 1, true], [2, 329, 1, true]], "state": {"rates": [0.65568, 0.6400000000000001, 0.6400000000000001, 0.6320640000000002], "skipped": [0, 0, 0, 0], "launched": 18, "hit": 11, "differs": 2, "miss": {"commit": 2, "pending": 2, "inputs": 1, "slots": 1}}}, {"op": "take", "asks": [[0, 539, 119, []], [1, 444, 213, [11]], [2, 845, 331, []]], "got": true, "state": {"rates": [0.724544, 0.7120000000000001, 0.7120000000000001, 0.6320640000000002], "skipped": [0, 0, 0, 0], "launched": 18, "hit": 12, "differs": 2, "miss": {"commit": 2, "pending": 2, "inputs": 1, "slots": 1}}}, {"op": "launch", "cands": [[1, 213, 1, 536, [11]], [2, 331, 4, 19, []]], "got": true, "state": {"rates": [0.724544, 0.7120000000000001, 0.7120000000000001, 0.6320640000000002], "skipped": [0, 0, 0, 0], "launched": 19, "hit": 12, "differs": 2, "miss": {"commit": 2, "pending": 2, "inputs": 1, "slots": 1}}}, {"op": "ingested", "commits": [[1, 213, 0, false], [2, 331, 4, true]], "state": {"rates": [0.724544, 0.7120000000000001, 0.7120000000000001, 0.6320640000000002], "skipped": [0, 0, 0, 0], "launched": 19, "hit": 12, "differs": 3, "miss": {"commit": 2, "pending": 2, "inputs": 1, "slots": 1}}}, {"op": "take", "asks": [[1, 153, 214, [11]], [2, 19, 336, []]], "got": false, "state": {"rates": [0.724544, 0.5696000000000001, 0.5696000000000001, 0.6320640000000002], "skipped": [0, 0, 0, 0], "launched": 19, "hit": 12, "differs": 3, "miss": {"commit": 3, "pending": 2, "inputs": 1, "slots": 1}}}, {"op": "launch", "cands": [[0, 119, 1, 195, []], [2, 336, 2, 43, []], [3, 427, 0, 519, [5]]], "got": true, "state": {"rates": [0.724544, 0.5696000000000001, 0.5696000000000001, 0.6320640000000002], "skipped": [0, 0, 0, 0], "launched": 20, "hit": 12, "differs": 3, "miss": {"commit": 3, "pending": 2, "inputs": 1, "slots": 1}}}, {"op": "ingested", "commits": [[0, 119, 1, true], [2, 336, 2, true], [3, 427, 0, true]], "state": {"rates": [0.724544, 0.5696000000000001, 0.5696000000000001, 0.6320640000000002], "skipped": [0, 0, 0, 0], "launched": 20, "hit": 12, "differs": 3, "miss": {"commit": 3, "pending": 2, "inputs": 1, "slots": 1}}}, {"op": "take", "asks": [[0, 195, 121, []], [2, 43, 339, []], [3, 519, 428, [5]]], "got": true, "state": {"rates": [0.7796352, 0.5696000000000001, 0.65568, 0.7056512000000001], "skipped": [0, 0, 0, 0], "launched": 20, "hit": 13, "differs": 3, "miss": {"commit": 3, "pending": 2, "inputs": 1, "slots": 1}}}, {"op": "launch", "cands": [[1, 214, 1, 426, [11]], [3, 428, 0, 401, [5]]], "got": true, "state": {"rates": [0.7796352, 0.5696000000000001, 0.65568, 0.7056512000000001], "skipped": [0, 0, 0, 0], "launched": 21, "hit": 13, "differs": 3, "miss": {"commit": 3, "pending": 2, "inputs": 1, "slots": 1}}}, {"op": "ingested", "commits": [[1, 214, 1, true], [3, 428, 0, true]], "state": {"rates": [0.7796352, 0.5696000000000001, 0.65568, 0.7056512000000001], "skipped": [0, 0, 0, 0], "launched": 21, "hit": 13, "differs": 3, "miss": {"commit": 3, "pending": 2, "inputs": 1, "slots": 1}}}, {"op": "take", "asks": [[1, 426, 216, [11]], [3, 401, 429, [5]]], "got": true, "state": {"rates": [0.7796352, 0.65568, 0.65568, 0.7645209600000001], "skipped": [0, 0, 0, 0], "launched": 21, "hit": 14, "differs": 3, "miss": {"commit": 3, "pending": 2, "inputs": 1, "slots": 1}}}, {"op": "launch", "cands": [[0, 121, 1, 990, []], [1, 216, 3, 224, [11]], [2, 339, 0, 407, []]], "got": true, "state": {"rates": [0.7796352, 0.65568, 0.65568, 0.7645209600000001], "skipped": [0, 0, 0, 0], "launched": 22, "hit": 14, "differs": 3, "miss": {"commit": 3, "pending": 2, "inputs": 1, "slots": 1}}}, {"op": "ingested", "commits": [[0, 121, 0, false], [1, 216, 1, false], [2, 339, 0, true]], "state": {"rates": [0.7796352, 0.65568, 0.65568, 0.7645209600000001], "skipped": [0, 0, 0, 0], "launched": 22, "hit": 14, "differs": 5, "miss": {"commit": 3, "pending": 2, "inputs": 1, "slots": 1}}}, {"op": "take", "asks": [[0, 527, 122, []], [1, 365, 218, [11]], [2, 407, 340, []]], "got": false, "state": {"rates": [0.6237081600000001, 0.524544, 0.524544, 0.7645209600000001], "skipped": [0, 0, 0, 0], "launched": 22, "hit": 14, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 1, "slots": 1}}}, {"op": "launch", "cands": [[3, 429, 2, 529, [5]]], "got": true, "state": {"rates": [0.6237081600000001, 0.524544, 0.524544, 0.7645209600000001], "skipped": [0, 0, 0, 0], "launched": 23, "hit": 14, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 1, "slots": 1}}}, {"op": "ingested", "commits": [[3, 429, 2, true]], "state": {"rates": [0.6237081600000001, 0.524544, 0.524544, 0.7645209600000001], "skipped": [0, 0, 0, 0], "launched": 23, "hit": 14, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 1, "slots": 1}}}, {"op": "take", "asks": [[3, 529, 432, [99]]], "got": false, "state": {"rates": [0.6237081600000001, 0.524544, 0.524544, 0.6116167680000002], "skipped": [0, 0, 0, 0], "launched": 23, "hit": 14, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 2, "slots": 1}}}, {"op": "launch", "cands": [[0, 122, 2, 278, []]], "got": true, "state": {"rates": [0.6237081600000001, 0.524544, 0.524544, 0.6116167680000002], "skipped": [0, 0, 0, 0], "launched": 24, "hit": 14, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 2, "slots": 1}}}, {"op": "ingested", "commits": [[0, 122, 2, true]], "state": {"rates": [0.6237081600000001, 0.524544, 0.524544, 0.6116167680000002], "skipped": [0, 0, 0, 0], "launched": 24, "hit": 14, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 2, "slots": 1}}}, {"op": "take", "asks": [[0, 278, 125, []]], "got": true, "state": {"rates": [0.698966528, 0.524544, 0.524544, 0.6116167680000002], "skipped": [0, 0, 0, 0], "launched": 24, "hit": 15, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 2, "slots": 1}}}, {"op": "launch", "cands": [[0, 125, 4, 941, []], [1, 218, 4, 584, [11]], [2, 340, 3, 717, []]], "got": true, "state": {"rates": [0.698966528, 0.524544, 0.524544, 0.6116167680000002], "skipped": [0, 0, 0, 0], "launched": 25, "hit": 15, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 2, "slots": 1}}}, {"op": "ingested", "commits": [[0, 125, 4, true], [1, 218, 4, true], [2, 340, 3, true]], "state": {"rates": [0.698966528, 0.524544, 0.524544, 0.6116167680000002], "skipped": [0, 0, 0, 0], "launched": 25, "hit": 15, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 2, "slots": 1}}}, {"op": "take", "asks": [[0, 941, 130, []], [2, 717, 344, []]], "got": false, "state": {"rates": [0.5591732224, 0.41963520000000004, 0.41963520000000004, 0.6116167680000002], "skipped": [0, 0, 0, 0], "launched": 25, "hit": 15, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 2, "slots": 2}}}, {"op": "launch", "cands": [[0, 130, 0, 347, []], [1, 223, 4, 427, [11]]], "got": true, "state": {"rates": [0.5591732224, 0.41963520000000004, 0.41963520000000004, 0.6116167680000002], "skipped": [0, 0, 0, 0], "launched": 26, "hit": 15, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 2, "slots": 2}}}, {"op": "ingested", "commits": [[0, 130, 0, true], [1, 223, 4, true]], "state": {"rates": [0.5591732224, 0.41963520000000004, 0.41963520000000004, 0.6116167680000002], "skipped": [0, 0, 0, 0], "launched": 26, "hit": 15, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 2, "slots": 2}}}, {"op": "take", "asks": [[1, 427, 228, [11]]], "got": false, "state": {"rates": [0.44733857792000004, 0.33570816000000003, 0.41963520000000004, 0.6116167680000002], "skipped": [0, 0, 0, 0], "launched": 26, "hit": 15, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 2, "slots": 3}}}, {"op": "launch", "cands": [[1, 228, 2, 643, [11]]], "got": true, "state": {"rates": [0.44733857792000004, 0.33570816000000003, 0.41963520000000004, 0.6116167680000002], "skipped": [0, 0, 0, 0], "launched": 27, "hit": 15, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 2, "slots": 3}}}, {"op": "ingested", "commits": [[1, 228, 2, true]], "state": {"rates": [0.44733857792000004, 0.33570816000000003, 0.41963520000000004, 0.6116167680000002], "skipped": [0, 0, 0, 0], "launched": 27, "hit": 15, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 2, "slots": 3}}}, {"op": "take", "asks": [[1, 643, 231, [11]]], "got": true, "state": {"rates": [0.44733857792000004, 0.468566528, 0.41963520000000004, 0.6116167680000002], "skipped": [0, 0, 0, 0], "launched": 27, "hit": 16, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 2, "slots": 3}}}, {"op": "launch", "cands": [[0, 131, 0, 15, []], [1, 231, 0, 750, [11]]], "got": true, "state": {"rates": [0.44733857792000004, 0.468566528, 0.41963520000000004, 0.6116167680000002], "skipped": [0, 0, 0, 0], "launched": 28, "hit": 16, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 2, "slots": 3}}}, {"op": "ingested", "commits": [[0, 131, 0, true], [1, 231, 0, true]], "state": {"rates": [0.44733857792000004, 0.468566528, 0.41963520000000004, 0.6116167680000002], "skipped": [0, 0, 0, 0], "launched": 28, "hit": 16, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 2, "slots": 3}}}, {"op": "take", "asks": [[0, 15, 132, []], [1, 750, 232, [11]]], "got": true, "state": {"rates": [0.557870862336, 0.5748532224, 0.41963520000000004, 0.6116167680000002], "skipped": [0, 0, 0, 0], "launched": 28, "hit": 17, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 2, "slots": 3}}}, {"op": "launch", "cands": [[0, 132, 1, 350, []], [2, 344, 1, 852, []], [3, 432, 1, 414, [5]]], "got": true, "state": {"rates": [0.557870862336, 0.5748532224, 0.41963520000000004, 0.6116167680000002], "skipped": [0, 0, 0, 0], "launched": 29, "hit": 17, "differs": 5, "miss": {"commit": 4, "pending": 2, "inputs": 2, "slots": 3}}}, {"op": "ingested", "commits": [[0, 132, 0, false], [2, 344, 1, true], [3, 432, 1, true]], "state": {"rates": [0.557870862336, 0.5748532224, 0.41963520000000004, 0.6116167680000002], "skipped": [0, 0, 0, 0], "launched": 29, "hit": 17, "differs": 6, "miss": {"commit": 4, "pending": 2, "inputs": 2, "slots": 3}}}, {"op": "take", "asks": [[0, 900, 133, []], [2, 852, 346, []], [3, 414, 434, [5]]], "got": false, "state": {"rates": [0.44629668986880006, 0.5748532224, 0.33570816000000003, 0.4892934144000002], "skipped": [0, 0, 0, 0], "launched": 29, "hit": 17, "differs": 6, "miss": {"commit": 5, "pending": 2, "inputs": 2, "slots": 3}}}, {"op": "launch", "cands": [[0, 133, 1, 161, []], [1, 232, 2, 456, [11]]], "got": true, "state": {"rates": [0.44629668986880006, 0.5748532224, 0.33570816000000003, 0.4892934144000002], "skipped": [0, 0, 0, 0], "launched": 30, "hit": 17, "differs": 6, "miss": {"commit": 5, "pending": 2, "inputs": 2, "slots": 3}}}, {"op": "ingested", "commits": [[0, 133, 1, true], [1, 232, 2, true]], "state": {"rates": [0.44629668986880006, 0.5748532224, 0.33570816000000003, 0.4892934144000002], "skipped": [0, 0, 0, 0], "launched": 30, "hit": 17, "differs": 6, "miss": {"commit": 5, "pending": 2, "inputs": 2, "slots": 3}}}, {"op": "take", "asks": [[0, 331, 135, []], [1, 456, 235, [11]]], "got": false, "state": {"rates": [0.35703735189504004, 0.45988257792000004, 0.33570816000000003, 0.4892934144000002], "skipped": [0, 0, 0, 0], "launched": 30, "hit": 17, "differs": 6, "miss": {"commit": 5, "pending": 3, "inputs": 2, "slots": 3}}}, {"op": "forget", "slot": 3, "state": {"rates": [0.35703735189504004, 0.45988257792000004, 0.33570816000000003, 1.0], "skipped": [0, 0, 0, 0], "launched": 30, "hit": 17, "differs": 6, "miss": {"commit": 5, "pending": 3, "inputs": 2, "slots": 3}}}, {"op": "launch", "cands": [[0, 135, 4, 794, []], [1, 235, 0, 93, [11]]], "got": true, "state": {"rates": [0.35703735189504004, 0.45988257792000004, 0.33570816000000003, 1.0], "skipped": [0, 0, 0, 0], "launched": 31, "hit": 17, "differs": 6, "miss": {"commit": 5, "pending": 3, "inputs": 2, "slots": 3}}}, {"op": "ingested", "commits": [[0, 135, 4, true], [1, 235, 0, true]], "state": {"rates": [0.35703735189504004, 0.45988257792000004, 0.33570816000000003, 1.0], "skipped": [0, 0, 0, 0], "launched": 31, "hit": 17, "differs": 6, "miss": {"commit": 5, "pending": 3, "inputs": 2, "slots": 3}}}, {"op": "take", "asks": [[0, 794, 140, []], [1, 93, 236, [11]]], "got": true, "state": {"rates": [0.485629881516032, 0.5679060623359999, 0.33570816000000003, 1.0], "skipped": [0, 0, 0, 0], "launched": 31, "hit": 18, "differs": 6, "miss": {"commit": 5, "pending": 3, "inputs": 2, "slots": 3}}}, {"op": "launch", "cands": [[3, 434, 2, 737, [5]]], "got": true, "state": {"rates": [0.485629881516032, 0.5679060623359999, 0.33570816000000003, 1.0], "skipped": [0, 0, 0, 0], "launched": 32, "hit": 18, "differs": 6, "miss": {"commit": 5, "pending": 3, "inputs": 2, "slots": 3}}}, {"op": "ingested", "commits": [[3, 434, 0, false]], "state": {"rates": [0.485629881516032, 0.5679060623359999, 0.33570816000000003, 1.0], "skipped": [0, 0, 0, 0], "launched": 32, "hit": 18, "differs": 7, "miss": {"commit": 5, "pending": 3, "inputs": 2, "slots": 3}}}, {"op": "take", "asks": [[3, 633, 435, [5]]], "got": false, "state": {"rates": [0.485629881516032, 0.5679060623359999, 0.33570816000000003, 0.8], "skipped": [0, 0, 0, 0], "launched": 32, "hit": 18, "differs": 7, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 3}}}, {"op": "clear", "state": {"rates": [0.485629881516032, 0.5679060623359999, 0.33570816000000003, 0.8], "skipped": [0, 0, 0, 0], "launched": 32, "hit": 18, "differs": 7, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 3}}}, {"op": "launch", "cands": [[0, 140, 4, 770, []], [2, 346, 4, 582, []], [3, 435, 0, 846, [5]]], "got": true, "state": {"rates": [0.485629881516032, 0.5679060623359999, 0.33570816000000003, 0.8], "skipped": [0, 0, 0, 0], "launched": 33, "hit": 18, "differs": 7, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 3}}}, {"op": "ingested", "commits": [[0, 140, 4, true], [2, 346, 4, true], [3, 435, 0, true]], "state": {"rates": [0.485629881516032, 0.5679060623359999, 0.33570816000000003, 0.8], "skipped": [0, 0, 0, 0], "launched": 33, "hit": 18, "differs": 7, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 3}}}, {"op": "take", "asks": [[0, 770, 145, []], [3, 385, 436, [5]]], "got": false, "state": {"rates": [0.38850390521282563, 0.5679060623359999, 0.268566528, 0.6400000000000001], "skipped": [0, 0, 0, 0], "launched": 33, "hit": 18, "differs": 7, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 4}}}, {"op": "launch", "cands": [[0, 145, 3, 270, []], [2, 351, 0, 467, []], [3, 436, 0, 766, [5]]], "got": true, "state": {"rates": [0.38850390521282563, 0.5679060623359999, 0.268566528, 0.6400000000000001], "skipped": [0, 0, 1, 0], "launched": 34, "hit": 18, "differs": 7, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 4}}}, {"op": "ingested", "commits": [[0, 145, 0, false], [2, 351, 0, false], [3, 436, 0, true]], "state": {"rates": [0.38850390521282563, 0.5679060623359999, 0.268566528, 0.6400000000000001], "skipped": [0, 0, 1, 0], "launched": 34, "hit": 18, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 4}}}, {"op": "take", "asks": [[0, 258, 146, []], [2, 467, 352, []], [3, 766, 437, [5]]], "got": false, "state": {"rates": [0.3108031241702605, 0.5679060623359999, 0.268566528, 0.5120000000000001], "skipped": [0, 0, 1, 0], "launched": 34, "hit": 18, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 5}}}, {"op": "launch", "cands": [[3, 437, 2, 785, [5]]], "got": true, "state": {"rates": [0.3108031241702605, 0.5679060623359999, 0.268566528, 0.5120000000000001], "skipped": [0, 0, 1, 0], "launched": 35, "hit": 18, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 5}}}, {"op": "ingested", "commits": [[3, 437, 2, true]], "state": {"rates": [0.3108031241702605, 0.5679060623359999, 0.268566528, 0.5120000000000001], "skipped": [0, 0, 1, 0], "launched": 35, "hit": 18, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 5}}}, {"op": "take", "asks": [[3, 785, 440, [5]]], "got": true, "state": {"rates": [0.3108031241702605, 0.5679060623359999, 0.268566528, 0.6096000000000001], "skipped": [0, 0, 1, 0], "launched": 35, "hit": 19, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 5}}}, {"op": "launch", "cands": [[0, 146, 0, 493, []], [2, 352, 0, 497, []], [3, 440, 2, 995, [5]]], "got": true, "state": {"rates": [0.3108031241702605, 0.5679060623359999, 0.268566528, 0.6096000000000001], "skipped": [0, 0, 2, 0], "launched": 36, "hit": 19, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 5}}}, {"op": "ingested", "commits": [[0, 146, 0, true], [2, 352, 0, false], [3, 440, 2, true]], "state": {"rates": [0.3108031241702605, 0.5679060623359999, 0.268566528, 0.6096000000000001], "skipped": [0, 0, 2, 0], "launched": 36, "hit": 19, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 5}}}, {"op": "take", "asks": [[0, 493, 147, []], [2, 497, 353, []], [3, 958, 443, [5]]], "got": false, "state": {"rates": [0.2486424993362084, 0.5679060623359999, 0.268566528, 0.4876800000000001], "skipped": [0, 0, 2, 0], "launched": 36, "hit": 19, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 6}}}, {"op": "launch", "cands": [[3, 443, 2, 396, [5]]], "got": true, "state": {"rates": [0.2486424993362084, 0.5679060623359999, 0.268566528, 0.4876800000000001], "skipped": [0, 0, 2, 0], "launched": 37, "hit": 19, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 6}}}, {"op": "ingested", "commits": [[3, 443, 2, true]], "state": {"rates": [0.2486424993362084, 0.5679060623359999, 0.268566528, 0.4876800000000001], "skipped": [0, 0, 2, 0], "launched": 37, "hit": 19, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 6}}}, {"op": "take", "asks": [[3, 396, 446, [5]]], "got": true, "state": {"rates": [0.2486424993362084, 0.5679060623359999, 0.268566528, 0.590144], "skipped": [0, 0, 2, 0], "launched": 37, "hit": 20, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 6}}}, {"op": "launch", "cands": [[0, 147, 4, 839, []], [1, 236, 4, 286, [11]], [2, 353, 0, 720, []]], "got": true, "state": {"rates": [0.2486424993362084, 0.5679060623359999, 0.268566528, 0.590144], "skipped": [1, 0, 3, 0], "launched": 38, "hit": 20, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 6}}}, {"op": "ingested", "commits": [[0, 147, 4, false], [1, 236, 4, true], [2, 353, 0, false]], "state": {"rates": [0.2486424993362084, 0.5679060623359999, 0.268566528, 0.590144], "skipped": [1, 0, 3, 0], "launched": 38, "hit": 20, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 6}}}, {"op": "take", "asks": [[2, 720, 354, []]], "got": false, "state": {"rates": [0.2486424993362084, 0.45432484986879995, 0.268566528, 0.590144], "skipped": [1, 0, 3, 0], "launched": 38, "hit": 20, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 7}}}, {"op": "launch", "cands": [[1, 241, 0, 860, [11]], [3, 446, 2, 1, [5]]], "got": true, "state": {"rates": [0.2486424993362084, 0.45432484986879995, 0.268566528, 0.590144], "skipped": [1, 0, 3, 0], "launched": 39, "hit": 20, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 7}}}, {"op": "ingested", "commits": [[1, 241, 0, true], [3, 446, 2, true]], "state": {"rates": [0.2486424993362084, 0.45432484986879995, 0.268566528, 0.590144], "skipped": [1, 0, 3, 0], "launched": 39, "hit": 20, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 7}}}, {"op": "take", "asks": [[1, 860, 242, [11]]], "got": false, "state": {"rates": [0.2486424993362084, 0.36345987989504, 0.268566528, 0.4721152], "skipped": [1, 0, 3, 0], "launched": 39, "hit": 20, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 8}}}, {"op": "launch", "cands": [[0, 152, 3, 399, []], [2, 354, 4, 78, []]], "got": false, "state": {"rates": [0.2486424993362084, 0.36345987989504, 0.268566528, 0.4721152], "skipped": [2, 0, 4, 0], "launched": 39, "hit": 20, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 8}}}, {"op": "ingested", "commits": [[0, 152, 3, false], [2, 354, 4, false]], "state": {"rates": [0.2486424993362084, 0.36345987989504, 0.268566528, 0.4721152], "skipped": [2, 0, 4, 0], "launched": 39, "hit": 20, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 8}}}, {"op": "take", "asks": [[0, 399, 156, []], [2, 78, 359, []]], "got": false, "state": {"rates": [0.2486424993362084, 0.36345987989504, 0.268566528, 0.4721152], "skipped": [2, 0, 4, 0], "launched": 39, "hit": 20, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 8}}}, {"op": "launch", "cands": [[2, 359, 2, 194, []], [3, 449, 2, 803, [5]]], "got": true, "state": {"rates": [0.2486424993362084, 0.36345987989504, 0.268566528, 0.4721152], "skipped": [2, 0, 5, 0], "launched": 40, "hit": 20, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 8}}}, {"op": "ingested", "commits": [[2, 359, 0, false], [3, 449, 2, true]], "state": {"rates": [0.2486424993362084, 0.36345987989504, 0.268566528, 0.4721152], "skipped": [2, 0, 5, 0], "launched": 40, "hit": 20, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 8}}}, {"op": "take", "asks": [[2, 935, 360, []], [3, 803, 452, [5]]], "got": false, "state": {"rates": [0.2486424993362084, 0.36345987989504, 0.268566528, 0.37769216000000005], "skipped": [2, 0, 5, 0], "launched": 40, "hit": 20, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 9}}}, {"op": "launch", "cands": [[2, 360, 1, 659, []], [3, 452, 2, 497, [5]]], "got": true, "state": {"rates": [0.2486424993362084, 0.36345987989504, 0.268566528, 0.37769216000000005], "skipped": [2, 0, 6, 0], "launched": 41, "hit": 20, "differs": 8, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 9}}}, {"op": "ingested", "commits": [[2, 360, 1, false], [3, 452, 0, false]], "state": {"rates": [0.2486424993362084, 0.36345987989504, 0.268566528, 0.37769216000000005], "skipped": [2, 0, 6, 0], "launched": 41, "hit": 20, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 9}}}, {"op": "take", "asks": [[2, 659, 362, []], [3, 756, 453, [99]]], "got": false, "state": {"rates": [0.2486424993362084, 0.36345987989504, 0.268566528, 0.30215372800000007], "skipped": [2, 0, 6, 0], "launched": 41, "hit": 20, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 10}}}, {"op": "launch", "cands": [[1, 242, 4, 684, [11]], [2, 362, 3, 122, []], [3, 453, 1, 658, [5]]], "got": true, "state": {"rates": [0.2486424993362084, 0.36345987989504, 0.268566528, 0.30215372800000007], "skipped": [2, 0, 7, 0], "launched": 42, "hit": 20, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 10}}}, {"op": "ingested", "commits": [[1, 242, 4, true], [2, 362, 3, false], [3, 453, 1, true]], "state": {"rates": [0.2486424993362084, 0.36345987989504, 0.268566528, 0.30215372800000007], "skipped": [2, 0, 7, 0], "launched": 42, "hit": 20, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 10}}}, {"op": "take", "asks": [[1, 684, 247, [11]], [2, 122, 366, []], [3, 658, 455, [5]]], "got": false, "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.268566528, 0.24172298240000006], "skipped": [2, 0, 7, 0], "launched": 42, "hit": 20, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 11}}}, {"op": "launch", "cands": [[2, 366, 1, 908, []], [3, 455, 0, 767, [5]]], "got": true, "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.268566528, 0.24172298240000006], "skipped": [2, 0, 0, 1], "launched": 43, "hit": 20, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 11}}}, {"op": "ingested", "commits": [[2, 366, 1, true], [3, 455, 0, false]], "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.268566528, 0.24172298240000006], "skipped": [2, 0, 0, 1], "launched": 43, "hit": 20, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 11}}}, {"op": "take", "asks": [[2, 908, 368, []]], "got": true, "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.4148532224, 0.24172298240000006], "skipped": [2, 0, 0, 1], "launched": 43, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 11}}}, {"op": "launch", "cands": [[1, 247, 4, 541, [11]], [2, 368, 1, 94, []]], "got": true, "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.4148532224, 0.24172298240000006], "skipped": [2, 1, 0, 1], "launched": 44, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 11}}}, {"op": "ingested", "commits": [[1, 247, 4, false], [2, 368, 1, true]], "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.4148532224, 0.24172298240000006], "skipped": [2, 1, 0, 1], "launched": 44, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 11}}}, {"op": "take", "asks": [[1, 541, 252, [11]], [2, 94, 370, []]], "got": false, "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.33188257792000003, 0.24172298240000006], "skipped": [2, 1, 0, 1], "launched": 44, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 12}}}, {"op": "clear", "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.33188257792000003, 0.24172298240000006], "skipped": [2, 1, 0, 1], "launched": 44, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 12}}}, {"op": "launch", "cands": [[0, 156, 0, 400, []], [3, 456, 4, 875, [5]]], "got": false, "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.33188257792000003, 0.24172298240000006], "skipped": [3, 1, 0, 2], "launched": 44, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 12}}}, {"op": "ingested", "commits": [[0, 156, 0, false], [3, 456, 4, false]], "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.33188257792000003, 0.24172298240000006], "skipped": [3, 1, 0, 2], "launched": 44, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 12}}}, {"op": "take", "asks": [[0, 400, 157, []], [3, 875, 461, [5]]], "got": false, "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.33188257792000003, 0.24172298240000006], "skipped": [3, 1, 0, 2], "launched": 44, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 12}}}, {"op": "launch", "cands": [[0, 157, 0, 1, []], [2, 370, 1, 238, []]], "got": true, "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.33188257792000003, 0.24172298240000006], "skipped": [4, 1, 0, 2], "launched": 45, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 12}}}, {"op": "ingested", "commits": [[0, 157, 0, false], [2, 370, 1, true]], "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.33188257792000003, 0.24172298240000006], "skipped": [4, 1, 0, 2], "launched": 45, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 12}}}, {"op": "take", "asks": [[0, 641, 158, []], [2, 238, 372, []]], "got": false, "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.26550606233600005, 0.24172298240000006], "skipped": [4, 1, 0, 2], "launched": 45, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 13}}}, {"op": "clear", "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.26550606233600005, 0.24172298240000006], "skipped": [4, 1, 0, 2], "launched": 45, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 13}}}, {"op": "launch", "cands": [[1, 252, 1, 809, [11]], [2, 372, 4, 1, []], [3, 461, 0, 550, [5]]], "got": false, "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.26550606233600005, 0.24172298240000006], "skipped": [4, 2, 1, 3], "launched": 45, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 13}}}, {"op": "ingested", "commits": [[1, 252, 1, false], [2, 372, 4, false], [3, 461, 0, false]], "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.26550606233600005, 0.24172298240000006], "skipped": [4, 2, 1, 3], "launched": 45, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 13}}}, {"op": "take", "asks": [[1, 809, 254, [11]], [2, 1, 377, []]], "got": false, "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.26550606233600005, 0.24172298240000006], "skipped": [4, 2, 1, 3], "launched": 45, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 13}}}, {"op": "launch", "cands": [[0, 158, 1, 683, []], [1, 254, 3, 947, [11]], [3, 462, 2, 232, [5]]], "got": false, "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.26550606233600005, 0.24172298240000006], "skipped": [5, 3, 1, 4], "launched": 45, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 13}}}, {"op": "ingested", "commits": [[0, 158, 1, false], [1, 254, 3, false], [3, 462, 2, false]], "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.26550606233600005, 0.24172298240000006], "skipped": [5, 3, 1, 4], "launched": 45, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 13}}}, {"op": "take", "asks": [[0, 683, 160, []], [1, 947, 258, [11]], [3, 232, 465, [5]]], "got": false, "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.26550606233600005, 0.24172298240000006], "skipped": [5, 3, 1, 4], "launched": 45, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 13}}}, {"op": "launch", "cands": [[1, 258, 2, 111, [11]], [3, 465, 4, 507, [5]]], "got": false, "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.26550606233600005, 0.24172298240000006], "skipped": [5, 4, 1, 5], "launched": 45, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 13}}}, {"op": "ingested", "commits": [[1, 258, 2, false], [3, 465, 3, false]], "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.26550606233600005, 0.24172298240000006], "skipped": [5, 4, 1, 5], "launched": 45, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 13}}}, {"op": "take", "asks": [[1, 111, 261, [11]], [3, 402, 469, [5]]], "got": false, "state": {"rates": [0.2486424993362084, 0.29076790391603197, 0.26550606233600005, 0.24172298240000006], "skipped": [5, 4, 1, 5], "launched": 45, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 13}}}, {"op": "forget", "slot": 1, "state": {"rates": [0.2486424993362084, 1.0, 0.26550606233600005, 0.24172298240000006], "skipped": [5, 0, 1, 5], "launched": 45, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 13}}}, {"op": "launch", "cands": [[0, 160, 3, 919, []], [1, 261, 2, 750, [11]], [3, 469, 0, 81, [5]]], "got": true, "state": {"rates": [0.2486424993362084, 1.0, 0.26550606233600005, 0.24172298240000006], "skipped": [6, 0, 1, 6], "launched": 46, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 13}}}, {"op": "ingested", "commits": [[0, 160, 2, false], [1, 261, 2, true], [3, 469, 0, false]], "state": {"rates": [0.2486424993362084, 1.0, 0.26550606233600005, 0.24172298240000006], "skipped": [6, 0, 1, 6], "launched": 46, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 13}}}, {"op": "take", "asks": [[0, 478, 163, []], [1, 750, 264, [11]], [3, 81, 470, [5]]], "got": false, "state": {"rates": [0.2486424993362084, 0.8, 0.26550606233600005, 0.24172298240000006], "skipped": [6, 0, 1, 6], "launched": 46, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 14}}}, {"op": "launch", "cands": [[0, 163, 4, 987, []], [3, 470, 1, 389, [5]]], "got": false, "state": {"rates": [0.2486424993362084, 0.8, 0.26550606233600005, 0.24172298240000006], "skipped": [7, 0, 1, 7], "launched": 46, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 14}}}, {"op": "ingested", "commits": [[0, 163, 4, false], [3, 470, 1, false]], "state": {"rates": [0.2486424993362084, 0.8, 0.26550606233600005, 0.24172298240000006], "skipped": [7, 0, 1, 7], "launched": 46, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 14}}}, {"op": "take", "asks": [[0, 987, 168, []], [3, 389, 472, [5]]], "got": false, "state": {"rates": [0.2486424993362084, 0.8, 0.26550606233600005, 0.24172298240000006], "skipped": [7, 0, 1, 7], "launched": 46, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 14}}}, {"op": "launch", "cands": [[0, 168, 3, 253, []], [3, 472, 3, 41, [5]]], "got": true, "state": {"rates": [0.2486424993362084, 0.8, 0.26550606233600005, 0.24172298240000006], "skipped": [0, 0, 1, 0], "launched": 47, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 14}}}, {"op": "ingested", "commits": [[0, 168, 3, true], [3, 472, 3, true]], "state": {"rates": [0.2486424993362084, 0.8, 0.26550606233600005, 0.24172298240000006], "skipped": [0, 0, 1, 0], "launched": 47, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 14}}}, {"op": "take", "asks": [[0, 253, 172, []]], "got": false, "state": {"rates": [0.19891399946896673, 0.8, 0.26550606233600005, 0.19337838592000006], "skipped": [0, 0, 1, 0], "launched": 47, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 15}}}, {"op": "launch", "cands": [[1, 264, 4, 44, [11]], [2, 377, 2, 764, []]], "got": true, "state": {"rates": [0.19891399946896673, 0.8, 0.26550606233600005, 0.19337838592000006], "skipped": [0, 0, 2, 0], "launched": 48, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 15}}}, {"op": "ingested", "commits": [[1, 264, 4, true], [2, 377, 2, false]], "state": {"rates": [0.19891399946896673, 0.8, 0.26550606233600005, 0.19337838592000006], "skipped": [0, 0, 2, 0], "launched": 48, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 15}}}, {"op": "take", "asks": [[1, 44, 269, [11]], [2, 764, 380, []]], "got": false, "state": {"rates": [0.19891399946896673, 0.6400000000000001, 0.26550606233600005, 0.19337838592000006], "skipped": [0, 0, 2, 0], "launched": 48, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 16}}}, {"op": "forget", "slot": 1, "state": {"rates": [0.19891399946896673, 1.0, 0.26550606233600005, 0.19337838592000006], "skipped": [0, 0, 2, 0], "launched": 48, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 16}}}, {"op": "launch", "cands": [[1, 269, 3, 834, [11]], [2, 380, 3, 135, []], [3, 476, 3, 187, [5]]], "got": true, "state": {"rates": [0.19891399946896673, 1.0, 0.26550606233600005, 0.19337838592000006], "skipped": [0, 0, 3, 1], "launched": 49, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 16}}}, {"op": "ingested", "commits": [[1, 269, 3, true], [2, 380, 2, false], [3, 476, 3, false]], "state": {"rates": [0.19891399946896673, 1.0, 0.26550606233600005, 0.19337838592000006], "skipped": [0, 0, 3, 1], "launched": 49, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 16}}}, {"op": "take", "asks": [[1, 834, 273, [11]], [2, 370, 383, []], [3, 187, 480, [5]]], "got": false, "state": {"rates": [0.19891399946896673, 0.8, 0.26550606233600005, 0.19337838592000006], "skipped": [0, 0, 3, 1], "launched": 49, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 17}}}, {"op": "launch", "cands": [[0, 172, 0, 493, []], [2, 383, 4, 557, []]], "got": false, "state": {"rates": [0.19891399946896673, 0.8, 0.26550606233600005, 0.19337838592000006], "skipped": [1, 0, 4, 1], "launched": 49, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 17}}}, {"op": "ingested", "commits": [[0, 172, 0, false], [2, 383, 0, false]], "state": {"rates": [0.19891399946896673, 0.8, 0.26550606233600005, 0.19337838592000006], "skipped": [1, 0, 4, 1], "launched": 49, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 17}}}, {"op": "take", "asks": [[0, 493, 173, []], [2, 510, 384, [99]]], "got": false, "state": {"rates": [0.19891399946896673, 0.8, 0.26550606233600005, 0.19337838592000006], "skipped": [1, 0, 4, 1], "launched": 49, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 17}}}, {"op": "launch", "cands": [[1, 273, 1, 426, [11]]], "got": true, "state": {"rates": [0.19891399946896673, 0.8, 0.26550606233600005, 0.19337838592000006], "skipped": [1, 0, 4, 1], "launched": 50, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 17}}}, {"op": "ingested", "commits": [[1, 273, 1, true]], "state": {"rates": [0.19891399946896673, 0.8, 0.26550606233600005, 0.19337838592000006], "skipped": [1, 0, 4, 1], "launched": 50, "hit": 21, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 17}}}, {"op": "take", "asks": [[1, 426, 275, [11]]], "got": true, "state": {"rates": [0.19891399946896673, 0.8400000000000001, 0.26550606233600005, 0.19337838592000006], "skipped": [1, 0, 4, 1], "launched": 50, "hit": 22, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 17}}}, {"op": "launch", "cands": [[2, 384, 2, 286, []]], "got": false, "state": {"rates": [0.19891399946896673, 0.8400000000000001, 0.26550606233600005, 0.19337838592000006], "skipped": [1, 0, 5, 1], "launched": 50, "hit": 22, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 17}}}, {"op": "ingested", "commits": [[2, 384, 2, false]], "state": {"rates": [0.19891399946896673, 0.8400000000000001, 0.26550606233600005, 0.19337838592000006], "skipped": [1, 0, 5, 1], "launched": 50, "hit": 22, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 17}}}, {"op": "take", "asks": [[2, 286, 387, []]], "got": false, "state": {"rates": [0.19891399946896673, 0.8400000000000001, 0.26550606233600005, 0.19337838592000006], "skipped": [1, 0, 5, 1], "launched": 50, "hit": 22, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 17}}}, {"op": "launch", "cands": [[1, 275, 1, 288, [11]]], "got": true, "state": {"rates": [0.19891399946896673, 0.8400000000000001, 0.26550606233600005, 0.19337838592000006], "skipped": [1, 0, 5, 1], "launched": 51, "hit": 22, "differs": 9, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 17}}}, {"op": "ingested", "commits": [[1, 275, 0, false]], "state": {"rates": [0.19891399946896673, 0.8400000000000001, 0.26550606233600005, 0.19337838592000006], "skipped": [1, 0, 5, 1], "launched": 51, "hit": 22, "differs": 10, "miss": {"commit": 6, "pending": 3, "inputs": 2, "slots": 17}}}, {"op": "take", "asks": [[1, 405, 276, [11]]], "got": false, "state": {"rates": [0.19891399946896673, 0.6720000000000002, 0.26550606233600005, 0.19337838592000006], "skipped": [1, 0, 5, 1], "launched": 51, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 17}}}, {"op": "launch", "cands": [[0, 173, 3, 37, []], [1, 276, 0, 4, [11]], [2, 387, 3, 904, []]], "got": true, "state": {"rates": [0.19891399946896673, 0.6720000000000002, 0.26550606233600005, 0.19337838592000006], "skipped": [2, 0, 6, 1], "launched": 52, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 17}}}, {"op": "ingested", "commits": [[0, 173, 3, false], [1, 276, 0, true], [2, 387, 0, false]], "state": {"rates": [0.19891399946896673, 0.6720000000000002, 0.26550606233600005, 0.19337838592000006], "skipped": [2, 0, 6, 1], "launched": 52, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 17}}}, {"op": "take", "asks": [[0, 37, 177, []], [1, 4, 277, [11]]], "got": false, "state": {"rates": [0.19891399946896673, 0.5376000000000002, 0.26550606233600005, 0.19337838592000006], "skipped": [2, 0, 6, 1], "launched": 52, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 18}}}, {"op": "launch", "cands": [[3, 480, 4, 266, [5]]], "got": false, "state": {"rates": [0.19891399946896673, 0.5376000000000002, 0.26550606233600005, 0.19337838592000006], "skipped": [2, 0, 6, 2], "launched": 52, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 18}}}, {"op": "ingested", "commits": [[3, 480, 4, false]], "state": {"rates": [0.19891399946896673, 0.5376000000000002, 0.26550606233600005, 0.19337838592000006], "skipped": [2, 0, 6, 2], "launched": 52, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 18}}}, {"op": "take", "asks": [[3, 266, 485, [5]]], "got": false, "state": {"rates": [0.19891399946896673, 0.5376000000000002, 0.26550606233600005, 0.19337838592000006], "skipped": [2, 0, 6, 2], "launched": 52, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 18}}}, {"op": "launch", "cands": [[0, 177, 2, 348, []], [1, 277, 1, 45, [11]]], "got": true, "state": {"rates": [0.19891399946896673, 0.5376000000000002, 0.26550606233600005, 0.19337838592000006], "skipped": [3, 0, 6, 2], "launched": 53, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 18}}}, {"op": "ingested", "commits": [[0, 177, 2, false], [1, 277, 1, true]], "state": {"rates": [0.19891399946896673, 0.5376000000000002, 0.26550606233600005, 0.19337838592000006], "skipped": [3, 0, 6, 2], "launched": 53, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 18}}}, {"op": "take", "asks": [[0, 348, 180, []]], "got": false, "state": {"rates": [0.19891399946896673, 0.4300800000000002, 0.26550606233600005, 0.19337838592000006], "skipped": [3, 0, 6, 2], "launched": 53, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 19}}}, {"op": "launch", "cands": [[0, 180, 0, 208, []], [1, 279, 0, 814, [11]], [2, 388, 3, 561, []]], "got": true, "state": {"rates": [0.19891399946896673, 0.4300800000000002, 0.26550606233600005, 0.19337838592000006], "skipped": [4, 0, 7, 2], "launched": 54, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 19}}}, {"op": "ingested", "commits": [[0, 180, 0, false], [1, 279, 0, true], [2, 388, 3, false]], "state": {"rates": [0.19891399946896673, 0.4300800000000002, 0.26550606233600005, 0.19337838592000006], "skipped": [4, 0, 7, 2], "launched": 54, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 19}}}, {"op": "take", "asks": [[0, 208, 181, []], [1, 814, 280, [11]], [2, 561, 392, []]], "got": false, "state": {"rates": [0.19891399946896673, 0.34406400000000015, 0.26550606233600005, 0.19337838592000006], "skipped": [4, 0, 7, 2], "launched": 54, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 20}}}, {"op": "clear", "state": {"rates": [0.19891399946896673, 0.34406400000000015, 0.26550606233600005, 0.19337838592000006], "skipped": [4, 0, 7, 2], "launched": 54, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 20}}}, {"op": "launch", "cands": [[1, 280, 0, 884, [11]], [3, 485, 2, 659, [5]]], "got": true, "state": {"rates": [0.19891399946896673, 0.34406400000000015, 0.26550606233600005, 0.19337838592000006], "skipped": [4, 0, 7, 3], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 20}}}, {"op": "ingested", "commits": [[1, 280, 0, true], [3, 485, 2, false]], "state": {"rates": [0.19891399946896673, 0.34406400000000015, 0.26550606233600005, 0.19337838592000006], "skipped": [4, 0, 7, 3], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 20}}}, {"op": "take", "asks": [[1, 884, 281, [11]], [3, 659, 488, [5]]], "got": false, "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [4, 0, 7, 3], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "launch", "cands": [[0, 181, 0, 52, []], [1, 281, 4, 145, [11]]], "got": false, "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [5, 1, 7, 3], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "ingested", "commits": [[0, 181, 0, false], [1, 281, 0, false]], "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [5, 1, 7, 3], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "take", "asks": [[0, 52, 182, []], [1, 356, 282, [11]]], "got": false, "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [5, 1, 7, 3], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "launch", "cands": [[0, 182, 3, 502, []]], "got": false, "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [6, 1, 7, 3], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "ingested", "commits": [[0, 182, 3, false]], "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [6, 1, 7, 3], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "take", "asks": [[0, 502, 186, []]], "got": false, "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [6, 1, 7, 3], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "launch", "cands": [[3, 488, 2, 54, [5]]], "got": false, "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [6, 1, 7, 4], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "ingested", "commits": [[3, 488, 2, false]], "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [6, 1, 7, 4], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "take", "asks": [[3, 54, 491, [5]]], "got": false, "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [6, 1, 7, 4], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "launch", "cands": [[1, 282, 4, 414, [11]]], "got": false, "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [6, 2, 7, 4], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "ingested", "commits": [[1, 282, 4, false]], "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [6, 2, 7, 4], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "take", "asks": [[1, 414, 287, [11]]], "got": false, "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [6, 2, 7, 4], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "clear", "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [6, 2, 7, 4], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "launch", "cands": [[3, 491, 2, 126, [5]]], "got": false, "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [6, 2, 7, 5], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "ingested", "commits": [[3, 491, 2, false]], "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [6, 2, 7, 5], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "take", "asks": [[3, 126, 494, [5]]], "got": false, "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [6, 2, 7, 5], "launched": 55, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "launch", "cands": [[0, 186, 0, 399, []], [1, 287, 4, 466, [11]], [2, 392, 4, 869, []]], "got": true, "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [7, 3, 0, 5], "launched": 56, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "ingested", "commits": [[0, 186, 0, false], [1, 287, 4, false], [2, 392, 4, true]], "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.26550606233600005, 0.19337838592000006], "skipped": [7, 3, 0, 5], "launched": 56, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 21}}}, {"op": "take", "asks": [[0, 399, 187, []], [1, 466, 292, [11]], [2, 869, 397, []]], "got": false, "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [7, 3, 0, 5], "launched": 56, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 22}}}, {"op": "launch", "cands": [[1, 292, 3, 109, [11]], [3, 494, 0, 131, [5]]], "got": false, "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [7, 4, 0, 6], "launched": 56, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 22}}}, {"op": "ingested", "commits": [[1, 292, 3, false], [3, 494, 0, false]], "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [7, 4, 0, 6], "launched": 56, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 22}}}, {"op": "take", "asks": [[1, 109, 296, [11]]], "got": false, "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [7, 4, 0, 6], "launched": 56, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 22}}}, {"op": "launch", "cands": [[0, 187, 0, 770, []], [2, 397, 4, 916, []], [3, 495, 3, 668, [5]]], "got": true, "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [0, 4, 1, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 22}}}, {"op": "ingested", "commits": [[0, 187, 0, true], [2, 397, 4, false], [3, 495, 3, false]], "state": {"rates": [0.19891399946896673, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [0, 4, 1, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 22}}}, {"op": "take", "asks": [[0, 770, 188, []], [2, 916, 402, []], [3, 668, 499, [5]]], "got": false, "state": {"rates": [0.1591311995751734, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [0, 4, 1, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "launch", "cands": [[0, 188, 2, 625, []]], "got": false, "state": {"rates": [0.1591311995751734, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [1, 4, 1, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "ingested", "commits": [[0, 188, 2, false]], "state": {"rates": [0.1591311995751734, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [1, 4, 1, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "take", "asks": [[0, 625, 191, []]], "got": false, "state": {"rates": [0.1591311995751734, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [1, 4, 1, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "launch", "cands": [[2, 402, 4, 987, []]], "got": false, "state": {"rates": [0.1591311995751734, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [1, 4, 2, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "ingested", "commits": [[2, 402, 1, false]], "state": {"rates": [0.1591311995751734, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [1, 4, 2, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "take", "asks": [[2, 630, 404, []]], "got": false, "state": {"rates": [0.1591311995751734, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [1, 4, 2, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "launch", "cands": [[1, 296, 1, 413, [11]]], "got": false, "state": {"rates": [0.1591311995751734, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [1, 5, 2, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "ingested", "commits": [[1, 296, 1, false]], "state": {"rates": [0.1591311995751734, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [1, 5, 2, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "take", "asks": [[1, 413, 298, [11]]], "got": false, "state": {"rates": [0.1591311995751734, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [1, 5, 2, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "launch", "cands": [[0, 191, 0, 651, []], [2, 404, 2, 989, []]], "got": false, "state": {"rates": [0.1591311995751734, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [2, 5, 3, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "ingested", "commits": [[0, 191, 0, false], [2, 404, 2, false]], "state": {"rates": [0.1591311995751734, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [2, 5, 3, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "take", "asks": [[0, 651, 192, []], [2, 989, 407, []]], "got": false, "state": {"rates": [0.1591311995751734, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [2, 5, 3, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "launch", "cands": [[0, 192, 1, 180, []], [1, 298, 4, 761, [11]]], "got": false, "state": {"rates": [0.1591311995751734, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [3, 6, 3, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "ingested", "commits": [[0, 192, 1, false], [1, 298, 4, false]], "state": {"rates": [0.1591311995751734, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [3, 6, 3, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "take", "asks": [[0, 180, 194, [99]], [1, 761, 303, [11]]], "got": false, "state": {"rates": [0.1591311995751734, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [3, 6, 3, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "forget", "slot": 0, "state": {"rates": [1.0, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [0, 6, 3, 7], "launched": 57, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "launch", "cands": [[1, 303, 4, 372, [11]], [3, 499, 0, 135, [5]]], "got": true, "state": {"rates": [1.0, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [0, 7, 3, 0], "launched": 58, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "ingested", "commits": [[1, 303, 4, false], [3, 499, 0, true]], "state": {"rates": [1.0, 0.27525120000000014, 0.21240484986880004, 0.19337838592000006], "skipped": [0, 7, 3, 0], "launched": 58, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23}}}, {"op": "launch", "cands": [[0, 194, 2, 546, []], [2, 407, 1, 423, []]], "got": true, "state": {"rates": [1.0, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 4, 0], "launched": 59, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23, "unused": 1}}}, {"op": "ingested", "commits": [[0, 194, 2, true], [2, 407, 1, false]], "state": {"rates": [1.0, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 4, 0], "launched": 59, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 23, "unused": 1}}}, {"op": "take", "asks": [[0, 546, 197, []], [2, 423, 409, []]], "got": false, "state": {"rates": [0.8, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 4, 0], "launched": 59, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 24, "unused": 1}}}, {"op": "launch", "cands": [[0, 197, 1, 892, []]], "got": true, "state": {"rates": [0.8, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 4, 0], "launched": 60, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 24, "unused": 1}}}, {"op": "ingested", "commits": [[0, 197, 1, true]], "state": {"rates": [0.8, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 4, 0], "launched": 60, "hit": 22, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 24, "unused": 1}}}, {"op": "take", "asks": [[0, 892, 199, []]], "got": true, "state": {"rates": [0.8400000000000001, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 4, 0], "launched": 60, "hit": 23, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 24, "unused": 1}}}, {"op": "clear", "state": {"rates": [0.8400000000000001, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 4, 0], "launched": 60, "hit": 23, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 24, "unused": 1}}}, {"op": "launch", "cands": [[2, 409, 4, 751, []], [3, 500, 3, 254, [5]]], "got": false, "state": {"rates": [0.8400000000000001, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 5, 1], "launched": 60, "hit": 23, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 24, "unused": 1}}}, {"op": "ingested", "commits": [[2, 409, 4, false], [3, 500, 3, false]], "state": {"rates": [0.8400000000000001, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 5, 1], "launched": 60, "hit": 23, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 24, "unused": 1}}}, {"op": "launch", "cands": [[0, 199, 0, 12, []]], "got": true, "state": {"rates": [0.8400000000000001, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 5, 1], "launched": 61, "hit": 23, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 24, "unused": 1}}}, {"op": "ingested", "commits": [[0, 199, 0, true]], "state": {"rates": [0.8400000000000001, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 5, 1], "launched": 61, "hit": 23, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 24, "unused": 1}}}, {"op": "take", "asks": [[0, 12, 200, []]], "got": true, "state": {"rates": [0.8720000000000001, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 5, 1], "launched": 61, "hit": 24, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 24, "unused": 1}}}, {"op": "launch", "cands": [[0, 200, 4, 316, []], [2, 414, 0, 307, []], [3, 504, 0, 910, [5]]], "got": true, "state": {"rates": [0.8720000000000001, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 6, 2], "launched": 62, "hit": 24, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 24, "unused": 1}}}, {"op": "ingested", "commits": [[0, 200, 4, true], [2, 414, 0, false], [3, 504, 0, false]], "state": {"rates": [0.8720000000000001, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 6, 2], "launched": 62, "hit": 24, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 24, "unused": 1}}}, {"op": "take", "asks": [[0, 316, 205, []], [2, 307, 415, []], [3, 910, 505, [5]]], "got": false, "state": {"rates": [0.6976000000000001, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 6, 2], "launched": 62, "hit": 24, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 25, "unused": 1}}}, {"op": "launch", "cands": [[0, 205, 2, 651, []], [2, 415, 4, 695, []], [3, 505, 3, 702, [5]]], "got": true, "state": {"rates": [0.6976000000000001, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 7, 3], "launched": 63, "hit": 24, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 25, "unused": 1}}}, {"op": "ingested", "commits": [[0, 205, 2, true], [2, 415, 4, false], [3, 505, 3, false]], "state": {"rates": [0.6976000000000001, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 7, 3], "launched": 63, "hit": 24, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 25, "unused": 1}}}, {"op": "take", "asks": [[0, 222, 208, []], [2, 695, 420, []], [3, 702, 509, [5]]], "got": false, "state": {"rates": [0.5580800000000001, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 7, 3], "launched": 63, "hit": 24, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 26, "unused": 1}}}, {"op": "launch", "cands": [[3, 509, 2, 615, [5]]], "got": false, "state": {"rates": [0.5580800000000001, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 7, 4], "launched": 63, "hit": 24, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 26, "unused": 1}}}, {"op": "ingested", "commits": [[3, 509, 2, false]], "state": {"rates": [0.5580800000000001, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 7, 4], "launched": 63, "hit": 24, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 26, "unused": 1}}}, {"op": "take", "asks": [[3, 615, 512, [5]]], "got": false, "state": {"rates": [0.5580800000000001, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 7, 7, 4], "launched": 63, "hit": 24, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 26, "unused": 1}}}, {"op": "launch", "cands": [[0, 208, 0, 447, []], [1, 308, 1, 584, [11]], [3, 512, 2, 808, [5]]], "got": true, "state": {"rates": [0.5580800000000001, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 0, 7, 5], "launched": 64, "hit": 24, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 26, "unused": 1}}}, {"op": "ingested", "commits": [[0, 208, 0, true], [1, 308, 1, true], [3, 512, 2, false]], "state": {"rates": [0.5580800000000001, 0.27525120000000014, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 0, 7, 5], "launched": 64, "hit": 24, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 26, "unused": 1}}}, {"op": "take", "asks": [[0, 447, 209, []], [1, 584, 310, [11]], [3, 808, 515, [5]]], "got": false, "state": {"rates": [0.44646400000000014, 0.2202009600000001, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 0, 7, 5], "launched": 64, "hit": 24, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 27, "unused": 1}}}, {"op": "launch", "cands": [[0, 209, 0, 67, []], [2, 420, 4, 780, []], [3, 515, 2, 204, [5]]], "got": true, "state": {"rates": [0.44646400000000014, 0.2202009600000001, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 0, 0, 6], "launched": 65, "hit": 24, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 27, "unused": 1}}}, {"op": "ingested", "commits": [[0, 209, 0, true], [2, 420, 4, true], [3, 515, 0, false]], "state": {"rates": [0.44646400000000014, 0.2202009600000001, 0.21240484986880004, 0.15470270873600006], "skipped": [0, 0, 0, 6], "launched": 65, "hit": 24, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 27, "unused": 1}}}, {"op": "take", "asks": [[0, 67, 210, []], [2, 780, 425, []]], "got": true, "state": {"rates": [0.5571712000000001, 0.2202009600000001, 0.36992387989504, 0.15470270873600006], "skipped": [0, 0, 0, 6], "launched": 65, "hit": 25, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 27, "unused": 1}}}, {"op": "launch", "cands": [[0, 210, 3, 102, []], [1, 310, 1, 100, [11]], [2, 425, 1, 301, []]], "got": true, "state": {"rates": [0.5571712000000001, 0.2202009600000001, 0.36992387989504, 0.15470270873600006], "skipped": [0, 1, 0, 6], "launched": 66, "hit": 25, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 27, "unused": 1}}}, {"op": "ingested", "commits": [[0, 210, 3, true], [1, 310, 1, false], [2, 425, 1, true]], "state": {"rates": [0.5571712000000001, 0.2202009600000001, 0.36992387989504, 0.15470270873600006], "skipped": [0, 1, 0, 6], "launched": 66, "hit": 25, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 27, "unused": 1}}}, {"op": "take", "asks": [[0, 102, 214, []], [1, 100, 312, [11]], [2, 301, 427, []]], "got": false, "state": {"rates": [0.44573696000000007, 0.2202009600000001, 0.295939103916032, 0.15470270873600006], "skipped": [0, 1, 0, 6], "launched": 66, "hit": 25, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 28, "unused": 1}}}, {"op": "clear", "state": {"rates": [0.44573696000000007, 0.2202009600000001, 0.295939103916032, 0.15470270873600006], "skipped": [0, 1, 0, 6], "launched": 66, "hit": 25, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 28, "unused": 1}}}, {"op": "launch", "cands": [[0, 214, 3, 721, []], [1, 312, 0, 550, [11]]], "got": true, "state": {"rates": [0.44573696000000007, 0.2202009600000001, 0.295939103916032, 0.15470270873600006], "skipped": [0, 2, 0, 6], "launched": 67, "hit": 25, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 28, "unused": 1}}}, {"op": "ingested", "commits": [[0, 214, 3, true], [1, 312, 0, false]], "state": {"rates": [0.44573696000000007, 0.2202009600000001, 0.295939103916032, 0.15470270873600006], "skipped": [0, 2, 0, 6], "launched": 67, "hit": 25, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 28, "unused": 1}}}, {"op": "take", "asks": [[0, 721, 218, []], [1, 550, 313, [11]]], "got": false, "state": {"rates": [0.3565895680000001, 0.2202009600000001, 0.295939103916032, 0.15470270873600006], "skipped": [0, 2, 0, 6], "launched": 67, "hit": 25, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 29, "unused": 1}}}, {"op": "launch", "cands": [[0, 218, 2, 502, []]], "got": true, "state": {"rates": [0.3565895680000001, 0.2202009600000001, 0.295939103916032, 0.15470270873600006], "skipped": [0, 2, 0, 6], "launched": 68, "hit": 25, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 29, "unused": 1}}}, {"op": "ingested", "commits": [[0, 218, 2, true]], "state": {"rates": [0.3565895680000001, 0.2202009600000001, 0.295939103916032, 0.15470270873600006], "skipped": [0, 2, 0, 6], "launched": 68, "hit": 25, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 29, "unused": 1}}}, {"op": "take", "asks": [[0, 502, 221, []]], "got": true, "state": {"rates": [0.48527165440000003, 0.2202009600000001, 0.295939103916032, 0.15470270873600006], "skipped": [0, 2, 0, 6], "launched": 68, "hit": 26, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 29, "unused": 1}}}, {"op": "launch", "cands": [[0, 221, 2, 834, []], [2, 427, 1, 960, []], [3, 516, 1, 510, [5]]], "got": true, "state": {"rates": [0.48527165440000003, 0.2202009600000001, 0.295939103916032, 0.15470270873600006], "skipped": [0, 2, 1, 7], "launched": 69, "hit": 26, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 29, "unused": 1}}}, {"op": "ingested", "commits": [[0, 221, 2, true], [2, 427, 0, false], [3, 516, 1, false]], "state": {"rates": [0.48527165440000003, 0.2202009600000001, 0.295939103916032, 0.15470270873600006], "skipped": [0, 2, 1, 7], "launched": 69, "hit": 26, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 29, "unused": 1}}}, {"op": "take", "asks": [[0, 834, 224, []], [2, 97, 428, []], [3, 510, 518, [5]]], "got": false, "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.15470270873600006], "skipped": [0, 2, 1, 7], "launched": 69, "hit": 26, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 30, "unused": 1}}}, {"op": "launch", "cands": [[2, 428, 1, 310, []]], "got": false, "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.15470270873600006], "skipped": [0, 2, 2, 7], "launched": 69, "hit": 26, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 30, "unused": 1}}}, {"op": "ingested", "commits": [[2, 428, 1, false]], "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.15470270873600006], "skipped": [0, 2, 2, 7], "launched": 69, "hit": 26, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 30, "unused": 1}}}, {"op": "take", "asks": [[2, 310, 430, []]], "got": false, "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.15470270873600006], "skipped": [0, 2, 2, 7], "launched": 69, "hit": 26, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 30, "unused": 1}}}, {"op": "launch", "cands": [[3, 518, 1, 544, [5]]], "got": true, "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.15470270873600006], "skipped": [0, 2, 2, 0], "launched": 70, "hit": 26, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 30, "unused": 1}}}, {"op": "ingested", "commits": [[3, 518, 1, true]], "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.15470270873600006], "skipped": [0, 2, 2, 0], "launched": 70, "hit": 26, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 30, "unused": 1}}}, {"op": "take", "asks": [[3, 544, 520, [5]]], "got": true, "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.3237621669888], "skipped": [0, 2, 2, 0], "launched": 70, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 30, "unused": 1}}}, {"op": "launch", "cands": [[1, 313, 1, 474, [11]], [2, 430, 3, 705, []], [3, 520, 2, 593, [5]]], "got": true, "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.3237621669888], "skipped": [0, 3, 3, 0], "launched": 71, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 30, "unused": 1}}}, {"op": "ingested", "commits": [[1, 313, 1, false], [2, 430, 3, false], [3, 520, 2, true]], "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.3237621669888], "skipped": [0, 3, 3, 0], "launched": 71, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 30, "unused": 1}}}, {"op": "take", "asks": [[1, 474, 315, [11]], [2, 705, 434, []], [3, 740, 523, [5]]], "got": false, "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 3, 3, 0], "launched": 71, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 31, "unused": 1}}}, {"op": "launch", "cands": [[1, 315, 2, 978, [11]]], "got": false, "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 4, 3, 0], "launched": 71, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 31, "unused": 1}}}, {"op": "ingested", "commits": [[1, 315, 2, false]], "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 4, 3, 0], "launched": 71, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 31, "unused": 1}}}, {"op": "take", "asks": [[1, 978, 318, [11]]], "got": false, "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 4, 3, 0], "launched": 71, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 31, "unused": 1}}}, {"op": "launch", "cands": [[1, 318, 3, 154, [11]]], "got": false, "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 5, 3, 0], "launched": 71, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 31, "unused": 1}}}, {"op": "ingested", "commits": [[1, 318, 2, false]], "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 5, 3, 0], "launched": 71, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 31, "unused": 1}}}, {"op": "take", "asks": [[1, 445, 321, [11]]], "got": false, "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 5, 3, 0], "launched": 71, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 31, "unused": 1}}}, {"op": "launch", "cands": [[2, 434, 1, 906, []]], "got": false, "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 5, 4, 0], "launched": 71, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 31, "unused": 1}}}, {"op": "ingested", "commits": [[2, 434, 1, false]], "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 5, 4, 0], "launched": 71, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 31, "unused": 1}}}, {"op": "launch", "cands": [[1, 321, 2, 474, [11]], [2, 436, 0, 145, []]], "got": false, "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 6, 5, 0], "launched": 71, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 31, "unused": 1}}}, {"op": "ingested", "commits": [[1, 321, 2, false], [2, 436, 0, false]], "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 6, 5, 0], "launched": 71, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 31, "unused": 1}}}, {"op": "take", "asks": [[2, 145, 437, []]], "got": false, "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 6, 5, 0], "launched": 71, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 31, "unused": 1}}}, {"op": "launch", "cands": [[0, 224, 1, 656, []], [2, 437, 0, 464, []], [3, 523, 3, 320, [5]]], "got": true, "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 6, 6, 1], "launched": 72, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 31, "unused": 1}}}, {"op": "ingested", "commits": [[0, 224, 1, true], [2, 437, 0, false], [3, 523, 1, false]], "state": {"rates": [0.38821732352000005, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 6, 6, 1], "launched": 72, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 31, "unused": 1}}}, {"op": "take", "asks": [[0, 656, 226, []], [2, 464, 438, []], [3, 419, 525, [5]]], "got": false, "state": {"rates": [0.3105738588160001, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 6, 6, 1], "launched": 72, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 32, "unused": 1}}}, {"op": "launch", "cands": [[2, 438, 0, 398, []]], "got": false, "state": {"rates": [0.3105738588160001, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 6, 7, 1], "launched": 72, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 32, "unused": 1}}}, {"op": "ingested", "commits": [[2, 438, 0, false]], "state": {"rates": [0.3105738588160001, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 6, 7, 1], "launched": 72, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 32, "unused": 1}}}, {"op": "take", "asks": [[2, 398, 439, []]], "got": false, "state": {"rates": [0.3105738588160001, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 6, 7, 1], "launched": 72, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 32, "unused": 1}}}, {"op": "launch", "cands": [[1, 324, 0, 867, [11]], [2, 439, 4, 467, []], [3, 525, 4, 209, [5]]], "got": true, "state": {"rates": [0.3105738588160001, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 7, 0, 2], "launched": 73, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 32, "unused": 1}}}, {"op": "ingested", "commits": [[1, 324, 0, false], [2, 439, 4, true], [3, 525, 4, false]], "state": {"rates": [0.3105738588160001, 0.2202009600000001, 0.295939103916032, 0.25900973359104], "skipped": [0, 7, 0, 2], "launched": 73, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 32, "unused": 1}}}, {"op": "take", "asks": [[1, 867, 325, [11]], [2, 467, 444, []], [3, 209, 530, [5]]], "got": false, "state": {"rates": [0.3105738588160001, 0.2202009600000001, 0.23675128313282562, 0.25900973359104], "skipped": [0, 7, 0, 2], "launched": 73, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 33, "unused": 1}}}, {"op": "launch", "cands": [[0, 226, 2, 391, []], [1, 325, 3, 62, [11]]], "got": true, "state": {"rates": [0.3105738588160001, 0.2202009600000001, 0.23675128313282562, 0.25900973359104], "skipped": [0, 0, 0, 2], "launched": 74, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 33, "unused": 1}}}, {"op": "ingested", "commits": [[0, 226, 2, true], [1, 325, 3, true]], "state": {"rates": [0.3105738588160001, 0.2202009600000001, 0.23675128313282562, 0.25900973359104], "skipped": [0, 0, 0, 2], "launched": 74, "hit": 27, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 33, "unused": 1}}}, {"op": "take", "asks": [[0, 391, 229, []], [1, 62, 329, [11]]], "got": true, "state": {"rates": [0.4484590870528, 0.3761607680000001, 0.23675128313282562, 0.25900973359104], "skipped": [0, 0, 0, 2], "launched": 74, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 33, "unused": 1}}}, {"op": "launch", "cands": [[1, 329, 1, 168, [11]], [2, 444, 1, 951, []], [3, 530, 0, 829, [5]]], "got": true, "state": {"rates": [0.4484590870528, 0.3761607680000001, 0.23675128313282562, 0.25900973359104], "skipped": [0, 0, 1, 3], "launched": 75, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 33, "unused": 1}}}, {"op": "ingested", "commits": [[1, 329, 1, true], [2, 444, 1, false], [3, 530, 0, false]], "state": {"rates": [0.4484590870528, 0.3761607680000001, 0.23675128313282562, 0.25900973359104], "skipped": [0, 0, 1, 3], "launched": 75, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 33, "unused": 1}}}, {"op": "take", "asks": [[1, 168, 331, [11]], [2, 951, 446, []], [3, 778, 531, [5]]], "got": false, "state": {"rates": [0.4484590870528, 0.30092861440000007, 0.23675128313282562, 0.25900973359104], "skipped": [0, 0, 1, 3], "launched": 75, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 34, "unused": 1}}}, {"op": "launch", "cands": [[0, 229, 2, 721, []], [2, 446, 3, 703, []]], "got": true, "state": {"rates": [0.4484590870528, 0.30092861440000007, 0.23675128313282562, 0.25900973359104], "skipped": [0, 0, 2, 3], "launched": 76, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 34, "unused": 1}}}, {"op": "ingested", "commits": [[0, 229, 2, true], [2, 446, 3, false]], "state": {"rates": [0.4484590870528, 0.30092861440000007, 0.23675128313282562, 0.25900973359104], "skipped": [0, 0, 2, 3], "launched": 76, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 34, "unused": 1}}}, {"op": "take", "asks": [[0, 721, 232, []], [2, 703, 450, []]], "got": false, "state": {"rates": [0.35876726964224004, 0.30092861440000007, 0.23675128313282562, 0.25900973359104], "skipped": [0, 0, 2, 3], "launched": 76, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 35, "unused": 1}}}, {"op": "launch", "cands": [[0, 232, 2, 156, []], [2, 450, 2, 874, []]], "got": true, "state": {"rates": [0.35876726964224004, 0.30092861440000007, 0.23675128313282562, 0.25900973359104], "skipped": [0, 0, 3, 3], "launched": 77, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 35, "unused": 1}}}, {"op": "ingested", "commits": [[0, 232, 2, true], [2, 450, 2, false]], "state": {"rates": [0.35876726964224004, 0.30092861440000007, 0.23675128313282562, 0.25900973359104], "skipped": [0, 0, 3, 3], "launched": 77, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 35, "unused": 1}}}, {"op": "take", "asks": [[0, 156, 235, []], [2, 874, 453, []]], "got": false, "state": {"rates": [0.28701381571379203, 0.30092861440000007, 0.23675128313282562, 0.25900973359104], "skipped": [0, 0, 3, 3], "launched": 77, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 36, "unused": 1}}}, {"op": "launch", "cands": [[0, 235, 2, 256, []]], "got": false, "state": {"rates": [0.28701381571379203, 0.30092861440000007, 0.23675128313282562, 0.25900973359104], "skipped": [1, 0, 3, 3], "launched": 77, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 36, "unused": 1}}}, {"op": "ingested", "commits": [[0, 235, 2, false]], "state": {"rates": [0.28701381571379203, 0.30092861440000007, 0.23675128313282562, 0.25900973359104], "skipped": [1, 0, 3, 3], "launched": 77, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 36, "unused": 1}}}, {"op": "take", "asks": [[0, 256, 238, []]], "got": false, "state": {"rates": [0.28701381571379203, 0.30092861440000007, 0.23675128313282562, 0.25900973359104], "skipped": [1, 0, 3, 3], "launched": 77, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 36, "unused": 1}}}, {"op": "launch", "cands": [[1, 331, 3, 810, [11]]], "got": true, "state": {"rates": [0.28701381571379203, 0.30092861440000007, 0.23675128313282562, 0.25900973359104], "skipped": [1, 0, 3, 3], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 36, "unused": 1}}}, {"op": "ingested", "commits": [[1, 331, 3, true]], "state": {"rates": [0.28701381571379203, 0.30092861440000007, 0.23675128313282562, 0.25900973359104], "skipped": [1, 0, 3, 3], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 2, "slots": 36, "unused": 1}}}, {"op": "take", "asks": [[1, 810, 335, [99]]], "got": false, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [1, 0, 3, 3], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "launch", "cands": [[0, 238, 1, 543, []], [1, 335, 0, 759, [11]], [2, 453, 3, 687, []]], "got": false, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [2, 1, 4, 3], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "ingested", "commits": [[0, 238, 0, false], [1, 335, 0, false], [2, 453, 3, false]], "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [2, 1, 4, 3], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "take", "asks": [[0, 504, 239, []], [1, 759, 336, [11]], [2, 687, 457, []]], "got": false, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [2, 1, 4, 3], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "launch", "cands": [[2, 457, 3, 712, []]], "got": false, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [2, 1, 5, 3], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "ingested", "commits": [[2, 457, 3, false]], "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [2, 1, 5, 3], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "take", "asks": [[2, 712, 461, []]], "got": false, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [2, 1, 5, 3], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "launch", "cands": [[0, 239, 0, 21, []], [1, 336, 4, 46, [11]], [3, 531, 2, 828, [5]]], "got": false, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [3, 2, 5, 4], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "ingested", "commits": [[0, 239, 0, false], [1, 336, 4, false], [3, 531, 0, false]], "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [3, 2, 5, 4], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "take", "asks": [[0, 21, 240, []], [1, 46, 341, [11]], [3, 567, 532, [5]]], "got": false, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [3, 2, 5, 4], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "launch", "cands": [[1, 341, 2, 567, [11]], [2, 461, 0, 846, []]], "got": false, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [3, 3, 6, 4], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "ingested", "commits": [[1, 341, 2, false], [2, 461, 0, false]], "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [3, 3, 6, 4], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "take", "asks": [[1, 567, 344, [99]], [2, 846, 462, []]], "got": false, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [3, 3, 6, 4], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "launch", "cands": [[2, 462, 1, 324, []]], "got": false, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [3, 3, 7, 4], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "ingested", "commits": [[2, 462, 1, false]], "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [3, 3, 7, 4], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "take", "asks": [[2, 89, 464, []]], "got": false, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [3, 3, 7, 4], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "clear", "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [3, 3, 7, 4], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "launch", "cands": [[0, 240, 2, 111, []], [1, 344, 0, 47, [11]]], "got": false, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [4, 4, 7, 4], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "ingested", "commits": [[0, 240, 2, false], [1, 344, 0, false]], "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [4, 4, 7, 4], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "take", "asks": [[0, 111, 243, []], [1, 47, 345, [11]]], "got": false, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [4, 4, 7, 4], "launched": 78, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "launch", "cands": [[0, 243, 3, 640, []], [2, 464, 1, 103, []], [3, 532, 1, 890, [5]]], "got": true, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [5, 4, 0, 5], "launched": 79, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "ingested", "commits": [[0, 243, 3, false], [2, 464, 1, true], [3, 532, 0, false]], "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.23675128313282562, 0.25900973359104], "skipped": [5, 4, 0, 5], "launched": 79, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 36, "unused": 1}}}, {"op": "take", "asks": [[0, 640, 247, []], [2, 103, 466, []], [3, 326, 533, [5]]], "got": false, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.1894010265062605, 0.25900973359104], "skipped": [5, 4, 0, 5], "launched": 79, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 37, "unused": 1}}}, {"op": "launch", "cands": [[0, 247, 0, 792, []], [1, 345, 3, 589, [11]], [3, 533, 3, 457, [5]]], "got": false, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.1894010265062605, 0.25900973359104], "skipped": [6, 5, 0, 6], "launched": 79, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 37, "unused": 1}}}, {"op": "ingested", "commits": [[0, 247, 0, false], [1, 345, 3, false], [3, 533, 3, false]], "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.1894010265062605, 0.25900973359104], "skipped": [6, 5, 0, 6], "launched": 79, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 37, "unused": 1}}}, {"op": "take", "asks": [[0, 792, 248, []], [1, 589, 349, [11]], [3, 457, 537, [5]]], "got": false, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.1894010265062605, 0.25900973359104], "skipped": [6, 5, 0, 6], "launched": 79, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 37, "unused": 1}}}, {"op": "launch", "cands": [[0, 248, 0, 223, []]], "got": false, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.1894010265062605, 0.25900973359104], "skipped": [7, 5, 0, 6], "launched": 79, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 37, "unused": 1}}}, {"op": "ingested", "commits": [[0, 248, 0, false]], "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.1894010265062605, 0.25900973359104], "skipped": [7, 5, 0, 6], "launched": 79, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 37, "unused": 1}}}, {"op": "take", "asks": [[0, 223, 249, []]], "got": false, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.1894010265062605, 0.25900973359104], "skipped": [7, 5, 0, 6], "launched": 79, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 37, "unused": 1}}}, {"op": "launch", "cands": [[0, 249, 1, 747, []], [1, 349, 0, 300, [11]], [3, 537, 4, 726, [5]]], "got": true, "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.1894010265062605, 0.25900973359104], "skipped": [0, 6, 0, 7], "launched": 80, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 37, "unused": 1}}}, {"op": "ingested", "commits": [[0, 249, 1, true], [1, 349, 0, false], [3, 537, 0, false]], "state": {"rates": [0.28701381571379203, 0.24074289152000006, 0.1894010265062605, 0.25900973359104], "skipped": [0, 6, 0, 7], "launched": 80, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 37, "unused": 1}}}, {"op": "take", "asks": [[0, 747, 251, []], [1, 300, 350, [11]], [3, 746, 538, [5]]], "got": false, "state": {"rates": [0.22961105257103365, 0.24074289152000006, 0.1894010265062605, 0.25900973359104], "skipped": [0, 6, 0, 7], "launched": 80, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 38, "unused": 1}}}, {"op": "launch", "cands": [[0, 251, 2, 971, []], [1, 350, 4, 745, [11]]], "got": false, "state": {"rates": [0.22961105257103365, 0.24074289152000006, 0.1894010265062605, 0.25900973359104], "skipped": [1, 7, 0, 7], "launched": 80, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 38, "unused": 1}}}, {"op": "ingested", "commits": [[0, 251, 2, false], [1, 350, 4, false]], "state": {"rates": [0.22961105257103365, 0.24074289152000006, 0.1894010265062605, 0.25900973359104], "skipped": [1, 7, 0, 7], "launched": 80, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 38, "unused": 1}}}, {"op": "take", "asks": [[0, 971, 254, []], [1, 745, 355, [11]]], "got": false, "state": {"rates": [0.22961105257103365, 0.24074289152000006, 0.1894010265062605, 0.25900973359104], "skipped": [1, 7, 0, 7], "launched": 80, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 38, "unused": 1}}}, {"op": "launch", "cands": [[2, 466, 2, 299, []], [3, 538, 2, 62, [5]]], "got": true, "state": {"rates": [0.22961105257103365, 0.24074289152000006, 0.1894010265062605, 0.25900973359104], "skipped": [1, 7, 1, 0], "launched": 81, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 38, "unused": 1}}}, {"op": "ingested", "commits": [[2, 466, 2, false], [3, 538, 2, true]], "state": {"rates": [0.22961105257103365, 0.24074289152000006, 0.1894010265062605, 0.25900973359104], "skipped": [1, 7, 1, 0], "launched": 81, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 38, "unused": 1}}}, {"op": "take", "asks": [[2, 299, 469, []], [3, 62, 541, [5]]], "got": false, "state": {"rates": [0.22961105257103365, 0.24074289152000006, 0.1894010265062605, 0.20720778687283203], "skipped": [1, 7, 1, 0], "launched": 81, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 39, "unused": 1}}}, {"op": "launch", "cands": [[0, 254, 3, 701, []], [1, 355, 3, 616, [11]], [3, 541, 1, 826, [5]]], "got": true, "state": {"rates": [0.22961105257103365, 0.24074289152000006, 0.1894010265062605, 0.20720778687283203], "skipped": [2, 0, 1, 1], "launched": 82, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 39, "unused": 1}}}, {"op": "ingested", "commits": [[0, 254, 3, false], [1, 355, 3, true], [3, 541, 1, false]], "state": {"rates": [0.22961105257103365, 0.24074289152000006, 0.1894010265062605, 0.20720778687283203], "skipped": [2, 0, 1, 1], "launched": 82, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 39, "unused": 1}}}, {"op": "take", "asks": [[0, 701, 258, []], [1, 616, 359, [11]], [3, 826, 543, [99]]], "got": false, "state": {"rates": [0.22961105257103365, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [2, 0, 1, 1], "launched": 82, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 40, "unused": 1}}}, {"op": "launch", "cands": [[0, 258, 4, 566, []], [1, 359, 3, 816, [11]], [3, 543, 3, 205, [5]]], "got": false, "state": {"rates": [0.22961105257103365, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [3, 1, 1, 2], "launched": 82, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 40, "unused": 1}}}, {"op": "ingested", "commits": [[0, 258, 4, false], [1, 359, 3, false], [3, 543, 2, false]], "state": {"rates": [0.22961105257103365, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [3, 1, 1, 2], "launched": 82, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 40, "unused": 1}}}, {"op": "take", "asks": [[0, 566, 263, []], [1, 816, 363, [11]], [3, 470, 546, [5]]], "got": false, "state": {"rates": [0.22961105257103365, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [3, 1, 1, 2], "launched": 82, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 40, "unused": 1}}}, {"op": "launch", "cands": [[0, 263, 3, 593, []], [3, 546, 4, 918, [5]]], "got": false, "state": {"rates": [0.22961105257103365, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [4, 1, 1, 3], "launched": 82, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 40, "unused": 1}}}, {"op": "ingested", "commits": [[0, 263, 3, false], [3, 546, 4, false]], "state": {"rates": [0.22961105257103365, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [4, 1, 1, 3], "launched": 82, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 40, "unused": 1}}}, {"op": "take", "asks": [[0, 593, 267, []], [3, 918, 551, [5]]], "got": false, "state": {"rates": [0.22961105257103365, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [4, 1, 1, 3], "launched": 82, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 40, "unused": 1}}}, {"op": "launch", "cands": [[0, 267, 1, 45, []], [1, 363, 3, 383, [11]], [2, 469, 0, 380, []]], "got": false, "state": {"rates": [0.22961105257103365, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [5, 2, 2, 3], "launched": 82, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 40, "unused": 1}}}, {"op": "ingested", "commits": [[0, 267, 1, false], [1, 363, 3, false], [2, 469, 0, false]], "state": {"rates": [0.22961105257103365, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [5, 2, 2, 3], "launched": 82, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 40, "unused": 1}}}, {"op": "take", "asks": [[0, 45, 269, []]], "got": false, "state": {"rates": [0.22961105257103365, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [5, 2, 2, 3], "launched": 82, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 40, "unused": 1}}}, {"op": "launch", "cands": [[0, 269, 2, 947, []], [2, 470, 2, 436, []], [3, 551, 0, 969, [5]]], "got": false, "state": {"rates": [0.22961105257103365, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [6, 2, 3, 4], "launched": 82, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 40, "unused": 1}}}, {"op": "ingested", "commits": [[0, 269, 2, false], [2, 470, 2, false], [3, 551, 0, false]], "state": {"rates": [0.22961105257103365, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [6, 2, 3, 4], "launched": 82, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 40, "unused": 1}}}, {"op": "take", "asks": [[0, 947, 272, []], [2, 436, 473, []]], "got": false, "state": {"rates": [0.22961105257103365, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [6, 2, 3, 4], "launched": 82, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 40, "unused": 1}}}, {"op": "launch", "cands": [[0, 272, 4, 655, []], [1, 367, 3, 944, [11]], [3, 552, 0, 723, [5]]], "got": false, "state": {"rates": [0.22961105257103365, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [7, 3, 3, 5], "launched": 82, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 40, "unused": 1}}}, {"op": "ingested", "commits": [[0, 272, 2, false], [1, 367, 3, false], [3, 552, 0, false]], "state": {"rates": [0.22961105257103365, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [7, 3, 3, 5], "launched": 82, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 40, "unused": 1}}}, {"op": "take", "asks": [[0, 942, 275, []], [1, 944, 371, [11]], [3, 227, 553, [5]]], "got": false, "state": {"rates": [0.22961105257103365, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [7, 3, 3, 5], "launched": 82, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 40, "unused": 1}}}, {"op": "launch", "cands": [[0, 275, 0, 857, []], [2, 473, 0, 264, []]], "got": true, "state": {"rates": [0.22961105257103365, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [0, 3, 4, 5], "launched": 83, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 40, "unused": 1}}}, {"op": "ingested", "commits": [[0, 275, 0, true], [2, 473, 0, false]], "state": {"rates": [0.22961105257103365, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [0, 3, 4, 5], "launched": 83, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 40, "unused": 1}}}, {"op": "take", "asks": [[0, 57, 276, []], [2, 264, 474, []]], "got": false, "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [0, 3, 4, 5], "launched": 83, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "launch", "cands": [[0, 276, 3, 331, []], [2, 474, 2, 263, []], [3, 553, 3, 127, [5]]], "got": false, "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [1, 3, 5, 6], "launched": 83, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "ingested", "commits": [[0, 276, 3, false], [2, 474, 2, false], [3, 553, 3, false]], "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [1, 3, 5, 6], "launched": 83, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "take", "asks": [[0, 331, 280, []], [2, 263, 477, []], [3, 127, 557, [5]]], "got": false, "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [1, 3, 5, 6], "launched": 83, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "launch", "cands": [[1, 371, 0, 948, [11]], [3, 557, 3, 862, [5]]], "got": false, "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [1, 4, 5, 7], "launched": 83, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "ingested", "commits": [[1, 371, 0, false], [3, 557, 3, false]], "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [1, 4, 5, 7], "launched": 83, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "take", "asks": [[1, 948, 372, [11]], [3, 862, 561, [5]]], "got": false, "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [1, 4, 5, 7], "launched": 83, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "launch", "cands": [[3, 561, 4, 910, [5]]], "got": true, "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [1, 4, 5, 0], "launched": 84, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "ingested", "commits": [[3, 561, 4, true]], "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.20720778687283203], "skipped": [1, 4, 5, 0], "launched": 84, "hit": 28, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "take", "asks": [[3, 910, 566, [5]]], "got": true, "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.3657662294982656], "skipped": [1, 4, 5, 0], "launched": 84, "hit": 29, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "launch", "cands": [[1, 372, 1, 266, [11]], [2, 477, 3, 111, []]], "got": false, "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.3657662294982656], "skipped": [1, 5, 6, 0], "launched": 84, "hit": 29, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "ingested", "commits": [[1, 372, 1, false], [2, 477, 0, false]], "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.3657662294982656], "skipped": [1, 5, 6, 0], "launched": 84, "hit": 29, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "take", "asks": [[1, 266, 374, [11]], [2, 947, 478, []]], "got": false, "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.3657662294982656], "skipped": [1, 5, 6, 0], "launched": 84, "hit": 29, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "launch", "cands": [[0, 280, 1, 993, []], [1, 374, 2, 442, [11]]], "got": false, "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.3657662294982656], "skipped": [2, 6, 6, 0], "launched": 84, "hit": 29, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "ingested", "commits": [[0, 280, 0, false], [1, 374, 0, false]], "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.3657662294982656], "skipped": [2, 6, 6, 0], "launched": 84, "hit": 29, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "take", "asks": [[0, 425, 281, []]], "got": false, "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.3657662294982656], "skipped": [2, 6, 6, 0], "launched": 84, "hit": 29, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "launch", "cands": [[1, 375, 0, 452, [11]], [2, 478, 4, 349, []]], "got": false, "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.3657662294982656], "skipped": [2, 7, 7, 0], "launched": 84, "hit": 29, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "ingested", "commits": [[1, 375, 0, false], [2, 478, 4, false]], "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.3657662294982656], "skipped": [2, 7, 7, 0], "launched": 84, "hit": 29, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "take", "asks": [[1, 452, 376, [11]], [2, 349, 483, []]], "got": false, "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.3657662294982656], "skipped": [2, 7, 7, 0], "launched": 84, "hit": 29, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "launch", "cands": [[1, 376, 1, 534, [11]]], "got": true, "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.3657662294982656], "skipped": [2, 0, 7, 0], "launched": 85, "hit": 29, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "ingested", "commits": [[1, 376, 1, true]], "state": {"rates": [0.18368884205682692, 0.19259431321600007, 0.1894010265062605, 0.3657662294982656], "skipped": [2, 0, 7, 0], "launched": 85, "hit": 29, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "take", "asks": [[1, 534, 378, [11]]], "got": true, "state": {"rates": [0.18368884205682692, 0.3540754505728, 0.1894010265062605, 0.3657662294982656], "skipped": [2, 0, 7, 0], "launched": 85, "hit": 30, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "launch", "cands": [[0, 281, 1, 140, []], [1, 378, 4, 685, [11]], [3, 566, 1, 596, [5]]], "got": true, "state": {"rates": [0.18368884205682692, 0.3540754505728, 0.1894010265062605, 0.3657662294982656], "skipped": [3, 0, 7, 0], "launched": 86, "hit": 30, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "ingested", "commits": [[0, 281, 1, false], [1, 378, 4, true], [3, 566, 1, true]], "state": {"rates": [0.18368884205682692, 0.3540754505728, 0.1894010265062605, 0.3657662294982656], "skipped": [3, 0, 7, 0], "launched": 86, "hit": 30, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 41, "unused": 1}}}, {"op": "take", "asks": [[0, 140, 283, []], [1, 685, 383, [11]], [3, 596, 568, [5]]], "got": false, "state": {"rates": [0.18368884205682692, 0.28326036045824, 0.1894010265062605, 0.2926129835986125], "skipped": [3, 0, 7, 0], "launched": 86, "hit": 30, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 42, "unused": 1}}}, {"op": "launch", "cands": [[1, 383, 2, 254, [11]], [2, 483, 1, 576, []]], "got": true, "state": {"rates": [0.18368884205682692, 0.28326036045824, 0.1894010265062605, 0.2926129835986125], "skipped": [3, 1, 0, 0], "launched": 87, "hit": 30, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 42, "unused": 1}}}, {"op": "ingested", "commits": [[1, 383, 2, false], [2, 483, 1, true]], "state": {"rates": [0.18368884205682692, 0.28326036045824, 0.1894010265062605, 0.2926129835986125], "skipped": [3, 1, 0, 0], "launched": 87, "hit": 30, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 42, "unused": 1}}}, {"op": "take", "asks": [[1, 254, 386, [11]]], "got": false, "state": {"rates": [0.18368884205682692, 0.28326036045824, 0.15152082120500843, 0.2926129835986125], "skipped": [3, 1, 0, 0], "launched": 87, "hit": 30, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 43, "unused": 1}}}, {"op": "launch", "cands": [[0, 283, 2, 731, []], [3, 568, 1, 836, [5]]], "got": false, "state": {"rates": [0.18368884205682692, 0.28326036045824, 0.15152082120500843, 0.2926129835986125], "skipped": [4, 1, 0, 1], "launched": 87, "hit": 30, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 43, "unused": 1}}}, {"op": "ingested", "commits": [[0, 283, 2, false], [3, 568, 1, false]], "state": {"rates": [0.18368884205682692, 0.28326036045824, 0.15152082120500843, 0.2926129835986125], "skipped": [4, 1, 0, 1], "launched": 87, "hit": 30, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 43, "unused": 1}}}, {"op": "take", "asks": [[0, 731, 286, []], [3, 836, 570, [5]]], "got": false, "state": {"rates": [0.18368884205682692, 0.28326036045824, 0.15152082120500843, 0.2926129835986125], "skipped": [4, 1, 0, 1], "launched": 87, "hit": 30, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 43, "unused": 1}}}, {"op": "forget", "slot": 1, "state": {"rates": [0.18368884205682692, 1.0, 0.15152082120500843, 0.2926129835986125], "skipped": [4, 0, 0, 1], "launched": 87, "hit": 30, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 43, "unused": 1}}}, {"op": "forget", "slot": 0, "state": {"rates": [1.0, 1.0, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 0, 1], "launched": 87, "hit": 30, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 43, "unused": 1}}}, {"op": "launch", "cands": [[1, 386, 0, 319, [11]]], "got": true, "state": {"rates": [1.0, 1.0, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 0, 1], "launched": 88, "hit": 30, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 43, "unused": 1}}}, {"op": "ingested", "commits": [[1, 386, 0, true]], "state": {"rates": [1.0, 1.0, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 0, 1], "launched": 88, "hit": 30, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 43, "unused": 1}}}, {"op": "take", "asks": [[1, 319, 387, [11]]], "got": true, "state": {"rates": [1.0, 1.0, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 0, 1], "launched": 88, "hit": 31, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 43, "unused": 1}}}, {"op": "launch", "cands": [[2, 485, 0, 857, []]], "got": false, "state": {"rates": [1.0, 1.0, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 1, 1], "launched": 88, "hit": 31, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 43, "unused": 1}}}, {"op": "ingested", "commits": [[2, 485, 0, false]], "state": {"rates": [1.0, 1.0, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 1, 1], "launched": 88, "hit": 31, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 43, "unused": 1}}}, {"op": "take", "asks": [[2, 857, 486, []]], "got": false, "state": {"rates": [1.0, 1.0, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 1, 1], "launched": 88, "hit": 31, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 43, "unused": 1}}}, {"op": "launch", "cands": [[1, 387, 0, 279, [11]]], "got": true, "state": {"rates": [1.0, 1.0, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 1, 1], "launched": 89, "hit": 31, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 43, "unused": 1}}}, {"op": "ingested", "commits": [[1, 387, 0, true]], "state": {"rates": [1.0, 1.0, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 1, 1], "launched": 89, "hit": 31, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 43, "unused": 1}}}, {"op": "take", "asks": [[1, 279, 388, [11]]], "got": true, "state": {"rates": [1.0, 1.0, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 1, 1], "launched": 89, "hit": 32, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 43, "unused": 1}}}, {"op": "launch", "cands": [[1, 388, 4, 232, [11]], [2, 486, 1, 150, []]], "got": true, "state": {"rates": [1.0, 1.0, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 2, 1], "launched": 90, "hit": 32, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 43, "unused": 1}}}, {"op": "ingested", "commits": [[1, 388, 4, true], [2, 486, 1, false]], "state": {"rates": [1.0, 1.0, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 2, 1], "launched": 90, "hit": 32, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 43, "unused": 1}}}, {"op": "take", "asks": [[1, 232, 393, [11]], [2, 150, 488, []]], "got": false, "state": {"rates": [1.0, 0.8, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 2, 1], "launched": 90, "hit": 32, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 44, "unused": 1}}}, {"op": "launch", "cands": [[3, 570, 0, 795, [5]]], "got": false, "state": {"rates": [1.0, 0.8, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 2, 2], "launched": 90, "hit": 32, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 44, "unused": 1}}}, {"op": "ingested", "commits": [[3, 570, 0, false]], "state": {"rates": [1.0, 0.8, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 2, 2], "launched": 90, "hit": 32, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 44, "unused": 1}}}, {"op": "take", "asks": [[3, 795, 571, [5]]], "got": false, "state": {"rates": [1.0, 0.8, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 2, 2], "launched": 90, "hit": 32, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 44, "unused": 1}}}, {"op": "launch", "cands": [[0, 286, 2, 529, []], [1, 393, 1, 980, [11]], [2, 488, 2, 255, []]], "got": true, "state": {"rates": [1.0, 0.8, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 3, 2], "launched": 91, "hit": 32, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 44, "unused": 1}}}, {"op": "ingested", "commits": [[0, 286, 2, true], [1, 393, 1, true], [2, 488, 2, false]], "state": {"rates": [1.0, 0.8, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 3, 2], "launched": 91, "hit": 32, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 44, "unused": 1}}}, {"op": "take", "asks": [[0, 529, 289, []], [1, 980, 395, [11]], [2, 255, 491, []]], "got": false, "state": {"rates": [0.8, 0.6400000000000001, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 3, 2], "launched": 91, "hit": 32, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 45, "unused": 1}}}, {"op": "launch", "cands": [[0, 289, 4, 272, []]], "got": true, "state": {"rates": [0.8, 0.6400000000000001, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 3, 2], "launched": 92, "hit": 32, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 45, "unused": 1}}}, {"op": "ingested", "commits": [[0, 289, 4, true]], "state": {"rates": [0.8, 0.6400000000000001, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 3, 2], "launched": 92, "hit": 32, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 45, "unused": 1}}}, {"op": "take", "asks": [[0, 272, 294, []]], "got": true, "state": {"rates": [0.8400000000000001, 0.6400000000000001, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 3, 2], "launched": 92, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 45, "unused": 1}}}, {"op": "launch", "cands": [[0, 294, 0, 444, []], [2, 491, 1, 973, []]], "got": true, "state": {"rates": [0.8400000000000001, 0.6400000000000001, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 4, 2], "launched": 93, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 45, "unused": 1}}}, {"op": "ingested", "commits": [[0, 294, 0, true], [2, 491, 1, false]], "state": {"rates": [0.8400000000000001, 0.6400000000000001, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 4, 2], "launched": 93, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 45, "unused": 1}}}, {"op": "take", "asks": [[0, 444, 295, []], [2, 973, 493, []]], "got": false, "state": {"rates": [0.6720000000000002, 0.6400000000000001, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 4, 2], "launched": 93, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 46, "unused": 1}}}, {"op": "launch", "cands": [[0, 295, 4, 134, []], [1, 395, 2, 937, [11]], [3, 571, 3, 591, [5]]], "got": true, "state": {"rates": [0.6720000000000002, 0.6400000000000001, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 4, 3], "launched": 94, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 46, "unused": 1}}}, {"op": "ingested", "commits": [[0, 295, 4, true], [1, 395, 2, true], [3, 571, 3, false]], "state": {"rates": [0.6720000000000002, 0.6400000000000001, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 4, 3], "launched": 94, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 46, "unused": 1}}}, {"op": "take", "asks": [[0, 134, 300, []], [1, 937, 398, [11]], [3, 591, 575, [5]]], "got": false, "state": {"rates": [0.5376000000000002, 0.5120000000000001, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 4, 3], "launched": 94, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 47, "unused": 1}}}, {"op": "launch", "cands": [[0, 300, 1, 341, []], [2, 493, 1, 193, []]], "got": true, "state": {"rates": [0.5376000000000002, 0.5120000000000001, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 5, 3], "launched": 95, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 47, "unused": 1}}}, {"op": "ingested", "commits": [[0, 300, 1, true], [2, 493, 1, false]], "state": {"rates": [0.5376000000000002, 0.5120000000000001, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 5, 3], "launched": 95, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 47, "unused": 1}}}, {"op": "take", "asks": [[0, 341, 302, []], [2, 193, 495, []]], "got": false, "state": {"rates": [0.4300800000000002, 0.5120000000000001, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 5, 3], "launched": 95, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 48, "unused": 1}}}, {"op": "launch", "cands": [[1, 398, 0, 790, [11]], [3, 575, 0, 162, [5]]], "got": true, "state": {"rates": [0.4300800000000002, 0.5120000000000001, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 5, 4], "launched": 96, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 48, "unused": 1}}}, {"op": "ingested", "commits": [[1, 398, 0, true], [3, 575, 0, false]], "state": {"rates": [0.4300800000000002, 0.5120000000000001, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 5, 4], "launched": 96, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 48, "unused": 1}}}, {"op": "take", "asks": [[1, 790, 399, [11]], [3, 162, 576, [5]]], "got": false, "state": {"rates": [0.4300800000000002, 0.40960000000000013, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 5, 4], "launched": 96, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 49, "unused": 1}}}, {"op": "launch", "cands": [[1, 399, 2, 143, [11]], [2, 495, 1, 631, []], [3, 576, 4, 870, [5]]], "got": true, "state": {"rates": [0.4300800000000002, 0.40960000000000013, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 6, 5], "launched": 97, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 49, "unused": 1}}}, {"op": "ingested", "commits": [[1, 399, 2, true], [2, 495, 1, false], [3, 576, 4, false]], "state": {"rates": [0.4300800000000002, 0.40960000000000013, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 6, 5], "launched": 97, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 49, "unused": 1}}}, {"op": "take", "asks": [[1, 143, 402, [99]], [2, 631, 497, []], [3, 870, 581, [5]]], "got": false, "state": {"rates": [0.4300800000000002, 0.32768000000000014, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 6, 5], "launched": 97, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 50, "unused": 1}}}, {"op": "launch", "cands": [[0, 302, 4, 153, []], [1, 402, 3, 870, [11]], [2, 497, 2, 893, []]], "got": true, "state": {"rates": [0.4300800000000002, 0.32768000000000014, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 7, 5], "launched": 98, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 50, "unused": 1}}}, {"op": "ingested", "commits": [[0, 302, 4, true], [1, 402, 3, true], [2, 497, 2, false]], "state": {"rates": [0.4300800000000002, 0.32768000000000014, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 7, 5], "launched": 98, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 50, "unused": 1}}}, {"op": "take", "asks": [[0, 153, 307, []], [1, 870, 406, [11]], [2, 893, 500, []]], "got": false, "state": {"rates": [0.34406400000000015, 0.2621440000000001, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 7, 5], "launched": 98, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 51, "unused": 1}}}, {"op": "launch", "cands": [[0, 307, 4, 311, []], [2, 500, 1, 446, []]], "got": true, "state": {"rates": [0.34406400000000015, 0.2621440000000001, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 0, 5], "launched": 99, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 51, "unused": 1}}}, {"op": "ingested", "commits": [[0, 307, 4, true], [2, 500, 1, true]], "state": {"rates": [0.34406400000000015, 0.2621440000000001, 0.15152082120500843, 0.2926129835986125], "skipped": [0, 0, 0, 5], "launched": 99, "hit": 33, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 51, "unused": 1}}}, {"op": "take", "asks": [[0, 311, 312, []], [2, 446, 502, []]], "got": true, "state": {"rates": [0.4752512000000001, 0.2621440000000001, 0.3212166569640067, 0.2926129835986125], "skipped": [0, 0, 0, 5], "launched": 99, "hit": 34, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 51, "unused": 1}}}, {"op": "launch", "cands": [[0, 312, 0, 262, []], [3, 581, 4, 917, [5]]], "got": true, "state": {"rates": [0.4752512000000001, 0.2621440000000001, 0.3212166569640067, 0.2926129835986125], "skipped": [0, 0, 0, 6], "launched": 100, "hit": 34, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 51, "unused": 1}}}, {"op": "ingested", "commits": [[0, 312, 0, true], [3, 581, 2, false]], "state": {"rates": [0.4752512000000001, 0.2621440000000001, 0.3212166569640067, 0.2926129835986125], "skipped": [0, 0, 0, 6], "launched": 100, "hit": 34, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 51, "unused": 1}}}, {"op": "take", "asks": [[0, 529, 313, []], [3, 440, 584, [5]]], "got": false, "state": {"rates": [0.3802009600000001, 0.2621440000000001, 0.3212166569640067, 0.2926129835986125], "skipped": [0, 0, 0, 6], "launched": 100, "hit": 34, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 52, "unused": 1}}}, {"op": "launch", "cands": [[0, 313, 0, 537, []], [1, 406, 1, 101, [11]], [2, 502, 3, 383, []]], "got": true, "state": {"rates": [0.3802009600000001, 0.2621440000000001, 0.3212166569640067, 0.2926129835986125], "skipped": [0, 1, 0, 6], "launched": 101, "hit": 34, "differs": 10, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 52, "unused": 1}}}, {"op": "ingested", "commits": [[0, 313, 0, true], [1, 406, 1, false], [2, 502, 1, false]], "state": {"rates": [0.3802009600000001, 0.2621440000000001, 0.3212166569640067, 0.2926129835986125], "skipped": [0, 1, 0, 6], "launched": 101, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 52, "unused": 1}}}, {"op": "take", "asks": [[0, 498, 314, []], [1, 101, 408, [11]], [2, 764, 504, []]], "got": false, "state": {"rates": [0.3041607680000001, 0.2621440000000001, 0.2569733255712054, 0.2926129835986125], "skipped": [0, 1, 0, 6], "launched": 101, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 53, "unused": 1}}}, {"op": "launch", "cands": [[0, 314, 2, 524, []], [2, 504, 1, 113, []]], "got": true, "state": {"rates": [0.3041607680000001, 0.2621440000000001, 0.2569733255712054, 0.2926129835986125], "skipped": [0, 1, 1, 6], "launched": 102, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 53, "unused": 1}}}, {"op": "ingested", "commits": [[0, 314, 2, true], [2, 504, 1, false]], "state": {"rates": [0.3041607680000001, 0.2621440000000001, 0.2569733255712054, 0.2926129835986125], "skipped": [0, 1, 1, 6], "launched": 102, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 53, "unused": 1}}}, {"op": "take", "asks": [[0, 524, 317, []], [2, 113, 506, []]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.2926129835986125], "skipped": [0, 1, 1, 6], "launched": 102, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 54, "unused": 1}}}, {"op": "launch", "cands": [[1, 408, 3, 186, [11]]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.2926129835986125], "skipped": [0, 2, 1, 6], "launched": 102, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 54, "unused": 1}}}, {"op": "ingested", "commits": [[1, 408, 3, false]], "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.2926129835986125], "skipped": [0, 2, 1, 6], "launched": 102, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 54, "unused": 1}}}, {"op": "take", "asks": [[1, 186, 412, [11]]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.2926129835986125], "skipped": [0, 2, 1, 6], "launched": 102, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 54, "unused": 1}}}, {"op": "launch", "cands": [[0, 317, 0, 806, []], [1, 412, 0, 314, [11]], [2, 506, 4, 4, []]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.2926129835986125], "skipped": [1, 3, 2, 6], "launched": 102, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 54, "unused": 1}}}, {"op": "ingested", "commits": [[0, 317, 0, false], [1, 412, 0, false], [2, 506, 4, false]], "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.2926129835986125], "skipped": [1, 3, 2, 6], "launched": 102, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 54, "unused": 1}}}, {"op": "take", "asks": [[0, 806, 318, []], [1, 314, 413, [11]], [2, 588, 511, []]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.2926129835986125], "skipped": [1, 3, 2, 6], "launched": 102, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 54, "unused": 1}}}, {"op": "launch", "cands": [[1, 413, 4, 777, [11]]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.2926129835986125], "skipped": [1, 4, 2, 6], "launched": 102, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 54, "unused": 1}}}, {"op": "ingested", "commits": [[1, 413, 4, false]], "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.2926129835986125], "skipped": [1, 4, 2, 6], "launched": 102, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 54, "unused": 1}}}, {"op": "clear", "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.2926129835986125], "skipped": [1, 4, 2, 6], "launched": 102, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 54, "unused": 1}}}, {"op": "launch", "cands": [[2, 511, 3, 825, []], [3, 584, 0, 665, [5]]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.2926129835986125], "skipped": [1, 4, 3, 7], "launched": 102, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 54, "unused": 1}}}, {"op": "ingested", "commits": [[2, 511, 3, false], [3, 584, 0, false]], "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.2926129835986125], "skipped": [1, 4, 3, 7], "launched": 102, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 54, "unused": 1}}}, {"op": "take", "asks": [[2, 825, 515, []], [3, 665, 585, [5]]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.2926129835986125], "skipped": [1, 4, 3, 7], "launched": 102, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 54, "unused": 1}}}, {"op": "launch", "cands": [[0, 318, 3, 638, []], [1, 418, 3, 20, [11]], [3, 585, 0, 225, [5]]], "got": true, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.2926129835986125], "skipped": [2, 5, 3, 0], "launched": 103, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 54, "unused": 1}}}, {"op": "ingested", "commits": [[0, 318, 0, false], [1, 418, 3, false], [3, 585, 0, true]], "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.2926129835986125], "skipped": [2, 5, 3, 0], "launched": 103, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 54, "unused": 1}}}, {"op": "take", "asks": [[0, 45, 319, []], [1, 20, 422, [11]], [3, 225, 586, [5]]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.23409038687889], "skipped": [2, 5, 3, 0], "launched": 103, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 55, "unused": 1}}}, {"op": "launch", "cands": [[0, 319, 3, 691, []], [3, 586, 4, 226, [5]]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.23409038687889], "skipped": [3, 5, 3, 1], "launched": 103, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 55, "unused": 1}}}, {"op": "ingested", "commits": [[0, 319, 3, false], [3, 586, 4, false]], "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.23409038687889], "skipped": [3, 5, 3, 1], "launched": 103, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 55, "unused": 1}}}, {"op": "take", "asks": [[0, 691, 323, []], [3, 226, 591, [5]]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.2569733255712054, 0.23409038687889], "skipped": [3, 5, 3, 1], "launched": 103, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 55, "unused": 1}}}, {"op": "forget", "slot": 2, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 1.0, 0.23409038687889], "skipped": [3, 5, 0, 1], "launched": 103, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 55, "unused": 1}}}, {"op": "launch", "cands": [[0, 323, 4, 892, []], [1, 422, 3, 343, [11]]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 1.0, 0.23409038687889], "skipped": [4, 6, 0, 1], "launched": 103, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 55, "unused": 1}}}, {"op": "ingested", "commits": [[0, 323, 4, false], [1, 422, 3, false]], "state": {"rates": [0.2433286144000001, 0.2621440000000001, 1.0, 0.23409038687889], "skipped": [4, 6, 0, 1], "launched": 103, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 55, "unused": 1}}}, {"op": "take", "asks": [[0, 892, 328, []], [1, 343, 426, [11]]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 1.0, 0.23409038687889], "skipped": [4, 6, 0, 1], "launched": 103, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 55, "unused": 1}}}, {"op": "launch", "cands": [[2, 515, 0, 349, []]], "got": true, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 1.0, 0.23409038687889], "skipped": [4, 6, 0, 1], "launched": 104, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 55, "unused": 1}}}, {"op": "ingested", "commits": [[2, 515, 0, true]], "state": {"rates": [0.2433286144000001, 0.2621440000000001, 1.0, 0.23409038687889], "skipped": [4, 6, 0, 1], "launched": 104, "hit": 34, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 55, "unused": 1}}}, {"op": "take", "asks": [[2, 349, 516, []]], "got": true, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 1.0, 0.23409038687889], "skipped": [4, 6, 0, 1], "launched": 104, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 55, "unused": 1}}}, {"op": "launch", "cands": [[3, 591, 3, 856, [5]]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 1.0, 0.23409038687889], "skipped": [4, 6, 0, 2], "launched": 104, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 55, "unused": 1}}}, {"op": "ingested", "commits": [[3, 591, 3, false]], "state": {"rates": [0.2433286144000001, 0.2621440000000001, 1.0, 0.23409038687889], "skipped": [4, 6, 0, 2], "launched": 104, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 55, "unused": 1}}}, {"op": "take", "asks": [[3, 856, 595, [5]]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 1.0, 0.23409038687889], "skipped": [4, 6, 0, 2], "launched": 104, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 55, "unused": 1}}}, {"op": "launch", "cands": [[1, 426, 4, 209, [11]], [2, 516, 1, 878, []], [3, 595, 3, 691, [5]]], "got": true, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 1.0, 0.23409038687889], "skipped": [4, 7, 0, 3], "launched": 105, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 55, "unused": 1}}}, {"op": "ingested", "commits": [[1, 426, 4, false], [2, 516, 1, true], [3, 595, 3, false]], "state": {"rates": [0.2433286144000001, 0.2621440000000001, 1.0, 0.23409038687889], "skipped": [4, 7, 0, 3], "launched": 105, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 55, "unused": 1}}}, {"op": "take", "asks": [[1, 209, 431, [11]], [2, 878, 518, []], [3, 691, 599, [5]]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.8, 0.23409038687889], "skipped": [4, 7, 0, 3], "launched": 105, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 56, "unused": 1}}}, {"op": "launch", "cands": [[0, 328, 4, 148, []], [3, 599, 2, 15, [5]]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.8, 0.23409038687889], "skipped": [5, 7, 0, 4], "launched": 105, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 56, "unused": 1}}}, {"op": "ingested", "commits": [[0, 328, 4, false], [3, 599, 2, false]], "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.8, 0.23409038687889], "skipped": [5, 7, 0, 4], "launched": 105, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 56, "unused": 1}}}, {"op": "take", "asks": [[0, 148, 333, []], [3, 15, 602, [5]]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.8, 0.23409038687889], "skipped": [5, 7, 0, 4], "launched": 105, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 56, "unused": 1}}}, {"op": "launch", "cands": [[0, 333, 2, 90, []], [2, 518, 1, 295, []], [3, 602, 1, 836, [5]]], "got": true, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.8, 0.23409038687889], "skipped": [6, 7, 0, 5], "launched": 106, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 56, "unused": 1}}}, {"op": "ingested", "commits": [[0, 333, 2, false], [2, 518, 1, true], [3, 602, 1, false]], "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.8, 0.23409038687889], "skipped": [6, 7, 0, 5], "launched": 106, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 56, "unused": 1}}}, {"op": "take", "asks": [[0, 90, 336, []], [2, 295, 520, []]], "got": false, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.6400000000000001, 0.23409038687889], "skipped": [6, 7, 0, 5], "launched": 106, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 57, "unused": 1}}}, {"op": "launch", "cands": [[0, 336, 3, 254, []], [1, 431, 3, 360, [11]], [2, 520, 0, 186, []]], "got": true, "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.6400000000000001, 0.23409038687889], "skipped": [7, 0, 0, 5], "launched": 107, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 57, "unused": 1}}}, {"op": "ingested", "commits": [[0, 336, 3, false], [1, 431, 3, true], [2, 520, 0, true]], "state": {"rates": [0.2433286144000001, 0.2621440000000001, 0.6400000000000001, 0.23409038687889], "skipped": [7, 0, 0, 5], "launched": 107, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 57, "unused": 1}}}, {"op": "take", "asks": [[0, 254, 340, []], [1, 360, 435, [11]], [2, 186, 521, []]], "got": false, "state": {"rates": [0.2433286144000001, 0.2097152000000001, 0.5120000000000001, 0.23409038687889], "skipped": [7, 0, 0, 5], "launched": 107, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 58, "unused": 1}}}, {"op": "launch", "cands": [[1, 435, 4, 509, [11]]], "got": false, "state": {"rates": [0.2433286144000001, 0.2097152000000001, 0.5120000000000001, 0.23409038687889], "skipped": [7, 1, 0, 5], "launched": 107, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 58, "unused": 1}}}, {"op": "ingested", "commits": [[1, 435, 4, false]], "state": {"rates": [0.2433286144000001, 0.2097152000000001, 0.5120000000000001, 0.23409038687889], "skipped": [7, 1, 0, 5], "launched": 107, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 58, "unused": 1}}}, {"op": "take", "asks": [[1, 509, 440, [11]]], "got": false, "state": {"rates": [0.2433286144000001, 0.2097152000000001, 0.5120000000000001, 0.23409038687889], "skipped": [7, 1, 0, 5], "launched": 107, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 58, "unused": 1}}}, {"op": "launch", "cands": [[0, 340, 2, 922, []]], "got": true, "state": {"rates": [0.2433286144000001, 0.2097152000000001, 0.5120000000000001, 0.23409038687889], "skipped": [0, 1, 0, 5], "launched": 108, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 58, "unused": 1}}}, {"op": "ingested", "commits": [[0, 340, 2, true]], "state": {"rates": [0.2433286144000001, 0.2097152000000001, 0.5120000000000001, 0.23409038687889], "skipped": [0, 1, 0, 5], "launched": 108, "hit": 35, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 58, "unused": 1}}}, {"op": "take", "asks": [[0, 922, 343, []]], "got": true, "state": {"rates": [0.39466289152, 0.2097152000000001, 0.5120000000000001, 0.23409038687889], "skipped": [0, 1, 0, 5], "launched": 108, "hit": 36, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 58, "unused": 1}}}, {"op": "launch", "cands": [[0, 343, 2, 215, []]], "got": true, "state": {"rates": [0.39466289152, 0.2097152000000001, 0.5120000000000001, 0.23409038687889], "skipped": [0, 1, 0, 5], "launched": 109, "hit": 36, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 58, "unused": 1}}}, {"op": "ingested", "commits": [[0, 343, 2, true]], "state": {"rates": [0.39466289152, 0.2097152000000001, 0.5120000000000001, 0.23409038687889], "skipped": [0, 1, 0, 5], "launched": 109, "hit": 36, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 58, "unused": 1}}}, {"op": "take", "asks": [[0, 215, 346, []]], "got": true, "state": {"rates": [0.515730313216, 0.2097152000000001, 0.5120000000000001, 0.23409038687889], "skipped": [0, 1, 0, 5], "launched": 109, "hit": 37, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 58, "unused": 1}}}, {"op": "launch", "cands": [[0, 346, 2, 969, []], [1, 440, 3, 453, [11]], [3, 604, 2, 708, [5]]], "got": true, "state": {"rates": [0.515730313216, 0.2097152000000001, 0.5120000000000001, 0.23409038687889], "skipped": [0, 2, 0, 6], "launched": 110, "hit": 37, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 58, "unused": 1}}}, {"op": "ingested", "commits": [[0, 346, 2, true], [1, 440, 3, false], [3, 604, 2, false]], "state": {"rates": [0.515730313216, 0.2097152000000001, 0.5120000000000001, 0.23409038687889], "skipped": [0, 2, 0, 6], "launched": 110, "hit": 37, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 58, "unused": 1}}}, {"op": "take", "asks": [[0, 969, 349, []], [1, 453, 444, [11]], [3, 708, 607, [5]]], "got": false, "state": {"rates": [0.41258425057280007, 0.2097152000000001, 0.5120000000000001, 0.23409038687889], "skipped": [0, 2, 0, 6], "launched": 110, "hit": 37, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 59, "unused": 1}}}, {"op": "launch", "cands": [[0, 349, 1, 556, []], [2, 521, 2, 255, []], [3, 607, 0, 172, [5]]], "got": true, "state": {"rates": [0.41258425057280007, 0.2097152000000001, 0.5120000000000001, 0.23409038687889], "skipped": [0, 2, 0, 7], "launched": 111, "hit": 37, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 59, "unused": 1}}}, {"op": "ingested", "commits": [[0, 349, 1, true], [2, 521, 2, true], [3, 607, 0, false]], "state": {"rates": [0.41258425057280007, 0.2097152000000001, 0.5120000000000001, 0.23409038687889], "skipped": [0, 2, 0, 7], "launched": 111, "hit": 37, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 59, "unused": 1}}}, {"op": "take", "asks": [[0, 556, 351, []], [2, 255, 524, []], [3, 172, 608, [5]]], "got": false, "state": {"rates": [0.33006740045824007, 0.2097152000000001, 0.40960000000000013, 0.23409038687889], "skipped": [0, 2, 0, 7], "launched": 111, "hit": 37, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 60, "unused": 1}}}, {"op": "launch", "cands": [[1, 444, 1, 601, [11]], [2, 524, 4, 246, []]], "got": true, "state": {"rates": [0.33006740045824007, 0.2097152000000001, 0.40960000000000013, 0.23409038687889], "skipped": [0, 3, 0, 7], "launched": 112, "hit": 37, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 60, "unused": 1}}}, {"op": "ingested", "commits": [[1, 444, 1, false], [2, 524, 4, true]], "state": {"rates": [0.33006740045824007, 0.2097152000000001, 0.40960000000000013, 0.23409038687889], "skipped": [0, 3, 0, 7], "launched": 112, "hit": 37, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 60, "unused": 1}}}, {"op": "take", "asks": [[1, 601, 446, [11]], [2, 246, 529, []]], "got": false, "state": {"rates": [0.33006740045824007, 0.2097152000000001, 0.32768000000000014, 0.23409038687889], "skipped": [0, 3, 0, 7], "launched": 112, "hit": 37, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 61, "unused": 1}}}, {"op": "launch", "cands": [[0, 351, 2, 12, []]], "got": true, "state": {"rates": [0.33006740045824007, 0.2097152000000001, 0.32768000000000014, 0.23409038687889], "skipped": [0, 3, 0, 7], "launched": 113, "hit": 37, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 61, "unused": 1}}}, {"op": "ingested", "commits": [[0, 351, 2, true]], "state": {"rates": [0.33006740045824007, 0.2097152000000001, 0.32768000000000014, 0.23409038687889], "skipped": [0, 3, 0, 7], "launched": 113, "hit": 37, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 61, "unused": 1}}}, {"op": "take", "asks": [[0, 12, 354, []]], "got": true, "state": {"rates": [0.46405392036659204, 0.2097152000000001, 0.32768000000000014, 0.23409038687889], "skipped": [0, 3, 0, 7], "launched": 113, "hit": 38, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 61, "unused": 1}}}, {"op": "launch", "cands": [[0, 354, 1, 332, []], [1, 446, 3, 479, [11]], [2, 529, 4, 371, []]], "got": true, "state": {"rates": [0.46405392036659204, 0.2097152000000001, 0.32768000000000014, 0.23409038687889], "skipped": [0, 4, 0, 7], "launched": 114, "hit": 38, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 61, "unused": 1}}}, {"op": "ingested", "commits": [[0, 354, 1, true], [1, 446, 3, false], [2, 529, 4, true]], "state": {"rates": [0.46405392036659204, 0.2097152000000001, 0.32768000000000014, 0.23409038687889], "skipped": [0, 4, 0, 7], "launched": 114, "hit": 38, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 61, "unused": 1}}}, {"op": "take", "asks": [[0, 497, 356, []], [1, 577, 450, [11]], [2, 500, 534, []]], "got": false, "state": {"rates": [0.37124313629327366, 0.2097152000000001, 0.2621440000000001, 0.23409038687889], "skipped": [0, 4, 0, 7], "launched": 114, "hit": 38, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 62, "unused": 1}}}, {"op": "launch", "cands": [[2, 534, 0, 659, []]], "got": false, "state": {"rates": [0.37124313629327366, 0.2097152000000001, 0.2621440000000001, 0.23409038687889], "skipped": [0, 4, 1, 7], "launched": 114, "hit": 38, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 62, "unused": 1}}}, {"op": "ingested", "commits": [[2, 534, 0, false]], "state": {"rates": [0.37124313629327366, 0.2097152000000001, 0.2621440000000001, 0.23409038687889], "skipped": [0, 4, 1, 7], "launched": 114, "hit": 38, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 62, "unused": 1}}}, {"op": "take", "asks": [[2, 659, 535, []]], "got": false, "state": {"rates": [0.37124313629327366, 0.2097152000000001, 0.2621440000000001, 0.23409038687889], "skipped": [0, 4, 1, 7], "launched": 114, "hit": 38, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 62, "unused": 1}}}, {"op": "launch", "cands": [[1, 450, 0, 25, [11]]], "got": false, "state": {"rates": [0.37124313629327366, 0.2097152000000001, 0.2621440000000001, 0.23409038687889], "skipped": [0, 5, 1, 7], "launched": 114, "hit": 38, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 62, "unused": 1}}}, {"op": "ingested", "commits": [[1, 450, 0, false]], "state": {"rates": [0.37124313629327366, 0.2097152000000001, 0.2621440000000001, 0.23409038687889], "skipped": [0, 5, 1, 7], "launched": 114, "hit": 38, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 62, "unused": 1}}}, {"op": "take", "asks": [[1, 25, 451, [11]]], "got": false, "state": {"rates": [0.37124313629327366, 0.2097152000000001, 0.2621440000000001, 0.23409038687889], "skipped": [0, 5, 1, 7], "launched": 114, "hit": 38, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 62, "unused": 1}}}, {"op": "launch", "cands": [[0, 356, 4, 334, []], [1, 451, 3, 188, [11]], [3, 608, 2, 327, [5]]], "got": true, "state": {"rates": [0.37124313629327366, 0.2097152000000001, 0.2621440000000001, 0.23409038687889], "skipped": [0, 6, 1, 0], "launched": 115, "hit": 38, "differs": 11, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 62, "unused": 1}}}, {"op": "ingested", "commits": [[0, 356, 4, true], [1, 451, 3, false], [3, 608, 1, false]], "state": {"rates": [0.37124313629327366, 0.2097152000000001, 0.2621440000000001, 0.23409038687889], "skipped": [0, 6, 1, 0], "launched": 115, "hit": 38, "differs": 12, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 62, "unused": 1}}}, {"op": "take", "asks": [[0, 334, 361, []], [1, 188, 455, [11]], [3, 967, 610, [5]]], "got": false, "state": {"rates": [0.29699450903461894, 0.2097152000000001, 0.2621440000000001, 0.18727230950311202], "skipped": [0, 6, 1, 0], "launched": 115, "hit": 38, "differs": 12, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 63, "unused": 1}}}, {"op": "launch", "cands": [[0, 361, 1, 704, []], [1, 455, 1, 167, [11]], [3, 610, 1, 453, [5]]], "got": false, "state": {"rates": [0.29699450903461894, 0.2097152000000001, 0.2621440000000001, 0.18727230950311202], "skipped": [1, 7, 1, 1], "launched": 115, "hit": 38, "differs": 12, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 63, "unused": 1}}}, {"op": "ingested", "commits": [[0, 361, 1, false], [1, 455, 1, false], [3, 610, 1, false]], "state": {"rates": [0.29699450903461894, 0.2097152000000001, 0.2621440000000001, 0.18727230950311202], "skipped": [1, 7, 1, 1], "launched": 115, "hit": 38, "differs": 12, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 63, "unused": 1}}}, {"op": "take", "asks": [[0, 704, 363, []], [3, 453, 612, [5]]], "got": false, "state": {"rates": [0.29699450903461894, 0.2097152000000001, 0.2621440000000001, 0.18727230950311202], "skipped": [1, 7, 1, 1], "launched": 115, "hit": 38, "differs": 12, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 63, "unused": 1}}}, {"op": "launch", "cands": [[0, 363, 4, 727, []]], "got": false, "state": {"rates": [0.29699450903461894, 0.2097152000000001, 0.2621440000000001, 0.18727230950311202], "skipped": [2, 7, 1, 1], "launched": 115, "hit": 38, "differs": 12, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 63, "unused": 1}}}, {"op": "ingested", "commits": [[0, 363, 4, false]], "state": {"rates": [0.29699450903461894, 0.2097152000000001, 0.2621440000000001, 0.18727230950311202], "skipped": [2, 7, 1, 1], "launched": 115, "hit": 38, "differs": 12, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 63, "unused": 1}}}, {"op": "take", "asks": [[0, 727, 368, []]], "got": false, "state": {"rates": [0.29699450903461894, 0.2097152000000001, 0.2621440000000001, 0.18727230950311202], "skipped": [2, 7, 1, 1], "launched": 115, "hit": 38, "differs": 12, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 63, "unused": 1}}}, {"op": "launch", "cands": [[1, 457, 0, 453, [11]], [2, 535, 4, 691, []], [3, 612, 2, 581, [5]]], "got": true, "state": {"rates": [0.29699450903461894, 0.2097152000000001, 0.2621440000000001, 0.18727230950311202], "skipped": [2, 0, 2, 2], "launched": 116, "hit": 38, "differs": 12, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 63, "unused": 1}}}, {"op": "ingested", "commits": [[1, 457, 0, true], [2, 535, 4, false], [3, 612, 2, false]], "state": {"rates": [0.29699450903461894, 0.2097152000000001, 0.2621440000000001, 0.18727230950311202], "skipped": [2, 0, 2, 2], "launched": 116, "hit": 38, "differs": 12, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 63, "unused": 1}}}, {"op": "take", "asks": [[1, 930, 458, [11]], [2, 691, 540, []], [3, 581, 615, [5]]], "got": false, "state": {"rates": [0.29699450903461894, 0.1677721600000001, 0.2621440000000001, 0.18727230950311202], "skipped": [2, 0, 2, 2], "launched": 116, "hit": 38, "differs": 12, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 64, "unused": 1}}}, {"op": "launch", "cands": [[1, 458, 2, 492, [11]], [2, 540, 1, 306, []], [3, 615, 2, 543, [5]]], "got": false, "state": {"rates": [0.29699450903461894, 0.1677721600000001, 0.2621440000000001, 0.18727230950311202], "skipped": [2, 1, 3, 3], "launched": 116, "hit": 38, "differs": 12, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 64, "unused": 1}}}, {"op": "ingested", "commits": [[1, 458, 0, false], [2, 540, 1, false], [3, 615, 2, false]], "state": {"rates": [0.29699450903461894, 0.1677721600000001, 0.2621440000000001, 0.18727230950311202], "skipped": [2, 1, 3, 3], "launched": 116, "hit": 38, "differs": 12, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 64, "unused": 1}}}, {"op": "take", "asks": [[1, 707, 459, [11]], [2, 306, 542, []], [3, 543, 618, [5]]], "got": false, "state": {"rates": [0.29699450903461894, 0.1677721600000001, 0.2621440000000001, 0.18727230950311202], "skipped": [2, 1, 3, 3], "launched": 116, "hit": 38, "differs": 12, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 64, "unused": 1}}}, {"op": "launch", "cands": [[1, 459, 1, 991, [11]]], "got": false, "state": {"rates": [0.29699450903461894, 0.1677721600000001, 0.2621440000000001, 0.18727230950311202], "skipped": [2, 2, 3, 3], "launched": 116, "hit": 38, "differs": 12, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 64, "unused": 1}}}, {"op": "ingested", "commits": [[1, 459, 0, false]], "state": {"rates": [0.29699450903461894, 0.1677721600000001, 0.2621440000000001, 0.18727230950311202], "skipped": [2, 2, 3, 3], "launched": 116, "hit": 38, "differs": 12, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 64, "unused": 1}}}, {"op": "take", "asks": [[1, 857, 460, [11]]], "got": false, "state": {"rates": [0.29699450903461894, 0.1677721600000001, 0.2621440000000001, 0.18727230950311202], "skipped": [2, 2, 3, 3], "launched": 116, "hit": 38, "differs": 12, "miss": {"commit": 7, "pending": 3, "inputs": 3, "slots": 64, "unused": 1}}}]} \ No newline at end of file diff --git a/zig/src/families/deepseek_v41/draft/golden_test.zig b/zig/src/families/deepseek_v41/draft/golden_test.zig new file mode 100644 index 0000000000..df31114ac9 --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/golden_test.zig @@ -0,0 +1,133 @@ +//! Reference decisions replayed (fixtures/golden.json): depth, joint, trees, calibration, rates, bit for bit. +const std = @import("std"); +const Value = std.json.Value; +const costs_mod = @import("costs.zig"); +const depth = @import("depth.zig"); +const joint = @import("joint.zig"); +const tree = @import("tree.zig"); + +const gpa = std.testing.allocator; +const golden = @embedFile("fixtures/golden.json"); + +fn num(v: Value) f64 { + return switch (v) { + .float => |f| f, + .integer => |i| @floatFromInt(i), + else => unreachable, + }; +} + +fn int(v: Value) usize { + return @intCast(v.integer); +} + +fn floats(a: std.mem.Allocator, v: Value) ![]f64 { + const out = try a.alloc(f64, v.array.items.len); + for (out, v.array.items) |*o, x| o.* = num(x); + return out; +} + +fn opt(v: Value) ?u32 { + return if (v == .null) null else @intCast(v.integer); +} + +fn expectFloats(want: Value, got: []const f64) !void { + for (want.array.items, got) |w, g| try std.testing.expectEqual(num(w), g); +} + +fn costs(a: std.mem.Allocator, root: Value) !costs_mod.Costs { + const c = root.object.get("costs").?; + return .{ .verify = try floats(a, c.object.get("verify").?), .draft = num(c.object.get("draft").?), .slot = num(c.object.get("slot").?) }; +} + +test "the cost table equals calib.py's from the same timings" { + var parsed = try std.json.parseFromSlice(Value, gpa, golden, .{}); + defer parsed.deinit(); + var mine = try @import("lanes_test.zig").servedCosts(gpa); + defer mine.deinit(gpa); + const want = try costs(parsed.arena.allocator(), parsed.value); + try std.testing.expectEqualSlices(f64, want.verify, mine.verify); + try std.testing.expectEqual(want.draft, mine.draft); +} + +test "depth traces replay the Python decisions and state" { + var parsed = try std.json.parseFromSlice(Value, gpa, golden, .{}); + defer parsed.deinit(); + const a = parsed.arena.allocator(); + const c = try costs(a, parsed.value); + for (parsed.value.object.get("traces").?.array.items) |trace| { + var d = try depth.Depth.init(gpa, c, 5, .{ .joint = @intCast(trace.object.get("joint").?.integer) }); + defer d.deinit(); + for (trace.object.get("ops").?.array.items) |op| { + const o = op.object; + const kind = o.get("op").?.string; + if (std.mem.eql(u8, kind, "joint")) { + const items = o.get("asks").?.array.items; + const asks = try a.alloc(depth.Depth.Ask, items.len); + for (asks, items) |*x, it| x.* = .{ .slot = @intCast(it.object.get("slot").?.integer), .conf = try floats(a, it.object.get("conf").?), .most = int(it.object.get("most").?), .first = opt(it.object.get("first").?) }; + var others: u64 = 0; + for (o.get("others").?.array.items) |r| others += int(r); + const ks = try a.alloc(usize, asks.len); + try d.chooseJoint(asks, others, o.get("others").?.array.items.len, null, ks); + for (o.get("ks").?.array.items, ks) |w, g| try std.testing.expectEqual(int(w), g); + } else if (std.mem.eql(u8, kind, "tree")) { + const sibs = try floats(a, o.get("sibs").?); + const got = try d.chooseTree(@intCast(o.get("slot").?.integer), try floats(a, o.get("conf").?), int(o.get("most").?), sibs, true, opt(o.get("first").?), .{ .ms = 0.4, .rows = true }, 4); + try std.testing.expectEqual(int(o.get("k").?), got.k); + for (o.get("lens").?.array.items, got.lens[0..sibs.len]) |w, g| try std.testing.expectEqual(int(w), g); + } else if (std.mem.eql(u8, kind, "record")) { + const slot: u32 = @intCast(o.get("slot").?.integer); + const tokens = o.get("tokens").?; + try d.record(slot, int(o.get("rows").?), int(o.get("keep").?), if (tokens == .null) null else int(tokens)); + d.bonus(slot, @intCast(o.get("bonus").?.integer)); + } else if (std.mem.eql(u8, kind, "reset")) { + d.reset(@intCast(o.get("slot").?.integer)); + } else { + try expectFloats(o.get("kept").?, d.cal.kept); + try expectFloats(o.get("prob").?, d.cal.prob); + for (o.get("rates").?.array.items, 0..) |w, s| try std.testing.expectEqual(num(w), try d.rate(@intCast(s))); + for (o.get("joint").?.array.items, 1..) |w, n| try std.testing.expectEqual(num(w), d.jointRate(n)); + try std.testing.expectEqual(@as(u64, @intCast(o.get("zero").?.integer)), d.stats.zero); + for (o.get("reached").?.array.items, o.get("statkept").?.array.items, 0..) |r, k, j| { + try std.testing.expectEqual(@as(u64, @intCast(r.integer)), d.stats.reached[j]); + try std.testing.expectEqual(@as(u64, @intCast(k.integer)), d.stats.kept[j]); + } + } + } + } +} + +test "tree plans, joint allocations and fairness weights equal Python's" { + var parsed = try std.json.parseFromSlice(Value, gpa, golden, .{}); + defer parsed.deinit(); + const a = parsed.arena.allocator(); + const c = try costs(a, parsed.value); + for (parsed.value.object.get("plans").?.array.items) |p| { + const o = p.object; + const got = tree.plan(try floats(a, o.get("qm").?), try floats(a, o.get("sibs").?), try floats(a, o.get("cont").?), c.verify, num(o.get("rate").?), int(o.get("least").?), .{ .ms = num(o.get("dup").?), .rows = true }, int(o.get("rows").?), 16); + try std.testing.expectEqual(int(o.get("k").?), got.k); + for (o.get("lens").?.array.items, 0..) |w, j| try std.testing.expectEqual(int(w), got.lens[j]); + try std.testing.expectEqual(num(o.get("ms").?), got.ms); + try std.testing.expectEqual(num(o.get("e").?), got.expected); + } + for (parsed.value.object.get("allocs").?.array.items) |p| { + const o = p.object; + const qs = o.get("qs").?.array.items; + const slots = try a.alloc(joint.Slot, qs.len); + const w = o.get("w").?; + for (slots, qs, 0..) |*s, q, i| s.* = .{ .qs = try floats(a, q), .least = int(o.get("lo").?.array.items[i]), .top = int(o.get("top").?.array.items[i]), .weight = if (w == .null) 1.0 else num(w.array.items[i]) }; + const ks = try a.alloc(usize, qs.len); + const best = try joint.allocate(gpa, slots, int(o.get("shared").?), c, num(o.get("rate").?), int(o.get("max").?), ks); + for (o.get("ks").?.array.items, ks) |x, k| try std.testing.expectEqual(int(x), k); + try std.testing.expectEqual(num(o.get("best").?), best); + } + for (parsed.value.object.get("weights").?.array.items) |p| { + const o = p.object; + const items = o.get("tpr").?.array.items; + const tpr = try a.alloc(?f64, items.len); + for (tpr, items) |*t, x| t.* = if (x == .null) null else num(x); + const out = try a.alloc(f64, items.len); + joint.weights(tpr, num(o.get("alpha").?), out); + try expectFloats(o.get("w").?, out); + } +} diff --git a/zig/src/families/deepseek_v41/draft/iface.zig b/zig/src/families/deepseek_v41/draft/iface.zig new file mode 100644 index 0000000000..9d1742f7e8 --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/iface.zig @@ -0,0 +1,154 @@ +//! The lanes backend's contract with the model: `Target` (verify windows) and `Pass` (DSpark over its own rings). +const std = @import("std"); +const lanes = @import("lanes"); +const dspark = @import("dspark.zig"); + +/// One slot's verify rows: row 0 the pending token at `start`; row r attends cache + `parents`, keyed at `draws[r]`. +pub const Segment = struct { + slot: u32, + start: u64, + tokens: []const u32, + parents: ?[]const i32, // each row's parent row (row 0: -1); null: a chain + draws: []const u64, + sampling: ?lanes.Sampling, +}; + +/// A slot's last rows' taps (bf16 [rows, 3 x hidden] at `address`); `map`: tree window row -> taps row (branches.zig). +pub const Taps = struct { address: u64, rows: u32, stride: u32, map: ?[]const u32 = null }; + +pub const Target = struct { + ptr: *anyopaque, + vtable: *const VTable, + + pub const VTable = struct { + /// Run the prompt into the slot's state; the keyed choice after its last row (drawn at `draw`). + prefill: *const fn (ptr: *anyopaque, slot: u32, ids: []const u32, sampling: ?lanes.Sampling, draw: u64) anyerror!u32, + /// One forward over every segment into `choices[i][r]`; rows stay until `keep` (never kept: all rows stay). + window: *const fn (ptr: *anyopaque, segments: []const Segment, choices: [][]u32) anyerror!void, + /// Keep the listed rows of the slot's last window (a parent chain); pool, SWA, carries roll back. + keep: *const fn (ptr: *anyopaque, slot: u32, path: []const u32) anyerror!void, + /// The taps of the slot's last window (or its prompt, after prefill). + taps: *const fn (ptr: *anyopaque, slot: u32) Taps, + release: *const fn (ptr: *anyopaque, slot: u32) void, + /// Before `prefill`: the request's max new tokens, for page-based pool admission. null: nothing to do. + admit: ?*const fn (ptr: *anyopaque, slot: u32, max_new: u64) void = null, + /// Prompt in pieces between rounds (kv/sched.zig): `begin`, `piece`s, `finish`. null: whole prompts. + begin: ?*const fn (ptr: *anyopaque, slot: u32, ids: []const u32) anyerror!Begun = null, + piece: ?*const fn (ptr: *anyopaque, slot: u32, ids: []const u32, start: u64, end: u64, save: bool) anyerror!void = null, + finish: ?*const fn (ptr: *anyopaque, slot: u32, ids: []const u32, sampling: ?lanes.Sampling, draw: u64) anyerror!u32 = null, + /// TF_DSV41_TAIL_JOIN: `finish` short of the last token, so a round's shared window runs it. null: `finish`. + tail: ?*const fn (ptr: *anyopaque, slot: u32, ids: []const u32, sampling: ?lanes.Sampling, draw: u64) anyerror!?u32 = null, + /// A round's pieces of several slots (TF_DSV41_PIECE_RUNS) in one forward where possible. null: `piece` each. + pieces: ?*const fn (ptr: *anyopaque, list: []const Piece) anyerror!void = null, + /// A round's finished prompts' CED decoder replays together (TF_DSV41_REPLAY_RUNS). null: nothing. + replays: ?*const fn (ptr: *anyopaque, list: []const Final) anyerror!void = null, + /// Engram reads ahead of the next window (TF_DSV41_ENGRAM_WARM); reads only, window bits are unaffected. + warm: ?*const fn (ptr: *anyopaque, asks: []const Warm) void = null, + }; + + /// A slot's whole prompt whose rows but the last are in (its `finish` / `tail` follows). + pub const Final = struct { slot: u32, ids: []const u32 }; + + /// A slot's prompt `ids` (the whole prompt) rows [start, end) of a round; `save`: its snapshot right after them. + pub const Piece = struct { slot: u32, ids: []const u32, start: u64, end: u64, save: bool }; + + pub fn warm(t: Target, asks: []const Warm) void { + if (t.vtable.warm) |f| f(t.ptr, asks); + } + + pub const Begun = struct { at: u64 = 0, damaged: bool = false }; + + pub fn admit(t: Target, slot: u32, max_new: u64) void { + if (t.vtable.admit) |f| f(t.ptr, slot, max_new); + } + + pub fn pieces(t: Target) bool { + return t.vtable.begin != null and t.vtable.piece != null and t.vtable.finish != null; + } + pub fn begin(t: Target, slot: u32, ids: []const u32) !Begun { + return (t.vtable.begin orelse return error.NoPieces)(t.ptr, slot, ids); + } + pub fn piece(t: Target, slot: u32, ids: []const u32, start: u64, end: u64, save: bool) !void { + return (t.vtable.piece orelse return error.NoPieces)(t.ptr, slot, ids, start, end, save); + } + pub fn finish(t: Target, slot: u32, ids: []const u32, sampling: ?lanes.Sampling, draw: u64) !u32 { + return (t.vtable.finish orelse return error.NoPieces)(t.ptr, slot, ids, sampling, draw); + } + pub fn runReplays(t: Target, list: []const Final) !void { + if (t.vtable.replays) |f| return f(t.ptr, list); + } + pub fn runPieces(t: Target, list: []const Piece) !void { + if (t.vtable.pieces) |f| return f(t.ptr, list); + for (list) |p| try t.piece(p.slot, p.ids, p.start, p.end, p.save); + } + pub fn tail(t: Target, slot: u32, ids: []const u32, sampling: ?lanes.Sampling, draw: u64) !?u32 { + const f = t.vtable.tail orelse return try t.finish(slot, ids, sampling, draw); + return f(t.ptr, slot, ids, sampling, draw); + } + + pub fn prefill(t: Target, slot: u32, ids: []const u32, sampling: ?lanes.Sampling, draw: u64) !u32 { + return t.vtable.prefill(t.ptr, slot, ids, sampling, draw); + } + pub fn window(t: Target, segments: []const Segment, choices: [][]u32) !void { + return t.vtable.window(t.ptr, segments, choices); + } + pub fn keep(t: Target, slot: u32, path: []const u32) !void { + return t.vtable.keep(t.ptr, slot, path); + } + pub fn taps(t: Target, slot: u32) Taps { + return t.vtable.taps(t.ptr, slot); + } + pub fn release(t: Target, slot: u32) void { + t.vtable.release(t.ptr, slot); + } +}; + +/// One slot of a DSpark pass: its anchor (the pending token) at P and the keyed draft parameters. +pub const Ask = struct { slot: u32, anchor: u32, start: u64, params: dspark.Params }; + +/// One slot's pass results: `block` drafts and confidences; for trees also candidates and pre-Markov base logits. +pub const Proposal = struct { drafts: []u32, conf: []f32, cand: ?[]i32 = null, base: ?[]f32 = null }; + +pub const Pass = struct { + ptr: *anyopaque, + vtable: *const VTable, + + pub const VTable = struct { + /// Committed rows into the slot's context rings: `taps` rows `rows[i]` land at positions start + i. + ingest: *const fn (ptr: *anyopaque, slot: u32, start: u64, taps: Taps, rows: []const u32) anyerror!void, + /// One pass over every ask (one launch a block over all slots); results into `out`. + propose: *const fn (ptr: *anyopaque, asks: []const Ask, out: []Proposal) anyerror!void, + /// A new request on the slot: its context restarts at position 0. + reset: *const fn (ptr: *anyopaque, slot: u32) void, + /// The Markov / confidence heads on the host, for trees (null: no trees). + markov: *const fn (ptr: *anyopaque) ?dspark.Markov, + /// Start a pass `collect` finishes (TF_DSV41_SPEC_DRAFT); only collective-free work between. Null: none. + begin: ?*const fn (ptr: *anyopaque, asks: []const Ask) anyerror!void = null, + /// The begun pass's results, in its asks' order (as `propose` gives them). + collect: ?*const fn (ptr: *anyopaque, out: []Proposal) anyerror!void = null, + /// Before a speculated pass's verify window: start from its device pick (`sampled`: keyed). Null: none. + arm: ?*const fn (ptr: *anyopaque, slot: u32, start: u64, rows: u32, sampled: bool) void = null, + }; + + pub fn ingest(p: Pass, slot: u32, start: u64, taps: Taps, rows: []const u32) !void { + return p.vtable.ingest(p.ptr, slot, start, taps, rows); + } + pub fn propose(p: Pass, asks: []const Ask, out: []Proposal) !void { + return p.vtable.propose(p.ptr, asks, out); + } + pub fn reset(p: Pass, slot: u32) void { + p.vtable.reset(p.ptr, slot); + } + pub fn markov(p: Pass) ?dspark.Markov { + return p.vtable.markov(p.ptr); + } +}; + +/// Which committed rows an ingest writes: only the last `window` (older ones are never read). +pub fn ingestSpan(n: usize, window: usize) struct { skip: usize, count: usize } { + const skip = n -| window; + return .{ .skip = skip, .count = n - skip }; +} + +/// An Engram warm ask (Target.warm). +pub const Warm = struct { slot: u32, start: u64, ids: []const u32 }; diff --git a/zig/src/families/deepseek_v41/draft/joint.zig b/zig/src/families/deepseek_v41/draft/joint.zig new file mode 100644 index 0000000000..2651049855 --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/joint.zig @@ -0,0 +1,210 @@ +//! A shared round's DSpark depths chosen together: the m best marginal gains, scanned against cost by total rows. +const std = @import("std"); +const Allocator = std.mem.Allocator; +const Costs = @import("costs.zig").Costs; + +extern "c" fn pow(x: f64, y: f64) f64; + +pub const eps = 1e-12; +pub const fair = 0.5; // TF_DSV41_DEPTH_FAIR (mode 2's alpha) +pub const host_ms = 3.0; // TF_DSV41_DEPTH_HOST_MS (mode 2) +pub const tpr0 = 2.0; // a slot's tokens a round before its first commit + +/// CPython's sum() of floats (3.12+): Neumaier's compensated sum, the compensation added at the end. +pub const PySum = struct { + sum: f64 = 0.0, + c: f64 = 0.0, + + pub fn add(p: *PySum, x: f64) void { + const t = p.sum + x; + p.c += if (@abs(p.sum) >= @abs(x)) (p.sum - t) + x else (x - t) + p.sum; + p.sum = t; + } + + pub fn total(p: PySum) f64 { + return if (p.c != 0.0 and std.math.isFinite(p.c)) p.sum + p.c else p.sum; + } +}; + +/// Each slot's weight (mode 2): (mean tpr / tpr_s) ^ alpha, scaled so sum w_s tpr_s = sum tpr_s. Writes `out`. +pub fn weights(tpr: []const ?f64, alpha: f64, out: []f64) void { + if (tpr.len == 0 or alpha <= 0.0) { + @memset(out, 1.0); + return; + } + var total: PySum = .{}; + for (tpr) |x| total.add(@max(x orelse tpr0, eps)); + const mean = total.total() / @as(f64, @floatFromInt(tpr.len)); + var dot: PySum = .{}; + for (tpr, out) |x, *w| { + const t = @max(x orelse tpr0, eps); + w.* = pow(mean / t, alpha); + dot.add(w.* * t); + } + const z = total.total() / dot.total(); + for (out) |*w| w.* *= z; +} + +/// Marginal expected tokens of drafts least+1 .. top: q_1 ... q_{j+1} for j = least .. top - 1, into `out`. +pub fn gains(qs: []const f64, least: usize, top: usize, out: *std.ArrayList(f64), gpa: Allocator) !void { + var run: f64 = 1.0; + for (0..top) |j| { + run *= qs[j]; + if (j >= least) try out.append(gpa, run); + } +} + +pub const Slot = struct { qs: []const f64, least: usize, top: usize, weight: f64 = 1.0 }; + +const Cand = struct { neg: f64, slot: u32, pos: u32 }; + +fn before(_: void, a: Cand, b: Cand) bool { + if (a.neg != b.neg) return a.neg < b.neg; + if (a.slot != b.slot) return a.slot < b.slot; + return a.pos < b.pos; +} + +/// Each slot's k into `ks` and the objective sum w E - rate C(R) without constants. `shared`: the round's other rows. +pub fn allocate(gpa: Allocator, slots: []const Slot, shared: u64, costs: Costs, rate: f64, max_rows: u64, ks: []usize) !f64 { + var base: u64 = shared; + for (slots, ks) |s, *k| { + k.* = @min(s.least, s.top); + base += 1 + k.*; + } + var cands: std.ArrayList(Cand) = .empty; + defer cands.deinit(gpa); + var g: std.ArrayList(f64) = .empty; + defer g.deinit(gpa); + for (slots, ks, 0..) |s, lo, i| { + g.clearRetainingCapacity(); + try gains(s.qs, lo, @max(lo, s.top), &g, gpa); + for (g.items, 0..) |x, j| try cands.append(gpa, .{ .neg = -x * s.weight, .slot = @intCast(i), .pos = @intCast(lo + j) }); + } + std.mem.sort(Cand, cands.items, {}, before); + const room = if (max_rows > base) max_rows - base else 0; + var best_m: usize = 0; + var best = -rate * costs.rowsMs(@intCast(base)); + var acc: f64 = 0.0; + for (0..@min(room, cands.items.len)) |i| { + acc += -cands.items[i].neg; + const v = acc - rate * costs.rowsMs(@intCast(base + i + 1)); + if (v > best + eps) { + best_m = i + 1; + best = v; + } + } + for (cands.items[0..best_m]) |c| ks[c.slot] += 1; + return best; +} + +/// Expand an allocation only when expected tokens / full round ms improves 1%; scans all row counts (nonconvex). +pub fn expand(gpa: Allocator, slots: []const Slot, shared: u64, costs: Costs, fixed_ms: f64, max_rows: u64, priced_rows: ?u64, ks: []usize) !usize { + var rows: u64 = shared; + var tokens: f64 = 0; + var cands: std.ArrayList(Cand) = .empty; + defer cands.deinit(gpa); + for (slots, ks, 0..) |s, k, i| { + rows += 1 + k; + var survival: f64 = 1; + tokens += 1; + for (s.qs[0..s.top], 0..) |q, j| { + survival *= q; + if (j < k) tokens += survival else try cands.append(gpa, .{ .neg = -survival, .slot = @intCast(i), .pos = @intCast(j) }); + } + } + if (rows >= max_rows or cands.items.len == 0) return 0; + std.mem.sort(Cand, cands.items, {}, before); + const baseline = tokens / (costs.rowsMs(@intCast(priced_rows orelse rows)) + fixed_ms); + var best = baseline; + var count: usize = 0; + for (cands.items[0..@min(cands.items.len, max_rows - rows)], 0..) |c, i| { + tokens -= c.neg; + const ratio = tokens / (costs.rowsMs(@intCast(priced_rows orelse (rows + i + 1))) + fixed_ms); + if (ratio > best + eps) { + best = ratio; + count = i + 1; + } + } + if (best < baseline * 1.01) return 0; + for (cands.items[0..count]) |c| ks[c.slot] += 1; + return count; +} + +test "expansion maximizes full round throughput under every depth floor and row cap" { + const gpa = std.testing.allocator; + // Nonconvex row prices: scanning past an expensive row can still win. + var verify = [_]f64{ 20, 22, 27, 27, 28, 33, 33, 34, 36, 40, 41, 42 }; + const c: Costs = .{ .verify = &verify, .draft = 4 }; + const qs = [_][5]f64{ .{ 0.95, 0.9, 0.85, 0.8, 0.75 }, .{ 0.9, 0.8, 0.7, 0.6, 0.5 } }; + const slots = [_]Slot{ .{ .qs = &qs[0], .least = 1, .top = 5 }, .{ .qs = &qs[1], .least = 1, .top = 5 } }; + for (1..6) |a| for (1..6) |b| for (4..13) |cap| { + var ks = [_]usize{ a, b }; + var es: [2][6]f64 = undefined; + for (qs, &es) |q, *e| { + e[0] = 1; + var survival: f64 = 1; + for (q, e[1..], 0..) |v, *out, j| { + survival *= v; + out.* = e[j] + survival; + } + } + const baseline = (es[0][a] + es[1][b]) / (c.rowsMs(@intCast(2 + a + b)) + 8); + var best = baseline; + for (a..6) |x| for (b..6) |y| { + if (2 + x + y > cap) continue; + best = @max(best, (es[0][x] + es[1][y]) / (c.rowsMs(@intCast(2 + x + y)) + 8)); + }; + const added = try expand(gpa, &slots, 0, c, 8, cap, null, &ks); + const got = (es[0][ks[0]] + es[1][ks[1]]) / (c.rowsMs(@intCast(2 + ks[0] + ks[1])) + 8); + try std.testing.expect(ks[0] >= a and ks[1] >= b); + if (best < baseline * 1.01) { + try std.testing.expectEqual(@as(usize, 0), added); + try std.testing.expectEqual(baseline, got); + } else try std.testing.expectApproxEqAbs(best, got, 1e-12); + }; +} + +/// allocate's objective of given depths (the brute-force check). +pub fn objective(gpa: Allocator, slots: []const Slot, ks: []const usize, shared: u64, costs: Costs, rate: f64) !f64 { + var total: f64 = 0.0; + var rows: u64 = shared; + var g: std.ArrayList(f64) = .empty; + defer g.deinit(gpa); + for (slots, ks) |s, k| { + g.clearRetainingCapacity(); + try gains(s.qs, s.least, k, &g, gpa); + var sum: f64 = 0.0; + for (g.items) |x| sum += x; + total += s.weight * sum; + rows += 1 + k; + } + return total - rate * costs.rowsMs(@intCast(rows)); +} + +test "allocate equals brute force over every allocation" { + const gpa = std.testing.allocator; + var verify: [64]f64 = undefined; + for (&verify, 0..) |*v, r| v.* = 21.0 + 4.0 * @as(f64, @floatFromInt(r)) + (if (r >= 8) @as(f64, 3.0) else 0.0); + const costs: Costs = .{ .verify = &verify, .draft = 3.5 }; + var prng = std.Random.DefaultPrng.init(7); + const rnd = prng.random(); + for (0..200) |_| { + var qbuf: [3][5]f64 = undefined; + var slots: [3]Slot = undefined; + for (&slots, &qbuf) |*s, *q| { + for (q) |*x| x.* = rnd.float(f64); + s.* = .{ .qs = q, .least = rnd.uintLessThan(usize, 2), .top = 1 + rnd.uintLessThan(usize, 5), .weight = 0.5 + rnd.float(f64) }; + } + const rate = 0.02 + 0.08 * rnd.float(f64); + var ks: [3]usize = undefined; + const got = try allocate(gpa, &slots, 1, costs, rate, 64, &ks); + var best = -std.math.inf(f64); + var try_k: [3]usize = undefined; + for (slots[0].least..slots[0].top + 1) |a| for (slots[1].least..slots[1].top + 1) |b| for (slots[2].least..slots[2].top + 1) |c| { + try_k = .{ a, b, c }; + best = @max(best, try objective(gpa, &slots, &try_k, 1, costs, rate)); + }; + try std.testing.expectApproxEqAbs(best, try objective(gpa, &slots, &ks, 1, costs, rate), 1e-9); + try std.testing.expectApproxEqAbs(got, best, 1e-9); + } +} diff --git a/zig/src/families/deepseek_v41/draft/lanes.zig b/zig/src/families/deepseek_v41/draft/lanes.zig new file mode 100644 index 0000000000..a74a0ea0a5 --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/lanes.zig @@ -0,0 +1,590 @@ +//! DeepSeek-V4.1's lane backend over a `Target` and a DSpark `Pass`, the DSpark depth as the round's row policy. +const std = @import("std"); +const Allocator = std.mem.Allocator; +const lanes = @import("lanes"); +const be = lanes.backend; +const Stream = lanes.Stream; +const iface = @import("iface.zig"); +const dspark = @import("dspark.zig"); +const tree = @import("tree.zig"); +const depth = @import("depth.zig"); +const calib = @import("calib.zig"); +const costs_mod = @import("costs.zig"); +const Policy = @import("policy.zig").Policy; +const branches = @import("branches.zig"); +const spec_mod = @import("spec.zig"); + +pub const max_block = 16; + +pub const Options = struct { + shape: dspark.Shape, + depth: depth.Settings = .{}, + siblings: u32 = 0, // first-position siblings (TF_DSV41_TREE - 1; 0: chains only) + sib_rows: u32 = 4, // a sibling branch's rows at most (TF_DSV41_TREE_ROWS) + dup: tree.Dup = .{}, // sibling pricing: TF_DSV41_TREE_DUP_MS a sibling, its pending row counted + slots: u32 = 4, // streams at once + spec: spec_mod.Settings = .{}, // the speculative pass (TF_DSV41_SPEC_DRAFT / _SPEC_NUCLEUS) +}; + +/// A stream's slot and what the backend holds for it between rounds. +pub const St = struct { + slot: u32, + len: u64 = 0, // rows the target holds (provisional after a window until keep) + base: u64 = 0, // the last window's start + first: u32 = 0, // the prompt's choice (for `first`) + held: [max_block]u32 = undefined, // the last pass's drafts (the main chain) + conf: [max_block]f64 = undefined, + n: u32 = 0, // drafts held (0: none this round) + start: u64 = 0, // the pending token's position when they were drafted + sib: [tree.max_siblings]tree.Sibling = undefined, + sib_tokens: [tree.max_siblings][max_block]u32 = undefined, // each sibling's root then its continuation + sib_len: [tree.max_siblings]u32 = @splat(0), + n_sib: u32 = 0, + shown: std.ArrayList(u32) = .empty, // the drafts this round's window verifies (node indices, window order) + shown_set: bool = false, + + /// Node i of the held tree: the main chain's drafts, then each sibling's rows. + pub fn nodes(st: *const St) u32 { + var n = st.n; + for (st.sib_len[0..st.n_sib]) |l| n += l; + return n; + } +}; + +pub const Lanes = struct { + gpa: Allocator, + target: iface.Target, + pass: iface.Pass, + opts: Options, + streams: std.AutoHashMapUnmanaged(*Stream, *St) = .empty, + free: std.ArrayList(u32) = .empty, + drawn: std.ArrayList(u32) = .empty, // handle -> token + policy: Policy, + scratch: std.heap.ArenaAllocator, + // a pass's buffers, sized for opts.slots + drafts: []u32, + conf: []f32, + cand: []i32, + base: []f32, + /// the speculative pass (null: off) + spec: ?spec_mod.Spec = null, + /// each slot's stream state (null: free) + by_slot: []?*St, + + pub fn init(gpa: Allocator, target: iface.Target, pass: iface.Pass, costs: costs_mod.Costs, opts: Options) !*Lanes { + const x = try gpa.create(Lanes); + errdefer gpa.destroy(x); + const b = opts.shape.block; + if (b > max_block or opts.siblings > tree.max_siblings) return error.BadOptions; + const c = opts.shape.candidates; + x.* = .{ + .gpa = gpa, + .target = target, + .pass = pass, + .opts = opts, + .policy = try Policy.init(gpa, costs, opts), + .scratch = std.heap.ArenaAllocator.init(gpa), + .drafts = try gpa.alloc(u32, opts.slots * b), + .conf = try gpa.alloc(f32, opts.slots * b), + .cand = try gpa.alloc(i32, opts.slots * b * c), + .base = try gpa.alloc(f32, opts.slots * b * c), + .by_slot = try gpa.alloc(?*St, opts.slots), + }; + @memset(x.by_slot, null); + x.policy.owner = x; + if (opts.spec.on) x.spec = try spec_mod.Spec.init(gpa, opts.spec, opts.slots, b, c); + var s = opts.slots; + while (s > 0) : (s -= 1) try x.free.append(gpa, s - 1); + return x; + } + + pub fn deinit(x: *Lanes) void { + var it = x.streams.valueIterator(); + while (it.next()) |st| x.drop(st.*); + x.streams.deinit(x.gpa); + x.free.deinit(x.gpa); + x.drawn.deinit(x.gpa); + x.policy.deinit(); + x.scratch.deinit(); + x.gpa.free(x.drafts); + x.gpa.free(x.conf); + x.gpa.free(x.cand); + x.gpa.free(x.base); + if (x.spec) |*sp| sp.deinit(); + x.gpa.free(x.by_slot); + x.gpa.destroy(x); + } + + fn drop(x: *Lanes, st: *St) void { + st.shown.deinit(x.gpa); + x.gpa.destroy(st); + } + + pub fn backend(x: *Lanes) be.Backend { + const pieces = x.target.pieces(); + return .{ .ptr = x, .vtable = if (pieces) &vtable_pieces else &vtable }; + } + + const vtable: be.Backend.VTable = .{ .prefill = prefill, .first = first, .queue = queue, .read = read, .verify = verify, .keep = keep, .draft = draft, .probabilities = probabilities, .tree = heldTree, .release = release }; + /// a target that runs prompts in pieces (iface.Target begin / piece / finish): the round planner may use them + const vtable_pieces: be.Backend.VTable = blk: { + var v = vtable; + v.begin = begin; + v.piece = piece; + v.finish = finish; + v.join = join; + v.pieces = roundPieces; + v.finals = roundFinals; + break :blk v; + }; + + /// The speculative pass's counts on stderr when it is on (the gates' and the server's line). + pub fn reportSpec(x: *const Lanes) void { + const sp = x.spec orelse return; + var buf: [256]u8 = undefined; + std.debug.print("dsv41 spec: {s}\n", .{sp.describe(&buf)}); + } + + /// Put the DSpark depth in charge of the engine's rows. + pub fn attach(x: *Lanes, e: *lanes.Engine) void { + e.trim = x.policy.rows(); + } + + /// What the engine's Config reads of this model: 16 rows a slot, `batch_rows` a shared forward, the table's costs. + pub fn model(x: *Lanes, arena: Allocator, batch_rows: u32) !lanes.Model { + x.policy.depth.forward_rows = batch_rows; + const v = x.policy.depth.costs.verify; + const window = try arena.alloc(lanes.config.Cost, @min(v.len, costs_mod.slot_rows)); + for (window, 0..) |*w, i| w.* = .{ .width = @intCast(i + 1), .ms = v[i] }; + const shared = try arena.alloc(lanes.config.Cost, @min(v.len, batch_rows)); + for (shared, 0..) |*w, i| w.* = .{ .width = @intCast(i + 1), .ms = v[i] }; + return .{ .exact_width = costs_mod.slot_rows, .mtp = true, .speculate = true, .speculate_early = false, .drafts = x.opts.shape.block, .window_costs = window, .mtp_step_ms = 0.0, .hidden_rows = true, .batch_rows = batch_rows, .max_streams = x.opts.slots, .shared_costs = shared, .draft_probabilities = true, .draft_streams = true, .join_tail = x.target.vtable.tail != null and joinTail(), .piece_runs = envOn("TF_DSV41_PIECE_RUNS"), .join_drafts = envOn("TF_DSV41_JOIN_DRAFTS"), .replay_runs = envOn("TF_DSV41_REPLAY_RUNS") }; + } + + /// TF_DSV41_TAIL_JOIN=1: a prompt in pieces leaves its last token to its first round's shared window (default off). + fn joinTail() bool { + return envOn("TF_DSV41_TAIL_JOIN"); + } + + /// TF_DSV41_PIECE_RUNS=1: a round's prompt pieces of several slots in one forward (default off). + fn envOn(name: [*:0]const u8) bool { + const v = std.c.getenv(name) orelse return false; + return std.mem.eql(u8, std.mem.span(v), "1"); + } + + pub fn state(x: *Lanes, s: *Stream) ?*St { + return x.streams.get(s); + } + + fn self(ptr: *anyopaque) *Lanes { + return @ptrCast(@alignCast(ptr)); + } + + fn get(x: *Lanes, s: *Stream) !*St { + return x.streams.get(s) orelse error.UnknownStream; + } + + fn value(x: *Lanes, feed: be.Feed) u32 { + return switch (feed) { + .handle => |h| x.drawn.items[h], + .value => |v| v, + }; + } + + fn ingest(x: *Lanes, st: *St, start: u64, rows: []const u32) !void { + const span = iface.ingestSpan(rows.len, x.opts.shape.window); + if (span.count == 0) return; + const taps = x.target.taps(st.slot); + const m = taps.map orelse return x.pass.ingest(st.slot, start + span.skip, taps, rows[span.skip..]); + // a tree resolved as chains: the path's rows in the taps of the chain that holds it + var mapped: [branches.max_rows]u32 = undefined; + if (span.count > mapped.len) return error.TreeRows; + for (rows[span.skip..], mapped[0..span.count]) |r, *o| { + if (r >= m.len or m[r] == branches.unrun) return error.TreeTaps; + o.* = m[r]; + } + try x.pass.ingest(st.slot, start + span.skip, taps, mapped[0..span.count]); + } + + fn prefill(ptr: *anyopaque, s: *Stream) anyerror!void { + const x = self(ptr); + if (x.streams.get(s) != null) return error.StreamLive; + const slot = x.free.pop() orelse return error.NoSlot; + errdefer x.free.append(x.gpa, slot) catch {}; + const st = try x.gpa.create(St); + errdefer x.gpa.destroy(st); + st.* = .{ .slot = slot }; + try x.settle(); + if (x.spec) |*sp| sp.forget(slot); + x.pass.reset(slot); + x.policy.depth.reset(slot); + const ids = s.prompt(); + x.target.admit(slot, s.max_new); + st.first = try x.target.prefill(slot, ids, s.sampling, ids.len); + st.len = ids.len; + _ = x.scratch.reset(.retain_capacity); + // the taps hold the prompt's last rows (after a verify-tail prompt only that row), at their positions + const tr: usize = @min(x.target.taps(slot).rows, ids.len); + const rows = try x.scratch.allocator().alloc(u32, tr); + for (rows, 0..) |*r, i| r.* = @intCast(i); + try x.ingest(st, ids.len - tr, rows); + try x.streams.put(x.gpa, s, st); + x.by_slot[slot] = st; + } + + /// A prompt in pieces (lane_host.zig `Rounds`): new slot, drafter context and depth reset, target admits/restores. + fn begin(ptr: *anyopaque, s: *Stream) anyerror!be.Backend.Begun { + const x = self(ptr); + if (x.streams.get(s) != null) return error.StreamLive; + const slot = x.free.pop() orelse return error.NoSlot; + errdefer x.free.append(x.gpa, slot) catch {}; + const st = try x.gpa.create(St); + errdefer x.gpa.destroy(st); + st.* = .{ .slot = slot }; + try x.settle(); + if (x.spec) |*sp| sp.forget(slot); + x.pass.reset(slot); + x.policy.depth.reset(slot); + x.target.admit(slot, s.max_new); + const b = try x.target.begin(slot, s.prompt()); + try x.streams.put(x.gpa, s, st); + x.by_slot[slot] = st; + return .{ .cached = b.at, .damaged = b.damaged }; + } + + fn piece(ptr: *anyopaque, s: *Stream, start: u64, end: u64, save: bool) anyerror!void { + const x = self(ptr); + const st = try x.get(s); + try x.settle(); + return x.target.piece(st.slot, s.prompt(), start, end, save); + } + + /// A round's pieces of several streams: the target's (one forward over their slots' rows where it can). + fn roundPieces(ptr: *anyopaque, list: []const be.Backend.Piece) anyerror!void { + const x = self(ptr); + try x.settle(); + var ps: [16]iface.Target.Piece = undefined; + if (list.len > ps.len) return error.TooManySlots; + for (list, ps[0..list.len]) |p, *q| { + const st = try x.get(p.stream); + q.* = .{ .slot = st.slot, .ids = p.stream.prompt(), .start = p.start, .end = p.end, .save = p.save }; + } + return x.target.runPieces(ps[0..list.len]); + } + + /// A round's finished prompts before their finishes: the target's shared work (their decoder replays in one run). + fn roundFinals(ptr: *anyopaque, streams: []const *Stream) anyerror!void { + const x = self(ptr); + try x.settle(); + var fs: [16]iface.Target.Final = undefined; + if (streams.len > fs.len) return error.TooManySlots; + for (streams, fs[0..streams.len]) |s, *q| q.* = .{ .slot = (try x.get(s)).slot, .ids = s.prompt() }; + return x.target.runReplays(fs[0..streams.len]); + } + + /// The prompt's rows are in: its last token's window (the first choice), its taps to the drafter as `prefill`'s. + fn finish(ptr: *anyopaque, s: *Stream) anyerror!void { + const x = self(ptr); + const st = try x.get(s); + try x.settle(); + const ids = s.prompt(); + return x.finished(st, ids, try x.target.finish(st.slot, ids, s.sampling, ids.len)); + } + + /// `finish` without the last token's window (TF_DSV41_TAIL_JOIN); true when the target ran the row anyway. + fn join(ptr: *anyopaque, s: *Stream) anyerror!bool { + const x = self(ptr); + const st = try x.get(s); + try x.settle(); + const ids = s.prompt(); + if (try x.target.tail(st.slot, ids, s.sampling, ids.len)) |first_| { + try x.finished(st, ids, first_); + return false; + } + st.len = ids.len - 1; + st.base = st.len; + return true; + } + + fn finished(x: *Lanes, st: *St, ids: []const u32, first_: u32) !void { + st.first = first_; + st.len = ids.len; + _ = x.scratch.reset(.retain_capacity); + const tr: usize = @min(x.target.taps(st.slot).rows, ids.len); + const rows = try x.scratch.allocator().alloc(u32, tr); + for (rows, 0..) |*r, i| r.* = @intCast(i); + try x.ingest(st, ids.len - tr, rows); + } + + fn first(ptr: *anyopaque, s: *Stream, position: u64) anyerror!u64 { + const x = self(ptr); + const st = try x.get(s); + if (position != st.len) return error.PositionMismatch; + try x.drawn.append(x.gpa, st.first); + return x.drawn.items.len - 1; + } + + fn queue(ptr: *anyopaque, s: *Stream, feed: be.Feed, position: u64) anyerror!u64 { + const x = self(ptr); + const st = try x.get(s); + if (position != st.len + 1) return error.PositionMismatch; + const token = [_]u32{x.value(feed)}; + try x.settle(); + if (x.spec) |*sp| sp.clear(); // an undrafted window: no speculation + var choice: [1]u32 = undefined; + var out = [_][]u32{&choice}; + try x.target.window(&.{.{ .slot = st.slot, .start = st.len, .tokens = &token, .parents = null, .draws = &.{position}, .sampling = s.sampling }}, &out); + st.base = st.len; + st.len += 1; + try x.ingest(st, st.base, &.{0}); + try x.drawn.append(x.gpa, choice[0]); + return x.drawn.items.len - 1; + } + + fn read(ptr: *anyopaque, handle: u64) anyerror!u32 { + return self(ptr).drawn.items[handle]; + } + + fn verify(ptr: *anyopaque, windows: []const be.Window, out: []be.Verified) anyerror!void { + const x = self(ptr); + try x.settle(); + _ = x.scratch.reset(.retain_capacity); + const a = x.scratch.allocator(); + const segs = try a.alloc(iface.Segment, windows.len); + const choices = try a.alloc([]u32, windows.len); + for (windows, segs, choices, out) |w, *g, *c, o| { + const st = try x.get(w.stream); + if (w.positions[0] != st.len + 1) return error.PositionMismatch; + const tokens = try a.alloc(u32, w.rows()); + tokens[0] = w.pending; + if (w.held > st.n) return error.HeldMismatch; + @memcpy(tokens[1 .. 1 + w.held], st.held[0..w.held]); + @memcpy(tokens[1 + w.held ..], w.tokens); + const parents = w.parents; // each row's parent row (row 0: -1) + g.* = .{ .slot = st.slot, .start = st.len, .tokens = tokens, .parents = parents, .draws = w.positions, .sampling = w.stream.sampling }; + c.* = o.sampled; + @memcpy(o.drafts, tokens[1..]); + } + if (x.spec != null) x.armSpec(windows, segs); + try x.target.window(segs, choices); + for (windows) |w| { + const st = try x.get(w.stream); + st.base = st.len; + st.len += w.rows(); + } + if (x.spec != null) try x.speculate(a, windows, segs, choices); + } + + /// Before the window: arm the slots `speculate` would launch, so passes start from the device pick (Pass.arm). + fn armSpec(x: *Lanes, windows: []const be.Window, segs: []const iface.Segment) void { + const arm = x.pass.vtable.arm orelse return; + const sp = &x.spec.?; + var tree_round = false; + var nucleus = false; + for (windows) |w| { + tree_round = tree_round or w.parents != null; + nucleus = nucleus or spec_mod.isNucleus(w.stream.sampling); + } + if (!sp.roundAllowed(tree_round, nucleus)) return; + for (windows, segs) |w, g| { + const st = x.get(w.stream) catch continue; + if (!dsparkRound(st, w) or !sp.wouldWant(st.slot)) continue; + // keyed rows: the sampler's device choice (vsample.choose); a nucleus row's statistics stay on the host + const sampled = if (w.stream.sampling) |smp| smp.temperature > 0 else false; + if (sampled and spec_mod.isNucleus(w.stream.sampling)) continue; + arm(x.pass.ptr, st.slot, g.start, @intCast(g.tokens.len), sampled); + } + } + + /// A DSpark round: the window verifies the pass's drafts only (a prefix of the held chain counts too). + fn dsparkRound(st: *const St, w: be.Window) bool { + if (st.n == 0) return false; + if (w.tokens.len == 0) return true; + return w.held == 0 and w.parents == null and w.tokens.len <= st.n and std.mem.eql(u32, w.tokens, st.held[0..w.tokens.len]); + } + + /// Collects a speculative pass still running (before any other pass, target or ingest work). + fn settle(x: *Lanes) !void { + if (x.spec) |*sp| try sp.settle(x.pass); + } + + /// After a window: the next round's pass for its DSpark windows (spec.zig), their kept rows ingested first. + fn speculate(x: *Lanes, a: Allocator, windows: []const be.Window, segs: []const iface.Segment, choices: []const []u32) !void { + const sp = &x.spec.?; + var tree_round = false; + var nucleus = false; + for (windows) |w| { + tree_round = tree_round or w.parents != null; + nucleus = nucleus or spec_mod.isNucleus(w.stream.sampling); + } + if (!sp.roundAllowed(tree_round, nucleus)) return sp.clear(); + const cands = try a.alloc(spec_mod.Candidate, windows.len); + var n: usize = 0; + for (windows, segs, choices) |w, g, c| { + const st = try x.get(w.stream); + // a DSpark round only (dsparkRound counts chain rounds read back as host tokens) + if (!dsparkRound(st, w)) continue; + var acc: u32 = 0; + while (acc + 1 < g.tokens.len and g.tokens[acc + 1] == c[acc]) acc += 1; + cands[n] = .{ .slot = st.slot, .start = g.start, .accepted = acc, .bonus = c[acc], .sampling = g.sampling }; + n += 1; + } + const asks = sp.launch(cands[0..n]); + if (asks.len == 0) return; + const rows = try a.alloc(u32, lanes_max_rows); + for (rows, 0..) |*r, i| r.* = @intCast(i); + for (asks) |ask| { + const st = x.by_slot[ask.slot] orelse return error.UnknownStream; + try x.ingest(st, st.base, rows[0 .. ask.start - st.base]); + } + try sp.run(x.pass, x.opts.siblings > 0); + } + + const lanes_max_rows = 64; + + fn keep(ptr: *anyopaque, windows: []const be.Window, paths: []const []const u32) anyerror!void { + const x = self(ptr); + for (windows, paths) |w, path| { + const st = try x.get(w.stream); + try x.target.keep(st.slot, path); + st.len = st.base + path.len; + } + } + + fn draft(ptr: *anyopaque, requests: []const be.DraftRequest) anyerror!void { + const x = self(ptr); + try x.settle(); + _ = x.scratch.reset(.retain_capacity); + const a = x.scratch.allocator(); + const asks = try a.alloc(iface.Ask, requests.len); + const whose = try a.alloc(*St, requests.len); + const depths = try a.alloc(u32, requests.len); + const row0 = try a.alloc(iface.Warm, requests.len); + const row0_ids = try a.alloc(u32, requests.len); + var n_row0: usize = 0; + var n: usize = 0; + for (requests) |r| { + const st = try x.get(r.stream); + var at = st.len; + if (r.rows) |path| { + if (r.start != st.base) return error.PositionMismatch; + const done = if (x.spec) |*sp| sp.ingested(st.slot, r.start, path) else false; + if (!done) try x.ingest(st, r.start, path); // a shared round drafts before its keep: the path, not st.len + at = r.start + path.len; + } + if (r.position != at + 1) return error.PositionMismatch; + const anchor = if (r.first) |f| x.value(f) else r.follow[r.follow.len - 1]; + // commit warm: the next window's pending row, before the pass + if (x.target.vtable.warm != null and n_row0 < row0.len) { + row0_ids[n_row0] = anchor; + row0[n_row0] = .{ .slot = st.slot, .start = at, .ids = row0_ids[n_row0..][0..1] }; + n_row0 += 1; + } + st.n = 0; + st.n_sib = 0; + st.start = at; + if (r.depth == 0) continue; + if (n == x.opts.slots) return error.TooManySlots; + asks[n] = .{ .slot = st.slot, .anchor = anchor, .start = at, .params = dspark.Params.of(r.stream.sampling, at) }; + whose[n] = st; + depths[n] = r.depth; + n += 1; + } + x.target.warm(row0[0..n_row0]); + if (n == 0) return; + const b = x.opts.shape.block; + const c = x.opts.shape.candidates; + const markov = if (x.opts.siblings > 0) x.pass.markov() else null; + const props = try a.alloc(iface.Proposal, n); + for (props, 0..) |*p, i| p.* = .{ + .drafts = x.drafts[i * b ..][0..b], + .conf = x.conf[i * b ..][0..b], + .cand = if (markov != null) x.cand[i * b * c ..][0 .. b * c] else null, + .base = if (markov != null) x.base[i * b * c ..][0 .. b * c] else null, + }; + const took = if (x.spec) |*sp| sp.take(asks[0..n], props) else false; + if (!took) try x.pass.propose(asks[0..n], props); + // draft-end warm: the pending row and every drafted one, before the depth cuts them + if (x.target.vtable.warm != null) { + const ws = try a.alloc(iface.Warm, n); + for (ws, props, asks[0..n]) |*w, p, ask| { + const ids = try a.alloc(u32, 1 + b); + ids[0] = ask.anchor; + @memcpy(ids[1..], p.drafts[0..b]); + w.* = .{ .slot = ask.slot, .start = ask.start, .ids = ids }; + } + x.target.warm(ws); + } + for (props, whose[0..n], depths[0..n], asks[0..n]) |p, st, d, ask| { + st.n = @min(d, b); + @memcpy(st.held[0..st.n], p.drafts[0..st.n]); + for (st.conf[0..st.n], p.conf[0..st.n]) |*q, f| q.* = f; + if (markov) |m| try x.siblings(a, st, m, p, ask); + } + } + + /// The siblings after the pending token: the draft distribution's next first tokens and their Markov chains. + fn siblings(x: *Lanes, a: Allocator, st: *St, m: dspark.Markov, p: iface.Proposal, ask: iface.Ask) !void { + const c = x.opts.shape.candidates; + const z0 = try a.alloc(f32, c); + dspark.markovZ(m, p.cand.?[0..c], p.base.?[0..c], ask.anchor, z0); + const smp = ask.params.sampling(); + const got = try tree.ranked(a, p.cand.?[0..c], z0, ask.start + 1, smp, st.held[0..1], st.sib[0..x.opts.siblings]); + const rows = @min(x.opts.sib_rows, st.n); + for (st.sib[0..got], 0..) |sib, j| { + st.sib_tokens[j][0] = sib.token; + const more = try tree.continuation(a, m, p.cand.?, p.base.?, c, sib.token, rows, ask.params, st.sib_tokens[j][1..]); + st.sib_len[j] = @intCast(1 + more); + } + st.n_sib = @intCast(got); + } + + /// The held drafts as host tokens: the main chain, then each sibling's root and its chain (gpa-owned). + fn heldTree(ptr: *anyopaque, s: *Stream, gpa: Allocator) anyerror!?lanes.stream.Held { + const x = self(ptr); + const st = try x.get(s); + if (st.n == 0) return null; + const total = st.nodes(); + const tokens = try gpa.alloc(u32, total); + errdefer gpa.free(tokens); + @memcpy(tokens[0..st.n], st.held[0..st.n]); + if (st.n_sib == 0) return .{ .count = total, .tokens = tokens }; + const parents = try gpa.alloc(i32, total); + for (parents[0..st.n], 0..) |*q, i| q.* = @as(i32, @intCast(i)) - 1; + var at: usize = st.n; + for (st.sib_tokens[0..st.n_sib], st.sib_len[0..st.n_sib]) |toks, len| { + for (0..len) |i| { + tokens[at + i] = toks[i]; + parents[at + i] = if (i == 0) -1 else @intCast(at + i - 1); + } + at += len; + } + return .{ .count = total, .tokens = tokens, .parents = parents }; + } + + /// Each held draft's chance of landing from the raw confidences (the engine's own allocation, without the policy). + fn probabilities(ptr: *anyopaque, s: *Stream, out: []f64) anyerror!bool { + const x = self(ptr); + const st = try x.get(s); + var run: f64 = 1.0; + for (out, 0..) |*o, i| { + run *= if (i < st.n) st.conf[i] else 0.0; + o.* = run; + } + return st.n > 0; + } + + fn release(ptr: *anyopaque, s: *Stream) void { + const x = self(ptr); + const kv = x.streams.fetchRemove(s) orelse return; + const st = kv.value; + x.settle() catch |e| std.log.err("dsv41 lanes: the speculative pass did not settle ({t})", .{e}); + if (x.spec) |*sp| sp.forget(st.slot); + x.by_slot[st.slot] = null; + x.target.release(st.slot); + x.pass.reset(st.slot); + x.policy.depth.reset(st.slot); + x.free.append(x.gpa, st.slot) catch {}; + x.drop(st); + } +}; diff --git a/zig/src/families/deepseek_v41/draft/lanes_test.zig b/zig/src/families/deepseek_v41/draft/lanes_test.zig new file mode 100644 index 0000000000..9ac61ad6ec --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/lanes_test.zig @@ -0,0 +1,245 @@ +//! The lane engine on the CPU twin with DeepSeek's backend: drafted == serial across sampling, sharing, trees, depths. +const std = @import("std"); +const lanes = @import("lanes"); +const twin = @import("twin.zig"); +const ln = @import("lanes.zig"); +const costs_mod = @import("costs.zig"); +const depth = @import("depth.zig"); +const spec_mod = @import("spec.zig"); + +const gpa = std.testing.allocator; + +const Case = struct { prompt: []const u32, max_new: u32 = 60, sampling: ?lanes.Sampling = null, drafts: bool = true, copies: bool = false }; + +const Setup = struct { siblings: u32 = 0, joint: u2 = 1, skip: bool = true, costs: Prices = .served, slots: u32 = 4, dims: twin.Dims = .{}, depth: depth.Settings = .{}, spec: bool = false }; + +const Prices = enum { served, flat, cheap }; + +/// A measured TP=2 cost table: rows 1-16, the wide buckets 24/32/48/64, the pass and slot ms. +pub fn servedCosts(alloc: std.mem.Allocator) !costs_mod.Costs { + const rows = [_]f64{ 21.954, 26.573, 31.977, 36.158, 40.852, 44.776, 47.237, 51.089, 57.951, 59.663, 60.793, 62.458, 69.962, 70.85, 70.971, 72.362 }; + const slot: f64 = 0.83; // the buckets were logged without the slot overhead: put back + const wide = [_]costs_mod.Point{ .{ .rows = 24, .ms = @as(f64, 100.2) + slot }, .{ .rows = 32, .ms = @as(f64, 115.2) + slot }, .{ .rows = 48, .ms = @as(f64, 147.1) + 2 * slot }, .{ .rows = 64, .ms = @as(f64, 196.3) + 3 * slot } }; + return costs_mod.measured(alloc, &rows, &wide, 3.61, 0.83); +} + +const Run = struct { + out: [][]u32, + drafted: u64, + accepted: u64, + zero: u64, + trees: u64, + sib_wins: u64, + joint: u64, + kept: [16]u64, // drafts kept by position (depth.Stats) + widest: usize, // the most rows one forward held + expanded: u64, + spec: spec_mod.Stats = .{}, // the speculative pass's counts (Setup.spec) + + fn deinit(r: Run) void { + for (r.out) |o| gpa.free(o); + gpa.free(r.out); + } +}; + +fn run(cases: []const Case, setup: Setup) !Run { + const t = try twin.Twin.init(gpa, setup.dims, setup.slots + 1); + defer t.deinit(); + const d = try twin.Drafter.init(gpa, t, setup.slots + 1); + defer d.deinit(); + var c = switch (setup.costs) { + .served => try servedCosts(gpa), + .flat => try costs_mod.defaults(gpa, 64), + .cheap => cheap: { // rows nearly free: every draft pays, so windows fill + const v = try gpa.alloc(f64, 64); + for (v, 0..) |*x, r| x.* = 20.0 + 0.01 * @as(f64, @floatFromInt(r)); + break :cheap costs_mod.Costs{ .verify = v, .draft = 1.0 }; + }, + }; + defer c.deinit(gpa); + const x = try ln.Lanes.init(gpa, t.target(), d.pass(), c, .{ .shape = d.shape, .siblings = setup.siblings, .slots = setup.slots, .depth = d: { + var set = setup.depth; + set.joint = setup.joint; + set.skip = setup.skip; + break :d set; + }, .spec = .{ .on = setup.spec } }); + defer x.deinit(); + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + var cfg = try lanes.Config.init(gpa, try x.model(arena.allocator(), 64), 16, 15); + defer cfg.deinit(gpa); + var clock: lanes.fake.FixedClock = .{}; + var e = lanes.Engine.init(gpa, &cfg, x.backend(), clock.clock()); + defer e.deinit(); + x.attach(&e); + const streams = try gpa.alloc(lanes.Stream, cases.len); + defer gpa.free(streams); + const props = try gpa.alloc(lanes.SuffixLookup, cases.len); + defer gpa.free(props); + for (cases, streams, props) |cs, *s, *p| { + p.* = try lanes.SuffixLookup.init(gpa, .{ .min_match = 4 }); + s.* = try lanes.Stream.init(gpa, .{ .id = "s", .prompt = cs.prompt, .max_new = cs.max_new, .sampling = cs.sampling, .drafts = cs.drafts, .proposer = if (cs.copies) p.proposer() else null }); + } + defer for (streams, props) |*s, *p| { + s.deinit(gpa); + p.deinit(); + }; + for (streams) |*s| try e.addStream(s); + while (e.activeCount() > 0) try e.step(); + const spec_stats: spec_mod.Stats = if (x.spec) |sp| sp.stats else .{}; + const out = try gpa.alloc([]u32, cases.len); + for (out, streams) |*o, *s| o.* = try gpa.dupe(u32, s.emitted()); + return .{ .out = out, .drafted = e.drafted, .accepted = e.accepted, .zero = x.policy.depth.stats.zero, .trees = x.policy.trees, .sib_wins = x.policy.sib_wins, .joint = x.policy.depth.stats.joint[0], .kept = x.policy.depth.stats.kept, .widest = t.widest, .expanded = x.policy.depth.expanded_rounds, .spec = spec_stats }; +} + +/// The twin's own serial decode of a case (no lanes at all). +fn serial(cs: Case, dims: twin.Dims) ![]u32 { + const t = try twin.Twin.init(gpa, dims, 1); + defer t.deinit(); + return t.serial(0, cs.prompt, cs.max_new, cs.sampling, null); +} + +fn expectSerial(cases: []const Case, r: Run, dims: twin.Dims) !void { + for (cases, r.out) |cs, got| { + const want = try serial(cs, dims); + defer gpa.free(want); + try std.testing.expectEqualSlices(u32, want, got); + } +} + +const p1 = [_]u32{ 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3, 8, 4 }; +const p2 = [_]u32{ 2, 7, 1, 8, 2, 8, 1, 8, 2, 8, 4, 5, 9, 0, 4, 5 }; +const p3 = [_]u32{ 10, 20, 30, 40, 10, 20, 30, 40, 10, 20, 30 }; +const hot: lanes.Sampling = .{ .seed = 11, .temperature = 0.8, .top_k = 0, .top_p = 0.9 }; +const warm: lanes.Sampling = .{ .seed = 5, .temperature = 1.0, .top_k = 20, .top_p = 0.95, .min_p = 0.02 }; + +test "code acceptance expansion preserves greedy and T0.7 keyed engine replies" { + var expanded: u64 = 0; + for ([_]twin.Dims{ .{}, .{ .bigram = 8.0, .head = 0.08 } }) |dims| { + for ([_]?lanes.Sampling{ null, .{ .seed = 11, .temperature = 0.7, .top_p = 0.9 } }) |s| { + const cases = [_]Case{ .{ .prompt = &p1, .sampling = s, .max_new = 160 }, .{ .prompt = &p2, .sampling = s, .max_new = 130 }, .{ .prompt = &p3, .sampling = s, .max_new = 150 }, .{ .prompt = &p2, .sampling = s, .max_new = 140 } }; + const old = try run(&cases, .{ .dims = dims }); + defer old.deinit(); + const new = try run(&cases, .{ .dims = dims, .depth = .{ .code_accept = true } }); + defer new.deinit(); + try expectSerial(&cases, new, dims); + for (old.out, new.out) |a, b| try std.testing.expectEqualSlices(u32, a, b); + expanded += new.expanded; + const old_lone = try run(cases[0..1], .{ .dims = dims, .slots = 1 }); + defer old_lone.deinit(); + const new_lone = try run(cases[0..1], .{ .dims = dims, .slots = 1, .depth = .{ .code_accept = true } }); + defer new_lone.deinit(); + try expectSerial(cases[0..1], new_lone, dims); + try std.testing.expectEqualSlices(u32, old_lone.out[0], new_lone.out[0]); + try std.testing.expectEqual(@as(u64, 0), new_lone.expanded); + try std.testing.expectEqual(old_lone.drafted, new_lone.drafted); + try std.testing.expectEqual(old_lone.accepted, new_lone.accepted); + try std.testing.expectEqualSlices(u64, &old_lone.kept, &new_lone.kept); + } + } + try std.testing.expect(expanded > 0); // token equality must exercise the changed policy +} + +test "low acceptance engine streams keep the old policy" { + const dims: twin.Dims = .{ .bigram = 0.2, .head = 1.0 }; + const cases = [_]Case{ .{ .prompt = &p1, .max_new = 80 }, .{ .prompt = &p2, .max_new = 80, .sampling = .{ .seed = 7, .temperature = 0.7 } } }; + const old = try run(&cases, .{ .dims = dims }); + defer old.deinit(); + const new = try run(&cases, .{ .dims = dims, .depth = .{ .code_accept = true } }); + defer new.deinit(); + try std.testing.expectEqual(@as(u64, 0), new.expanded); + try std.testing.expectEqual(old.drafted, new.drafted); + for (old.out, new.out) |a, b| try std.testing.expectEqualSlices(u32, a, b); + try expectSerial(&cases, new, dims); +} + +test "drafted rounds commit the serial decode, greedy and keyed sampling" { + for ([_]?lanes.Sampling{ null, hot, warm }) |s| { + const cases = [_]Case{.{ .prompt = &p1, .sampling = s, .max_new = 80 }}; + const r = try run(&cases, .{}); + defer r.deinit(); + try expectSerial(&cases, r, .{}); + try std.testing.expect(r.accepted > 0 and r.kept[1] > 0); // windows kept two drafts and more + const plain = try run(&.{.{ .prompt = &p1, .sampling = s, .max_new = 80, .drafts = false }}, .{}); + defer plain.deinit(); + try std.testing.expectEqualSlices(u32, plain.out[0], r.out[0]); + try std.testing.expectEqual(@as(u64, 0), plain.drafted); + } +} + +test "a confident drafter keeps whole blocks and stays serial" { + const dims: twin.Dims = .{ .bigram = 8.0, .head = 0.08 }; + const cases = [_]Case{ .{ .prompt = &p3, .max_new = 120 }, .{ .prompt = &p1, .sampling = warm, .max_new = 120 } }; + const r = try run(&cases, .{ .dims = dims }); + defer r.deinit(); + try expectSerial(&cases, r, dims); + try std.testing.expect(r.kept[3] > 0 and r.accepted * 10 >= r.drafted * 8); +} + +test "shared rounds commit what each stream commits alone, every joint mode" { + const cases = [_]Case{ .{ .prompt = &p1 }, .{ .prompt = &p2, .sampling = hot, .max_new = 45 }, .{ .prompt = &p3, .copies = true }, .{ .prompt = &p2, .drafts = false, .max_new = 30 } }; + for ([_]u2{ 0, 1, 2 }) |mode| { + const r = try run(&cases, .{ .joint = mode }); + defer r.deinit(); + try expectSerial(&cases, r, .{}); + if (mode > 0) try std.testing.expect(r.joint > 0); + } +} + +test "first-position trees keep the serial decode" { + var trees: u64 = 0; + var wins: u64 = 0; + for ([_]?lanes.Sampling{ null, hot }) |s| { + const cases = [_]Case{.{ .prompt = &p2, .sampling = s, .max_new = 120 }}; + const r = try run(&cases, .{ .siblings = 2 }); + defer r.deinit(); + try expectSerial(&cases, r, .{}); + trees += r.trees; + wins += r.sib_wins; + } + try std.testing.expect(trees > 0 and wins > 0); +} + +test "zero depth under an overconfident head keeps the serial decode" { + const dims: twin.Dims = .{ .conf = 0.3 }; + for ([_]u2{ 1, 2 }) |mode| { + const cases = [_]Case{ .{ .prompt = &p2, .max_new = 100 }, .{ .prompt = &p1, .sampling = hot, .max_new = 60 } }; + const r = try run(cases[0..1], .{ .dims = dims, .joint = mode }); + defer r.deinit(); + try expectSerial(cases[0..1], r, dims); + try std.testing.expect(r.zero > 0); + const shared = try run(&cases, .{ .dims = dims, .joint = mode }); + defer shared.deinit(); + try expectSerial(&cases, shared, dims); + } +} + +test "shared forwards past 32 rows (Python's 64) keep every stream serial" { + const dims: twin.Dims = .{ .bigram = 8.0, .head = 0.08, .block = 10 }; // a wider block than V4.1's 5 fills the rows + const flat = [_]u32{ 7, 3, 9, 1 }; + const cases = [_]Case{ .{ .prompt = &p1, .max_new = 90 }, .{ .prompt = &p2, .max_new = 90 }, .{ .prompt = &p3, .max_new = 90 }, .{ .prompt = &flat, .max_new = 90 }, .{ .prompt = &p1, .sampling = warm, .max_new = 90 }, .{ .prompt = &p2, .sampling = hot, .max_new = 90 } }; + const r = try run(&cases, .{ .dims = dims, .slots = 6, .costs = .cheap, .depth = .{ .cap = 10 } }); + defer r.deinit(); + try expectSerial(&cases, r, dims); + try std.testing.expect(r.widest > 32 and r.widest <= 64); +} + +test "the speculative pass runs on chain rounds (held drafts read back as host tokens) and changes no draft" { + // chain held drafts come back as host tokens, so those windows are DSpark rounds and nearly all are speculated + for ([_]?lanes.Sampling{ null, warm }) |smp| { + const cases = [_]Case{.{ .prompt = &p1, .sampling = smp, .max_new = 120 }}; + const off = try run(&cases, .{ .slots = 1 }); + defer off.deinit(); + const on = try run(&cases, .{ .slots = 1, .spec = true }); + defer on.deinit(); + try expectSerial(&cases, on, .{}); + try std.testing.expectEqualSlices(u32, off.out[0], on.out[0]); + try std.testing.expectEqual(off.drafted, on.drafted); + try std.testing.expectEqual(off.accepted, on.accepted); + try std.testing.expectEqualSlices(u64, &off.kept, &on.kept); + const st = on.spec; + try std.testing.expect(st.launched > 0 and st.hit > 0); + // 120 tokens in rounds of up to 6: dozens of chain rounds, each speculated + try std.testing.expect(st.launched >= 20 and st.hit + st.missed() == st.launched); + } +} diff --git a/zig/src/families/deepseek_v41/draft/lookup.zig b/zig/src/families/deepseek_v41/draft/lookup.zig new file mode 100644 index 0000000000..3b61d67a2a --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/lookup.zig @@ -0,0 +1,340 @@ +//! Priced per-request lookup drafting (TF_DSV41_LOOKUP_PLAN=1): history suffix matches drafted only when they pay. + +const std = @import("std"); +const Allocator = std.mem.Allocator; +const lanes = @import("lanes"); +const costs_mod = @import("costs.zig"); +const depth = @import("depth.zig"); +const joint = @import("joint.zig"); + +/// (shortest match of the band, prior p) +pub const bands = [_]struct { least: i64, prior: f64 }{ .{ .least = 0, .prior = 0.45 }, .{ .least = 8, .prior = 0.7 }, .{ .least = 16, .prior = 0.8 }, .{ .least = 32, .prior = 0.9 } }; +pub const prior_weight: f64 = 2.0; +pub const decay: f64 = 0.85; +pub const probe_every = 8; +pub const probe_match: i64 = 16; +pub const alt_window = 8; +pub const candidates = 64; +pub const extend_to = 64; +/// An image row's virtual id base: never a token to draft +pub const vbase: u32 = 1 << 24; +pub const max_rows = depth.slot_rows; // 16 + +/// TF_DSV41_LOOKUP_PLAN, TF_DSV41_LOOKUP, TF_DSV41_LOOKUP_MIN, TF_DSV41_MAX_ROWS. +pub const Settings = struct { + plan: bool = false, + lookup: bool = true, + min_match: i64 = 4, + max_rows: i64 = max_rows, + + pub fn fromEnv() !Settings { + var s: Settings = .{}; + s.plan = try flag(std.c.getenv("TF_DSV41_LOOKUP_PLAN"), false); + s.lookup = try flag(std.c.getenv("TF_DSV41_LOOKUP"), true); + if (std.c.getenv("TF_DSV41_LOOKUP_MIN")) |v| if (std.mem.span(v).len > 0) { + s.min_match = std.fmt.parseInt(i64, std.mem.trim(u8, std.mem.span(v), " "), 10) catch return error.BadLookupMin; + }; + if (std.c.getenv("TF_DSV41_MAX_ROWS")) |v| if (std.mem.span(v).len > 0) { + s.max_rows = std.fmt.parseInt(i64, std.mem.trim(u8, std.mem.span(v), " "), 10) catch return error.BadMaxRows; + }; + if (s.min_match < 1 or s.min_match > 64) return error.BadLookupMin; + if (s.max_rows < 2 or s.max_rows > max_rows) return error.BadMaxRows; + return s; + } + + /// The lookup flag: anything but 0 / false / off / no / "" is on; unset is `default`. + fn flag(v: ?[*:0]const u8, default: bool) !bool { + const raw = std.mem.trim(u8, std.mem.span(v orelse return default), " \t"); + var low: [8]u8 = undefined; + if (raw.len > low.len) return true; + const l = std.ascii.lowerString(&low, raw); + for ([_][]const u8{ "0", "false", "off", "no", "" }) |n| if (std.mem.eql(u8, l, n)) return false; + return true; + } +}; + +/// Where every n-gram of a growing history ends. +pub const SuffixIndex = struct { + gpa: Allocator, + n: usize, + tokens: std.ArrayList(u32) = .empty, + ends: std.AutoHashMapUnmanaged([3]u32, std.ArrayList(u32)) = .empty, + indexed: usize = 0, + + pub fn init(gpa: Allocator, n: usize) SuffixIndex { + std.debug.assert(n >= 1 and n <= 3); + return .{ .gpa = gpa, .n = n }; + } + + pub fn deinit(x: *SuffixIndex) void { + var it = x.ends.valueIterator(); + while (it.next()) |l| l.deinit(x.gpa); + x.ends.deinit(x.gpa); + x.tokens.deinit(x.gpa); + } + + pub fn extend(x: *SuffixIndex, tokens: []const u32) !void { + try x.tokens.appendSlice(x.gpa, tokens); + } + + fn key(x: *const SuffixIndex, end: usize) [3]u32 { + var k: [3]u32 = @splat(0); + @memcpy(k[0..x.n], x.tokens.items[end - x.n .. end]); + return k; + } + + /// The n-grams ending at indexed .. len - 1 (the one ending at the history's end joins once a token follows it). + fn index(x: *SuffixIndex) !void { + const last = x.tokens.items.len; + var end = @max(x.indexed, x.n); + while (end < last) : (end += 1) { + const g = try x.ends.getOrPut(x.gpa, x.key(end)); + if (!g.found_existing) g.value_ptr.* = .empty; + try g.value_ptr.append(x.gpa, @intCast(end)); + } + x.indexed = @max(x.indexed, last); + } + + pub const Match = struct { len: i64, end: i64 }; + + /// The longest match of the history's suffix ending before its end (ties to the most recent), (0, -1) for none. + pub fn match(x: *SuffixIndex) !Match { + const t = x.tokens.items; + const L = t.len; + if (L <= x.n) return .{ .len = 0, .end = -1 }; + try x.index(); + const ends = x.ends.get(x.key(L)) orelse return .{ .len = 0, .end = -1 }; + const reach = @max(extend_to, x.n); + var best: Match = .{ .len = 0, .end = -1 }; + const from = ends.items.len -| candidates; + var i = ends.items.len; + while (i > from) { + i -= 1; + const end: usize = ends.items[i]; + var length: usize = x.n; + const limit = @min(reach, end); + while (length < limit and t[end - 1 - length] == t[L - 1 - length]) length += 1; + if (length > best.len) { + best = .{ .len = @intCast(length), .end = @intCast(end) }; + if (length >= reach) break; + } + } + return best; + } + + /// Up to `count` drafts (into `out`) from the longest match of at least `min_match` tokens, and its length. + pub fn propose(x: *SuffixIndex, count: usize, min_match: i64, out: *std.ArrayList(u32)) !i64 { + out.clearRetainingCapacity(); + const m = try x.match(); + if (m.end < 0 or m.len < min_match or count == 0) return m.len; + const t = x.tokens.items; + const end: usize = @intCast(m.end); + for (0..count) |i| { + const j = end + i; + const d = if (j < t.len) t[j] else out.items[j - t.len]; // past the end the copy reads its own output + if (d >= vbase) break; + try out.append(x.gpa, d); + } + return m.len; + } +}; + +pub const Kind = enum { serial, dspark, lookup }; + +/// One request's drafting state (the lookup part). +pub const Request = struct { + index: SuffixIndex, + acc: [bands.len]f64 = @splat(0.0), + tri: [bands.len]f64 = @splat(0.0), + skipped: [bands.len]u32 = @splat(0), + alt: [alt_window][2]f64 = undefined, // (tokens, ms), oldest first from `alt_head` + alt_len: usize = 0, + alt_head: usize = 0, + kind: Kind = .serial, + band: usize = 0, + out: std.ArrayList(u32) = .empty, + + pub fn deinit(r: *Request) void { + r.index.deinit(); + r.out.deinit(r.index.gpa); + } + + fn pushAlt(r: *Request, tokens: f64, ms: f64) void { + r.alt[(r.alt_head + r.alt_len) % alt_window] = .{ tokens, ms }; + if (r.alt_len < alt_window) r.alt_len += 1 else r.alt_head = (r.alt_head + 1) % alt_window; + } +}; + +/// The batcher's lookup decisions: `start`, `window`, `observe`. +pub const Planner = struct { + set: Settings, + costs: *const costs_mod.Costs, + dspark: bool, + n: usize, + + pub fn init(set: Settings, costs: *const costs_mod.Costs, dspark: bool) Planner { + return .{ .set = set, .costs = costs, .dspark = dspark, .n = @intCast(@max(1, @min(3, set.min_match))) }; + } + + pub fn start(p: *const Planner, gpa: Allocator, prompt: []const u32) !Request { + var r: Request = .{ .index = SuffixIndex.init(gpa, p.n) }; + if (p.set.lookup) try r.index.extend(prompt); + return r; + } + + fn band(length: i64) usize { + var b: usize = 0; + for (bands, 0..) |x, i| if (length >= x.least) { + b = i; + }; + return b; + } + + pub fn pOf(r: *const Request, b: usize) f64 { + return (r.acc[b] + bands[b].prior * prior_weight) / (r.tri[b] + prior_weight); + } + + /// Tokens a modelled ms of recent non-lookup rounds (before any: a one-draft DSpark round, or a serial step). + pub fn altRate(p: *const Planner, r: *const Request) f64 { + if (r.alt_len > 0) { + var tokens: f64 = 0.0; + var ms: joint.PySum = .{}; + for (0..r.alt_len) |i| { + const x = r.alt[(r.alt_head + i) % alt_window]; + tokens += x[0]; + ms.add(x[1]); + } + return tokens / ms.total(); + } + const c = p.costs; + return if (p.dspark) 2.0 / (c.windowMs(2) + c.draft) else 1.0 / c.windowMs(1); + } + + /// This round's lookup drafts (empty: none); `left`: tokens still allowed; `eos`: cut at the first end token. + pub fn window(p: *const Planner, r: *Request, left: i64, drafts: bool, eos: []const u32) ![]const u32 { + const room: i64 = @max(0, left - 1); + r.kind = .serial; + r.out.clearRetainingCapacity(); + if (!drafts or room <= 0) return &.{}; + if (p.set.lookup) { + const count: usize = @intCast(@min(p.set.max_rows - 1, room)); + const length = try r.index.propose(count, p.set.min_match, &r.out); + var ds = r.out.items; + for (ds, 0..) |d, i| if (std.mem.indexOfScalar(u32, eos, d) != null) { + ds = ds[0..i]; + break; + }; + if (ds.len > 0) { + const b = band(length); + var qs: [max_rows]f64 = undefined; + const q = @min(pOf(r, b), 0.995); + for (qs[0..ds.len]) |*x| x.* = q; + const best = depth.bestK(qs[0..ds.len], p.costs.verify, p.altRate(r), 1); + var go = best.k > 0 and best.surplus > 0.0; + if (!go) { + r.skipped[b] += 1; + go = best.k > 0 and bands[b].least >= probe_match and r.skipped[b] >= probe_every; + } + if (go) { + r.skipped[b] = 0; + r.kind = .lookup; + r.band = b; + return ds[0..best.k]; + } + } + } + if (p.dspark) r.kind = .dspark; + return &.{}; + } + + /// After the round: `rows` the window's rows, `kept` its drafts kept, `emitted` the tokens it added. + pub fn observe(p: *const Planner, r: *Request, rows: usize, kept: usize, emitted: []const u32) !void { + const drafts = rows -| 1; + const k: f64 = @floatFromInt(kept); + if (r.kind == .lookup) { + const b = r.band; + r.acc[b] = r.acc[b] * decay + k; + r.tri[b] = r.tri[b] * decay + k + @as(f64, if (kept < drafts) 1.0 else 0.0); + } else { + const ms = p.costs.windowMs(@intCast(rows)) + (if (r.kind == .dspark) p.costs.draft else 0.0); + r.pushAlt(k + 1.0, ms); + } + if (p.set.lookup) try r.index.extend(emitted); + } +}; + +/// The planner as a stream's lanes proposer (`priced`): one decision per history length, `round` feeds outcomes back. +pub const Proposer = struct { + planner: *const Planner, + req: Request, + eos: []const u32, + /// the history length the planner's index holds (grows by committed tokens) + synced: usize, + /// the round's ask (history length, room) and its drafts: a second ask reads them back + asked: ?struct { len: usize, max_draft: i64 } = null, + kept_k: usize = 0, + + pub fn init(planner: *const Planner, gpa: Allocator, prompt: []const u32, eos: []const u32) !Proposer { + return .{ .planner = planner, .req = try planner.start(gpa, prompt), .eos = eos, .synced = prompt.len }; + } + + pub fn deinit(x: *Proposer) void { + x.req.deinit(); + } + + pub fn proposer(x: *Proposer) lanes.proposer.Proposer { + return .{ .ptr = x, .vtable = &vtable }; + } + + const vtable: lanes.proposer.Proposer.VTable = .{ .propose = propose, .last_match = lastMatch, .priced = true, .round = round }; + + fn self(p: *anyopaque) *Proposer { + return @ptrCast(@alignCast(p)); + } + + fn propose(p: *anyopaque, context: []const u32, max_draft: i64) anyerror![]const u32 { + const x = self(p); + if (x.asked) |a| if (a.len == context.len and a.max_draft == max_draft) return x.req.out.items[0..x.kept_k]; + // the history's new tokens (the reply so far) into the index + if (context.len > x.synced and x.planner.set.lookup) try x.req.index.extend(context[x.synced..]); + x.synced = @max(x.synced, context.len); + const ds = try x.planner.window(&x.req, max_draft + 1, true, x.eos); + x.kept_k = ds.len; // the drafts are a prefix of req.out + x.asked = .{ .len = context.len, .max_draft = max_draft }; + return ds; + } + + fn lastMatch(_: *anyopaque) i64 { + return 0; + } + + fn round(p: *anyopaque, rows: u32, kept: u32) void { + const x = self(p); + if (x.asked == null) x.req.kind = .serial; // not asked this round: a stream that does not draft + x.asked = null; + // no tokens: the index takes the committed ones from the context at the next propose (the same tokens) + x.planner.observe(&x.req, rows, kept, &.{}) catch unreachable; + } +}; + +test "lookup plan: a repeated span is drafted when its band pays, priced against the request's own rate" { + const gpa = std.testing.allocator; + var verify = [_]f64{ 42, 47, 52, 57, 62, 67, 72, 77, 82, 87, 92, 97, 102, 107, 112, 117 }; + const c: costs_mod.Costs = .{ .verify = &verify, .draft = 5.6 }; + const pl = Planner.init(.{ .plan = true }, &c, true); + const span = [_]u32{ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 }; + var prompt: std.ArrayList(u32) = .empty; + defer prompt.deinit(gpa); + try prompt.appendSlice(gpa, &span); + try prompt.appendSlice(gpa, &.{ 1, 2 }); + try prompt.appendSlice(gpa, span[0..12]); // a 12-token match: band 1 (p 0.7) + var r = try pl.start(gpa, prompt.items); + defer r.deinit(); + const ds = try pl.window(&r, 100, true, &.{}); + try std.testing.expect(r.kind == .lookup and ds.len > 0 and ds[0] == 22); + // no match: DSpark + var r2 = try pl.start(gpa, &.{ 1, 2, 3, 4, 5 }); + defer r2.deinit(); + try std.testing.expectEqual(@as(usize, 0), (try pl.window(&r2, 100, true, &.{})).len); + try std.testing.expect(r2.kind == .dspark); +} diff --git a/zig/src/families/deepseek_v41/draft/lookup_test.zig b/zig/src/families/deepseek_v41/draft/lookup_test.zig new file mode 100644 index 0000000000..e643c50c11 --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/lookup_test.zig @@ -0,0 +1,98 @@ +//! lookup.zig replays fixtures/lookup-golden.json round for round, then the same rounds through lookup.Proposer. +const std = @import("std"); +const Value = std.json.Value; +const costs_mod = @import("costs.zig"); +const lookup = @import("lookup.zig"); + +const gpa = std.testing.allocator; +const golden = @embedFile("fixtures/lookup-golden.json"); + +fn ints(a: std.mem.Allocator, v: Value) ![]u32 { + const out = try a.alloc(u32, v.array.items.len); + for (v.array.items, out) |x, *o| o.* = @intCast(x.integer); + return out; +} + +fn num(v: Value) f64 { + return switch (v) { + .float => |f| f, + .integer => |i| @floatFromInt(i), + else => unreachable, + }; +} + +const Scenario = struct { costs: costs_mod.Costs, set: lookup.Settings, eos: []u32, prompt: []u32, rounds: []const Value }; + +fn scenario(a: std.mem.Allocator, s: Value) !Scenario { + const o = s.object; + const vs = o.get("costs").?.object.get("verify").?.array.items; + const verify = try a.alloc(f64, vs.len); + for (vs, verify) |x, *y| y.* = num(x); + return .{ + .costs = .{ .verify = verify, .draft = num(o.get("costs").?.object.get("draft").?) }, + .set = .{ .plan = true, .min_match = o.get("min_match").?.integer, .max_rows = o.get("max_rows").?.integer }, + .eos = try ints(a, o.get("eos").?), + .prompt = try ints(a, o.get("prompt").?), + .rounds = o.get("rounds").?.array.items, + }; +} + +fn kindOf(k: []const u8) lookup.Kind { + return if (std.mem.eql(u8, k, "l")) .lookup else if (std.mem.eql(u8, k, "d")) .dspark else .serial; +} + +test "the lookup plan replays Python's Planner round for round" { + var parsed = try std.json.parseFromSlice(Value, gpa, golden, .{}); + defer parsed.deinit(); + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + const a = arena.allocator(); + var rounds: usize = 0; + var lookups: usize = 0; + for (parsed.value.object.get("scenarios").?.array.items) |sv| { + const sc = try scenario(a, sv); + const pl = lookup.Planner.init(sc.set, &sc.costs, true); + var r = try pl.start(gpa, sc.prompt); + defer r.deinit(); + for (sc.rounds) |rv| { + const o = rv.object; + const want = try ints(a, o.get("drafts").?); + const got = try pl.window(&r, o.get("left").?.integer, true, sc.eos); + try std.testing.expectEqual(kindOf(o.get("kind").?.string), r.kind); + try std.testing.expectEqualSlices(u32, want, got); + try pl.observe(&r, @intCast(o.get("rows").?.integer), @intCast(o.get("kept").?.integer), try ints(a, o.get("emitted").?)); + rounds += 1; + lookups += @intFromBool(r.kind == .lookup); + } + } + try std.testing.expect(rounds > 3000 and lookups > 400); +} + +test "the lanes proposer gives the Planner's drafts from the stream's context" { + var parsed = try std.json.parseFromSlice(Value, gpa, golden, .{}); + defer parsed.deinit(); + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + const a = arena.allocator(); + for (parsed.value.object.get("scenarios").?.array.items) |sv| { + const sc = try scenario(a, sv); + const pl = lookup.Planner.init(sc.set, &sc.costs, true); + var x = try lookup.Proposer.init(&pl, gpa, sc.prompt, sc.eos); + defer x.deinit(); + const p = x.proposer(); + var context: std.ArrayList(u32) = .empty; + defer context.deinit(gpa); + try context.appendSlice(gpa, sc.prompt); + for (sc.rounds) |rv| { + const o = rv.object; + const want = try ints(a, o.get("drafts").?); + const room = o.get("left").?.integer - 1; + const got = try p.propose(context.items, room); + try std.testing.expectEqualSlices(u32, want, got); + // a second ask in the same round reads the decision back (no second count of a skipped band) + try std.testing.expectEqualSlices(u32, want, try p.propose(context.items, room)); + p.round(@intCast(o.get("rows").?.integer), @intCast(o.get("kept").?.integer)); + try context.appendSlice(gpa, try ints(a, o.get("emitted").?)); + } + } +} diff --git a/zig/src/families/deepseek_v41/draft/policy.zig b/zig/src/families/deepseek_v41/draft/policy.zig new file mode 100644 index 0000000000..dff9d8efd6 --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/policy.zig @@ -0,0 +1,193 @@ +//! The DSpark depth as the lane engine's row policy (lanes.trim); commits feed calibration, rates, counterfactual. +const std = @import("std"); +const Allocator = std.mem.Allocator; +const lanes = @import("lanes"); +const lt = lanes.trim; +const depth = @import("depth.zig"); +const calib = @import("calib.zig"); +const tree = @import("tree.zig"); +const Costs = @import("costs.zig").Costs; +const ln = @import("lanes.zig"); +const acclog = @import("acclog.zig"); + +pub const Policy = struct { + gpa: Allocator, + depth: depth.Depth, + sibcal: calib.SibCal = .{}, + owner: *ln.Lanes = undefined, + sib_rows: usize, + dup: tree.Dup, + trees: u64 = 0, // rounds that verified a sibling + sib_wins: u64 = 0, + + pub fn init(gpa: Allocator, costs: Costs, opts: ln.Options) !Policy { + return .{ .gpa = gpa, .depth = try depth.Depth.init(gpa, costs, opts.shape.block, opts.depth), .sib_rows = opts.sib_rows, .dup = opts.dup }; + } + + pub fn deinit(p: *Policy) void { + p.depth.deinit(); + } + + pub fn rows(p: *Policy) lt.Trim { + return .{ .ptr = p, .vtable = &.{ .choose = choose, .commit = commit } }; + } + + fn self(ptr: *anyopaque) *Policy { + return @ptrCast(@alignCast(ptr)); + } + + fn choose(ptr: *anyopaque, windows: []const lt.Window, alone: bool, arena: Allocator, out: []lt.Choice) anyerror!void { + try chooseRows(ptr, windows, alone, arena, out); + if (std.c.getenv("TF_DSV41_ACCEPT_LOG") != null) try logRound(self(ptr), windows, arena); + } + + /// TF_DSV41_ACCEPT_LOG: the round's windows, each drafted slot's raw / calibrated confidences and chosen depth. + fn logRound(p: *Policy, windows: []const lt.Window, arena: Allocator) !void { + const x = p.owner; + var total: u64 = 0; + var others: u64 = 0; + var la: std.ArrayList(acclog.Ask) = .empty; + for (windows) |w| { + total += w.rows; + const st = x.state(w.stream); + if (!w.drafted or st == null or st.?.n == 0) { + others += w.rows; + continue; + } + const s = st.?; + var k: usize = 0; + if (s.shown_set) for (s.shown.items) |n| { + k += @intFromBool(n < s.n); + }; + const main = @min(s.n, w.rows - 1); + const q = try arena.alloc(f64, main); + for (s.conf[0..main], q, 0..) |c, *v, j| v.* = p.depth.cal.q(j, c); + try la.append(arena, .{ .slot = s.slot, .start = s.start, .most = main, .k = k, .conf = s.conf[0..main], .q = q }); + } + acclog.choose(windows.len, total, others, la.items); + } + + fn chooseRows(ptr: *anyopaque, windows: []const lt.Window, alone: bool, arena: Allocator, out: []lt.Choice) anyerror!void { + const p = self(ptr); + const x = p.owner; + var asks: std.ArrayList(depth.Depth.Ask) = .empty; + var at: std.ArrayList(usize) = .empty; // the window of each ask + var others: u64 = 0; + var other_windows: usize = 0; + for (windows, 0..) |w, i| { + const st = x.state(w.stream); + if (!w.drafted or st == null or st.?.n == 0) { + others += w.rows; + other_windows += 1; + continue; + } + const main = @min(st.?.n, w.rows - 1); + const conf = try arena.dupe(f64, st.?.conf[0..main]); + try asks.append(arena, .{ .slot = st.?.slot, .conf = conf, .most = main, .first = st.?.held[0], .position = st.?.start }); + try at.append(arena, i); + } + if (asks.items.len == 0) return; + const single = alone and asks.items.len == 1 and other_windows == 0; + if (single) { + const i = at.items[0]; + const st = x.state(windows[i].stream).?; + if (st.n_sib > 0 and windows[i].parents != null and windows[i].rows - 1 == st.nodes()) { + out[i] = try p.loneTree(arena, st, asks.items[0]); + return; + } + } + const ks = try arena.alloc(usize, asks.items.len); + try p.depth.chooseJoint(asks.items, others, other_windows, single, ks); + for (at.items, ks) |i, k| { + const st = x.state(windows[i].stream).?; + out[i] = .{ .count = @intCast(k) }; + try show(p.gpa, st, k, &.{}); + } + } + + /// A lone window with siblings: tree.plan picks the main chain's k and each sibling's rows. + fn loneTree(p: *Policy, arena: Allocator, st: *ln.St, ask: depth.Depth.Ask) !lt.Choice { + var sibs: [tree.max_siblings]f64 = undefined; + for (st.sib[0..st.n_sib], sibs[0..st.n_sib], 0..) |s, *q, j| q.* = p.sibcal.q(j, s.p); + const c = try p.depth.chooseTree(ask.slot, ask.conf, ask.most, sibs[0..st.n_sib], true, ask.first, p.dup, p.sib_rows); + var lens: [tree.max_siblings]u32 = @splat(0); + var total: u32 = @intCast(c.k); + for (lens[0..st.n_sib], c.lens[0..st.n_sib], st.sib_len[0..st.n_sib]) |*l, want, have| { + l.* = @intCast(@min(want, have)); + total += l.*; + } + if (total == c.k) { + try show(p.gpa, st, c.k, &.{}); + return .{ .count = @intCast(c.k) }; + } + // the main chain's first k, then each sibling's first rows (node indices in the held tree's order) + const nodes = try arena.alloc(u32, total); + for (nodes[0..c.k], 0..) |*n, i| n.* = @intCast(i); + var at: usize = c.k; + var node: u32 = st.n; + for (lens[0..st.n_sib], st.sib_len[0..st.n_sib]) |l, have| { + for (0..l) |i| nodes[at + i] = node + @as(u32, @intCast(i)); + at += l; + node += have; + } + try show(p.gpa, st, c.k, lens[0..st.n_sib]); + return .{ .count = total, .nodes = nodes }; + } + + /// Remember what the window verifies: `k` main drafts, then `lens[j]` rows of sibling j. + fn show(gpa: Allocator, st: *ln.St, k: usize, lens: []const u32) !void { + st.shown.clearRetainingCapacity(); + for (0..k) |i| try st.shown.append(gpa, @intCast(i)); + var node: u32 = st.n; + for (lens, st.sib_len[0..lens.len]) |l, have| { + for (0..l) |i| try st.shown.append(gpa, node + @as(u32, @intCast(i))); + node += have; + } + st.shown_set = true; + } + + /// The sibling a node belongs to (null: the main chain). + fn siblingOf(st: *const ln.St, node: u32) ?usize { + if (node < st.n) return null; + var at: u32 = st.n; + for (st.sib_len[0..st.n_sib], 0..) |l, j| { + if (node < at + l) return j; + at += l; + } + return null; + } + + fn commit(ptr: *anyopaque, c: lt.Commit) anyerror!void { + const p = self(ptr); + const st = p.owner.state(c.stream) orelse return; + defer st.shown_set = false; + acclog.commit(st.slot, c.rows, c.path.len, c.drafted, c.stream.finished, c.stream.emitted().len, c.stream.max_new); + if (!st.shown_set or !c.drafted) { + try p.depth.record(st.slot, c.rows, c.path.len, null); + p.depth.bonus(st.slot, c.bonus); + return; + } + var main_rows: usize = 1; + var any_sib = false; + for (st.shown.items) |n| { + if (n < st.n) main_rows += 1 else any_sib = true; + } + if (!any_sib) { + try p.depth.record(st.slot, c.rows, c.path.len, null); + p.depth.bonus(st.slot, c.bonus); + return; + } + // the branch the target chose: its first row's node, a sibling root or the main chain's first draft + const won: ?usize = if (c.path.len > 1) siblingOf(st, st.shown.items[c.path[1] - 1]) else null; + var seen: [tree.max_siblings]bool = @splat(false); + for (st.shown.items) |n| if (siblingOf(st, n)) |j| { + seen[j] = true; + }; + for (seen[0..st.n_sib], st.sib[0..st.n_sib], 0..) |s, sib, j| if (s) p.sibcal.record(j, sib.p, won == j); + p.trees += 1; + p.sib_wins += @intFromBool(won != null); + const main_keep = if (won == null) c.path.len else 1; + try p.depth.record(st.slot, main_rows, main_keep, c.path.len); + p.depth.bonus(st.slot, c.bonus); + } +}; diff --git a/zig/src/families/deepseek_v41/draft/spec.zig b/zig/src/families/deepseek_v41/draft/spec.zig new file mode 100644 index 0000000000..cd03680ee1 --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/spec.zig @@ -0,0 +1,307 @@ +//! The next round's DSpark pass started after a verify window (TF_DSV41_SPEC_DRAFT=1): GPU drafts while host commits. +const std = @import("std"); +const Allocator = std.mem.Allocator; +const lanes = @import("lanes"); +const iface = @import("iface.zig"); +const dspark = @import("dspark.zig"); + +pub const decay: f64 = 0.8; // typed: 1 - decay rounds in f64 +pub const min_rate: f64 = 0.3; +pub const probe = 8; + +pub const Settings = struct { + on: bool = false, // TF_DSV41_SPEC_DRAFT + nucleus: bool = false, // TF_DSV41_SPEC_NUCLEUS: rounds with top-p-without-top-k rows speculate too + + pub fn fromEnv() !Settings { + return parse(env("TF_DSV41_SPEC_DRAFT"), env("TF_DSV41_SPEC_NUCLEUS")); + } + + /// The knob values: 0 | 1 | on | off | true | false (case-insensitive), empty is 0. + pub fn parse(spec: ?[]const u8, nucleus: ?[]const u8) !Settings { + return .{ .on = try flag(spec), .nucleus = try flag(nucleus) }; + } + + fn flag(v: ?[]const u8) !bool { + const t = std.mem.trim(u8, v orelse return false, " \t\r\n"); + if (t.len == 0) return false; + var low: [8]u8 = undefined; + if (t.len > low.len) return error.BadSpecKnob; + const l = std.ascii.lowerString(&low, t); + for ([_][]const u8{ "1", "on", "true" }) |y| if (std.mem.eql(u8, l, y)) return true; + for ([_][]const u8{ "0", "off", "false" }) |n| if (std.mem.eql(u8, l, n)) return false; + return error.BadSpecKnob; + } + + fn env(name: [*:0]const u8) ?[]const u8 { + return if (std.c.getenv(name)) |v| std.mem.span(v) else null; + } +}; + +/// A nucleus row: T > 0, top_k 0 and 0 < top_p < 1. +pub fn isNucleus(s: ?lanes.Sampling) bool { + const x = s orelse return false; + return x.temperature > 0.0 and x.top_k == 0 and x.top_p > 0.0 and x.top_p < 1.0; +} + +/// Why a speculation was dropped. +pub const Why = enum { reset, unused, slots, commit, pending, inputs }; + +pub const Stats = struct { + launched: u64 = 0, + hit: u64 = 0, + miss: std.EnumArray(Why, u64) = .initFill(0), + commit_differs: u64 = 0, + /// commits whose ingest the speculation had done + ingests_skipped: u64 = 0, + /// slots speculated over every launch (each ingested its window's rows once) + launched_slots: u64 = 0, + + pub fn missed(s: Stats) u64 { + var n: u64 = 0; + for (s.miss.values) |v| n += v; + return n; + } +}; + +/// A DSpark window of the round, as the speculation sees it. +pub const Candidate = struct { + slot: u32, + start: u64, // the window's start (its pending row's position) + accepted: u32, // drafts the window's choices accept + bonus: u32, // the choice after them: the next pending token + sampling: ?lanes.Sampling, +}; + +const Win = struct { start: u64, accepted: u32, ask: iface.Ask, gen: u64, matched: ?bool = null }; + +pub const Spec = struct { + gpa: Allocator, + settings: Settings, + block: usize, + cands: usize, // gathered candidates a draft row (trees) + rate: []f64, + skipped: []u32, + gen: []u64, + /// the live speculation: its slots' windows (by slot), its asks in launch order + wins: []?Win, + asks: []iface.Ask, + n: usize = 0, + live: bool = false, + /// a pass begun on the device and not collected yet (`Pass.begin`) + begun: bool = false, + /// its proposals, in launch order (sized for every slot) + drafts: []u32, + conf: []f32, + cand: []i32, + base: []f32, + trees: bool = false, // the live pass gathered candidates and base logits + stats: Stats = .{}, + + pub fn init(gpa: Allocator, settings: Settings, slots: usize, block: usize, candidates: usize) !Spec { + var s: Spec = .{ .gpa = gpa, .settings = settings, .block = block, .cands = candidates, .rate = &.{}, .skipped = &.{}, .gen = &.{}, .wins = &.{}, .asks = &.{}, .drafts = &.{}, .conf = &.{}, .cand = &.{}, .base = &.{} }; + errdefer s.deinit(); + s.rate = try gpa.alloc(f64, slots); + @memset(s.rate, 1.0); + s.skipped = try gpa.alloc(u32, slots); + @memset(s.skipped, 0); + s.gen = try gpa.alloc(u64, slots); + @memset(s.gen, 0); + s.wins = try gpa.alloc(?Win, slots); + @memset(s.wins, null); + s.asks = try gpa.alloc(iface.Ask, slots); + s.drafts = try gpa.alloc(u32, slots * block); + s.conf = try gpa.alloc(f32, slots * block); + s.cand = try gpa.alloc(i32, slots * block * candidates); + s.base = try gpa.alloc(f32, slots * block * candidates); + return s; + } + + pub fn deinit(s: *Spec) void { + const gpa = s.gpa; + gpa.free(s.rate); + gpa.free(s.skipped); + gpa.free(s.gen); + gpa.free(s.wins); + gpa.free(s.asks); + gpa.free(s.drafts); + gpa.free(s.conf); + gpa.free(s.cand); + gpa.free(s.base); + } + + // -- bookkeeping ------------------------------------------------------------------------------------------------- + + /// The slot was reset (a new request, a release): its speculation and rate start over. + pub fn forget(s: *Spec, slot: u32) void { + s.gen[slot] += 1; + s.rate[slot] = 1.0; + s.skipped[slot] = 0; + if (s.live and s.wins[slot] != null) s.drop(.reset); + } + + pub fn wanted(s: *Spec, slot: u32) bool { + if (s.rate[slot] >= min_rate) return true; + s.skipped[slot] += 1; + if (s.skipped[slot] >= probe) { + s.skipped[slot] = 0; + return true; + } + return false; + } + + /// `wanted` without counting a skip: whether a launch after this slot's coming window would take it (the arm). + pub fn wouldWant(s: *const Spec, slot: u32) bool { + return s.rate[slot] >= min_rate or s.skipped[slot] + 1 >= probe; + } + + fn score(s: *Spec, hit: bool) void { + for (s.asks[0..s.n]) |a| s.rate[a.slot] = decay * s.rate[a.slot] + (1.0 - decay) * @as(f64, if (hit) 1.0 else 0.0); + } + + fn end(s: *Spec) void { + for (s.asks[0..s.n]) |a| s.wins[a.slot] = null; + s.n = 0; + s.live = false; + } + + fn drop(s: *Spec, why: Why) void { + s.stats.miss.getPtr(why).* += 1; + s.score(false); + s.end(); + } + + /// A window without speculation: an unused one is dropped. + pub fn clear(s: *Spec) void { + if (s.live) s.drop(.unused); + } + + /// Whether the round's windows may speculate at all (not tree rounds; nucleus rows only with the knob). + pub fn roundAllowed(s: *const Spec, tree_round: bool, any_nucleus: bool) bool { + return !tree_round and (!any_nucleus or s.settings.nucleus); + } + + // -- launch (after the window) ----------------------------------------------------------------------------------- + + /// Drops an unused speculation, takes the wanted candidates; returns their asks (caller ingests, then `run`s). + pub fn launch(s: *Spec, cands: []const Candidate) []const iface.Ask { + if (s.live) s.drop(.unused); + for (cands) |c| { + if (!s.wanted(c.slot)) continue; + if (s.wins[c.slot] != null) continue; // one window a slot + const next = c.start + c.accepted + 1; + const ask: iface.Ask = .{ .slot = c.slot, .anchor = c.bonus, .start = next, .params = dspark.Params.of(c.sampling, next) }; + s.asks[s.n] = ask; + s.wins[c.slot] = .{ .start = c.start, .accepted = c.accepted, .ask = ask, .gen = s.gen[c.slot] }; + s.n += 1; + } + if (s.n == 0) return &.{}; + s.live = true; + s.stats.launched += 1; + s.stats.launched_slots += s.n; + return s.asks[0..s.n]; + } + + fn proposals(s: *Spec, out: []iface.Proposal, trees: bool) void { + const b = s.block; + const c = s.cands; + for (out, 0..) |*p, i| p.* = .{ + .drafts = s.drafts[i * b ..][0..b], + .conf = s.conf[i * b ..][0..b], + .cand = if (trees) s.cand[i * b * c ..][0 .. b * c] else null, + .base = if (trees) s.base[i * b * c ..][0 .. b * c] else null, + }; + } + + /// The pass over the launched asks: begun on device if possible (`settle` collects), else now; `trees`: gather too. + pub fn run(s: *Spec, pass: iface.Pass, trees: bool) !void { + if (!s.live) return; + s.trees = trees; + if (pass.vtable.begin) |begin| { + try begin(pass.ptr, s.asks[0..s.n]); + s.begun = true; + return; + } + var props: [64]iface.Proposal = undefined; + if (s.n > props.len) return error.TooManySlots; + s.proposals(props[0..s.n], trees); + try pass.propose(s.asks[0..s.n], props[0..s.n]); + } + + /// Collects a begun pass before other pass, target or ingest work (collective order); proposals stay for `take`. + pub fn settle(s: *Spec, pass: iface.Pass) !void { + if (!s.begun) return; + s.begun = false; + var props: [64]iface.Proposal = undefined; + s.proposals(props[0..s.n], s.trees); + try pass.vtable.collect.?(pass.ptr, props[0..s.n]); + } + + // -- consumers (the next round) ---------------------------------------------------------------------------------- + + /// At the commit's ingest: true when the speculation ingested this window's kept path (a chain 0..accepted). + pub fn ingested(s: *Spec, slot: u32, start: u64, path: []const u32) bool { + if (!s.live) return false; + const w = &(s.wins[slot] orelse return false); + if (w.start != start or w.gen != s.gen[slot]) return false; + var ok = path.len == w.accepted + 1; + if (ok) for (path, 0..) |r, i| { + if (r != i) ok = false; + }; + w.matched = ok; + if (ok) s.stats.ingests_skipped += 1 else s.stats.commit_differs += 1; + return ok; + } + + /// At the draft: the speculated proposals into `out` (caller's ask order) and true, or false; the speculation ends. + pub fn take(s: *Spec, asks: []const iface.Ask, out: []iface.Proposal) bool { + if (!s.live) return false; + const why: ?Why = check: { + if (asks.len != s.n) break :check .slots; + for (asks) |a| if (a.slot >= s.wins.len or s.wins[a.slot] == null) break :check .slots; + for (asks) |a| { + const w = s.wins[a.slot].?; + if (w.matched != true or w.gen != s.gen[a.slot]) break :check .commit; + if (w.ask.anchor != a.anchor or w.ask.start != a.start) break :check .pending; + if (!std.meta.eql(w.ask.params, a.params)) break :check .inputs; + } + break :check null; + }; + if (why) |y| { + s.drop(y); + return false; + } + const b = s.block; + const c = s.cands; + for (asks, out) |a, o| { + const i = for (s.asks[0..s.n], 0..) |x, j| { + if (x.slot == a.slot) break j; + } else unreachable; + @memcpy(o.drafts[0..b], s.drafts[i * b ..][0..b]); + @memcpy(o.conf[0..b], s.conf[i * b ..][0..b]); + if (o.cand) |cd| @memcpy(cd[0 .. b * c], s.cand[i * b * c ..][0 .. b * c]); + if (o.base) |bs| @memcpy(bs[0 .. b * c], s.base[i * b * c ..][0 .. b * c]); + } + s.score(true); + s.stats.hit += 1; + s.end(); + return true; + } + + /// "speculative DSpark passes L launched, H used, M dropped (why n, ...)". + pub fn describe(s: *const Spec, buf: []u8) []const u8 { + var w = std.Io.Writer.fixed(buf); + const st = s.stats; + w.print("speculative DSpark passes {d} launched, {d} used, {d} dropped", .{ st.launched, st.hit, st.missed() }) catch return w.buffered(); + var first = true; + for (std.enums.values(Why)) |y| { + const v = st.miss.get(y); + if (v == 0) continue; + w.print("{s}{t} {d}", .{ if (first) " (" else ", ", y, v }) catch return w.buffered(); + first = false; + } + if (!first) w.writeAll(")") catch return w.buffered(); + w.print(", {d} commit ingests skipped", .{st.ingests_skipped}) catch {}; + return w.buffered(); + } +}; diff --git a/zig/src/families/deepseek_v41/draft/spec_test.zig b/zig/src/families/deepseek_v41/draft/spec_test.zig new file mode 100644 index 0000000000..fbeb227aee --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/spec_test.zig @@ -0,0 +1,214 @@ +//! spec.zig replays fixtures/spec-golden.json bit for bit; on the CPU twin a speculated run == the run without it. +const std = @import("std"); +const Value = std.json.Value; +const lanes = @import("lanes"); +const twin = @import("twin.zig"); +const ln = @import("lanes.zig"); +const costs_mod = @import("costs.zig"); +const spec = @import("spec.zig"); +const iface = @import("iface.zig"); +const dspark = @import("dspark.zig"); + +const gpa = std.testing.allocator; +const golden = @embedFile("fixtures/spec-golden.json"); + +test "the knobs parse as Python's" { + try std.testing.expectEqual(spec.Settings{}, try spec.Settings.parse(null, null)); + try std.testing.expectEqual(spec.Settings{ .on = true }, try spec.Settings.parse(" On", "")); + try std.testing.expectEqual(spec.Settings{ .on = true, .nucleus = true }, try spec.Settings.parse("true", "1")); + try std.testing.expectEqual(spec.Settings{}, try spec.Settings.parse("0", "off")); + try std.testing.expectError(error.BadSpecKnob, spec.Settings.parse("2", null)); + try std.testing.expect(spec.isNucleus(.{ .seed = 1, .temperature = 0.7, .top_k = 0, .top_p = 0.9 })); + try std.testing.expect(!spec.isNucleus(.{ .seed = 1, .temperature = 0.7, .top_k = 20, .top_p = 0.9 })); + try std.testing.expect(!spec.isNucleus(.{ .seed = 1, .temperature = 0.7, .top_k = 0, .top_p = 1.0 })); + try std.testing.expect(!spec.isNucleus(null)); +} + +fn int(v: Value) u64 { + return @intCast(v.integer); +} + +/// The golden's keyed noise: [] greedy, [seed] sampled (compared as params). +fn samplingOf(v: Value) ?lanes.Sampling { + if (v.array.items.len == 0) return null; + return .{ .seed = int(v.array.items[0]), .temperature = 0.7, .top_k = 20, .top_p = 0.95 }; +} + +fn expectState(s: *const spec.Spec, want: Value) !void { + const o = want.object; + for (o.get("rates").?.array.items, s.rate) |w, g| try std.testing.expectEqual(switch (w) { + .float => |f| f, + .integer => |i| @as(f64, @floatFromInt(i)), + else => unreachable, + }, g); + for (o.get("skipped").?.array.items, s.skipped) |w, g| try std.testing.expectEqual(int(w), g); + try std.testing.expectEqual(int(o.get("launched").?), s.stats.launched); + try std.testing.expectEqual(int(o.get("hit").?), s.stats.hit); + try std.testing.expectEqual(int(o.get("differs").?), s.stats.commit_differs); + const miss = o.get("miss").?.object; + for (std.enums.values(spec.Why)) |y| { + const w: u64 = if (miss.get(@tagName(y))) |v| int(v) else 0; + try std.testing.expectEqual(w, s.stats.miss.get(y)); + } +} + +test "the decisions replay spec.py's (launch, ingested, take, forget, clear, rates, probes)" { + var parsed = try std.json.parseFromSlice(Value, gpa, golden, .{}); + defer parsed.deinit(); + try std.testing.expectEqual(spec.decay, parsed.value.object.get("decay").?.float); + try std.testing.expectEqual(spec.min_rate, parsed.value.object.get("min_rate").?.float); + try std.testing.expectEqual(@as(u64, spec.probe), int(parsed.value.object.get("probe").?)); + var s = try spec.Spec.init(gpa, .{ .on = true }, 4, 5, 1); + defer s.deinit(); + var drafts: [4][5]u32 = undefined; + var conf: [4][5]f32 = undefined; + for (parsed.value.object.get("ops").?.array.items) |op| { + const o = op.object; + const kind = o.get("op").?.string; + if (std.mem.eql(u8, kind, "forget")) { + s.forget(@intCast(int(o.get("slot").?))); + } else if (std.mem.eql(u8, kind, "clear")) { + s.clear(); + } else if (std.mem.eql(u8, kind, "launch")) { + var cands: [4]spec.Candidate = undefined; + const items = o.get("cands").?.array.items; + for (items, cands[0..items.len]) |c, *x| { + const a = c.array.items; + x.* = .{ .slot = @intCast(int(a[0])), .start = int(a[1]), .accepted = @intCast(int(a[2])), .bonus = @intCast(int(a[3])), .sampling = samplingOf(a[4]) }; + } + const asks = s.launch(cands[0..items.len]); + try std.testing.expectEqual(o.get("got").?.bool, asks.len > 0); + } else if (std.mem.eql(u8, kind, "ingested")) { + for (o.get("commits").?.array.items) |c| { + const a = c.array.items; + var path: [16]u32 = undefined; + const n = int(a[2]) + 1; + for (path[0..n], 0..) |*p, i| p.* = @intCast(i); + try std.testing.expectEqual(a[3].bool, s.ingested(@intCast(int(a[0])), int(a[1]), path[0..n])); + } + } else { + const items = o.get("asks").?.array.items; + var asks: [4]iface.Ask = undefined; + var out: [4]iface.Proposal = undefined; + for (items, asks[0..items.len], out[0..items.len], 0..) |c, *x, *p, i| { + const a = c.array.items; + const start = int(a[2]); + x.* = .{ .slot = @intCast(int(a[0])), .anchor = @intCast(int(a[1])), .start = start, .params = dspark.Params.of(samplingOf(a[3]), start) }; + p.* = .{ .drafts = &drafts[i], .conf = &conf[i] }; + } + try std.testing.expectEqual(o.get("got").?.bool, s.take(asks[0..items.len], out[0..items.len])); + } + try expectState(&s, o.get("state").?); + } +} + +const Case = struct { prompt: []const u32, max_new: u32 = 70, sampling: ?lanes.Sampling = null }; + +const Run = struct { + out: [][]u32, + drafted: u64, + accepted: u64, + kept: [16]u64, + passes: u64, + ingests: u64, + stats: spec.Stats, + + fn deinit(r: Run) void { + for (r.out) |o| gpa.free(o); + gpa.free(r.out); + } +}; + +const Mode = enum { off, sync, device }; + +fn run(cases: []const Case, mode: Mode, settings: spec.Settings, siblings: u32) !Run { + const dims: twin.Dims = .{}; + const slots: u32 = 4; + const t = try twin.Twin.init(gpa, dims, slots + 1); + defer t.deinit(); + const d = try twin.Drafter.init(gpa, t, slots + 1); + defer d.deinit(); + var c = try @import("lanes_test.zig").servedCosts(gpa); + defer c.deinit(gpa); + const pass = if (mode == .device) d.passAsync() else d.pass(); + var set = settings; + set.on = mode != .off; + const x = try ln.Lanes.init(gpa, t.target(), pass, c, .{ .shape = d.shape, .slots = slots, .siblings = siblings, .spec = set }); + defer x.deinit(); + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + var cfg = try lanes.Config.init(gpa, try x.model(arena.allocator(), 64), 16, 15); + defer cfg.deinit(gpa); + var clock: lanes.fake.FixedClock = .{}; + var e = lanes.Engine.init(gpa, &cfg, x.backend(), clock.clock()); + defer e.deinit(); + x.attach(&e); + const streams = try gpa.alloc(lanes.Stream, cases.len); + defer gpa.free(streams); + for (cases, streams) |cs, *s| s.* = try lanes.Stream.init(gpa, .{ .id = "s", .prompt = cs.prompt, .max_new = cs.max_new, .sampling = cs.sampling, .drafts = true }); + defer for (streams) |*s| s.deinit(gpa); + for (streams) |*s| try e.addStream(s); + while (e.activeCount() > 0) try e.step(); + const out = try gpa.alloc([]u32, cases.len); + for (out, streams) |*o, *s| o.* = try gpa.dupe(u32, s.emitted()); + return .{ .out = out, .drafted = e.drafted, .accepted = e.accepted, .kept = x.policy.depth.stats.kept, .passes = d.passes, .ingests = d.ingests, .stats = if (x.spec) |sp| sp.stats else .{} }; +} + +fn expectSame(cases: []const Case, settings: spec.Settings, siblings: u32) ![3]spec.Stats { + const off = try run(cases, .off, settings, siblings); + defer off.deinit(); + for (cases, off.out) |cs, got| { // the reference itself is the serial decode + const t = try twin.Twin.init(gpa, .{}, 1); + defer t.deinit(); + const want = try t.serial(0, cs.prompt, cs.max_new, cs.sampling, null); + defer gpa.free(want); + try std.testing.expectEqualSlices(u32, want, got); + } + var stats: [3]spec.Stats = undefined; + for ([_]Mode{ .sync, .device }, 1..) |m, i| { + const r = try run(cases, m, settings, siblings); + defer r.deinit(); + for (off.out, r.out) |a, b| try std.testing.expectEqualSlices(u32, a, b); + // the same drafts: the same windows, acceptance and kept counts as the run without speculation + try std.testing.expectEqual(off.drafted, r.drafted); + try std.testing.expectEqual(off.accepted, r.accepted); + try std.testing.expectEqualSlices(u64, &off.kept, &r.kept); + // every used speculation replaced a pass, every launch ran one; skipped commits saved their ingests + try std.testing.expectEqual(off.passes + r.stats.launched - r.stats.hit, r.passes); + try std.testing.expectEqual(off.ingests + r.stats.launched_slots, r.ingests + r.stats.ingests_skipped); + stats[i] = r.stats; + } + stats[0] = .{}; + return stats; +} + +const p1 = [_]u32{ 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3, 8, 4 }; +const p2 = [_]u32{ 2, 7, 1, 8, 2, 8, 1, 8, 2, 8, 4, 5, 9, 0, 4, 5 }; +const p3 = [_]u32{ 10, 20, 30, 40, 10, 20, 30, 40, 10, 20, 30 }; +const hot: lanes.Sampling = .{ .seed = 11, .temperature = 0.8, .top_k = 0, .top_p = 0.9 }; +const warm: lanes.Sampling = .{ .seed = 5, .temperature = 1.0, .top_k = 20, .top_p = 0.95, .min_p = 0.02 }; + +test "one slot: speculated == unspeculated == serial, greedy and keyed, most speculations used" { + for ([_]?lanes.Sampling{ null, warm }) |s| { + const st = try expectSame(&.{.{ .prompt = &p1, .sampling = s, .max_new = 90 }}, .{}, 0); + for (st[1..]) |x| try std.testing.expect(x.launched > 0 and x.hit * 2 > x.launched and x.ingests_skipped > 0); + } +} + +test "shared rounds over several slots: one pass serves the speculated slots" { + const cases = [_]Case{ .{ .prompt = &p1 }, .{ .prompt = &p2, .sampling = warm, .max_new = 50 }, .{ .prompt = &p3 } }; + const st = try expectSame(&cases, .{}, 0); + for (st[1..]) |x| try std.testing.expect(x.launched > 0 and x.hit > 0); +} + +test "nucleus rows speculate only with TF_DSV41_SPEC_NUCLEUS" { + const cases = [_]Case{.{ .prompt = &p2, .sampling = hot, .max_new = 60 }}; + const off = try expectSame(&cases, .{}, 0); + for (off[1..]) |x| try std.testing.expectEqual(@as(u64, 0), x.launched); + const on = try expectSame(&cases, .{ .nucleus = true }, 0); + for (on[1..]) |x| try std.testing.expect(x.launched > 0 and x.hit > 0); +} + +test "tree rounds are not speculated, the chain rounds around them are" { + _ = try expectSame(&.{.{ .prompt = &p2, .max_new = 120 }}, .{}, 2); +} diff --git a/zig/src/families/deepseek_v41/draft/tree.zig b/zig/src/families/deepseek_v41/draft/tree.zig new file mode 100644 index 0000000000..664f3a99c4 --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/tree.zig @@ -0,0 +1,213 @@ +//! First-position siblings for DSpark rounds, each its own Markov chain beside the main one; cost model picks the tree. +const std = @import("std"); +const Allocator = std.mem.Allocator; +const lanes = @import("lanes"); +const dspark = @import("dspark.zig"); + +extern "c" fn exp(x: f64) f64; +extern "c" fn log(x: f64) f64; + +pub const max_siblings = 3; +const max_nodes = 64; + +/// What a sibling costs: `ms` a duplicated pending row; `rows`: duplicates take window rows (shadow slots). +pub const Dup = struct { ms: f64 = 0.0, rows: bool = false }; + +pub const Plan = struct { k: usize, lens: [max_siblings]usize, ms: f64, expected: f64 }; + +const Node = struct { p: f64, kind: u2, j: u8 }; // kind 0 main, 1 sibling root, 2 sibling row + +fn likelier(_: void, a: Node, b: Node) bool { + return a.p > b.p; +} + +/// Tree maximizing E - rate (verify[R-1] + dup.ms x siblings) over likeliest nodes; `qm` main, `sibs`, `cont` chances. +pub fn plan(qm: []const f64, sibs: []const f64, cont: []const f64, verify: []const f64, rate: f64, least: usize, dup: Dup, sib_rows: usize, max_rows: usize) Plan { + const top = @min(verify.len, max_rows); + var nodes: [max_nodes]Node = undefined; + var n: usize = 0; + var p: f64 = 1.0; + for (qm) |q| { + p *= q; + nodes[n] = .{ .p = p, .kind = 0, .j = 0 }; + n += 1; + } + for (sibs[0..@min(sibs.len, max_siblings)], 1..) |sp, j| { + p = sp; + for (0..sib_rows) |i| { + if (i > 0) p *= if (i - 1 < cont.len) cont[i - 1] else 0.0; + if (p <= 0.0 or n == max_nodes) break; + nodes[n] = .{ .p = p, .kind = if (i == 0) 1 else 2, .j = @intCast(j) }; + n += 1; + } + } + const head: usize = @intFromBool(qm.len > 0); // the main chain's first draft before any sibling + std.sort.insertion(Node, nodes[head..n], {}, likelier); // stable: parents stay before children + var out: Plan = .{ .k = 0, .lens = @splat(0), .ms = 0.0, .expected = 0.0 }; + var best_s = -std.math.inf(f64); + var e: f64 = 1.0; + var dups: usize = 0; + var best: usize = 0; + for (0..n + 1) |m| { + if (m > 0) { + e += nodes[m - 1].p; + dups += @intFromBool(nodes[m - 1].kind == 1); + } + if (m + 1 + (if (dup.rows) dups else 0) > top) break; + if (m < least) continue; + const ms = verify[m] + dup.ms * @as(f64, @floatFromInt(dups)); + const s = e - rate * ms; + if (s > best_s + 1e-12) { + best = m; + best_s = s; + out.expected = e; + out.ms = ms; + } + } + for (nodes[0..best]) |x| { + if (x.kind == 0) out.k += 1 else out.lens[x.j - 1] += 1; + } + return out; +} + +/// TF_DSV41_TREE (0 off, 2-4 branches), _TREE_DUP_MS (0.4), _TREE_ROWS (4 a branch); _TREE_PC is refused. +pub const Settings = struct { + siblings: u32 = 0, + sib_rows: u32 = 4, + dup: Dup = .{ .ms = 0.4, .rows = true }, + + pub fn parse(tree: ?[]const u8, dup_ms: ?[]const u8, rows: ?[]const u8, pc: ?[]const u8) !Settings { + const b = try std.fmt.parseInt(u32, trimmed(tree) orelse "0", 10); + if (b == 1 or b > max_siblings + 1) return error.TreeBranches; + const ms = try std.fmt.parseFloat(f64, trimmed(dup_ms) orelse "0.4"); + const r = try std.fmt.parseInt(u32, trimmed(rows) orelse "4", 10); + if (ms < 0.0 or r < 1 or r > 15) return error.TreeKnobs; + if (trimmed(pc)) |v| if (!std.mem.eql(u8, v, "0") and !std.mem.eql(u8, v, "off") and !std.mem.eql(u8, v, "false")) return error.TreePcNotPorted; + return .{ .siblings = if (b == 0) 0 else b - 1, .sib_rows = r, .dup = .{ .ms = ms, .rows = true } }; + } + + pub fn fromEnv() !Settings { + return parse(env("TF_DSV41_TREE"), env("TF_DSV41_TREE_DUP_MS"), env("TF_DSV41_TREE_ROWS"), env("TF_DSV41_TREE_PC")); + } + + fn env(name: [*:0]const u8) ?[]const u8 { + return if (std.c.getenv(name)) |v| std.mem.span(v) else null; + } + + fn trimmed(v: ?[]const u8) ?[]const u8 { + const t = std.mem.trim(u8, v orelse return null, " \t"); + return if (t.len == 0) null else t; + } +}; + +pub const Sibling = struct { token: u32, p: f64 }; + +const Cand = struct { z: f64, id: u32, score: f64 = 0.0, p: f64 = 0.0, at: usize = 0 }; + +fn byZ(_: void, a: Cand, b: Cand) bool { + return a.z > b.z or (a.z == b.z and a.id < b.id); +} + +fn byScore(_: void, a: Cand, b: Cand) bool { + return a.score > b.score or (a.score == b.score and a.at < b.at); +} + +/// The first `out.len` tokens of `z` over `ids` not in `exclude`, in keyed order, with their draft probabilities. +pub fn ranked(scratch: Allocator, ids: []const i32, z: []const f32, position: u64, sampling: ?lanes.Sampling, exclude: []const u32, out: []Sibling) !usize { + var c: std.ArrayList(Cand) = .empty; + defer c.deinit(scratch); + for (ids, z) |id, v| if (id >= 0) try c.append(scratch, .{ .z = v, .id = @intCast(id) }); + if (c.items.len == 0 or out.len == 0) return 0; + std.sort.pdq(Cand, c.items, {}, byZ); + var keep = c.items; + const probs = try scratch.alloc(f64, keep.len); + defer scratch.free(probs); + if (sampling) |s| { + keep = keep[0..@max(1, @min(if (s.top_k != 0) @as(usize, s.top_k) else keep.len, keep.len))]; + const t = @max(s.temperature, 1e-6); + for (keep) |*x| x.z /= t; // z is now the scaled value + var top = keep[0].z; + for (keep) |x| top = @max(top, x.z); + for (keep, probs[0..keep.len]) |x, *q| q.* = exp(x.z - top); + normalize(probs[0..keep.len]); + if (s.top_p > 0.0 and s.top_p < 1.0) { + var cum: f64 = 0.0; + var below: usize = 0; + for (probs[0..keep.len]) |q| { + cum += q; + below += @intFromBool(cum < s.top_p); + } + keep = keep[0..@min(keep.len, below + 1)]; + } + if (s.min_p > 0.0) { + const floor = keep[0].z + s.minLog(); + var n: usize = 0; + for (keep) |x| n += @intFromBool(x.z >= floor); + keep = keep[0..n]; + } + normalize(probs[0..keep.len]); + for (keep, probs[0..keep.len], 0..) |*x, q, i| { + x.p = q; + x.at = i; + x.score = x.z - log(-log(lanes.sampling.uniform(s.seed, position, x.id))); + } + std.sort.pdq(Cand, keep, {}, byScore); + } else { + for (keep, probs) |x, *q| q.* = exp(x.z - keep[0].z); + normalize(probs); + for (keep, probs) |*x, q| x.p = q; + } + var n: usize = 0; + for (keep) |x| { + if (std.mem.indexOfScalar(u32, exclude, x.id) != null) continue; + out[n] = .{ .token = x.id, .p = x.p }; + n += 1; + if (n == out.len) break; + } + return n; +} + +fn normalize(p: []f64) void { + const total = lanes.sampling.pairwiseSum(p); + for (p) |*x| x.* /= total; +} + +/// The Markov chain behind `first`: the drafts of positions 1 .. rows - 1 (the chain rule keyed at pos0 + i). +pub fn continuation(scratch: Allocator, m: dspark.Markov, ids: []const i32, base: []const f32, c: usize, first: u32, rows: usize, params: dspark.Params, out: []u32) !usize { + const z = try scratch.alloc(f32, c); + defer scratch.free(z); + const n_rows = ids.len / c; + var prev = first; + var n: usize = 0; + var i: usize = 1; + while (i < @min(rows, n_rows)) : (i += 1) { + const row = ids[i * c ..][0..c]; + dspark.markovZ(m, row, base[i * c ..][0..c], prev, z); + const tok = (try dspark.pick(scratch, row, z, params.pos0 + i, params.sampling())) orelse break; + out[n] = tok; + n += 1; + prev = tok; + } + return n; +} + +test "tree settings read as Python's tree.settings" { + const off = try Settings.parse(null, null, null, null); + try std.testing.expectEqual(@as(u32, 0), off.siblings); + const on = try Settings.parse("3", " 0.5 ", "6", "0"); + try std.testing.expectEqual(@as(u32, 2), on.siblings); + try std.testing.expectEqual(@as(u32, 6), on.sib_rows); + try std.testing.expect(on.dup.rows and on.dup.ms == 0.5); + try std.testing.expectError(error.TreeBranches, Settings.parse("1", null, null, null)); + try std.testing.expectError(error.TreeKnobs, Settings.parse("2", null, "16", null)); + try std.testing.expectError(error.TreePcNotPorted, Settings.parse("2", null, null, "1")); +} + +test "a confident main chain takes no sibling; an unsure first draft buys one" { + var verify: [16]f64 = undefined; + for (&verify, 0..) |*v, r| v.* = 21.4 + 4.0 * @as(f64, @floatFromInt(r)); + const sure = plan(&.{ 0.95, 0.95, 0.95 }, &.{0.03}, &.{ 0.95, 0.95 }, &verify, 0.05, 1, .{}, 4, 16); + try std.testing.expectEqual(@as(usize, 0), sure.lens[0]); + const unsure = plan(&.{ 0.4, 0.9 }, &.{0.45}, &.{0.9}, &verify, 0.03, 1, .{}, 4, 16); + try std.testing.expect(unsure.lens[0] >= 1 and unsure.k >= 1); +} diff --git a/zig/src/families/deepseek_v41/draft/twin.zig b/zig/src/families/deepseek_v41/draft/twin.zig new file mode 100644 index 0000000000..c665150483 --- /dev/null +++ b/zig/src/families/deepseek_v41/draft/twin.zig @@ -0,0 +1,469 @@ +//! A CPU twin of the lanes' model path: a one-layer attention `Target` and a DSpark-shaped `Pass` on the same vtables. +const std = @import("std"); +const Allocator = std.mem.Allocator; +const lanes = @import("lanes"); +const iface = @import("iface.zig"); +const dspark = @import("dspark.zig"); + +extern "c" fn exp(x: f64) f64; + +pub const Dims = struct { vocab: u32 = 48, dim: u32 = 16, seed: u64 = 1, bigram: f32 = 6.0, head: f32 = 0.12, conf: f32 = 0.03, window: u32 = 16, block: u32 = 5 }; + +const Slot = struct { + k: std.ArrayList(f32) = .empty, // committed rows' keys [len][dim] + v: std.ArrayList(f32) = .empty, + base: usize = 0, // rows before the last window + rows: std.ArrayList(f32) = .empty, // the last window's keys, values and taps [rows][3 dim] + parents: std.ArrayList(i32) = .empty, + taps: std.ArrayList(f32) = .empty, + live: bool = false, + + fn deinit(s: *Slot, gpa: Allocator) void { + s.k.deinit(gpa); + s.v.deinit(gpa); + s.rows.deinit(gpa); + s.parents.deinit(gpa); + s.taps.deinit(gpa); + } + + fn len(s: Slot, d: usize) usize { + return s.k.items.len / d; + } +}; + +pub const Twin = struct { + gpa: Allocator, + dims: Dims, + emb: []f32, + wq: []f32, + wk: []f32, + wv: []f32, + wo: []f32, + bigram: []f32, + slots: []Slot, + windows: u64 = 0, // rows' passes run (tests) + widest: usize = 0, // the most rows one forward held + + pub fn init(gpa: Allocator, dims: Dims, slots: usize) !*Twin { + var prng = std.Random.DefaultPrng.init(dims.seed); + const r = prng.random(); + const v: usize = dims.vocab; + const d: usize = dims.dim; + const t = try gpa.create(Twin); + t.* = .{ .gpa = gpa, .dims = dims, .emb = try normal(gpa, r, v * d, 1.0), .wq = try normal(gpa, r, d * d, 0.5), .wk = try normal(gpa, r, d * d, 0.5), .wv = try normal(gpa, r, d * d, 0.5), .wo = try normal(gpa, r, d * v, dims.head), .bigram = try normal(gpa, r, v * v, dims.bigram), .slots = try gpa.alloc(Slot, slots) }; + for (t.slots) |*s| s.* = .{}; + return t; + } + + pub fn deinit(t: *Twin) void { + for (t.slots) |*s| s.deinit(t.gpa); + for ([_][]f32{ t.emb, t.wq, t.wk, t.wv, t.wo, t.bigram }) |x| t.gpa.free(x); + t.gpa.free(t.slots); + t.gpa.destroy(t); + } + + fn normal(gpa: Allocator, r: std.Random, n: usize, scale: f32) ![]f32 { + const out = try gpa.alloc(f32, n); + for (out) |*o| o.* = r.floatNorm(f32) * scale; + return out; + } + + /// x = emb[token] + a position signal. + pub fn input(t: *const Twin, token: u32, pos: u64, x: []f32) void { + const d = t.dims.dim; + for (x, t.emb[@as(usize, token) * d ..][0..d], 0..) |*o, e, j| o.* = e + 0.5 * @sin(@as(f32, @floatFromInt(pos % 4096)) / @as(f32, @floatFromInt(j + 1))); + } + + pub fn project(t: *const Twin, w: []const f32, x: []const f32, out: []f32) void { + const d = t.dims.dim; + @memset(out, 0.0); + for (x, 0..) |xi, i| { + for (out, w[i * d ..][0..d]) |*o, wij| o.* += xi * wij; + } + } + + /// Attention of query `q` over keys/values (row-major [n][dim]) in their order, added to `h`. + pub fn attend(t: *const Twin, q: []const f32, keys: []const []const f32, values: []const []const f32, h: []f32) void { + const d = t.dims.dim; + var m: f32 = -std.math.inf(f32); + for (keys) |k| m = @max(m, dot(q, k) / @sqrt(@as(f32, @floatFromInt(d)))); + var sum: f32 = 0.0; + for (keys) |k| sum += @floatCast(exp(@as(f64, dot(q, k) / @sqrt(@as(f32, @floatFromInt(d))) - m))); + for (keys, values) |k, v| { + const a: f32 = @as(f32, @floatCast(exp(@as(f64, dot(q, k) / @sqrt(@as(f32, @floatFromInt(d))) - m)))) / sum; + for (h, v) |*o, vi| o.* += a * vi; + } + } + + pub fn logits(t: *const Twin, h: []const f32, prev: u32, out: []f32) void { + const v = t.dims.vocab; + @memset(out, 0.0); + for (h, 0..) |hi, i| { + for (out, t.wo[i * v ..][0..v]) |*o, w| o.* += hi * w; + } + for (out, t.bigram[@as(usize, prev) * v ..][0..v]) |*o, b| o.* += b; + } + + /// The keyed choice over the whole vocabulary (greedy: value desc, id asc). + pub fn choose(t: *const Twin, scratch: Allocator, row: []const f32, draw: u64, s: ?lanes.Sampling) !u32 { + const smp = s orelse { + var best: usize = 0; + for (row, 0..) |x, i| if (x > row[best]) { + best = i; + }; + return @intCast(best); + }; + const vals = try scratch.alloc(f64, row.len); + defer scratch.free(vals); + const ids = try scratch.alloc(u64, row.len); + defer scratch.free(ids); + for (vals, ids, row, 0..) |*o, *id, x, i| { + o.* = x; + id.* = i; + } + _ = t; + return @intCast(try lanes.sampling.choose(scratch, vals, ids, draw, smp)); + } + + fn rowsOf(t: *Twin, s: *Slot, tokens: []const u32, parents: ?[]const i32, start: u64, draws: []const u64, smp: ?lanes.Sampling, choices: []u32) !void { + const d = t.dims.dim; + const a = t.gpa; + s.rows.clearRetainingCapacity(); + s.taps.clearRetainingCapacity(); + s.parents.clearRetainingCapacity(); + const n = tokens.len; + try s.rows.resize(a, n * 2 * d); + try s.taps.resize(a, n * d); + for (0..n) |r| try s.parents.append(a, if (parents) |p| p[r] else @as(i32, @intCast(r)) - 1); + const committed = s.len(d); + const keys = try a.alloc([]const f32, committed + n); + defer a.free(keys); + const vals = try a.alloc([]const f32, committed + n); + defer a.free(vals); + for (0..committed) |i| { + keys[i] = s.k.items[i * d ..][0..d]; + vals[i] = s.v.items[i * d ..][0..d]; + } + var x: [64]f32 = undefined; + var q: [64]f32 = undefined; + const row = try a.alloc(f32, t.dims.vocab); + defer a.free(row); + var chain: [64]usize = undefined; + for (0..n) |r| { + var depth: usize = 0; + var at: i32 = @intCast(r); + while (at >= 0) : (at = s.parents.items[@intCast(at)]) { + chain[depth] = @intCast(at); + depth += 1; + } + const kr = s.rows.items[r * 2 * d ..][0..d]; + const vr = s.rows.items[r * 2 * d + d ..][0..d]; + const h = s.taps.items[r * d ..][0..d]; + t.input(tokens[r], start + depth - 1, x[0..d]); + t.project(t.wq, x[0..d], q[0..d]); + t.project(t.wk, x[0..d], kr); + t.project(t.wv, x[0..d], vr); + for (0..depth) |i| { // ancestors root first, then the row itself + const anc = chain[depth - 1 - i]; + keys[committed + i] = s.rows.items[anc * 2 * d ..][0..d]; + vals[committed + i] = s.rows.items[anc * 2 * d + d ..][0..d]; + } + @memcpy(h, x[0..d]); + t.attend(q[0..d], keys[0 .. committed + depth], vals[0 .. committed + depth], h); + t.logits(h, tokens[r], row); + choices[r] = try t.choose(a, row, draws[r], smp); + } + s.base = committed; + for (0..n) |r| { // every row stays until keep (a chain that is never kept keeps them all) + try s.k.appendSlice(a, s.rows.items[r * 2 * d ..][0..d]); + try s.v.appendSlice(a, s.rows.items[r * 2 * d + d ..][0..d]); + } + t.windows += 1; + } + + pub fn target(t: *Twin) iface.Target { + return .{ .ptr = t, .vtable = &.{ .prefill = prefill, .window = window, .keep = keep, .taps = taps, .release = release } }; + } + + fn self(ptr: *anyopaque) *Twin { + return @ptrCast(@alignCast(ptr)); + } + + fn prefill(ptr: *anyopaque, slot: u32, ids: []const u32, smp: ?lanes.Sampling, draw: u64) anyerror!u32 { + const t = self(ptr); + const s = &t.slots[slot]; + if (s.live) return error.SlotLive; + s.k.clearRetainingCapacity(); + s.v.clearRetainingCapacity(); + s.live = true; + const draws = try t.gpa.alloc(u64, ids.len); + defer t.gpa.free(draws); + for (draws, 0..) |*w, i| w.* = i + 1; + draws[ids.len - 1] = draw; + const choices = try t.gpa.alloc(u32, ids.len); + defer t.gpa.free(choices); + try t.rowsOf(s, ids, null, 0, draws, smp, choices); + return choices[ids.len - 1]; + } + + fn window(ptr: *anyopaque, segments: []const iface.Segment, choices: [][]u32) anyerror!void { + const t = self(ptr); + var rows: usize = 0; + for (segments) |g| rows += g.tokens.len; + t.widest = @max(t.widest, rows); + for (segments, choices) |g, c| { + const s = &t.slots[g.slot]; + if (!s.live or g.start != s.len(t.dims.dim)) return error.PositionMismatch; + try t.rowsOf(s, g.tokens, g.parents, g.start, g.draws, g.sampling, c); + } + } + + fn keep(ptr: *anyopaque, slot: u32, path: []const u32) anyerror!void { + const t = self(ptr); + const s = &t.slots[slot]; + const d = t.dims.dim; + for (path[1..], path[0 .. path.len - 1]) |r, parent| if (s.parents.items[r] != @as(i32, @intCast(parent))) return error.NotAPath; + s.k.shrinkRetainingCapacity(s.base * d); + s.v.shrinkRetainingCapacity(s.base * d); + for (path) |r| { + try s.k.appendSlice(t.gpa, s.rows.items[r * 2 * d ..][0..d]); + try s.v.appendSlice(t.gpa, s.rows.items[r * 2 * d + d ..][0..d]); + } + } + + /// Drop the slot's last window without committing a row (branches.zig: a chain whose branch lost). + pub fn drop(t: *Twin, slot: u32) void { + const s = &t.slots[slot]; + s.k.shrinkRetainingCapacity(s.base * t.dims.dim); + s.v.shrinkRetainingCapacity(s.base * t.dims.dim); + } + + fn taps(ptr: *anyopaque, slot: u32) iface.Taps { + const t = self(ptr); + const s = &t.slots[slot]; + return .{ .address = @intFromPtr(s.taps.items.ptr), .rows = @intCast(s.taps.items.len / t.dims.dim), .stride = t.dims.dim }; + } + + fn release(ptr: *anyopaque, slot: u32) void { + const t = self(ptr); + t.slots[slot].live = false; + } + + /// The serial reference without lanes: the prompt, then one row a step, each drawn at its own position. + pub fn serial(t: *Twin, slot: u32, prompt: []const u32, n: usize, smp: ?lanes.Sampling, eos: ?u32) ![]u32 { + var out: std.ArrayList(u32) = .empty; + errdefer out.deinit(t.gpa); + var tok = try prefill(t, slot, prompt, smp, prompt.len); + while (out.items.len < n) { + try out.append(t.gpa, tok); + if (eos != null and tok == eos.?) break; + const at: u64 = prompt.len + out.items.len - 1; + var c: [1]u32 = undefined; + var cs = [_][]u32{&c}; + try window(t, &.{.{ .slot = slot, .start = at, .tokens = &.{tok}, .parents = null, .draws = &.{at + 1}, .sampling = smp }}, &cs); + tok = c[0]; + } + release(t, slot); + return out.toOwnedSlice(t.gpa); + } +}; + +fn dot(a: []const f32, b: []const f32) f32 { + var s: f32 = 0.0; + for (a, b) |x, y| s += x * y; + return s; +} + +/// The twin's DSpark: rings of target taps, a non-causal block over them, the target head, the bigram chain rule. +pub const Drafter = struct { + gpa: Allocator, + t: *Twin, + shape: dspark.Shape, + rings: [][]f32, // slot -> [ring][dim] + tags: [][]i64, // slot -> the position each ring row holds (-1: none) + w1: []u16, + w2: []u16, + cw: []f32, + passes: u64 = 0, + ingests: u64 = 0, // ingest calls (tests) + /// `begin` / `collect` (passAsync): the pass reads rings at `begin`, the host takes results at `collect`; all slots + stash_drafts: []u32, + stash_conf: []f32, + stash_cand: []i32, + stash_base: []f32, + stash_n: usize = 0, + begun: bool = false, + + pub fn init(gpa: Allocator, t: *Twin, slots: usize) !*Drafter { + const v = t.dims.vocab; + const rank = std.math.ceilPowerOfTwo(u32, v) catch unreachable; + const shape: dspark.Shape = .{ .block = t.dims.block, .noise = v - 1, .window = t.dims.window, .rank = rank, .hidden = t.dims.dim, .candidates = v }; + const x = try gpa.create(Drafter); + x.* = .{ .gpa = gpa, .t = t, .shape = shape, .rings = try gpa.alloc([]f32, slots), .tags = try gpa.alloc([]i64, slots), .w1 = try gpa.alloc(u16, @as(usize, v) * rank), .w2 = try gpa.alloc(u16, @as(usize, v) * rank), .cw = try gpa.alloc(f32, t.dims.dim + rank), .stash_drafts = try gpa.alloc(u32, slots * shape.block), .stash_conf = try gpa.alloc(f32, slots * shape.block), .stash_cand = try gpa.alloc(i32, slots * shape.block * v), .stash_base = try gpa.alloc(f32, slots * shape.block * v) }; + const ring = shape.ring(); + for (x.rings, x.tags) |*r, *g| { + r.* = try gpa.alloc(f32, @as(usize, ring) * t.dims.dim); + g.* = try gpa.alloc(i64, ring); + @memset(g.*, -1); + } + @memset(x.w1, 0); + for (0..v) |p| x.w1[p * rank + p] = dspark.toBf16(1.0); + for (0..v) |id| for (0..rank) |r| { + x.w2[id * rank + r] = if (r < v) dspark.toBf16(t.bigram[r * v + id]) else 0; + }; + var prng = std.Random.DefaultPrng.init(t.dims.seed ^ 0x5eed); + for (x.cw) |*c| c.* = prng.random().floatNorm(f32) * t.dims.conf; + return x; + } + + pub fn deinit(x: *Drafter) void { + for (x.rings, x.tags) |r, g| { + x.gpa.free(r); + x.gpa.free(g); + } + x.gpa.free(x.rings); + x.gpa.free(x.tags); + x.gpa.free(x.w1); + x.gpa.free(x.w2); + x.gpa.free(x.cw); + x.gpa.free(x.stash_drafts); + x.gpa.free(x.stash_conf); + x.gpa.free(x.stash_cand); + x.gpa.free(x.stash_base); + x.gpa.destroy(x); + } + + pub fn markovHeads(x: *const Drafter) dspark.Markov { + return .{ .w1 = x.w1, .w2 = x.w2, .rank = x.shape.rank, .conf = x.cw }; + } + + pub fn pass(x: *Drafter) iface.Pass { + return .{ .ptr = x, .vtable = &.{ .ingest = ingest, .propose = propose, .reset = reset, .markov = markovFn } }; + } + + /// The same pass with `begin` / `collect` (iface.Pass): the speculative pass's device path on the CPU. + pub fn passAsync(x: *Drafter) iface.Pass { + return .{ .ptr = x, .vtable = &.{ .ingest = ingest, .propose = propose, .reset = reset, .markov = markovFn, .begin = begin, .collect = collect } }; + } + + fn stashed(x: *Drafter, i: usize) iface.Proposal { + const b = x.shape.block; + const v = x.t.dims.vocab; + return .{ .drafts = x.stash_drafts[i * b ..][0..b], .conf = x.stash_conf[i * b ..][0..b], .cand = x.stash_cand[i * b * v ..][0 .. b * v], .base = x.stash_base[i * b * v ..][0 .. b * v] }; + } + + fn begin(ptr: *anyopaque, asks: []const iface.Ask) anyerror!void { + const x = self(ptr); + if (x.begun) return error.PassBegun; + var props: [16]iface.Proposal = undefined; + if (asks.len > props.len or asks.len * x.shape.block > x.stash_drafts.len) return error.TooManySlots; + for (props[0..asks.len], 0..) |*p, i| p.* = x.stashed(i); + try propose(ptr, asks, props[0..asks.len]); + x.stash_n = asks.len; + x.begun = true; + } + + fn collect(ptr: *anyopaque, out: []iface.Proposal) anyerror!void { + const x = self(ptr); + if (!x.begun or out.len != x.stash_n) return error.NoPassBegun; + x.begun = false; + for (out, 0..) |o, i| { + const p = x.stashed(i); + @memcpy(o.drafts, p.drafts[0..o.drafts.len]); + @memcpy(o.conf, p.conf[0..o.conf.len]); + if (o.cand) |c| @memcpy(c, p.cand.?[0..c.len]); + if (o.base) |b| @memcpy(b, p.base.?[0..b.len]); + } + } + + fn self(ptr: *anyopaque) *Drafter { + return @ptrCast(@alignCast(ptr)); + } + + fn ingest(ptr: *anyopaque, slot: u32, start: u64, taps: iface.Taps, rows: []const u32) anyerror!void { + const x = self(ptr); + const d = x.t.dims.dim; + const ring = x.shape.ring(); + if (x.begun) return error.PassBegun; // the lanes collect a begun pass before any other pass work + x.ingests += 1; + const src: [*]const f32 = @ptrFromInt(taps.address); + for (rows, 0..) |r, i| { + if (r >= taps.rows) return error.TapOutOfRange; + const pos = start + i; + @memcpy(x.rings[slot][(pos % ring) * d ..][0..d], src[@as(usize, r) * taps.stride ..][0..d]); + x.tags[slot][pos % ring] = @intCast(pos); + } + } + + fn reset(ptr: *anyopaque, slot: u32) void { + @memset(self(ptr).tags[slot], -1); + } + + fn markovFn(ptr: *anyopaque) ?dspark.Markov { + return self(ptr).markovHeads(); + } + + fn propose(ptr: *anyopaque, asks: []const iface.Ask, out: []iface.Proposal) anyerror!void { + const x = self(ptr); + if (x.begun) return error.PassBegun; + const t = x.t; + const d = t.dims.dim; + const n = x.shape.block; + const v = t.dims.vocab; + const a = x.gpa; + const ring = x.shape.ring(); + x.passes += 1; + var ids: [16]u32 = undefined; + var xs: [16][64]f32 = undefined; + var ks: [16][64]f32 = undefined; + var vs: [16][64]f32 = undefined; + const hid = try a.alloc(f32, n * d); + defer a.free(hid); + const cand = try a.alloc(i32, n * v); + defer a.free(cand); + const base = try a.alloc(f32, n * v); + defer a.free(base); + const keys = try a.alloc([]const f32, x.shape.window + n); + defer a.free(keys); + const vals = try a.alloc([]const f32, x.shape.window + n); + defer a.free(vals); + var kbuf = try a.alloc(f32, (x.shape.window) * 2 * d); + defer a.free(kbuf); + for (asks, out) |ask, o| { + const ctx = dspark.Context.of(x.shape, ask.start, 0); + for (0..ctx.count) |i| { // the ring must hold exactly the committed rows before P + const pos = ctx.first + i; + if (x.tags[ask.slot][pos % ring] != @as(i64, @intCast(pos))) return error.RingStale; + const row = x.rings[ask.slot][(pos % ring) * d ..][0..d]; + t.project(t.wk, row, kbuf[i * 2 * d ..][0..d]); + t.project(t.wv, row, kbuf[i * 2 * d + d ..][0..d]); + keys[i] = kbuf[i * 2 * d ..][0..d]; + vals[i] = kbuf[i * 2 * d + d ..][0..d]; + } + dspark.blockIds(x.shape, ask.anchor, ids[0..n]); + for (0..n) |i| { + t.input(ids[i], ask.start + i, xs[i][0..d]); + t.project(t.wk, xs[i][0..d], ks[i][0..d]); + t.project(t.wv, xs[i][0..d], vs[i][0..d]); + keys[ctx.count + i] = ks[i][0..d]; + vals[ctx.count + i] = vs[i][0..d]; + } + for (0..n) |i| { + var q: [64]f32 = undefined; + t.project(t.wq, xs[i][0..d], q[0..d]); + const h = hid[i * d ..][0..d]; + @memcpy(h, xs[i][0..d]); + t.attend(q[0..d], keys[0 .. ctx.count + n], vals[0 .. ctx.count + n], h); + const row = base[i * v ..][0..v]; + @memset(row, 0.0); + for (h, 0..) |hi, j| { + for (row, t.wo[j * v ..][0..v]) |*r, w| r.* += hi * w; + } + for (cand[i * v ..][0..v], 0..) |*c, id| c.* = @intCast(id); + } + if (o.cand) |c| @memcpy(c[0 .. n * v], cand); + if (o.base) |b| @memcpy(b[0 .. n * v], base); + try dspark.chain(a, x.markovHeads(), cand, base, v, ask.anchor, ask.params, hid, o.drafts[0..n], o.conf[0..n]); + } + } +}; diff --git a/zig/src/families/deepseek_v41/engram_host.zig b/zig/src/families/deepseek_v41/engram_host.zig new file mode 100644 index 0000000000..cb40006758 --- /dev/null +++ b/zig/src/families/deepseek_v41/engram_host.zig @@ -0,0 +1,307 @@ +//! Engram's host side: table layout, n-gram hashes of a window's tokens, a rank's heads, and the synthetic index rows. + +const std = @import("std"); +const quiet = @import("builtin").is_test; // a test's stderr is the build runner's channel + +pub const dead_id: i64 = -1; +pub const max_layers = 4; +pub const max_ngram = 8; +pub const max_cols = 64; + +/// Where a window's table rows come from (local NVMe shards or a remote service); every source gives the same bytes. +pub const Source = struct { + ptr: *anyopaque, + vtable: *const VTable, + + pub const VTable = struct { + /// Starts fetching Engram layer index `li`'s rows `idx` (this rank's heads' global rows); returns at once. + issue: *const fn (ptr: *anyopaque, li: usize, idx: []const i64) anyerror!void, + /// Layer index `li`'s rows `idx` as bf16 bits [idx.len, dim] (waits for any still in flight). + rows: *const fn (ptr: *anyopaque, li: usize, idx: []const i64, dim: usize, out: []u16) anyerror!void, + }; +}; + +pub const Host = struct { + layer_ids: []const u32, + max_ngram: u32, + heads: u32, + /// token id -> compressed id + token_map: []const u32, + pad_id: i64, + mult: [max_layers][max_ngram]i64 = undefined, + primes: [max_layers][max_cols]i64 = undefined, + offsets: [max_layers][max_cols]i64 = undefined, + + pub fn cols(h: *const Host) u32 { + return (h.max_ngram - 1) * h.heads; + } + + /// Prime bucket sizes: for each layer and order the next unused primes above `vocab_size - 1` (reference order). + pub fn layout(h: *Host, vocab_size: u64) void { + var seen: [max_layers * max_cols]i64 = undefined; + var nseen: usize = 0; + for (0..h.layer_ids.len) |li| { + var col: usize = 0; + for (0..h.max_ngram - 1) |_| { + var cur: i64 = @intCast(vocab_size - 1); + for (0..h.heads) |_| { + cur += 1; + while (!isPrime(@intCast(cur)) or std.mem.indexOfScalar(i64, seen[0..nseen], cur) != null) cur += 1; + seen[nseen] = cur; + nseen += 1; + h.primes[li][col] = cur; + col += 1; + } + } + var acc: i64 = 0; + for (0..col) |c| { + h.offsets[li][c] = acc; + acc += h.primes[li][c]; + } + } + } + + /// Table rows [t][layers][cols] of `ids` after `lookback` (oldest first; short: sequence start); past map: dead. + pub fn hash(h: *const Host, ids: []const u32, lookback: []const u32, out: []i64) void { + const nb = lookback.len; + const L = h.layer_ids.len; + const C = h.cols(); + std.debug.assert(out.len == ids.len * L * C); + for (0..ids.len) |t| for (0..L) |li| { + var blocked = false; + var rolling: i64 = 0; + for (0..h.max_ngram) |s| { + const j = @as(i64, @intCast(t + nb)) - @as(i64, @intCast(s)); + const before = j < 0; + const v: i64 = if (before) h.pad_id else h.src(if (j < @as(i64, @intCast(nb))) lookback[@intCast(j)] else ids[@as(usize, @intCast(j)) - nb]); + blocked = blocked or before or v == dead_id; + const value = if (blocked) h.pad_id else v; + rolling ^= value *% h.mult[li][s]; + if (s == 0) continue; + for (0..h.heads) |hh| { + const c = (s - 1) * h.heads + hh; + out[(t * L + li) * C + c] = @mod(rolling, h.primes[li][c]) + h.offsets[li][c]; + } + } + }; + } + + fn src(h: *const Host, id: u32) i64 { + return if (id >= h.token_map.len) dead_id else h.token_map[id]; + } + + /// (first head, heads) rank `rank` of `world` owns: complete heads, TP-major (head_shard). + pub fn headShard(h: *const Host, rank: u32, world: u32) [2]u32 { + const n = h.cols(); + const part = (n + world - 1) / world; + const h0 = @min(rank * part, n); + return .{ h0, @min(h0 + part, n) - h0 }; + } + + /// This rank's heads' global rows of Engram layer `layer` for a window's `ids` after `lookback`: idx [n][heads]. + pub fn shardIndex(h: *const Host, a: std.mem.Allocator, ids: []const u32, lookback: []const u32, layer: u32, rank: u32, world: u32) ![]i64 { + const li = std.mem.indexOfScalar(u32, h.layer_ids, layer) orelse return error.NotEngram; + const L = h.layer_ids.len; + const C = h.cols(); + const sh = h.headShard(rank, world); + const hashes = try a.alloc(i64, ids.len * L * C); + defer a.free(hashes); + h.hash(ids, lookback, hashes); + const idx = try a.alloc(i64, ids.len * sh[1]); + for (0..ids.len) |t| @memcpy(idx[t * sh[1] ..][0..sh[1]], hashes[(t * L + li) * C + sh[0] ..][0..sh[1]]); + return idx; + } + + /// Engram layer `layer`'s window rows, this rank's heads, bf16 [ids.len, heads x dim]: from `source` or index rows. + pub fn windowRows(h: *const Host, a: std.mem.Allocator, source: ?Source, ids: []const u32, lookback: []const u32, layer: u32, rank: u32, world: u32, dim: usize, out: []u16) !void { + const idx = try h.shardIndex(a, ids, lookback, layer, rank, world); + defer a.free(idx); + const s = source orelse return indexRows(idx, dim, out); + const li = std.mem.indexOfScalar(u32, h.layer_ids, layer).?; + return s.vtable.rows(s.ptr, li, idx, dim, out); + } + + /// Starts reading a coming window's rows on every Engram layer; a wrong guess costs a read; errors only logged. + pub fn prefetch(h: *const Host, a: std.mem.Allocator, source: ?Source, ids: []const u32, lookback: []const u32, rank: u32, world: u32) void { + const s = source orelse return; + for (h.layer_ids, 0..) |layer, li| { + const idx = h.shardIndex(a, ids, lookback, layer, rank, world) catch return; + defer a.free(idx); + s.vtable.issue(s.ptr, li, idx) catch |e| if (!quiet) std.log.scoped(.dsv41).warn("engram: prefetch of layer {d} failed ({t})", .{ layer, e }); + } + } + + /// Loads recipe/dsv41_engram_host.py's file (the map borrowed from `bytes`, which must outlive the host). + pub fn load(bytes: []const u8, heads: u32, pad_token: u32, vocab_size: u64, layer_buf: []u32) !Host { + if (bytes.len < 24 or !std.mem.eql(u8, bytes[0..8], "DSV41EH1")) return error.BadEngramHost; + const L = std.mem.readInt(u32, bytes[8..12], .little); + const M = std.mem.readInt(u32, bytes[12..16], .little); + const V = std.mem.readInt(u32, bytes[16..20], .little); + if (L > max_layers or M > max_ngram or (M - 1) * heads > max_cols or L > layer_buf.len) return error.BadEngramHost; + var at: usize = 24; + for (0..L) |i| layer_buf[i] = std.mem.readInt(u32, bytes[at + 4 * i ..][0..4], .little); + at += 4 * L; + var h: Host = .{ .layer_ids = layer_buf[0..L], .max_ngram = M, .heads = heads, .token_map = &.{}, .pad_id = 0 }; + for (0..L) |li| for (0..M) |s| { + h.mult[li][s] = std.mem.readInt(i64, bytes[at..][0..8], .little); + at += 8; + }; + if (bytes.len - at < 4 * @as(usize, V)) return error.BadEngramHost; + h.token_map = @alignCast(std.mem.bytesAsSlice(u32, bytes[at..][0 .. 4 * V])); + h.pad_id = h.token_map[pad_token]; + h.layout(vocab_size); + return h; + } +}; + +/// The committed ids before `start` (last `keep_n`): the tail, then kept leading rows of `pending`; null: unreachable. +pub fn lookbackAt(tail: []const u32, pos: u64, pending: ?[]const u32, start: u64, keep_n: usize, buf: []u32) ?[]const u32 { + if (start < pos) return null; + const k: usize = @intCast(start - pos); + const extra: []const u32 = if (pending) |p| (if (k <= p.len) p[0..k] else return null) else if (k == 0) &.{} else return null; + if (tail.len + extra.len > buf.len) return null; + @memcpy(buf[0..tail.len], tail); + @memcpy(buf[tail.len..][0..extra.len], extra); + const m = tail.len + extra.len; + const n = @min(m, keep_n); + return buf[m - n .. m]; +} + +/// A slot's Engram tail after keeping `ids`: the last `keep_n` of tail + ids, into `tail`; returns its new length. +pub fn tailAfter(tail: []u32, len: usize, keep_n: usize, ids: []const u32) usize { + var all: [2 * max_ngram + 64]u32 = undefined; + const old = tail[0..len]; + const take = ids[ids.len -| keep_n..]; + @memcpy(all[0..old.len], old); + @memcpy(all[old.len..][0..take.len], take); + const m = old.len + take.len; + const k = @min(m, keep_n); + @memcpy(tail[0..k], all[m - k .. m]); + return k; +} + +test "an Engram warm's lookback at a draft before its window's keep is the window's tail after it" { + // TF_DSV41_ENGRAM_WARM: warmEngram asks at pos + kept, pending window in flight; stageEngram reads after the keep + var prng = std.Random.DefaultPrng.init(56); + const rnd = prng.random(); + for (0..500) |_| { + const keep_n = 3; + var tail: [max_ngram]u32 = undefined; + const len = rnd.uintAtMost(usize, keep_n); + for (tail[0..len]) |*t| t.* = rnd.int(u32); + var pend: [6]u32 = undefined; + const pn = 1 + rnd.uintLessThan(usize, pend.len); + for (pend[0..pn]) |*t| t.* = rnd.int(u32); + const pos: u64 = 100 + rnd.uintAtMost(u64, 50); + const kept = rnd.uintLessThan(usize, pn); // drafts kept: rows 0..kept, the next window starts past them + const start = pos + kept + 1; + var buf: [2 * max_ngram + 64]u32 = undefined; + const before = lookbackAt(tail[0..len], pos, pend[0..pn], start, keep_n, &buf).?; + var after = tail; + const alen = tailAfter(&after, len, keep_n, pend[0 .. kept + 1]); + try std.testing.expectEqualSlices(u32, after[0..alen], before); + // a keep already done (the commit before the draft): no pending window, the same tail + var buf2: [2 * max_ngram + 64]u32 = undefined; + try std.testing.expectEqualSlices(u32, after[0..alen], lookbackAt(after[0..alen], start, null, start, keep_n, &buf2).?); + } +} + +/// Small primes, then Miller-Rabin with bases 2, 7, 61 (exact below 4.7e9). +pub fn isPrime(n: u64) bool { + if (n < 2) return false; + for ([_]u64{ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37 }) |p| if (n % p == 0) return n == p; + var d = n - 1; + var r: u32 = 0; + while (d % 2 == 0) : (r += 1) d /= 2; + for ([_]u64{ 2, 7, 61 }) |a| { + var x = powMod(a, d, n); + if (x == 1 or x == n - 1) continue; + var ok = false; + var i: u32 = 1; + while (i < r) : (i += 1) { + x = @intCast(@as(u128, x) * x % n); + if (x == n - 1) { + ok = true; + break; + } + } + if (!ok) return false; + } + return true; +} + +fn powMod(b0: u64, e0: u64, m: u64) u64 { + var b: u128 = b0 % m; + var e = e0; + var r: u128 = 1; + while (e > 0) : (e >>= 1) { + if (e & 1 == 1) r = r * b % m; + b = b * b % m; + } + return @intCast(r); +} + +/// Synthetic rows: row i, value j = (((i * 2654435761 + j * 40503) % 511) - 255) / 1024, exact in bf16, into `out`. +pub fn indexRows(rows: []const i64, dim: usize, out: []u16) void { + for (rows, 0..) |i, r| for (0..dim) |j| { + const v = @mod(i *% 2654435761 +% @as(i64, @intCast(j)) * 40503, 511) - 255; + const f: f32 = @as(f32, @floatFromInt(v)) / 1024.0; + out[r * dim + j] = @truncate(@as(u32, @bitCast(f)) >> 16); + }; +} + +const testing = std.testing; + +test "the n-gram hashes: the reference NgramHasher on the release config and tokenizer (fixtures/engram-hash-ref.bin)" { + const a = testing.allocator; + const fx: []const u8 = @embedFile("fixtures/engram-hash-ref.bin"); + const L = std.mem.readInt(u32, fx[8..12], .little); + const M = std.mem.readInt(u32, fx[12..16], .little); + const V = std.mem.readInt(u32, fx[16..20], .little); + var at: usize = 24 + 4 * L + 8 * L * M; + // a full-size map holding only the entries the fixture's ids use + const map = try a.alloc(u32, V); + defer a.free(map); + @memset(map, 0); + const used = std.mem.readInt(u32, fx[at..][0..4], .little); + at += 4; + for (0..used) |_| { + map[std.mem.readInt(u32, fx[at..][0..4], .little)] = std.mem.readInt(u32, fx[at + 4 ..][0..4], .little); + at += 8; + } + var file: std.ArrayList(u8) = .empty; + defer file.deinit(a); + try file.appendSlice(a, fx[0 .. 24 + 4 * L + 8 * L * M]); + try file.appendSlice(a, std.mem.sliceAsBytes(map)); + const bytes = try a.alignedAlloc(u8, .@"4", file.items.len); + defer a.free(bytes); + @memcpy(bytes, file.items); + var ids_buf: [max_layers]u32 = undefined; + const h = try Host.load(bytes, 8, 2, 16_000_000, &ids_buf); + // the reference layout: the first and last primes, the last head's offset + try testing.expectEqual(@as(i64, 16_000_057), h.primes[0][0]); + try testing.expectEqual(@as(i64, 16_000_889), h.primes[1][23]); + try testing.expectEqual(@as(i64, 368_005_705), h.offsets[0][23]); + const cases = std.mem.readInt(u32, fx[at..][0..4], .little); + at += 4; + for (0..cases) |_| { + const n = std.mem.readInt(u32, fx[at..][0..4], .little); + const nb = std.mem.readInt(u32, fx[at + 4 ..][0..4], .little); + at += 8; + const ids_c = try a.alloc(u32, n); + defer a.free(ids_c); + for (ids_c, 0..) |*x, i| x.* = std.mem.readInt(u32, fx[at + 4 * i ..][0..4], .little); + at += 4 * n; + const lb_c = try a.alloc(u32, nb); + defer a.free(lb_c); + for (lb_c, 0..) |*x, i| x.* = std.mem.readInt(u32, fx[at + 4 * i ..][0..4], .little); + at += 4 * nb; + const got = try a.alloc(i64, n * L * h.cols()); + defer a.free(got); + h.hash(ids_c, lb_c, got); + for (got, 0..) |g, i| try testing.expectEqual(std.mem.readInt(i64, fx[at + 8 * i ..][0..8], .little), g); + at += 8 * got.len; + } + try testing.expectEqual(fx.len, at); + try testing.expectEqual([2]u32{ 12, 12 }, h.headShard(1, 2)); +} diff --git a/zig/src/families/deepseek_v41/engram_io.zig b/zig/src/families/deepseek_v41/engram_io.zig new file mode 100644 index 0000000000..60be56ea5a --- /dev/null +++ b/zig/src/families/deepseek_v41/engram_io.zig @@ -0,0 +1,534 @@ +//! Engram shard reads: the batches, LRU tables and reader paths (io_uring, native AIO, a thread pool, serial). + +const std = @import("std"); +const linux = std.os.linux; +const engram_rows = @import("engram_rows.zig"); +const Rows = engram_rows.Rows; +const Header = engram_rows.Header; +const header_bytes = engram_rows.header_bytes; +const sector = engram_rows.sector; +const row_bytes = engram_rows.row_bytes; +const merge_gap = engram_rows.merge_gap; +const page = engram_rows.page; + +/// One extent of a batch: file bytes [off, off + len) into the batch buffer at `at`, its rows `first .. + count`. +const Extent = struct { off: u64, len: u64, at: usize, first: u32, count: u32 }; + +pub const Batch = struct { + table: u32, + /// sorted unique global rows + rows: []u64, + ext: []Extent, + buf: []align(page) u8, + left: u32, + failed: bool = false, + in_use: bool = true, + /// a `rows` call took at least half its rows (a bulk batch is read through once read); `taken`: this call's count + read: bool = false, + taken: u32 = 0, + unread: u8 = 0, // `rows` calls that settled it unread: a TF_DSV41_PREFETCH_AHEAD batch waits through one + + /// The record of `rows[k]` (k found by the caller). + pub fn record(b: *const Batch, k: usize, lo: u64) []const u8 { + var a: usize = 0; + var z: usize = b.ext.len; + while (z - a > 1) { + const m = (a + z) / 2; + if (b.ext[m].first <= k) a = m else z = m; + } + const e = b.ext[a]; + const at = e.at + @as(usize, @intCast(header_bytes + (b.rows[k] - lo) * row_bytes - e.off)); + return b.buf[at..][0..row_bytes]; + } +}; + +const Entry = union(enum) { ready: u32, pending: u32 }; +const none: u32 = std.math.maxInt(u32); + +/// One layer's shard and record cache. +pub const Table = struct { + fd: linux.fd_t, + direct: bool, + h: Header, + size: u64, + map: std.AutoHashMapUnmanaged(u64, Entry) = .empty, + /// cap records, their ids, and the LRU links (head: most recent) + recs: []u8, + ids: []u64, + prev: []u32, + next: []u32, + used: u32 = 0, + head: u32 = none, + tail: u32 = none, + /// keys removed since the map was last rehashed: each left a tombstone that a missing key's probe walks past + dead: u32 = 0, + + pub fn unlink(t: *Table, s: u32) void { + if (t.prev[s] != none) t.next[t.prev[s]] = t.next[s] else t.head = t.next[s]; + if (t.next[s] != none) t.prev[t.next[s]] = t.prev[s] else t.tail = t.prev[s]; + } + + pub fn front(t: *Table, s: u32) void { + t.prev[s] = none; + t.next[s] = t.head; + if (t.head != none) t.prev[t.head] = s; + t.head = s; + if (t.tail == none) t.tail = s; + } + + /// A slot for a new record: an unused one, else the least recent (its id leaves the map). + pub fn take(t: *Table) u32 { + if (t.used < t.ids.len) { + t.used += 1; + return t.used - 1; + } + const s = t.tail; + t.unlink(s); + t.drop(t.ids[s]); + return s; + } + + /// std's tombstones never clear and keys fill 80 %: rehash at a tenth, so a tenth of the map always stays free. + pub fn drop(t: *Table, x: u64) void { + if (!t.map.remove(x)) return; + t.dead += 1; + if (@as(u64, t.dead) * 10 < t.map.capacity()) return; + t.map.rehash(std.hash_map.AutoContext(u64){}); + t.dead = 0; + } +}; + +/// One extent for a pool thread: file bytes [off, off + buf.len) into `buf` (the batch's own buffer), batch `bi`. +const Job = struct { bi: u32, fd: linux.fd_t, size: u64, direct: bool, buf: []u8, off: u64 }; + +/// The reader threads without io_uring; `mu` guards the queue, every batch's `left` / `failed`, and the batch list. +pub const Pool = struct { + mu: std.c.pthread_mutex_t = std.c.PTHREAD_MUTEX_INITIALIZER, + work: std.c.pthread_cond_t = .{}, + done: std.c.pthread_cond_t = .{}, + jobs: std.ArrayList(Job) = .empty, + head: usize = 0, + stop: bool = false, + threads: []std.Thread = &.{}, + + pub fn lock(p: *Pool) void { + _ = std.c.pthread_mutex_lock(&p.mu); + } + + pub fn unlock(p: *Pool) void { + _ = std.c.pthread_mutex_unlock(&p.mu); + } +}; + +/// Linux native AIO (TF_DSV41_ENGRAM_AIO=1): one io_submit a batch, reaped by the waiter; works without io_uring. +pub const Aio = struct { + ctx: u64 = 0, + in_flight: u32 = 0, + poll: bool = false, // TF_DSV41_ENGRAM_POLL=1: a wait spins on io_getevents up to `poll_ns`, then sleeps + + pub const poll_ns: u64 = 20 * std.time.ns_per_ms; + + pub const depth: u32 = 1024; + + const Iocb = extern struct { + data: u64, + key: u32 = 0, + rw_flags: i32 = 0, + opcode: u16 = 0, // IOCB_CMD_PREAD + reqprio: i16 = 0, + fildes: u32, + buf: u64, + nbytes: u64, + offset: i64, + reserved2: u64 = 0, + flags: u32 = 0, + resfd: u32 = 0, + }; + + const Event = extern struct { data: u64, obj: u64, res: i64, res2: i64 }; + + pub fn init() !Aio { + var a: Aio = .{ .poll = pollEnv() }; + const rc = linux.syscall2(.io_setup, depth, @intFromPtr(&a.ctx)); + if (linux.errno(rc) != .SUCCESS) return error.AioRefused; + return a; + } + + pub fn deinit(a: *Aio) void { + _ = linux.syscall1(.io_destroy, a.ctx); + } +}; + +/// TF_DSV41_ENGRAM_POLL: 0 (default) | 1 (with TF_DSV41_ENGRAM_AIO=1). +pub fn pollEnv() bool { + const v = std.c.getenv("TF_DSV41_ENGRAM_POLL") orelse return false; + return std.mem.eql(u8, std.mem.span(v), "1"); +} + +/// TF_DSV41_ENGRAM_AIO: 0 (default) | 1. +pub fn aioEnv() bool { + const v = std.c.getenv("TF_DSV41_ENGRAM_AIO") orelse return false; + return std.mem.eql(u8, std.mem.span(v), "1"); +} + +/// TF_DSV41_ENGRAM_QD: the reader threads without io_uring (default 64; 0: none, reads serial). +pub fn poolThreads() u32 { + const v = std.c.getenv("TF_DSV41_ENGRAM_QD") orelse return 64; + return std.fmt.parseInt(u32, std.mem.span(v), 10) catch 64; +} + +/// Native AIO in place of io_uring or the pool (tests, the probe): every read async through one context. +pub fn startAio(r: *Rows) !void { + if (r.ring) |*g| g.deinit(); + r.ring = null; + r.stopPool(); + if (r.aio == null) r.aio = try Aio.init(); +} + +/// The reader threads (no io_uring): `n` workers waiting for extents. +pub fn startPool(r: *Rows, n: u32) !void { + if (r.pool != null or r.ring != null or n == 0) return error.EngramPool; + const p = try r.gpa.create(Pool); + errdefer r.gpa.destroy(p); + p.* = .{}; + p.threads = try r.gpa.alloc(std.Thread, n); + var made: usize = 0; + errdefer { + p.lock(); + p.stop = true; + p.unlock(); + _ = std.c.pthread_cond_broadcast(&p.work); + for (p.threads[0..made]) |t| t.join(); + r.gpa.free(p.threads); + } + r.pool = p; + errdefer r.pool = null; + for (p.threads) |*t| { + t.* = try std.Thread.spawn(.{ .stack_size = 1 << 20 }, worker, .{r}); + made += 1; + } +} + +/// The reader threads stopped (their queue drained); reads are serial afterwards. +pub fn stopPool(r: *Rows) void { + const p = r.pool orelse return; + p.lock(); + p.stop = true; + p.unlock(); + _ = std.c.pthread_cond_broadcast(&p.work); + for (p.threads) |t| t.join(); + r.gpa.free(p.threads); + p.jobs.deinit(r.gpa); + r.gpa.destroy(p); + r.pool = null; +} + +/// A reader thread: extents off the queue until `stop` (the queue drained first). +pub fn worker(r: *Rows) void { + const p = r.pool.?; + while (true) { + p.lock(); + while (p.head == p.jobs.items.len and !p.stop) _ = std.c.pthread_cond_wait(&p.work, &p.mu); + if (p.head == p.jobs.items.len) { + p.unlock(); + return; + } + const j = p.jobs.items[p.head]; + p.head += 1; + if (p.head == p.jobs.items.len) { + p.jobs.clearRetainingCapacity(); + p.head = 0; + } + p.unlock(); + const ok = if (readAt(j.fd, j.size, j.direct, j.buf, j.off)) true else |_| false; + p.lock(); + const b = &r.batches.items[j.bi]; + if (!ok) b.failed = true; + b.left -= 1; + const last = b.left == 0; + p.unlock(); + if (last) _ = std.c.pthread_cond_broadcast(&p.done); + } +} + +pub fn openTable(gpa: std.mem.Allocator, path: [:0]const u8, cap: u32) !Table { + var direct = true; + var rc = linux.openat(linux.AT.FDCWD, path, .{ .ACCMODE = .RDONLY, .CLOEXEC = true, .DIRECT = true }, 0); + if (linux.errno(rc) != .SUCCESS) { + direct = false; + rc = linux.openat(linux.AT.FDCWD, path, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); + if (linux.errno(rc) != .SUCCESS) { + std.log.scoped(.dsv41).err("engram: cannot open {s}", .{path}); + return error.FileNotFound; + } + } + const fd: linux.fd_t = @intCast(rc); + errdefer _ = linux.close(fd); + var st: linux.Statx = undefined; + if (linux.errno(linux.statx(fd, "", linux.AT.EMPTY_PATH, .{ .SIZE = true }, &st)) != .SUCCESS) return error.StatFailed; + const hb = try gpa.alignedAlloc(u8, .fromByteUnits(page), page); + defer gpa.free(hb); + try preadAll(fd, hb, 0); + if (!direct) _ = linux.fadvise(fd, 0, page, linux.POSIX_FADV.DONTNEED); + const h = try Header.parse(hb[0..48], st.size); + const t: Table = .{ + .fd = fd, + .direct = direct, + .h = h, + .size = st.size, + .recs = try gpa.alloc(u8, cap * row_bytes), + .ids = try gpa.alloc(u64, cap), + .prev = try gpa.alloc(u32, cap), + .next = try gpa.alloc(u32, cap), + }; + return t; +} + +pub fn closeTable(r: *Rows, t: *Table) void { + _ = linux.close(t.fd); + t.map.deinit(r.gpa); + r.gpa.free(t.recs); + r.gpa.free(t.ids); + r.gpa.free(t.prev); + r.gpa.free(t.next); +} + +/// A batch for sorted unique `rows` of table `ti`, its extents submitted; `urgent`: runs before queued read-ahead. +pub fn start(r: *Rows, ti: u32, rows_in: []const u64, urgent: bool) !u32 { + const t = &r.tables[ti]; + const gpa = r.gpa; + var ext: std.ArrayList(Extent) = .empty; + errdefer ext.deinit(gpa); + var total: usize = 0; + for (rows_in, 0..) |x, k| { + const s = header_bytes + (x - t.h.lo) * row_bytes; + const s0 = s / sector * sector; + const s1 = (s + row_bytes + sector - 1) / sector * sector; + if (ext.items.len > 0) { + const e = &ext.items[ext.items.len - 1]; + if (s0 <= e.off + e.len + merge_gap) { + const end = @max(e.off + e.len, s1); + total += @intCast(end - e.off - e.len); + e.len = end - e.off; + e.count += 1; + continue; + } + } + total = std.mem.alignForward(usize, total, page); + try ext.append(gpa, .{ .off = s0, .len = s1 - s0, .at = total, .first = @intCast(k), .count = 1 }); + total += @intCast(s1 - s0); + } + // extents keep their buffer offsets page aligned (O_DIRECT) + const buf = try gpa.alignedAlloc(u8, .fromByteUnits(page), std.mem.alignForward(usize, total, page)); + errdefer gpa.free(buf); + const owned = try gpa.dupe(u64, rows_in); + errdefer gpa.free(owned); + const b: Batch = .{ .table = ti, .rows = owned, .ext = try ext.toOwnedSlice(gpa), .buf = buf, .left = 0 }; + if (r.pool) |p| { + // the batch's slot and its extents queued under the pool's lock (a worker settles batches.items[bi]) + try t.map.ensureUnusedCapacity(gpa, @intCast(owned.len)); + p.lock(); + const queued = r.queue(p, t, b, urgent); + p.unlock(); + const bi = try queued; + _ = std.c.pthread_cond_broadcast(&p.work); + for (owned) |x| t.map.putAssumeCapacity(x, .{ .pending = bi }); + return bi; + } + const bi = try r.slotFor(b); + for (owned) |x| try t.map.put(gpa, x, .{ .pending = bi }); + const bp = &r.batches.items[bi]; + r.stats.reads += bp.ext.len; + for (bp.ext) |e| r.stats.bytes += e.len; + if (r.ring) |*g| { + for (bp.ext, 0..) |e, k| { + const sqe = g.read(@as(u64, bi) << 32 | k, t.fd, .{ .buffer = bp.buf[e.at..][0..@intCast(e.len)] }, e.off) catch blk: { + // a full submission queue: submit what is queued, take finished reads, then the slot is free + _ = try g.submit(); + try r.reap(0); + break :blk try g.read(@as(u64, bi) << 32 | k, t.fd, .{ .buffer = bp.buf[e.at..][0..@intCast(e.len)] }, e.off); + }; + _ = sqe; + bp.left += 1; + } + _ = try g.submit(); + } else if (r.aio != null) { + try r.aioSubmit(bi, t); + } else { + for (bp.ext) |e| try r.readExtent(t, bp.buf[e.at..][0..@intCast(e.len)], e.off); + } + return bi; +} + +pub fn slotFor(r: *Rows, b: Batch) !u32 { + for (r.batches.items, 0..) |*x, i| if (!x.in_use) { + x.* = b; + return @intCast(i); + }; + try r.batches.append(r.gpa, b); + return @intCast(r.batches.items.len - 1); +} + +/// A batch `b` in a free slot, its extents queued on `p` (locked): at the end, or `urgent` at the head. +pub fn queue(r: *Rows, p: *Pool, t: *const Table, b: Batch, urgent: bool) !u32 { + try p.jobs.ensureUnusedCapacity(r.gpa, b.ext.len); + const bi = try r.slotFor(b); + const bp = &r.batches.items[bi]; + r.stats.reads += bp.ext.len; + const n = bp.ext.len; + const at = if (urgent) p.head else p.jobs.items.len; + if (urgent) { + // one shift of the queued jobs past the head, then this batch's in their place + const old = p.jobs.items.len; + p.jobs.items.len += n; + std.mem.copyBackwards(Job, p.jobs.items[at + n ..], p.jobs.items[at..old]); + } else p.jobs.items.len += n; + for (bp.ext, p.jobs.items[at..][0..n]) |e, *j| { + r.stats.bytes += e.len; + j.* = .{ .bi = bi, .fd = t.fd, .size = t.size, .direct = t.direct, .buf = bp.buf[e.at..][0..@intCast(e.len)], .off = e.off }; + } + bp.left = @intCast(bp.ext.len); + return bi; +} + +pub fn readExtent(r: *Rows, t: *const Table, buf: []u8, off: u64) !void { + _ = r; + return readAt(t.fd, t.size, t.direct, buf, off); +} + +/// Batch `bi`'s extents on the AIO context (all of them, reaping finished reads while the context is full). +pub fn aioSubmit(r: *Rows, bi: u32, t: *const Table) !void { + const a = &r.aio.?; + const bp = &r.batches.items[bi]; + var cbs: [64]Aio.Iocb = undefined; + var ptrs: [64]*Aio.Iocb = undefined; + var k: usize = 0; + while (k < bp.ext.len) { + if (a.in_flight == Aio.depth) try r.reap(1); + const n = @min(bp.ext.len - k, cbs.len, Aio.depth - a.in_flight); + for (cbs[0..n], ptrs[0..n], bp.ext[k..][0..n], 0..) |*cb, *pp, e, j| { + cb.* = .{ .data = @as(u64, bi) << 32 | (k + j), .fildes = @intCast(t.fd), .buf = @intFromPtr(bp.buf[e.at..].ptr), .nbytes = e.len, .offset = @intCast(e.off) }; + pp.* = cb; + } + const rc = linux.syscall3(.io_submit, a.ctx, n, @intFromPtr(&ptrs)); + switch (linux.errno(rc)) { + .SUCCESS => {}, + .AGAIN, .INTR => { + try r.reap(if (a.in_flight > 0) 1 else 0); + continue; + }, + else => return error.EngramReadFailed, + } + bp.left += @intCast(rc); + a.in_flight += @intCast(rc); + k += rc; + } +} + +/// Takes finished AIO reads (waiting for at least `at_least`). +pub fn aioReap(r: *Rows, at_least: u32) !void { + const a = &r.aio.?; + var evs: [64]Aio.Event = undefined; + var need = @min(at_least, a.in_flight); + const zero: linux.timespec = .{ .sec = 0, .nsec = 0 }; + const t0 = if (a.poll and need > 0) nowNs() else 0; + var polling = a.poll and need > 0; + while (true) { + // polling: whatever has landed, at once; else the kernel's wait for `need` + if (polling and nowNs() - t0 > Aio.poll_ns) polling = false; + const rc = if (polling) + linux.syscall5(.io_getevents, a.ctx, 0, evs.len, @intFromPtr(&evs), @intFromPtr(&zero)) + else + linux.syscall5(.io_getevents, a.ctx, need, evs.len, @intFromPtr(&evs), 0); + switch (linux.errno(rc)) { + .SUCCESS => {}, + .INTR => continue, + else => return error.EngramReadFailed, + } + for (evs[0..rc]) |ev| { + const b = &r.batches.items[@intCast(ev.data >> 32)]; + const e = b.ext[@intCast(ev.data & 0xFFFF_FFFF)]; + const t = &r.tables[b.table]; + const want = @min(e.len, t.size - e.off); + if (ev.res < 0 or @as(u64, @intCast(ev.res)) < want) b.failed = true; + if (!t.direct) _ = linux.fadvise(t.fd, @intCast(e.off), @intCast(e.len), linux.POSIX_FADV.DONTNEED); + b.left -= 1; + a.in_flight -= 1; + } + need -|= @intCast(rc); + if (need == 0) return; + if (polling and rc == 0) std.atomic.spinLoopHint(); + } +} + +/// Takes finished reads off the ring (waiting for at least `at_least`). +pub fn reap(r: *Rows, at_least: u32) !void { + if (r.aio != null) return r.aioReap(at_least); + const g = &(r.ring orelse return); + var cqes: [64]linux.io_uring_cqe = undefined; + var need = at_least; + while (true) { + const n = try g.copy_cqes(&cqes, need); + for (cqes[0..n]) |c| { + const b = &r.batches.items[@intCast(c.user_data >> 32)]; + const e = b.ext[@intCast(c.user_data & 0xFFFF_FFFF)]; + const t = &r.tables[b.table]; + const want = @min(e.len, t.size - e.off); + if (c.res < 0 or @as(u64, @intCast(c.res)) < want) b.failed = true; + if (!t.direct) _ = linux.fadvise(t.fd, @intCast(e.off), @intCast(e.len), linux.POSIX_FADV.DONTNEED); + b.left -= 1; + } + need -|= n; + if (need == 0) return; + } +} + +/// Blocks until batch `bi`'s reads are done. +pub fn wait(r: *Rows, bi: u32) !void { + if (r.pool) |p| { + p.lock(); + defer p.unlock(); + while (r.batches.items[bi].left > 0) _ = std.c.pthread_cond_wait(&p.done, &p.mu); + if (r.batches.items[bi].failed) return error.EngramReadFailed; + return; + } + while (r.batches.items[bi].left > 0) try r.reap(1); + if (r.batches.items[bi].failed) return error.EngramReadFailed; +} + +/// An extent's bytes [off, off + buf.len) of a shard of `size` bytes (stops at its end), pages dropped if buffered. +pub fn readAt(fd: linux.fd_t, size: u64, direct: bool, buf: []u8, off: u64) !void { + const want = @min(buf.len, size - off); + var done: usize = 0; + while (done < want) { + const rc = linux.pread(fd, buf[done..].ptr, buf.len - done, @intCast(off + done)); + switch (linux.errno(rc)) { + .SUCCESS => {}, + .INTR => continue, + else => return error.EngramReadFailed, + } + if (rc == 0) return error.EngramShortRead; + done += rc; + } + if (!direct) _ = linux.fadvise(fd, @intCast(off), @intCast(buf.len), linux.POSIX_FADV.DONTNEED); +} + +pub fn preadAll(fd: linux.fd_t, out: []u8, off: u64) !void { + var done: usize = 0; + while (done < out.len) { + const rc = linux.pread(fd, out[done..].ptr, out.len - done, @intCast(off + done)); + switch (linux.errno(rc)) { + .SUCCESS => {}, + .INTR => continue, + else => return error.EngramReadFailed, + } + if (rc == 0) return error.EngramShortRead; + done += rc; + } +} + +pub fn nowNs() u64 { + var ts: std.c.timespec = undefined; + _ = std.c.clock_gettime(.MONOTONIC, &ts); + return @as(u64, @intCast(ts.sec)) * std.time.ns_per_s + @as(u64, @intCast(ts.nsec)); +} diff --git a/zig/src/families/deepseek_v41/engram_rows.zig b/zig/src/families/deepseek_v41/engram_rows.zig new file mode 100644 index 0000000000..7854df9ec0 --- /dev/null +++ b/zig/src/families/deepseek_v41/engram_rows.zig @@ -0,0 +1,306 @@ +//! Engram table rows from packed per-rank shards on local NVMe, through io_uring, AIO or reader threads, LRU-cached. + +const std = @import("std"); +const quiet = @import("builtin").is_test; // a test's stderr is the build runner's channel +const linux = std.os.linux; +const eh = @import("engram_host.zig"); + +pub const magic: u64 = 0x31344E4531565344; // "DSV41EN1"; file `/engram-l-rof.bin` +pub const header_bytes: u64 = 4096; // 6 little-endian u64: magic, layer, lo, hi, total rows, row bytes +pub const sector: u64 = 512; +pub const row_bytes: u64 = 264; // 256 e4m3 values and 8 UE8M0 scales (one a 32) +pub const values = 256; +pub const merge_gap: u64 = 4096; // holes up to this merged into one extent +pub const page = 4096; +const engram_io = @import("engram_io.zig"); + +pub const Header = struct { + layer: u64, + lo: u64, + hi: u64, + total: u64, + row_bytes: u64, + + /// The header's six words, checked against the file's size. + pub fn parse(b: *const [48]u8, size: u64) !Header { + var w: [6]u64 = undefined; + for (&w, 0..) |*x, i| x.* = std.mem.readInt(u64, b[8 * i ..][0..8], .little); + if (w[0] != magic) return error.NotEngramShard; + const h: Header = .{ .layer = w[1], .lo = w[2], .hi = w[3], .total = w[4], .row_bytes = w[5] }; + if (h.row_bytes != row_bytes or h.hi < h.lo or size != header_bytes + (h.hi - h.lo) * h.row_bytes) return error.BadEngramShard; + return h; + } +}; + +/// torch's float8_e4m3fn -> float32 (c10 fp8e4m3fn_to_fp32_value, bit for bit). +pub fn e4m3(b: u8) f32 { + const w: u32 = @as(u32, b) << 24; + const sign = w & 0x8000_0000; + const nonsign = w & 0x7FFF_FFFF; + var shift: u32 = @clz(nonsign); + shift = if (shift > 4) shift - 4 else 0; + const inf_nan: u32 = @bitCast((@as(i32, @bitCast(nonsign +% 0x0100_0000)) >> 8) & 0x7F80_0000); + const zero: u32 = @bitCast(@as(i32, @bitCast(nonsign -% 1)) >> 31); + const r = sign | ((((nonsign << @intCast(shift)) >> 4) +% ((0x78 -% shift) << 23) | inf_nan) & ~zero); + return @bitCast(r); +} + +/// float32 -> bfloat16 as torch's CPU vector path: round to nearest even, NaN -> 0xFFFF (no packed table holds a NaN). +pub fn bf16(x: f32) u16 { + if (std.math.isNan(x)) return 0xFFFF; + const u: u32 = @bitCast(x); + return @intCast((u +% (0x7FFF + ((u >> 16) & 1))) >> 16); +} + +/// The table [scale << 8 | byte] -> bf16 bits of e4m3(byte) x 2^(scale - 127) (scale 0: x 0). +pub fn fillTable(t: *[65536]u16) void { + for (0..256) |s| { + const mul: f32 = @bitCast(@as(u32, @intCast(s)) << 23); + for (0..256) |b| t[s << 8 | b] = bf16(e4m3(@intCast(b)) * mul); + } +} + +const Batch = engram_io.Batch; +const Table = engram_io.Table; +const Pool = engram_io.Pool; +const Aio = engram_io.Aio; +pub const pollEnv = engram_io.pollEnv; +pub const aioEnv = engram_io.aioEnv; +pub const poolThreads = engram_io.poolThreads; +const nowNs = engram_io.nowNs; + +pub const Stats = struct { hits: u64 = 0, waited: u64 = 0, misses: u64 = 0, issued: u64 = 0, reads: u64 = 0, bytes: u64 = 0, wait_ns: u64 = 0 }; + +pub const Rows = struct { + gpa: std.mem.Allocator, + tables: []Table, + ring: ?linux.IoUring, + /// no io_uring: the reader threads (null: serial reads on the engine thread) + pool: ?*Pool = null, + /// no io_uring, TF_DSV41_ENGRAM_AIO=1: native AIO (in place of the pool) + aio: ?Aio = null, + batches: std.ArrayList(Batch) = .empty, + lut: *[65536]u16, + cap: u32, + stats: Stats = .{}, + /// scratch reused by `rows` (sorted unique ids, their records) + uniq: std.ArrayList(u64) = .empty, + recs: std.ArrayList([*]const u8) = .empty, + want: std.ArrayList(u32) = .empty, + + /// Opens rank `rank` of `world`'s shard of every Engram layer in `layer_ids` under `dir`, `cap` records a layer. + pub fn open(gpa: std.mem.Allocator, dir: []const u8, rank: u32, world: u32, layer_ids: []const u32, cap: u32) !*Rows { + if (cap < 4096) return error.EngramCacheTooSmall; + const r = try gpa.create(Rows); + errdefer gpa.destroy(r); + r.* = .{ .gpa = gpa, .tables = try gpa.alloc(Table, layer_ids.len), .ring = null, .lut = try gpa.create([65536]u16), .cap = cap }; + fillTable(r.lut); + var made: usize = 0; + errdefer { + for (r.tables[0..made]) |*t| r.closeTable(t); + gpa.free(r.tables); + gpa.destroy(r.lut); + } + for (layer_ids, r.tables) |l, *t| { + var nb: [4096]u8 = undefined; + const p = try std.fmt.bufPrint(nb[0 .. nb.len - 1], "{s}/engram-l{d}-r{d}of{d}.bin", .{ dir, l, rank, world }); + nb[p.len] = 0; + const path = nb[0..p.len :0]; + t.* = try openTable(gpa, path, cap); + made += 1; + if (t.h.layer != l) return error.BadEngramShard; + } + // TF_DSV41_ENGRAM_URING=0: the reader threads even where io_uring works (a container that allows it, A/B) + const uring_off = if (std.c.getenv("TF_DSV41_ENGRAM_URING")) |v| std.mem.eql(u8, std.mem.span(v), "0") else false; + r.ring = (if (uring_off) error.PermissionDenied else linux.IoUring.init(256, 0)) catch |e| blk: { + if (aioEnv()) { + if (Aio.init()) |a| { + r.aio = a; + std.log.scoped(.dsv41).info("engram: no io_uring ({t}); rows read by native AIO (TF_DSV41_ENGRAM_AIO, {d} in flight), waits {s}", .{ e, Aio.depth, if (a.poll) "polled (TF_DSV41_ENGRAM_POLL)" else "asleep in the kernel" }); + break :blk null; + } else |ae| if (!quiet) std.log.scoped(.dsv41).warn("engram: TF_DSV41_ENGRAM_AIO=1 but io_setup failed ({t}): the reader threads", .{ae}); + } + const n = poolThreads(); + if (n > 0) { + try r.startPool(n); + std.log.scoped(.dsv41).info("engram: no io_uring ({t}); rows read by {d} threads (TF_DSV41_ENGRAM_QD)", .{ e, n }); + } else if (!quiet) std.log.scoped(.dsv41).warn("engram: no io_uring ({t}) and TF_DSV41_ENGRAM_QD=0; rows are read when a window needs them", .{e}); + break :blk null; + }; + return r; + } + + pub const startAio = engram_io.startAio; + pub const startPool = engram_io.startPool; + pub const stopPool = engram_io.stopPool; + const worker = engram_io.worker; + const openTable = engram_io.openTable; + const closeTable = engram_io.closeTable; + + pub fn close(r: *Rows) void { + // every read in flight lands before its buffer goes + for (r.batches.items, 0..) |*b, i| if (b.in_use) { + r.wait(@intCast(i)) catch {}; + b.read = true; + }; + r.settle(null); + r.stopPool(); + if (r.ring) |*g| g.deinit(); + if (r.aio) |*a| a.deinit(); + for (r.tables) |*t| r.closeTable(t); + r.gpa.free(r.tables); + r.batches.deinit(r.gpa); + r.uniq.deinit(r.gpa); + r.recs.deinit(r.gpa); + r.want.deinit(r.gpa); + r.gpa.destroy(r.lut); + r.gpa.destroy(r); + } + + /// engram_host's interface to these rows. + pub fn source(r: *Rows) eh.Source { + return .{ .ptr = r, .vtable = &.{ .issue = issueFn, .rows = rowsFn } }; + } + + fn issueFn(p: *anyopaque, li: usize, idx: []const i64) anyerror!void { + const r: *Rows = @ptrCast(@alignCast(p)); + return r.issue(li, idx); + } + + fn rowsFn(p: *anyopaque, li: usize, idx: []const i64, dim: usize, out: []u16) anyerror!void { + const r: *Rows = @ptrCast(@alignCast(p)); + return r.rows(li, idx, dim, out); + } + + /// Sorted unique `idx` into r.uniq (checked against the shard's range). + fn unique(r: *Rows, t: *const Table, idx: []const i64) !void { + r.uniq.clearRetainingCapacity(); + try r.uniq.ensureTotalCapacity(r.gpa, idx.len); + for (idx) |i| { + if (i < 0 or @as(u64, @intCast(i)) < t.h.lo or @as(u64, @intCast(i)) >= t.h.hi) return error.EngramRowOutsideShard; + r.uniq.appendAssumeCapacity(@intCast(i)); + } + std.mem.sort(u64, r.uniq.items, {}, std.sort.asc(u64)); + var m: usize = 0; + for (r.uniq.items) |x| if (m == 0 or r.uniq.items[m - 1] != x) { + r.uniq.items[m] = x; + m += 1; + }; + r.uniq.shrinkRetainingCapacity(m); + } + + /// Starts reading layer `li`'s rows `idx` that are neither cached nor in flight; returns at once. + pub fn issue(r: *Rows, li: usize, idx: []const i64) !void { + const t = &r.tables[li]; + try r.unique(t, idx); + var m: usize = 0; + for (r.uniq.items) |x| if (!t.map.contains(x)) { + r.uniq.items[m] = x; + m += 1; + }; + if (m == 0) return; + r.stats.issued += m; + _ = try r.start(@intCast(li), r.uniq.items[0..m], false); + } + const start = engram_io.start; + pub const slotFor = engram_io.slotFor; + pub const queue = engram_io.queue; + pub const readExtent = engram_io.readExtent; + pub const aioSubmit = engram_io.aioSubmit; + pub const aioReap = engram_io.aioReap; + pub const reap = engram_io.reap; + const wait = engram_io.wait; + + /// Finished batches into the caches (bulk read through); `li`: the table whose `rows` call settles (null: none). + fn settle(r: *Rows, li: ?usize) void { + // the pool's workers write `left` / `failed`: settled under its lock (the batches' list stays put) + if (r.pool) |p| p.lock(); + defer if (r.pool) |p| p.unlock(); + for (r.batches.items) |*b| { + if (!b.in_use or b.left > 0) continue; + const t = &r.tables[b.table]; + const keep = !b.failed and b.rows.len <= r.cap / 4; + // a settle on another table never drops it: segments read layer 1 then 14, so the next 14 batch sees both + if (!keep and !b.failed and !b.read and (b.unread < 1 or li != b.table)) { + if (li == b.table) b.unread += 1; + continue; + } + for (b.rows, 0..) |x, k| { + if (!keep) { + t.drop(x); + continue; + } + const s = t.take(); + @memcpy(t.recs[s * row_bytes ..][0..row_bytes], b.record(k, t.h.lo)); + t.ids[s] = x; + t.front(s); + // the entry is this batch's (a row is in one batch at most; evictions only remove ready entries) + t.map.getPtr(x).?.* = .{ .ready = s }; + } + r.gpa.free(b.rows); + r.gpa.free(b.ext); + r.gpa.free(b.buf); + b.in_use = false; + } + } + + /// Layer `li`'s rows `idx` (global row ids this rank owns, any order, duplicates allowed) as bf16 [idx.len, dim]. + pub fn rows(r: *Rows, li: usize, idx: []const i64, dim: usize, out: []u16) !void { + if (dim > values or dim % 32 != 0 or out.len != idx.len * dim) return error.BadEngramRows; + const t = &r.tables[li]; + try r.unique(t, idx); + const u = r.uniq.items; + try r.recs.resize(r.gpa, u.len); + r.want.clearRetainingCapacity(); + // cached records now; in-flight ones after their batches; the rest read as one batch + var miss: std.ArrayList(u64) = .empty; + defer miss.deinit(r.gpa); + for (u, 0..) |x, k| { + if (t.map.get(x)) |e| switch (e) { + .ready => |s| { + r.recs.items[k] = t.recs[s * row_bytes ..].ptr; + t.unlink(s); + t.front(s); + r.stats.hits += 1; + }, + .pending => |bi| { + r.batches.items[bi].taken += 1; + if (std.mem.indexOfScalar(u32, r.want.items, bi) == null) try r.want.append(r.gpa, bi); + r.stats.waited += 1; + }, + } else try miss.append(r.gpa, x); + } + if (miss.items.len > 0) { + r.stats.misses += miss.items.len; + const bi = try r.start(@intCast(li), miss.items, true); + r.batches.items[bi].read = true; + try r.want.append(r.gpa, bi); + } + const t0 = nowNs(); + for (r.want.items) |bi| { + try r.wait(bi); + const b = &r.batches.items[bi]; + if (2 * @as(usize, b.taken) >= b.rows.len) b.read = true; + b.taken = 0; + } + r.stats.wait_ns += nowNs() - t0; + for (u, 0..) |x, k| switch (t.map.get(x).?) { + .ready => {}, + .pending => |bi| { + const b = &r.batches.items[bi]; + r.recs.items[k] = b.record(std.sort.binarySearch(u64, b.rows, x, orderU64).?, t.h.lo).ptr; + }, + }; + // every position: its record's values through the table (scale byte of each 32 values, then the e4m3 byte) + for (idx, 0..) |i, p| { + const rec = r.recs.items[std.sort.binarySearch(u64, u, @as(u64, @intCast(i)), orderU64).?]; + const o = out[p * dim ..][0..dim]; + for (o, 0..) |*v, j| v.* = r.lut[@as(usize, rec[values + j / 32]) << 8 | rec[j]]; + } + r.settle(li); + } +}; + +fn orderU64(a: u64, b: u64) std.math.Order { + return std.math.order(a, b); +} diff --git a/zig/src/families/deepseek_v41/engram_rows_test.zig b/zig/src/families/deepseek_v41/engram_rows_test.zig new file mode 100644 index 0000000000..b750d6143a --- /dev/null +++ b/zig/src/families/deepseek_v41/engram_rows_test.zig @@ -0,0 +1,383 @@ +//! engram_rows.zig against reference Engram host fixtures: bf16 table, packed shard via sync, threads and io_uring. + +const std = @import("std"); +const testing = std.testing; +const er = @import("engram_rows.zig"); +const eh = @import("engram_host.zig"); + +const ref = @embedFile("fixtures/engram-rows-ref.txt"); + +/// engram_fixture.record: byte j of global row r. +fn recordByte(r: u64, j: u64) u8 { + const h = (r * 2654435761 + j * 40503) % (1 << 32); + return if (j < 256) @truncate(h >> 7) else @intCast(100 + (h >> 9) % 40); +} + +const Fx = struct { lo: u64, rows: u64, total: u64, sets: [3]struct { idx: []i64, sha: [32]u8 } }; + +fn parseFx(a: std.mem.Allocator, lut_sha: *[32]u8) !Fx { + var fx: Fx = undefined; + var set: usize = 0; + var it = std.mem.tokenizeScalar(u8, ref, '\n'); + while (it.next()) |line| { + var w = std.mem.tokenizeScalar(u8, line, ' '); + const kind = w.next().?; + if (std.mem.eql(u8, kind, "lut")) { + _ = try std.fmt.hexToBytes(lut_sha, w.next().?); + } else if (std.mem.eql(u8, kind, "shard")) { + fx.lo = try std.fmt.parseInt(u64, w.next().?, 10); + fx.rows = try std.fmt.parseInt(u64, w.next().?, 10); + fx.total = try std.fmt.parseInt(u64, w.next().?, 10); + } else { + var ids: std.ArrayList(i64) = .empty; + var c = std.mem.tokenizeScalar(u8, w.next().?, ','); + while (c.next()) |x| try ids.append(a, try std.fmt.parseInt(i64, x, 10)); + fx.sets[set].idx = try ids.toOwnedSlice(a); + _ = try std.fmt.hexToBytes(&fx.sets[set].sha, w.next().?); + set += 1; + } + } + return fx; +} + +/// Writes the fixture's shard (the packer's layout) as engram-l1-r0of2.bin under `dir`. +fn writeShard(a: std.mem.Allocator, io: std.Io, dir: std.Io.Dir, fx: Fx) !void { + return writeShardAs(a, io, dir, fx, 1); +} + +fn writeShardAs(a: std.mem.Allocator, io: std.Io, dir: std.Io.Dir, fx: Fx, layer: u64) !void { + const n: usize = @intCast(er.header_bytes + fx.rows * er.row_bytes); + const b = try a.alloc(u8, n); + defer a.free(b); + @memset(b, 0); + for ([_]u64{ er.magic, layer, fx.lo, fx.lo + fx.rows, fx.total, er.row_bytes }, 0..) |v, i| std.mem.writeInt(u64, b[8 * i ..][0..8], v, .little); + for (0..fx.rows) |k| for (0..er.row_bytes) |j| { + b[er.header_bytes + k * er.row_bytes + j] = recordByte(fx.lo + k, j); + }; + var nb: [32]u8 = undefined; + try dir.writeFile(io, .{ .sub_path = try std.fmt.bufPrint(&nb, "engram-l{d}-r0of2.bin", .{layer}), .data = b }); +} + +fn sha(out: []const u16) [32]u8 { + var d: [32]u8 = undefined; + std.crypto.hash.sha2.Sha256.hash(std.mem.sliceAsBytes(out), &d, .{}); + return d; +} + +test "engram rows: the bf16 table equals torch's dequant(...).to(bfloat16)" { + var want: [32]u8 = undefined; + var arena = std.heap.ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + _ = try parseFx(arena.allocator(), &want); + const t = try testing.allocator.create([65536]u16); + defer testing.allocator.destroy(t); + er.fillTable(t); + try testing.expectEqual(want, sha(t)); + // spot checks: 1.0 x 2^0, -0.0, the smallest subnormal x 2^-6, scale byte 0 (x 0) + try testing.expectEqual(@as(u16, 0x3F80), t[127 << 8 | 0x38]); + try testing.expectEqual(@as(u16, 0x8000), t[127 << 8 | 0x80]); + try testing.expectEqual(@as(u16, 0x3800), t[121 << 8 | 0x01]); // 2^-9 x 2^-6 + try testing.expectEqual(@as(u16, 0x0000), t[0 << 8 | 0x38]); +} + +/// How a test reads: serially on the calling thread, by the reader threads (no io_uring), or by io_uring. +const Mode = enum { sync, pool, uring, aio, aio_poll }; +const modes = [_]Mode{ .sync, .pool, .uring, .aio, .aio_poll }; + +fn openRows(a: std.mem.Allocator, io: std.Io, tmp: *testing.TmpDir, mode: Mode) !*er.Rows { + const root = try tmp.dir.realPathFileAlloc(io, ".", a); + defer a.free(root); + const r = try er.Rows.open(a, root, 0, 2, &.{1}, 4096); + if (mode == .aio or mode == .aio_poll) { + try r.startAio(); // TF_DSV41_ENGRAM_AIO's reader: native AIO, no threads + r.aio.?.poll = mode == .aio_poll; // + TF_DSV41_ENGRAM_POLL: waits polled + return r; + } + if (r.aio) |*x| { // TF_DSV41_ENGRAM_AIO=1 in the environment: the other modes without it + _ = x; + r.aio.?.deinit(); + r.aio = null; + } + if (mode != .uring) if (r.ring) |*g| { + g.deinit(); + r.ring = null; + }; + if (mode == .pool and r.pool == null) try r.startPool(8); + if (mode == .sync) r.stopPool(); // no io_uring here: open started the pool + return r; +} + +test "engram rows: a packed shard read as the reference ShardRows.rows(...).to(bfloat16), synchronously, by the reader threads and by io_uring" { + const a = testing.allocator; + const io = testing.io; + var arena = std.heap.ArenaAllocator.init(a); + defer arena.deinit(); + var lut_sha: [32]u8 = undefined; + const fx = try parseFx(arena.allocator(), &lut_sha); + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + try writeShard(a, io, tmp.dir, fx); + for (modes) |mode| { + const r = try openRows(a, io, &tmp, mode); + defer r.close(); + try testing.expectEqual(fx.lo, r.tables[0].h.lo); + for (fx.sets) |s| { + const out = try a.alloc(u16, s.idx.len * 256); + defer a.free(out); + try r.rows(0, s.idx, 256, out); + try testing.expectEqual(s.sha, sha(out)); + // again: every record from the cache now, the same bits + const hits = r.stats.hits; + try r.rows(0, s.idx, 256, out); + try testing.expectEqual(s.sha, sha(out)); + try testing.expect(r.stats.hits > hits); + } + try testing.expectError(error.EngramRowOutsideShard, r.rows(0, &.{@intCast(fx.lo - 1)}, 256, try arena.allocator().alloc(u16, 256))); + } +} + +test "engram rows: issued ahead, then waited for; bulk reads past the cache; eviction keeps the bits" { + const a = testing.allocator; + const io = testing.io; + var arena = std.heap.ArenaAllocator.init(a); + defer arena.deinit(); + var lut_sha: [32]u8 = undefined; + const fx = try parseFx(arena.allocator(), &lut_sha); + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + try writeShard(a, io, tmp.dir, fx); + for (modes) |mode| { + const r = try openRows(a, io, &tmp, mode); + defer r.close(); + const s = fx.sets[1]; + try r.issue(0, s.idx); + try testing.expectEqual(@as(u64, s.idx.len), r.stats.issued); + try r.issue(0, s.idx); // nothing new: every row is in flight or cached + try testing.expectEqual(@as(u64, s.idx.len), r.stats.issued); + const out = try a.alloc(u16, s.idx.len * 256); + defer a.free(out); + try r.rows(0, s.idx, 256, out); + try testing.expectEqual(s.sha, sha(out)); + try testing.expectEqual(@as(u64, 0), r.stats.misses); + // every row of the shard once (a prompt's bulk read: past cap / 4, read through), then the sets again + const all = try a.alloc(i64, fx.rows); + defer a.free(all); + for (all, 0..) |*x, k| x.* = @intCast(fx.lo + fx.rows - 1 - k); + const big = try a.alloc(u16, all.len * 256); + defer a.free(big); + try r.rows(0, all, 256, big); + for (all, 0..) |x, k| for (0..256) |j| { + const row: u64 = @intCast(x); + const v = recordByte(row, j); + const sc = recordByte(row, 256 + j / 32); + try testing.expectEqual(r.lut[@as(usize, sc) << 8 | v], big[k * 256 + j]); + }; + for (fx.sets) |q| { + const o = try a.alloc(u16, q.idx.len * 256); + defer a.free(o); + try r.rows(0, q.idx, 256, o); + try testing.expectEqual(q.sha, sha(o)); + } + try testing.expect(r.tables[0].used <= 4096); + } +} + +test "engram rows: a prompt segment's bulk reads issued ahead wait through one unrelated read, then serve it" { + // TF_DSV41_PREFETCH_AHEAD: segment k + 1's read issued before k reads; k must not drop it, k + 1 finds every row + const a = testing.allocator; + const io = testing.io; + var arena = std.heap.ArenaAllocator.init(a); + defer arena.deinit(); + var lut_sha: [32]u8 = undefined; + const fx = try parseFx(arena.allocator(), &lut_sha); + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + try writeShard(a, io, tmp.dir, fx); + const all = try a.alloc(i64, fx.rows); + defer a.free(all); + for (all, 0..) |*x, k| x.* = @intCast(fx.lo + k); + try testing.expect(all.len > 4096 / 4); // a bulk batch: read through, not cached + const big = try a.alloc(u16, all.len * 256); + defer a.free(big); + for (modes) |mode| { + const r = try openRows(a, io, &tmp, mode); + defer r.close(); + try r.issue(0, all); + const s = fx.sets[0]; + const o = try a.alloc(u16, s.idx.len * 256); + defer a.free(o); + try r.rows(0, s.idx, 256, o); // segment k: waits for the rows it shares with the batch, the batch stays + try testing.expectEqual(s.sha, sha(o)); + const reads = r.stats.reads; + try r.rows(0, all, 256, big); // segment k + 1: every row from the batch issued ahead + try testing.expectEqual(@as(u64, 0), r.stats.misses); + try testing.expectEqual(reads, r.stats.reads); + for (all, 0..) |x, k| for (0..256) |j| { + const row: u64 = @intCast(x); + try testing.expectEqual(r.lut[@as(usize, recordByte(row, 256 + j / 32)) << 8 | recordByte(row, j)], big[k * 256 + j]); + }; + // a wrong guess: issued, then two unrelated reads of its table, then it is gone (read again on demand) + try r.issue(0, all); + try r.rows(0, s.idx, 256, o); + try r.rows(0, s.idx, 256, o); + const misses = r.stats.misses; + try r.rows(0, all, 256, big); + try testing.expect(r.stats.misses > misses); + for (r.batches.items) |b| try testing.expect(!b.in_use); + } +} + +test "engram rows: a prompt segment ahead on two tables survives the segment's layer-1 then layer-14 reads" { + // both layers' batches of k + 1 issued, k reads 1 then 14, k + 1 reads 1 (settle) then 14: no miss on either table + const a = testing.allocator; + const io = testing.io; + var arena = std.heap.ArenaAllocator.init(a); + defer arena.deinit(); + var lut_sha: [32]u8 = undefined; + const fx = try parseFx(arena.allocator(), &lut_sha); + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + try writeShardAs(a, io, tmp.dir, fx, 1); + try writeShardAs(a, io, tmp.dir, fx, 14); + const all = try a.alloc(i64, fx.rows); + defer a.free(all); + for (all, 0..) |*x, k| x.* = @intCast(fx.lo + k); + const big = try a.alloc(u16, all.len * 256); + defer a.free(big); + const s = fx.sets[0]; + const o = try a.alloc(u16, s.idx.len * 256); + defer a.free(o); + for (modes) |mode| { + const root = try tmp.dir.realPathFileAlloc(io, ".", a); + defer a.free(root); + const r = try er.Rows.open(a, root, 0, 2, &.{ 1, 14 }, 4096); + defer r.close(); + if (mode != .uring) if (r.ring) |*g| { + g.deinit(); + r.ring = null; + }; + if (mode == .pool and r.pool == null) try r.startPool(8); + if (mode == .sync) r.stopPool(); + try r.issue(0, all); + try r.issue(1, all); + for (0..2) |li| { + try r.rows(li, s.idx, 256, o); // segment k: a few shared rows of each table (waits for the batch) + try testing.expectEqual(s.sha, sha(o)); + } + const reads = r.stats.reads; + for (0..2) |li| { + try r.rows(li, all, 256, big); // segment k + 1: layer 1, then layer 14, all from the batches issued ahead + try testing.expectEqual(@as(u64, 0), r.stats.misses); + try testing.expectEqual(reads, r.stats.reads); + for (all, 0..) |x, k| for (0..256) |j| { + const row: u64 = @intCast(x); + try testing.expectEqual(r.lut[@as(usize, recordByte(row, 256 + j / 32)) << 8 | recordByte(row, j)], big[k * 256 + j]); + }; + } + for (r.batches.items) |b| try testing.expect(!b.in_use); + } +} + +test "engram rows: the reader threads with many batches in flight (issued, missed, waited for out of order)" { + const a = testing.allocator; + const io = testing.io; + var arena = std.heap.ArenaAllocator.init(a); + defer arena.deinit(); + var lut_sha: [32]u8 = undefined; + const fx = try parseFx(arena.allocator(), &lut_sha); + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + try writeShard(a, io, tmp.dir, fx); + const r = try openRows(a, io, &tmp, .pool); + defer r.close(); + var rng = std.Random.DefaultPrng.init(4101); + const rand = rng.random(); + const sets = try a.alloc([]i64, 48); + defer { + for (sets) |q| a.free(q); + a.free(sets); + } + for (sets) |*q| { + q.* = try a.alloc(i64, 1 + rand.uintLessThan(usize, 300)); + for (q.*) |*x| x.* = @intCast(fx.lo + rand.uintLessThan(u64, fx.rows)); + } + // every set issued ahead, then read in reverse (later batches waited for first), each read checked value by value + for (sets) |q| try r.issue(0, q); + var k = sets.len; + while (k > 0) { + k -= 1; + const q = sets[k]; + const out = try a.alloc(u16, q.len * 256); + defer a.free(out); + try r.rows(0, q, 256, out); + for (q, 0..) |x, i| for (0..256) |j| { + const row: u64 = @intCast(x); + try testing.expectEqual(r.lut[@as(usize, recordByte(row, 256 + j / 32)) << 8 | recordByte(row, j)], out[i * 256 + j]); + }; + } + try testing.expect(r.stats.issued > 0); +} + +test "engram rows: shard headers" { + var b: [48]u8 = undefined; + for ([_]u64{ er.magic, 14, 10, 20, 100, er.row_bytes }, 0..) |v, i| std.mem.writeInt(u64, b[8 * i ..][0..8], v, .little); + const h = try er.Header.parse(&b, er.header_bytes + 10 * er.row_bytes); + try testing.expectEqual(@as(u64, 14), h.layer); + try testing.expectError(error.BadEngramShard, er.Header.parse(&b, er.header_bytes + 9 * er.row_bytes)); + b[0] ^= 1; + try testing.expectError(error.NotEngramShard, er.Header.parse(&b, er.header_bytes + 10 * er.row_bytes)); +} + +test "engram lookback: the tail, then the kept rows of the window in flight" { + var buf: [16]u32 = undefined; + // nothing in flight: the next window starts at the slot's position + try testing.expectEqualSlices(u32, &.{ 7, 8, 9 }, eh.lookbackAt(&.{ 7, 8, 9 }, 50, null, 50, 3, &buf).?); + try testing.expect(eh.lookbackAt(&.{ 7, 8, 9 }, 50, null, 51, 3, &buf) == null); + // a window [a, d1, d2] at 50 not kept yet; the next starts at 52: a and d1 were kept + try testing.expectEqualSlices(u32, &.{ 9, 100, 101 }, eh.lookbackAt(&.{ 7, 8, 9 }, 50, &.{ 100, 101, 102 }, 52, 3, &buf).?); + try testing.expectEqualSlices(u32, &.{ 100, 101, 102 }, eh.lookbackAt(&.{ 7, 8, 9 }, 50, &.{ 100, 101, 102 }, 53, 3, &buf).?); + try testing.expect(eh.lookbackAt(&.{ 7, 8, 9 }, 50, &.{ 100, 101, 102 }, 54, 3, &buf) == null); + // the sequence start: fewer than keep_n + try testing.expectEqualSlices(u32, &.{5}, eh.lookbackAt(&.{}, 0, &.{ 5, 6 }, 1, 3, &buf).?); +} + +/// Slots of the record map that hold neither a key nor a tombstone: a lookup that misses probes until it finds one. +fn freeSlots(r: *const er.Rows) usize { + const m = &r.tables[0].map; + const meta = m.metadata orelse return 0; + var n: usize = 0; + for (meta[0..m.capacity()]) |x| n += @intFromBool(x.isFree()); + return n; +} + +test "engram rows: a long run of bulk reads and evictions leaves the record map free slots (no probe of the whole map)" { + // bulk reads past cap / 4 (inserted, then dropped) and evictions, as a long uncached prefill does + const a = testing.allocator; + const io = testing.io; + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + const fx: Fx = .{ .lo = 1000, .rows = 20000, .total = 40000, .sets = undefined }; + try writeShard(a, io, tmp.dir, fx); + const root = try tmp.dir.realPathFileAlloc(io, ".", a); + defer a.free(root); + const r = try er.Rows.open(a, root, 0, 2, &.{1}, 4096); + defer r.close(); + const ids = try a.alloc(i64, 1500); + defer a.free(ids); + const out = try a.alloc(u16, ids.len * 256); + defer a.free(out); + var rng = std.Random.DefaultPrng.init(7); + // the worst sample: free slots against the map's slots (it grows meanwhile) + var worst: [2]usize = .{ 1, 1 }; + for (0..300) |i| { + // a prompt segment's bulk read (past cap / 4: read through, its keys dropped), then a decode window's few + const n: usize = if (i % 2 == 0) ids.len else 64; + for (ids[0..n]) |*x| x.* = @intCast(fx.lo + rng.random().uintLessThan(u64, fx.rows)); + try r.rows(0, ids[0..n], 256, out[0 .. n * 256]); + const all = r.tables[0].map.capacity(); + const free = freeSlots(r); + if (free * worst[1] < worst[0] * all) worst = .{ free, all }; + } + errdefer std.debug.print("record map: {d} free of {d} slots at the worst\n", .{ worst[0], worst[1] }); + try testing.expect(worst[0] * 10 >= worst[1]); +} diff --git a/zig/src/families/deepseek_v41/ex_stacks.zig b/zig/src/families/deepseek_v41/ex_stacks.zig new file mode 100644 index 0000000000..c04a0b8135 --- /dev/null +++ b/zig/src/families/deepseek_v41/ex_stacks.zig @@ -0,0 +1,139 @@ +//! TF_DSV41_EX_STACKS=1: routed and shared expert suh / svh uploaded back to back, so the forms' stacks are views. + +const std = @import("std"); +const buffers = @import("buffers.zig"); + +const log = std.log.scoped(.dsv41); + +pub fn enabled() bool { + const v = std.c.getenv("TF_DSV41_EX_STACKS") orelse return false; + return std.mem.eql(u8, std.mem.trim(u8, std.mem.span(v), " "), "1"); +} + +/// The shared expert's tensor for a routed suh / svh ("L3.moe.w1.suh" -> "L3.moe.shared.0.w1.suh"), else null. +pub fn sharedOf(buf: []u8, name: []const u8) ?[]const u8 { + const at = std.mem.indexOf(u8, name, ".moe.w") orelse return null; + const rest = name[at + ".moe.".len ..]; + if (rest.len != "w1.suh".len or rest[1] < '1' or rest[1] > '3') return null; + if (!std.mem.endsWith(u8, rest, ".suh") and !std.mem.endsWith(u8, rest, ".svh")) return null; + return std.fmt.bufPrint(buf, "{s}.moe.shared.0.{s}", .{ name[0..at], rest }) catch null; +} + +/// The routed weight starting a stack role ("s.L3.ex.suh_g" -> "L3.moe.w1.suh"; g w1, u w3, d w2), else null. +pub fn stackWeight(buf: []u8, role: []const u8) ?[]const u8 { + if (!std.mem.startsWith(u8, role, "s.L")) return null; + const ex = std.mem.indexOf(u8, role, ".ex.") orelse return null; + const what = role[ex + 4 ..]; + if (what.len != 5 or what[3] != '_') return null; + const kind = what[0..3]; + if (!std.mem.eql(u8, kind, "suh") and !std.mem.eql(u8, kind, "svh")) return null; + const w: []const u8 = switch (what[4]) { + 'g' => "w1", + 'u' => "w3", + 'd' => "w2", + else => return null, + }; + return std.fmt.bufPrint(buf, "{s}.moe.{s}.{s}", .{ role[2..ex], w, kind }) catch null; +} + +/// Index pairs (routed, shared) of `names` to upload together: both present and the routed one's length 16-aligned. +pub fn pairs(gpa: std.mem.Allocator, names: []const []const u8, lens: []const usize) ![][2]usize { + var at: std.StringHashMapUnmanaged(usize) = .empty; + defer at.deinit(gpa); + for (names, 0..) |n, i| try at.put(gpa, n, i); + var out: std.ArrayList([2]usize) = .empty; + var buf: [192]u8 = undefined; + for (names, 0..) |n, i| { + const s = sharedOf(&buf, n) orelse continue; + const j = at.get(s) orelse continue; + if (lens[i] % 16 != 0) continue; // the shared half must start where a kernel may read it as a tensor + try out.append(gpa, .{ i, j }); + } + return out.toOwnedSlice(gpa); +} + +pub const Bound = struct { roles: u32 = 0, bytes: u64 = 0 }; + +/// Binds `plan`'s stacks to their routed weight where the shared half follows (r: run.Runner, weights: load.Weights). +pub fn bind(r: anytype, weights: anytype, plan: *const buffers.Plan) !Bound { + var out: Bound = .{}; + var nb: [192]u8 = undefined; + var sb: [192]u8 = undefined; + for (plan.sizes.keys(), plan.sizes.values()) |role, size| { + const wn = stackWeight(&nb, role) orelse continue; + if (r.addressOf(role) != null) continue; + const t = weights.get(wn) orelse continue; + const sn = sharedOf(&sb, wn) orelse continue; + const s = weights.get(sn) orelse continue; + if (s.ptr != t.ptr + t.len or size > t.len + s.len) continue; + try r.external(role, t.ptr); + out.roles += 1; + out.bytes += t.len + s.len; + } + log.info("expert stacks (TF_DSV41_EX_STACKS): {d} stacks read in place, {d:.2} GiB not allocated", .{ out.roles, @as(f64, @floatFromInt(out.bytes)) / (1 << 30) }); + return out; +} + +const testing = std.testing; + +const Span = struct { ptr: u64, len: usize }; +const Weights = struct { + map: std.StringHashMapUnmanaged(Span) = .empty, + fn get(w: *const Weights, name: []const u8) ?Span { + return w.map.get(name); + } +}; +const Roles = struct { + gpa: std.mem.Allocator, + addrs: std.StringHashMapUnmanaged(u64) = .empty, + fn addressOf(r: *const Roles, role: []const u8) ?u64 { + return r.addrs.get(role); + } + fn external(r: *Roles, role: []const u8, addr: u64) !void { + try r.addrs.put(r.gpa, role, addr); + } +}; + +test "expert stacks: names, pairs, and a stack bound only where its shared half follows the routed one" { + const gpa = testing.allocator; + var b: [192]u8 = undefined; + try testing.expectEqualStrings("L3.moe.shared.0.w1.suh", sharedOf(&b, "L3.moe.w1.suh").?); + try testing.expectEqualStrings("L40.moe.shared.0.w2.svh", sharedOf(&b, "L40.moe.w2.svh").?); + try testing.expectEqual(@as(?[]const u8, null), sharedOf(&b, "L3.moe.w1.trellis")); + try testing.expectEqual(@as(?[]const u8, null), sharedOf(&b, "L3.moe.shared.0.w1.suh")); + try testing.expectEqualStrings("L3.moe.w1.suh", stackWeight(&b, "s.L3.ex.suh_g").?); + try testing.expectEqualStrings("L3.moe.w3.svh", stackWeight(&b, "s.L3.ex.svh_u").?); + try testing.expectEqualStrings("L3.moe.w2.suh", stackWeight(&b, "s.L3.ex.suh_d").?); + try testing.expectEqual(@as(?[]const u8, null), stackWeight(&b, "s.L3.ex.tp_g")); + try testing.expectEqual(@as(?[]const u8, null), stackWeight(&b, "s.L3.ex.k2_d")); + // the builder's order: a pair is found wherever its halves sit; an odd routed length is left alone + const names = [_][]const u8{ "L3.moe.shared.0.w1.suh", "L3.attn.wq_b.trellis", "L3.moe.w1.suh", "L3.moe.w2.svh", "L3.moe.shared.0.w2.svh" }; + const p = try pairs(gpa, &names, &.{ 64, 8, 4096, 4090, 64 }); + defer gpa.free(p); + try testing.expectEqual(@as(usize, 1), p.len); + try testing.expectEqual([2]usize{ 2, 0 }, p[0]); + // binding: suh_g's halves adjacent (bound), suh_u's apart (its own buffer), a stack larger than both (its own) + var w: Weights = .{}; + defer w.map.deinit(gpa); + try w.map.put(gpa, "L3.moe.w1.suh", .{ .ptr = 0x1000, .len = 4096 }); + try w.map.put(gpa, "L3.moe.shared.0.w1.suh", .{ .ptr = 0x2000, .len = 64 }); + try w.map.put(gpa, "L3.moe.w3.suh", .{ .ptr = 0x9000, .len = 4096 }); + try w.map.put(gpa, "L3.moe.shared.0.w3.suh", .{ .ptr = 0x20000, .len = 64 }); + try w.map.put(gpa, "L3.moe.w2.suh", .{ .ptr = 0x30000, .len = 4096 }); + try w.map.put(gpa, "L3.moe.shared.0.w2.suh", .{ .ptr = 0x31000, .len = 64 }); + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + var plan: buffers.Plan = .{ .a = arena.allocator() }; + try plan.sizes.put(arena.allocator(), "s.L3.ex.suh_g", 4096 + 64); + try plan.sizes.put(arena.allocator(), "s.L3.ex.suh_u", 4096 + 64); + try plan.sizes.put(arena.allocator(), "s.L3.ex.suh_d", 4096 + 128); + try plan.sizes.put(arena.allocator(), "s.L3.ex.tp_g", 64); + var r: Roles = .{ .gpa = gpa }; + defer r.addrs.deinit(gpa); + const got = try bind(&r, &w, &plan); + try testing.expectEqual(@as(u32, 1), got.roles); + try testing.expectEqual(@as(?u64, 0x1000), r.addressOf("s.L3.ex.suh_g")); + try testing.expectEqual(@as(?u64, null), r.addressOf("s.L3.ex.suh_u")); + try testing.expectEqual(@as(?u64, null), r.addressOf("s.L3.ex.suh_d")); + try testing.expectEqual(@as(?u64, null), r.addressOf("s.L3.ex.tp_g")); +} diff --git a/zig/src/families/deepseek_v41/exl3.zig b/zig/src/families/deepseek_v41/exl3.zig new file mode 100644 index 0000000000..68050611a4 --- /dev/null +++ b/zig/src/families/deepseek_v41/exl3.zig @@ -0,0 +1,254 @@ +//! EXL3 groups as the pack stores them: trellis geometry, codebooks, a rank's TP slice as byte spans, expert layouts. + +const std = @import("std"); + +/// The codebook a group's trellis decodes with: the `.mul1` / `.mcg` marker's value, else the 3-instruction default. +pub const Codebook = enum(u8) { + inst3, + mul1, + mcg, + + pub const marker_mul1: u32 = 0x83DCD12D; + pub const marker_mcg: u32 = 0xCBAC1FED; + + /// A marker tensor's value (the low 32 bits of its first element); unknown values are refused. + pub fn fromMarker(v: u32) !Codebook { + return switch (v) { + marker_mul1 => .mul1, + marker_mcg => .mcg, + else => error.UnknownCodebookMarker, + }; + } +}; + +/// How a rank holds a group under TP (every boundary a multiple of 128, so Hadamard blocks stay whole). +pub const Split = enum { + /// all of it, on every rank + whole, + /// this rank's output columns: N tiles and `svh` + col, + /// this rank's input rows: K tiles and `suh` (a partial sum the ranks exchange) + row, +}; + +/// One EXL3 matrix y = x @ W, W [K, N]: trellis int16 [K/16, N/16, words], suh fp16 [K], svh fp16 [N]. +pub const Group = struct { + k_tiles: u32, + n_tiles: u32, + /// int16 words a 16x16 tile: 16 x bits (the trellis's last dimension) + words: u32, + + /// From the trellis entry's shape; a tile must hold whole K2 bytes and 1 to 8 bits a value. + pub fn fromShape(shape: []const usize) !Group { + if (shape.len != 3) return error.BadTrellis; + const w = shape[2]; + if (w == 0 or w % 8 != 0 or w > 128 or shape[0] == 0 or shape[1] == 0) return error.BadTrellis; + return .{ .k_tiles = @intCast(shape[0]), .n_tiles = @intCast(shape[1]), .words = @intCast(w) }; + } + + pub fn k(g: Group) u32 { + return 16 * g.k_tiles; + } + + pub fn n(g: Group) u32 { + return 16 * g.n_tiles; + } + + /// Half-bits a value (the kernels' K2): 4 for 2 bits, 16 for 8 bits. + pub fn k2(g: Group) u32 { + return g.words / 8; + } + + /// Bits a value, in halves exact (k2 / 2). + pub fn bits(g: Group) f32 { + return @as(f32, @floatFromInt(g.words)) / 16.0; + } + + pub fn tileBytes(g: Group) u64 { + return @as(u64, g.words) * 2; + } + + pub fn trellisBytes(g: Group) u64 { + return @as(u64, g.k_tiles) * g.n_tiles * g.tileBytes(); + } + + /// The rank's part of the group; a split that does not fall on 128-value blocks is refused. + pub fn part(g: Group, split: Split, rank: u32, world: u32) !Part { + std.debug.assert(rank < world); + var p: Part = .{ .group = g, .trellis = .{ .offset = 0, .len = g.trellisBytes() }, .suh = .{ .offset = 0, .len = 2 * @as(u64, g.k()) }, .svh = .{ .offset = 0, .len = 2 * @as(u64, g.n()) } }; + switch (split) { + .whole => {}, + .row => { + if (g.k() % (world * 128) != 0) return error.SplitNotOnBlocks; + const per = g.k_tiles / world; + p.group.k_tiles = per; + const row = @as(u64, g.n_tiles) * g.tileBytes(); + p.trellis = .{ .offset = rank * per * row, .len = per * row }; + p.suh = .{ .offset = 2 * 16 * @as(u64, rank * per), .len = 2 * 16 * @as(u64, per) }; + }, + .col => { + if (g.n() % (world * 128) != 0) return error.SplitNotOnBlocks; + const per = g.n_tiles / world; + p.group.n_tiles = per; + // one K-tile row at a time: this rank's N tiles are a run inside each row + p.trellis = .{ .offset = rank * per * g.tileBytes(), .len = per * g.tileBytes(), .stride = @as(u64, g.n_tiles) * g.tileBytes(), .count = g.k_tiles }; + p.svh = .{ .offset = 2 * 16 * @as(u64, rank * per), .len = 2 * 16 * @as(u64, per) }; + }, + } + return p; + } +}; + +/// Bytes of a stored tensor to read: `count` runs of `len` bytes, `stride` apart, from `offset` into the tensor. +pub const Span = struct { + offset: u64, + len: u64, + stride: u64 = 0, + count: u64 = 1, + + pub fn bytes(s: Span) u64 { + return s.len * s.count; + } + + /// The `i`th run's start. + pub fn at(s: Span, i: u64) u64 { + return s.offset + i * s.stride; + } +}; + +/// A rank's slice of a group: its geometry and where its trellis, suh and svh bytes sit in the stored tensors. +pub const Part = struct { + group: Group, + trellis: Span, + suh: Span, + svh: Span, + + pub fn bytes(p: Part) u64 { + return p.trellis.bytes() + p.suh.bytes() + p.svh.bytes(); + } +}; + +/// Routed experts of one projection on a rank: one width (X3Stack) or a width per expert (X3Ragged, E2). +pub const Layout = struct { + kind: enum { stack, ragged }, + k_tiles: u32, + n_tiles: u32, + /// each expert's int16 words a tile + words: []u16, + /// each expert's first int16 element in the projection's trellis buffer (back to back, as `ragged()` packs them) + offsets: []u64, + /// int16 elements of every expert's trellis + elements: u64, + + /// The layout of the rank's expert parts; parts must share K and N (the kernels' tables index one shape). + pub fn of(gpa: std.mem.Allocator, parts: []const Part) !Layout { + if (parts.len == 0) return error.NoExperts; + const words = try gpa.alloc(u16, parts.len); + errdefer gpa.free(words); + const offsets = try gpa.alloc(u64, parts.len); + errdefer gpa.free(offsets); + const kt = parts[0].group.k_tiles; + const nt = parts[0].group.n_tiles; + var at: u64 = 0; + var same = true; + for (parts, words, offsets) |p, *w, *o| { + if (p.group.k_tiles != kt or p.group.n_tiles != nt) return error.ExpertShapesDiffer; + w.* = @intCast(p.group.words); + o.* = at; + at += @as(u64, kt) * nt * p.group.words; + same = same and p.group.words == parts[0].group.words; + } + return .{ .kind = if (same) .stack else .ragged, .k_tiles = kt, .n_tiles = nt, .words = words, .offsets = offsets, .elements = at }; + } + + pub fn deinit(l: *Layout, gpa: std.mem.Allocator) void { + gpa.free(l.words); + gpa.free(l.offsets); + l.* = undefined; + } + + pub fn count(l: *const Layout) usize { + return l.words.len; + } + + pub fn trellisBytes(l: *const Layout) u64 { + return 2 * l.elements; + } + + /// Byte offset of expert `e`'s trellis in the projection buffer: 16-byte aligned (a tile is 16 K2 bytes). + pub fn byteOffset(l: *const Layout, e: usize) u64 { + return 2 * l.offsets[e]; + } + + /// Experts at each K2 (index = K2, 1..16): the widths x3gm launches once each. + pub fn histogram(l: *const Layout) [17]u32 { + var h: [17]u32 = @splat(0); + for (l.words) |w| h[w / 8] += 1; + return h; + } + + /// Distinct widths: x3gm's launches for this projection. + pub fn widths(l: *const Layout) u32 { + var n: u32 = 0; + for (l.histogram()) |c| n += @intFromBool(c > 0); + return n; + } +}; + +const testing = std.testing; + +test "group geometry and K2" { + const g = try Group.fromShape(&.{ 320, 144, 32 }); + try testing.expectEqual(@as(u32, 5120), g.k()); + try testing.expectEqual(@as(u32, 2304), g.n()); + try testing.expectEqual(@as(u32, 4), g.k2()); + try testing.expectEqual(@as(f32, 2.0), g.bits()); + try testing.expectError(error.BadTrellis, Group.fromShape(&.{ 320, 144, 30 })); + try testing.expectError(error.BadTrellis, Group.fromShape(&.{ 320, 144 })); + try testing.expectEqual(Codebook.mcg, try Codebook.fromMarker(0xCBAC1FED)); + try testing.expectError(error.UnknownCodebookMarker, Codebook.fromMarker(7)); +} + +test "TP parts: rows contiguous, columns a run a K-tile row" { + const w2 = try Group.fromShape(&.{ 144, 320, 48 }); // a 3-bit routed down projection: 2304 -> 5120 + const r1 = try w2.part(.row, 1, 2); + try testing.expectEqual(@as(u32, 72), r1.group.k_tiles); + try testing.expectEqual(@as(u64, 72 * 320 * 96), r1.trellis.len); + try testing.expectEqual(r1.trellis.len, r1.trellis.offset); + try testing.expectEqual(Span{ .offset = 2 * 1152, .len = 2 * 1152 }, r1.suh); + try testing.expectEqual(@as(u64, 2 * 5120), r1.svh.len); + const w1 = try Group.fromShape(&.{ 320, 144, 32 }); + const c1 = try w1.part(.col, 1, 2); + try testing.expectEqual(@as(u32, 72), c1.group.n_tiles); + try testing.expectEqual(@as(u64, 320), c1.trellis.count); + try testing.expectEqual(@as(u64, 144 * 64), c1.trellis.stride); + try testing.expectEqual(@as(u64, 72 * 64), c1.trellis.offset); + try testing.expectEqual(c1.group.trellisBytes(), c1.trellis.bytes()); + // the 8-bit indexer key projection (512 -> 128) cannot split its 128 outputs over two ranks + const wk = try Group.fromShape(&.{ 32, 8, 128 }); + try testing.expectError(error.SplitNotOnBlocks, wk.part(.col, 0, 2)); + try testing.expectEqual(wk.trellisBytes(), (try wk.part(.whole, 1, 2)).trellis.bytes()); +} + +test "expert layouts: one width stacks, mixed widths go ragged with aligned offsets" { + const a = testing.allocator; + const two = try (try Group.fromShape(&.{ 144, 320, 32 })).part(.row, 0, 2); + const three = try (try Group.fromShape(&.{ 144, 320, 48 })).part(.row, 0, 2); + var stack = try Layout.of(a, &.{ two, two, two }); + defer stack.deinit(a); + try testing.expect(stack.kind == .stack); + try testing.expectEqual(@as(u32, 1), stack.widths()); + // a mixed-bit pack's layer 2 down projection on rank 0: 383 experts at 2 bits and one at 3 bits + var parts: [384]Part = @splat(two); + parts[200] = three; + var rag = try Layout.of(a, &parts); + defer rag.deinit(a); + try testing.expect(rag.kind == .ragged); + try testing.expectEqual(@as(u64, 566968320), rag.trellisBytes()); + try testing.expectEqual(@as(u32, 383), rag.histogram()[4]); + try testing.expectEqual(@as(u32, 1), rag.histogram()[6]); + try testing.expectEqual(@as(u64, 0), rag.byteOffset(201) % 16); + try testing.expectEqual(rag.byteOffset(200) + three.trellis.bytes(), rag.byteOffset(201)); + const odd = try (try Group.fromShape(&.{ 144, 160, 32 })).part(.row, 0, 2); + try testing.expectError(error.ExpertShapesDiffer, Layout.of(a, &.{ two, odd })); +} diff --git a/zig/src/families/deepseek_v41/fixtures/chain-op-cap16.json b/zig/src/families/deepseek_v41/fixtures/chain-op-cap16.json new file mode 100644 index 0000000000..5692bf6033 --- /dev/null +++ b/zig/src/families/deepseek_v41/fixtures/chain-op-cap16.json @@ -0,0 +1,227 @@ +{ + "name": "_chain", + "function": "tensorfold.families.deepseek_v41.cuda.dspark._chain", + "grid": [ + 1, + 1, + 1 + ], + "options": { + "num_warps": 4 + }, + "args": [ + { + "name": "CAND", + "t": "tensor", + "dtype": "int32", + "shape": [ + 1, + 5, + 128 + ], + "stride": [ + 640, + 128, + 1 + ] + }, + { + "name": "CVAL", + "t": "tensor", + "dtype": "float32", + "shape": [ + 1, + 5, + 128 + ], + "stride": [ + 640, + 128, + 1 + ] + }, + { + "name": "c_stride", + "t": "int", + "v": 128 + }, + { + "name": "n_cand", + "t": "int", + "v": 128 + }, + { + "name": "W1", + "t": "tensor", + "dtype": "bfloat16", + "shape": [ + 129280, + 256 + ], + "stride": [ + 256, + 1 + ], + "weight": "dspark.markov.w1" + }, + { + "name": "W2", + "t": "tensor", + "dtype": "bfloat16", + "shape": [ + 129280, + 256 + ], + "stride": [ + 256, + 1 + ], + "weight": "dspark.markov.w2" + }, + { + "name": "HID", + "t": "tensor", + "dtype": "bfloat16", + "shape": [ + 1, + 5, + 5120 + ], + "stride": [ + 25600, + 5120, + 1 + ] + }, + { + "name": "CW", + "t": "tensor", + "dtype": "float32", + "shape": [ + 5376 + ], + "stride": [ + 1 + ], + "weight": "dspark.conf" + }, + { + "name": "ANCHOR", + "t": "tensor", + "dtype": "int32", + "shape": [ + 1 + ], + "stride": [ + 1 + ] + }, + { + "name": "IPAR", + "t": "tensor", + "dtype": "int64", + "shape": [ + 1, + 4 + ], + "stride": [ + 4, + 1 + ] + }, + { + "name": "FPAR", + "t": "tensor", + "dtype": "float32", + "shape": [ + 1, + 3 + ], + "stride": [ + 3, + 1 + ] + }, + { + "name": "DRAFT", + "t": "tensor", + "dtype": "int32", + "shape": [ + 1, + 5 + ], + "stride": [ + 5, + 1 + ] + }, + { + "name": "CONF", + "t": "tensor", + "dtype": "float32", + "shape": [ + 1, + 5 + ], + "stride": [ + 5, + 1 + ] + }, + { + "name": "SCR", + "t": "tensor", + "dtype": "float64", + "shape": [ + 1, + 2, + 128 + ], + "stride": [ + 256, + 128, + 1 + ] + }, + { + "name": "D", + "t": "int", + "v": 5120 + }, + { + "name": "N", + "t": "int", + "v": 5 + }, + { + "name": "KP", + "t": "int", + "v": 128 + }, + { + "name": "CH", + "t": "int", + "v": 16 + }, + { + "name": "R", + "t": "int", + "v": 256 + }, + { + "name": "RB", + "t": "int", + "v": 32 + }, + { + "name": "HB", + "t": "int", + "v": 1024 + }, + { + "name": "HAS_CONF", + "t": "bool", + "v": true + } + ] +} diff --git a/zig/src/families/deepseek_v41/fixtures/config.json b/zig/src/families/deepseek_v41/fixtures/config.json new file mode 100644 index 0000000000..eeec94126b --- /dev/null +++ b/zig/src/families/deepseek_v41/fixtures/config.json @@ -0,0 +1,184 @@ +{ + "architectures": [ + "DeepseekV41ForCausalLM" + ], + "model_type": "deepseek_v41", + "dtype": "bfloat16", + "transformers_version": "5.6.0", + "bos_token_id": 0, + "eos_token_id": 1, + "pad_token_id": 2, + "image_token_id": 129264, + "quantization_config": { + "quant_method": "exl3", + "version": "1.4.2", + "avg_bits": 2.9, + "head_bits": 6, + "calibration": { + "rows": 250, + "cols": 2048 + }, + "out_scales": "always", + "codebook": "mul1", + "original_quantization_config": { + "quant_method": "fp8", + "activation_scheme": "dynamic", + "weight_block_size": [ + 32, + 32 + ], + "scale_fmt": "ue8m0", + "expert_dtype": "fp4" + }, + "mtp_bits": 4 + }, + "text_config": { + "model_type": "deepseek_v41_text", + "vocab_size": 129280, + "hidden_size": 5120, + "moe_intermediate_size": 2304, + "num_hidden_layers": 40, + "num_attention_heads": 64, + "num_key_value_heads": 1, + "head_dim": 512, + "qk_rope_head_dim": 64, + "q_lora_rank": 1280, + "o_lora_rank": 1024, + "o_groups": 8, + "hidden_act": "silu", + "swiglu_limit": 10.0, + "rms_norm_eps": 1e-20, + "attention_bias": false, + "attention_dropout": 0.0, + "initializer_range": 0.02, + "use_cache": true, + "tie_word_embeddings": false, + "max_position_embeddings": 1048576, + "rope_theta": 10000, + "rope_scaling": { + "rope_type": "yarn", + "factor": 16, + "beta_fast": 32, + "beta_slow": 1, + "original_max_position_embeddings": 65536 + }, + "n_routed_experts": 384, + "n_shared_experts": 1, + "num_experts_per_tok": 6, + "scoring_func": "sqrtsoftplus", + "topk_method": "noaux_tc", + "norm_topk_prob": true, + "routed_scaling_factor": 1.5, + "sliding_window": 128, + "compress_ratios": [ + 0, + 0, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0 + ], + "compress_rope_theta": 160000, + "kv_source_layer_ids": [ + 2, + 8, + 14, + 20 + ], + "index_source_layer_ids": [ + 2, + 8, + 14, + 20, + 24, + 28, + 32, + 36 + ], + "index_n_heads": 32, + "index_head_dim": 128, + "index_topk": 512, + "candidate_source_layer_id": 20, + "candidate_topk_blocks": 2048, + "candidate_block_size": 8, + "hc_mult": 4, + "hc_sinkhorn_iters": 20, + "hc_eps": 1e-06, + "engram_layer_ids": [ + 1, + 14 + ], + "engram_num_embeddings": [ + 384006168, + 384016682 + ], + "engram_max_ngram_size": 4, + "engram_vocab_size": 16000000, + "engram_n_heads": 8, + "engram_head_dim": 256, + "engram_pad_token_id": 2, + "engram_compressed_vocab_size": 99092, + "num_nextn_predict_layers": 3, + "dspark_block_size": 5, + "dspark_noise_token_id": 128799, + "dspark_target_layer_ids": [ + 37, + 38, + 39 + ], + "dspark_markov_rank": 256, + "dspark_n_routed_experts": 128, + "dspark_num_experts_per_tok": 3 + }, + "vision_config": { + "model_type": "deepseek_v41_vision", + "num_hidden_layers": 32, + "hidden_size": 1024, + "num_attention_heads": 16, + "intermediate_size": 2816, + "patch_size": 14, + "rope_theta": 10000, + "downsample_ratio": 3, + "max_image_tokens": 1024, + "min_pixels": 295936, + "max_wh_ratio": null + }, + "engram_table_dir": null +} \ No newline at end of file diff --git a/zig/src/families/deepseek_v41/fixtures/dense-k2.txt b/zig/src/families/deepseek_v41/fixtures/dense-k2.txt new file mode 100644 index 0000000000..7f8584e776 --- /dev/null +++ b/zig/src/families/deepseek_v41/fixtures/dense-k2.txt @@ -0,0 +1,496 @@ +# dense group K2 of a 2.8 bpw EXL3 pack (attn bits by layer, csa2 / engram classes, head, DSpark mtp_bits; ix_wk 8-bit) x 2 +L0.attn.wkv 10 +L0.attn.wo_a.0 10 +L0.attn.wo_a.1 10 +L0.attn.wo_a.2 10 +L0.attn.wo_a.3 10 +L0.attn.wo_b 8 +L0.attn.wq_a 10 +L0.attn.wq_b 8 +L0.moe.shared.0.w1 10 +L0.moe.shared.0.w2 10 +L0.moe.shared.0.w3 10 +L1.attn.wkv 10 +L1.attn.wo_a.0 10 +L1.attn.wo_a.1 10 +L1.attn.wo_a.2 10 +L1.attn.wo_a.3 10 +L1.attn.wo_b 8 +L1.attn.wq_a 8 +L1.attn.wq_b 8 +L1.engram.wkv 10 +L1.moe.shared.0.w1 10 +L1.moe.shared.0.w2 10 +L1.moe.shared.0.w3 10 +L10.attn.wkv 8 +L10.attn.wo_a.0 8 +L10.attn.wo_a.1 8 +L10.attn.wo_a.2 8 +L10.attn.wo_a.3 8 +L10.attn.wo_b 8 +L10.attn.wq_a 8 +L10.attn.wq_b 8 +L10.moe.shared.0.w1 12 +L10.moe.shared.0.w2 12 +L10.moe.shared.0.w3 12 +L11.attn.wkv 8 +L11.attn.wo_a.0 8 +L11.attn.wo_a.1 8 +L11.attn.wo_a.2 8 +L11.attn.wo_a.3 8 +L11.attn.wo_b 8 +L11.attn.wq_a 8 +L11.attn.wq_b 8 +L11.moe.shared.0.w1 12 +L11.moe.shared.0.w2 12 +L11.moe.shared.0.w3 12 +L12.attn.wkv 8 +L12.attn.wo_a.0 8 +L12.attn.wo_a.1 8 +L12.attn.wo_a.2 8 +L12.attn.wo_a.3 8 +L12.attn.wo_b 8 +L12.attn.wq_a 8 +L12.attn.wq_b 8 +L12.moe.shared.0.w1 12 +L12.moe.shared.0.w2 12 +L12.moe.shared.0.w3 12 +L13.attn.wkv 8 +L13.attn.wo_a.0 8 +L13.attn.wo_a.1 8 +L13.attn.wo_a.2 8 +L13.attn.wo_a.3 8 +L13.attn.wo_b 8 +L13.attn.wq_a 8 +L13.attn.wq_b 8 +L13.moe.shared.0.w1 12 +L13.moe.shared.0.w2 12 +L13.moe.shared.0.w3 12 +L14.attn.comp_wgate 10 +L14.attn.comp_wkv 10 +L14.attn.ix_wk 16 +L14.attn.ix_wq_b 10 +L14.attn.wkv 8 +L14.attn.wo_a.0 8 +L14.attn.wo_a.1 8 +L14.attn.wo_a.2 8 +L14.attn.wo_a.3 8 +L14.attn.wo_b 8 +L14.attn.wq_a 8 +L14.attn.wq_b 8 +L14.engram.wkv 10 +L14.moe.shared.0.w1 12 +L14.moe.shared.0.w2 12 +L14.moe.shared.0.w3 12 +L15.attn.wkv 8 +L15.attn.wo_a.0 8 +L15.attn.wo_a.1 8 +L15.attn.wo_a.2 8 +L15.attn.wo_a.3 8 +L15.attn.wo_b 8 +L15.attn.wq_a 8 +L15.attn.wq_b 8 +L15.moe.shared.0.w1 12 +L15.moe.shared.0.w2 12 +L15.moe.shared.0.w3 12 +L16.attn.wkv 8 +L16.attn.wo_a.0 8 +L16.attn.wo_a.1 8 +L16.attn.wo_a.2 8 +L16.attn.wo_a.3 8 +L16.attn.wo_b 8 +L16.attn.wq_a 8 +L16.attn.wq_b 8 +L16.moe.shared.0.w1 12 +L16.moe.shared.0.w2 12 +L16.moe.shared.0.w3 12 +L17.attn.wkv 8 +L17.attn.wo_a.0 8 +L17.attn.wo_a.1 8 +L17.attn.wo_a.2 8 +L17.attn.wo_a.3 8 +L17.attn.wo_b 8 +L17.attn.wq_a 8 +L17.attn.wq_b 8 +L17.moe.shared.0.w1 12 +L17.moe.shared.0.w2 12 +L17.moe.shared.0.w3 12 +L18.attn.wkv 8 +L18.attn.wo_a.0 8 +L18.attn.wo_a.1 8 +L18.attn.wo_a.2 8 +L18.attn.wo_a.3 8 +L18.attn.wo_b 8 +L18.attn.wq_a 8 +L18.attn.wq_b 8 +L18.moe.shared.0.w1 12 +L18.moe.shared.0.w2 12 +L18.moe.shared.0.w3 12 +L19.attn.wkv 8 +L19.attn.wo_a.0 8 +L19.attn.wo_a.1 8 +L19.attn.wo_a.2 8 +L19.attn.wo_a.3 8 +L19.attn.wo_b 8 +L19.attn.wq_a 8 +L19.attn.wq_b 8 +L19.moe.shared.0.w1 12 +L19.moe.shared.0.w2 12 +L19.moe.shared.0.w3 12 +L2.attn.comp_wgate 10 +L2.attn.comp_wkv 10 +L2.attn.ix_wk 16 +L2.attn.ix_wq_b 10 +L2.attn.wkv 10 +L2.attn.wo_a.0 8 +L2.attn.wo_a.1 8 +L2.attn.wo_a.2 8 +L2.attn.wo_a.3 8 +L2.attn.wo_b 8 +L2.attn.wq_a 8 +L2.attn.wq_b 8 +L2.moe.shared.0.w1 10 +L2.moe.shared.0.w2 10 +L2.moe.shared.0.w3 10 +L20.attn.comp_wkv 10 +L20.attn.ix_wk 16 +L20.attn.ix_wq_b 10 +L20.attn.wkv 8 +L20.attn.wo_a.0 8 +L20.attn.wo_a.1 8 +L20.attn.wo_a.2 8 +L20.attn.wo_a.3 8 +L20.attn.wo_b 8 +L20.attn.wq_a 8 +L20.attn.wq_b 8 +L20.moe.shared.0.w1 12 +L20.moe.shared.0.w2 12 +L20.moe.shared.0.w3 12 +L21.attn.wkv 8 +L21.attn.wo_a.0 8 +L21.attn.wo_a.1 8 +L21.attn.wo_a.2 8 +L21.attn.wo_a.3 8 +L21.attn.wo_b 8 +L21.attn.wq_a 8 +L21.attn.wq_b 8 +L21.moe.shared.0.w1 12 +L21.moe.shared.0.w2 12 +L21.moe.shared.0.w3 12 +L22.attn.wkv 8 +L22.attn.wo_a.0 8 +L22.attn.wo_a.1 8 +L22.attn.wo_a.2 8 +L22.attn.wo_a.3 8 +L22.attn.wo_b 8 +L22.attn.wq_a 8 +L22.attn.wq_b 8 +L22.moe.shared.0.w1 12 +L22.moe.shared.0.w2 12 +L22.moe.shared.0.w3 12 +L23.attn.wkv 8 +L23.attn.wo_a.0 8 +L23.attn.wo_a.1 8 +L23.attn.wo_a.2 8 +L23.attn.wo_a.3 8 +L23.attn.wo_b 8 +L23.attn.wq_a 8 +L23.attn.wq_b 8 +L23.moe.shared.0.w1 12 +L23.moe.shared.0.w2 12 +L23.moe.shared.0.w3 12 +L24.attn.ix_wq_b 10 +L24.attn.wkv 8 +L24.attn.wo_a.0 8 +L24.attn.wo_a.1 8 +L24.attn.wo_a.2 8 +L24.attn.wo_a.3 8 +L24.attn.wo_b 8 +L24.attn.wq_a 8 +L24.attn.wq_b 8 +L24.moe.shared.0.w1 12 +L24.moe.shared.0.w2 12 +L24.moe.shared.0.w3 12 +L25.attn.wkv 8 +L25.attn.wo_a.0 8 +L25.attn.wo_a.1 8 +L25.attn.wo_a.2 8 +L25.attn.wo_a.3 8 +L25.attn.wo_b 8 +L25.attn.wq_a 8 +L25.attn.wq_b 8 +L25.moe.shared.0.w1 12 +L25.moe.shared.0.w2 12 +L25.moe.shared.0.w3 12 +L26.attn.wkv 8 +L26.attn.wo_a.0 8 +L26.attn.wo_a.1 8 +L26.attn.wo_a.2 8 +L26.attn.wo_a.3 8 +L26.attn.wo_b 8 +L26.attn.wq_a 8 +L26.attn.wq_b 8 +L26.moe.shared.0.w1 12 +L26.moe.shared.0.w2 12 +L26.moe.shared.0.w3 12 +L27.attn.wkv 8 +L27.attn.wo_a.0 8 +L27.attn.wo_a.1 8 +L27.attn.wo_a.2 8 +L27.attn.wo_a.3 8 +L27.attn.wo_b 8 +L27.attn.wq_a 8 +L27.attn.wq_b 8 +L27.moe.shared.0.w1 12 +L27.moe.shared.0.w2 12 +L27.moe.shared.0.w3 12 +L28.attn.ix_wq_b 10 +L28.attn.wkv 8 +L28.attn.wo_a.0 8 +L28.attn.wo_a.1 8 +L28.attn.wo_a.2 8 +L28.attn.wo_a.3 8 +L28.attn.wo_b 8 +L28.attn.wq_a 8 +L28.attn.wq_b 8 +L28.moe.shared.0.w1 12 +L28.moe.shared.0.w2 12 +L28.moe.shared.0.w3 12 +L29.attn.wkv 8 +L29.attn.wo_a.0 8 +L29.attn.wo_a.1 8 +L29.attn.wo_a.2 8 +L29.attn.wo_a.3 8 +L29.attn.wo_b 8 +L29.attn.wq_a 8 +L29.attn.wq_b 8 +L29.moe.shared.0.w1 12 +L29.moe.shared.0.w2 12 +L29.moe.shared.0.w3 12 +L3.attn.wkv 8 +L3.attn.wo_a.0 8 +L3.attn.wo_a.1 8 +L3.attn.wo_a.2 8 +L3.attn.wo_a.3 8 +L3.attn.wo_b 8 +L3.attn.wq_a 8 +L3.attn.wq_b 8 +L3.moe.shared.0.w1 10 +L3.moe.shared.0.w2 10 +L3.moe.shared.0.w3 10 +L30.attn.wkv 8 +L30.attn.wo_a.0 8 +L30.attn.wo_a.1 8 +L30.attn.wo_a.2 8 +L30.attn.wo_a.3 8 +L30.attn.wo_b 8 +L30.attn.wq_a 8 +L30.attn.wq_b 8 +L30.moe.shared.0.w1 12 +L30.moe.shared.0.w2 12 +L30.moe.shared.0.w3 12 +L31.attn.wkv 8 +L31.attn.wo_a.0 8 +L31.attn.wo_a.1 8 +L31.attn.wo_a.2 8 +L31.attn.wo_a.3 8 +L31.attn.wo_b 8 +L31.attn.wq_a 8 +L31.attn.wq_b 8 +L31.moe.shared.0.w1 12 +L31.moe.shared.0.w2 12 +L31.moe.shared.0.w3 12 +L32.attn.ix_wq_b 10 +L32.attn.wkv 8 +L32.attn.wo_a.0 8 +L32.attn.wo_a.1 8 +L32.attn.wo_a.2 8 +L32.attn.wo_a.3 8 +L32.attn.wo_b 8 +L32.attn.wq_a 8 +L32.attn.wq_b 8 +L32.moe.shared.0.w1 12 +L32.moe.shared.0.w2 12 +L32.moe.shared.0.w3 12 +L33.attn.wkv 8 +L33.attn.wo_a.0 8 +L33.attn.wo_a.1 8 +L33.attn.wo_a.2 8 +L33.attn.wo_a.3 8 +L33.attn.wo_b 8 +L33.attn.wq_a 8 +L33.attn.wq_b 8 +L33.moe.shared.0.w1 12 +L33.moe.shared.0.w2 12 +L33.moe.shared.0.w3 12 +L34.attn.wkv 8 +L34.attn.wo_a.0 8 +L34.attn.wo_a.1 8 +L34.attn.wo_a.2 8 +L34.attn.wo_a.3 8 +L34.attn.wo_b 8 +L34.attn.wq_a 8 +L34.attn.wq_b 8 +L34.moe.shared.0.w1 12 +L34.moe.shared.0.w2 12 +L34.moe.shared.0.w3 12 +L35.attn.wkv 8 +L35.attn.wo_a.0 8 +L35.attn.wo_a.1 8 +L35.attn.wo_a.2 8 +L35.attn.wo_a.3 8 +L35.attn.wo_b 8 +L35.attn.wq_a 8 +L35.attn.wq_b 8 +L35.moe.shared.0.w1 12 +L35.moe.shared.0.w2 12 +L35.moe.shared.0.w3 12 +L36.attn.ix_wq_b 10 +L36.attn.wkv 8 +L36.attn.wo_a.0 8 +L36.attn.wo_a.1 8 +L36.attn.wo_a.2 8 +L36.attn.wo_a.3 8 +L36.attn.wo_b 8 +L36.attn.wq_a 8 +L36.attn.wq_b 8 +L36.moe.shared.0.w1 12 +L36.moe.shared.0.w2 12 +L36.moe.shared.0.w3 12 +L37.attn.wkv 8 +L37.attn.wo_a.0 8 +L37.attn.wo_a.1 8 +L37.attn.wo_a.2 8 +L37.attn.wo_a.3 8 +L37.attn.wo_b 8 +L37.attn.wq_a 8 +L37.attn.wq_b 8 +L37.moe.shared.0.w1 12 +L37.moe.shared.0.w2 12 +L37.moe.shared.0.w3 12 +L38.attn.wkv 8 +L38.attn.wo_a.0 8 +L38.attn.wo_a.1 8 +L38.attn.wo_a.2 8 +L38.attn.wo_a.3 8 +L38.attn.wo_b 8 +L38.attn.wq_a 8 +L38.attn.wq_b 8 +L38.moe.shared.0.w1 12 +L38.moe.shared.0.w2 12 +L38.moe.shared.0.w3 12 +L39.attn.wkv 8 +L39.attn.wo_a.0 8 +L39.attn.wo_a.1 8 +L39.attn.wo_a.2 8 +L39.attn.wo_a.3 8 +L39.attn.wo_b 8 +L39.attn.wq_a 8 +L39.attn.wq_b 8 +L39.moe.shared.0.w1 12 +L39.moe.shared.0.w2 12 +L39.moe.shared.0.w3 12 +L4.attn.wkv 8 +L4.attn.wo_a.0 8 +L4.attn.wo_a.1 8 +L4.attn.wo_a.2 8 +L4.attn.wo_a.3 8 +L4.attn.wo_b 8 +L4.attn.wq_a 8 +L4.attn.wq_b 8 +L4.moe.shared.0.w1 12 +L4.moe.shared.0.w2 12 +L4.moe.shared.0.w3 12 +L40.attn.wkv 8 +L40.attn.wo_a.0 8 +L40.attn.wo_a.1 8 +L40.attn.wo_a.2 8 +L40.attn.wo_a.3 8 +L40.attn.wo_b 8 +L40.attn.wq_a 8 +L40.attn.wq_b 8 +L40.moe.shared.0.w1 8 +L40.moe.shared.0.w2 8 +L40.moe.shared.0.w3 8 +L41.attn.wkv 8 +L41.attn.wo_a.0 8 +L41.attn.wo_a.1 8 +L41.attn.wo_a.2 8 +L41.attn.wo_a.3 8 +L41.attn.wo_b 8 +L41.attn.wq_a 8 +L41.attn.wq_b 8 +L41.moe.shared.0.w1 8 +L41.moe.shared.0.w2 8 +L41.moe.shared.0.w3 8 +L42.attn.wkv 8 +L42.attn.wo_a.0 8 +L42.attn.wo_a.1 8 +L42.attn.wo_a.2 8 +L42.attn.wo_a.3 8 +L42.attn.wo_b 8 +L42.attn.wq_a 8 +L42.attn.wq_b 8 +L42.moe.shared.0.w1 8 +L42.moe.shared.0.w2 8 +L42.moe.shared.0.w3 8 +L5.attn.wkv 8 +L5.attn.wo_a.0 8 +L5.attn.wo_a.1 8 +L5.attn.wo_a.2 8 +L5.attn.wo_a.3 8 +L5.attn.wo_b 8 +L5.attn.wq_a 8 +L5.attn.wq_b 8 +L5.moe.shared.0.w1 12 +L5.moe.shared.0.w2 12 +L5.moe.shared.0.w3 12 +L6.attn.wkv 8 +L6.attn.wo_a.0 8 +L6.attn.wo_a.1 8 +L6.attn.wo_a.2 8 +L6.attn.wo_a.3 8 +L6.attn.wo_b 8 +L6.attn.wq_a 8 +L6.attn.wq_b 8 +L6.moe.shared.0.w1 12 +L6.moe.shared.0.w2 12 +L6.moe.shared.0.w3 12 +L7.attn.wkv 8 +L7.attn.wo_a.0 8 +L7.attn.wo_a.1 8 +L7.attn.wo_a.2 8 +L7.attn.wo_a.3 8 +L7.attn.wo_b 8 +L7.attn.wq_a 8 +L7.attn.wq_b 8 +L7.moe.shared.0.w1 12 +L7.moe.shared.0.w2 12 +L7.moe.shared.0.w3 12 +L8.attn.comp_wgate 10 +L8.attn.comp_wkv 10 +L8.attn.ix_wk 16 +L8.attn.ix_wq_b 10 +L8.attn.wkv 8 +L8.attn.wo_a.0 8 +L8.attn.wo_a.1 8 +L8.attn.wo_a.2 8 +L8.attn.wo_a.3 8 +L8.attn.wo_b 8 +L8.attn.wq_a 8 +L8.attn.wq_b 8 +L8.moe.shared.0.w1 12 +L8.moe.shared.0.w2 12 +L8.moe.shared.0.w3 12 +L9.attn.wkv 8 +L9.attn.wo_a.0 8 +L9.attn.wo_a.1 8 +L9.attn.wo_a.2 8 +L9.attn.wo_a.3 8 +L9.attn.wo_b 8 +L9.attn.wq_a 8 +L9.attn.wq_b 8 +L9.moe.shared.0.w1 12 +L9.moe.shared.0.w2 12 +L9.moe.shared.0.w3 12 +head 12 diff --git a/zig/src/families/deepseek_v41/fixtures/engram-hash-ref.bin b/zig/src/families/deepseek_v41/fixtures/engram-hash-ref.bin new file mode 100644 index 0000000000..2e1b20b38c Binary files /dev/null and b/zig/src/families/deepseek_v41/fixtures/engram-hash-ref.bin differ diff --git a/zig/src/families/deepseek_v41/fixtures/engram-rows-ref.txt b/zig/src/families/deepseek_v41/fixtures/engram-rows-ref.txt new file mode 100644 index 0000000000..d9ff54b326 --- /dev/null +++ b/zig/src/families/deepseek_v41/fixtures/engram-rows-ref.txt @@ -0,0 +1,5 @@ +lut ac4091794caec6f553a226c3857da2756d0b29c4c7b24e02ce61274a3df93185 +shard 1000 3000 9000 +rows 1000,1001,1001,3999,1007,2500,1007 82b5fc83bc4eb5195c2d36925f57ef8af91663ed60af344641924535b8dc25b4 +rows 1000,1977,2954,3931,1908,2885,3862,1839,2816,3793,1770,2747,3724,1701,2678,3655,1632,2609,3586,1563,2540,3517,1494,2471,3448,1425,2402,3379,1356,2333,3310,1287,2264,3241,1218,2195,3172,1149,2126,3103,1080,2057,3034,1011,1988,2965,3942,1919,2896,3873,1850,2827,3804,1781,2758,3735,1712,2689,3666,1643,2620,3597,1574,2551,3528,1505,2482,3459,1436,2413,3390,1367,2344,3321,1298,2275,3252,1229,2206,3183,1160,2137,3114,1091,2068,3045,1022,1999,2976,3953,1930,2907,3884,1861,2838,3815 6a0439b98aad93e03a744b515286f8041b7567bd09e15a2f1e358e00b4950199 +rows 1259,1258,1257,1256,1255,1254,1253,1252,1251,1250,1249,1248,1247,1246,1245,1244,1243,1242,1241,1240,1239,1238,1237,1236,1235,1234,1233,1232,1231,1230,1229,1228,1227,1226,1225,1224,1223,1222,1221,1220,1219,1218,1217,1216,1215,1214,1213,1212,1211,1210,1209,1208,1207,1206,1205,1204,1203,1202,1201,1200 f8ef91955c85ec1ba1caa46f4116ddc94cea8204c72f97670292cbb9d8ce82b5 diff --git a/zig/src/families/deepseek_v41/fixtures/expert-k2.txt b/zig/src/families/deepseek_v41/fixtures/expert-k2.txt new file mode 100644 index 0000000000..111e03078b --- /dev/null +++ b/zig/src/families/deepseek_v41/fixtures/expert-k2.txt @@ -0,0 +1,44 @@ +# expert K2 ranges of a 2.8 bpw EXL3 pack (bits x 2; DSpark blocks: mtp_bits 4): layer gate/up lo hi, down lo hi +0 4 10 4 10 +1 4 10 4 10 +2 4 10 4 10 +3 4 10 4 10 +4 4 12 4 12 +5 4 12 4 12 +6 4 12 4 12 +7 4 12 4 12 +8 4 12 4 12 +9 4 12 4 12 +10 4 12 4 12 +11 4 12 4 12 +12 4 12 4 12 +13 4 12 4 12 +14 4 12 4 12 +15 4 12 4 12 +16 4 12 4 12 +17 4 12 4 12 +18 4 12 4 12 +19 4 12 4 12 +20 4 12 4 12 +21 4 12 4 12 +22 4 12 4 12 +23 4 12 4 12 +24 4 12 4 12 +25 4 12 4 12 +26 4 12 4 12 +27 4 12 4 12 +28 4 12 4 12 +29 4 12 4 12 +30 4 12 4 12 +31 4 12 4 12 +32 4 12 4 12 +33 4 12 4 12 +34 4 12 4 12 +35 4 12 4 12 +36 6 12 6 12 +37 6 12 6 12 +38 6 12 6 12 +39 6 12 6 12 +40 8 8 8 8 +41 8 8 8 8 +42 8 8 8 8 diff --git a/zig/src/families/deepseek_v41/fixtures/lanes-ref.bin b/zig/src/families/deepseek_v41/fixtures/lanes-ref.bin new file mode 100644 index 0000000000..231acd0531 Binary files /dev/null and b/zig/src/families/deepseek_v41/fixtures/lanes-ref.bin differ diff --git a/zig/src/families/deepseek_v41/fixtures/serve/fixtures.zig b/zig/src/families/deepseek_v41/fixtures/serve/fixtures.zig new file mode 100644 index 0000000000..779975f58b --- /dev/null +++ b/zig/src/families/deepseek_v41/fixtures/serve/fixtures.zig @@ -0,0 +1,2 @@ +//! The serving goldens committed with the code (recipe/server_tests/gen_*.py wrote them from the Python engine). +pub const sampling = @embedFile("sampling.jsonl"); diff --git a/zig/src/families/deepseek_v41/fixtures/serve/sampling.jsonl b/zig/src/families/deepseek_v41/fixtures/serve/sampling.jsonl new file mode 100644 index 0000000000..0db56e154b --- /dev/null +++ b/zig/src/families/deepseek_v41/fixtures/serve/sampling.jsonl @@ -0,0 +1,240 @@ +{"logits": "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", "world": 2, "sampling": [3541722407261185182, 0.3, 0, 0.8, 0.0], "position": 463552, "count": 512, "stats": [[1.40625, 10.689110477635008], [13.6875, 1.0]], "full": false, "token": 314} +{"logits": "AAD7vgAAWr8AAF28AABLvgAAVr8AAKM9AACGvgAA370AADM/AAD4PgAAfT4AACe+AAAXvwAAp74AAD6+AAAhPwAA7L4AAIq+AACKuwAASD4AAAo/AAAqPwAAl70AAHY/AACsPgAAyL4AAPm+AABWvgAAAb4AAMk+AADRvgAAkr4AANm+AAAHPgAAOr8AAIw+AADLvgAA4L0AAFs+AAAXPgAAsT8AAOm+AACFvwAAKL8AANM+AAAJvwAAAz8AAOK9AAC9PgAARL8AAAQ/AAATPwAA774AACg/AACoPwAACLsAAPC+AACZvgAAFL4AAAE/AABpPwAAVr4AAOi9AAAxvwAAhr4AAHA+AACAvwAAsD4AAOQ+AAAaPwAAkr4AAEi/AAAPvgAABL8AAGG/AADDvgAAMr8AAHY/AAC/PgAA4T4AAA6/AAAvPwAAQL4AANk+AAACvwAAnz4AAFa/AACyvQAA+r0AAC+/AABQPAAAkr4AAEu+AAC8PgAAJT8AAMy9AAAkPwAAAD8AAL4+AABLPwAAQL8AACo/AABBPgAASL4AAKu9AAACPwAAGj8AALg+AAAUvwAAOr8AAMw8AACkPgAArr4AAOM+AACePgAAfD4AAMC+AAAgPwAA+z4AAC8/AABJPgAAH78AAEs+AADhPgAAe70AABC/AACTPgAAqD4AAB6/AAC2vgAAN78AACw+AADAPQAAXz8AAOi+AAArPwAA2r0AAAw+AAAdvwAAYTwAAIe+AABfvQAAvj0AAHk+AADVPgAAHLwAAOO+AAADPwAALT0AAJq+AAAvPwAAFj4AALo+AABXvwAAnr4AAIE/AACCPgAAej4AALO+AACHvQAAPz4AABC9AABgvQAAYL8AAMu+AABmvgAAkD4AAIM+AAAKvAAAur4AALq+AAD1PQAALr8AAOu9AAAlvgAACL4AAFI/AACivQAA2D4AAEk/AAB+PgAA5bsAACK/AABMPwAAEL8AAIO/AAAFPwAAPr8AABy/AAC9vgAAG78AAI2/AACcPQAAYj4AALY+AAAbvQAAZL4AAJS+AAAVPwAAyT4AAHu+AAACPgAAHb8AAGM+AAAQvwAA0L0AABW+AACvvwAAmz4AADy9AADYvgAAyL4AAK+9AADwvQAAQD4AABo9AACIPgAArj0AADq/AAA7vwAASz4AACU+AAATvQAAL78AAMS+AADevgAAmT4AAN2+AAA0vwAA2r4AAPo9AABBPwAAjb4AAAc/AACAvgAAd74AAOa+AAC7PgAAiL4AAOQ+AABkvgAAVz8AALY9AAAQPAAAG78AACA/AAALPgAA2TwAAB86AACCvgAAiL4AACg+AADOvgAAmr0AAI8/AACHvQAAEr4AAOM+AAD5vgAAMT8AAAk7AAAUvwAAkr0AALC+AACvPgAAa74AALc+AAB0vwAAX74AAJA+AAB/PgAA9TwAAL4+AADFPgAA1boAANU9AADkPgAAqD0AAKO/AABIvwAAp74AAK0+AAD+PAAApD4AAP4+AACJPgAA8b4AANQ+AAAsvwAAJL8AAGm+AACRvwAAUT8AAJQ/AADTvQAAWL4AAIi/AAByPgAAwb4AACG/AAA5vwAAxL0AABW+AADVvQAAsb4AAGa/AAArPwAAuT4AAKy+AAA3vwAADL8AAJk+AABUPgAAxT0AAAw/AACjvgAAKz8AACY9AADVvgAAWT4AAPE9AADHvgAAWT8AAFu/AAC0PQAAJz4AAMm+AABBvAAAHT8AABe/AAATvwAAUL4AAGA+AAAGPwAAgz4AABe/AAAFPwAApT4AAPU+AABbvwAAYb8AAMG+AACKvgAAXL0AABi/AAAVvwAAIz4AAI6/AAAlvwAANb0AAKE+AACkPQAAPD8AAME9AACXPgAAsj4AABW9AAA2vwAATb4AACi+AAD+vgAAiD4AAKm+AABPvwAAnL0AABO/AABCPgAAsL8AAGu/AACRvQAAST0AAA+9AAAuPwAAS74AAKm+AACtPgAAcb4AAPo9AAAOPwAAHL4AAAC+AAC/vgAAAD4AAHc+AADlPgAAiT4AAG4/AACfPAAAHjwAAEa+AAAVPwAABrwAAEK+AAAxvwAA370AAMQ+AACpvwAAF78AAP2+AADjvgAAhLwAAGi+AADwPAAA2z0AACI/AAAhvgAADj4AABK+AABKPQAATL4AABm/AAArPgAAAj8AAPu+AAArvQAALr0AAIm/AABuPwAAMj8AAA4/AADuPgAA5b4AAJ6/AABovQAAdTwAACS+AAB5vgAA7D4AALc+AAAYvQAAjb4AAIw/AADDPgAAxjwAACy/AABKvwAAsr8AABy+AADuPQAA7L0AAOu8AAC9PQAAxT4AAGe/AAAlvwAA0z4AAOQ9AABjPgAABr8AADg/AAALPQAADr4AAOy+AACTPQAAej0AAKC9AABIPwAAEj8AAK+8AACbvgAAbr4AACA+AACyvwAAET4AAPO9AACGPwAAWD4AALW9AACwPAAAob4AALG+AAAFvQAAfr8AAEs7AABVvwAAtb4AALm+AACyPgAARz8AADU/AAByvgAA470AALU8AACnPQAAED8AAK4+AAAeOwAAdj8AAOg+AABsPwAAnbwAAJg+AAA1PgAAzj0AAAG/AAAPPgAANb8AAFM+AABfPwAAFT8AACk+AAACvgAAyz4AAAC/AAAVvwAAIb8AAFc+AAAkvwAAnj4AAAU/AAB2QQAAHL0AAGU+AAAVvwAA5j0AAFg/AAA9PwAARr8AAMm8AAAgvgAA7T4AABQ+AAAkvQAAnr4AAK09AACCvQAAGj0AAIu9AAAbvwAAVz4AAGw+AADrPQAACj4AABw/AAAMPwAAVD8AALQ+AADbPgAADz0AAAK/AAASvwAAiD4AAJE9AACOPgAAkr8AANm9AABXPQAARL4AABK+AABovwAAlb0AABs/AAAWvwAAfz4AAMS+AACEPQAALz8AAG07AAAlPgAAmD4AABS9AAAaPwAAgr8AADe+AAB3vwAARr8AAM28AAAfvwAAY74AACc/AACBPgAA7D8AAIu9AACVPAAAn78AANW8AAB5vgAAZD4AAIc7AACsvgAAfb8AALs+AAC8vgAAYb8AADI+AACCvwAAq74AAIi/AAA1vgAAYb8AALU/AAAGPwAASj4AAAo+AACUPgAAxT4AAMk9AAAWvwAALT0AAFO/", "world": 2, "sampling": [6622313928416904403, 1.0, 0, 0.95, 0.0], "position": 774070, "count": 512, "stats": [[1.3828125, 82.12357175447882], [15.375, 1.000068267655997]], "full": false, "token": 510} +{"logits": "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", "world": 2, "sampling": [3276727240458267347, 0.6, 40, 0.8, 0.05], "position": 938373, "count": 48, "stats": [], "full": false, "token": 863} +{"logits": "AADxvgAAyz4AAMa9AAAOvwAAKb8AACQ+AACOvwAA9D8AADM+AAAaQAAAr78AANy/AACDvwAANb4AAE09AAABQAAAgsAAAA0/AABNwAAA578AAKa+AAATPgAALEAAAMy+AAAfQAAApz8AAKW+AAD0PwAAUb8AAJg/AAB3QAAAvj8AAOm8AACfvwAA9r4AAJW/AABpQAAA5D8AANc+AACDPwAAgUAAAKK+AAACvwAAT0AAAM4/AADxvwAAtD8AAAw+AACivgAAFL8AAEi/AADyPgAA7D8AANi/AACJPwAANMAAAKq+AAAIQAAALT4AAPO+AABKQAAAqj8AAFM/AACwPwAAFsAAAKy+AABzQAAA3j8AAGJAAADGvwAACL8AAPU/AAACQAAAb8AAALY+AACPvgAAhL8AAHU/AADTvwAASD8AAJY/AACcvwAArL0AAJzAAAA+PwAAkL4AAGvAAABuwAAADr8AAHpAAABOPQAAJMAAAGO/AACOwAAAy78AAPu/AADyvQAAAUAAAGy/AABnQAAA4b4AAHE+AAAlvwAAF8AAANG/AACXvwAALkAAABi/AABKwAAAsT4AAMU+AAAawAAAGD8AACw+AAA1wAAAzz8AAD/AAAD1vwAAiT8AAFXAAACrwAAAvb8AALM9AAA/vgAAjb8AADjAAAD+PwAA0z8AADE+AABJPwAAccAAAJg/AAAjQAAAvT8AANU/AACxPgAAeT4AAI/AAACUvwAAqj4AAP6/AABFwAAAY78AAPC/AAAVQAAAVr8AALO+AAAmQAAADMAAAP8+AABEQAAA4b8AAFi/AAD3PwAA4T8AAJO/AACJvwAAkL8AAM2/AACyPwAAG78AALq/AAC8vwAA2D8AALDAAABTvwAA9j4AAK2/AAD3vwAAgj8AAPM+AAB3QAAAob8AAFc/AAABwAAAG0AAAABAAACiPwAABkAAALo/AAByPwAAVz0AAPA/AACUPwAAFL8AAOi8AADkPgAAREAAAFI+AAAnvgAAGMAAANY/AAAcPwAA8z4AAPM+AACaPwAARcAAANW/AADSvgAA1j8AAH8/AADwPwAASz8AAKg/AACfvwAAbb8AAKS+AADRvgAAtT8AABA+AAAzPgAAKLsAADlAAABKvwAArj8AAG6/AABGPwAATD8AAJm+AADvvwAAQz8AAP8+AADAvgAAPb4AAAG/AACUQAAAQ78AADrAAABFwAAAuj8AAFjAAACXPwAAb78AAOc+AAA8PQAA4T8AAGe/AAA3vgAA2r8AAIbAAAAbQAAAMMAAAIE/AADuPwAAOT8AAOi/AABNQAAAFMAAAPk/AAAcQAAAIcAAALY/AADLvwAAkD8AAHDAAABtQAAAzj8AALs+AADgvgAATUAAAKI/AACNPwAA7b4AAJS+AAAIPgAApr8AAEs/AAB6QAAAuz8AAARAAACxPwAAokAAAP++AABLwAAArcAAAP6+AACKPwAAKEAAABjAAAAVvwAAYUAAACrAAACwvwAAK74AAM+/AADBPgAAW8AAAIK/AABhvwAAEkAAAKo/AAC4PwAA778AADFAAAAPvgAANUAAAD/AAAAJwAAAl78AAD9AAAAAwAAAUzwAAFS/AAD3PwAAOL4AAFxAAADivwAAjkAAACs+AAATwAAAzz8AALS8AADSvwAANkAAAFO/AAAPwAAARj8AANk/AADJPwAANcAAAFVAAAAZPwAApz8AAAjAAADJvwAAhb8AAPI9AACQQAAACj8AAMY+AAA6QAAAVEAAAPo/AAA8vwAATMAAAI/AAAAivwAAqr8AAOM/AACCQAAATL8AAKq+AABEPgAAbMAAAIY+AAAJQAAAvEAAACDAAAAIQAAAa0AAAO+/AAChPAAAU0AAAFG/AAAkvgAAjT8AAEpAAAC4PwAAz74AAPY/AAC0vwAADcAAAB2+AADMPQAARD8AAGk/AAAYvwAAW8AAAE7AAACnvgAAAEAAAL6/AABDvwAA1z8AAA7AAADsPwAAHb8AADS/AADTPwAAar8AABdAAADsvwAAsL8AACa/AADrPwAA0b8AAGc/AACVPwAAJkAAAFm/AACQPwAACUAAAIpAAACbPgAAnj8AACo/AADPwAAA7D4AAJS9AAANwAAA0r8AAIa+AACcPgAAAb0AAG6/AABAPgAA+j8AACu+AAAuQAAAVUAAABRAAABDvwAAa74AAKjAAADXvgAAQL8AAOa/AACbwAAAzT8AAAZAAAALvwAA0z8AAFW/AACIwAAAkb4AAIq/AAA0vwAAxr4AABvAAABfwAAA/D8AAL09AAA3PwAAH8AAALLAAABQwAAAGkAAAGw/AACLwAAA1b8AACzAAAAgvQAAZUAAAKm/AABKPwAAur4AAIC/AAAIwAAAsL8AAJc/AABwPwAAwj8AAMa/AACsvwAArD4AAJi+AADkvwAAf78AAFZAAACQvgAAmj8AAP8/AAA3vgAAcUAAADw/AADnPwAALr8AANI/AADLPQAAvD8AAIe/AACDvwAAOLwAANg/AAAhwAAArD4AABM9AAAbQAAAjsAAADfAAADmPwAAaz8AAKU/AACTPgAAZj8AAJQ/AAAEQAAAGMAAALe/AAAeQAAAHUAAAN2/AABuQAAArD8AAARAAAB0vgAAw78AAH4+AABhPQAABcAAAL4+AAB6PgAATcAAAMG/AAAEvgAAvL8AAL8/AAAiQAAAAcAAAA8/AABuPwAA7z8AAEHAAAD4vwAAVT8AABlAAAA1PwAA078AAIU/AAC0vwAAOMAAALQ+AAD4PwAAB0AAAAU/AAC0vwAAFMAAALU/AAD1vwAAHD8AAE8/AACovwAApL4AAJ69AADAPwAAoL8AADe+AAD+PgAAF8AAAGi/AAB1vwAAyj4AAA7AAABFvQAALT8AADdAAAAqPgAAS0AAAHk/AAC7PwAAq78AAIu/AACEPwAAlL8AAFNAAADePgAArEAAAIW/AACsPwAAkj8AAOY+AABnPwAAg8AAAIE+AAANQAAAIr0AACHAAAA0vwAAID4AAOw/AACfvwAAE0AAALa/AAD8PwAADT8AAJVAAACuPwAAIEAAABq/AAB2wAAAvT0AADs+AACyvwAAXL0AAG7AAAAQvwAAor8AADFAAAAPPQAAkz0AADS9AAB7wAAABcAAABhAAACHPwAAOMAAALA+AAB7vwAANj8AAMY8AACEvgAAN8AAAFu/AADzvwAAQ78AAMk/AAALPwAAYT8AAKo/AAAhwAAAPb8AAOU/AAANQAAAk8AAABLAAAAGPwAAu0AAANG/AAAPvwAALz4AAL8/AABCwAAAwr8AAJy/AADQvwAAr78AAD8/AAAQQAAAdT8AAH8+AACrvgAAzL8AAC7AAAAqwAAAAUAAAAxAAABiPwAA1j4AANW9AABAPwAAg78AAALAAAA+wAAAdT0AAAfAAAB8wAAAg78AAO+/AACzPwAAMEAAAIc/AADTPwAAdUAAACjAAAAhwAAAgLoAAEy/AADvPgAAEUAAAJO9AABiwAAAtEAAAAjAAAAlwAAADUAAAFrAAAAYwAAACz0AANK/AACYPgAAEEAAAD0+AABLvwAAjD8AACE/AAC6QAAA9r8AAChAAAAJvwAAWz8AAILAAAAPwAAAIsAAAGHAAACrvwAAQUAAAFzAAAAtQAAA4L4AAAS+AABAwAAAdD4AADBAAACtvwAAE7wAAFC+AACCvwAADcAAAAxAAAC4PwAAyL8AABBAAAD3vwAAz7sAAFvAAACDwAAAm78AAFdAAADmPQAABkAAAFxAAAAePwAA378AABQ+AACNvwAAgT8AAJq/AAAvQAAADUAAAOQ+AABBPgAAdj8AAN69AADDPgAA+j8AAK+/AABOvwAA274AAJDAAAAtQAAAIL8AAEJAAACCwAAAiMAAADu/AAALPgAA/j8AANM/AAAxQAAAMT4AAKVAAADePwAAYsAAANG/AADVvwAAab4AALS/AAAIPwAAI0AAAEfAAAClvwAAaUAAAGZAAAB1QAAAEz8AAChAAABnPwAAVsAAAKu/AAC7PwAAXT4AAO6/AAAEwAAAWj8AAPk+AACTvwAASUAAAIG/AAB4vwAAVD0AADW+AACRvgAAhMAAAJ1AAACdPwAAQD8AAJ2/AACRvgAAbz8AAKi/AADVPwAArT8AACO/AADAvwAAxD8AAJhAAAADPwAAAkAAAHW+AADRvwAAGUAAABe/AACPQAAApL8AAEXAAAAmPwAA2j8AAN8/AABnPgAAHsAAABRAAAACwAAAk0AAAI+/AADFvgAAC78AADhAAACAQAAAfT8AAIQ/AAD2vwAAAkAAAJfAAAAVQAAAcT8AANq/AADXPwAAyz8AAHhAAABcQAAAyz4AAHg/AAAiwAAA2D8AAB3AAACSvwAAOcAAAI2/AACIPwAA278AAAQ+AAAhwAAAdz8AAPA/AAA5PgAAsb0AAPC/AACyPwAAG0AAAN++AAA6vQAAdL8AACXAAABnvgAATcAAABZAAABnQAAA8D8AADI/AABqvgAAVz4AACDAAAAIPwAAPz4AAK0/AABpQAAALj8AAFtAAADvPwAAnb8AAMo/AADEPgAAsz8AALc/AACOPwAAJD0AAM4/AAAQvgAAf78AANk+AAAowAAAzz8AAAdAAAALPgAATUAAAAxAAABkPwAA1D8AABBAAAD0vgAAtj8AAOg/AAC9PwAAx74AAD1AAAB5wAAAyr4AAME+AABgQAAALsAAAADAAAAEwAAAnb4AALS/AAACwAAA9T8AAJI/AACmvAAAqL8AAAi/AABkvgAAi0AAAAfAAABxvwAAdL8AAI++AADGPwAALcAAAALAAAAYQAAAGT8AAEhAAACrvwAAP8AAAJG/AACsvwAAsT8AAMq/AAB7PgAAzD8AALM+AAAUvwAACL8AABA/AAC6PwAA+r8AACzAAABNQAAAPsAAAAy/AADavwAAKUAAABE/AAAxQAAALEAAAKW/AAD/PgAASsAAAHm/AADPPwAAgT8AAGk/AABkPgAASb8AAL0/AAD3PgAA7D0AAA1AAAAxvwAAekAAAIK+AABjQAAACz8AAEm/AABxvgAAzj8AAJk/AAC+PwAALT8AAI2+AACFvwAApb8AALu+AABuwAAAFj8AAHc+AAAFvgAA0L8AAFI9AAC4PwAAAsAAAP2/AAAUvwAAAj8AABHAAAC6PwAAe8AAAFlAAAAxwAAACMAAAJi/AAAvwAAA1L4AALm/AABBQAAAYj0AAOk/AACEPgAAGUAAAHA/AAAvwAAAM78AAFw/AABwPwAAg8AAAO2/AABPwAAARsAAAE8+AAAyPg==", "world": 2, "sampling": [2108485141294840224, 0.3, 0, 1.0, 0.0], "position": 81648, "count": 1000, "stats": [], "full": false, "token": 346} +{"logits": "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", "world": 2, "sampling": [5622335728660430884, 0.6, 1, 0.5, 0.0], "position": 541942, "count": 9, "stats": [], "full": false, "token": 534} +{"logits": "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", "world": 1, "sampling": [2627740236182225780, 0.6, 40, 0.5, 0.0], "position": 803282, "count": 48, "stats": [], "full": false, "token": 374} +{"logits": "AADgPgAAur0AADY+AABGPwAAxD8AALS+AABkPQAAuj8AAGa+AAA0PwAAPz4AAEy/AAALvwAAUD4AAKM+AABzPgAAR70AAI06AAAPvgAAXL8AAIU/AACNPwAAbj8AABG/AABBPgAAQL8AAKO+AACBPwAAFb8AABe/AADWPgAAFT8AAF2+AACTvQAAP74AAJS/AADLugAAm74AAJI+AAA0PgAAbb8AACm+AACXvgAAsz4AAGU+AACPPQAAub0AAAM+AABcPgAAvr4AAFy+AAAqvwAABb8AAEM/AAANPgAANj4AAIU8AACMvwAAi74AAB4/AACaPgAAhL4AANU+AACTvgAALT8AACO+AABMPwAArL4AACG/AAAcvQAA3T0AAKO/AACYvgAAwr4AANu+AAA2vgAABL8AAKS+AABTPQAAsL4AAEs/AAApvwAAIL8AAAO/AAA7PgAAOr8AAIo/AADqPgAAnLwAANq+AACCPQAAtb4AAAy+AABGPwAAlrwAAL0+AACBPgAALT8AAH2+AABPPwAAnz8AAJm+AADbPgAAyr0AACg+AABWvgAAh74AAHS6AABbvgAAj70AANC+AAB+vwAATD8AAOa8AACbvgAAOL8AAA8/AADVvgAA5j4AABG+AACFPwAAJ74AANY+AACxvgAAVTwAAJQ/AAAMvgAAJT4AACu/AAAUvwAApz0AALU+AACDPgAAb74AAFq9AAAAPwAAer4AAK09AACRPQAAsL4AANy+AAAtPgAAqj4AABw/AABgvgAAPT4AADI/AAAnPwAAHr8AAGC+AACyvgAAwz4AALa+AAAVPwAAhD4AACu/AAD/vgAAuT4AAII9AACUPQAAr74AACS+AACQvQAAKD4AAAI/AADpvgAAID8AAFE+AADavgAAIz8AAE++AABEPwAAQT4AAN4+AABqPgAAvz4AAGK/AAAyPgAAmT4AALs9AAAHvwAAgT8AAIY/AACyvgAAqL4AAAO/AAAlPwAAr74AAPq+AAAxvwAA+7wAAEO+AABfvwAAT74AAEc+AAAIvwAA5b0AAI0+AACiPgAAnz4AAAO/AACcPgAAfb4AANS8AACmPgAACT4AANI9AABXPgAAsb4AAGC/AAAUPwAApr4AAMw9AABjvwAAjT8AAMQ9AAA+vgAA0D4AABg+AACAvgAAUL0AAFM/AAA4PwAAs74AABs/AAA4PwAAIL4AADY/AABnvwAA3b0AABC+AAB/vgAAwD4AALI9AADAvgAA474AAMW+AABJvgAASb8AANq9AADQPgAASj4AAH+/AAA4vwAAur0AAKg+AAC7PwAAgb0AAJg+AAAyvwAAZDwAAAG/AAAVvAAAFj8AAG++AABvPwAAaj4AAHI+AAA9PwAAf74AAAQ/AADDvgAANj0AADU+AAC3vgAA+T4AADi9AAAQPwAAXj4AAHC/AAB/vgAAY74AAB2/AACePgAAHL8AAKw+AADXPgAAGb4AAKK+AACwvgAAIL8AANo9AAD6vgAAQT8AAIQ+AABWPwAAu74AAJK+AABlPgAAfD8AAKM8AABzvwAAlr4AALu+AAA2vwAAHr8AAEA+AADrPQAA678AAEs+AAAivwAA4D0AAH0/AAApvwAAYr4AAEo/AAACPgAAmj4AAIS+AAADPwAAh78AAJU9AACHvwAAjT0AAO28AABAPgAAv74AAEo+AAAfPgAAfD4AALW+AACvvgAAQb4AAMa+AABCPgAA1L0AAEs+AAC5PgAARL0AANg+AAAtPgAAkb8AAI4/AAADPwAA374AAIg/AAD5vgAAtLsAALk+AADevAAAvb0AAPI+AACVPgAAPD8AAIY+AABMvgAAJj4AAGs/AABHPQAATz8AACk/AADfvAAAT78AAKK+AAAHvwAA8D0AADm+AAAFvgAAxj4AADI/AAC/PQAAjj8AAGG/AABKPgAA0j8AAA69AACzvgAA6r4AAHc9AABqPgAAFz8AACu/AAC6vgAAA74AADE9AACVvgAAGz8AAHI9AADvvgAAEb4AALc+AAC/PgAA4T4AALQ9AAB4PgAACr8AAI29AAD+vQAA5z4AAMW/AACYvgAAmj0AACO/AAAyvwAA3j0AAB4+AAABvwAA0D4AAEA+AABdvQAA+b0AAJ6/AACHPwAAAD8AAJo+AACYvQAA3j4AAOe+AAADvwAAG74AAF8/AABWvgAAAz4AABq/AAAMPwAA6b4AAJi+AACYvQAAPL8AAN2+AAARPQAAD74AAAo/AAA7vwAAnr4AAKU+AAB6QQAAFD8AAIM+AAA6PgAA1D4AAFI/AACIvwAAtrsAAFi/AABNvwAAlr8AAIW+AACTPwAAMb0AADy+AAACvwAAq7wAAB0/AAA5vwAAgL0AAGo+AACZPgAABb8AAJ6+AAB8PQAA9b4AAJY+AAAjPgAAbj8AAJm+AADjPgAANL8AAEK+AAA+vwAAqrsAANo+AAAsPwAA4r0AAC49AABWvwAAOr4AAIo+AAD6vQAAhz4AABC/AABZvwAA+70AAIs/AAANvQAAsD4AAMa+AADrvgAAgr8AAAW+AADoPgAAzL4AAL29AAAtPwAAGj8AAAQ+AACYPgAATT8AAIe+AABRvwAAF70AAB8+AADHPgAAar8AALu+AACOvgAAh70AAG8/AAChPQAAGr4AAN6+AABxPwAAVb4AAHi/AAAZvwAA8bsAANK+AADHPAAA9r0AABI+AADVvQAAbL4AABY/AADTvgAACL8AAPY+AAATPwAAir8AAIw+AAAQPwAAFb4AAIK+AAD3vQAAiz4AACW+AAALvgAAp70AAPo9AADCPQAAED8AAA49AADXvgAAkr8AAGW/AACgPgAAC78AABi+AADcuwAA8T4AAE+/AABZPwAAD78AACq/AABDPwAAnb0AAM4+AACjvgAAzT4AAIu+AABlPgAAST4AAIu+AABGPwAAaT4AAII+AADQPgAAtb4AACi9AADhPgAAhr8AANY+AADbPgAAjLwAAKi7AAAhvwAA1D4AAEO/AACoPgAATr8AAHa9AABuvwAAtj4AAIO9AAApvgAAyb4AADa/AAAXvwAAt70AAAU+AADoPgAAgr4AAIe/AABFvgAAS78AABI/AAAjvgAApb4AAL6+AAB9PgAAjj4AAHu/AAAiPwAA974AAMy+AAChPQAApb0AABs/AADhPgAAuz4AAIC+AAA9PwAAPr8AAK2+AAAnvwAA2z4AALq9AABoPgAAQr8AACc/AACYvgAAAD8AANI+AACFvgAAcr4AAIO9AADhPgAA474AAF2/AAAsPwAAAT8AAHq/AAAjvgAAAb0AAAc+AACIvgAAlD4AAIU+AACFPgAAPz8AAOU+AABfPgAAS74AAFW+AABjPAAA0D0AAC8/AAAvPwAAHr4AAC29AADYPgAApj4AAL++AABcPwAAhj4AAOW9AADmvgAAqj4AAKI9AACyPAAAOz8AAJM+AAD5PgAAUz8AAGy+AACBvQAAGj4AANc9AABUPQAAFj8AAPO+AACBvgAAjb0AAAe+AACYvwAAyL4AAII/AAA2vgAAuD8AAJ+/AADEPQAAdb4AAJ0+AAAXPwAAUT8AAMs9AACFvgAA1D4AAEC+AAAaPgAAlL4AABm/AAAbvwAAOz8AAPA9AACWPgAAE7wAACs+AAB4PgAACj4AALC+AAAPvgAAsz4AAKi/AADmvgAAkD4AAPC9AAARvwAANr0AAOe+AACgvQAABT4AADG/AADgPgAAUj8AAMa+AABNvwAAorsAAEq+AABHvgAA/70AADA+AACDvwAAjT4AAAI/AAB8vQAAw74AAIS/AABIPgAADb4AAM4+AAD3PgAATz4AAAS/AABHPgAAbj4AAOQ+AAB1PwAA1z4AAH4/AAAjvgAAGb4AANe8AAB/PgAApDwAAEq/AAA7vwAAK74AANq+AAB7PgAAcT8AAEW9AAAhvwAAPb4AABM/AAAwvgAAhj4AABI/AAADvgAAUz8AANW+AADzvgAA+TwAAKg+AACKvwAAl7wAAFU9AAC/PgAAhb4AAK0+AAAhvwAA4b4AANO9AADcPQAAdD8AAJy9AAAzPwAABr8AALC+AAB8vgAAsT4AAAk+AAALPwAAGr4AALo8AACYvgAAuL4AAKm+AABDPwAAsb4AAMM9AAD/vQAAz70AALs+AAC0PwAAN78AAH2+AAD5vgAAtT4AAO49AACaPgAAmD0AAEq+AACrvwAArL0AAAO/AAB6PgAACT8AADS/AAApPgAAAb8AAIy/AABbvQAAHD8AAPQ+AAAFPgAAB78AADI/AACLvgAAUb8AAHe+AABIPgAAzb4AAKk9AAAfvgAACz8AAKG8AAAVvQAAT78AAPc+AAAAPwAAOD4AAPE9AABfvgAAeL4AAGg+AADfPQAAnL4AAAi/AACEvgAA3D4AAKm+AACLPwAA/zsAABO/AAAHPgAAtT8AADI/AADUvQAACL8AABg/AAD3vgAAvz0AAJA+AAAAvwAAKb4AANK+AADmvgAAjL8AADU/AADLvQAALr8AANY9AACgPAAARD4AAHm+AAAGPwAAa78AABM+AACVPwAAjz4AAA2/AACCvgAALz4AAAe/AACAvwAArb4AAFK/AACGPQAAl78AANa+AACFPwAAgTsAABY/AABLPgAAiL8AAHu+AADHvgAAkD4AAFi/AABMPgAAHr8AAPM+AABaPgAAlD4AAHg/AADovQAAgT0AAAa/AAA5vgAA9r4AADY+AADvvgAArjwAAGM/AACIvgAAsDsAAAG/AAALPwAABz8AAKY+AADKPgAAJD8AAOk8AAC9vgAA9D0AAHi/AAAIvwAAIL8AAM6+AACjPgAAhL8AAPk+AABJvwAAvT4AADW/AACUvAAALr8AAKw+AABhvQAAlb8AAAk/AABmvgAA5L0AAE+/AACcPgAAHL4AAEK/AADoPAAAH74AAOq+AADaPQAAgr8AAE67AAAOvgAAOz4AALG+AABkvgAATz4AAJY+AADcPgAAHT0AAP4+AAAkvwAAKD8AAIU/AADIvgAAGL8AAOG+AAAZPwAAND4AAHS/AABSvwAAkz4AAGq+AAAJvwAA574AAMW+AACFvwAAFL8AACe+AACQPgAAoj4AAD+/AACcPgAAKb8AAEE/AACwPgAAUj8AAKQ9AAAVvwAAuD4AALO8AACWvQAAGL8AAFo/AAArPgAAAD8AAPs+AAAxvgAAKz8AAJA9AACjPgAAr74AAMU+AAC2vgAAUT8AALy9AABjvwAATT8AAAy/AAApvgAAGT0AAOI9AADhPQAAdz4AAC0/AAAHvwAAwj0AAJE+AACiPQ==", "world": 1, "sampling": [6385203097676985184, 1.0, 0, 0.8, 0.0], "position": 405408, "count": 512, "stats": [[15.625, 1.0001836294342914]], "full": false, "token": 426} +{"logits": "AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPw==", "world": 2, "sampling": [3983594920067682244, 0.3, 0, 1.0, 0.05], "position": 355844, "count": 64, "stats": [], "full": false, "token": 57} +{"logits": "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", "world": 2, "sampling": [7645419992647707171, 1.0, 0, 0.5, 0.05], "position": 865331, "count": 512, "stats": [[10.9375, 3.6614357768307126], [15.125, 1.046149833524614]], "full": false, "token": 671} +{"logits": "AACwPwAA8D4AADw/AADxvwAAKsAAAFK/AABZvwAAOcAAAD++AACZwAAACsAAAHi/AADBPAAAdj8AACS/AACePwAAtr8AAGM/AAAAQAAAd0AAALu/AADoPwAAJ0AAAJk/AAAcwAAAe0AAABHAAAAIwAAA/b8AABG+AADmPgAA378AAEU+AACBPwAAvT0AAA2+AACrvgAAYT8AAOs+AABivwAArD8AAEHAAACRQAAAET4AAMe/AAAUvwAAEj8AAP6+AADTvwAANj8AAD++AAAVwAAAjkAAAP29AAAFvwAAC8AAAIY/AACFQAAAEL8AAJg+AACxPwAABcAAAAbAAAAYwA==", "world": 2, "sampling": [2000480645396851406, 1.3, 0, 0.99, 0.0], "position": 697307, "count": 64, "stats": [[3.921875, 3.9139929977025765], [4.53125, 3.5863108636284915]], "full": false, "token": 18} +{"logits": "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", "world": 2, "sampling": [4336471399420355200, 1.0, 20, 0.8, 0.0], "position": 570673, "count": 28, "stats": [], "full": false, "token": 660} +{"logits": "AAAaPwAAHL8AAGu/AACLPgAAk74AAAa/AAA5vwAAKr4AAHY/AAA8PwAAvb0AAFK/AABovwAAdr8AAOo+AAD8PgAAUL4AAKI7AAAsvwAAAD8AAEc9AACQPgAAeT4AAD0/AACSvgAA/b0AAIq+AADBvQAAGj4AAE++AADZvQAAgb4AACe/AABPvwAAjD4AAJO/AAB3vwAA9DwAAC4+AACFuwAAib4AAGG/AAAqvwAArD4AABO/AAC3vgAA0D0AAAs+AAA3vwAATL4AAA88AACgPgAAF78AADq+AADCPgAAMz4AAL2+AAA+vwAAir8AAAq/AADfvQAAg74AAEk+AAADvw==", "world": 2, "sampling": [3194338306841766504, 0.3, 40, 0.8, 0.05], "position": 886152, "count": 48, "stats": [], "full": false, "token": 0} +{"logits": "AAAJQAAAxsAAAJ+/AAArwAAApMAAACc/AAChQAAAqUAAABXAAADDQAAAkMAAAG/AAACUQAAAbMAAALy/AAA+PgAAEb4AAH1AAADXPwAA8cAAACJAAAALwQAAKEIAACc/AAADQAAAmkAAALTAAAAEwAAArkAAAAJAAABHQAAAncAAAF6/AAD4wAAAtUAAAD/BAACwQAAAt8AAAFjAAADoPwAACMAAAMlAAADAPwAA3z8AAMO9AADNQAAAWMAAABNAAACAQAAA7D8AAIFAAAChPwAANz8AALm/AADXQAAAkEAAALpAAACxQAAA0kAAALRAAACsPwAAkL8AAO8/AADJQA==", "world": 2, "sampling": null, "position": 474999, "count": 9, "stats": [], "full": false, "token": 22} +{"logits": "AAAmPwAAIcEAACtCAABawQAAhj8AAGnAAAAsQQAAHMEAAJlAAAAswQAACMAAAEe/AADavwAAoEAAALK9AACDwQAAJ8EAABhBAAAswQAAtEAAADnBAAAcQQAALUAAANC/AADAwAAACMEAAMfAAAAwwQAAn78AABNAAABPvgAACL8AAOjAAAAtPwAArL4AALJAAABOwQAA0UAAAKo/AABSQQAAkz4AAJhAAAAzQQAAUUEAAB1BAACmPwAACMEAAAxBAABcwQAABr4AAJhAAAAEPwAAksAAAKg/AAAywQAAEEAAAI4/AACDwAAAGcEAAELBAAAaQQAASj8AAPXAAAAewA==", "world": 2, "sampling": [5896480837471692415, 0.6, 1, 0.95, 0.0], "position": 573967, "count": 9, "stats": [], "full": false, "token": 2} +{"logits": "AACYvwAAf8EAACjBAAB9PwAABMEAAGPAAADgPwAAF0AAAMxAAAC6vwAAzUAAABrBAADxQAAAHD8AABNBAADcPgAAkMAAAPu+AAB3vwAAqb4AAFrAAACJwQAAucEAAOg/AAAEQAAAGsEAACLBAACFwQAA+78AAMG/AAByPwAAF0AAAKbBAAApQQAAOMEAADVAAABWQAAAGMAAAA/BAACMwAAAkcAAAIfBAABNwQAAvEAAADbAAAAUwQAApUAAALpAAACGwAAAmb8AABDBAAAvQQAAokAAADpBAADZwAAAD0EAAD9AAABDQQAABMAAAOlAAAA9QQAAE0EAACNBAAB/Pg==", "world": 2, "sampling": [5129484378844343179, 0.6, 40, 1.0, 0.0], "position": 455624, "count": 48, "stats": [], "full": false, "token": 53} +{"logits": "MJRfPvJ94j2poiK/cCR8vcvwwz74ZKk+wLaNPjTckj4EKMG+C/DavgXlF768jdU+zLc/v0vq/L7GAIm+jhQZP6oP1j1st/q9tVLuvgGCAb7P7D09yoBKP8ENbz1aApc9cChGP8XAfb55EaK9OfRUPgvPzr6L3Ws/aELKPZ864T4u6gs/2xp+PguhVr8/s1c9UEqFPgjfeT93Dji/U2+GvwR4/z7gwN09TMmzvp6PNb9H8RQ/ruD8Pns9fr9q5dS9kpGCPogIKL6VyAc/j3FoPFlrUT6CHJe/MTEiv1YT2726GyA/0hweP43Q5z2TKVW+qyr6PphI0DxmDcu+HP7SvQ==", "world": 2, "sampling": [8155933656193009301, 1.0, 0, 1.0, 0.0], "position": 233436, "count": 64, "stats": [], "full": false, "token": 24} +{"logits": "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", "world": 2, "sampling": [3326766725954977808, 1.0, 40, 0.5, 0.0], "position": 757416, "count": 48, "stats": [], "full": false, "token": 954} +{"logits": "AAAJwAAAQr8AAIA/AABbQAAAEL8AAA1AAACEvwAAY8AAAFS/AAAmQAAAHbwAAL6+AADQPwAAEz0AAK6/AAAlQgAA8r8AAARAAADJvwAA+D8AAAXAAAAivwAAO8AAAII/AABdvwAAsT0AAEDAAAA9vgAAozwAANy/AAB7vwAAQD8AADJAAAAfPwAAg0AAAC/AAACxvwAAB8AAAGFAAAChPgAAeb8AAGRAAABePwAAkcAAAEXAAAAWQAAAl74AAFbAAADyvwAAiz8AAC6/AADTPwAA378AABHAAABkPwAAhEAAAO0/AADWPwAAiL8AAGHAAAAwQAAAnz8AAMC+AAB6vw==", "world": 1, "sampling": [5433127353341257068, 0.3, 1, 0.99, 0.0], "position": 99581, "count": 9, "stats": [], "full": false, "token": 15} +{"logits": "AAARPwAACL8AANW+AACyvQAAmL4AAA89AABcvQAASj0AAOW+AAAuvwAAHL0AAN2+AADZPgAAML4AAM++AAD4vQAAs70AAGlBAADUvgAAmL4AAD6/AACXPAAApb4AAHQ8AAAavwAAA74AALy+AADzPgAAmT4AAH+9AABjPwAAC78AAOA+AAC6PQAAbD8AAL2/AABjPwAAMb8AACA/AAAkvwAAnz4AAIC9AABfPwAAMr8AADW/AACcPgAAlj4AAEK/AACRvQAAcj0AAKo9AACqvgAArD4AACY/AAAZvwAAMD8AAMu+AABvPQAAML4AAOk9AAAKvwAApL0AAIQ+AAAGvg==", "world": 1, "sampling": [7868724959119062256, 0.3, 0, 0.5, 0.0], "position": 914445, "count": 64, "stats": [[14.5625, 1.0]], "full": false, "token": 17} +{"logits": "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", "world": 2, "sampling": [728934788216445051, 1.3, 1, 0.8, 0.0], "position": 441140, "count": 9, "stats": [], "full": false, "token": 675} +{"logits": "fgK0QX8ECr98IQ/ApzkoweR/dUFFJ/pAulcWP5GtNsGx4rHAlSGLv3muFEBtAn++5j9GQf7mj0FZb4jBWeIDwX2vgkDxV0JAPQFVQcRgHcFnUH7ALt2YQFIOQL/CZALAnSUVv3njiz/YqApAeC4bQNyZ1b9l6fvARjgzvwNDFD+lYPg/AwO7wM8e+UBZlRPAIPYTv7rY6sADkx5APfi7wK/HgcAvJhjAd1DOQOqBAMDHfnjBytTPv+TjKcAuGHFACmO/wNXoR79Cgp1A6trywGzmhcBaogBBB9mxQLAPYsCQR7ZAVvABwAnkC8EXhJ5A4wk+wTN1qEHA+kRBDDuxwA==", "world": 2, "sampling": [7643698037345803213, 1.3, 0, 0.95, 0.0], "position": 73540, "count": 64, "stats": [[22.501216888427734, 1.0364087704572806], [21.057226181030273, 1.0013172644604391]], "full": false, "token": 0} +{"logits": "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", "world": 1, "sampling": [7888715264481348929, 1.0, 20, 0.95, 0.0], "position": 399017, "count": 28, "stats": [], "full": false, "token": 332} +{"logits": "AACHPwAA8b4AAB2/AACBvgAAvj4AAAS+AACuPQAAgr4AAFG9AAAWPgAAFr8AAKu9AACmvgAAb74AAJa+AAAwPwAAzr4AAJA/AAAIvwAATD8AAEg/AAB+PwAApD4AALG+AACCPwAAUT4AADM/AAAkPwAAhz4AAC29AABdPwAAFr4AAAk/AAC0PQAAQD8AAJm/AAAWPgAAgz8AAAA+AABBPwAALL8AAEY/AAArvwAAQr4AAH4/AABhvgAAuT4AAOs9AABuQQAADD8AAPi+AABpPwAART0AAPU+AABGvwAALL8AAAQ9AADfPgAAQL8AAEY/AADcvgAAAz8AAJc/AAApvw==", "world": 1, "sampling": [6460997834232181092, 0.7, 40, 0.99, 0.05], "position": 572256, "count": 48, "stats": [], "full": false, "token": 48} +{"logits": "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", "world": 2, "sampling": [2238233101849137407, 1.0, 0, 0.8, 0.0], "position": 305292, "count": 512, "stats": [[1.3509163856506348, 137.84486104176221], [1.2605286836624146, 152.48197813016543]], "full": true, "token": 724} +{"logits": "AABLQAAAcL8AACDAAAAGQAAACUAAAGI+AADdPwAAd0AAADM/AAAdvwAALMAAABdAAAAWwAAA2j4AAFY+AAAcQAAAmj8AAHm/AAAmPwAASb8AALa+AAAvQAAAUMAAAOO/AADcvgAAlT8AAMK/AAAhQAAArz8AAEy/AAAswAAAuD8AAGrAAAAOPgAAOD4AAOY+AAA1wAAAgcAAABlAAAASQAAAED8AAIu/AABevgAApL8AANE/AADmPwAAWr8AAMG9AAB7vQAArT4AAAPAAAC7PwAApj8AACBAAAB7PwAAVkAAAHS/AAB8vwAAij4AAD5AAAA/QAAAtD8AABpAAACmPw==", "world": 2, "sampling": [5758705804083121511, 1.3, 0, 0.99, 0.0], "position": 902419, "count": 64, "stats": [[3.859375, 4.932009869478883], [3.34375, 7.05404458481549]], "full": false, "token": 51} +{"logits": "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", "world": 2, "sampling": [2823557158306780406, 1.0, 0, 0.99, 0.0], "position": 235692, "count": 512, "stats": [[14.375, 1.077435010141136], [12.6875, 2.140759294133117]], "full": false, "token": 289} +{"logits": "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", "world": 1, "sampling": [5018778404450408796, 1.0, 20, 0.5, 0.0], "position": 14400, "count": 28, "stats": [], "full": false, "token": 466} +{"logits": "AAC2vgAAf74AAFc/AADyPgAAKr8AAN++AAAZvgAAVb4AACc9AAAbPwAAzz0AAE6/AABGvwAAPL4AAKs/AADAPgAALD4AAAS+AACyPQAAGr0AAHe+AAAhvQAARL8AAFs/AACjvQAA9b4AALw+AAAnPgAACz8AAKs+AAAcvQAAxD4AAIE8AAC/vQAA3z4AAFy/AAC8vQAAuj4AAGw9AADIPQAAxj4AAFA+AACvPgAA0T4AAPE9AADuvgAAmr4AAC6/AAAFPwAAsj4AAB0/AABNvQAAID8AAG4+AACGPgAAPr0AAIM/AACXvQAAA7wAABy/AACUvwAApz4AABG/AABQvwAAqD4AACg9AAB9PgAAaj4AAAc/AAC8PQAAfz0AANS+AABWvwAAxD4AAHU9AAA4PwAAJz8AAA2/AACWPgAAur4AANm+AAA3PwAAWT8AAI++AABKvwAAI78AAPo+AADFvQAAyz4AAAq+AADPvgAAwz4AABq+AAByPwAALb8AAFi/AABAvwAA6j4AADK/AACxvgAAlj4AADC/AACYPgAAST8AAMi+AACZPgAALj4AAOe+AACmvgAAqz0AABY/AADSvgAAj74AAP29AACCvgAAjT8AACm/AAAOPwAAHD8AAK29AAD+PgAAAz8AAJI+AAABPgAAwb4AAA6/AADwvgAAE78AAKK+AAC2vgAAAD8AAI09AAAyPgAAB74AAEC/AADiPgAAoT0AAHe9AACovgAAUT8AANc+AAD9vgAAdD8AAKU+AABUvQAA/L4AAOk+AACsvQAAQ74AAC8+AAASvAAA1r4AANS9AAAkvwAAwj0AAFK+AAAFPgAAgT8AAPo9AACnvQAAhD4AACq/AAAgPAAAwb4AAKc+AAArPwAAojwAADw/AACMPgAALT8AANQ+AADePQAACz8AAPW9AAAXvgAAqT0AABS+AAAavgAAHb8AAK++AACjPgAAfj8AABC/AAAVPgAAvr4AAOw+AAA5PwAAT74AADE+AAA/vwAAOb4AABE+AAC2PgAAVb4AABa/AAAYPwAAdz4AAEy8AACPvgAAOj4AADK/AAAhvwAA674AAJ6+AAB/PgAAnD4AAGY/AAAgPwAAzj4AAMC7AACMPgAAPT4AABs+AACOvgAAsz4AAIK8AACGPQAAqD4AALo+AAC9vgAAAj8AAL4+AADFPgAAv74AACk+AAAGvwAAJr8AACA/AABrvQAAj74AAGo+AAACvgAAYD4AAJu9AADZPgAAQz8AAFE+AAAaPwAA7b4AAKS+AAAtPwAAiz4AAAG+AADmPQAAcT4AACo/AABtvgAAUD4AAM6+AAAVPwAAUj8AANm+AAAxvgAA1b0AAII+AAD7vAAAtL4AAJS+AAAjPgAACj0AAI8+AABkvwAA7j4AAMA+AACuvQAACb4AABe+AADkPgAAF74AAJK+AABhPgAALD8AALE+AABlPAAAMDwAACq/AAAQPwAAsj0AAEG/AAArPgAALz4AAF0/AADavgAALL4AABk7AABvPgAA5D4AAJm+AACCvQAAaL4AADw/AAAyPwAAFL4AAFq+AAA5vwAAuT0AAKI9AAAevwAAkD8AAOa+AACGvwAAJT4AAJe9AADIvAAAWD8AAIi/AABmvQAAKr4AAEA/AACHvgAAEr8AACU+AABRvQAABb8AADw/AAA0vwAAVb4AABE/AAAXPwAADD8AAES/AACmuwAAAr8AAKG/AAAMvwAABT8AAKK8AACSPQAAab4AAKa+AAD2PgAAYD4AAES/AABYPgAA174AAG0+AAANvwAAAL8AAE6+AABnPgAAj78AAIY/AAAFPwAA8L4AAC4/AAB/vwAAMr4AAPO+AACNPgAAh70AAEm+AAAZvwAASr4AACA+AABqPQAACL4AAE49AADXvgAAQb8AALi+AADePgAAPz8AAGy7AAATvwAAd78AADE8AACuPgAAbL8AALW+AAD+vQAARD8AAE0/AAA9vwAAyz8AAIs+AADNvgAAVj8AAJC+AAAUvgAAlD8AABU/AACCPwAA5j0AANU9AAC1vgAASz4AAFA9AABpvwAACz4AAD6+AACIvwAAUr4AAB6/AAAtvgAAjL4AAJq+AAC8vQAAsb4AAOC+AAA2vwAAWT4AAMm+AACWPQAAqr0AAIq+AACOvwAAIr8AAIS9AAAAPgAACD8AADA+AABQPwAApj4AALk7AAAhPwAADz4AABS+AAByvgAAEL4AAEc+AADUvgAA470AALg7AACPvAAAnD4AAPQ+AAAVvwAAOr4AAFY+AAAJvQAADb8AAJu/AAA/PwAAqD4AAPk+AABjPgAAHz4AAAu+AAATPgAA3D0AAHO/AAALPwAAUz4AAHg9AAD0PgAAlD4AAEU/AADpPQAAIT8AAEs+AACWPgAA0b4AAKe+AACBvQAAUz4AADi/AAApvwAAMT8AAGY+AAC5vgAAVb4AAH4/AACHuwAA2zwAADY9AADUPgAA9L4AAI2/AACTvAAASj4AANc+AABavgAAvj4AADu+AAAVPgAAtL4AANw+AACEvgAAJr4AAA2/AAAuvwAAcb4AACG/AABNPgAAlL4AACw/AAClPQAABr4AAGa+AAAjPwAAeD4AAOg+AADXPgAAML8AABS+AADTPAAAsT4AAOG+AABZPgAANT8AABg/AAC4vgAA6L4AADc+AADePgAA5D4AAFu+AABBPwAACD8AAMy9AADVPgAAE78AAIA+AAB9PAAAzjwAAJG+AAADPwAAvD4AAPY+AAAYPwAA+T0AADW/AAD9PgAAdD4AAKm9AACEvgAATb4AACQ/AAAiPwAAyr4AAPc+AAC3vgAA5z4AAIk+AADSPgAAnr4AAP69AACLPQAAQr8AAOa+AABWvQAAcj8AACO+AACnvgAACD8AAPE+AAC/vQAAyT0AAIM+AACQvgAAbD8AAIY+AAApPwAAX70AAPS8AADrvgAAl74AAPy+AAA6PgAAMz8AABS+AAAaPwAAS74AAKA+AAAOvgAAer4AAIa+AACFvAAAUj8AADq/AABKPwAAmz4AAJC9AABUPwAA9T4AAFO+AABYPwAAC78AACU+AAB4uQAAAT8AAJA+AADNOwAAZb4AADG+AACOvgAAmz4AAGM+AAB2vQAAK78AAMQ/AAByvgAAUL4AACY/AAClvQAAIb8AAIU9AABXPgAA4z4AAJ8+AAACvwAANj8AAOa+AAAEvwAA0T4AAH6+AABaPgAAlz4AAEy/AAArvgAAnD4AABc/AAAkPgAANz4AALu9AABTvwAACT8AAAK/AAAnPwAAhb8AALk9AAAEvwAArz4AACG/AAD7PgAAUD8AAO69AACdugAAQj4AAAW/AAC1PgAAK78AANG9AABXvwAAqr0AAHC9AABmvQAAHL8AAPQ8AAAmvwAAtr4AAG6+AABDvgAAFDsAADo/AACBvgAAGr0AAKo+AAB2uwAAgL8AANy+AABDPwAAgL4AADA/AADevQAAFLwAAHC+AACnPgAAQ78AAI2/AAAUvgAAtr4AAAy9AAA/PgAAtb4AALo8AAAYvwAAAD8AAAa/AACtPwAAsDsAAHW+AACJvgAAkL4AAKs+AABUvgAAl74AADC+AABgPQAATz8AAL4/AAClvgAASr4AACM8AAAtPwAADb8AAEc7AABlvgAAdL4AAHU+AABhPgAAQj8AAAC/AACjvQAAnL4AAEK/AACsPgAA/r4AADk/AAAzvgAAET4AAAk/AAA1PwAAe7wAAGQ/AAD4PgAA+r4AAPI+AAAkvwAAor0AAPi+AACqPQAAq74AAAU+AAAhvgAAvT4AAJK/AAANPwAAET8AAAq/AADOPQAAdj4AAAY+AACbPgAA/z0AAKU+AAB1vgAA174AAIu+AAAGvwAAkT8AAEO/AABgPwAART4AAKe+AAAWvwAAL74AALG+AAAJvwAAtb4AABU+AAATvwAAgj8AAPY+AAC0vQAAhT4AADy/AAAcPwAADj4AAAQ/AACZvgAAcr4AALs9AABMvwAAAz8AABO/AABPvgAAMT8AAKy7AABNPwAAHLwAAOu9AAASPwAAET8AABe/AAD7vgAAgz4AAE4+AABEPwAASD4AAJu9AABUvwAAl74AACU9AAC2vgAAkz8AANE+AAAsvgAAQzwAAEi+AAAmvwAArL4AAJa+AABlvwAAaj0AAGE+AAAuvwAAK74AADi/AADYvgAAED4AANa+AABrvgAAiD8AAPG+AAC1vQAAKD4AAIw+AABcvwAAZz4AAL4+AAAVPgAAoD4AAOU+AACKvgAADj4AAAE+AAA4PwAAJT8AAIg/AAAsvwAAjb8AAP4+AAAgPgAAz74AAMk+AADKPgAA6D4AACg+AACBPgAABrwAAE6/AAAXvgAAZD8AAIu+AACFvwAAHr8AAFE/AAAUPwAAtL4AAJk+AAAWPwAAAL8AACa/AABzvgAArL8AAMU9AADQvgAAzT4AANs/AACbPQAAUDsAAHo+AADOPgAAQD8AADo/AADkvQAAoj4AAK89AAAQvgAAFL8AAN++AAAgvwAAY70AALQ+AAAdvgAAiT4AAC+/AACevgAAlD0AABC/AABBvgAADb8AAJa+AADMvQAAHz8AAP2+AAA0PgAAy74AAGM8AAAxvwAAVr4AAOc+AACevgAAH78AAEa/AAAbvwAADb8AABA/AAAGPwAAL70AADq9AAAhvgAA97wAAIW/AAAOvgAAlT4AACE/AAADvgAAkroAANi7AACjvwAAnD4AAAw/AAAqvwAAwj4AAKm+AAC6PgAATz8AAFO+AACWPgAA6L4AALm+AAATvwAA8z4AAEW+AACjvgAABz8AAJa/AABCvwAA3z4AANa+AABqvgAA8b0AADO/AACMPgAAUb8AACE+AACtPgAABr8AAGC+AAAyvAAAJr0AAEG/AACJPgAAlD0AAIG+AACivQAAGz4AAGW+AAAFvwAAWLwAAF+/AAAAvQAAdD4AAIm+AADBPQAAyz4AAI29AADTPQAA3z4AAL4+AADzvgAAvD4AAPU+AAAEPwAAUT8AAJe9AABVvgAAHT4AAIg/AAABPwAACDsAANu+AAD8vAAAZD4AAAy+AADWvgAA0L0AAPU+AAA7PwAA4D0AAHg8AACdvQAART8AAK0+AAAGPwAADj8AABg/AACMPwAA6L4AAHc+AADivgAAmb4AAHM+AACcvgAAA78AAHi+AAD3PAAADL8AAFq+AAABvwAAhz0AAOI+AAC1PgAAez4AAJ8+AADhPgAAUb8AAOe+AAC3vgAA3j4AAIU+AAAZPgAAgT0AALw9AAD7vgAA074AAA+/AAACPw==", "world": 1, "sampling": [4795828537081568439, 0.7, 0, 0.5, 0.0], "position": 95581, "count": 512, "stats": [[1.7109375, 111.55573025023642]], "full": false, "token": 762} +{"logits": "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", "world": 1, "sampling": [7643606086582808783, 0.6, 0, 0.8, 0.0], "position": 957339, "count": 512, "stats": [[2.234375, 34.38224937592227]], "full": false, "token": 774} +{"logits": "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", "world": 1, "sampling": [2579397671769820717, 0.3, 20, 0.99, 0.0], "position": 897483, "count": 28, "stats": [], "full": false, "token": 213} +{"logits": "AAD7vwAAXb4AAD8+AAAxvQAAtb4AANk/AABFvwAA4D4AACy+AAAXvwAAbL8AALnAAAAhwAAAjD8AAGM/AACYPwAAnb8AAJQ/AADMvQAAsz4AAOc+AAD4vgAAEsAAAJw/AABiwAAAVD8AAEZAAACCPwAANL4AADS/AABsQAAAHD4AADi+AAAjQAAADD8AAIk/AACEQAAAdMAAAEE/AAC2vwAAXj8AAIg/AAAVPgAAWT8AANg/AACEPwAAqL8AAHo+AABIPAAAVb8AAL2/AAB9PwAAi78AAMY+AACUPwAARkAAAAJAAADdPwAAFD8AAIw/AAAuwAAAkz8AAIw/AACaPw==", "world": 2, "sampling": null, "position": 267656, "count": 9, "stats": [], "full": false, "token": 36} +{"logits": "uJJtwAIhesB9h5nA2Rc/QMgkLz8RJEW/WLJzP0Z8hr6HgZ7AGs6zP8hOQz98DAdALJk5QE943z9aMy5AmRK8wNEGBL6QjkfAe2Ckv1Y/jL/x7ujAVMR/PlY0AUAK1Ne/9WZZP3Ii7z+nUku/CJGXwJP0ED+THITAvwgZwICrV8CrQW7ANh+bP1Sw0r7n+UFACAdhwBZvAj+v5mPAFjObwHyaA8CIWjlAl4IkQP1trr+srYDAuyQ3v7Rw174LZJvA3PZXQdnVlUD577bAC4iHv1J3LMFwTJDAQbzsQGxtNUDyL1zAhb9PP300XL9rn+HAyCYCPwD0iEDdJg9BkzuAPw==", "world": 1, "sampling": [2724200561806884066, 1.0, 0, 0.99, 0.0], "position": 963635, "count": 64, "stats": [[13.49776840209961, 1.013303711704592]], "full": false, "token": 48} +{"logits": "TgvlP55VVr/hFR1AuZCmvUe4qb8mKCY/eCODP9o14D93+f8/UseMvwwzOT/TL/k/pZcIQGov3b+yYcI//9njvxTQtz9pFKM/zPcZv5LPXkA7Nro/Q7BhwPvjhj9mKkm+7TZCQAqRFECLbga/t4YYP5fMqD75bv2/xNP9PswFpT5Bwi6/tHjkPmgDhz6gtnQ9tq55P98phb+wgia/Lqslv9DDQ73CMlu/DbCWv1fA4T4dlvs/SFKNQI8tCcAv/E9AqDxrvywQHkA2OOI+gVReQAO7uL8TelY/12u8QCj3e7+1yZC/pP/Kv2fvuL/GrhRAUiGlP3hs/L/gaAc/8wvNPxFp6z/DxTO/82wgwHDzZL8yM4fA+7KoPs8nVD8vjeu/U9QSv0mKBkDvY5E/H0kkPxkxqz/99xnA9WS+P8C2HT/5YwS/6EqPvbHv6Twtgd0/lbawv8PoxD4hE9Y++mQqP6TELb7/2S6/pBUCvxGbiL59RLK/YhwBP5NmKED8t5dAj4lpv2Yu174jlwNAygAmwJCGiT8iH5rAvfVYQGmhfUA2Coe/SEQzv8Vmyj94eSRA1vlxvvEMgsCfqVW+VQISQDfbZr+ofFc/FK2qvzHOmr/B/VI/6kbWv/uMhj9hnJs/96eGQKZVf8AP6FA/ZTdewIsEY8Djc07A8Bu4PyNbuD9PrZS/59J+QKhDgj9TUkPA+XP3P3PUBEBk6/c/T2l+PZXRC8BStUE/JMy7v0AQaMDcoH9AY22EwFGVlT58bA5AyeIjPlHzMsAWpBA/75Cjv8MANkBIKv4/yHOCwBmDmcCcvwTA04EdQNthyj/GIMY+ewrRv4EdI8Cut+c/A5ECwPxjQb+3bCa/U4h9Pucmlr8m6CVAELQQQLP+H0BbedO/VSW1vWfsvj9mYaY+7a9Lv1snQcBeIcG/xUIUwFW+AMCql+W/gK8iv4peyD+tOkxA2qh8vWcRN8DqsBc/BXf1P8L+iD8g9x+/KBOuv+pxnr9yvqE/hG+yv7p54L/3BYI//6JMv8xBnj4NW3DAOzaKvszY5r4Z7P6+vrA+wIRlKEBGuWPAcbqAv6lMQLuBEiJAVtBLQLfNjT+E3gM/hV/bP+NRz7546MXAH/cYv8411b6Usuc/OrzdPgmufb+2vra/lo0bQKdVsr9IFx3A7hzUPzvH6L7YJB/AcucgQIfYaz/Oi0/AZ12Nv0bWs75+bSe9YLYdwL6HgkCJO4k/83OHQFL0CT4vWyq/hRONv7JVDUAZJ4S+I48KPvwGRj6lsmfAmAB8PgpYlr/wy0bAtkehvn6F/z85o1lAqgF5wADR0L+kfLu/AG0pwKJzfL4iV6W/TmUdvxAnvT/a3Ke+BBRiwOFghz9iCS2/2zN1v8JcHr/ZXKS/BcbNQHcnhj9tdpc9wW8UwM7swryp4/2+ZYthwGTr+L0fuDpAxhORP46DKz8xj0k/bRLqPzML3T8HFQ1AXbUrv78Ibr8WhFE/Uy6Ev3erJsBqnpg/vlUovSPsCT9Iz04/wbN7P0lw179XRbS/IjPePayB7z9HGoHAKfqMP65RvT5Adx6+NLK4P9sxPL85sZ1AronYvssDhb9vJhe/qj8WwDnoFL6hdT3ANYVAQMcIOD9qGvg/DewgPqh2HkBtNmk/Wd/BPyAFK0BZQH0/ihgNv1sFjUC4BCxA1Yhiv3zqeb7My7E/EHl1Pyb1Dj/g982/IawyP+rdS8AUehw+hlaDv8pFsD709DzADmK5v7hCVD4RVbO/9NQLvGMB1D2cjhPAxf9QQITKj7/ptQ7A5bqhP1IJP0BLANc/Qs+rvuIbyb3ekB4/AJ/2vunoMD98Q88/4Gl+v9eANj/TBopAQqAvwBA77z9aPGBACu25vd2Exz8Tma2+mabxP08Upj+YSxlAW6QyP4pdFUD4ljC/f4g9v0W+4r+dw3fAjNXePzElEcDEgcC/bXG4PzEntD8aIs+/zS88Px5ZPcAXpna/p0xJPw/DNUCIeyVA1y43wGCX1z96W2E/5Xw3QHK7gj8mWn7AtPCUv61Fz75wbI4//1TnPliSmL+jHL/Ae3yxvqpZWEAy71I/S0lmQNWngr80RkTABS8AwPNqpz9v64vAY7d1v24ceED+3DpA1J5Uv/eoIT/+q9U/L52qv4aEVMDL2vo/zkDZPyxfuT5SjFDAeveAPS0dnL8O0xQ/Y+gGwIuZ07/A2DO/8n24vxrcJT/GQj5AP7SJv+xXFz+soABAdcqhvh09O0D+axXAIGMRv3rRYL/lwAtAjgqhPtptx788P2VA25yfP9fJWcAerQo/Q7fSv7ZhT8DpIcu9e35OwF8JcT+S3f8+FhymPzL1or8fI92/wUFPQM3y778BtUdAPX9uPy3rHr+1xjXAXHt3viyKpr5z4g6/FRnWP7THJr9QHuY/5szNPSunTMCqW0I/r9i/vvLjSEDZ5mE/yoM4wMPdJ0C44RBA3KnVvzmeRUCziE+/oCdrv71uyT+z3+K/dDyGQBR2A7+TYj4/huG0v/m0RD4I7zPAXkQIQBgj/r75E+w+iJw6PyjtbL9eJKI/3RYyPrnjAb+RGVu/t3R3QHeaRkCWHks9k7cFv0KPgUDDknW/7txMQP63akAU8/E/1/l/PwbjBsDSwA3AhQ1XvwD7AkASnkrAOtklwFgjsj/M12Y+wuUbwOCPe0BL0AW/VfdPwGXnLsDSzF+/5BtKv9DLBUDBAvq/bWurPj+/3T6vXD+/wdwZPl9eA7/iWzNAtdMrwDP8YcCia6+/VAZzvwLqtT9THgFAFqUSwIm7DMAhzxTAF++tv4vkFkBxKG7ArZyUv6ZebT9ykYc/XFEqwHcmx78yeNC+q/u3P7Qcqb+KjhHA8zULQMyybkBb3VJA1D0xwIOsYsB0HEvAtTrWP0cIF7+hT7A/8awzv7QnKMDUoro/lxBRwLL4nL8o2pG+oc+MP54c+L4sQoa/+f/+P4PjPD8bDEXABlw1vwL6l0A62+K/NDTkP0vtcL9CPba/Wk4xvzBPdj6/UYnABcLyv/TDH0ACuSG/phKAP/JVH8AHfx5AerBBvrq0CT42n9O8AKRAQFlOF0I12OU/Wyq1P8nx2j6I8ZtAPpPZP3huWD+aM3g+JQO8v9K1dL4xvzW+JmvLvyen7r8g2Lo/2zk7PyJimD8ZLXc9vVuwP+tIQcD9o8e/eKs0QBM3y74f//E/jsgWQCDZB0AaCbY/n6pfQAYpEUBrIeq9ry/zP9hTScCMA9Y/GTPNvXNjiL9X5MA9NFrsPlySzj3YHvu/YBy7PyDfkEAUoYm/ijyfPxdg8L+tZZa9GmPnv2SgAMA0JSjA3AC4vww5f79OvyG/d7OawNLr1L/7984/fJxmwOzpgj97GZHAFq1FwMzZ2D+oYZ++tInsP3u9xD9s22I/YQNGP8ngRL/POGg/xqqZvw9+H7+O7zrAOUSaPnV0g7+EqjC/c1Tyv+efC78mJso/2gyKPziuXsAxte8/HPkbPx5/w78jVhs/XvRdPga/nD/WmOq/D808P3zogT9yl2JA6AaUwOU4HEB5jwZAETvdv3S6wrt8KOo/yRScvwnGzT+/TU+/nJpPP987u7/n4mw+NMLjv6rRvD7iRNe/2Datvbzftz+/ODy/gE6kPzvIE8Dt6aDAKBmRP9kg1r4hmOU9wMWPPWw8NL9VhZfAuKnKv3/OEsBC1T1ANFJlwD7Gwj127XDAJ3WDwFFubD9KzxFAjUxJv/sy778VPXpAAiyCP9zesz7cYRXAFSp3P5DXjT9MwJrAz4auP2O3bMDUCYq/cV9JwPo0NT6+aD1AX9ACQLVFcD7T+Ea/zth8P2fCTcDU/wdA6ulEvhwxEz+ft5I+H6WCwB8Nrb/8Vqu/4BQEP+HpesCbiI5A8qpDQDBbDEAxAEJAkuR8QI924T1RD729Un5fP2LxwD+BcjdAtP6mwKpnBD6Z4Li/9YXDPzneK77URjjAAuUpP9/0tL+uoB6/yibOvimk6b/g3IS+WoIbv/fAF0CvvwHANJ7av/xHcb+AkE49kdUQQALc5757mHM/5YPVPwB/CD9IIxa/tu+JvzwZLkBNDNo/hraLPxGsTkAtd9y/ahQVQC4Qg75SV1HAeNHrv2wuyz4PByZAhvSFwCNRDkD8C/e+nfI2PmiOhj+czkPAhRb9PppoEb9lLqS84BWPQLwIFz9MP5q+Y6FlPtw9kEC8u0ZAjIHzP70EhD5JLRg+RhwVwEy8MsAtvidA3kPLPxQJlz4eakC9OnQIwIdvA0Bpvjc/yEIvP/A+HMD1E3NAfhYZvzTU0L+3MoG+HKAgv3N8AEA3UL8+uGz6vxviRcD4UHLASHvlv4QdDT/rThzAj8OVPQJOTMD9aue9+KI2v0PIEcDCuoM/FAIZQFIJAb/eQdS+PnCBv/OpVz7QPYs/f3ryP3adQ74DPYtAZpDEQGxco0C+doLA/n4twAtsqr+y7m8+wfcEQLysX7/vG8u//GVtv0J2CsCEEPG/C7GGPdZizMDcYIJAoxh1P5iThz/hQzRAY72av4/U8L72ls4+qgDXv35+sT0i1IU+sLtvP4hKEUDGk6E/0XWEv0IKB8AvAZu/I5StP2CmjcDgFw8/gf4SwAvxvb98kpy+v+BDwDCecT7PKom926KNvFYt8r+iYZJA5NkVPjUCFECpK7A/vAYnwDhhSz8XdA1AccPtv+la0z8mnUlAcfKRv+z83b70Waq/pv+Uv5jVG0A1Wfe/799XwGCWqL/hi3dAsdmjvstqDT9Fpb4/gceKP1KI+j8UCGdAHb4NvI3ydT8vBA9ATssVwOyzWEAL6Oa/+FB9QKyTub9FTrs/BTz9v3rHu74EKDm/c80Ev8v40T5YtZK/qiS5v+gJMUAHnv2/g2ZTwJP3XkDAQ41AewJ+vx9B1j8yOAO+fvLkP0nryz7tD8a/aN+0P3ENn0Boc8W/1dE/QOZygsDxYDi/sEwOQC4cLUD124y/KdsEQAZW+D6XwdE/mP8iQDtsCcCx//a+E9NSwH4/WEA5Nim/9nryvz6WgsA18bK8v5MgwMWFgr8RPyxAvGoeP9EhlT3YIS9AXyNjQH6alT0oXcy/4YzIv5cflr5pXN0/+HIaQA8JXsBF9ms+ey13P/sd6D8wnoVA/XapP46XjL4gBPu/S/JGwNP+HkAHGsu/T+aBv0oT3z/Llag/+C4VP19SBkDBdxrAUPzNPb2XqD9gcAA+1acTwHzFXcCyI6A/tOdxP7KH1b8oVfG/PUGSP3kbgj/j8s6/2Za8v0BXSbw7ewbAtsN6P8QpL8DmVlrA2L4yQAuwY8AOj6C/xXpxP1Y4FMAjD4xAAlMzvw==", "world": 1, "sampling": [2815629182552080625, 1.3, 0, 0.5, 0.0], "position": 65875, "count": 512, "stats": [[37.82651138305664, 1.0000000007778023]], "full": false, "token": 577} +{"logits": "M+pTPnRtLr8zBmq+w4Qsv7bbNj9Dns6+jp0cPt9ynL57BI0+i8SrPa8rLj45kZU/wGbDvlWi5T3tq7s9nxpmP6Klxb62Jh0/9EaePiuNmr02HUA/9qzUuwDJoT03gTc+8Ebtvsva470SZ8u9QqctvjNklLspGao+VASgPkXrxr11w789JfxtQT2oCb+lfG69EdBJPePUd740XJm/wLFcP0S3gb4F+cO+cMYKv1Bsaj6kiZC+TPHzvkD42b6CP3s+6BURv0PYnb5iHMi8kH6pPcGZa76G1o++CWd0vm7LiD66Ox0/vouYvhimOT5TcqE+c3CEul/gQz57kyy/Mib/Pg==", "world": 2, "sampling": [5546898806231359481, 1.0, 0, 0.8, 0.0], "position": 218977, "count": 64, "stats": [[1.1684943437576294, 11.799017394031486], [14.874058723449707, 1.0000105015945941]], "full": false, "token": 33} +{"logits": "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", "world": 2, "sampling": [1251444901632250865, 1.0, 0, 1.0, 0.0], "position": 307485, "count": 1000, "stats": [], "full": false, "token": 660} +{"logits": "AAAywAAAMrsAAJjAAAB8wAAAoD4AAAQ/AABywAAAYkAAABBBAACGQAAA5sAAAILAAAAyvwAANL8AACjAAABYwAAAMEAAANHAAAD7vgAAA0AAAMvAAABjPgAAXUAAAFXAAACFQAAArj8AAPy+AAClvgAAdb8AAD6/AAA6QAAAyEAAANPAAABmPgAAqEAAAJrAAADlvgAAcD8AAFbAAACBPgAArMAAAJRAAADaPwAA5sAAAOXAAACGPgAA6UAAADDAAAAJQQAASUAAAIJAAADfQAAAAUEAANC/AAA6QAAAi0AAAKK/AAC9wAAArD4AALrAAADdvwAAjcAAAIpAAABOwA==", "world": 2, "sampling": [5502536033504622170, 0.3, 0, 0.5, 0.0], "position": 839737, "count": 64, "stats": [[9.0, 1.0001046932953028], [8.5625, 1.2077951532162297]], "full": false, "token": 8} +{"logits": "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", "world": 1, "sampling": [5537853181833000174, 0.6, 20, 1.0, 0.0], "position": 1005974, "count": 28, "stats": [], "full": false, "token": 624} +{"logits": "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", "world": 2, "sampling": [351744546297901401, 0.3, 20, 0.5, 0.0], "position": 765823, "count": 28, "stats": [], "full": false, "token": 11} +{"logits": "AACWuwAAwUAAAMVAAACrwAAAC8EAAAO/AACTwAAACcAAAHXAAAC3PQAAisAAAJvAAAAWQAAAl8AAACJAAAAhvwAAnMAAAOK/AAA2wQAA1cAAAFw+AABWQAAAl8AAAMG/AAAPQAAAHEEAAPi/AABjwAAAtz4AAIq/AAAUQAAAA0AAAPrAAAAcQQAAqj8AAEjAAABqvwAAgUAAAE7AAACiQAAAhkAAAGFAAAC3QAAAd0AAAKa/AABaPwAAO0AAACjAAAAVwAAAqz8AAAk/AABlvwAAl8AAAEdAAAC8wAAA3T8AACDAAABCQAAA8b8AAIZAAAB6QAAAEUAAAC1AAADIvw==", "world": 2, "sampling": [6263511016085360065, 0.3, 0, 0.5, 0.0], "position": 893772, "count": 64, "stats": [[9.75, 1.0000104099687248], [9.75, 1.0000016541249022]], "full": false, "token": 33} +{"logits": "BzVXQLMF68DpQuo/+jPdPxj2CL4AeqvAPxLKv+CeikB8NeW/b7upwM+qDkBhrnfAWGBgQCK8JsHKbI0/VcuRwEcXqkDXSaLAjpvOQEO3lD/D79A+Hg+Ov+y+nz/TZ/2/S8HKvsfXW0A2Nr5Aq4H0v8y31T8NhaDA2xZhwBlfpL/9NuXABQmFQIISF0CdgzHAT5hLQC6KB0Ep2grAkAwYv9r4ZMDDB4dAxl6RwCLeuL8q5da/PaNcwMQaGsCVw62/GnFFvqlra8DuxldAPQykwMi1Hj8KE4rAHGavQIoELD85mAJBzsNywEfU8MD79dW/vsmCP2LRxz/AiS3A74vZPw==", "world": 1, "sampling": null, "position": 267208, "count": 9, "stats": [], "full": false, "token": 37} +{"logits": "AAA8wAAARsEAAKhAAACNQQAAwEAAAEbBAABfQQAAB0AAAAjBAADiQAAAtEAAAOA+AADivwAADEAAAGNAAAD9wAAAk0EAALPBAACcPAAAyUAAAJm/AAA8QAAABcEAAP4/AABXPwAA3z4AAKhAAABEPwAAbkAAAE3AAAARwQAAB8EAAA5BAAAWQQAAKj0AAGZAAACswAAAucAAACTAAAAbwAAASsEAAF3AAAA7PwAAKMAAABjBAAA3wAAAZkEAAEXBAAC7QAAACsEAAE3BAABrPwAAdL8AALK+AAA9vwAAFMEAACNBAACnwAAA/MAAAP1AAACIQAAAOsEAAP/AAAB0wA==", "world": 2, "sampling": [5436420925822711171, 0.6, 20, 0.99, 0.0], "position": 430987, "count": 28, "stats": [], "full": false, "token": 16} +{"logits": "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", "world": 2, "sampling": [807967184645909430, 1.3, 0, 0.8, 0.0], "position": 757892, "count": 512, "stats": [[12.5, 2.533578366547059], [11.125, 2.5994292997642514]], "full": false, "token": 82} +{"logits": "AADLvwAAgr4AAE2+AADQPQAAwr8AAO09AAC3PQAAA8AAALM/AACoPwAABcAAAG7AAACHvwAAcEAAAA3AAADdvgAA8z4AAAVAAAA1wAAAA8AAABPAAACNPwAAOb8AAPy/AABHwAAA3j8AAANAAACyvwAA378AAMrAAAA1QAAA5D8AAJi/AADivwAAPT0AAAPAAACivgAAFb4AAIW9AAC1PQAAW78AAKY/AACfvgAAkEAAAG2/AADUPgAASj8AAIc/AACqvwAAi78AALg/AABrPwAARkAAAA+/AACVvwAAE0AAAAM/AACRPgAA4T8AAPG/AAAHvwAAFEAAADXAAAC5Pw==", "world": 1, "sampling": [3579970593629625229, 0.6, 0, 0.95, 0.0], "position": 6651, "count": 64, "stats": [[4.5, 1.6120257993051061]], "full": false, "token": 43} +{"logits": "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", "world": 1, "sampling": [2226850938819503005, 1.0, 40, 0.95, 0.0], "position": 558466, "count": 48, "stats": [], "full": false, "token": 11} +{"logits": "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", "world": 1, "sampling": [7593264999108242879, 0.7, 0, 0.95, 0.0], "position": 445027, "count": 512, "stats": [[1.4453125, 157.88736183651196]], "full": true, "token": 14} +{"logits": "AAAtvgAAGb8AACQ/AADMuwAAHkIAAM49AACjvgAADb8AAKO/AAC2PQAA3z0AACg/AAAdvwAApb4AANU+AADFvgAAAL4AAPi+AAA3PwAAIj8AADI/AAC4PgAAsL4AADA/AAAQvwAAzj4AALU+AACkvgAAsj4AAP4+AAAlvwAAtr8AAMi+AAC4PgAAoD0AAC0/AABRPgAAJj8AAJC+AADivgAArj4AAAE/AAAtOQAACL8AADY/AABovwAAUT8AACS/AABTvgAAOrwAAAo/AABSPwAAbb8AAPQ+AACOvgAA7r0AANk+AADHvgAA9L0AAKA+AAALvwAAtD4AANA+AAAfvw==", "world": 2, "sampling": [6013298978724209702, 0.7, 20, 0.8, 0.05], "position": 167619, "count": 28, "stats": [], "full": false, "token": 4} +{"logits": "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", "world": 1, "sampling": [1595588067874164893, 0.3, 40, 0.8, 0.05], "position": 559396, "count": 48, "stats": [], "full": false, "token": 91} +{"logits": "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", "world": 1, "sampling": null, "position": 672532, "count": 9, "stats": [], "full": false, "token": 19} +{"logits": "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", "world": 2, "sampling": [609062151086132905, 1.0, 0, 0.5, 0.05], "position": 373389, "count": 512, "stats": [[1.515625, 124.40715146732799], [1.8203125, 90.86554480692185]], "full": false, "token": 317} +{"logits": "AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/", "world": 2, "sampling": [5411272736871381790, 1.3, 0, 0.8, 0.0], "position": 72093, "count": 512, "stats": [[1.5, 300.0], [1.5, 300.0]], "full": true, "token": 169} +{"logits": "AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPw==", "world": 2, "sampling": [6930172116737199808, 1.0, 40, 0.8, 0.0], "position": 194903, "count": 48, "stats": [], "full": false, "token": 26} +{"logits": "AAAjQAAAj8AAAHPAAACYwAAAy8AAAD5BAABuwAAA7MAAAGvAAADSPwAAP0EAABY/AACZPgAApkAAAChBAADkvgAAwsAAAKXAAAA5wQAAH0EAAApBAACPvwAAj8AAAANBAABHwAAAK8EAAFg/AACGQQAA1j8AABvBAAD/QAAAm8AAABnAAADGQAAAPcEAALJBAABOQAAAMUEAALHAAACMQAAABEEAAC/AAACMQAAAZ0EAAKRAAACCPwAATkAAAJBAAADkwAAAHEAAAA9BAABCwQAAvEAAAFDAAABnQAAATsEAABfAAAALwAAAMT8AAAxBAAAyPwAAbsEAAOzAAACpvA==", "world": 2, "sampling": [955431498076510889, 1.0, 1, 0.95, 0.0], "position": 814075, "count": 9, "stats": [], "full": false, "token": 35} +{"logits": "hd77v23+ej735lc+CYCVP9KhAD/3btI/SaBNv3X51r9AK4LAGLwVwCuazb+OHSvATNEvvxsbIj6n1eq9AoTRP8ZjVsD5Um6/JBRjP6EzPb9fqE7Akheuv0aPIEJ8mqc+m9z7Pvk3zL+ZKYe+WsMGwGwhjT/h6RZACvkOv3cnNsD0SuQ/EePUv87rF8DY9VHAK3srP1YNbj+6b6k/cjEUv47tJcCK+h5ApuOkPLbU377pvEdAdDfaPhOhn7+VbEFAKqSbv2d9GEDKTCZA6Eofv5cxFT75jwVA1m9UP96RX8D1iXM+Px/nPjQ65T6vDNA/YTqVP11uCcC1bFQ/b98FQA==", "world": 1, "sampling": [3767648873011594051, 1.0, 0, 0.95, 0.0], "position": 328662, "count": 64, "stats": [[40.139915466308594, 1.0000000000000007]], "full": false, "token": 22} +{"logits": "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", "world": 1, "sampling": [4301940593855989694, 1.0, 20, 0.95, 0.0], "position": 308519, "count": 28, "stats": [], "full": false, "token": 301} +{"logits": "AAACQAAAXUAAAMvAAAA+QAAAGkEAAIpAAAB7wAAAF0EAALK/AAAIQQAARMAAALVAAACwvQAAAUEAAGXBAAATwQAAUUEAAJzAAACfQAAA6EAAACfBAAAiwQAAHL8AALnAAAAMwgAABMEAAIHAAAAVQQAAvMAAAI0/AACaQAAAI0EAAMXAAAAMQAAAKUAAAElAAAB7wAAAVkEAANdAAABtwQAADsAAAKfAAABmwAAA8UAAABtBAABfQAAAhT8AALVAAACpQAAAgMAAACBBAADzvgAA0EAAADdBAACVvwAALcEAAIZAAAA7QgAAgMAAAGPBAADUwAAAVMEAAJxAAACxQA==", "world": 1, "sampling": [6257672678104799005, 0.7, 0, 0.95, 0.0], "position": 337274, "count": 64, "stats": [[46.75, 1.0]], "full": false, "token": 57} +{"logits": "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", "world": 2, "sampling": [1683239117713480859, 0.3, 0, 0.5, 0.0], "position": 105896, "count": 512, "stats": [[6.0, 1.4201463843889328], [38.5, 1.0]], "full": false, "token": 581} +{"logits": "AADwvgAAuz0AAIG/AAAoPQAAFL8AAG2+AAByPgAATT0AAB8/AABivgAAUb8AAA+8AADTPgAAML8AAHBBAABhPgAAHD4AACQ/AACFvQAAjT4AAN6+AAC1vAAAOb8AADC/AAC+vAAAuj4AAOu9AAAdPQAA574AAA8/AACyvgAAvz4AAIM+AAAzvgAAW78AAMI+AAB5vwAAgb4AAJU+AAACPAAAcT8AAIg/AAAaPwAAmD8AABk/AACDvwAALr4AAJU/AACjPwAAyj4AAL4+AAA7vwAAhD4AAKU+AADqvgAADj8AANU+AACYvQAAhj8AAAM9AAA5vgAAeD4AAEK+AAAcPw==", "world": 2, "sampling": [4587751295763329081, 1.0, 0, 0.95, 0.0], "position": 518073, "count": 64, "stats": [[15.0, 1.0000094755426254], [1.2734375, 13.277135038259319]], "full": false, "token": 14} +{"logits": "AAC0QAAAjD0AAAVBAABUwAAA0j8AABHAAAALQQAAhkAAAIxAAAD2QAAAfEAAACk/AAA1vwAAUT4AAKW/AAAGwAAAwD8AADNAAAAkvwAAGMAAAB3AAAByvwAAuT8AAI5AAADKPgAAPEEAACA/AAAVQAAA6EAAAMG/AAB8wAAAuL4AAGG/AAC9PwAAhcAAALs/AACpwAAAtT8AAIa/AADowAAAC0EAALHAAAA/PwAAt8AAAIVAAADbvwAAh0AAAAhAAABpQAAAE0AAALDAAACsPwAAdMAAAF9AAACAwAAAn0AAAGtAAAAPQAAAOkAAAEHAAADhwAAAm74AAD1AAADKPw==", "world": 2, "sampling": [4936335477732862116, 1.0, 20, 0.95, 0.05], "position": 376427, "count": 28, "stats": [], "full": false, "token": 25} +{"logits": "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", "world": 1, "sampling": [8744036631365205789, 0.7, 0, 0.5, 0.05], "position": 192676, "count": 512, "stats": [[26.125, 1.0423730871978458]], "full": false, "token": 422} +{"logits": "AAD4vQAAm74AAKG9AADCPgAAKb8AAMe9AACGvwAART4AANe9AADivgAAHr8AADI/AACSvgAArbwAABu/AABOPwAAjT4AAJi9AAAmvwAAqT4AANk+AAANvgAAJb8AAKK+AADbuwAAU78AADu/AAAbPwAAu74AAL6+AACIPgAAYr4AAHw/AADrvQAANz0AAIO/AADtPgAA874AAKK+AAAoPwAAs74AAGg+AAB2vwAAu74AACm+AAAbPwAAjz4AAKc/AACWPAAAvj8AAMe+AACcvwAAXr4AALm9AABCPwAA3j0AAJI+AADsPgAA4j4AAOU9AABTPgAAmL8AACe/AAAKvwAA+L0AAAQ+AACvvgAAtr4AABO/AAAGPgAAML4AAGY+AADEPgAAzD4AAMs+AACKvwAAFr8AADO+AACivQAAXz8AAKs+AAALvwAAKz8AAKS/AABTvgAAmb8AAGU/AAA3PwAA+L4AABK+AAAavgAATj0AACy/AACmvgAAV78AALq+AAB/vgAAlT4AAC4/AAADvwAAYD8AAJC+AAAuPwAA9r4AAG8+AAC+PQAAgL4AAJ2/AAD0PgAA5L4AAE6+AABdvwAAdj8AAGa9AABSvwAAXj4AACI/AACRPgAA6z4AALC9AAA2PAAAUr8AAF6+AAAtPwAAnD4AAFc+AAB8vwAApT4AABk/AACXvgAAa74AAL++AAAwPgAAQ78AAGs/AADQvQAATbwAAIG+AACuvgAAvL0AAMy+AADNvgAAzj0AAIy+AADqPQAA2T4AAKE/AAAvvwAA6TwAAAa+AAAVvwAAeL8AAAq/AAA7PwAAND0AAEW/AACPPgAAJj8AAN+8AAAivwAAYD0AAC0+AADlvgAAvz4AAJE/AABjvgAAKj8AAKi/AADbvgAAXz0AAOQ+AAAmvwAAFb4AAIs+AAD4PgAAkb4AAIM7AABFPwAA5L4AAD68AAC0vgAAfT8AADW/AAD/vgAA7D4AAJ09AABgPwAAuj4AAOU+AABuvQAApb4AALc9AAAiPwAAy74AAAM/AAAVvwAA2j4AAOa+AAA+vgAAPL8AAFg+AABNPwAAnT4AADc/AABKvgAAPb8AAMG+AADFPQAAUj8AAD0+AADXPQAAtz4AAD+/AADpvQAAiD4AADs/AAAkvwAAHL8AAIy+AADUvgAA574AAEY9AAA/PwAAk74AAGw/AADvPgAALT8AAPI+AAAWPQAAsD4AAMK+AADuvgAAbj0AADW/AAB2vgAAkz4AAIg/AADhPgAAKToAAIk8AAD2vAAAUD8AABq+AACAvwAA1D4AAFs+AADRPgAAu74AANs9AABgPgAAHL8AAF69AACfvgAASr4AABG/AAA2PwAARz8AAFM+AAAMvwAAGj4AAFg+AADvPgAAET4AAAA+AAA8PgAAvz4AACq/AABJPQAAgr4AAHQ9AAByvgAA770AALI9AACYPwAAWz4AABc8AABdPwAAUT4AACA+AAAEvwAATT4AAMs8AACiPgAA0T4AAEu/AACBvgAAgb8AAJO+AAB7vgAAjb4AAGi8AAC9PgAApr4AAAY/AACAPgAA570AAIi+AABwPAAAGT0AAGg+AADlvgAAVL8AANQ+AAAEPgAAGj0AAOY9AADSvgAAqD4AAIC+AADMPQAAur4AAB8/AADYvgAAyb4AAF+/AAD/vgAAcD8AAAw+AAAEvwAAlz4AAI89AACyPgAA5T0AAJe/AABCvgAAGr8AAPu9AABEvwAAmr4AAAu+AAA2PwAAHL8AAEc+AAASvwAAzL0AAAe+AAApPwAA/j0AAAQ+AAALPwAAIb8AALk6AAA4PwAAgb4AAIO+AACLPwAAHL8AAG0+AACrvQAAszwAAPM+AADaPgAABL8AAP88AADbvQAA9T0AABu+AABpvQAAIb4AABY+AAD2vgAAwD4AADu/AAD5PgAAVD4AADG+AAADPwAACb8AABg/AAD9PgAAAr8AAFK/AAABPQAAKj4AAL8+AABiPgAATD4AAEa+AABMvwAALr4AAOg+AACLPgAAIr8AAEO+AAAsvgAArb8AAJU/AACVPgAADz8AAI0+AAC0vgAArz0AANE+AADlPAAAAj8AAOW+AADkPgAAKz8AAKs9AAAHPgAAc78AAGA+AAApPwAAOL4AAHo+AAA0vwAAW74AAIg/AAC4vQAAgL4AAIW/AACZvgAA8r4AABu/AAAjPwAAbz4AAKi+AAAZvwAAwj4AABE/AACaPgAAjr0AAD6/AADtPgAAMz8AAGc/AADRPgAA+T4AAOI+AABuvwAAnL4AACu/AAC1PgAAgb4AAIo+AAB4PgAAlL4AACW+AAAKvwAAfj4AAN6+AADQvQAADr8AAEO+AADUvQAAUL8AAPi9AACEPwAAbj8AAAQ/AAB6uwAA6b4AAD6+AABzPgAAL74AAJA/AACjvgAAzD4AADK+AAAlPgAAET8AAFC/AAA1PwAANT0AAIa9AAB2vwAAMT8AAO8+AAAFvgAAMD4AADi/AAAPvQAAhr8AAFU7AAAqvgAAlb4AAHm+AABtPgAAUj8AAEe/AADfvgAARb0AAMG+AAD7vQAAnL4AAMm+AABBPgAA+D4AAB6/AACIvAAA/z0AABK+AAB7vgAAnL4AAAA+AABuvgAAub4AAMC/AAA+PwAAEr4AAM4/AABgPQAAA78AADI+AACovQAAUj4AAEs+AACUPwAAij0AAKq+AAAlPwAASz8AAE4/AADPPgAAJb8AAJC9AADNPgAAnr4AAI2+AADUvgAAxj0AAA2/AACQuwAAor4AAMA+AABtPgAA0r0AANO+AAC2vQAAgT0AALM+AACOPwAAOb8AACg/AABsPgAAJj8AAGQ+AACWvwAAR74AAEe+AABHPgAAOD8AAC4/AAAiPgAA6z4AAFg+AAB/vQAAk74AAFA/AAABPAAAuL4AABU/AAA0PgAAKL8AAJi/AAC4PgAAgD0AANC+AAD2vgAA874AAKA+AAAIvwAAG70AAAW9AAAVvwAAEj8AAJY/AAA9PQAARz8AABk/AAC4PQAA+T0AAFA/AAALPwAACT8AALy6AAD+PgAAqT4AAPK8AADCPQAAJz4AAIc+AADZvgAADj8AAC2/AADuvgAAzD4AAFO/AAAEvwAAxjwAAEY/AAANPwAAOr8AAJy+AAA+vwAAhr4AAMK+AADkugAAEz0AAKY/AACnPgAA/L4AAE69AACNvgAAID8AAP49AAC4vgAAL78AAIs/AAAguwAAnr8AAP++AAChPgAAY74AAG+/AAChPgAAQr8AAMw+AAAwPgAAJD8AAI8+AADSvgAAVL0AAFQ9AABFPwAA1bwAALE+AAC4vgAAZT4AAAc/AADBvgAAhL0AALY9AACNvwAAar8AAKA+AACgvgAAn0AAAHs+AACVvgAADz8AAKG/AAAgvgAAgT0AAFw/AAASvgAA9j4AAAS/AAAFvwAAvb4AAEy/AAAJvwAAP74AANy+AACMvgAA3j0AAA6/AABZvQAA6LwAAAm+AABoPwAAsL4AAFY+AAAMPgAAW78AAA+/AAA/vwAAGL8AAA2+AADdvgAART4AACQ9AAD7vgAAAz4AAHq/AABgPwAAoT4AAJ6+AAAxvwAAUr4AAFU+AAAivwAAwb0AALw+AAAkPwAACL8AAHg/AABKvgAAvz4AAMc9AABevwAAB78AANi+AADZOgAApL4AAKo+AAA6PgAAWj8AAFo+AABGvwAAVT4AAB2/AAAUPgAAVD4AAI6+AADgvQAAJ74AAFa/AAARvwAA3T4AAAa6AAACPwAABL4AABa+AACnvwAAdL4AAJ69AACUvgAAMj4AAN8+AABOvwAAGr4AAGC+AAA8vwAAL78AAIK9AACCPwAA2j4AAIE/AAAxPQAASb8AAJa+AACVPQAASD0AADY/AADMPgAAET8AAPu9AADivgAAjb4AAHU9AAB9PwAAgz8AAM0+AABjPgAAub4AAM88AAA5PwAAwr0AACe/AAASvwAAx74AAJu+AABpvQAAzb0AAH+9AACuPQAAFL8AAMS+AAAqvgAAeT4AAFs8AAAVvQAAHb8AAIu+AACIvgAAjT4AACi7AADgvgAACb8AAC6/AAA2PwAAhLwAALw/AAAPPgAAVL8AAIu+AAAzvgAAPj4AAKa+AAASPwAAiz4AAAa/AABZPwAA2T4AAGC+AADOPgAAej4AAKE9AAAavwAAcT4AAAc/AAA7vwAAyj4AABs/AAASvwAAKD8AAJ4+AAAjvgAA+74AAAG/AAC0PgAAnz4AAAw+AABfPQAA474AAJE+AAB1PgAAjb4AAJC/AACVvgAAhL8AAFm/AADivgAAOD4AAF4+AACSvgAA6LwAAJk/AAATvwAARj4AACM+AACGPgAA9b4AABw/AAD8PQAAa74AAMM9AAAsvgAArT0AAJY9AADyvgAA674AACC9AABRPgAAf78AAKE9AADvvgAARD4AAJY+AAC5vgAAR74AADI+AAC+vQAAj74AAM69AAA3PQAAJb8AAAe8AAArPgAA6j0AAIe+AADSvQAAC78AAES/AADhvQAAR78AAOs9AAC4vgAAXD8AAD+/AAB8PwAAx74AAD8/AACtvgAA/D4AAGu+AACWvwAAZz8AAEY9AAC/vgAAQ74AACC/AAAYvwAAkD8AAAk/AAAJPwAAl74AAM49AABkvgAAKD8AALw/AABgPgAADz4AAJO/AACyvgAAgD4AAFS+AABIPgAA0r4AAA+/AACwPQAAyT4AAN49AAAUvwAAKL8AAKc+AACfPgAAjT8AAMi+AAA8PQAAab4AAKs+AAA2PwAAuD4AAMw+AADPPQAASD8AAFs9AADjvQAAD74AAMQ+AADoPgAAHr8AAPG+AAAivwAAHr8AAC2/AACiPAAABL8AAAa+AAC/PgAALr8AAKO9AADMPgAAiL4AAGk/AAAxPgAAUb8AAC6+AAAwPQAAY78AAD6+AAABvwAAF78AAJu7AABXPQAAy74AAMc+AACGvgAAfL4AAJy/AADcPgAAWj4AAAq+AABZvgAAHz4AAJo+AAAaPgAAxT4AAFg/AAAVPwAAfD0AAGA+AACmPgAANj8AACu/AACAPwAAGr8AAAW/AACpPgAAbL0AABM+AABSvgAA2j0AABe9AAALvwAARj4AAHG/AAAoPQAAJL8AAKa9AAC0vQAAML4AAPA+AAAnvwAAtL4AADy/AACfPgAAgT8AAJC9AAA3vQAA4L4AAOC+AAB5vgAAzr4AAPw+AADrPgAAE74AAIm9AAC2PgAAgD8AAAk/AABNvwAAEL8AAK09AABUPgAAp78AAIA/AABgPg==", "world": 2, "sampling": [8913800998998537029, 0.3, 40, 0.95, 0.0], "position": 654784, "count": 48, "stats": [], "full": false, "token": 637} +{"logits": "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", "world": 1, "sampling": [7100671880527856778, 1.0, 0, 0.95, 0.0], "position": 858952, "count": 512, "stats": [[22.875, 1.7378080275014667]], "full": false, "token": 824} +{"logits": "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", "world": 2, "sampling": [2965236650000917055, 1.3, 40, 1.0, 0.0], "position": 572741, "count": 48, "stats": [], "full": false, "token": 420} +{"logits": "AADmPQAAFz4AAMI+AAAKvQAApD4AABU7AAC6vgAA8b4AAK29AACpvgAACj4AAAY/AABPPwAAM78AADq+AAAUvwAATT4AAFu+AABpvgAAoD4AACA/AADlPgAAxL8AAD0/AAAMPwAAJz4AAKO+AAC1PgAAVT8AAAe+AABvPwAABj4AAM69AAAwvwAAoT0AAA+/AACPvgAAqj0AAHdBAACevgAA3r8AAPU+AAC+ugAAJb0AAIs9AAC8PgAAAz8AAJI/AACoPgAAcrwAAG09AABfPwAALj0AALA8AAA8vwAAkr4AAIe+AABhPwAAh78AAIO+AAC8vgAA7z0AANw+AACqvg==", "world": 2, "sampling": [5179649205749018986, 0.3, 0, 0.8, 0.0], "position": 871667, "count": 64, "stats": [[0.93359375, 5.253945232896875], [15.4375, 1.0]], "full": false, "token": 38} +{"logits": "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", "world": 1, "sampling": null, "position": 477085, "count": 9, "stats": [], "full": false, "token": 568} +{"logits": "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", "world": 2, "sampling": null, "position": 558869, "count": 9, "stats": [], "full": false, "token": 847} +{"logits": "MsK+Pt+bfL2OfFo/9vVMvggZF75cP/k+PfK7PupNO73engU+eukxv4sDCj7v3tK9FmaTP0/mCD6i7Y297SJYPQt1Fb+Nmc29mWTEvnh9FL8C7Rk/IYk9v3k0l74XYo8+oK4PPsRNc7+wNqU+32e2vSes5j0xG4O/6w6svq4IKb/2yKi8zSeAPfIVlj9M0xu+DphVP4cSSL9B8A2/Q/2rvlxqAD36Dbw+qmNoPIZkyL/Q+Gu/EbBQv/0Vuz+pk5w+TCoPPqsiQD9c/1O+VfkgPgVGDz4mdpy+bRI6PxRr6j45j9M+zm8vPgcP5L5y18u99T+4Pu5S9L6Haw++ZEqlvg==", "world": 1, "sampling": [3621344759957349613, 1.0, 1, 0.5, 0.05], "position": 682925, "count": 9, "stats": [], "full": false, "token": 46} +{"logits": "AACgPwAAacAAAGlAAABnPwAAXUAAAEXAAAAGvwAAGD8AAE0/AABvQAAAkcAAAEU/AABSPgAAeUAAAEm/AACFvwAAxT8AANs/AACNQAAAKz8AANRAAAB/PwAAx0AAAEZAAAAHQAAAFkAAACvAAACHQAAAkb8AALNAAAA3wAAARMAAAC4/AACMwAAAVL8AANTAAADzwAAADUAAAAFBAABbQAAADj8AAF3AAABmQAAA68AAAOu/AACTQAAAQkAAAK+/AACHQAAAuL8AAPg/AADqvwAA8sAAALi/AACBPwAAM8AAAA/AAADLPwAAZ0AAAKbAAADyPQAAL0AAACPAAACYwA==", "world": 2, "sampling": [8101704021458262809, 0.7, 40, 0.5, 0.0], "position": 337383, "count": 48, "stats": [], "full": false, "token": 38} +{"logits": "AADVQAAAtsAAAA5AAACIPwAAFcEAAAHBAABUQQAAEkEAAJvAAACvvwAAjkAAANBAAAAvQQAArEAAAL0/AACcwAAANsEAABpAAAC2vwAAU8AAADG+AAAvwQAAA0EAAIrAAAAiQAAA78AAAD9AAADTPwAAn0AAAE7AAAC+wAAAv0AAANY/AABBQQAAh8AAAALBAABXQQAAG0EAAHLAAADQwAAAU0AAACTAAADFwAAADkEAAA/BAAC2QQAAaMAAAGxAAAChQAAAZ8EAALxAAABZQAAAKb8AACPBAAA4wQAAc0AAAE/AAAA3PwAAQ8EAAL6/AAAwQQAAmUAAAInAAACcPw==", "world": 2, "sampling": [5451278120048176373, 0.3, 40, 0.99, 0.0], "position": 145731, "count": 48, "stats": [], "full": false, "token": 45} +{"logits": "AACAvQAA4L4AAB+/AADDvgAAXD0AAEy/AACZvwAAyr4AAFm/AABLPwAAWD4AAHS+AABkPgAAYj4AAGw+AAACPwAAgT4AAFi/AACivgAA3jsAAFe+AAAXvwAAm78AAKg/AAAAvwAAxb4AAGY+AACovQAA5r4AABO/AAAoPwAAiz4AAOE9AACMPAAAYb8AAAM/AAA1vQAAAj8AACg/AABGvgAAOL0AAEM+AADsPQAAj70AACw/AAAXvwAAoT8AAEG/AAB0PgAAnj0AALc+AAC/PgAA1DwAACA+AADdOwAAbT8AAD2+AAAxvwAASD0AANa+AAC2PgAA5D4AAPa9AABAvg==", "world": 2, "sampling": [3710670771893193194, 0.7, 0, 0.95, 0.0], "position": 937675, "count": 64, "stats": [[1.3125, 5.495567830155166], [1.2578125, 7.542199728229428]], "full": false, "token": 41} +{"logits": "AADDPwAAGD8AABw/AABLQAAAaT4AAK69AACpQAAAj0AAAHNAAACivgAAQ8AAACDAAABjQAAAREAAADPAAACZvwAAMr0AABTAAAC0QAAAJsAAAOQ+AACjPgAADcAAAIXAAAAMwQAADj4AAF3AAACXQAAAij4AAOS/AACPwAAArb4AAFhAAAAGwAAA/b8AAFxAAADjQAAAjb8AABRAAACXQAAArD8AAAXAAACBwAAA0UAAAE7AAACrQAAAK8AAAGQ/AACtPwAArMAAAH9AAAB8QAAAmUAAAGvAAABYQAAApMAAAEe/AAAVwAAAAcAAAFy/AAAsQAAA7z8AAEHAAAAmQA==", "world": 2, "sampling": [9033404821149532766, 0.6, 40, 0.5, 0.0], "position": 782805, "count": 48, "stats": [], "full": false, "token": 36} +{"logits": "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", "world": 1, "sampling": [8276951797015516615, 1.3, 40, 1.0, 0.0], "position": 910535, "count": 48, "stats": [], "full": false, "token": 667} +{"logits": "AAA/wQAA2sAAAN9AAABjQQAAeEAAAJfAAAAWQQAAv78AAMDAAACuQAAAGsEAAPxAAABJQAAAlcAAADDBAAAMQQAAEL8AAC/BAAA4QgAALMEAAGfAAABIwQAA178AAKdAAADWvwAAKsAAAPM/AABTQAAAM0EAADG/AADzwAAA3D8AAB/AAADWvwAAgcEAANe/AAAwQQAAh0EAAAhBAACCQAAAxL4AAE/AAAB1QAAAzcAAANTAAABMwQAAO0EAAERAAAA3wAAADUEAAJzBAAA6QQAAe8AAAMhAAABvvgAAhkEAALG/AADLQAAAg0AAAK3AAABbwQAAFcEAAL9AAABhwA==", "world": 1, "sampling": null, "position": 33123, "count": 9, "stats": [], "full": false, "token": 18} +{"logits": "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", "world": 1, "sampling": [4270885159196656458, 0.6, 20, 0.95, 0.0], "position": 114204, "count": 28, "stats": [], "full": false, "token": 73} +{"logits": "96hXwNNFkr/NJh8/ugOSv+iJKMABmTfAuvNHwOiuisADYFnA1CISwLzTXEBj26m/MYbfv/tKmMBQzFe/8TIYQCcsmz7cTbw/VjpsQOgeB7/kKuA+FjcpP5qu1L8vRcO/F53jPookBMCfZNC/dz3eP5lwQL8Y9OG/dC9pP/VwOsAFzQc/IKg6v/A2EkB7Qlq/03COPxZhU0ApizE/5r7ev60jT795wto/z5r8Pt8+VcBLnLk/Ar4pPxaIjj/CBP+/GvV6v2Jsqz8Lmbq/1t8FQBfZ7r/ggs0/gbMuwLCw9z6jciC/q3CjwBixvb3YXvc/4pGQPeOFSr/lgAdA9kGWPw==", "world": 1, "sampling": [7384270503591009187, 1.0, 0, 0.99, 0.0], "position": 965823, "count": 64, "stats": [[3.6910605430603027, 5.2309592911642255]], "full": false, "token": 59} +{"logits": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", "world": 1, "sampling": [6066518423599484387, 0.6, 0, 1.0, 0.0], "position": 665413, "count": 64, "stats": [], "full": false, "token": 16} +{"logits": "AABEwAAAisAAADNAAACMPwAAzz4AAAK/AADuvgAAC0AAAAm/AACIQAAAoT8AAIy/AAA3QAAAvL8AAJFAAAARwAAAt74AAEFAAAA9vwAAtL0AAPM8AAAiwAAAwb8AACTAAACLPQAAqr8AAG4+AAASwAAANr4AAKq/AADnPwAABEAAALc/AAC2PwAADsAAAF2/AAB2vwAAp70AAGjAAAAQQAAAVMAAAAjAAABhQAAA6jsAAHPAAABaQAAAVr8AAOg9AADKvwAAVEAAAPq+AAAYPwAAjb8AAL0/AACHPwAA3L8AAKa9AAClPwAA2z4AAKM+AAAhwAAAsb8AABzAAADDvw==", "world": 2, "sampling": [2501048646813996520, 1.3, 1, 0.95, 0.0], "position": 138091, "count": 9, "stats": [], "full": false, "token": 14} +{"logits": "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", "world": 1, "sampling": [5934752991553478172, 0.6, 20, 0.8, 0.0], "position": 488057, "count": 28, "stats": [], "full": false, "token": 519} +{"logits": "S0a8Pr3FEz64aQo/fCDiPftUAL8iWlU9X784P0n7Yz/zmLi+RTMjPwQU8L6UMj4+i7IcP25/zT4SjNe+InPOPOectT9JGem9u76FP0Ye1r53Hja+/0anPqmV5r4W3LE9/0RmPtFv/z4+qF2+9qaFO+B17b4lQfi9WBuPv68puj8Sgd08OkM2vzluJz+4/iO+WN4gP0//mz3Lljc+Su9MPhelOz+qH5W+Iw8kP89XvT2zwGQ+Xo9AvXm6iL6lMwG9NSXpPkt9VD9u0NO+VOZWv7AxHz8O8X0/p5lEPc6lez9dtCQ+0LiIvy8GWTz/jZc/b0MGv71bJD9TrQM+WS33vA==", "world": 2, "sampling": [702976621119561002, 1.3, 20, 0.5, 0.05], "position": 969646, "count": 28, "stats": [], "full": false, "token": 16} +{"logits": "jWmivwnz8L4jfPA/TcprPioHm7+txva/lInFv8UN/r8r/0W+jbcEPyggxL7tmyhAXDyMP6dwWb/8qw5ABaOovzfu3z4pHEw/r+JCwAsj678AqUTALJmMP8N8F0B5LoBAbUb3PeJe7z5AaQzAmcAAv3GH+L7dcdE/eVTmv/WeZ8CDH4a+t/RSP86hGT+RwuE/gH8KQgAaNUD9RY6/S6PwPxIhScCQraC/R349wEl7zD/NPQjAwLsXQAqhTUDjb4pAb7uGP+U4C0DDwtg/YaOAQAC/gj57mgLAs3TdP350qr6rBvG+VghtwG0wlz9fx3c8lKPAvnZDPr7y8S/As2DaPw==", "world": 2, "sampling": [8670694174503907686, 0.6, 1, 0.5, 0.0], "position": 1022453, "count": 9, "stats": [], "full": false, "token": 36} +{"logits": "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", "world": 2, "sampling": [276151459174940250, 0.6, 40, 0.95, 0.0], "position": 1013085, "count": 48, "stats": [], "full": false, "token": 355} +{"logits": "AAD0PwAAzT8AAIg/AAB6wAAAzD8AALY/AAD3PwAAXL8AABfAAACiQAAAA74AAE/AAABxPwAAyj4AAAs/AADLPwAAmMAAABG/AADKvwAA0UAAAOK/AADUPwAA7z4AAOc/AADNQAAA+kAAAIc/AACyPwAARcAAADTAAADrPwAA1L8AADfAAACYQAAAgL8AABJAAAA4wAAArb8AAMC/AAAnPwAALz8AACnAAADmPwAANcAAACk/AAAaQAAAhL8AALTAAADrQAAA2b8AAJc+AAAqwAAA0D4AABtAAAAbvgAAHsAAABnAAADBvwAAYUAAAAvAAACkPwAAG8AAAE9AAADNvwAANkAAAMPAAADjvwAA60AAADPAAACdPgAAQz8AAJfAAAAswAAAysAAAFXAAACPwAAAI0AAAA6+AADXPgAAm0AAAFc/AACAPwAAQUAAAI4+AAAhwAAA0cAAAIo+AABqQAAAij8AAJ7AAAAgwAAAkMAAAEfAAAC9vwAAcsAAALrAAAAJwAAAEcEAABlAAACEPwAACUEAAC4/AACBwAAAn0AAADHAAADLwAAAnMAAAGTAAAA2wAAAmUAAAERAAAAgwQAAjD4AAHw/AACYvwAAz8AAAPHAAAAtwAAAX0AAAMC/AACKQAAAAEAAAChAAAA8wAAA6b8AALQ/AACSQAAAssAAAJnAAACswAAAYEAAACFAAADIwAAALUAAAP9AAAAYQAAAX78AAGO/AABAQAAAAEEAALK8AACQvwAAyb8AAMFAAAC0vwAA60AAADQ/AAC0wAAAc0AAAKrAAACdvwAABUEAAAPAAAAOwAAAbUAAABbAAAADvwAAxsAAAJQ/AADDvQAA7z8AAN7AAAC4vwAAAUEAAAHBAABtPwAABcAAAGlAAABEPwAAq78AALrAAACPwAAA+j8AAAzAAAAfwAAA1r8AAFPAAABNPQAA2sAAAJw/AAA6wAAAlr8AABDAAACmQAAA+78AAARBAAANQAAAysAAAPi9AACoQAAAOD4AAAJAAADqPwAAEMAAAA/AAADtvwAA+0AAAJC/AADaQAAAFD4AAPO/AAB1vwAAj8AAAE5AAAAawAAAtMAAAIo+AAAvwQAAWcAAAP6/AAA7wAAAekAAACPAAADhvgAAjT8AAHRAAACgQAAAFL8AABLAAACDQAAAZz4AAI8/AABuQAAAk8AAALFAAABQPwAAzz8AAC7AAAAzwAAAfcAAACi/AAA2PwAAKb8AAPO/AAAOwQAAtj8AAIa+AAASQAAABcAAAIjAAACrQAAAKsAAAJNAAACrPwAArsAAAEtAAABdvwAAQcEAAEPAAABYQAAAiUAAAGfAAAC0QAAAQ78AADpAAAAsvwAA1r8AAC8/AAAQwQAAE74AAIRAAADtwAAA3UAAAG0/AACnwAAA+j8AAAg/AACEQAAAyEAAAI+8AAApQAAAokAAAKvAAAB1PwAAYj8AAME/AAA9vgAAoMAAAFA/AABtwAAAh8AAAD1AAABQPwAAscAAAIg9AADuPwAATEAAAD9AAAAKQAAAjT8AAJ1AAABauwAAEEEAAMg/AADkPgAArkAAAIA9AACsvwAAYz8AAB0/AAC/wAAAWUAAANvAAAAWPwAAfkAAAHG9AAAQwAAAwz8AABq/AABfwAAA9b8AABRAAAD1vgAA5EAAABXAAABYQAAAGcEAAIk/AADNPgAAKb8AAD9AAAAHwAAAq78AAJ4/AACKQAAA5L8AAHrAAACQPwAA7UAAAJ69AABHPQAAc78AAAS/AAAtwAAAd0AAAG5AAABhQAAAX78AAPDAAAArwAAADcEAALK/AAAJQAAAjT8AAGRAAADWvwAAekAAAB/AAADvPgAAu8AAAIRAAAAgQAAABkAAAKS9AAAOQAAAD8AAAJLAAACYwAAAij8AAA3BAABxQAAAnb8AAG5AAABGQAAAeT8AABJAAAAUvwAAvrwAAI9AAABWQQAANT8AAJdAAADoPwAA2b8AADZAAACJQAAAhT8AAN1AAAA6QAAA8D4AAMJAAABOwAAAp8AAALQ/AAA5QAAA6L8AAAM/AADrPwAA38AAADhAAAARQAAAGkAAAB7AAAD7vwAAJcEAAJo+AAB/vwAAUMEAAMW/AAD8vQAAwT8AANU/AACAQAAA7r8AAPnAAACjQAAABUAAAG09AADQvQAAuz8AAJdAAAAjQAAAMD4AAEHAAAAOQAAAIUAAAHrAAAANvQAAzz8AAOHAAAAfQQAAOb4AAInAAAAnQAAAkr8AAJ3AAADjQAAA8j0AAE1BAAALQAAADUAAAEM+AACevwAA6D8AAKbAAAAGwAAAJ78AAHVAAADtvwAAVD8AAKvAAAD9QAAAJ0EAAAG/AAAfwAAAU0AAAKHAAAAHPwAAg74AAHLAAAC7PwAAs74AAFrAAADXPwAA0MAAAMc/AABfwAAAkr8AACw/AACLvwAAL0AAAIk+AAAmvwAAHsAAAJjAAACuvwAAkUAAACRAAACIQAAA7L8AAHpAAADpvwAAkr4AAHO/AADSwAAAYUAAAJDAAACBQAAAlsAAAI7AAAA+QAAAqcAAAGTAAAAFwAAAnMAAAKXAAACaQAAAaUEAAMe/AAAFQAAAvL4AAOw/AACCQAAAnb8AAAvBAACIPgAAa0AAABk/AACiPgAAU8AAAJvAAACGQAAAokAAAMQ+AAD3vgAAZr8AAC2/AAAavgAAcsAAAPC+AAAXwAAAJj8AAIC+AACPvwAAPMAAAEi/AAAovwAAyz8AAOtAAAD7PwAAEMAAAHxAAADiwAAAEMAAAKRAAABdvwAAScAAAIpAAAAZQAAArkEAAB6/AACfvwAA0D8AAAvBAABYPwAA9kAAAMK/AABgQAAAREAAAG2/AADOPwAAAsAAAAnBAACKvwAAaj8AAElAAADHPwAAKsAAAPhAAACgQAAA3L8AAPA+AACBQAAAFr0AADZAAACUvgAAuT8AAE6/AABvvQAAIMAAAEA/AABLQAAAe8AAABLAAACywAAAnMAAAPi/AABIwAAA1L8AAIW/AABgPwAAdsAAAMRAAACYQAAAJMAAAKE/AAC5wAAANcAAANjAAADVQAAA0j8AALG/AACjwAAACUAAAI7AAADLvwAAPsAAAA3AAABUQAAAgUAAAO4/AAAGwAAAssAAAPk/AAA1QAAAAMEAACFAAAC/vgAAgcAAANTA", "world": 2, "sampling": [1711520886761389839, 0.3, 1, 0.95, 0.0], "position": 596230, "count": 9, "stats": [], "full": false, "token": 529} +{"logits": "cU39v2VB5b57fDPAdS2MQA+MUcBTUuq/FvITwC50lr96rKPAB7oAP8nmp79+gsO/a7OzP5Xfy0AGIQbAmOkiv8vJAEAt9SFBixFwvxbkiT/m76C/RgHCP+AEaD+u4PM/eh+/v6yVM8FabeQ/m/QCwdz0gUBqP5S/pVQOwUadgT+D4q2/7Ig5vsUPQsB4JwjADJiaQLIm/kCEy1W+QOeXP64IYEBz8RPAHhYqvrfh2EAYsD3Amn+ewIScX8A7K4/AquY1QNpCecBc9qm9qX7HPzIzUT/5F1nAHuZfwPAM5kDld3rA+noOwGvbur8Ndo2/4qgfQAbUPcALqUI/PckTwA==", "world": 2, "sampling": [7309524698809621492, 1.0, 40, 0.99, 0.0], "position": 749847, "count": 48, "stats": [], "full": false, "token": 17} +{"logits": "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", "world": 2, "sampling": [3904186157250332442, 0.7, 0, 0.8, 0.0], "position": 922440, "count": 512, "stats": [[7.1875, 1.4350729202235541], [5.15625, 9.750832555525035]], "full": false, "token": 181} +{"logits": "AACFPwAAFj8AAB4/AAABvwAAOD8AAPI/AAA8QAAA/T8AAKvAAAB8QAAAHsAAAAe/AABYQAAA1r4AAB2+AACdvwAAFj8AAB/AAABiQAAAGT8AAJe/AADUvwAAWUAAAF4/AAAMvwAAN8AAAFW/AADfvwAACkAAAGc+AACcPgAAnL8AAIi+AABhwAAAob8AAMs/AADcPgAA0T8AADpAAABHwAAAOsAAABs+AADKvwAAZL8AAP4+AACsPgAAw70AAII/AACEPwAAlMAAAIa+AAAmQAAA8r8AAPu9AABVvgAANL8AACQ/AAACPwAAvT8AAOc+AAAevwAAhz8AALI9AAB9vw==", "world": 1, "sampling": [2017590111683187940, 1.3, 1, 1.0, 0.05], "position": 29612, "count": 9, "stats": [], "full": false, "token": 9} +{"logits": "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", "world": 1, "sampling": [8037648035165098638, 0.6, 40, 0.99, 0.0], "position": 132348, "count": 48, "stats": [], "full": false, "token": 557} +{"logits": "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", "world": 2, "sampling": [5413306568669523959, 0.6, 0, 0.95, 0.0], "position": 339103, "count": 512, "stats": [[10.875, 1.837578836877348], [11.9375, 1.4801354436608432]], "full": false, "token": 568} +{"logits": "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", "world": 1, "sampling": [5895153570623443898, 0.6, 0, 0.95, 0.0], "position": 484272, "count": 512, "stats": [[24.47679901123047, 2.006784999977779]], "full": false, "token": 990} +{"logits": "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", "world": 1, "sampling": [7769662988480380717, 0.3, 0, 1.0, 0.0], "position": 309705, "count": 1000, "stats": [], "full": false, "token": 24} +{"logits": "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", "world": 2, "sampling": [228601088372730951, 0.6, 40, 0.95, 0.05], "position": 788573, "count": 48, "stats": [], "full": false, "token": 512} +{"logits": "AABgvgAAMEAAALo/AABOPwAAcz4AAAm+AAA4PAAA0D8AAKZAAAAxvgAAAUAAAMg+AADfvgAAzL8AAClAAABnQAAAV78AACbAAAC2vwAAEsAAABs/AACAwAAADkAAALa/AAAFQAAADMAAACXAAADCPgAA7D8AAJQ/AACyvQAAo0AAACPAAAAWvwAADUAAADrAAAADQAAAE74AAALAAAAkvwAAgr8AAPk/AAClPwAAPz8AAHq/AACJPwAAFcAAAC3AAAANPwAAaT8AAKe+AAAfQAAAvL8AAMS/AACoPgAAlr8AAAfAAACGPwAARsAAALg/AADePwAAk0AAAFq/AAB9wA==", "world": 2, "sampling": [8779806953631716610, 1.0, 0, 0.8, 0.0], "position": 161289, "count": 64, "stats": [[5.1875, 2.608872903989641], [4.59375, 1.6876132783562228]], "full": false, "token": 61} +{"logits": "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", "world": 1, "sampling": [2126918695243810442, 1.0, 0, 1.0, 0.05], "position": 828343, "count": 600, "stats": [], "full": false, "token": 366} +{"logits": "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", "world": 2, "sampling": [4834968246499130613, 0.6, 0, 0.5, 0.05], "position": 681379, "count": 512, "stats": [[4.90625, 11.536868923413035], [5.9375, 2.947922205628429]], "full": false, "token": 620} +{"logits": "AABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwA==", "world": 2, "sampling": [1556540676270715358, 1.0, 0, 0.5, 0.0], "position": 265897, "count": 512, "stats": [[-3.0, 500.0], [-3.0, 500.0]], "full": true, "token": 215} +{"logits": "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", "world": 1, "sampling": [3303281035977655556, 1.0, 0, 1.0, 0.0], "position": 140048, "count": 600, "stats": [], "full": false, "token": 379} +{"logits": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", "world": 1, "sampling": [778494034063285528, 1.0, 0, 1.0, 0.0], "position": 316766, "count": 64, "stats": [], "full": false, "token": 24} +{"logits": "AAA8QAAA7D4AAJJAAACWwAAAQEAAAA3AAAAAQQAA1j8AAIq/AABNwAAAHz8AAKDAAABzQAAAKMEAABo/AABgwAAA5b8AAGHAAAAywQAAoD4AAIXAAABjPwAAAcEAALA/AACQPwAA9D8AAMC/AABAQAAAlEAAAN4/AABGQAAAKL8AAALAAAB0QAAAJ8AAAME9AAD5vwAAKz8AAOU/AADKQAAAAEAAAAVAAACVwAAAqT4AABLBAACKPwAAWj8AAM8/AAA/wAAA9z4AAEJAAADUPQAAzD8AAAO/AABkwAAAisAAAOLAAAC0PwAAqkAAAHvAAAAnQAAAtsAAAI9AAAC/vw==", "world": 2, "sampling": [5073097337968017332, 0.6, 1, 0.5, 0.0], "position": 896740, "count": 9, "stats": [], "full": false, "token": 6} +{"logits": "AACNvwAAQcAAAAfAAACHQAAAJkAAAOA+AAALwAAAa8AAACHAAAArvwAAjr0AALw/AAC1PwAAp78AAOc/AADQPwAAkj8AAALAAADqvgAAcT4AAEk/AAA/QAAAoT8AAE9AAAAXvgAAy78AANo/AACDPwAAXsAAAKPAAADDvgAA7z4AAGg/AABhvwAADsAAAHfAAABRwAAAfT8AAI2/AABAQAAAF8AAANE+AABrQAAAqr8AAD9AAAAcQAAAl78AAIg+AAAXvwAAFr8AAC4/AACHvgAAej4AADi/AAAGQAAAXEAAADBAAABBQAAAez8AAA4+AAD2vwAAvb4AAKw/AACyvw==", "world": 2, "sampling": [9047417549858896852, 1.3, 0, 0.95, 0.0], "position": 810962, "count": 64, "stats": [[4.21875, 3.5577061308719804], [3.671875, 6.09694532837834]], "full": false, "token": 21} +{"logits": "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", "world": 2, "sampling": [7488439582557862296, 1.0, 1, 0.8, 0.0], "position": 540473, "count": 9, "stats": [], "full": false, "token": 599} +{"logits": "AAAkPwAAqz0AACa+AACSPgAAJz8AABG/AABYvAAAOj4AAHU+AACGPgAAvb4AAHm9AAD0PgAA3z0AADe+AACxPgAAUD8AAA++AADBvgAA0r4AANc+AADCvgAAaL8AAFk/AADHvgAAxb4AAGY9AAAsPwAAkDsAACo/AAC2vAAAq74AAPq+AADYPQAAqj0AALM/AAC/PgAAlb4AAPI+AACRvgAA7TkAAMk+AAB0vwAAiL8AAOi+AAAwvwAAvr4AAJ0+AABnvgAAFzwAADS/AAApPwAAuj8AAPs+AAAJPwAAYL4AAO0+AAAcvQAAk7wAAAY/AABBPwAAE78AAKa+AAD5vg==", "world": 1, "sampling": null, "position": 183150, "count": 9, "stats": [], "full": false, "token": 52} +{"logits": "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", "world": 2, "sampling": [105467092449962069, 0.7, 20, 0.5, 0.05], "position": 425720, "count": 28, "stats": [], "full": false, "token": 735} +{"logits": "AAASvgAAJL8AABy+AAAdQgAAgD4AACu/AACwvgAAFb0AAL6/AADtPgAADz4AAEK/AADgPgAA+j4AACI+AACRPgAAJ70AAG0+AACpvgAAsL4AAHY+AAAavwAAjr4AAE4/AAAlPgAAl78AAPu9AAASvgAAZz4AAB89AAAxPwAAyT4AAMe+AABsPwAABL4AANS9AABtPgAA8D4AADW/AACNPQAAyD4AALy+AADuPQAAuT4AAHo/AACyvgAA+z4AAIG/AADRvgAA3z0AADO+AADCvgAALD8AAKO9AADcPgAAGz0AAI+9AAAkPwAAWT8AALw9AAAWPwAAsT4AAES+AADuPQ==", "world": 2, "sampling": null, "position": 997119, "count": 9, "stats": [], "full": false, "token": 3} +{"logits": "AAA4PwAAHcAAAP2/AADJvwAA9b8AAGc+AABKQAAACUAAAM4/AADmPwAATkAAAGy/AAA6wAAAk78AAF/AAAAPwAAAY78AADA/AAAVwAAAgz8AAOi9AAA6QAAA378AADa/AACcPwAAD8AAAM3AAACrvwAAsD8AAHY/AAAEvQAAwj8AAOZAAAAWvwAAQr4AAI6/AAB3PwAAzz8AALo/AAAxvgAAA8AAAAm/AADMvwAAPcAAAAZAAAC8vwAA8T4AAOK9AAAxvgAAEUAAAEFAAABlPgAA/b8AAALAAABWQAAAGb8AAB4/AAAoPwAACsAAADPAAACSQAAAPL8AAA2+AABTwA==", "world": 1, "sampling": null, "position": 82288, "count": 9, "stats": [], "full": false, "token": 32} +{"logits": "XpcBwN/CS75VblM/Y4QBP8hzhD1Fcaq/IiKJvsPCvz93HYU/spOQvyMaIb82dFM/fK/Bv1XjhcDfxYm/xQJoQIXeVb+AuoC/B50oQO3NyT7rILlAh0G7vkXthUATdgnAmg1Bv+43KT/jcIw91o+WP7FO+j9RhM2/mR2pvvF7OkCQr4Y+yuWWP6+d4b+fDau/ttFawKx/Ib8iKZS/ZQMTP2vDSj5KVohAi1kKv0SJRD8T1vQ+BVHcP4e7+D64kWRAo+AqP7pNDEDz8ZO/y2UEQIPW+D/mLIS/KFUCvx+rib8jrMu4taj1vxcFtL8bFEXAXHLxPZBrr79+1gtARPUTwA==", "world": 2, "sampling": [5036920421531086387, 0.6, 1, 0.5, 0.05], "position": 898140, "count": 9, "stats": [], "full": false, "token": 20} +{"logits": "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", "world": 2, "sampling": [38960699981039977, 1.3, 0, 0.95, 0.05], "position": 411976, "count": 512, "stats": [[38.75, 1.000000000059839], [1.4921875, 170.8347849551986]], "full": false, "token": 223} +{"logits": "AAANQAAA0L8AANi+AABoPQAAiD0AADvAAAA6PwAAK8AAAKc/AABvwAAAQr8AAAFAAADuPwAA+z4AACRAAABCPwAAND8AAPq/AAAIwAAA9L8AALY/AADIvwAAUj8AAKW/AACAQAAAlj0AAAFAAADtPwAAD8AAANq+AACGwAAAlz8AAAbAAACCvQAAmD0AADW/AAC6vwAAD78AAMS/AAAOwAAAu78AABhCAABmwAAAHsAAAHfAAAAuwAAAID8AABzAAAC8PwAAFkAAANQ/AADaPwAAh78AAAS/AAB/vwAAhj8AAFrAAAA/QAAAccAAABpAAADDPwAAJEAAAL8/AAAMvg==", "world": 2, "sampling": [9166211358840179356, 0.7, 40, 1.0, 0.0], "position": 617450, "count": 48, "stats": [], "full": false, "token": 41} +{"logits": "AAAvvwAAwD8AANQ/AACzPwAAqb4AAClAAAC3QAAAwj4AANk/AACTQAAAvj4AADHAAAALPwAAfz4AAD1AAAAAQAAAfL8AAJm/AACjPwAAIz4AAP6/AAATwAAALsAAAMc/AADWvQAAK8AAAPc/AACJPgAAO0AAAAG/AADWvwAAgb4AAD7AAAAWvQAAVL8AAN2/AACEvwAAuL8AAFTAAACuPwAAtj4AACM/AAARvwAAg0EAAEk/AAB4vwAAjz4AADu/AADIvwAA6b4AABY/AACjvwAAM78AAJ6/AAA3wAAAgL8AAMO+AAAMvwAA+T8AAIk/AAAEwAAAdj4AAAbAAAAAQA==", "world": 2, "sampling": [7656741360828666302, 0.7, 40, 0.99, 0.0], "position": 6586, "count": 48, "stats": [], "full": false, "token": 43} +{"logits": "q76Av0URDUGYHyrA+CF6QUxzasHMF6fAXoiGwNe0R0FvHTPBHhpLwTiWH8Hy+YPB658UwS9OG0EJ14LBXOVHQOftGEE8QrJB+nbFwEbTTcFbZLK+ivXpQIFHUMG3jANBUQIUQLkaQkDbZRdBYULxwHz+hcCPPm1An5K6PtryV72XU7NB4faLP6KsBkF5QoRAK50kQONnrEDkAbK/NpD7wKzQC8Dt8CK/O3IbwOPg5z6HVIXAfdrvwCQkA8AdF8bAdVuVwfhdZEGDhuTAl++eQBqbzMCUtutA0vlWwMSZk8BZKKdAxmRCwb+SkECX34LAdsDSQPYoGT8b0i7AIUBQwC6DEUGzDCPAzR9pQUIAJcGz15tAW5osQJLp2z72k0fBbSvaQI4DG7+MswJBbzZSQJUz5j6xTYTArARivznkJsHfiE1BRwLeQOCTqr4LjxhBPE64QO0QyUC4a5pAtyeSwfk+EUGpGFRBN3r8QLHBgcAxAZo+Fbb7P+ZiYsAXQ4xBszrPQNUJl0C5hehABDg0wa9qUMBF6HFA1tkawRyF3MD/aVrAyek8vygkREGdbJS/fo8twUv6J0GNEIDA+8z6wB7U8j1KOb4+dwjrQBh0h0F6Mo+/h9hYQA7+DcFLhfBAzQIIwTnykT+91pNAeOdrwSIf/7/6ikHAIggxvyR6oEH9JQFAERTrQAtjAkHP9RXAZ0BpwLxweb649wXBkBo7wNCyOkE1wqNAlK9cvxiFocCpXiXBUzt5QLpTFrw6lO4/+zTjwEsfL8BzNNU/NYRZwYL9GcAvklRBO5ODwannYMCIG0LBu4GkQOvATkFxCFVBIwaoPa/0ysDXws6/sYWTQTtGYL/usCfBiLg6QGI12UAwjVJBU6P8v5OGpcAet/C/qaE5PrGQgEDGXYfBHM8HwVMvXT54tVpAd7InwNuiP8GBOLjA39M2wH0uAsB2Q8k/xiViQbJj1r/920fBDZdlPtt/SEGUpps/rUfPwIhJREBXtObAkvG7vx6W7kCcVsG/gHvhQIv6IMFx21pBySxXwEYlDr/kiPlAUqVpQAupR78ogkO/wtxwQZl5t79b7hXBYCKuwBmryr/QSwnAYJsCwbog10BTFEFA2j7vQDUtREBYosBAAjK2QE/ZzUBtk2pA37nzQIv49MA1hNnAewZnwdVCEsDCgne/2WQNQYYkqUDNSde/PlP+wChTCUEroAFBH/uFv/QjksB/ZLBA1G/FPgMyI0FP25jAwycaQOJlOUBrhTxAtjguwLvNxEAq9XfAAfRdwGE9MkDmhBxBTkZJQN/pJ8Co18A9ak2mv4xehcHz/TbBnYAyQY7gMUGLn4ZBryZCwcQJ+T+2pgDB0rUEQeIVqsAo5p4/fgx6vzghC0DDWCXBQFaPQCAaEkE47KbA+TqlQGMm6sAttGLB5cQgQRfxm0DNcGM/IEAWP7LV1z6XlShBna5CwNuHib+ro60/P3hbQNNUsMB5zMlA3jOBwX9W28AZrjBBCdLav7Eu4MDkPyfA/TKhQCQ4iUDVp7M+WlubQI8VEEDxUizBttrjQDlkgMAYI0JACBUXwZUfDsGS3MNAzbXOwJYZw79LOg3BnEGDwZEf2MAMl4DAq733QIPjecHDJhbBOzhvQBOHfEAc5uG/xgArwEdIZEAzp/FALTzzvpuXA8DR/YtALbqvQJNdrUBNEQLBetuNwLFIWMBqrQnBfgGHwfSkQUF8PSnBUcP9P0Huw8BUUAdBeLe3wMT+HT67rAHAxcjEPpj52MBSveXAgCTyv/rCjUE9Qv++cCIwQC7GEsEA+4JAb1SiQB7RdkBmitq/72b4v7jfLMCdhIRBEUNIwIR0HT75axpBiyP/P/qeEkFgyka/dnmEQKHLtMA47f6+eqo0P1e0ZcBUj67AkZFtPjBfgMBjSW3ABljNwASltL8i/hzBsuCPPwvn1z/NdvZA3ZCtvxMeKUCr9cxAKvAjv6WuiMDaZ4bAQ4YoQcDIUkCKAvc/0/kXQeqH7z/hPVPB042+P7lcjsBfP6++L6AyQAgDp0E3+yfASQo9QBhOAcF2eoa/hsINQVUHlD96psNAL6kWQaKtBMGBJO09y2OaQDZHSMGCtABBeHLUP1O9NMFDDmNAUy1lwJPJA8Hsap1ADK3AwO87R0FQ/ZRAZGX9v/HHQMA1bynAGrCgwIlOhEDvFVHAbDoRQZnEEMEP9hLBsNRDQFuLOcE5f57ADoM6wdUEeMFLc+jAk2IOQesnZ0EzbmDBaQ8Uvom4hj84Ld7AehbMv/ulV78dHIO/xowRwbtDo8AdzWNBQ5i1QKJci8D4k09BgOhcQQMPEj8r4Zs/ksnswECS5L8dtfw/iNMVQGp2FUFcTFFBxEmdwOFim8D60pzAq9uOwOFCM7+lYwDBo7mLQAU8P8FsF8PAFQw9wIetScAST+7Bn55gwe+37b/kfWpANuPcQANrrUDVZL0/66gSQLYLgcBLlZPBWkLDv3W8t8Dlkru/UawoQdV1QcHsRTDBmIyUwBQwmr/4ta9ANyFLQZSqEsHckbNAQjYjwCmbmMCr7HfAS1mnQdxtqEDicErBw06dQcJYd0C1uVq/uBeawPLZDMAaRCBBn6jTQK5/dT/LrZFBQ4RJwUo3dUFXnALB/ObjwCbOF8CQOgTBOQidwLyWsMDyihBBmSpxwcqjWEEis4E+VCWlQZzkMUGEtzvBscsxP8rjeUCqbr3AFfYdwDCuQ7/fqPfAF7iDQWTBL0DVISBAOAhWQazp1MC3KgHB4s1EwcSixEBg3/FAnmqtvzDR68C2FJI/tgQIwPRRCsCxkplAYjgMQbSF68CwskJAAnL6QLAWBb8e3YC9UFkCQfAbikH3IX9A0tT0wAZurL8ZTDFC29FXwVReH8BxZpPApoByQLDKZkFoOWfBzF8OwPkqx8CNcT3B+UrUQCotF79tezVBIkk4vw0pcMB5FjFAPyw3QToDf8BlH9+/qu4zwZiGzb4/GNtA9tcnQdmI6EDf2D/Al8TbP1hyf8AzblLBOxYNwf7z4r06z1JAgi2IP+TjXMG0ieM+YN4ewMmy8MCNSXnBULNEQQ6+ysDDmDnBv5Y4P8w4JsAiYrzAIAxWwWGjEsFDWOK/up73QKAzPsDBTBxAcLXpP5u/Pr8NVodAiDIIP/Hd6kA6r5FAXzDFPgNIBMHobiO/NyzvwJ9g8D4UqZPA", "world": 2, "sampling": [6631579302532087282, 1.0, 20, 0.8, 0.0], "position": 938757, "count": 28, "stats": [], "full": false, "token": 539} +{"logits": "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", "world": 1, "sampling": [7392006164395191210, 0.6, 0, 0.99, 0.0], "position": 286748, "count": 512, "stats": [[4.625, 1.3766838608834289]], "full": false, "token": 450} +{"logits": "AACUwAAAwD4AAOjAAAC2PwAAokAAAJHAAABqPwAApEAAACTAAADtvwAA978AAFG/AAAAwQAAp0AAABHAAACMPwAAtD8AAK5AAADsvwAA48AAAKG/AADTPwAAub4AABjAAADDPwAAA0AAAGM/AADQQAAA+b8AAKrAAAD5PwAARD8AAH4+AACXwAAAzj8AAB1AAAC9PwAARsEAACe/AADUvgAAzj8AAFe/AAD8PQAA/78AAIzAAADqPwAAzjwAACXAAAA+PwAAJ0AAAC/AAABawAAAF0AAAMtAAACCwAAA8sAAAEC/AAD8PwAAAkEAAEDAAABpwAAAnr8AAG9AAACJwA==", "world": 2, "sampling": [518284061310874269, 0.7, 40, 1.0, 0.0], "position": 200138, "count": 48, "stats": [], "full": false, "token": 58} +{"logits": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", "world": 2, "sampling": [4993815241061751863, 1.0, 1, 1.0, 0.0], "position": 1003467, "count": 9, "stats": [], "full": false, "token": 0} +{"logits": "AADQvwAAz78AALxAAADewAAAl8AAAJ1AAAC4PwAA8D8AAM8/AADSvwAAKMAAAB0+AABGPwAAO78AAK9AAAARQAAAZMAAAGNAAAAXPwAAC70AAJS/AABtwAAAQ0AAAFjAAABOPwAAQj8AABlAAABHvgAA08AAADs8AABXQAAAHkAAAEI/AAAOwAAAPsAAAI/AAADgvwAAVz8AALpAAADxPwAAhMAAALZAAAAIQQAAoEAAABtAAACUwAAAjEAAAFhAAACWvgAAgMAAAEbAAAD5PwAAI0AAAATAAAC2QAAAyz8AAJtAAAA+wAAAp0AAAChAAABkQAAA278AAPLAAACbwA==", "world": 1, "sampling": null, "position": 562000, "count": 9, "stats": [], "full": false, "token": 42} +{"logits": "AAC2wAAAPEEAALw/AADbwAAAHz4AAElAAADlwAAAksAAAFzAAAB1vwAAbsAAAPDAAAC3QAAAh8AAAAtAAACVPwAA3sAAALpAAAAzwQAABsAAACG/AADhPwAAgL8AAIbAAACPvwAA4r4AACzAAACHPwAAOUAAAAbBAABXQAAAhsAAAENAAAAdwAAAFT4AAKbAAABtwAAA6z8AAF/AAACavgAACj8AAJG/AAC6QAAAf8AAAJLAAACUwAAAOsAAAA/AAACiQAAA6sAAACtAAAAiQAAAAcEAAEjAAADbvQAA6D4AAI+/AACrQAAAQEAAAHnAAACMvwAAir4AAHlAAAD3vw==", "world": 2, "sampling": [5434649641093504565, 1.3, 0, 0.99, 0.0], "position": 830999, "count": 64, "stats": [[11.75, 1.0265952221748047], [5.8125, 3.0392240405819764]], "full": false, "token": 1} +{"logits": "AABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwA==", "world": 2, "sampling": [5963777490652472632, 1.3, 0, 0.95, 0.0], "position": 902490, "count": 64, "stats": [[-3.0, 32.0], [-3.0, 32.0]], "full": true, "token": 45} +{"logits": "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", "world": 2, "sampling": [7481488219091479144, 1.0, 0, 0.5, 0.0], "position": 548474, "count": 512, "stats": [[11.1875, 2.3146075892010383], [12.1875, 1.5901552098893346]], "full": false, "token": 897} +{"logits": "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", "world": 2, "sampling": [5418269547465709453, 0.6, 0, 1.0, 0.05], "position": 314413, "count": 1000, "stats": [], "full": false, "token": 153} +{"logits": "bYGFQN07/j7VWsJA53APQa0dgkD/V/vAJYa1v6fcBEFIi4c9SO9SQDA84z5tcF7ALwSZwDSXhUDzfcK+36wWP+JQLcGVgZ0/js+PwKSOi0DcXaO/92AoQRG6D0DFf9M/k7dSwBSxhj1dLqvATz0QQRUPk79fgW5A67QGQGMdHD/U01TAHrq8Pwiv4T8S1Be9FZKKQF6Ji0AMcWDAgLTbwLSKO8Dyj/hAshGgQLlJosCQ7TlAqEcVv3a02j/xPRJBFpa3QKPkVsClLpo/6vGqQMmlIECInpJAkE5MQJdAHD91iQfBR4efQLMDxMBSdFk/mQ+GQB+I7D8xmFZA2r/MQPsNuT9vkiA+0qDdQFB/QkBzA7g+DMQdQMe5sL98WrrAQ7/yv8ZtlUB3WaE/qZQmQKWKrj9ZYpG/qgOlPq7Fu0Awkuk+y2k3QMg+gkAIhr3A44OfPxUuMT/6Lta/iAy1Pk8i7T/m0WVAyz0lP64ZnkAb17m/LGeDQMCnj8BI49O/sBlpQJSswsAXDeC/WQzLv6Ia80CEXqNAxa1TwK6730Dvb2c/rfbyP2OZMkEcpvG+eBfCwGt/cMCy/ok/m2onv0ehQcDfB2BAUdecvyvRQkDHWuG/dsIVvyKoBkDSy8M/AxE7wD7RfEBSXuE/4QULwPN+BUGAXKnAXF8JQG65W0DhuDJAZrVLPxZvz0BuQwbBIQvVvzePez+PFJE/u4O7P9LsiMAPBDTB9aXZP4bNkD92wQTB+xOuQOaCHEDT0Ri+18XNQNI8Cz/mFYZAPc36QBivf8Bv0S1A8lo2QMoHWD+DVGDAf6iPQOfpTkAAZIrAr1iHwKuRqMC2Zhq/EdyzP+F8vz9G0gdA5ocoPoW3zj+1ByfAFUuHQKmDvr+IHvVAny45wNE1CcB3w7S+U1L8PyTXG0Dr4ge/5NYWv8ZRDEFachlBTPu2PzzinkAFGj/ABqwKQPRPSEC30ibA3b+qP3asjkDrwADA90HRP6YI4EDFMlRACjpHwDt/g0DL8wLBCKsjwBmttMCImhLA+E8vQLanY8AUmAbBRFQBQKnaHsBZe3++IbUxv2eVMb8V89BAh8hzQI8W8kCSNz2//8qwQFKIv7+pZSa/k8mXwOGQxr9aOpLA/bGYQLZ4rj9QuCTALZaLP4RNVMCadAdAKJ2HvwKcSsC0tuU/FZmYQJA9yb9LXk5AtSMEwerFrEBQEi9BXcIpQCqKQ0BFGcC+LyZpv2igtcCcKybAOPOIQCVNfUBITgI95KFZQK9O/z/hiZ9AiWOMv6wHecBo/CDBDyo3vzKfrL+tbL3A7qNUQDk3jb+KWrA+hbR1QAGVHMBV38TAFV2kP7IjbED/0ONAN6mTwFtnC7/cDIlA/+I6wGeamsCswrI/tWYnwASxkr9CKi1ACYB8wFduL8APA3DABhl6P8783r1ixVw/U/iOvwNLAkHmV6g/50SwQR7ZhMBIhCXA9sgBv72EpkDRXJPACyzwv8q5IcFq1FLANxq/v/WuI0Be66xAj0CjQM9U7T+oK4vACbUFv2BOSkAkBsa/TuZeP6HP+D8I2YJA2c5AwNk89z+htM6/4IQjQJcl8L8/fdVAYQ4EQYV+DEDZONA/Ea/AwE4dAEBuWcK8D/6fP3kKZ0BpT/LAeo9qQNM2dcAtxPBAXUj0wBAtxL/UaZpAUzurv2ycQD+rKl3AsL3dv3ACtT/rRT3A31DhvvQb6sD7dFRAM46KwIspob9gJp0/iVGVQIQa9D5kx6K/8RdkQPC1NEBG9pm/ThS/v5lp4b5dHGhATsSsPk9Piz/KRCQ/mIZLQPNeUb9mDxm/ve+AP4u4PMBglNs/uKz7wOvMXkB9QXPAtW5XQJIeBcHZhj1A1C/Hv8LC2r/bWh/Ax0jpwB7dYEA2ab1AGH2owOP4bb/ksqjAaPYDPxTQr0DVvfY+GOqHQPk9wr8QRB5AVDlqwPRzaz/VggvAbLRXQAZ3rj9Bi1g/hPysP+EbX0A0srK/YOKewDWNlUAsb3jAKfQwwL+7s8DeAmZArqgSwF4AUkDuMsm/fxJAQFfzkr/Z6QFBRxYMwEXFLUCtsfJAtm2JQBq5KEBnFXfAB1CUv2NxUr5sxlXAluldwNxwYT9OA5FAAMV9Pz+NIr/2H7/Aqz3av7LGuEDIhdQ/7noUwYWT8b/M4qVAVeX4P21un7828LC/Ht8nwBAtosDXgsDA4p++wNSVHcBuk25AvMjrQFyenMDDfZjA8DKYwAvoMsDemQNBs0+CwGE9B0GBDwTB1B4OwD9nJz5t+nVAXlOrv4TQwD90LyhBjeQ8P6lSG8D7/F5AvWPPQGl/TsDNpxnAQ3jRQFyUL7/dwDfAeoWeQL8VhkBHHsK/24+Cv+yeMkGHLvK/T0rGv31HKcDTGgvBnDtvQK9i3r9HRAJBXdcHQEM05UCkOobADEbVP3/ucMC3irW+++WaQBukgEDTNyvBCIi3P9RT48BgeLNAMmjcwLsjBMCQLAfAGGxdPzftcT9NH+E/w8epQHGIiz8XFP0/qgUmQFRFRUCb9VY+UGtFwO0MgkC3gA6/VSSkwPqnTEB52K+/szs2P76Yzz8L1xpAcq6zP9w3A8Bbk09A9mDPwBWVukAbOR5AwNCfwO7o18CLVJK+GAFowAKUesD+J4/AYnXJv00P9kBmQkpAXmNnPzprHD+7OBBAd+6ZvvBhMT2K4qHA53l7QOYJJUHeuNDAIzcWwVZDZ0CaezrA2nWKv6UGrj9DRiXAWuCGvqNpf7zesTpAm/koQFVG90ASpHTAQuQ9wBucS0ARtg3B5AIpwH6pJ8A6n0bAxXeuP/8yXL/RGhdAXTo6P9ToEkHwk/w9vFTjv09WFEDiQMi/zoJGQA28lMBU99e/TEnSv5q3QD9qBPXA53EMQPZgR0A2x57AVYYOQBaqSUAHzSLA2hOLP75sRkAtz+q/GLFdQHK/JEDob0zAnKNRwQtHqMAIaCrAi1RowOG2u0DnpYu/MDHgQFbJPz+ydTxAQlmrwGoIfECGKDBAJ0ZjQOXDdb9OibjAmM6CPntl1j+FeQ3BOInswGNYJsGiUUW/9aakwEuw5cC7FQO/cCoLQFhlKMAbwIVAB0ahwD50mcDKNfu8CQm5wPwaM8ALyks/ZSL2P8sz0j/Xch29+P6IvzyHAcHLLANArovWvo7XgMARfajA6Ef1vqkMjz6Upsc/9AvFvylikD6jda3Aaahtvxcjnr+3/e0/Q/aswKLTDMCaFBnB5lCZQCWaPz/aB3O/XqJbwBmoRD9OQ53A+9FwwMSXmD+Cc7bA0d+pwM2tgz7LxgpACe8kwMOWdECTAlXADSMgQbBBVb8jKdA/WzvCwPjnEr9AjFfAJJkBQeCTysDh6RG/e10SwNQR28AZSF7ATOaMv7bCIb8xzmfA5eq2P0poIEC0+R5Alr6wQPqkkMCCi3s/XOK6v9USPj9mTAFAC4n5wKLj48ACKMTAiDudv/K2G8HmY2TAHeOTPwHpAcByST5APWChwD+hr8Bd9y7ASXoOwcSnMb6LjZ5Ab9htP62RMD12tXHAJTLEv4UIAMC6PDpApjFxQNKOJcBhypXAiM8NPRxKl8C3nlK/W8elwNT+0b/yKbS9rZJPwAPF4cBCoGfA31aUwEFfLED6ihJATn0yP8eCRUADUQfA6jbxvjLadj4bRptAMTd2QO4xwMDXR+rALcyrvwgklUAb6pLAx9IAwAgKakCIBq8/j5b2P1Ln40BvZK4+pmIvQEmVkMB+0/I/HkaEwElYsL40DOm+u1N/wHlXcEB3hxLBegmOwKQtZ74oQkJAVDvlvlvwwEB7GFvAJMsBQMf7EcDDFGtAc29YwJX5aL86Q8G+3xtawKER1T9Cfd+/ldexP1lKBb/xufm+4ImZP8iej0A63sc+Ts0OwcGwoEA67StAp87Hvy3qCL86lFPARrUhPhd7zkCtf4ZATxEev9Qjyb93GGvAXK+ewM5mgj3xC+A/2vYIwCZBE8GTHQpAWSj5wBsiXEA/VnPAPfyCvycXhT4b/p1A/7sqvhH5yL//E7LAn9EkwFWCvMBJ9LfAyhYNQBWYFb+7NGo/pDcGQNp2H0DPbulA7Bo2wE1ppb93eKpA+kSTwKjjyEAppojADSuOP6cynEA/7La/PHLUv6Hqnj8s47a9FgDrQA+8gsAR2SK/WK1IwKdFy7/2G2k/6omhvwLPHT8qZQtAM2qsPyggX0CCpIXAy1uCwNMBIMBbnYs/tx9wv+fbwUAIjsQ/ML14wBGToUAkc2lAlgjLQJen10ALf2u8MfkxwOoquj0bZrXAsvHVwFYo4UB0MZXAedcFQHAdPj/ll5q/sNC0P/6v80CRztlANvTRwBwKoz9Rk53A8DOwwAz8bMAM/RpAll6yPxclpUCyyIO/ixGewL5fL0A/TEpAZDTtP3RxRcCbqhxBqm2nPgnlV8CpmHxAKE0xQBbN00BIhhDAPT0LwP8dI7+SFErAUbIkv19ocEA9K2K+MErpv7fPG8B0ZX1A/EXxQEYRE8BoxOE/MN7BPlV4l7/91Bs/KbgNQAXW978d2/pAwNJiQEsy4kD+/c0/kFWTvw8uHUAwzTjArx/lv+aVET81labAHIPIQHM0lkA1JEo/YhCGQKA4OUDheWRAUTsjQS/4aD82w+c/GyKWPoQ6EsBOfOrAN9lVwEkDKL6u8Pu/0qLhvwsYfUADEc5A2YXAQKfjgT9a9VjABJCtwMrVmD7Dh5VAfLX/QG4p1z/G55m+n2OHwOh0N0Bm0ZfArQSEwLh46j/GdNM8d0GowJRK4T8QXr+/3X9vQCvqHT8Elsw/HrrLvyzlgEBvaGpAGQ8cwE9+Jz9LzYU/qaCeQJnXvD8shVLARUP7P7+6sL6XWR6/a04APzQY0UCX5+y+NXNjQX+cjz4VgC0/CGvAv47Pc78VDou/9LvaPgI4Xj/afOS/pBdiv/xKx8BKuuQ/Kd7GQBaXRz9skN4/GCOEv48GAMETdys/M1afwPB/UcBwHAi/Mr89v0Hju7+wq2lAAATvv1u+r75KuF4++D2Jv9sk0D8FM4JAk9ClvgSyPr520lhAO5pmv6KB0cARdwBBpxqvQAEwfz8yXyC85QabwKFptL8FpCbA1K6ovyjNYUDlPpLA8Pukv45RDcCvZEC+xmsLwJwXor8zs3fAW2sfwGyUCMBzuAY/apCDv4koyUCY4WFAVCkVP8oin0AAdjPAqc/Av6Q1/j1LIJM/qmCUwCvD0UCOrPc+QzmrQEo2QkB5kWfAkhNDwJ27osBVvlC/7BZhQDiltUCsJ0tAb94zQNn5yb94zSE/37KFvzZ2WUCSS55AABCLwA==", "world": 2, "sampling": [2625373553994134435, 0.7, 40, 0.5, 0.05], "position": 445265, "count": 48, "stats": [], "full": false, "token": 273} +{"logits": "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", "world": 1, "sampling": [8425167578815465254, 0.7, 40, 0.95, 0.0], "position": 58018, "count": 48, "stats": [], "full": false, "token": 222} +{"logits": "AABBvwAARUAAAB3AAACvvwAAhMAAAIg+AADUPwAAkD8AAEm/AAAjQAAASb4AAKBAAAA/QAAAwj4AAEVCAACCPwAAqr8AANq/AAD5QAAANcAAAAVBAADAwAAAnz8AAAxAAAAwvwAAFj0AAAXBAADbwAAAUj8AAI2/AADSQAAAKb4AACa+AABxwAAAnL8AAB5AAAALQAAAFkAAAJc/AABhvwAAx0AAAIzAAACNQAAAUb8AANQ/AABXQAAAQsAAAM7AAACLPwAAiUAAABhBAACwwAAAEcAAACtAAAC0vwAA7D0AABpAAAB8vgAAm0AAADjAAAASQAAAIUAAAH49AAAfwA==", "world": 1, "sampling": [5357569952068313574, 1.3, 20, 0.95, 0.0], "position": 1045072, "count": 28, "stats": [], "full": false, "token": 14} +{"logits": "qI2UvnLbq77Fr0q+/UsXPr6JSL55HZC+DR4Zv/Bw+77XF28/lVkAv65ByT53Rvu+o8FyQbIenr4zIGI+CEyXvaduiD5N9Mo/nc8dv+gFL75adZk+iNxoPrtzkz6zDPm93olsvsXLkT83s5w+QkUzvkvDnr0x63q/bt27vaiYlz6V4By+3obdPl6S2L6nm/S+A1LbPvQGTT9A9hO+J/LjvsJVTT9V0YW/yBi6vic/cL4nKYY/bxw8PzicCj8K5sC9gTU2P7PDGT/DBqc9SUNKPLTl4D10EwC+ZpC3PkHWmT78dMG+OmIqPjbcYT8dqDK+WHZUv4DpFL7PBxU/4oTmvg==", "world": 2, "sampling": [2367400135866267260, 0.3, 0, 0.8, 0.0], "position": 242064, "count": 64, "stats": [[15.172274589538574, 1.0], [1.0481308698654175, 4.536551805373671]], "full": false, "token": 12} +{"logits": "kfvCPzWLG0Dq0h7AFP5dwE6igz+MjbE/KD8rv8AqTL/56CjAyxdxPwgVAUBoXNA+IigXwLQxe76rBDK/DXoFPQSXGcCwB++/foACwFQo7z+1vN69L+YvwCA2C8AQ1cK/choAQDUtNMAG0XlAcsmcP39WO8A4798+NFdAQP8iNb/CYzdArlYNQGlFd0D1zYG/HvdsQDrGLcCfwytAWRQ7wAa6KMBBwxlA0fY5wC2JCkDVPUc/JFSuvvLi5L/U9lHArW2+v1fdZMCQ8zZA+nbfv9qgkL/f7zpALwAdv3hnJT+Ms/y/6P48P0ixyj9xrgfANCppv2rQEb9n66w/27wYQFZLor90EdA/8V6dv0jPdUDpIjrAPclDwKzbCz86uzK/JVIAv7Qxh8AGhhZAaQ3SPIZERsCc0PO+JT8wv032rr1BObw/+9ePPyscVz2clBg/2kyev2arK78Fypm/fiEawE/KA8Ak+Uw/xqIFQLTWhcApFQVAdZ9lQBAjYT86JAu/AkyHv+PthD/D0mc/+nVcvaq0Ir9Icn0+rR0BwM07QMADLBc/hWADQMvxOr8k86c/pEeJwPVlCr8a5Q4+1t1rwMvOE7+A3VNAR26OPwB8Qb1xJYPAzmeevyPbO8DJ8Xg/UvREQEfLMcC4YDfA+w0PQEHOkr/nMdS/G2I3wLwv376lpOU/3FixQJHumr849EPApSYawOxh8L4G1VjAgSbzPkcM878iRJXA7n8fQISFX7+Devg/5/1Hv2VFq0AXKMS/bKw1QJ1uWj8ilJM+8EiZv2huK0Aohuo/BhOKPxLyLkAcals8T1ylvgTZaL8svA9A7tcRvjXk8r7dW7q/uvIRPynxUEBAV9E+GKUvwGmiyb+h0eC+HkXrv+GdisALY+Q/TnXZvwr7YkD/vuU/S2eevsFnGMBjz3U/zooOwAdZhz6YcNG/4AZdQNn2G78EQL++wgFnv1ZlU8CCckNA4zAQPzEGlL5mhNu/W9wyP9KfHT7gs7jAqvK4v0LFRb74qHe/xUgtQLJCp7/HoXe9tFGxP13dXL7wNiA/sWwzQOGRCkBAYYw/q9x+wNYB8D8dnAe/CHEfwNF3ZsDskeg/pBtbv05FXT9hd1FAyDSFwJmsz75hdfO/ZrvoPyKzmz+sYsI/avdyP5lA87+vkW2/lEQRP1cV2z+Dh7a+ImBOwLHhjr+OJQG/rR4/P60Xgz7UGoc/UvwVP6ECgz8JwzfA7Uy+vVEFJD9XpTDA11kzwKlOiMBUWxvARnlCP0KKHTu5LPm/UGYPQB2Je8B6+E4+UopRwFJXF8C1bvm/2XljQHTwgD+srBHAd1cuwI83sL+Qxna/IhDcvy15tz/2EXk/pNibPoxrG8BonQg/d8lPvkaDAUBNaa28T+8rwJ/4DUAJn9I/2zbNv8PyyD9boM0/l+d0wMK7DT/EPKy/wn+sv5PG8b9Sn1TAK3AFQFY/R78luYo/rMr5v7qSmr/ZDynAUTipvuPhLMANcyq//PxgQBwsn76M1jfA9iq3PtgZ+L/kPaW/8Vg9QPUhGb+U3QPA4X8DQP8Obr9JFPa+/WMqv5u9RsDwgog/ttukvl305j2Amok/QJ1Nuy6gBr+CwkG/ExCKv2VyUcBQoQ/AhFDiP68/Pj/XH7u/wKMUP94BxD+Cp5o+Nfy3wCKHcT/fBCPAVvFPP3R7UcAWpQm/skuMP2brob+ViFM/d86fwJxhtD7G2ni8yoYAQAnmnL+RdH6+r35+QBZSQ0AoRRjAA6hkQDCBgL4vlxw/snErvy5Svb9dcApAr30VwKKV6j8m0j0/G+0dwKceOT5WvHo/d1waQJkNlL5y+ya+xL0Fv6I6K0D+aps/+ZbDv5xZnEDZiEK/IWmBQK/SDsBzhte/bZe8vrTTCECaLQtAC3SyPSBhasCmHMS/TOIbQPkPUj8WHOO/jCsdvp4Y9b/T4ETAOSdVv36CCMAHIas+73OzP+fiG8BmbL2//I5zwLdzoj84NOE/mw7Mv3Nshz7leOQ8/e4dwN1iKsDz0RPAGCEZP0dB/r6Xrym+qH9aPoVUIbueBppAcbDKPpJj7L/THYw+sFeTvwVU9j/klzrA3lXDvUObML+eJpQ//AOlP7UfgsBOyVg+I/UPQFm1I0CIozJAFQVbQH2LmD8LIPG/5VvQvkLq3z7C53FAv7vbPlb5gb/y3STAQDxtwBws2j4PXgE/wubovc6hVkCOj+2+9O+6PxwOx7/AkkY98gyXPNFPj7+ZqTPAo34JwNfVTED2LfS/7IT8Py4N274uSbG/5vR/P58ywj9jvEA/bD0RPwhhpT4Ki+m/MEXjPwjLmj7df4Q+p2OYPwkE/z4uUJ/AHMccv+IRnL+IQNw/IbqVP6YZesBUrQtA+9pxQKe+QcDtPg6+zFpiOjJRDsDO6RZAGf77PyMCsz+Ja+C+5qP2v8OMKD9Ue0s+KhidvmQpvD8U3BW/MiAkQHOoF0Drlwy/CugVv2ceSr5abcC/3njEPjPgIkDTEi7A84AxP8eRjz//C4s/KxUwwAVyGEA+Alk9UJZOP+lRMEAZQYs+GwQDQOioN0CppL6/vvkqPpxwpz8VBe++GIuIQFs2BEBIUxi/U/JtwG8gLcCWdNc/PyAYP9vnQD5iLKq/WjGev/wvRMARua+/jvatP1j7AsBlhW1AP7aiPhY1hT9C1pm/QNfFP6uspT2uEZO/KXKfPumIQ8DH1q8/lKn/PpRrJj9J7US/IzT5vz4Lf0DjDgPAtJWCwHUWkb687zA/w3mfP5pwpz9ovmM/3oiLQAPOBr4eU40/Px8BPrSkyz+ihWS/Dm7EPkRhqb+/H2RAIrGivj1sFEC8D5U/A5z0PkfY8L/k6kbAiAoZwJL2Sz+YAA1AfRfcPpKdMr8OegZApeRNwIEOQsBXEVs+6WoOPmph0b4OVWO/jO8VP2N9MT90Xu09t3ToP03lir95pxO+TVqtPl55Lz8Q1KM+iTmPPlFDZ74Mq6Q+s40+P+MnXUD6M5PA4osNwMu7PkBODOC+w/dMPrreFkDl4ljAZWKKP/GFib5fL9a/Olwkv0yWFsAew0I/X0XtP1QMU78IQmu/IuA0wBQ5F7/5EvW/os5mQD0rjj4jjPy+8OIlQBZM5T9y/US/bgrjPR0FuL8fTSXAoRikP7YjvD9I3AM/4QF2PzYmP74e1vu/EW97P3XwasCTVyfA", "world": 2, "sampling": null, "position": 969991, "count": 9, "stats": [], "full": false, "token": 129} +{"logits": "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", "world": 2, "sampling": [1461127527142604147, 0.6, 20, 0.95, 0.0], "position": 371154, "count": 28, "stats": [], "full": false, "token": 595} +{"logits": "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", "world": 2, "sampling": [8685758559592131211, 0.6, 40, 0.8, 0.0], "position": 534119, "count": 48, "stats": [], "full": false, "token": 119} +{"logits": "AACCwAAA6r8AAIdAAADyPwAA8j8AAARAAAAAQAAAj0AAAI3AAADnPwAAmMAAAPDAAABIQQAAhD8AAKk/AACaPwAAcEAAAGvAAACNQAAAaD4AALg/AAAtQAAA178AAOU+AABAQAAAKkAAAItAAAD9PgAAU0AAANxAAABewAAAH0AAAI7AAADfvwAAk8AAAALBAABHwAAAEMAAALJAAAAfQQAAYkAAAPg/AAAPwQAAnz8AABbBAADxvwAA3kAAAABBAAAIQAAAfMAAAPpAAACsPwAAi0AAABM/AAB9wAAALEEAAD3AAADxPwAAzsAAAM9AAAAowAAAC0AAADBAAAAmwA==", "world": 1, "sampling": null, "position": 724174, "count": 9, "stats": [], "full": false, "token": 12} +{"logits": "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", "world": 2, "sampling": null, "position": 965398, "count": 9, "stats": [], "full": false, "token": 983} +{"logits": "AACDPgAAfD8AAME9AACEvQAABD4AANI+AAAMPQAAez4AAKs+AACFPgAAKD8AANk+AAAZvQAAkL4AAKy8AADyPQAAxb4AANe+AAAmvwAA4r0AADg/AAADvwAAtz4AAA2+AAAmPgAA0LwAACy+AABGvAAAdz0AAAM/AACovgAAmzsAAKBAAAA1PwAAAT0AAN2+AADHvgAAzL0AAMC+AAD0vgAApj4AAKY+AAAnPwAAMj0AANM+AABrPgAAjz8AAFe9AAAVPwAAtL4AAB+/AADWvgAACr8AACK+AAA6vgAA1r4AAJ2+AAC/vQAAzj4AACy/AADhPAAA0D4AAM8+AAAfPQ==", "world": 1, "sampling": [6804413911003786672, 0.6, 0, 0.95, 0.0], "position": 109482, "count": 64, "stats": [[5.0, 1.0206347723437528]], "full": false, "token": 32} +{"logits": "AABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwA==", "world": 1, "sampling": [6478053289746747272, 0.7, 0, 0.95, 0.05], "position": 353015, "count": 512, "stats": [[-3.0, 1000.0]], "full": true, "token": 115} +{"logits": "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", "world": 2, "sampling": [2099696756756371483, 1.0, 20, 0.5, 0.0], "position": 263903, "count": 28, "stats": [], "full": false, "token": 158} +{"logits": "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", "world": 2, "sampling": [6215649667398155264, 1.3, 0, 0.8, 0.0], "position": 1004274, "count": 512, "stats": [[5.96875, 10.978004497127214], [6.53125, 7.190097263301747]], "full": false, "token": 310} +{"logits": "AAAwPwAAsMAAAOzAAAAOQAAAfr8AAOs+AACNPwAAwj4AADTAAACJwAAAoL8AAOrAAABjwAAAT0AAAIu+AAAZwAAAmsAAAANAAADvvQAAg8AAAAc/AAAmQAAAzj8AALG/AAAxQAAAvD4AAPi/AABSPwAATsAAAEg/AACswAAAVMAAAJ1AAADjQAAAmb8AAFlAAAC0QAAAF0AAAPm/AADpwAAAhL8AAKtAAADRwAAAcb4AAAa/AABQvwAAhD8AAJDAAAA5QAAAqT8AAFTAAAABvwAAY78AAPzAAADBvwAASkAAABS+AAAqwAAABUEAAO+/AABvQAAAEsAAAErAAACNvg==", "world": 2, "sampling": [7998374234821231961, 0.7, 40, 0.95, 0.05], "position": 288759, "count": 48, "stats": [], "full": false, "token": 58} +{"logits": "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", "world": 1, "sampling": [2430019737075096401, 1.0, 1, 0.5, 0.05], "position": 798386, "count": 9, "stats": [], "full": false, "token": 889} +{"logits": "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", "world": 2, "sampling": [3815267943762999074, 0.7, 40, 0.8, 0.05], "position": 762698, "count": 48, "stats": [], "full": false, "token": 669} +{"logits": "AACcQQAAqsEAACC/AAD9QAAABcAAAAA/AAB4QAAAv0AAAM1AAACyQAAAQsEAAALBAAB9wAAAa0AAAAI/AAD0QAAAkMAAAG9BAABXwQAAQ8AAANVAAADywAAA/UAAAATBAADWwAAAwMAAAIJAAACTQQAA1L8AABU+AAARQQAAxsAAAItAAAA0vwAAGMEAAEzAAADlPgAAn0EAALVAAAAjwQAAecAAAF3AAAB9wAAAn0AAAG/AAAADQQAAgsAAAI1AAADRQAAAtb8AAAtBAACMwAAAukAAACXAAADmPwAA6kAAABLAAAB3QAAA1cAAAArBAAAQPwAAZ0EAADpBAAAYQAAAQkEAAI1AAADkQAAAksAAAAJBAABgwAAAbEAAAIC/AAB5QAAAosAAAPW/AAC4wQAAdcEAAGJBAACMQAAASL8AAJ3AAADPvgAAZb8AAJ1AAADQwAAALUAAAI+/AADxQAAA50AAAB+/AADAvwAAcEAAALLAAAAzQAAAfjwAACZBAAAuQQAAEkEAAEJBAADKPwAAzD8AAHpBAACCQAAAI8EAAGzAAAC6QQAAPkAAAAbBAADyvwAAxcAAAFzAAADCQAAAR8EAAA9BAAD4vQAA+UAAAFTBAAATwAAAhEAAAJLAAABlwQAAxz4AAOI/AACAPwAA2UAAAEBAAACRQQAAq0AAAO1AAAAYwQAAAkAAAI2/AAAGQQAADMEAADrBAAC6QAAAiz8AAKXBAABLwQAAZkEAADg/AAAsQAAA7cAAAOFAAADjwAAA90AAADLBAACvvwAABEAAALVAAAA4wQAArUAAANXAAAAdQQAAtUEAAEnBAADvwAAABsEAADRAAADLwAAAEEAAAKw/AAA8PwAAxcAAACtBAADbPwAACUAAAIy/AABVQQAA1MAAAHRAAAA3wQAAA0IAAJ9AAAArQQAAO8EAAB3AAADDQAAAsUAAAGVAAAB2vwAAhMEAAEdAAACeQAAAqUAAAP3AAAAfQQAAIMEAAGjBAABAwQAAg8AAAOy/AAAGQAAAC0EAAKNAAAAewQAAgMEAAJ2/AAC2QAAAZb8AAH5AAABkQQAAr8AAADVAAACGwQAA+T8AAIbAAACDPwAA370AAP1AAABdwQAAW8EAAO7AAABswAAA3cAAAGJBAACPvwAAKcEAAPC+AACcwQAAvkAAAJA/AAA0wAAAykAAAHNAAADLQQAARr4AAEfAAAA/wAAAKcAAAC3BAAB+QQAAUMAAAIhAAACOwQAAGsAAAOfAAAAiQQAAmD8AAE9AAAA6vwAAzcAAAFhBAAA/wQAA578AAINAAADKwAAAaz8AAGhAAAAowQAA9D8AABy/AAC0PwAACcEAALBAAADWwAAAmcAAABtBAABuQQAAlMEAACK/AABOwQAAsT8AABxBAACewAAAScAAAPY+AADCwAAAGkEAANo/AAAswQAAYL8AAJ/AAAABQQAA8b4AALfAAABuQQAAl0EAAP3AAADKwAAA28AAAEnBAAA3QAAATkEAANnAAAAiwQAA3sAAAAO/AABwQQAAtkAAANVAAABLQQAA1MAAAJlAAACDwQAAfT8AAHVAAAAqQQAABkEAAJfAAAAuQAAAOkAAAM5AAAAgwQAAbMEAALy/AABBPwAAFcEAAOlAAAAGwQAALsEAAB1BAADXQAAAs0AAAKm/AADOQAAAcr8AABFAAACSvgAAiz8AABTBAAArQQAAl0AAAJDAAAA5QAAA90AAALXAAAD3PQAACMAAAEfBAADmPgAAn8AAACHBAAA8QQAAEsEAAMPAAAAUQQAAtUAAAABAAAAFwQAAYMAAAJVAAAAIwQAAN0EAAIi8AACLwQAAMcEAAMHAAAAqwQAAvUAAAHlBAAABvwAAA8EAAJ7AAAAOwQAAHb4AACFBAAArQAAAMMAAANK+AACPwQAABUEAABjBAADcPwAACEEAAJLBAACnvgAAsb4AAOhAAABZQQAARUEAABvAAADuQAAALMAAAA/BAAC/vwAAtMAAAKLAAACUwAAAQMEAAJTAAACOwQAA1cAAAFXAAACPQQAALEEAANbAAADlvwAAk8AAACPBAAB9wQAAK0EAAMLAAAB9wAAAusAAAITAAAAOwAAA+cAAAGVBAABtvgAA9b8AAARBAAC6QAAAk0AAAANAAABJwQAAyUAAABLAAACvPwAAV8AAABnBAADjQAAAfr8AAHNAAAC0wAAAJsEAAFpBAADevwAAbUEAAMi+AAAdwAAA0kEAAMo9AACCQAAAmEEAAFLAAAA2QQAAU8AAAKbAAACcPwAAg0AAAJNAAABuQAAABkAAAIJAAAABQAAA00AAANrAAAAQQAAAHsAAAJxAAAB4wAAAh8EAAJQ/AADxvwAADUEAACBBAAAyQQAA+EAAAM0+AADlQAAA8sAAABlBAACXwAAASEEAAMo/AACPwAAAVUAAAHDAAACKQAAAr0AAAChAAAAyQQAAL0EAAM1AAACDwAAAxsAAAKa/AADnwAAAs78AAIFAAACGwAAAQ0EAAA6/AAASQAAAS8EAABnBAADOQAAAPr8AAIzBAADoQAAAjkAAABzAAADkQAAAR0AAAMdAAAAYwQAAYcEAAMHAAABbwQAAJD8AALE/AAB8QAAAGUAAAAxBAACuQAAAmMEAANo/AAA/wQAATsAAAC3BAADlPwAArb0AAD1BAADgQAAAnkAAAK4/AABjQAAAPMAAAPXAAACfvwAAFD8AABHBAAAsQQAAPEAAAFlBAAAZwAAAW0AAALfAAAAXQAAAW8AAAPi/AAAlQAAAOEEAAGbBAADkvwAAFUEAAG7BAACNwAAAxEAAAP4/AACDPgAApEEAAO1AAAA9wQAAb0AAAB7BAAC+QAAAG8EAAO0/AAALPwAAhEEAAJRAAACPPwAApsEAAATBAADfQAAA8kAAANhAAACyvwAAFsEAAIa+AABOQAAAo8EAAPm/AADcPwAAc78AAFM/AAC9QAAAjL8AAJFBAAANQAAA3EAAAAJBAACBwQAAkEAAADZBAACIPwAAkT8AAMdBAADKQAAAPMEAAGJBAABIQQAAYEAAAOTAAAABPwAALMEAABpBAAAhwQAAE0AAAMfAAADUQAAA6j8AAM9AAAAkQQAADMAAAPI+AAD9vgAANcAAAAJBAABswAAAFL8AAOY/AABfwAAAAsEAALnAAADCvwAAmb0AALFA", "world": 2, "sampling": [5145662704008434226, 0.3, 0, 0.5, 0.05], "position": 860490, "count": 512, "stats": [[32.75, 1.0000000000210865], [26.25, 1.010220774994256]], "full": false, "token": 172} +{"logits": "AABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDA", "world": 1, "sampling": [2459017176156714999, 0.3, 1, 0.8, 0.05], "position": 303845, "count": 9, "stats": [], "full": false, "token": 0} +{"logits": "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", "world": 2, "sampling": [6903875733063697708, 0.3, 0, 0.95, 0.05], "position": 353298, "count": 512, "stats": [[1.65625, 10.191327576082081], [4.09375, 1.0022944918200811]], "full": false, "token": 877} +{"logits": "AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPw==", "world": 1, "sampling": null, "position": 944562, "count": 9, "stats": [], "full": false, "token": 0} +{"logits": "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", "world": 2, "sampling": [5490760785568055123, 0.3, 0, 1.0, 0.0], "position": 889086, "count": 1000, "stats": [], "full": false, "token": 4} +{"logits": "AABewQAA6EAAAFLAAACAQAAAYUAAALQ/AABEQAAAJD8AACzAAADIQAAAxr8AAAlBAADiQAAAJcEAAK5AAAAmQQAASMEAAIJAAABFQQAAJcEAAIpAAACiPwAA2UAAAJA/AADwQAAAuD4AAL7AAABLwQAAKMEAADzAAABTQAAAnkAAADbBAACIwAAARMEAAGfBAABqwQAAuD0AABq/AADEQAAAiEAAAOPAAABOwQAAMMEAAMlAAAAnwAAAyj8AAIlAAABjwAAAuUAAAARBAAAcwQAAQz8AAOrAAAAqwQAAfMEAACXBAAC0wAAAFMEAAF1AAAAxwAAARUEAAKvAAACYvw==", "world": 2, "sampling": null, "position": 1005214, "count": 9, "stats": [], "full": false, "token": 18} +{"logits": "AAAqQAAAK8EAAAC/AADNPwAAGcEAAOvAAACOwAAAlMAAAI8/AAA2PwAAor8AAETBAADfPwAAar8AAAPAAACfwAAAJ8AAAEpBAADMPwAAiUAAAOlAAABHQAAAlcAAAGzBAAAFwQAAJUEAAIQ/AAD/QAAAcUAAAEfAAAAxwAAAGkEAAK1AAACOwAAA80AAAE3AAAAgwQAAdr8AAGtBAAAgQAAAIr8AAOG/AACJwQAAS8AAAIVAAAAQQQAAIcEAAA7AAACbPwAAGUEAABbAAACvQAAAb0AAAI9AAACEQAAAjL8AAIA+AACvvwAAvr8AAEJBAABPQQAACEEAAK5AAAB8QA==", "world": 1, "sampling": [20991884269343105, 1.0, 20, 0.5, 0.0], "position": 871405, "count": 28, "stats": [], "full": false, "token": 38} +{"logits": "AAAZQAAA2b8AADJBAABZQAAAhsAAABPBAACtQAAAi8EAAI5AAACgQAAAe8AAABfBAABjwQAAUEEAAB3BAADaQAAAccAAAB9BAAAQQQAAwcAAAHHAAADhQAAA4MAAAK5AAACCwAAAHkEAAPFAAADlQAAAPb8AAL09AABgwAAARsAAAHlAAAALQQAA4D8AAFjBAADtPwAAMz8AAHdAAABrQQAArkAAAHdAAACFwAAAWcAAANO/AAAmQQAAIEAAAG3BAAANwQAA10AAACBBAACQQAAAU8AAACXBAAA9QAAAF0EAAJDAAACVPwAALMAAABO/AACnvwAAmMEAAMRAAAAoQQAAwcAAAB5BAABSQQAAP8AAABnAAADjvgAAI8AAAAlBAAByQAAA78AAAHLBAACcQAAAJkEAAEbBAAA+QQAAgEAAAIXAAACRwAAA08AAAAfBAAAxwQAALMEAAA1AAABFwQAArr8AANnAAACpwAAAjMAAADLAAACVQAAAK8EAAEHAAABPQQAApcAAAHo/AACRQAAAi8AAAObAAAAwwQAAGsEAABNBAAAnQAAAtz8AAADBAAAjQQAAqkAAAAdBAACgQAAAC78AAIxAAAB6QQAAF0AAABJBAABdQQAANMEAAPA/AABMwQAAb0EAAK1AAAAdwQAAJcEAAEhAAAA6QQAA/0AAAMhAAACPQAAAB0EAABjBAAB3wAAAPsAAAOhAAADHQAAAl8AAANRAAAAbwAAAksAAAItBAAATwQAAc8EAAOpAAABNQQAABL8AAAbAAABxQQAAkMAAAKW/AAAZwQAACcEAAIZBAABFQQAAA0EAAIVAAACawQAAs0AAACPAAAADQQAAFMAAAAFBAADdPwAAl8AAAApBAABmPwAABcEAAGo/AAAjQAAA8cAAAEzBAABGwQAAtUAAAI1BAABHwAAAO0EAABvAAABSvwAAAcEAAF++AADJQAAAHkEAAOlAAAAOQQAApD8AADlAAAASQQAA0UAAAKXAAAA5wQAA0kAAACVBAABIQQAAFMEAAJZAAAB8wAAAYsAAAAzBAAAHQAAA0T8AAH/BAAC4QAAAlsEAAODAAAD/wAAANMEAABpBAADBwAAAu8AAAJs/AAC9QAAAyMEAACvBAADCPwAAgUAAAB++AAAyQQAAEUEAAC5AAAA9QAAAsr8AAFjBAACDQAAApcAAAKRAAAAmvgAAHEAAAFs9AABawAAAuD8AABLBAABKwQAA0D8AAAm+AABqQAAArMAAAGtAAAAUwQAALEEAACNBAACcPgAA7EAAAD7AAAAbQQAAeUAAAOw/AABLQAAATb4AALFAAAAtQAAAxsAAAMI/AACowAAAFsEAAD9BAABaQQAAnUEAAEfAAADQQAAAVEAAAHs/AABdQQAAUcAAABfBAADFQAAAOkIAAIFAAABEPwAA4MAAAIy+AABxQAAAv8AAAAZBAAB5QAAACEEAAOZAAAAsQAAALkEAAJDBAACiQAAA1sAAAD3AAAC0QAAAK8EAAJM9AACPQQAAxsAAADnBAAD3PgAAhsAAAM9AAAAcwQAAvj8AAGjAAACvQAAAtEAAAKnAAABwQAAApEAAAEdBAADivQAA0r8AAEhAAABPwQAAML8AAJ/AAACpQAAAnMEAAFjAAAB6wAAAIb8AANPAAAARPgAANUEAACHAAACfvwAA974AABJAAABTwAAAh8EAAIxBAAD7PwAAtT8AAP/AAAA/wQAAf0AAAAlBAABXPgAAvUAAAMVBAAAbQQAAyEAAAJrAAAAnwQAAjsAAAEPBAAAFQAAAAkEAAINAAAApPwAAUkAAABg/AABcQAAAhcAAAPZAAADkQAAAjMAAAINAAAAewAAAxkAAAIRAAACxwAAACb4AAMc/AABHPwAAdEEAAFnBAAA+wAAAoEAAABe/AAASwQAA9z8AAMK/AADEwAAA/z8AALzAAABgQAAANb8AAAhBAAA/QQAAPEEAAJ/AAACQPwAAh78AAIpAAAA8wAAAvEAAALNAAABqwAAAaUEAAB9AAACqvgAAokAAAFXBAAC6QAAACMAAAMG/AACSPwAABcEAAKFBAACMvwAAGMEAAIQ+AAAFQQAANEEAADNBAACRQAAApkAAAC1AAABgwAAAzD4AALTBAABiwAAAJ0EAACc+AACrPwAAiMAAAChBAAAXQQAAGEEAAKPAAADZwAAAskAAAJU/AADIvwAA0cAAADo+AACYPwAA9j8AAIBAAACswAAAJUAAADzBAACRQAAA8j8AAMJAAAA4QAAAv0AAAF/AAAAMQAAAP0EAAJTAAABawQAA6UAAADHAAACVvwAAiT0AADA9AAA8wAAAwr8AAHFAAACoQAAArMAAAIdAAACPPwAAOMEAANE/AACwwAAAiMAAAAM/AADQwAAA6kAAABdAAACOwQAAU8EAAJ0+AABivgAALEEAADbAAAAjQQAAVsEAALXAAABZwQAAhkAAAN/AAAAWwAAAiEEAAD3AAABvPwAAM8EAAIvBAABJQQAAbMEAAMLAAABBwQAAk8AAANPAAACdPgAA3T4AACtBAABAQQAA7sAAAHDBAAAYQAAAIEEAABHBAAA2PgAAacEAACrBAACrPwAAnUAAAHVBAAAqwQAAwL4AAH9AAAD4wAAA578AAI8/AADHPgAA4EAAAIhAAABaQQAALcEAAD/AAAD9QAAAoj8AAAhBAADeQAAAOkEAAJQ/AAB+wAAAKUEAANRAAABdQAAAG8EAANZAAAAJQAAAk78AAPrAAADYwAAAsUAAAClAAACbQAAAXsAAADtAAAA1QAAATcEAAFpBAACFvwAAAMAAADZBAACIwAAApUAAAA4+AABQwAAACr8AANM9AAARQAAAW8EAAERAAAAswQAARb4AAFnBAAAuQQAA4j8AAO7AAAArPwAACUEAAMLAAAB4vgAAK78AAJlAAAB1QAAAhb4AACpBAAD4QAAAlr8AAJq/AAB8QAAA48AAAChBAAA9PQAArsEAACnAAAB2QAAAH0AAAALAAAA3QQAAoEAAAL5AAABtQQAADsEAAD3AAACpwAAAVMAAAEHBAAAFQQAAHkEAAJE/AAACQQAA/UAAACU/AAD1wAAAGEAAABbBAADJQAAASkEAAOXAAABDwAAAxL8AAB5BAAA3QAAAm8EAAI3AAACzwAAA+kAAAFzAAACXQAAAEMEAACZB", "world": 2, "sampling": [2867416116879948863, 0.7, 0, 0.95, 0.0], "position": 449519, "count": 512, "stats": [[46.5, 1.0], [24.625, 1.0016763849719161]], "full": false, "token": 265} +{"logits": "AABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwA==", "world": 2, "sampling": [4202703382188702481, 1.0, 0, 0.95, 0.05], "position": 583324, "count": 64, "stats": [[-3.0, 32.0], [-3.0, 32.0]], "full": true, "token": 32} +{"logits": "AABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDA", "world": 2, "sampling": [6128823139739893867, 1.3, 1, 0.5, 0.0], "position": 468428, "count": 9, "stats": [], "full": false, "token": 0} +{"logits": "AADbPgAAjD4AAIw+AACbvgAAIb4AADG/AAA7vwAAZD4AAOe+AAAPvwAACL0AAMw+AAAZvwAAt74AALS+AACMvgAAIz8AAL+9AAAoPwAAnr4AAAQ9AABtvwAAIEIAAEm/AAAWvgAAnL4AACM/AAATPwAAEb8AALE+AAAiPgAA6T4AAP09AAAxPgAAf74AANQ+AAAqPwAAHz8AAEa/AAALPgAA1L0AAPG+AAD6vgAAF70AAFO/AAB1vwAABT4AALa+AACWPgAAXT4AAJO+AADAPgAA974AACY/AABEvwAAVj0AAP6+AAByPgAAkr8AADk+AADgvgAAor4AAAQ+AADiPQ==", "world": 1, "sampling": [3120426452569638665, 1.0, 0, 0.5, 0.0], "position": 899876, "count": 64, "stats": [[40.0, 1.0000000000000002]], "full": false, "token": 22} +{"logits": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", "world": 2, "sampling": [1229521477456153372, 0.6, 0, 0.95, 0.05], "position": 97981, "count": 64, "stats": [[0.0, 32.0], [0.0, 32.0]], "full": true, "token": 43} +{"logits": "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", "world": 2, "sampling": null, "position": 1038074, "count": 9, "stats": [], "full": false, "token": 224} +{"logits": "v9EBwd44jMDyFLJA89C9v2v7u0C1r6ZAomVswL5gGsHN033AwDnnwAxvBsHlcjlAD48AQHkoUMDFwxlBw+tjPwrbF8B4GXM/BjRGQLAnyEBIoX0/AQ+HQKu/fj5bLhlARBK7QHCvzEB6/hhAHPe0QHBlAEHuE4O+fZbhv/aGokCI+pbAjoFiQM8bqT/4Kh6/HiUiwHLFH0DE+kpARUnDPylz00CKeMU+iftFP4Zh1cAYNh7AKghBP88HEcErJqO/v0PDwGiAU74QtodAB8iyQHP9Fz/78Dm/DldzwAxF1ECzcpU/emN3wE5E8b+cxoK/Ho9Av/XYj8A4KOVAbX00wA==", "world": 2, "sampling": [4159966750370523751, 0.3, 0, 0.5, 0.05], "position": 307211, "count": 64, "stats": [[9.610295295715332, 1.005114109512843], [7.161159515380859, 1.3356302032490186]], "full": false, "token": 14} +{"logits": "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", "world": 2, "sampling": [7548455046001259218, 1.3, 0, 0.5, 0.0], "position": 1257, "count": 512, "stats": [[6.15625, 14.882261696010945], [12.25, 1.1422802501714617]], "full": false, "token": 873} +{"logits": "AABCvwAAAD0AAHG/AADUvwAAz78AAEW6AAAQvQAAMEAAAFE/AAAlvgAAUT8AABy/AADzvwAAxj8AABLAAADgPwAAqT8AACa/AAD2vwAAzb0AAE4/AACQPgAAc74AACnAAADCvwAArL0AACtAAAAcvwAAWb4AAADAAAAtQAAATsAAAE8/AAA2QAAAG0AAAJo/AADkPwAAyT8AALO/AABNwAAACz0AAOM/AADXPwAARj8AAKC+AACMPwAAkb4AABS/AAA2wAAAuT8AAKM/AABtPwAAhr4AABw/AABbQAAArb8AACA/AACEQAAAD74AAIE+AACVPgAA0D8AALM+AAB9Pg==", "world": 2, "sampling": [4730011398637560265, 0.6, 0, 0.95, 0.05], "position": 619020, "count": 64, "stats": [[2.75, 3.4433525347301277], [4.125, 1.6367545173043907]], "full": false, "token": 7} +{"logits": "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", "world": 2, "sampling": [7584609200915961881, 0.3, 0, 0.99, 0.0], "position": 80603, "count": 512, "stats": [[53.5, 1.0], [22.125, 1.0029299296907133]], "full": false, "token": 210} +{"logits": "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", "world": 2, "sampling": [5864389120410712817, 1.0, 40, 0.5, 0.05], "position": 681265, "count": 48, "stats": [], "full": false, "token": 310} +{"logits": "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", "world": 2, "sampling": [3395343829114783657, 1.0, 40, 1.0, 0.0], "position": 569270, "count": 48, "stats": [], "full": false, "token": 273} +{"logits": "AADpwAAABsAAAExAAACaPwAAj8AAAIHAAADnvwAADcAAAJDAAACSPQAAtkAAABrAAACbwAAADMEAADDAAACqQAAAv0AAAChAAAD2wAAAl8AAADjAAAAVQAAAQEAAAKE/AAAGQAAAcD8AAODAAADHwAAArT8AANI/AACTPwAAkD8AAE9AAAB1QAAAm8AAALBAAABpQAAAc0AAAMPAAADIQAAAUEAAAO+8AADlPwAAN8AAAL09AACLQAAAikAAAL8/AADzPgAAscAAAGBAAACjwAAAsUAAAOY/AADFwAAAfkAAAD9AAAB+PwAAAcAAAL2/AADlQAAAID4AAK1AAADOwAAAE0EAAIFAAAAVQAAAwr0AALM9AADevwAAm8AAACTAAAAwwAAAQcAAAJdAAAC4vwAAX8AAAPhAAACbQAAAcb8AAKZAAACdPwAAkz8AALQ+AABavgAAgz8AACBAAADHPwAAesAAAK/AAADAwAAAnT8AADzAAABVPwAA8kAAADfAAACZQAAAKL8AAJQ/AACivwAAHUAAAEdAAAC8vwAATsAAAPc/AAAuQAAARkAAACBBAACVQAAAAUAAAJg9AADhvwAA4r4AAEU/AAD7vgAAbMAAAIm/AAAEwQAAq78AAGjAAACsPwAAlcAAAERAAAANQQAAD8EAANq8AAAXQAAAsb4AAAnAAAB6PgAA40AAALzAAABZvwAAi0AAAMxAAACsQAAAwEAAABFAAAB+QAAA88AAABc+AADEwAAAWEAAAOJAAADUPgAAnsAAAMNAAADyvwAA378AANhAAABJwAAAbUAAAGRAAACwQAAA5cAAALFAAADGvwAASEAAABZAAAA5QAAAckAAAJ9AAAD+QAAATr4AAA1AAABowAAA+r8AAKC+AACZwAAA1kAAAHK+AABAwAAAnsAAAAXAAABevwAAK78AAHJAAABWwQAAR8AAAAZAAACswAAA1cAAAIw/AACDwAAADr8AAAFBAADGvwAAH74AABpAAADwQAAA/D8AABXBAAAAvwAAQb8AAJRAAABdQAAAGUAAAIzAAACJQAAAMsAAAFZAAAASvwAAVsAAAHA/AADhwAAAbz8AACFBAADuvwAAHMEAAIm/AAA+PwAAzL4AADZAAADgPwAAt8AAACy/AABlPwAAoj8AACU/AAAgwAAArUAAAO89AACtQAAApsAAAOa/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", "world": 1, "sampling": [4000035118823430088, 1.0, 0, 0.95, 0.0], "position": 23031, "count": 512, "stats": [[34.0, 1.0000000008769803]], "full": false, "token": 807} +{"logits": "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", "world": 2, "sampling": null, "position": 294963, "count": 9, "stats": [], "full": false, "token": 25} +{"logits": "AACkvQAA8r8AAFS+AABDQAAA9r8AAJO/AACPPwAAxz8AAHnAAACZwAAAQcAAAHo/AAB0PwAAIcAAAC5AAAAswAAAisAAACJAAACePgAAzr8AABnAAAD+vwAAjj8AAJTAAABlPwAAiL8AAGvAAACOQAAAFr4AAJ4/AACOwAAAXsAAAHc/AACVQAAAA8AAAHe/AAAcwQAA0UAAAGo9AAAsPwAAxz8AADnBAAB8wAAA6L8AAFtAAABlQAAAEMAAACVAAABxPgAAur8AAE1AAACjQAAA48AAAIq9AAC6vgAAV0EAABNAAAAjQQAAHkAAACK/AACYQAAAucAAAHY9AADNwA==", "world": 2, "sampling": [2856416796140742043, 0.6, 0, 0.5, 0.0], "position": 601029, "count": 64, "stats": [[4.4375, 1.229741410357921], [13.4375, 1.0044540297078044]], "full": false, "token": 55} +{"logits": "AADnPwAAHj8AAHdAAABxvQAAwr4AADY/AAAFQAAAM8AAAATAAACqvwAAhD8AABLAAADYvAAAqD8AAEQ/AADhPwAAYEAAAPM+AABHwAAAbz4AAEJAAAB5PwAA+78AAOE/AADMvwAAN0AAAIy/AACmvgAAQz4AABA/AACovQAAL0AAAOq/AAAxPwAACD8AAB8/AACcvwAA1r8AAO+/AADkPgAA8r8AADa/AAAjPwAAmcAAAOC/AAD4PwAANb4AAPA/AAAfvwAAwUAAAMU/AAAAPwAA4b4AAHPAAADmvwAAcr8AAIvAAABxwAAAHMAAABI/AAAdQAAAPT8AAIlAAABdPw==", "world": 2, "sampling": [9217561501749610865, 0.7, 0, 0.99, 0.0], "position": 661941, "count": 64, "stats": [[3.859375, 2.71540021204212], [6.03125, 1.0998423858153252]], "full": false, "token": 49} +{"logits": "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", "world": 2, "sampling": [8244723098003781942, 1.3, 0, 0.95, 0.0], "position": 238049, "count": 512, "stats": [[6.665753364562988, 6.095503478811157], [4.647669792175293, 19.781889460370135]], "full": false, "token": 445} +{"logits": "H5n/wOItjMFNF7FAzF+KwFqpB8BvcNbAmJEVQbCqmMBUeT3BDnbqwIG1qL+R6/DA1mlwQL44Gj1oGP2/9Cqbv/0427/p8aRAzwJywDV8NkDMk5pA3J6RQGwjb7+6mVpAd85BwWIqKcGnM4pAFu3NwIW2UsHVcpjAPWoKwfYRWUEkOBrAHPmEwWeztkCpohZB9vVFPqKM2T7jJtjA9NzpQCjUIUBd+3BB7B12wIwusMAoQvNA0fGTvw4wtcBX8b7A6Q8UwevHiT5PQiLBDc0lwIvF1kAjVoo+IAV0PzYS5T+RJnpA+OwiQdDzJcGUjZnAUdmkv9lwucAW/yRBlAI7QA==", "world": 2, "sampling": [905695517614187785, 1.0, 1, 0.95, 0.0], "position": 1003392, "count": 9, "stats": [], "full": false, "token": 41} +{"logits": "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", "world": 2, "sampling": null, "position": 432112, "count": 9, "stats": [], "full": false, "token": 606} +{"logits": "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", "world": 2, "sampling": [7887376645644738170, 1.3, 40, 0.99, 0.0], "position": 1041063, "count": 48, "stats": [], "full": false, "token": 996} +{"logits": "AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPwAAwD8AAMA/AADAPw==", "world": 2, "sampling": null, "position": 4779, "count": 9, "stats": [], "full": false, "token": 0} +{"logits": "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", "world": 1, "sampling": [8439187422879683867, 1.0, 0, 0.95, 0.0], "position": 950565, "count": 512, "stats": [[21.375, 4.0138612730172865]], "full": false, "token": 767} +{"logits": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", "world": 1, "sampling": [3209754826167320062, 0.3, 20, 0.95, 0.0], "position": 239270, "count": 28, "stats": [], "full": false, "token": 0} +{"logits": "AACywAAAksAAAFHAAAD9wAAAmsAAAEw/AAAzwQAA1z8AAHxAAABXwAAAYMAAAARBAADOvwAAt74AAKI+AACEwAAAuT8AALTAAAB/QAAAWkAAAAtAAAAeQAAA+UAAAPe/AAB1QAAALb8AAKW/AABOQAAAvD8AAHrAAABKQAAAYL8AACJAAACYwAAA9j8AAARBAAA6PwAADcAAAEpAAABVQAAAFcAAAOi+AAAlwAAACL8AAIVAAACiwAAAMkAAAGs+AADeQAAAukAAACDAAACXQAAArkAAAIG9AACFvwAAn78AAPW+AACrQAAAlL4AAEPAAAB+wAAAnMAAAIXAAAAFwA==", "world": 2, "sampling": [5967432252765234952, 1.0, 0, 0.8, 0.0], "position": 711758, "count": 64, "stats": [[8.25, 1.696372156705591], [8.25, 1.542026432788309]], "full": false, "token": 35} +{"logits": "/SokwWoUFsEPeGVACi0NQVXQgcAyP7dAqUV2wNVvQMFcOdfAKiUIwcNRI0H6IrbAha/YP+oSi0BYZkFBGr7hQG+YOUFLcvRA/lacQb5cEMHwTihBmvP2v4WUSMHPy05BjDmAv7dUS8Ci4JNAxTPrwE8QnkBrVL1AO5aIQHohVcA7Jhw/ix1pwGpFFEBQAOvAgNC1wNEsqcDdGA5AVYBgQaRLSUGEBghAX/hEwZFJ7sCXNA9A96qkQPUHJUBU2D/BJjMFwTTA08A5q1XAHefJv88hXcAlvRvBi1x9QRKo/T0C8yTB3tgdwS34R0A3DrfA3yANwbRVLsExotm+g/5yQQ==", "world": 2, "sampling": [996828895719986860, 1.3, 40, 0.8, 0.05], "position": 973342, "count": 48, "stats": [], "full": false, "token": 18} +{"logits": "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", "world": 2, "sampling": [1603949915040393188, 1.0, 0, 1.0, 0.0], "position": 789562, "count": 600, "stats": [], "full": false, "token": 137} +{"logits": "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", "world": 1, "sampling": [4131847952902142327, 0.7, 20, 0.95, 0.0], "position": 431724, "count": 28, "stats": [], "full": false, "token": 581} +{"logits": "AADxPwAAFD8AAMu/AABQwAAAGkAAAKA/AADRPwAAFUAAABjAAACmPwAA2D4AAGk/AAAmQAAAsT8AAPC+AADgPgAAMMAAAKU/AACEPQAA0j4AACy/AAAsPwAAD0AAAAa/AAAhwAAAScAAACbAAADCQAAABj8AAO8/AADNPgAAgr8AAPI/AABiPgAASL8AAIg/AAC9vwAAHr8AAIdAAAACwAAAQUAAACw/AAC6vwAAG74AALjAAADEPwAAhD4AAE0/AAAwPgAAc8AAAI0/AACSPgAAi78AAAhAAACcvwAAUUAAAMa/AAAuvwAADT8AANi/AACaQAAA4z8AABNAAABVwAAAKT8AAKq/AABxQAAA174AAJW/AAArQAAAwL8AAHU/AAAbvgAAeEAAAAa/AADIvgAAEcAAAAZAAAAwQAAAiz8AAF4/AABYwAAAGEAAAAc/AAAlwAAA8j8AACHAAAB1wAAAPL8AAIC/AAAawAAAwL8AAOk/AACqwAAAaT8AAFK+AADKvwAAET4AACg+AACtwAAAhD8AADU+AABBwAAAgb8AAKa/AAAwQAAAG8AAAB9AAAAnPwAAaj8AADPAAAAMQAAAAr8AAPM+AACEQAAAHj4AAPI/AAC2vwAAC0AAAB5AAABwwAAAMsAAAO4+AACjPgAAUz4AAMC9AABNQAAAjboAABDAAAC2PgAAtz4AABNAAACkPwAACkAAABi+AACLvgAAtL0AAIG/AAA6PwAAoL8AAH8/AABWvgAAh78AAJc/AACrwAAAVL8AAIDAAADZPwAAE78AANI+AABhvwAAGj8AAGE/AABwwAAApT8AAOK+AADPPQAAa78AAMg/AAASPgAAvD8AAOe/AAChvgAAeT8AAJg/AADwPwAAVj0AAKG/AADivwAAJMAAAL+/AADpPgAAGj8AAIVAAABBvwAAj74AAGBAAACHvwAAg74AAAhAAAA6wAAAij8AAP2/AADPPwAA3r8AAF+/AADpvgAAQkAAADzAAABLwAAA1cAAAEXAAADqvgAAx74AAAu/AAAFvwAA3T8AAAbAAAAOwAAAMEAAADFAAAACwAAAXz8AACtAAADhPwAALUAAANY+AAAzwAAAIkAAAJu/AACTwAAAUT8AACtAAADbvgAAS0AAACq/AADgPwAAe74AAJQ/AADDvgAAkT4AAMS9AABlPwAA5b8AALA+AABDvgAAtb8AACBAAABHvwAAL8AAAJNAAAD7PgAAR0AAAOg/AABnvwAAwL8AAKO/AABiQAAALD8AAIe+AACNPgAATkAAAII/AADGPgAApT8AAHA/AAArvwAACcAAADbAAADUvwAAR8AAAFC+AAA0wAAABT8AACU/AAAsvwAAGkAAAPc/AACBvwAAzbwAADlAAAAxvwAAUb8AAJC+AABxvgAAkj8AAFXAAABhwAAA0z4AABvAAAC2vwAA1r8AAC7AAABBvwAA1r4AAIO+AABQPwAAWL8AANE/AADTPwAA7b0AAL4+AABKPwAAN0AAAB8+AADdvgAAHkAAAAC/AAB9PwAAdL8AAA3AAABGwAAAS78AAPW9AAAIwAAAUb8AALpAAADiPgAAor8AAB3AAADNwAAAZ0AAAPa+AAAQQAAA8T4AAG1AAACqvwAAzD8AAE+/AACVvwAA1j8AAMk/AADLvwAAnT8AANy/AAB8QAAA978AAKm/AAAbwAAATD8AABM/AACLPwAAN8AAALI/AABkwAAA/b8AAKJAAAA0QAAALj8AAJG/AACgvwAA4z4AANW+AAAXPwAAuj8AAD6/AAARPwAAtL8AABHAAACMvwAAp78AAMg9AACVPwAAZMAAACpAAAAXwAAAd0AAAIXAAADSvwAAFD8AAHA9AADlvwAAhr8AAHe/AACFPgAAxL8AAJo/AAAKPgAAAUAAAFc/AACePwAAekAAAH/AAABLQAAAqT8AAFs/AABSQAAARUAAAF8+AABOQAAAS78AACNAAAD0vwAAlEAAAL0+AADXvgAAYT4AAFK/AAADQAAADUAAAIO/AAAWQAAA3z8AAI/AAAC+vwAAiD8AAAs/AAAqQAAAiD8AAKPAAADuPwAAnj8AABTAAAAJvwAAsz8AAIc/AACJPgAAgb8AAKY/AADlvwAABD4AANq/AAC5PwAAA74AAJc+AACrvwAAIkAAAARAAACsvwAATj8AAILAAAA/QAAAKsAAAIxAAADZvwAAVD8AABW/AAAwwAAACr8AAD6/AADOPgAAIz8AADo/AACfPgAAe8AAAGk/AAB5QAAAhUAAAFy/AABYvgAAob4AAOM+AADBvgAABcAAAA1AAAAXwAAAdT8AAB1AAACnPwAAGMAAABU+AABkvwAAFUAAACfAAAB/vwAAFsAAACTAAACbwAAAGkAAABRAAAAIQAAAXj8AAJ8/AAApQAAAzr8AAJC+AABdvwAARD8AADW/AAB5QAAAWsAAABVAAACoPwAAyL0AAJ++AAAbvgAALcAAADrAAABxPgAA6z8AACa/AACPQAAAd8AAANa/AAAPvwAA5L8AAGTAAADtvwAAzD0AAIe/AAALPwAA8L8AAMY+AAAGvwAAnUAAAMc/AABkvwAA6b4AAEc/AAAJwAAAXj4AAGC/AABvvgAA274AAEdAAAB6PwAAOEAAAC6/AAAMvwAAKsAAAOY/AABPvQAADsAAAIo/AAALwAAAD74AAIw/AAAKPgAAub0AAJ+/AAA9QAAA/78AADM+AABzQAAAQ0AAACA/AAC1PQAAgb4AAC4+AAAdwAAAiEAAAA2/AADgPwAAH8AAAG4/AAAWQAAAKUAAAIY/AADIPwAA4r4AAAxAAABIvwAAyz8AAOC/AACpPgAAQkAAAF/AAACWvwAAUj8AABVAAABfwAAADD8AAP4+AADcvwAAD8AAACLAAADnvwAAtz4AAAs/AAAGPgAA3b8AAI7AAAAiwAAAz74AANu/AADJPwAAZUAAAA6+AABavwAAFMAAAJQ/AACmvQAAyD8AAJu/AAC9PwAAGb8AAHu/AACJPwAARsAAACZAAAClvwAA+r8AAFY/AADkPgAAir4AAP6/AACGPwAAkz8AAK0/AADtvwAA6z8AAP4/AACUPwAAST8AAAXAAAD/vwAAbz4AABu/AAA/vwAAlb8AAPE/AAAQvwAAAr4AAFtAAAAZwAAABcAAAJRAAAC0vwAAIL8AAPS/AACNvwAAcsAAAJm+AABMQAAAG8AAAHC+AAAjPwAA/T8AAAtAAAATvwAAREAAADHAAABzPgAA2D8AAA9AAAAjvwAAqr8AAKQ/AACEvwAAZ8AAAHHAAABHwAAAZ8AAAPg/AAAMwAAAZ8AAAFM/AAAWQAAAjj8AALm/AABNPwAAg78AABy/AACevwAArT4AANm+AAAcwAAABr8AAHu+AAAWwAAAuD8AAMc/AAD2OwAACUAAAKI+AADAvwAAcz8AACPAAADUvwAAE0AAAAM/AACgwAAAI78AABfAAACowAAA0L4AAIO+AAD1vgAA+j8AAABAAAC4vQAAHD0AABlAAABgwAAAV0AAAOe/AADqPgAAuT8AAINAAAD2PgAA7j4AALq/AABKvwAA4z4AAEm/AAA0QAAAicAAACzAAADJvgAATj4AANG/AABxwAAAI74AACs/AAAEvgAABMAAAFlAAAAuQAAAxz4AAEI/AABDQAAAe74AAC+/AACowAAAxb0AAJQ/AACwvwAAH74AALU/AAB7wAAAB8AAAHxAAABYvAAAekAAABXAAACvwAAAlT8AAJ+9AAAOwAAAg78AAOw/AACYPwAAFMAAAJO/AAD8vAAA+z4AAABAAAA3PgAAsL4AANM/AABTPwAAB0AAAJK+AADnPwAAdj4AAPO/AAAVwAAA5L8AAPC/AAByPgAAZz0AALE/AACUPwAAhMAAAKk/AACrvwAAOEAAAGHAAAD4vwAAHEAAAG0/AAB6PwAAt78AAAzAAADPPgAA674AAGW/AAA2QAAASUAAABRAAAD1PwAANMAAAA6/AADIPgAAXL4AAIA/AACCwAAAKT4AAG3AAACzvwAADEAAAFjAAABWwAAATEAAAHE/AABEQAAAO78AACLAAAAPwAAA7D8AAFU/AADdPwAAhj8AAIc/AAAvvwAASL8AAKe+AAC3PQAAqEAAAM++AACjvwAAor8AAG6/AABfwAAA+L8AAC0/AAABvwAApj8AAGi/AADUPgAAUL8AANY/AABpPwAABz8AACW/AAA6QAAAv7wAADBAAAAJwAAAUsAAAO4+AAATwAAAEL8AAIk/AACFvwAAFEAAAHu/AADzvgAAKr8AAKU/AABavwAAdb8AAFpAAABEPwAAzT0AADE/AACWQAAAAr8AAAG/AAA4vwAA/z4AABfAAACuvwAA4T8AAFPAAAD3vgAAnj8AAO4/AADRPwAAhD8AAJ2+AAA4wAAAMUAAAEE/AADRvwAAwz8AAKzAAAC7PwAAMT8AAHg/AAALwAAACMAAAIu/AAAcvwAAAkAAAPK/AACCQAAA/D8AABm+AAAlPgAAEsAAADZAAAAMwAAAS8AAAFq/AAD2PgAAKT4AAI89AAAlwAAAQMAAAEzAAAAsQAAALz4AALG+AAC/QAAAAkAAABrAAAApwAAAT0AAANM/AADZvwAAgz8AAFA+AAC2vwAAvT8AACfAAADivgAA6j8AAMY/AAAZPwAAF0AAAH1AAACjPwAA6r8AALk/AACCPQAAoz8AAEFAAACrvwAAOr8AAIW/AADNPwAAEUAAAAhAAAAovwAAfr8AAFhAAAALPwAAEcAAAIY/AADcPwAAr0AAAKQ+AACLvQAA3D4AAM09AADYPgAA7D4AAJHAAADJvwAAy78AAEy/AAA2vwAAd70AAJk+AACGQAAAQr8AAPs+AABAwAAAoz4AABG+AAA7wAAAtD8AAMw/AAARwAAA3D8AAIW7AACTwAAAgL8AAIS+AADhPwAAXL4AACA/AADCPwAARr8AAJy+AAD7PwAADMAAAExAAAC1PwAA8D8AABpAAACFvQAAmj8AAEc+AADsvgAAWUAAAGC+AABkwAAAbUAAAKxAAACsPwAAuT8AAEy/AABRwAAAPD4AAMu/AABsvgAAu78AADtAAAATPwAACUAAADI/AADUPgAA070AAGC/AAAsQAAA5z8AAFVAAAACPwAAF0AAAOu+AAA7PgAAjT8AAA/AAAAYwAAAR0AAAMa/AABkQAAAyD8AAAzAAAAZPwAAVMAAAL2+AACVvQAAm0AAAOS/AACUvwAAZUAAANq/AAASwAAANz4AAAhAAADePwAA2D4AABtAAABiQAAABT8AAPU/AACSPA==", "world": 1, "sampling": [8047880102361090112, 0.7, 20, 0.5, 0.0], "position": 246769, "count": 28, "stats": [], "full": false, "token": 864} +{"logits": "AACGQAAAkD8AADTAAAAOQAAAEUAAAD4/AAAdQQAANkAAAJTAAAD1vwAAJEAAAIs/AACBwAAA5r4AACvAAADEwAAAiMAAABG/AADCQAAA2T8AAPG/AAAgwAAAKj4AAAZAAABQwAAAZcAAAMA/AACpvwAABcAAAEZAAAAaQAAAzL0AAKhAAAAtwAAAH8AAAIxAAAB3QAAAjkAAAC5AAACdPwAAD8AAAE8/AACWQAAAmD8AALm9AABEQQAAu0AAAGnAAABcvwAAgr8AANvAAABMvwAAPr8AAHhAAACCvwAABT8AAOnAAAAmvwAAC0AAADg/AABLPgAAOcAAANXAAAAmvwAAg78AACfAAADWwAAA2sAAAA9AAABoQAAA2kAAAJtAAACbPgAAR0AAAKe/AAB5wAAAQr8AAFNAAAD0PwAAz8AAAKHAAACXPwAA5sAAAPK/AADcPgAA9z8AACu/AADEwAAAVb4AADRAAAA5wAAAOEAAAMM/AABTwAAAzcAAAFK/AACfvwAAqcAAACbBAACJwAAA+78AAL7AAACtQAAAoEAAAMPAAADEQAAAD0AAADDAAAAcwAAAeEAAANFAAABcvwAApkAAAOs/AABowAAAisAAAPE/AAB3PwAA48AAACjBAAB3PwAAyb8AAOq/AAAKQAAA778AAOzAAAAEPwAAvL8AAPm/AACbQAAAEUAAAFjAAAAIwQAAeEAAAIA/AACewAAAjMAAAJK/AABjvgAA1UAAABPBAAA2wAAA8L8AAKC+AACRvwAA9sAAAKRAAACkwAAAfkAAABq/AAAAwAAA2kAAACdAAACIvwAAgz8AAPI/AAA6wAAA2z8AAMU+AAC8PgAAEcEAAJrAAAAIvwAAYMAAAHG/AACAQAAAh78AAAe/AABzvwAAzL4AAI2/AABQQAAAwj4AAI7AAAC7QAAAcb8AAC0/AACrPgAAiT8AACY/AADjvwAA00AAAIw/AADWvwAAv0AAAIrAAABowAAAbkAAAKNAAAD5vgAA18AAAMRAAACCPwAAFMAAANDAAABVvgAAJ8AAADPAAACFwAAA278AAKZAAADoPwAAAcEAAG+/AACqQAAAE8EAALg+AAAdwAAA78AAAD9AAAABvQAA+sAAAAfAAADkvgAAL0AAAD7AAAC0wAAAxUAAAIO+AAAHQQAAeUAAALRAAAB2wAAAlUAAAAbBAACNwAAAEMAAAKLAAADsvwAA1EAAALI/AAAIQAAAoMAAAFw/AAA7PwAAjsAAAEc+AAAuQAAAfr8AAERBAABJwQAAG8EAAN7AAAB/wAAADEAAAJnAAACiwAAAWkAAAJvAAADSPwAAdL8AALI/AAC+vgAAwMAAAGnAAADnPwAArUAAAMu/AABcvwAAwb8AAKXAAAB/QAAArEAAADDAAACSwAAAM8AAAEg/AABVwAAA9T8AAK3AAAAvQAAAssAAAP29AACUwAAAMEAAAHvAAADHQAAAqEAAALQ/AACAvwAAcL8AAB5AAABTQAAAEr4AAATBAABevwAA6D4AADHAAABZPwAABcAAAFZAAAAFvwAAMEAAAClAAABDPwAA0L4AAANBAAD9wAAA+b8AAHnAAACbwAAAQcAAAJu/AAA4QAAARsAAAOk/AAAeQQAAtsAAADM/AAAvvwAAx0AAAD5AAAA9wAAAq78AACHAAAAvQAAAHEAAAMw/AABCQAAAAEAAAPS/AAAfQAAA3T8AAARAAAAlwAAARMAAAA1AAAAaQAAAp0AAANG/AACLwAAAZMAAAAc/AAATwAAAOEEAAL2/AAAnvgAAacAAAOA/AACjQAAAaUAAAFO+AAAIPwAApkAAAGy+AABvQAAAQz8AAEE/AAA7QAAAxsAAAKs/AACJvwAA/T8AAK/AAAANwAAAdEAAABo/AAAYvwAA2z8AAHJAAADKvQAARD0AALjAAAC6vwAAnMAAABBBAADOwAAAK8AAACLAAACmuwAAPEAAAHjAAABSPwAA6r8AABJBAAAswAAAiL8AAA7BAAASQAAAo0AAAHHAAACNPwAAAkAAAME9AAAXQAAAT8AAAJo/AADkPwAAKr8AALi/AADnPwAAVUAAANDAAABEQAAAfcAAADI/AAAnQQAAVj8AAGhAAABbvwAAbz4AALDAAACHwAAADkAAAAzAAAAjQAAA3MAAAADAAAB/QAAADcAAANq/AABAwAAAj8AAAHVAAACTQAAAFMAAAI9AAAAjvwAAq8AAAIk/AAAPQAAA9cAAAHVAAADoPgAA0D8AAFa+AABvwAAAv8AAAPU/AAD5PwAAicAAAKW/AABWvQAATD8AAIbAAADdPgAAdkAAAJ+/AAAswAAAVb4AAGc/AABxwAAAgr4AACbBAAB/wAAAtEAAAN3AAACCwAAAtD0AAA1AAABWwAAA20AAALq/AADIvgAAnsAAAJK+AABCPwAAGT8AAOnAAACwPwAASEAAAI+/AACJQAAApkAAAChAAADkvQAA5b8AAJBAAABMPgAANj8AAFA/AACFvwAACEEAABhAAADcvwAAET8AAMZAAAAUQAAAiUAAADK9AAAOwAAAQj8AAMfAAABTQAAAqcAAAMTAAADpvwAAjcAAANw+AAC9PwAArcAAALa/AAAHQAAAl0AAAGg/AACSPwAAmL4AAJw/AADOvwAABMAAACfAAAA3wAAASsAAACxBAABpQAAARUAAAI3AAADFPgAA4UAAAL8/AADOPwAAhkAAAI3AAADdwAAAmr8AAFdAAAA1vwAAacAAAEFAAACBPwAAsb8AAEXAAABewAAA2T8AABjAAAA9QQAA7cAAAK6/AADHQAAA0D8AAC/AAACpPwAA/kAAAMrAAAB2QAAAn0AAAHy/AAAVvwAAhz8AALVAAADivAAALMAAAFNAAAAsvQAArUAAAPzAAADUvwAAL8AAAFk/AAA3wAAAV78AAGjAAADyvwAARr8AAL9AAACsQAAAgT8AANK/AABQwAAAgMAAAIq+AACbvgAAtUAAAM6/AADQvwAAbsAAADe/AACXQAAAez8AAE4+AAD6wAAAHsEAACnBAAAwQAAAWsAAAIzAAAB3wAAAZ78AADHAAABoPwAAib4AAGg/AAATQAAAE8AAAJdAAAAywQAAmj8AAITAAADnPwAAyj8AACzAAAB+QAAAlb8AAARBAAACQQAAtz8AADpAAAANvwAAp8AAAAdAAAAWPgAACD8AAI7AAAAtQAAA1z4AAInAAADrQAAA5D8AACHBAAAbQAAAQkAAAOI/AAAVQAAAEr8AAMc+AADFwAAA47kAANs/AAAEQQAAF8AAAF4/AABMvQAA7kAAAOs/AAANQAAACMAAAJLAAAAuQAAAkMAAAPI/AAD2wAAACkAAAMXAAACfPgAAxMAAACBAAACCPwAAO8AAAJJAAABWwAAAMb8AAA/AAAC1QAAAgj8AAGnAAADHvwAADsEAAGtAAAA6PwAAPkAAAJfAAADRwAAARkAAAGk+AAA4QAAAIUAAAJs/AAASwAAA0cAAAGvAAABmQAAAHEAAAKY9AAAlvwAAvb8AAPY/AAC6PgAAxMAAACXAAADaQAAA0kAAAMpAAAAhwAAAjcAAAFW/AABHPgAAD70AAATAAADAwAAAU8AAALu/AACVQAAApcAAAIG/AABfPwAA0r0AAIPAAADGvwAAbz8AAB0/AADwwAAATUAAAKJAAADzvwAA2UAAAPS+AAAUQAAAlEAAACRAAABGwAAAqkAAAB7AAADbwAAA9UAAAFNAAACivwAAbkAAACW/AAAdQAAANsAAAB3AAACkvwAAvcAAAGTAAADbvwAAML8AAHm+AACAwAAAQcAAAAhAAABUPgAAc8AAAOPAAACBPwAAjUAAAN+/AABGQAAAwT8AAIk+AAB7PwAAdj4AAG2/AACrQAAAasAAAFBAAADHvgAAVkAAAETAAAA0PwAA2z8AAGPAAACpPwAALT8AAK0/AAC/vwAAJL8AAGe/AACfQAAAIEAAAFDAAADYvgAAt78AAHjAAAB3QAAALMAAABlAAAA5wAAAjEAAAL++AACOQAAA6cAAAP0/AADuPQAAl0AAAApAAABVQAAA+D8AAJXAAAC+vwAAPT8AADbAAABfwAAAJj8AAEa/AABMQAAA3z8AADHAAACYQAAAqr8AAMrAAABawAAAisAAAIs/AABIwAAAMEAAAAPAAABCwAAABMAAAM2/AACrQAAAor4AAArAAAAdvwAACsAAAIw+AACpwAAA9z8AAFJAAAA3QAAAiz4AALfAAAC0PwAAFT8AAG1AAACEQAAAiUAAAH9AAAA5QAAAuL8AAEZAAACRwAAAFsAAAADBAADkPwAApUAAADFAAAAaPwAALsAAAIm/AABAvwAA1MAAANo/AACmPwAAHDwAAOQ/AABAvwAAJr8AAI8/AADPQAAAwUAAAGrAAAAqQAAAer4AAEFAAAA5QAAASD8AAGhAAAAhwAAAzr8AAItAAACKwAAA0sAAAAS/AABYQAAATkAAAL++AAAawQAA3z8AALDAAADKQAAAk8AAAFs8AACXQAAAh0AAAPi/AAC2vwAAAEAAAL7AAAC5wAAAD78AAL3AAADsvwAARMAAAJI/AACMwAAAKcAAAKjAAADgvwAAQ8AAABdAAABswAAAJkAAAJ/AAAC7wAAALEAAAJg+AABTQAAAKkEAAIFAAACXvQAAsr4AAERAAAB9PwAAkEAAAN4+AAAivgAAdUAAAH6/AAD5vwAA28AAAFjAAAD+wAAAvD8AAPe/AAAwQQAAJsAAALM+AAC8QAAAJcEAAJ6/AAAKPwAAicAAAEe/AACsPwAAiUAAAAHAAAD7vwAANb4AAJPAAACZQAAAq8AAAD5AAAC0wAAAY0AAALVAAABjvwAAAcAAAH0/AAAEwAAA3L4AAHK9AAARvwAAAUAAAKvAAAAUQAAAYr4AAJy/AAAywAAAsb8AAKtAAADyvwAAfcAAABNBAAA7vgAAsz8AALLAAAANQAAATEAAAPE/AAAKwAAAT0AAAChBAADHPwAAkcAAABVAAAA+wAAA8sAAAPQ/AACCQAAA670AABFAAABTvwAAaMAAANK/AAAvQAAAJ0AAANa/AAAiQAAAr8AAAK0/AACVPwAAHcEAABvAAAAPwAAALT8AAK0/AAB3QAAABD8AAG7AAAApwAAAD0EAAHbAAADcQAAAJsEAAPrAAABnQAAAtcAAADnAAAD8PgAAn78AACXAAAAIwAAAGEAAAKxAAACiwAAArr4AAIpAAADEvwAAfj8AAMZAAACYPwAAx0AAAN4/AACRwAAAr78AAF2/AAB2PwAAi0AAALfAAADVvg==", "world": 1, "sampling": [1808449382136270776, 0.7, 0, 0.99, 0.05], "position": 894555, "count": 512, "stats": [[12.25, 3.562254078794172]], "full": false, "token": 45} +{"logits": "AACcwAAAmj8AAIo/AAAdQAAAfr8AAGNAAAArPwAAUb4AABzAAABpQAAAID4AAG/AAADevwAAIkAAACW/AAB9wAAAekAAALk/AAC+vwAA/D0AAI0/AACvvwAAM0AAAK++AAASPwAAf8AAAMu/AAAOvwAA4r8AAI/AAABwPwAA00AAAFDBAAD3PwAA4T8AAIbAAAAkQAAAL0AAAIG/AADVvwAA8D4AACM/AACbPwAA3T8AAC0+AACmPQAA8sAAALA/AABCvwAAkMAAAKC/AADOwAAAQz8AAMk/AACbwAAAF8EAAIO/AACnvwAAKb4AAJ3AAACvQAAA7sAAAJZAAADEQAAA1L8AAIY/AAAMPwAAnD8AAERAAAAywQAA1D8AABVBAAAhPgAAwkAAAMdAAADGwAAAtcAAABpAAABdvwAAAkAAACLBAAC9QAAAv0AAANU/AACoPwAADUEAAPRAAABYQAAAp8AAAAHAAABbQAAAnUAAAK++AADRwAAAhEAAAJnAAAD5PgAA+z8AAFBAAABuwAAASkAAANO/AADZvQAAW0AAAHVAAAD8PwAApcAAALm/AABMQAAA9j8AAOo+AADzPwAAxb8AAEnAAAA6wAAAmcAAAKK/AACDPwAAuMAAAKJAAACqQAAAkUAAAFY+AAAPwQAAlkAAAKhAAAAOwAAAdT8AALtAAACXvwAAaz4AAKdAAAA+QAAAasAAABDAAADqQAAAHD8AAIPAAABEQQAAs78AAN6/AAAHQAAA674AADbAAAADwAAApL8AANVAAACywAAAlUAAAD9AAACXPQAAtD8AAEBAAAAJwAAAuD8AAPc/AACcQAAAWT8AAM6/AABowAAAdT8AAF2/AAAPQQAAeD8AAM3AAACZwAAAjcAAAFfAAAAgQAAAnEAAAA9AAAAuwAAAlsAAAIRAAABdQAAAPT4AABu9AACXvwAAgcAAAEw+AABRwAAARr4AAOY8AABqwAAAIT8AAKG/AAAlvgAAI74AALFAAAAlwQAA8r8AAKe/AAAvwAAAIMEAAPJAAADVPwAAXb8AAJhAAACFvwAAk74AAB/AAAAdwAAAj70AAG0/AACowAAAIMEAAEbAAADPPwAAoT8AAEs/AADzPwAA3D8AACO/AAASQAAABr8AAJY+AAAJwQAAWcAAAONAAAAIwQAAhMAAAJA+AAC6QAAABz8AANI/AABFQAAAVsAAALy/AACTPwAArj8AALVAAAD+wAAAT0AAAMdAAACzvwAAkb8AAIg/AADQwAAAjsAAANBAAAADPwAAY0AAAABBAABHQAAAHcAAAF3AAACaQAAACUEAAJHAAACaQAAA2kAAANNAAADGPwAAir8AAMQ/AAC/vwAA6sAAAMrAAADTvgAAisAAAJw/AACbwAAAW0AAANq/AABRvwAAzz8AAJU+AABSQAAAR78AAHLAAACOPQAAD8AAAHvAAADkvwAAkEAAABS/AACvQAAAcz8AAF9AAADGQAAAyEAAAM2/AAAdvgAAbsAAAMxAAAAIvwAAdMAAAFxAAAAdPwAAk74AABnAAABeQAAA5sAAABG/AAC+QAAAMD8AAD0/AAAJQAAAHMAAAK1AAADKPwAAYL8AANi/AAD3PwAAEEAAAD9AAACkQAAAlD8AAHK/AAAbQAAAm8AAAIM/AADivwAAp78AACk/AADAwAAABEAAAANAAAD0PgAA1r8AABxAAACPwAAA9D8AAL9AAACWQAAABEEAAAhAAACrQAAAZD8AADZAAACbQAAAzb8AACvAAAAzvwAAwD8AAOu/AAB+vwAA30AAANTAAAAawAAANcAAANM/AAC/PwAAYsAAAH6/AABSwAAAu78AADhAAABXwAAAoMAAABPAAAB1QAAA4kAAAALAAAAdwAAAqD8AAPs/AAALwAAAVEAAABM9AAAvwQAAm8AAAC7AAAALQAAAH0AAACu+AAC7QAAAL8AAAF/AAAAMQAAAOcAAALBAAAD+vwAAZEAAAME/AAAXwAAAE78AAH7AAACxQAAAMEAAADI/AADUQAAA9jsAAGq/AABlvwAAeUAAAGfAAAAxQAAATEAAACnAAABoQAAA4b4AAG5AAAAXPwAAR8AAAGXAAABsQAAArMAAABu/AAD0vgAAJcAAAN3AAAA2wAAAucAAAHa9AADOvwAANMAAAIY9AACGQAAA8b4AALg/AAClvwAA5L8AAPu/AAAYQAAABsAAAEHAAABbQAAAkT8AAKBAAADdvQAArD8AAEHAAACrvwAAEEEAAC9AAABXwAAA3D8AABs/AABvPwAAVD8AAHfAAADvvwAA8L8AAOrAAABYQAAAmkAAABpAAAD/vwAApsAAAK0/AACVPwAAhUAAAIu/AAAMwAAAhz8AAB3AAAB5wAAAH0AAALU/AAB4QAAAj74AAIe+AAC5QAAAvsAAAF8+AAAXQAAA7j8AAEXAAAAEvwAAy78AAK0/AACmQAAAzT8AAHLAAADWPgAA5TwAAEDAAAAIwAAAsD4AAMe/AABMwAAAZb8AAM/AAADsQAAAdr8AAP2/AAC1QAAAkj4AALPAAAAMwAAAXcEAAAvAAAAUQQAAoEAAACW/AACkvwAAQr8AAOw/AACrwAAAj0AAAJa+AADPPQAA5L8AALTAAABEQAAAJUEAAGpAAADovgAAWEAAAGi9AAAOPwAACMAAAGk/AAAXvwAAp74AAHpAAADLvwAAg0AAAEPAAACcQAAAdcAAAKK+AABovgAAjsAAAODAAAAzQAAAuEAAAETAAACSwAAAez4AAIRAAADzwAAAmD8AAEjAAACbQAAAFMEAALVAAACmPgAAgb8AAF5AAAAKwAAA/L8AAOnAAACBwAAAL0AAAMFAAAAIQAAAi0AAAJa/AADvQAAABz4AAO2+AAC4vwAA88AAACBAAABFwAAAlsAAAJU/AACdPwAA6sAAAMZAAAAJwAAAZsAAAAtBAACOQAAAkL8AAJI/AAC2QAAAAkEAAIXAAAD6PwAAlb8AAG4/AAAxQAAA+z8AADjAAAA/wAAAo0AAAKc/AAAEvwAAkMAAAGBAAABoQAAA+0AAAApAAACqvwAAvz8AAHu/AACFvwAAVcAAAGw+AAAUPwAAFcAAAAxBAACCvwAA4T0AALg+AABAQQAAlsAAAPpAAADNvwAAdMAAAPo+AADFvgAANMAAADo/AABXPwAAmD4AAHy/AADBPwAACkAAAMW/AADlvwAA9kAAAG1AAABYwAAADj8AAPLAAAA0QQAAs78AAHHAAADnQAAArkAAAB5AAABfwAAAiz4AAClAAACuvQAAIUAAAKxAAABWQAAAf8AAAIa/AADlPwAA1cAAAFLAAAA7wAAAML8AAKI/AAC/vwAABcEAAILAAADgPwAATr8AAOm/AABRQAAAur4AAMg+AADCvwAA+74AAKy/AAD4vwAAR0AAAHzAAACQwAAAqUAAAHZAAADyvgAAoEAAAKnAAAAjwQAAvkAAAJy/AADeQAAA1MAAAMA/AAAvQAAAHEAAAFDAAABGQQAAa0AAAA9AAACFwAAAg0AAAOa/AABNwAAAEUAAAGtAAAB+QAAA2b8AAFzAAAAmvwAARUAAAMhAAABNQAAAxEAAABJAAACxPwAANUAAAMRAAADTQAAAukAAAJM+AAD/PwAALD8AANHAAACXvwAABb8AAN2/AACPvwAADsAAAJG/AAA8QAAAD8AAAPm/AACpwAAAXEAAAFxAAABavwAAZMAAAE/AAAAnQAAAi74AANNAAAAePwAAQ8AAAABAAAA9wAAAJ0AAAEBAAABGPwAAXr8AAK3AAADVPwAAhMAAAFQ/AAAuwAAAJkAAALZAAABaQAAAEEAAALxAAABlQAAAiD8AAEtAAACOwAAAbz4AADJAAADjwAAAXEAAAC3AAAAowAAACkEAANlAAAAYQAAA8sAAAFlAAADyvwAASEAAAMU/AADovwAApEAAAJDAAAA/wAAACEAAAIxAAADOQAAApz8AACvAAAAqQAAAmj8AALPAAABnQAAARcAAABzAAADDQAAA8L8AAAU+AACjwAAAtb0AAGlAAAB1vwAAWkAAAN9AAACMQAAADb4AACdAAAAQwAAAFsAAAAVAAACIvwAAZcAAAIa/AADUPwAAFEEAAKzAAADrPwAAZr8AACFAAAAXvwAAw0AAAH7AAAAWPgAA+cAAAAA/AADKwAAAsUAAAADAAABrwAAAkEAAAAPAAADXvgAAcEAAAMzAAAAnQAAAxUAAAHhAAACnwAAAbsAAAHY/AACuQAAAHEAAACbAAAClwAAAq74AAAPBAACwQAAAMsAAAKs/AACdvwAA9L4AAP2/AAATQAAAn0AAAG/AAABvwAAAkMAAAJC+AADvvgAAwcAAAGPAAAC6PwAA0j8AALq9AABKQAAANMAAAP8+AAAOQAAAIMEAAKLAAADxPwAAhUAAAMA+AAD1PgAAI8AAADXAAACTPwAAUUAAALm/AAA9wAAAsj8AACpAAAAvQAAAY8AAALI/AACqvgAALEEAAJ4/AAAcwAAAD8AAAMLAAACzvwAALcAAAAhBAABXwAAA1z8AAMW/AABHQAAADkAAAJ7AAAClPwAAz0AAAEbBAADiPwAAlD8AAEfAAADfvwAAZcAAAFpAAAAWPgAAsUAAAHrAAABZwAAAPMAAALDAAACBQAAA3L0AAIS/AACKwAAAvD8AAO6/AAATwAAAZUAAAFy/AABKwAAAsD8AACo/AACvPwAAnsAAADtAAACpQAAADcAAAJ2+AABzQAAAUUAAACm/AACaQAAAacAAAEG/AACPQAAAHUAAALK/AAAGwQAAAUAAAEVAAABNvwAAhsAAAHLAAADRQAAAxEAAAMrAAAAewAAAn8AAAA/AAAAKQAAAjMAAAF9AAAD4wAAA0kAAAPDAAACiQAAAHkAAAONAAACTvwAAP74AAD5AAAC7wAAAFj8AADXAAAB8QAAA7j4AAIO/AACuQAAA+0AAABdBAAD1vwAAlz8AAPk/AABevwAAEr8AAFlAAAAFwAAAvMAAAKlAAADbvwAAS0EAAC+/AAChwAAAyL4AAGjAAAAwwAAAzj8AALZAAABkQAAA4EAAADq+AABfvwAApMAAAGnAAACcwAAA7z4AAIhAAABOQAAAdUAAAIrAAADJPgAAKkEAAJLAAABEQAAAC0EAAHxAAADgPwAAl8AAAA1AAAAnvwAAIcAAAHM9AACSQAAADr8AANG/AADFwAAALMAAAOM/AAB7QAAAYT8AAJbAAADIPwAAmkAAAKHAAABZQAAAYsAAAJC9AABBwAAAsL8AACHAAAAlwAAAq8AAABpAAAAOvw==", "world": 1, "sampling": [6699470294267645275, 1.3, 0, 0.99, 0.0], "position": 196831, "count": 512, "stats": [[12.6875, 5.742142097965326]], "full": false, "token": 588} +{"logits": "AAChwAAAqj8AAMy/AAA2QAAAu0AAACc/AAAwQAAAXD8AAOc+AAA9QAAAAL0AAMA9AAA6vgAAvUAAADBAAABUvwAALUEAAE0/AACKPwAA/j8AADzAAABnQAAAFsEAAAVAAADUwAAA978AABPAAAAXvwAAsT8AAHlAAACswAAALEAAANC+AABZwAAAz0AAAIdAAACVQAAAJsAAAM++AAClQAAAY8AAANe+AACYwAAAkb8AAPk/AAAZwAAAiz8AAIs/AABBwAAASMAAAD/AAADSPwAACsAAAJU/AAC7wAAAvsAAAPTAAAAhQAAA5D8AACg9AAB7vgAAakAAAL6+AABfwA==", "world": 2, "sampling": [1013073603140376705, 1.0, 1, 0.99, 0.05], "position": 149170, "count": 9, "stats": [], "full": false, "token": 16} +{"logits": "AADswAAA3z8AAEo+AAA8QQAAEj4AAC3AAACVwAAAlEAAAOq+AAA9QAAAO0AAAKu/AAAuwAAAgr4AAEk/AACtvAAAdsAAALdAAADlPwAAkMAAANVAAABOwAAAk0AAAANBAAD8vwAAKMEAAExAAAA1QAAAW0AAAOW/AACnvwAABkAAAL3AAAAmvwAANUAAAKbAAAAdwAAAKMEAADq/AAAfwAAAMr8AANdAAAB4wAAAWUAAAEvAAAC+vwAAdL4AAOA/AAA4wAAABUAAADc/AABDPwAAdsAAAMq/AACSwAAAhr4AAO/AAADHPwAA8r8AACe+AABjQAAAxj8AACnAAABKQA==", "world": 2, "sampling": [9199230241091237671, 1.0, 1, 0.95, 0.05], "position": 666442, "count": 9, "stats": [], "full": false, "token": 3} +{"logits": "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", "world": 1, "sampling": [981467450835081126, 0.3, 20, 0.8, 0.05], "position": 937297, "count": 28, "stats": [], "full": false, "token": 14} +{"logits": "mo6iwHg9AEExzHTBObPtPx5N7MBZi1fBtdFsPtxjD0EzFefANDXNwOKGXb+pLIfAOCKrQFyXvMF8k9lAzwsqQVONOsBDyXjAkvTxwD4QKEBXWl1BDLJLwYeLKUA1eEbBDlHDQEr+HEECuolBQ/oxQf96LsFBbLLAi4zDv6zFjUCvshTBW/ldwR81hT8jMlA+fex0QAKaSUBJapRAjlctwEQ3qcArl49AOP21QERdj0AkFJdBwESCwYuzD0FWel/BXGBHwCOumcDbD0hAX6+gwAptbMGQ0wPBhfYbQMa2FUHsArLAJ/e2weOiuEA/yMVAt9n+P8l+q0DPjTBB05rwQA==", "world": 2, "sampling": [2294664951983179103, 0.6, 0, 0.5, 0.0], "position": 233619, "count": 64, "stats": [[17.215824127197266, 1.0036307477572444], [18.88483428955078, 1.000002281145911]], "full": false, "token": 44} +{"logits": "AAAnQAAAeb8AAIk/AACPPwAA6T8AALdAAADxPgAA8r4AAMe+AADHPwAASz4AAEs/AADmPwAABb4AAKQ/AADSPwAACL8AAM2/AAAMwAAAjD4AAPe+AAC/vwAAZr8AAGw/AADIPgAALb4AAEk/AADIPwAAEz8AADFAAAAWQAAA+b8AAGzAAAB1wAAAsr8AAJ1AAAAwwAAAG0AAAB7AAACXPwAAej8AAOS9AACnQAAASj8AAG1AAABSQAAAKEAAADNAAADtvwAA+z4AAG+/AAAcwAAAK78AABs+AAAIPQAACD8AABnAAAAnvwAAdj8AAGFAAABQPwAACkAAAKDAAADIvw==", "world": 1, "sampling": [5746581240604106033, 0.7, 0, 0.8, 0.05], "position": 494708, "count": 64, "stats": [[5.71875, 2.0446070010508945]], "full": false, "token": 35} +{"logits": "AAALvwAASr8AAKO+AADGvgAAtj0AAPO8AAC2PAAABr4AAA6/AABfPwAAiT4AAE8+AABkvgAArz4AAG49AACCvgAA5z4AAA0/AADDvgAAvT8AAJS+AAD/PQAA/D4AAGk/AAAMvwAAfD8AAOo9AAAWPgAAvrwAAJi+AACvQAAALD4AAKo9AABHvgAAuz4AAIe9AAApPwAAoj4AAKE/AAA1PwAABj8AAIo+AAAovwAAi74AAEm/AAAQPwAAzj0AABC+AABXvgAAfD4AADW/AAD/PgAAKz4AAFW+AACCvwAApT8AAK28AACDPQAALb8AAOK+AAB+vgAAsTwAAIE9AADlvg==", "world": 1, "sampling": [3577998369617995743, 0.6, 0, 0.95, 0.05], "position": 119037, "count": 64, "stats": [[5.46875, 1.0116931772187339]], "full": false, "token": 30} +{"logits": "AABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDA", "world": 2, "sampling": [2156278021789485964, 0.6, 1, 1.0, 0.0], "position": 122108, "count": 9, "stats": [], "full": false, "token": 0} +{"logits": "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", "world": 1, "sampling": null, "position": 27119, "count": 9, "stats": [], "full": false, "token": 837} +{"logits": "AADUQAAAgMEAACRBAAD8QAAAekAAAJ1AAABFwAAAj0AAALlAAACyQAAAiUAAABLBAACgPwAAmEAAADPAAAAxQAAAzEAAALTAAAAdQQAAyz4AAKfAAABJwAAAz0AAAF1BAAAlQQAAZEAAACrBAABVwQAA3z8AAKJBAACJQQAABUEAADXBAACGwQAAi0AAAAjBAAB6QQAAD8EAAMzAAABEQQAAh8AAACbBAADlvwAAn0AAAFe+AABCwQAA5b8AAOpAAACgwAAAyb8AAKrAAAAOwQAAlMEAAEJBAABgQQAAFkEAAD3AAAAGQAAAMcEAALVAAAAhQAAAAsAAAKZAAABpwA==", "world": 2, "sampling": [2100816495798663990, 0.3, 0, 0.95, 0.0], "position": 1028813, "count": 64, "stats": [[20.25, 1.000029929957789], [15.625, 1.0044635115593954]], "full": false, "token": 29} +{"logits": "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", "world": 2, "sampling": [3211727052911288279, 1.0, 0, 0.99, 0.0], "position": 878141, "count": 512, "stats": [[38.0, 1.000000000000104], [6.09375, 7.605865054110172]], "full": false, "token": 32} +{"logits": "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", "world": 1, "sampling": null, "position": 97406, "count": 9, "stats": [], "full": false, "token": 360} +{"logits": "AABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwA==", "world": 1, "sampling": [1580234971795223331, 0.3, 0, 0.95, 0.0], "position": 207799, "count": 512, "stats": [[-3.0, 1000.0]], "full": true, "token": 223} +{"logits": "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", "world": 2, "sampling": null, "position": 48432, "count": 9, "stats": [], "full": false, "token": 929} +{"logits": "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", "world": 1, "sampling": [7435814333966903546, 0.6, 0, 0.8, 0.0], "position": 1048260, "count": 512, "stats": [[7.8125, 1.2771161823461723]], "full": false, "token": 417} +{"logits": "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", "world": 2, "sampling": null, "position": 238010, "count": 9, "stats": [], "full": false, "token": 282} +{"logits": "AACQPQAASb8AAKQ9AACPPgAAND4AAA++AACMPgAA0D4AAAa+AABrPQAAgj8AADa+AABXvwAAlD8AAIQ9AADIPQAAfT4AAAK/AAAJPgAA1D4AANg+AAAuPwAAYD8AAFU9AAAoPwAAhz8AADw+AAAJwAAAcUEAABa+AADOPQAAgD0AAMQ+AAACvQAAKb8AACW/AAB3PwAALT4AAKW/AADePgAAgj4AACk/AAAqPwAA3j4AAEK/AAAAvwAA+L0AAOs+AAD0vQAAQD4AAO0+AACcPQAAgL4AAOW9AACOPgAA+z0AAAM+AAApvgAAuT4AAAi/AAAKPwAAY74AAPu+AABxvg==", "world": 1, "sampling": [82188245437917029, 0.3, 0, 1.0, 0.0], "position": 264213, "count": 64, "stats": [], "full": false, "token": 28} +{"logits": "AADlvwAApsAAAAvAAAACPwAAcz4AAL0+AACmPwAAKMAAAGy/AACBPwAA4D8AAKG/AACJPwAAfz0AABC/AACUPwAA5T0AAJE/AAAewAAAEUAAAPu/AADxvgAArj8AADA/AACNPwAA778AAI7AAAAIQAAA5L8AAJtAAAAgQAAAEMAAAI/AAACIPQAAmr8AANo/AAD0vwAAc78AAGhAAACGvwAAScAAAAG/AAA8QAAARL4AAIRAAABWPwAAk74AABo/AAAQQAAAuMAAAEu/AAC/PQAAN8AAABA+AAAzQAAAO8AAAGNAAAB0wAAASz8AAJa/AABvPgAAh74AAGa/AAC6PwAAq78AAFE/AACcPwAACkAAADa+AACOPgAA3L8AADDAAADVvwAAoD8AAPm9AAAEQAAAGL8AALy/AACfuwAAxL4AAIO/AAAvQAAAd78AABNAAABQPwAAk70AAIe/AACjPQAA1LwAAI8/AAC5PwAAXr8AADfAAACiPgAAW78AAMK9AABNPwAALb8AAKzAAACivwAACD8AAPY/AACqwAAAiD8AAAxAAAA4vwAAsD8AABpAAAARwAAAesAAACU/AAA2wAAAcL8AAELAAAAYvwAAEMAAAJs/AACNPwAAuz8AAA7AAABWPwAAOr8AAOY/AAAUPwAA5r4AAMy+AAC/vwAAacAAAI6+AACqvwAAcb4AAARAAADNvwAAgr4AAJhAAACQPgAAir8AADu+AAA1PgAAlT8AAGI7AAB1QAAAIj4AAMY/AABYvgAAPUAAAFNAAACPQAAAKMAAADxAAABjwAAA0T8AAAK/AACDPwAACMAAAAi/AABLwAAA9z8AANq/AACsPwAAIEAAAOI+AAAzvwAAVj4AAPC/AAAjwAAAGkAAAA/AAADBvgAADMAAAJpAAACsvgAAnz8AAFFAAABiPwAAO78AAHBAAABVwAAAU74AANo+AADCvgAAxjwAAPW+AADhQAAAiL4AAAPAAADMPwAAtL4AAMS/AACRPwAACD8AAAa/AACwPQAARz8AADG/AAAvvwAAkb8AADlAAABTPwAACD8AAAvAAACBQAAAn74AAP6/AAAswAAAEEAAAPw/AACzvwAAsL8AAAbAAAC3vwAASz8AAHtAAABIvgAAXsAAAOq/AAAkPgAAzj4AAKG/AADtvwAAOb4AADbAAAC4QAAA6b8AAIm/AABkQAAAAsAAAL+/AADZPwAAnD8AAMg8AABewAAADL4AANy/AACCwAAAjEAAAMg/AACOvwAAbD8AAK4/AABZvwAABkAAAMC/AAC/PwAA+b8AAAdAAADfvwAAiD8AAEXAAABjPwAAPsAAAM4+AACHPwAA4r0AAAG/AAACPwAAM78AAELAAADOPwAAjb8AAH6/AAAdQAAAvr4AACTAAAARwAAAF0AAACQ/AACHPwAAUMAAAKDAAADKPwAAhr4AAH+/AAAMwAAA4D4AADBAAABhQAAALb8AAFvAAAApvwAAKz8AAFtAAABRPwAA9L4AAA4/AAC3PgAAkr8AACpAAADPvwAAPT8AABQ/AACEQAAAJT8AAL0+AAC2PwAAWEAAAJU8AAD5vwAAB0AAAKc/AADNvgAAHUAAAPE+AABsvwAA+T8AAB5AAAAGQAAA6z8AAAVAAAB4wAAAGEAAADu/AAAfQAAAzj4AAMe/AAB6vwAA4z8AAJk+AADDPwAAGUAAAEFAAAAbwAAAsL8AAAfAAAC1vgAA2T8AAHu+AACcwAAAsT8AAIg/AABowAAAF0AAANq/AAC9PgAAXz8AAEU/AADEvwAADT8AAAq/AABmwAAAkT8AANk/AAAiQAAAd0AAAKY/AABLQAAA178AAArAAABDwAAAFz8AAFO/AAABQAAA+z4AAIO/AABXQAAAMb8AAMM/AABMPwAAKb8AAPA/AAATwAAAiT8AACm+AAANQAAAkbwAAOU/AACSQAAAS0AAAC4/AAASwAAAN78AAENAAABbwAAAF8AAAOC/AACkQAAA5r8AAN+/AAAywAAAh0AAAMBAAADzvwAAfT8AAC5AAADFPwAABD0AAE5AAAATQAAAzj8AABLAAADFuwAAhkAAAPc/AAASQAAA7r8AAANAAAAWQAAANT8AACRAAADXvwAAUj8AAEHAAAAeQAAAPT8AAKA/AADuPwAAqb8AAB8/AAAPPwAAPz8AAJRAAAAyQAAAcr8AAELAAABqvwAAbsAAAAi/AABfQAAABz4AADs+AABsvwAAs78AAEu/AACqvwAAfT8AAJI/AAAIPwAA5zsAAKy/AAApQAAA1L4AAOO+AAAWvwAAWb8AANo/AAASPwAApz8AAJ3AAACYvwAA5z8AAHNAAACyvwAAacAAAMw/AAAyPwAAFr4AAI2/AAC4vwAA0D8AAKm9AABIQAAAYsAAAFTAAAD7PwAAbsAAAG0/AAA2PwAAND8AAOU/AACovwAAsD4AAGRAAAAsPwAAkD8AAAI/AAB+vwAADUAAAMO/AADOPwAAQEAAANJAAAAWQAAAjr8AAF7AAABzQAAAKsAAAHjAAABtwAAAmz4AANo/AADcvwAA174AAMg+AACLPwAAUz8AAAs9AADGPgAABUAAANu9AACavwAAIT4AAMVAAAB5vwAA5j0AAEm/AAAoPwAAvUAAAFG/AACSwAAAIEAAALM/AADgvgAAqD8AADw/AABwwAAAqr8AANO/AAAgwAAAmj8AAPA/AADlPQAAk78AAN6+AAAwQAAAiUAAAMA/AAAXwAAAeUAAACjAAACSwAAAJr8AADo/AACpvQAABUAAAHk/AACVvQAAt78AADW/AAAyPwAACUAAADLAAAA3PwAA3D8AADw/AACBQAAARb4AAD7AAAD4vwAAEsAAAC9AAADCPwAAl78AAAc+AAAeQAAAdEAAAD6/AADevgAAQL8AAHrAAAACQAAAmj8AAI6+AACFPgAAEL8AAEO/AAApwAAAlj8AALy/AABjPgAA3j8AAF4/AACWPwAAzb4AALu/AAAIwAAATcAAAKM+AABDPwAAbj8AAPI9AAAvwAAAtEAAAFk/AAC3PwAAsD8AALW/AACfPwAAQkAAAOk/AAA+PgAAjz8AAII/AADRvwAAFj8AAEvAAAAuwAAAyT4AAFTAAACHvwAALL4AAA/AAAAAwAAAJz8AAAY+AAAKPwAAOEAAAJE/AACRQAAArz8AAM+/AAAnwAAAnz4AABLAAABFPQAAxr4AAHg/AAChvwAAzD8AAN0/AACsQAAAqL8AADBAAAATvwAA7z8AAGFAAADHvwAAkj4AAEQ/AADLPwAAD8AAAMS/AACUPwAAvj8AALG/AACaPQAAXr8AAOc/AABDQAAAkD4AAAC/AACbPwAAmb0AAJG/AADKPwAAfT0AAOM+AAAEQAAA0z8AABbAAAC6vwAAA8AAAAFAAADPPgAA4T8AAJK+AAA4vwAAl74AACzAAAC4wAAAtz8AAMs/AACpvwAAdb4AAIO7AACHPwAACr8AAH6/AAAYQAAAAL8AANy+AADMvgAAir4AAItAAACfPwAA8T8AABtAAACSPgAA0j8AAFc/AAA1vwAAv74AAI9AAACGvwAAA78AAPO/AAACwAAAhT8AAII/AAADvgAApj0AAE/AAAAHwAAABsAAANm/AABLwAAAHEAAAArAAABQPwAAC0AAAJu+AAD3vwAAPrsAAGU/AAB7vwAAmD8AAJM8AACUPwAAR0AAAIO/AABRPwAAgcAAAKI/AABkQAAA0D8AAJo/AAAkPwAAAkAAAPE/AAAXQAAABsAAACDAAAC/PgAAaj8AAOw/AADYPwAAPEAAADg+AAANQAAAz78AAGDAAAC4PwAAMMAAAIvAAAClvwAAhL8AAKk/AAA5PwAAsD0AAAO/AACYPwAAzD8AABVAAACKPwAA9D4AAE3AAAB/PwAAv8AAAGi+AAC0vwAAe8AAACI/AADPvwAAFL8AAPs/AAC2PwAA4b4AAHTAAABKPwAAfL8AAL4/AAAqQAAASj8AAC9AAACTvwAAVsAAACzAAACCvwAAE8AAAPo/AABdPwAA9D8AAFxAAAC6PgAAeL8AAKa/AAAQwAAAtL8AAIO/AABGvgAAKL8AAA5AAAC3PwAAib8AANC+AADQPQAAj78AAMo+AACBvgAARcAAAJ0/AABVPwAA6b8AAPk/AACavAAAGMAAAMLAAACjPwAAhD8AAI8/AAC4PgAAsr8AAPk/AADrPwAART8AANI/AACLvQAAtT8AANO/AADqPQAADzwAAJw/AAAwvwAAb78AAMu/AABcQAAAXcAAAMW/AABKQAAAmL8AACRAAACSQAAAmj8AAAjAAACavwAA3z8AAE1AAADNvwAALkAAAKM/AAAqwAAA0z8AAJs+AAAjvwAAvr8AAHc/AACSPwAAnz8AACS+AABYwAAALj8AAITAAACXPwAAGsAAAIw/AABjPwAAgj4AABdAAACyvwAALL8AAMM9AACJQAAAsj8AAA0/AACxvgAAXT8AANI/AACSvwAAXr8AAD9AAAA2QAAALMAAAKzAAAANQAAA7j8AAF9AAAC+PwAA970AAFPAAACgvgAAyb0AAIW/AAA6wAAAaT8AAGHAAADjvwAA178AACXAAABXPwAAQL8AAEtAAACaPwAAkL8AANy/AACBPwAAPr4AAF5AAABswAAAfT8AADfAAAAJwAAAA0AAALg/AADSPgAAXD8AABBAAAD9PgAA3D8AAO6+AAA2QAAABEAAANy/AADVPwAAZz8AANE/AACTvwAASj8AADNAAAAhvwAACUAAAEo/AACaPwAAtL4AACM/AACLvwAAWb8AAPK/AAAmQAAAQEAAADRAAADWvwAABT4AAIo/AAD4vwAAjz8AAJM/AADfvAAAsj8AANy/AACsPwAATT8AAPE/AABMQAAAnb8AAIDAAABbvgAAub4AAIG/AACxPwAAmT8AALK/AAAPQAAAJb4AAFW/AADNvwAAg70AAMW9AACvPwAALEAAABxAAABFvQAAlb8AAIs+AAAhwAAAhD8AAEhAAAD4vwAASD8AAIa/AADyPgAAPcAAAEFAAAD3PwAAXD8AAPa/AABZPgAAp8AAAIs+AAAMPwAArL8AAIy/AACFvwAAaT8AAH8/AABHvwAAFD4AAL0/AABJwAAAEcAAAHW/AAABvAAA7r8AAB9AAACdPwAAlj8AADxAAAA7wAAA9T8AAI6/AADkvgAA4T8AAHrAAAD8PgAAhL4AABk/AAAyPwAA1L8AAEe/AAAiPgAADkAAABO+AACSPgAAoz8AALA/AAAzQAAACcAAAP2/AADlvwAAfT8AAHM+AADpvw==", "world": 2, "sampling": [945058242412624098, 0.6, 1, 0.95, 0.0], "position": 772085, "count": 9, "stats": [], "full": false, "token": 183} +{"logits": "AACpPwAAVEAAADtAAAAvQAAAcr4AAN4/AACiPwAAFEAAAARAAACAvwAACMAAABjAAABdPwAAhL4AAHy+AAAkQAAAAEAAAHk/AADAPgAAUz0AANw/AADPPQAAS8AAAHY/AAC0PwAAXL8AAIA/AAAUwAAABUAAANU/AABIwAAA7b4AAFfAAAC3PgAAW0AAAB7AAADyvwAA18AAAFtAAAABQQAACkAAAOu/AACpPwAARUAAAFJAAACcPwAAez4AAE5AAAACvgAAGUAAAM+9AACbPwAAgrwAAOg/AAAoQAAALkAAADTAAAD3vwAAZ0AAABw/AAApQAAAGcAAAKm+AAB8wA==", "world": 2, "sampling": [7944816823174743003, 1.0, 0, 0.95, 0.05], "position": 403409, "count": 64, "stats": [[3.3125, 5.600735510813498], [8.0625, 1.0817652980143202]], "full": false, "token": 39} +{"logits": "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", "world": 2, "sampling": [4066144615226573507, 0.7, 0, 0.8, 0.05], "position": 183431, "count": 512, "stats": [[25.298585891723633, 2.054185653678333], [38.69921875, 1.0000000002575342]], "full": false, "token": 949} +{"logits": "AAAtwAAAMUAAAIg+AADvvgAA7L8AAI8/AACgQAAA8jwAAEjAAAAjPwAAgL4AAI0+AADOvwAA2b8AACG/AAC+vwAANEAAABo/AABjPgAAoj4AAHG/AABZPwAAyb8AAPY+AADWPwAAob8AADdAAAA7vwAAIMAAANs/AABcvwAAQMAAAB0/AADTvwAAAUAAABA/AADMvgAATb8AAAdAAACaPwAANUAAAKO/AAAtPwAAET8AAPq/AABmvwAA/j4AAOE/AACTvgAAWEAAACRAAADcPwAAtr8AAEG/AADrPwAAasAAAJw/AAAcvwAALkAAAKLAAABovgAA3z8AAOA/AACgvwAAikAAAIHAAACovwAAxj0AACPAAAAhwAAAUr8AAHw/AAClPgAAVT8AAG+/AAByQAAAtD0AABS/AADgPwAAOb8AAFc/AAAcwAAA4j8AACFAAACRvwAAxD8AAJe/AAA5PwAA8b8AAH6/AAALwAAAHD8AAFhAAAAyQAAAqL8AABI/AABTvwAAM78AAL5AAABMwAAAkj8AAJ4/AAB0wAAAwb8AAAjAAAB2vgAAQUAAAEhAAABXQAAAJ0AAAHa/AADsvwAAo78AACO/AADoPwAARsAAACBAAAByPQAAkD8AACpAAABKQAAARj8AAFI+AADzPwAAP78AABzAAAACwAAAYEAAAKO/AABsvgAACUAAAEHAAAB7QAAAJ78AAAS/AAD3vgAA1UAAANq/AABXQAAA078AAMm/AACMQAAADsAAAEHAAAAFQAAApz0AADM/AAA9PgAAXEAAAPo/AAANwAAA+D8AABtAAAB6PwAA/D8AAE9AAADsvwAAQb8AAF+/AABLvwAAO0AAAO2+AACCPwAAkz4AAAg+AAAXwAAAAEAAANU7AAAMwAAASUAAAJG/AACTvwAAqD8AAC3AAADSPQAAjr8AANK/AAAbvwAAPjwAAAFAAAAFPwAAmj8AAGM+AADbPwAAD0AAAIW+AADTPwAA7L8AAM2/AAAdvwAAlD8AAOs/AAALwAAA6b8AANe/AACTPgAARcAAABa/AAC/vwAAvL8AAMs/AABcvwAAqkAAAIy/AADxPwAAXb8AAKw9AAAIQAAA7b8AAME+AAAnPwAAsr8AAEU/AACYvQAA0L8AAME/AACQvwAA/T0AAE7AAACZwAAA3r8AAEu/AADWPQAAHsAAAAZAAABjvwAAGT8AACXAAACCPwAAX78AAK0/AADRvwAAPUAAAEo/AAASvwAAgT4AAIA/AACCPgAADD8AAMq/AACsvwAAGL4AAOQ/AABSQAAAgj8AADS/AACkvwAAb78AAIy+AAC+PwAA6j4AAGu/AABoPwAAX8AAAP+9AAAgQAAA+L8AAE4+AAA7vwAAKEAAAEFAAAAHvwAAlr8AAPS+AAApPgAAT78AALPAAABVQAAArj0AAJs/AADUvgAAUz8AAL0+AADfPwAAFEAAACO/AAAhwAAAjMAAAJbAAABCQAAAsz8AAPE+AAClQAAAKT8AADLAAAA7QAAAIz8AAJg/AAB1PwAAbEAAAH2/AADhPwAA6T8AAOw/AABmvwAA0r8AAApAAABwwAAAGj4AAE6/AABIvwAA0j0AALC/AAB4QAAAdMAAAIM/AAC+vwAAFMAAAFO/AAAoPwAAIT8AAPw/AADsvwAAsT8AAIO+AAD+PwAAlD8AAEk/AADiPgAAB78AAOS/AAC6vwAAtz8AAAZAAAAvwAAAGz8AAAFAAAAiQAAA4L8AAAS9AAAwwAAA2r8AAJg+AAAnQAAAYsAAAA4/AACovwAA1b8AAO89AADlPgAAjj8AAOM/AAAUQAAARj0AAJS+AADzPAAADL4AAJI/AABCwAAAYz8AAB7AAAAzPgAASr8AALg+AACxvwAADMAAAIq/AABBwAAAX0AAAFDAAAAAwAAAn78AAL+/AAAmwAAAXsAAAEdAAAD7vwAAUD4AAHW/AAAWvwAAK78AAPY/AAA+vwAAX78AALc9AAAVwAAAJ0AAALS9AAB9PwAARMAAAIU/AAA4wAAAVMAAAGbAAABxwAAA4D8AAAM/AAAfQAAApT8AAFJAAAAlPwAAv74AAGhAAABMwAAANsAAANA+AADLvwAAfr4AAK8+AABwPwAAhT4AAAk/AAAuvwAANMAAAD4+AABGwAAAoT4AAFzAAABfQAAADEAAAHM8AACpvwAA2D4AALc/AAChvgAAsr8AAKS/AACZPwAAh74AAK7AAAA6wAAA1T8AAJq/AACBPwAAjz4AACk/AAA4wAAAA0AAAA88AAALvwAAi0AAAGu9AAAWwAAAs78AACS/AAB0vwAA2T8AABu/AACHPgAACkAAAG/AAAByQAAAeMAAAIe/AABewAAAor8AALi+AAAUvwAAg8AAAIVAAAAjQAAApL8AAEtAAAA8PwAAIb4AALQ/AACBvwAAkT8AAAM/AAAZwAAA178AAKk/AAArQAAATz8AAEI/AACNwAAACr8AABi+AADmPwAAIEAAAB8/AAAowAAA270AACbAAADoPwAALEAAAHhAAAD7PgAAEcAAAOq/AAC3vgAAEz8AAP4/AAD4PwAAcsAAACDAAACcPgAAWUAAAGxAAAAAvwAAVcAAAIa/AABwPwAACkAAAMC/AAC9PwAAkD4AAC2/AACPPwAAJcAAACXAAAAevwAAN78AAIg+AABpwAAAJj8AACK/AABlvwAAd78AAIzAAAA0PgAAFD0AADvAAACQvwAABL8AAEw/AAAnQAAARsAAAEfAAACgPwAAIkAAABFAAADVPgAANEAAAJ+/AAC3PwAAkz8AAG2/AAA1PgAA+78AAH8/AAB+wAAAyb8AAJHAAADXvwAAFUAAAKk/AAAgvgAAhkAAAIK/AAABwAAAG8AAACq+AAClQAAAJ0AAANm/AAADQAAA0j4AAMk/AAAzQAAAhT0AALnAAADlvwAA170AAIbAAACNvwAAu78AAADAAAClvwAAR74AAF3AAADkvwAAu74AADtAAABtvwAAlj4AAIc/AABtwAAAHUAAAAG/AACFvAAA4MAAAAa/AACRPwAAjL8AAIU/AACnPwAAn78AAKS+AADXvwAADL8AAJm/AAD+PwAA3z8AAEY/AAArQAAAnj8AAAtAAADkPwAAlEAAAPy+AAAWPwAAXL4AAMI/AAAcQAAAG8AAADPAAAA7vwAAVEAAALS/", "world": 1, "sampling": null, "position": 447445, "count": 9, "stats": [], "full": false, "token": 136} +{"logits": "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", "world": 1, "sampling": [1575542946575770700, 1.3, 0, 0.95, 0.0], "position": 1034737, "count": 512, "stats": [[1.4549052715301514, 205.52997365117716]], "full": true, "token": 234} +{"logits": "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", "world": 2, "sampling": [6292447795977879710, 0.7, 1, 0.5, 0.0], "position": 429163, "count": 9, "stats": [], "full": false, "token": 550} +{"logits": "AABgwAAAK0AAACDAAAA4PwAAh8AAALjAAACeQAAA6j8AALJAAABQwAAAiD8AAOXAAACXvwAAXMAAAANBAADMPwAAzsAAAAnAAACBwAAALb8AAOO/AACcQAAABL8AALO/AACiQAAAiz8AAH/AAAAWQAAAYEAAALI/AAA8PwAA6b8AAAM+AACPQAAAeMAAACBAAAAVQAAAlD8AAIdAAAAjwAAAycAAAKpAAABFwAAAgT8AAItAAABwwAAAB78AAA6/AAC3vgAAJEAAAO2/AAC4QAAAiz8AAJzAAAAQwQAAj0AAAF7AAAB7wAAAA0AAAGRAAACIQAAA9j8AAMS+AACSPwAAv78AAK5AAABPQAAA10AAAO+/AACTvwAAJMAAANK+AACIQAAAGMAAACC/AAA+wAAAR8EAAGrAAAAQQAAAxUAAAJ5AAACKvwAAmT8AAORAAAAIQAAAZ78AAPhAAABiQAAA5cAAAOs9AAC3wAAA3z4AAJ+/AAB0wAAAo8AAAHbAAABWQAAAGcAAAAdAAAAiQAAAfT4AAOLAAACCPgAAKT8AAPS+AABSPwAAzz8AAB/AAAAHQAAAVMAAAAtAAAAsQAAAur8AAO9AAACXvQAA3T8AANfAAAAMwAAAxb8AAIZAAAADvwAABb8AAEdAAACEQAAAEUAAAKNAAADYvwAAyj8AAAE/AAC5PgAAj8AAAPs/AACjQAAADMAAANq+AACGPwAAFkEAALtAAACWwAAAHD8AABBBAACFwAAAdD8AAPC/AACnwAAAs0AAAO6+AABsPwAACUAAADM/AADLwAAAN0AAAJM/AACaQAAAbsAAAANAAAC2vwAAAUEAAB29AADYQAAAlT8AAARAAAAGwQAAJEAAAIvAAAARwAAA/z8AAB5BAAABwAAAwcAAAAm+AABGwAAAMsAAALtAAADowAAA8r4AAMK/AACjPwAAmL0AAOzAAACVQAAAYkAAAD3AAAARwAAA3r8AAB0/AABywAAAUsAAAGjAAACAwAAASz8AADHAAADgPwAAaj8AAK/AAABhQAAAJj8AALJAAAAQvgAAT0AAAMVAAAAFQAAAnD8AAGtAAAC7PwAAA8AAADRAAAA1wAAAKcAAALm+AAAoQAAADEAAAInAAAAIvwAAGT8AAOG/AADSwAAAhEAAAKo+AACcQAAAEL4AAIJAAADpQAAA/L8AAOc+AABnPwAAbMAAAJZAAAADwAAAzcAAAAlAAAA7wAAAL78AAAPAAABjQAAAqEAAAAbBAAAvQQAAIr8AAKE/AACdQAAAV74AAAlBAABZvwAAA0EAAELAAABhwAAApz8AAGxAAAByvwAAXz8AAEJAAABrQAAAyb8AAE7AAABLQAAAxMAAAKJAAACPPgAAGr8AAITAAACgwAAApr8AAJ1AAABZQAAANT8AABXAAAAEwAAAcsAAACXAAABAQAAAz78AAHFAAADsvQAALkAAAGc+AADCvwAAyEAAAFdAAABLQAAATcAAAAfBAAAtvwAA978AABw/AADQPwAA/8AAAJ4/AAC3QAAAwT4AADRAAAC7wAAAB8AAAGE7AACOwAAAtkAAADvAAADdPwAAkUAAAMxAAABrwAAAPL8AAIvAAADevwAAK8AAAPK+AACmQAAAWMAAAI8/AAApPwAAgj4AADrAAAC/QAAAY0AAAFfAAACvQAAA4cAAAA5AAACkwAAAF8AAAM9AAACaPwAAN8AAABLAAAD5QAAAbL8AAIZAAACEwAAAZz8AABNAAAApPwAAiUAAALHAAABiPwAAmz0AABVAAAD5QAAAZMAAAKLAAAD2wAAAsr8AALU+AAApQAAAcL8AANM/AADXPwAARMAAAAm+AAA5QAAAJL0AACI+AACUPwAANkAAAIhAAACKQAAAUMAAAKg8AAC7PwAArsAAACFAAABtvwAAob8AAAbBAACcwAAAkz8AAJLAAAA7wAAAP8AAALw8AAD6vwAA+78AAKvAAACPPgAAdj4AAOe/AACOwAAAFkAAAF1AAACOwAAAKsAAAEA/AACVwAAAGMAAALPAAABdvwAAqD8AAEpAAACCwAAAO0AAAChAAAAjvwAAD8AAAI/AAAD9vwAACkAAALq/AAB2wAAAEkAAAMPAAAAcwAAAh78AAE1AAACcwAAAjMAAAAXAAAAkPgAAvb8AAP6/AAB4wAAAaD8AAFPAAACXQAAATUAAAKFAAACVQAAAsT8AAJ3AAAAiPgAAqcAAAHjAAACEQAAAwT4AAIVAAAB5PAAATr4AAMy/AAAzQAAAK0AAAKTAAADbvwAAYsAAABpAAAAJwQAAtUAAAOBAAAD8PwAAKsAAACHAAACgPwAAQ78AAGRAAADtQAAA4D8AAIc/AADWwAAA678AAI++AADiwAAAbj8AABnBAAAUQAAAf0AAAHhAAAAVvQAArr8AADA/AADLvwAAmUAAALG+AADIPwAAFkAAAHs/AAC3PwAAuz8AAEJAAAA1wAAA0D4AAB1AAAByPwAAgsAAANe/AACGwAAAQMAAAGlAAACowAAApL8AANi/AAAJQAAAkL8AAO6/AACfPwAAhcAAAJNAAABgwAAAsL8AAJ+/AACHwAAAjEAAABhAAADCPwAAbUAAACPBAAAGPgAAHEAAANY/AACCPwAAJMAAABi/AAB+PgAAx8AAABBBAAAWQAAA7z4AAGBAAACGQAAAm8AAACO9AAApwAAADkEAALs/AAD9PgAAkMAAAGFAAAAMvwAAmkAAAPbAAAAbwAAA1sAAAGPAAADDwAAAQ0AAALG+AADqPwAAEEAAAEFAAAApvwAAzcAAAIRAAACpQAAAcUAAAEXAAAB+QAAAjUAAAJnAAAB9vwAA8L8AANG/AACNvgAARkAAALm+AADvPwAAEUEAACC/AABYwAAAJD8AAIbAAACVQAAAIEAAAAfAAAD1PwAA9j0AAKxAAACovgAAREAAAKdAAADpvwAAZ0AAAMTAAACGPgAAYL8AAJhAAAARPwAAwD8AAJO/AAD7PwAA7D4AABpAAACtvgAAFcAAAIy/AADWQAAAK8AAANS/AAC8QAAATsAAAETAAAAyvwAAGkAAAPLAAAApQAAAtUAAAD3AAADBPwAAxUAAANLAAAAZPgAAxkAAAN2/AADWwAAAAL8AAJpAAADdwAAAGcEAAIPAAAARwAAAn0AAAEbAAADaPwAAfb4AALc/AACkwAAAbb0AAF9AAACUQAAAt78AACNAAABvwAAAmEAAAC2/AAD1wAAAj78AALm/AAAXPwAAqcAAAHS/AACtPgAAPsAAALlAAADfvgAA8sAAAHrAAABDPwAAwr0AAMC/AADbPgAA4T8AAC+/AAApQAAAyEAAAJa/AACCwAAAFr8AAHG/AACyvwAABT8AAKFAAAB3wAAAEkEAAAPAAABvQAAAYEAAAP4/AADfwAAAC0AAAEhAAACqwAAATsAAAEo/AAA5wAAAaj8AAHa/AACmwAAALT8AAAdBAAAawAAA5r8AACi/AADtPwAAsT8AAE5AAAB5QAAAEUAAAClAAABLQAAARMAAAEfAAACuwAAARMAAAIjAAAAIwQAAJEAAAA1BAAAnwAAAC0EAAF2/AAAYQAAAlsAAAItAAADOPgAAf78AALXAAABDQAAAUcAAAETAAACFwAAADcEAAO7AAAC0PwAAJL8AAAfAAAAbQAAAw0AAABtBAADcPwAAVT4AAGlAAADOwAAA3cAAAERAAAD3wAAAlcAAAAQ/AAASwQAAab8AAGjAAABXvgAAFkAAAHNAAADivwAA7T4AAG8/AACOQAAAkD8AAJo/AAB5vwAAor8AAO6/AACpvwAAi8AAAG/AAADawAAAgMAAAP6/AACXwAAASz8AAKRAAAAdQQAAlsAAAGI9AADgQAAAa0AAAEy/AAAPvgAAzL8AAFJAAADcwAAA3T8AAIC/AACQwAAApkAAAADAAABRQAAA3L4AAFrAAAAtwQAAGsAAAIXAAAA8wAAAlUAAAK5AAADqQAAAw8AAACLAAACGwAAAvj8AAAzAAACfQAAA/b8AAKFAAABDvwAAxL8AACJAAACKQAAABcAAAI3AAADQvwAAWMAAAJzAAAAxQAAA7D8AABzBAAAgvwAAgD8AAKNAAABcQAAAHEEAAGvAAACgPwAApcAAAAe/AADQPgAAicAAAKO+AACywAAAaEAAAIXAAACgvwAAksAAAG7AAACnQAAAZsAAAMzAAADBQAAAzr8AAKtAAACjwAAAzL8AAEi+AADnQAAAf78AANzAAADZQAAA3sAAAOi/AADMwAAA98AAAKQ/AAD7wAAAb0AAAJ0/AADSwAAAdcAAAPA/AABNPgAAHsAAAFRAAACfwAAA278AAOg/AACWvwAAxL0AADnAAACWQQAAIr8AAFm/AABSwAAAhkAAAORAAAC7QAAAG78AAGs9AAAkwAAA4MAAAMTAAACSwAAAPz4AAMTAAAD/vwAAIj8AANRAAACAwAAAW8AAACXAAABxvwAAnkAAAHRAAAAlwAAAmj8AAMdAAACkwAAApr8AACHAAADmQAAA0z8AAFRAAAB9QAAAEUAAAAlBAAABQAAAMEAAABHAAACTwAAA9r8AAPRAAADjQAAAS8AAAF3AAADwvwAAwb8AAJVAAAAOwAAA3j8AAKc/AAAMPwAAp78AAP3AAAAOPwAA3z8AAJe/AACCwAAAt0AAAKs/AAADwAAAQD8AAEhAAAARwAAAQsAAAGu/AACtwAAAQT8AAO6/AAB5wAAAqUAAAI/AAAA8QAAAMEAAAL8/AABvwAAABkEAALQ/AACUQAAAWsAAALG/AAApPwAAB8AAAEZAAAClwAAAh8AAAN1AAAAmQAAA7L4AAJhAAABhvwAAX0AAALLAAADMwAAAvUAAAI/AAAClPwAA0kAAAIxAAAB+wAAA+z4AAMJAAACTQAAAuMAAAGy+AABdwAAA0EAAAM4/AACnPwAAmUAAAMA+AAApQAAAUkAAABjAAAB+vwAALcAAADPAAACxQAAAH8AAACZAAACCQAAAvr8AADZBAAD7PgAAWUAAADnAAAD7vgAA/sAAAGC/AACnwAAADsAAAEPAAAAWQAAAZ8AAAFW/AAByPwAApz8AAKa/AADsvwAAmcAAAM0/AACcwAAAbMAAAEg/AAA9QAAAoUAAAG++AADPPwAAkUAAAFnAAAAAvwAAlkAAAHe/AAAjPwAAGsAAANm+AAAWQAAA9sAAAKXAAAC4QAAAnkAAALPAAAAnQQAABsAAAEQ/AAB9wAAAWkAAAFlAAACNwAAAgb8AAJy9AACQvgAA0j4AAJI9AAC0PQAAmT8AAJQ/AADkPQ==", "world": 1, "sampling": [3896836857074606297, 0.3, 1, 0.8, 0.05], "position": 993531, "count": 9, "stats": [], "full": false, "token": 822} +{"logits": "AAAhQAAAwT4AAEbAAAAFwQAAmcAAABo/AADMvwAAu0AAALo+AACTQAAAT78AANG/AACyPgAACkEAAPfAAACHwAAA7sAAADg/AAC1wAAAqkAAAArAAACRQAAAx8AAAOtAAADAvwAAeMAAAGBAAAALQAAAgz8AAJY/AAA9wQAAJcAAAIY/AACbwAAAI78AALJAAACmvgAAg0AAANg/AADpPwAA5UAAAG6/AADCvwAAFT8AACDBAAA6QAAAekAAAFZAAADavwAAlT8AAOFAAADIPwAAOb4AAEfAAACwPwAAWkAAAGhAAABQPwAAsz0AAEfAAADvPgAAED8AAMpAAAC/vwAAncAAANA/AACBQAAAmL8AAGJAAAC4QAAAO8EAAA/BAAATwAAAU0AAANRAAADJvgAAKb4AAGtAAAA4wAAAXEAAAAfAAADhvwAAFsEAAJ/AAACLwAAAcsAAAArAAADAvwAAbL4AABVAAAABPwAAmMAAANG/AAD6wAAAaj8AAH3AAABUvgAA074AAMk/AAAvwAAA2sAAAEDAAABmQAAA/b8AAANAAAApwAAAg0AAAH8/AABuQAAA3D4AAP2/AACkwAAAnsAAAAbAAADcPwAAzj8AABtAAABAPwAAy8AAADNAAACSwAAAS8AAAMi/AACOQAAAkkAAABE/AADQvwAAuz8AADFAAACdwAAAZsAAAJ/AAABjvgAAdcAAANtAAAD3vgAAfD4AAJFAAAB0QAAAvEAAALNAAADtwAAAsb4AAGpAAACHQAAA6L8AALQ/AAA/QAAAdEAAAE9BAACNQAAAkL8AAGvAAADavwAA8T0AAKU+AADhvgAAuL8AABbBAADPPQAA678AAMQ/AACeQAAA7L4AAH2/AACLwAAAAEAAAEnAAAB2vwAArL8AAKE/AABKvgAAn0AAANJAAAB2vwAAAMEAAIu/AAD3vgAAK0AAAAzAAAAGvwAAtMAAAKVAAACJPwAAFcAAAMDAAABlQAAAjEAAAJu+AABwQAAARsEAAMbAAACdwAAAWcEAABo/AAAVwAAAjD4AAO1AAABzQAAAycAAAF1AAADuvwAAK8EAAITAAABCQAAA8MAAAPS/AAAHQAAAIcAAAGJAAAAWwQAAHcAAAIu+AADVvwAABsAAAC5AAACjvwAAmLwAAIbAAAAJQAAAr0AAAAO/AAAsQQAAn74AACTAAAAtQAAA/D8AACDAAAAfQQAAS8AAAEnAAABAPgAAoMAAAFFAAABsQAAAlMAAACrAAADwvwAAv78AAJW/AABMPwAAsEAAABhAAACvQAAAUUAAAB3AAACzwAAACEAAAJC/AABdQAAAyEAAALY/AADevwAAob8AALxAAACwPwAAjsAAAJhAAAAvQAAAQr8AANfAAADlQAAALsAAAF6/AABEwAAA5T8AALK9AAC0QAAA1cAAAJa/AAA+wAAAiUAAAH9AAAC/QAAAn8AAANE/AABvQAAAejsAAKa/AABpQAAA8r8AADA/AADkPQAAdMAAAEpAAAANwQAAC0EAAAPAAACUwAAAjUAAANM/AAAYQAAAAkAAAIC+AAALwQAAhkAAAAc/AADFPwAAEMAAAIxAAABGwAAAhz8AAGzAAACZQAAADcEAABU/AACqwAAASUAAAAHAAAAjQAAA9j8AAFQ/AABBQAAAS0AAAIhAAAAFQQAA70AAAClBAACXwAAAjkAAAMZAAAChPwAAhz0AAMTAAADSwAAAp0AAAOs+AAAIvwAAxD8AALPAAACWwAAALUAAAFjAAACaPwAACUAAAHXAAADpvwAAHcAAAApBAAALQAAA2L8AABNBAAALQAAABEAAAA+/AABVwAAATcAAAEDAAABnPwAAaEAAAJxAAACKwAAA7T8AAKTAAAAWQQAAlb8AACPAAAC/wAAA4T8AAHfAAACRwAAAVUAAAM0/AACqQAAACMAAAJbAAADYPwAAqsAAACc/AADnvgAALb4AAJvAAABAwAAANUAAABjAAACLvgAAqMAAACc8AAB0vwAAH0AAAF+/AADQvwAAgMAAACtAAAAbQAAA6T8AAFFAAAA5wQAAIkAAAG5AAACbPwAAwr8AAOq/AACxvwAAPD8AAKdAAACzPwAAZb8AAAbAAAAfPwAA3sAAAHY/AABQwAAAcsAAAJTAAACdwAAAbUAAAMy/AADtvwAAgkAAAAhAAADrQAAAmr8AAPXAAACHwAAAMMAAALTAAACtQAAAJkAAAFrAAACiQAAA+r0AAF1AAABXPgAA4EAAABu/AAD/PwAA28AAAAg+AADIvgAAmsAAACVAAABHQAAAIEAAAExAAACiwAAAK8AAANVAAACHwAAAYb8AAFHAAAAtwAAA0D8AAJa/AAAEQAAAPMAAALA/AABZQAAATsAAAL1AAAAewAAAQsAAANNAAABCQAAATsAAACdAAAAnwAAA/L8AAHvAAABBwAAACMAAALfAAABrPgAAKD8AALXAAABjvwAAtUAAAAPAAACbwAAA+74AAHy/AAAFPwAA30AAALxAAACFwAAAVsAAAAhBAADKQAAAGkAAAI8/AABzQAAAk0AAAMlAAAD6vQAAvcAAAEnAAAAbwAAAScAAANLAAAC8vQAAhEAAAENAAACivwAApUAAAIO/AAAbQQAA2j8AAOJAAAAtQAAAs0AAAKrAAAAwvwAAsb0AAJnAAABPPwAAB0AAABjAAADQwAAAFz8AANc/AACkvwAAMT8AAA1AAACQQAAAgMAAALQ/AABcQAAAksAAACa/AAAXPwAAvD8AANw/AABOPwAAqcAAAFNAAACmwAAAjEAAAPW/AABUQAAAvcAAAGjAAAApQAAAPEAAAAFAAADvQAAALT8AAJ+/AADdPwAABb4AADtAAAC1PwAAPsAAAILAAABWwAAAlz4AAJI+AAAjQAAAIsAAAKdAAADhQAAAAUAAAEC+AACbvwAAt8AAAJDAAAC0PgAAhUAAANI/AAC3QAAAikAAAPi+AACOwAAALkAAAM7AAACEvwAA7L8AAJPAAACQPgAAwsAAAJ7AAABXPwAAgj8AAOI/AAClQAAACEAAAI9AAADhPwAAIUAAACi+AABmPwAA778AAHM/AADOwAAAxsAAAHo9AABxQAAAvD8AAPO/AAAFwAAAAsAAANzAAAAFQQAACcEAAPU+AAC8QAAAKT4AALnAAADDwAAA0cAAAF/A", "world": 1, "sampling": [8402815308221694417, 0.7, 20, 0.5, 0.05], "position": 62367, "count": 28, "stats": [], "full": false, "token": 149} +{"logits": "B9T2PqI7bD90yGc/yuoOv4NsqLx8Aoc/NgYKPqY2Vz6z4k096deqO7sZrD4frbI+CHZFvviZoz7VWPG+/zPBvXQEMj/YulU+0CfqvvR5iz8A8vA+8kWePoRUW79qqu69jvMGv7T1qj72U4m97jeHvs0Ncr5u6rm+6h5mvjooGb/w84a/LidaPwX+Hb7d/qu+0cxYvxIZ577ata6/wiYMvzQIhL9VqXu+SXmUP0kVSj4HLNw9Bj0bvgjff76UVqU+6aF6PjfktD2bWn6/eO4PP7LvKj8nmn6+DoeJPhTrxj7VoAw+fEnju+KFQj/kTa0+ljSjPjw+oz92xeS9r5eOvg==", "world": 2, "sampling": [4653581798197582494, 1.3, 0, 0.99, 0.0], "position": 727784, "count": 64, "stats": [[1.0896592140197754, 15.956374460188286], [1.275336742401123, 13.294788135185827]], "full": false, "token": 44} +{"logits": "AABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwA==", "world": 2, "sampling": [4217093379447365224, 0.7, 20, 0.95, 0.0], "position": 971488, "count": 28, "stats": [], "full": false, "token": 9} +{"logits": "CfnDPwsFQcERcmJAFNF1QcBzgb42FphArDGxQFKsGEFCi2vBT4kEwSxJyUAHwE7AFktkwcjSLsFQwl3ANIs1QU+yk8GGGcK/j9tBQUdSoz90k/XALFkYQRoOq8BjXjc/aptLwSpXDcHqDuw+4Q0pQG6U2j0Fv27A1OZtwR7d9EDHhT1BjvNPvni0i8CGc5jBOwhDPlp3kcFHDdJAwUggQao9w73hDs4+Z10WQEjVCsEuCl7AQ1RTP1SEu8Akg51AIXu7v+JCd8DBfF9B3u1+v7t8l8D2zSVBz7usQPl6pcFbRr9A8uXGQAwZFUELD7BApDK0QCxOJsF4pz5Bi8W4wA==", "world": 2, "sampling": [2064312137021036135, 0.6, 0, 0.95, 0.0], "position": 507577, "count": 64, "stats": [[15.363544464111328, 1.005821095298125], [13.967957496643066, 1.0660626034192853]], "full": false, "token": 3} +{"logits": "AACFPgAAzLwAALY9AAB1PgAAErwAALy+AAA8vQAA+z4AAB6/AAA+PwAAJz0AAH6+AACwvgAANT8AADi7AAC5vQAAeT4AAA6+AADIvgAAsz4AAOE+AADBPgAAir0AAH6+AADlPgAAcr0AAI+/AACZPgAA/z4AAJO8AADTvQAAZz0AAL09AACyvgAAtT4AAA++AAAwvwAAyT4AAOm9AAD0vgAA9b4AACm/AADpvAAATT8AAKi+AAASvQAAmr4AANY+AAAZvgAAYr8AAIE/AAC4vgAAn74AAH4+AAA3vgAAqD4AAPg+AADmvgAAV78AABe+AABavgAAa74AALe+AAA/vQ==", "world": 2, "sampling": [5449768404199417945, 0.3, 0, 0.99, 0.0], "position": 911784, "count": 64, "stats": [[0.7421875, 6.052770111213705], [1.0078125, 2.622841720656623]], "full": false, "token": 53} +{"logits": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", "world": 2, "sampling": null, "position": 986129, "count": 9, "stats": [], "full": false, "token": 0} +{"logits": "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", "world": 1, "sampling": [8693240618906991338, 0.7, 0, 0.99, 0.0], "position": 27778, "count": 512, "stats": [[6.03125, 7.933660923299614]], "full": false, "token": 317} +{"logits": "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", "world": 2, "sampling": [1564305188008521931, 0.7, 1, 0.95, 0.0], "position": 752058, "count": 9, "stats": [], "full": false, "token": 670} +{"logits": "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", "world": 2, "sampling": [509711387434460336, 0.3, 0, 0.99, 0.0], "position": 523987, "count": 512, "stats": [[21.269813537597656, 1.7590161150799952], [21.369291305541992, 1.016903128659502]], "full": false, "token": 481} +{"logits": "jjHTPWvoy7+1KhNAS/YzQLAi0z8jJvq/mg13QDh7/ryaStc/v4r0Pqp7AcBm79K/HmEKv3mT0r86u4NARzLJv/GFLkAzRy4/+H0cwNt4378LcVVAqJ+MP6SXKsASKAc/oVbXPqvElz/nHci/jFkjv7G1ez82ZTfA8enWvvwaq79L5qm+5MetPl2uyz+LoZy+nWZNP1Jjk0BB5C++6klAv4I8xz+t/8y/uvX8P+VsXT9SsUfADXGwv5zemL9WQ0xAfIU/v/R9pz9KeDG/G2wOQKsmnT7hrYFAKq7kP2ozor8tBJPAFW+Rv0fb6z2H/bY/y55pP/BA5b7/bzg/Re6YPw==", "world": 2, "sampling": [5497948431090193914, 1.3, 0, 0.5, 0.0], "position": 38030, "count": 64, "stats": [[4.116604804992676, 4.469245575464131], [4.605874061584473, 3.3512285507913653]], "full": false, "token": 53} +{"logits": "+LQbwTc3xUBhfojBMCZ/Qelrm0F5UqDA3KEDwGr9xkDrRjC+RvVDwXsuo0C1q41AparcPVY2BMD4MfPAonFWv6pko7+2sjjBqhr/wDTxCkDVumpB7KGYPuTvZUFfFGRBYV5VwVQRPz+K4XJBq0R+QQl818Am3eBAEIkBQWusa0BLrJTA3DwiQcQQt0AK2nHB37a/wFMJw8B1uhtCggO4wIp1+kBk6ai+pbOWQZYTwb5ixBRBHt0dQQtua8A9s1/BL0YOvrxOaMExDRTB5/AAQVe4J8Aaz3lAOK2xQMsT+8Aq6wPATzNTQXoBqMHuxVBBBKtSQNUnFMFW4+O/cr81wQ==", "world": 2, "sampling": [1028448992721755268, 0.3, 20, 0.95, 0.0], "position": 313486, "count": 28, "stats": [], "full": false, "token": 38} +{"logits": "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", "world": 2, "sampling": [7470818699889355424, 1.0, 1, 0.8, 0.0], "position": 914155, "count": 9, "stats": [], "full": false, "token": 81} +{"logits": "AAChPwAAJ78AABDAAAAJQAAAckAAAE0/AACNvwAAHcAAAKk+AAAuwAAA2b8AAL4/AAA2vwAAJ0AAAOg/AAC3vwAAsT8AAOc/AACiPwAADMAAAAHAAAD3vgAAQsAAABlAAAAvvgAAuzsAAE8+AAB3wAAAKz8AAIm/AAAKwAAADr8AADo+AADSPgAAtD0AAH7AAACIvQAAB8AAACHAAACIvgAAxkAAAI1AAACXvwAAMcAAAJU+AAAgPwAALL8AAHC+AADKPwAAob8AAGVAAAB5PwAAMUAAAOi+AAB1QAAAv78AAOU8AAC6vQAAPz4AAGQ+AADdvgAACj8AAC/AAABWPw==", "world": 2, "sampling": [1942957567702031103, 0.3, 0, 1.0, 0.0], "position": 393649, "count": 64, "stats": [], "full": false, "token": 40} +{"logits": "AAC6PgAArz4AAAe/AADGvQAA8D4AAP4+AAANPwAADj8AAE6+AADcvgAAUr4AADm/AAArvgAAg78AAOa9AAAWvwAAfT8AAGC/AACfvgAA/74AABA/AAAuvgAAmz4AAM89AAARvwAACr8AANc+AADuPQAAnz4AABC/AAA5PgAAm78AAIy+AACJvwAAkD4AAL8+AABMvQAAsD4AAG6+AADuvgAA9D0AAGs/AACLPwAAML4AANQ+AACeQAAArz4AAHa+AACRvgAAKj4AAJo+AABXPwAAAL0AALO7AADWvwAAB78AAFG/AAD2vgAAjj8AAE8+AAC1PQAAsj8AACA/AAAwvg==", "world": 2, "sampling": [2956665435520002774, 1.3, 40, 0.8, 0.05], "position": 6352, "count": 48, "stats": [], "full": false, "token": 45} +{"logits": "8eoNQOGqEMDaxO4/6AypQDIFpMBZVa8/z6OiwIAG3D8elrbA0IQevy3ewj8E3FhAXrmtPnuuYsAyZ57AXpkjP+HZVkCWpdu/fBiXQHOwIUD/LAhB2UzuP/zjg7/86tO/L46wwBnANj83sek/kOF4P/Sj7b+3SRdAG1wDwQwAJMD9dIFAAiyAwMPhssCc4cfAKS73wHFz20AraFS++tPCwGA0NsAnRYRALIytwOE60z+PX1jA3ojev5HR5sDGdoK/o64PwNfYNkAs9vbAzIoPQASClr9RyQDAS5GNwC3CRkAQBnBAybVEP2TxiEBNKpLArU8Uvj69h0BHPUM/R8UFQQ==", "world": 2, "sampling": null, "position": 995069, "count": 9, "stats": [], "full": false, "token": 20} +{"logits": "AAA8PwAAAT8AALY9AAAKvwAAvb0AAHC+AAAvvwAA7r4AAD8+AABaPgAA2r4AAKg+AACQvgAAaT0AAC49AABdPgAAir0AADI/AACTvgAABj8AADS/AADzPgAACL8AADE/AAAFvwAAxr4AABG/AABHPwAAaL0AAMy+AADMvQAAHj8AAJM+AAANPgAAaT8AAK29AABavwAArT4AAFE9AAD2PAAAIz8AABG/AAAKvwAA+j4AABi+AABBPgAA8L4AAFi/AAA+PwAAQ78AAGy/AAAvPQAAuj4AAMG+AACGvgAAnD4AAKS/AAAvPwAARb4AALm+AAC9vgAASb4AAGs/AAAZvw==", "world": 2, "sampling": null, "position": 493039, "count": 9, "stats": [], "full": false, "token": 62} +{"logits": "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", "world": 1, "sampling": [8055498263137530238, 0.3, 20, 0.95, 0.05], "position": 418809, "count": 28, "stats": [], "full": false, "token": 601} +{"logits": "AABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwA==", "world": 2, "sampling": [3001985841620759949, 0.7, 0, 0.8, 0.05], "position": 425493, "count": 512, "stats": [[-3.0, 500.0], [-3.0, 500.0]], "full": true, "token": 793} +{"logits": "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", "world": 2, "sampling": null, "position": 860932, "count": 9, "stats": [], "full": false, "token": 46} +{"logits": "AAAowQAADkEAANXAAACzQAAAT8AAAApAAADzwAAAHEEAAIg/AAAkQQAAoj8AADPAAAA/wAAAPb8AABzAAACRQAAAksEAAGlAAAA0QQAAJz8AANfAAAAbwAAAL8EAANxAAAA4QQAAR0AAAFrBAAANwAAAGEEAAMvAAACzQAAABkEAADBBAACuwAAA+cAAAGA/AACKQAAA6D8AAIVAAAA7PwAApT4AAOE/AADRPwAABMAAAJFAAAD/PwAAMsEAAKu/AADsPwAAbD8AABQ/AADVwAAAH8EAADlBAAAJvwAA0sAAAODAAADIwQAAiL8AABDBAAC8QAAAa0AAAGXAAACTQA==", "world": 1, "sampling": null, "position": 449476, "count": 9, "stats": [], "full": false, "token": 53} +{"logits": "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", "world": 1, "sampling": [4236403893570094697, 0.6, 40, 0.5, 0.05], "position": 644454, "count": 48, "stats": [], "full": false, "token": 321} +{"logits": "AADVwAAA6r8AAKHAAACWvwAABsAAADQ/AAASwAAAbsAAAKBAAADFwAAAzcAAAPk9AACkvwAADcAAANA/AAC8vgAAhEAAAP1AAAD4PwAAx8AAAJxAAADbQAAADUAAAJhAAAAHvwAAlj8AAMK/AADpwAAACkAAAG3AAACpwAAAsb8AAMtAAACWwAAAFsAAAP6/AAAPvgAAwEAAAH+/AAD2vgAAhEAAAMe+AADxwAAAzEAAABdBAAAIPwAAlr8AAJu/AAC8wAAAJUAAANbAAAA2wAAAgD8AABxAAAB3wAAAk8AAAM1AAAANQQAAKUAAAAZAAACDwAAArj8AAPG/AADOvw==", "world": 2, "sampling": [4150015254158312572, 1.0, 40, 0.95, 0.0], "position": 726539, "count": 48, "stats": [], "full": false, "token": 44} +{"logits": "dYKFv04FtcArSLy/EegTQFy6z7/zFnHAiYwCQArHCUA7N5U/bMQeQBo55j//RENAYMQiv/FGL8BC30PALWYdwDhw/j6EYEE/OdpBPyGfez+wgKE9KeWQwLNxG78TFwFALzlMwE7NBcCz/VrAUbKdPkwvD0CwC+k+0r4hwD/M7r/Bt2pAsx+YQHDAKL1gMlXADK4sQGf/EcAgm0TAh4K+vwSeFMD1Y4q9Bo6Gv4fR1j138j/APZ0dvsbz6z95RhjAznhUv3v4zUDQ3mM/b++ivzvnq7+WW33A91mfP60XHsDjiZW+lNFIP/dfXEBJgSo/wlIcQuUWDD6jIYi+ExkzQA==", "world": 1, "sampling": [7940635526413781665, 0.3, 0, 1.0, 0.0], "position": 778123, "count": 64, "stats": [], "full": false, "token": 60} +{"logits": "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", "world": 2, "sampling": [5891574379358378226, 0.6, 0, 0.99, 0.0], "position": 896240, "count": 512, "stats": [[23.30751609802246, 1.0629354452849828], [25.65738868713379, 1.8233457172985965]], "full": false, "token": 563} +{"logits": "AABYvQAATj8AAES/AADLvgAAeT8AADo/AAC5PQAAx78AAH6/AACGPgAA974AAHO/AAD8PAAADL8AACw9AACpvQAARz0AADe/AACsPQAAbz4AACE/AAApPwAAhT4AAI8/AABOPgAAlz4AAJS+AADgvgAAQz4AAE2+AAB3PwAAHUIAAMK9AADIPQAApL0AAP8+AAAlvQAAnj0AAAa/AACFPgAAa70AALy9AABBvgAAbD8AALK9AAAsPwAAzz4AABG/AACNPwAAEL8AABK/AACPPwAA1L0AACK+AACfPgAAZD0AALs9AADtPgAAEj4AAEc+AAB4vwAAmz4AAJW/AAA+vQ==", "world": 1, "sampling": [2517740317445858494, 1.3, 0, 0.5, 0.0], "position": 195584, "count": 64, "stats": [[39.25, 1.0000000000054357]], "full": false, "token": 31} +{"logits": "e1OWQL2QicAZZA/A2guZQIe4cMDkyVW+5aK0QMJ/oLzpaC9A9NBpwPYjrT9MQ/bA02U5QCefIkAufH3AyoeDwG15EEAsnf+/f60Mvi5p8L8JVYRAH49KQGgVhT+kvMA/GQ+jP8ToaMA8AOlAbHYSPwGtBEFFOd5AwYJLwC9dzL80xQ08zgL+PmqEZkA8X4xAtQklv6CNq0CKAxRBWIoDwVjyF0Di+bxAhymYwANQQcAZUo7A0DTDv150vT9u4ivAFd+rwCXYScAPjxvAVpCWP16ClkBiXIO/3gQBQU8+60A4qQ9AvtLDwJqMUr2eIGw/RWgGwIRQbEA0BQ3AfmQjQD9vEUB3i8lABS8yv5A9dEB8gMO/RTkOP4tIDcBpYFlAtQa8QA5YfT0/sDhBkoeGwBf4CUDyYpdA0YvvPoZ0ZT+zWJXALjDMQKzRGkJeWYHAtQWqQPNVwsD1u3Q+F+MzQEpgAj91kNlAM/KdwLPkqEA/NjdA052TPxcglcBSQR4/fePGQGJTD0CgCak+GZx9PyXAW8DNLdFAhS7Gv3UAqj+NLZE8MQTlvxEA/L8xry7ARTTBwPz99EBXtJjA7PrdwBZ4EEBrTERA7BKWwKj+UECFUSc/F5vfPy2hrb6zlfXA8+sYQFw16j1USyE/8l9uwDQYA78i3kLAi3VTQPNn4UAr/HtAaG8ZwBQ1rUC2tHE/Kiy2wDoPLkD3YYlA+pqlQLcHcr+osP3A1SRqQM2PvD9wTFE/X2qXQIcOG0A1ZMI/lI7ZPlOCcz8lEU09agNHwEGNRUB2SBlAP2nawGsYgUBMFNk/9YwYwTl4CkAdJzjAUegrwc3/10CVqvw/MTEuv56vx8BgARPAx1NhQKkccT8iguI+cvuQQCK7DsDsTdfAzxO8vvMpYcAsMnzAJ8GRQCcPosD8UdC+fgujQJ+EyEDWvFu/93/6QG6QK0ApIZS/WH8WwCO0oD8d3nRAgtHGv6nJu7/oRwzBDJAZwI0tnL9D66Y+2vJwwBnDhsDl3qm/LSg7wIXNjUAPfQ3BnBwGvzRIEsFraCo/OoWBwKX9RcAulQXA9KamPv/OYD4bJek/6cNzv/7gmz35CLVAOZkavwzpk8B60S6/1V+RwNnnP0B9dlpATgGlwATEDECF+2A/EzHUv3j9gcD5NADApF+7Pw0MscDH++lAGFwIQPcxF8BMsZfAhW0hQXgR5D9s3SFAR4KEQM48GMDHwaLAJNPEwIZFw724HFVACG44wNfonT8iWmG+GU6HP/IpwL89F1VAEnpgPkIs0EAoLU+/NUOQvuFlqL91Ko7ADO38PXpZzsApz+w/JgnmvsJw2b9Mex2/dLmaQFQfEMBfIQHAJZnuP9RXG0CbB41AtugiQOF2qUC8hcE+xTrcvy/1u8B/O7U/Qf8eQKASacA1q6fAs4sWwZN68L8lylFAqfj5QJ0HD0CM6JFAdTGsP58GQT8kZy3AxZtQP2Klk8DwdLQ/4L1SwO+dWcDjqxlBQNIewJ31NUDKU7g/PsAcPzLbnED6tRxBOsVgwBhfvEBivVXABzWQvOjKMcCYHaG/azYav0kwyMBWtcE+YJQFQZs6Zb9teGk/CAMfPwqkn0D7DDXAh1jdPyStu8BnqHZAqY1LQEtD0MC8OgrANz23wFbyVsFCUXRA8F5RQC8gmMBYP6+/7YOnQM++4D9WJYU9vsPYv486gT9zH8e/RrD/PRaPgcD+mA7A7cliwPEJhECtzQ5AV7J9QNyaGcAIz4q/11MuQMwWIEC+ntLAXH2YQG/0XMA7wom/FNZeQM5MWEA505vA2pMcwMsS0T//fevAhq+JwMM11L4vTq2/N9FrwGY1QMCBqCpAsOf7PkmUqkCCLMo/WsSbPxn6jz+1Da/AODV0v5Wxf0AmxQpASBMSQA0OD8E6NUXAgSUOPmaQGEAZZ8E/deaCwAo9y0AIHOlARQ+Mvyi1AcEW28O/zrJlQI+ocb+1Pw/AGB2/P0ypNMAZSIdAtd05wPiyJ8D8fKvAe5TiwDA1VMB1F04/Da+FQIM+/r+9f7FAUqOUwFGVcUAhEkFBVqMtv+JNp8BjUv3AHp2IvzyKQ0DzxBnAZqwFPV9lokAGIPa/GsgywZNxN0Fo9y5AYK0MwFDFCsHFeizBJR9EwLmDmz8w4bU+uokbwI1Rrb6xnoG/qLnIv9IMpsADCBNBStb7v5IVZMGR4cM9RJWSwKe0MMCxW5s/cuUKwAQPD0C4fyTAHWpZwKsF677IZptAOL1wwBrFgL4ilo7AwumnQLZvEcBQZVLAqCmVwBQ+7MCeNdo/wtOzQNj2Nb8bh3bAFqJHwJbPrsBrSZpASgE/v/Pee8AcZaO/5kEewIQaQEBCXbHATNwWQN3dBD+r18G/DsWAwOSZzkDj8SlAltYtwHqxyD/6ZMHA7haewHSVjT+sWWi/l3JoQLM+NsA8lrlADYNtQHxrAD8Mr8O+LpamP9BRlEA2B+c/9unMwGYBLUHh23S/Jb1AQGu1tj+PHwdAg309QKDOI8BuP9pAKSMBQRvgoEDA43LA0J0EwTe/HcDcGT3AMa2ZP05J3L8BDKzARESqQCICg0DfHSTAn/u2v7R0iUCo8J6/UzPSQH80Wz+GDNW/LZCswNCmiEDsmrfA23mDQK13RcDr15HANqjHvZJ4ccCejmbA8jTQwJUslL/J6HNA0mjoQAEarr8H81w/dt1zP8l6kr/gu7BAoKonwFOx00A625/AupztQGcx/MAy4hbAGXILQFhyTMA2dUXANs31v9huUL/m3IVAavD+vmap2z+XY/6/9Sg9QI11hL9gJo7A6C76v93fmcBPW2RAutP+v8aR48Bk6qdAmljaP694S0DVU61AIdF0vCJuhT8Be6XAOdTkP7rurT9zx4NAnjTrwDEivUDki4bAEncBQEFmFT/ZA2E+wRNuP/3BlUA9b72/TzdbQHC0b7/TCi/AEi9BQH/6Ib9GwqDAmwBewLcvHL8DWqdAdnCtQOWelr60WDLA7sqMv/VOx7+UQGJA1LCdwG42jEBF2pA/nmYhPTIVrcB/s6O/UEojwMy4NkBANVfAJ3XFP5X2HcAFVVY/GYdYwOudEUBzPqlAhJRUPx6pib8d4XZARzFtPuxa40CjRuPAHVaHPR9HfD+t5w2/QiaXP5Qy2r+a+VE/BuH+vwZbikAKzehAJxA4Qb6VC0Bpnq4/", "world": 2, "sampling": null, "position": 615193, "count": 9, "stats": [], "full": false, "token": 82} +{"logits": "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", "world": 2, "sampling": [6896630898922646123, 1.0, 0, 0.95, 0.0], "position": 344972, "count": 512, "stats": [[1.6953125, 104.75318846965509], [15.375, 1.0001187460261776]], "full": false, "token": 522} +{"logits": "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", "world": 1, "sampling": [2411728117549415207, 1.3, 0, 0.95, 0.0], "position": 490337, "count": 512, "stats": [[13.5, 2.0363934669602326]], "full": false, "token": 548} +{"logits": "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", "world": 2, "sampling": [9158381809759211376, 0.7, 0, 1.0, 0.0], "position": 317864, "count": 600, "stats": [], "full": false, "token": 403} +{"logits": "AABmvwAAvD8AAJdAAADYvwAATj8AAA7AAAA9wAAAA0AAAMhAAACQQAAAIb4AACRCAAAZQAAAlL8AACs/AAADvwAAIz8AAGg8AABgwAAAeT8AACA/AACbvwAAjj4AADc/AAA2wAAAi78AAN2/AABkPwAAlD8AAP4/AAAZQAAAC0AAAB5AAACOQAAAQT8AACXAAAA3PwAA5j8AANK/AAC1PwAAtsAAAIVAAACrPwAAq74AAKy+AADHPgAApD8AAFhAAAC6wAAAlj8AAH0/AAAlQAAApEAAABo9AABLvwAAa78AACg/AACRvgAA/j4AAPe+AAAYPwAAWsAAAE2/AAACQA==", "world": 1, "sampling": [6399580127821990291, 0.3, 0, 0.5, 0.0], "position": 1027784, "count": 64, "stats": [[41.0, 1.0]], "full": false, "token": 11} +{"logits": "AADdPgAA8D4AAB+/AAB0PAAAnb4AAJK+AADZvgAAsr8AABi9AAAxvgAAtD4AABQ+AAD+PgAA4r4AALQ9AAD6vQAAr74AAIG9AABvQQAADj4AAIs9AACRPgAAX78AACC/AAAGPwAAvb4AAKW+AABVPgAAWb8AAAw+AAAOPgAASz0AAHK+AACUPQAAZL8AAGK/AAAWvwAAzzwAAGy9AACEvQAAqz4AAFU8AACNvwAADDwAAFS9AACGvwAA+TwAAFm+AABdPwAAir4AAJs/AAD4vgAAq74AAEI+AAAuPgAAEj8AAKY/AACzPgAACL8AAJs+AAB2PwAAbT8AAEs/AAA3vg==", "world": 2, "sampling": null, "position": 564851, "count": 9, "stats": [], "full": false, "token": 18} +{"logits": "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", "world": 1, "sampling": [7727759592410910042, 1.0, 0, 0.95, 0.0], "position": 28394, "count": 512, "stats": [[23.09699249267578, 1.7561819110858556]], "full": false, "token": 705} +{"logits": "AAB2wAAAJ8AAAM4/AADIvwAAQ8AAAB6/AACMPgAAPT8AAANAAACSwAAAUsAAACO/AAAlPwAALb8AAAnAAAA8vwAAEkAAAGvAAADsvgAANkAAAMa+AAARvgAAIcAAAGLAAAApvwAAZr8AAPG+AAAOwAAAIj4AAARAAAAMQAAAZr4AAGC/AAC4PgAAHj4AACVAAAC6PgAAsz8AAL+8AAAGQAAAhj8AAG2/AACdPwAApj8AAJG/AABSQAAAKUAAABQ/AACyPwAAZD8AANK/AADgvwAAAcAAAEi/AABKQAAAAsAAAJ0+AADlPwAAFkAAAKK/AACMvwAAAsAAAH2/AADkvw==", "world": 2, "sampling": [7964593186117017384, 1.0, 0, 0.5, 0.05], "position": 466795, "count": 64, "stats": [[2.84375, 4.093025949327643], [3.28125, 5.031897248399165]], "full": false, "token": 58} +{"logits": "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", "world": 2, "sampling": [7971103624041308224, 0.6, 40, 0.99, 0.0], "position": 780673, "count": 48, "stats": [], "full": false, "token": 105} +{"logits": "AABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwA==", "world": 1, "sampling": [917481593731582185, 1.3, 0, 0.95, 0.0], "position": 649766, "count": 64, "stats": [[-3.0, 64.0]], "full": true, "token": 19} +{"logits": "AACyPAAAIEIAAF3AAAAqwQAAzT8AAPK+AAAPwQAAob4AAAfAAAAUwAAAZ0AAAB3AAAD4vQAA0b8AAGM+AADCPwAAyT8AALdAAABrwAAAiL8AAGJAAACAwAAA3z4AAJ9AAADRQAAAFkEAAI4/AAANQAAAU74AAIfAAAA1wAAAW0AAAAdAAADfvwAA3z8AAEq/AAAdQAAAl8AAABe/AADBPwAAI8EAAELAAADXQAAAwEAAAOQ/AAB6QAAAr0AAAJ4+AABFwAAAX8AAAIlAAABkQAAA378AAPrAAACcwAAAZj8AAJHAAACGQAAAd8AAAK9AAADtQAAAr8AAAHK/AACdvw==", "world": 1, "sampling": [825767021967002929, 1.0, 0, 0.95, 0.05], "position": 437327, "count": 64, "stats": [[40.0, 1.0000000000000708]], "full": false, "token": 1} +{"logits": "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", "world": 2, "sampling": null, "position": 470167, "count": 9, "stats": [], "full": false, "token": 875} +{"logits": "1QsEQFVU88B4AQTB388wwGORRMDM2l/AJUnFv/FQDD9f/JdAR54tP9fXecCJwpxA9K5EwMgTJsBPbRjADmzrwEHev8BgTQPBLJIRP6ZWVMAbU/dAe4BeQIPBLUABbsZAwXqHQBjtbb428kzA+C4AwdRwkT9M2z0/yV2IP1NhWMDdDA4/YkH+v/7ncUDLQ76/yZLqv+g0uz+xumQ/IAhuQN25C8CdOdq9/ZUZP1ebfEBZx1/AfHcPQHFGVcCeu/o/sFiqwChZpcDNcvo/xhmlwHA4uD+aOLzA0J1yQGGzSsDtSarAtOvFPzGnUj6Hs9FA3TmMP6Wk3kDvVm2/vqP5v9VMAr4C6mlAb3oEPVK6H8BdyCFA6GK5P8W7oz1siZo/bLNrPiCfN0BbSC3ARZAYP8f4CUBAY3nAfKicwNdYr0CmAa2+JAGgQARFdb8jh4FA2OtzvzbXBkDMnCDA4jkhQORR0EAL8pBAJP8HQTGi9j9N5eZAipPuPqwfisAiPa5Ao6zHvxY1cD/dwyg/TGndv2dRm0CNJj0/iivRP8CNncBO0YzAysvFwOofmMAC7hRAkgpXQJgaxUA8OurA1afAQCLLDEFPX47AhEchP26LIEBn2c+/xRSJP1h5DEC/4QFBz0TaQEtF80DTEA5AS/AeQL8BoMBp+EBAWa/4QDSfyr8Nx9i8pkRYP/1XzUDXZMzAOz6Sv28rpz9aNhm/76aEQDq3Y8BkqTY+Vms3wItEgMCFy3U//TW9QMQUhD2FiDtBN2eNP3/y+cBUpFFAfOM2QELkHUCTTIU/QAN0Pw5XpUD8e5HA41NJQBWLMUDUBUa//TDNv9vO/b5+hcDA2vaWQGWFwr9Ahs9AS1hwQEBuSkAuG7U9Qq08P/3YvcDbZ1HAXCwiP2bz/L21aek/q5+oQGPvukAF3NpAjtYmwPq51Tw2iFVAJa61wFccnUDCilu/92i3wJiDjL4EVrW9cLOuwPWW/z/LFw6/aoMkv2uZhEAaetI/IbkUwfiuf8CIgQ3BqOgtwQRc9sDDFVRAhfFJQGbnuMBkgnC/ApVgvfXnBEF6tfm+PSMnP75Rt8DqSXk/1kH0P9hJYL/78iTBP4ozwHQjIkFKXwDB+rvFQJTu17/64JRA76l7wNPZgkCH1UlAS/zKQCK+ab+1i5hArh2Jv7iz+z29rYA/DrQcQDBj2D5FeGO/R4mTwCO6GUA/nRdAySMRQM9vl8AYKQvAsCtPQFynx0DMvZK+i2G7wNk/pkA8y5tA9Ss+P6/qub+JIJc/RAXUvtS1lEDDSKnAx4VuQBfTSMDsQ8o/g5rTvXz9yr4SS15AD8ypvq6fFUFPOofAZEXMvw6z8sD8eLk/MpzXwMGSGEDaYahA8nfAwJ9ukb8qGmlAWUGqwEptJkCTdtpAmJldQGKCXT73BRFASKOZwLhTm8AXXqe/VLczQIb4a0DFLa+/36pPQOqFt8CJ5qlA0C9EQEYcnUDEygLA3BZtwB+zCsDmbKHAgLZzQLQi/8DUfQY/d1tDQND+OEBAAJbAJc7LPZSNPEBdBbbAQUwPQAPCisDk4Ym/QD3HwNeQOEAPtCrA2oG3QMNeg8DvX0c/zlyXv4UGvcDikSvAklgTP6uo/8ALehNACx90v7GKqr+HIzW9VjOcQC2Fd8CRXOY/NUW7QElVrj51Lx1AFsKJQP3Nob8bQpVAmxeWvhZpKj8bwom/mRyRwPpRu7+87cjAVXS0QCkip8BvSn/AelR0wFFMU76+O3JACDrLvjalosB3Hw7AHGKCwEiqDUDzkgc++d5MPmViE8HNlyS+oA3owLbe4rwF5AXBPDgeweEKHL4TMBdApr8ZwUow1T///W7Aeu0mQFQICr91Wt69VJm9v2tIND4APIRA/3ESQN1l28Dwp2xAAqHdQOEZt8Bzr78/t8LoP33QgD/bmmzACWeOvvcL078fHmK/Nko/vuIKGMDsWyK/UK6lvq6JQ7+Iy50/yGZIv/fIJEBeMY+/MM+zv8iEEcCIGN7A5eFwQD1Cb0CtwzXAGIjhQBE+Gj93Q7s/8vAQwXbQo8DxHM4/RRw8QG0eo7/zela/UZgDvr0l5D8x6Py+YohpwIoC60CdiIDAYkikvogEzsDDFcM/rkSpPgJ51cAzBwc/MOZtwApJPT/Tlb+/48vWv5mlvr8dL0HAh3dcQI+err7i1zI/TfUzQPf6Ij9WIbdA0NcswORBnkAP6tK/7qJYQHvtqb/qUYQ9+Ha6vkOY8UBcrELAqIN2P9NbJ0CUkBPAA5qLwH13db90K9PAtHx1wIVdP71dXCnAngcvv3bbMcCL2vxA8euywFwSusBMJPI/+aF/P0qjlb2QdGQ+O0e5QDqIu7+KdgtA76PtwC6M872QV529t2HVP5Ym5D/LmHXA3hbIQBO+I0Dqi7JA8sjaPj+CHkAhyA3AhnFFwFXX8kAnJ47A6mG1QHwcrECq6L8/isr7P73hXT9cW42/AuCLQItBHcDODF3AxpWlwKwnQkCQKQi+9IPhwLXpmcCgLnxAReJZQCR7CkDmM7fAvruQQNu+Fj+/HpG/8645QF0dJkCYJU6/joEPQIcfJMDB3t7Awo0oQFWI9UCSru0+A82GwDsH+b+UN7c/O3zzPz1BQ7+8zBE/WzJNQGPa1EB0zHu/pBwAwadrxL+KIzW/bS0CwJRmlkDeVIZAIyHeP1RSqkBuBTZAK59mwDQpIcAE5AjADQoywIAY2sAE8g9Bgq8AQMD5i78BiORAfdoMQKB9cMCAv7/ACaVLQLr3E0HIeKk+jJ4nQQ228b4PykfA5yINwMkoC0FuN8dArx8NQMuxEz4W8nK/79m/P8wN2T4Emus+NOFbwJ0L4bzEZMhASRiWwFTLsT92LMFAJOgPwbyaJUCoZ4k/DyncQNKoj8B8DYq/Y372v+YbI79spHk/fuJBwP3gN7/Ej93Aou4bwMTPHUCcB7g+OXw4P8WZCsA11je/MTABQTgKgUASuivA2zl4P5HYHECZsse+M0zLQLV5PcDld08/hxAPP648xL8F6GvAszPRP6V0LUB1bIc++bsbwDcLkj9OJSHAaiWDQD9GecAidc5AVbWgQDlsusD1TCBAga1MQNXbW7+jjD1AQVp/wAD49b5BbqHA+0cEQWiEj79RAR++yWCov/gxgMCTTy9AW4gSwHroob5e6xfAmWTsP0diCT2f2hLA", "world": 2, "sampling": [8385309347531864483, 1.0, 0, 0.95, 0.05], "position": 222757, "count": 512, "stats": [[11.720829963684082, 1.6388780508879695], [10.476207733154297, 2.5380009521449303]], "full": false, "token": 522} +{"logits": "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", "world": 2, "sampling": [1863564344454920592, 0.7, 1, 0.5, 0.0], "position": 40762, "count": 9, "stats": [], "full": false, "token": 49} +{"logits": "AACPQAAAh0EAABLAAAAOPwAAU0EAAFzBAACAwAAAS0EAAHrAAAClwAAAlEEAAB5BAACPQAAAkEEAAItBAAAAQQAAo8AAABfBAACFvwAA8T4AAJFAAACewAAALEEAAPe/AAAOQQAAFMEAALk9AAA4wAAAQ8EAAFrBAABEQAAABcEAAPE/AADhwAAAuMAAAEzBAAATvwAArL8AAAe/AACxwAAA6EAAAJVBAAAdQAAAsDwAADLBAADuQAAAFUEAAGbBAADRPwAAh8AAALBAAABUQQAAQMAAAKnBAADwwAAAJr8AALTAAACBwAAAAEAAAI9BAAAWQQAANUEAAONAAADtvw==", "world": 2, "sampling": [8742572511829253156, 1.3, 40, 0.95, 0.0], "position": 676502, "count": 48, "stats": [], "full": false, "token": 41} +{"logits": "AABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwA==", "world": 1, "sampling": [8781272243357171815, 1.0, 0, 0.99, 0.0], "position": 17681, "count": 64, "stats": [[-3.0, 64.0]], "full": true, "token": 15} +{"logits": "LTx9PjOdB766dJi/x7OMP6OTlz88wYZBbo4KQMLSMkCxxok/fEsHvtmO9T+8Iv0+dphEPxYSDcBzpay+OVMVQPupsT/wtEZAbL7OPnX4f0C2YH4+eHiEwFhvaUDPI6c/9LuOvuRG377KWAI/JRFrwBLpWL/lB8s/B+0yQCsgMb79jEfAafMuwGlIlj8IYsC/fzCPwBtqIMCcswq/+D9BvrRxG79s0CE+pHABwCExLL/61PW+OcvQvy4gRL/Bk2g/oQIrQBuFnD4vRR1AYvSsv/tGED2oT0HAdNMxwIC9Er8sdWo/GHmrPyJwEsBnAr29u4ccwFJdKUDfOw3AJQMFQA==", "world": 2, "sampling": [5662083960203238540, 0.3, 1, 1.0, 0.05], "position": 751577, "count": 9, "stats": [], "full": false, "token": 5} +{"logits": "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", "world": 1, "sampling": [5837550548726644620, 1.0, 40, 1.0, 0.0], "position": 859114, "count": 48, "stats": [], "full": false, "token": 203} +{"logits": "AABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDA", "world": 2, "sampling": [1617361617655023375, 0.6, 0, 0.8, 0.0], "position": 919886, "count": 512, "stats": [[-3.0, 300.0], [-3.0, 300.0]], "full": true, "token": 270} +{"logits": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", "world": 2, "sampling": [9061196699207699641, 1.0, 1, 0.5, 0.0], "position": 55956, "count": 9, "stats": [], "full": false, "token": 0} +{"logits": "AACUvwAA/kAAALfAAAAzwQAAf8AAACNBAACwQAAAfcAAAIrAAAAwQAAAoUAAAE5AAAD/vgAA178AAODAAAAVvwAAKUEAAIpAAABfwAAAlj8AAL89AAB4QAAA+MAAAOtAAACxwAAAJkAAAJ3BAAB3wQAARsEAABPBAABVQAAAYb8AAJrAAACevwAAnEEAAN5AAABtvwAA7j4AACJBAAApQAAAsL4AAIZAAAAAQQAAPsEAADdBAABMwAAALEEAAOLAAADAwAAA1EAAAApAAAA5wQAALkEAADfBAAA9wQAAZcEAAA9BAAClwAAAQsEAALU/AABJwAAAZMEAAD9AAAAnQA==", "world": 1, "sampling": [6093220874619830383, 0.6, 1, 0.95, 0.05], "position": 615794, "count": 9, "stats": [], "full": false, "token": 34} +{"logits": "AADGQAAAR0AAAMQ/AAAGvQAAnD8AAAnBAABMQAAAvEEAALvAAAAvQAAA2T8AAP++AADWvQAAVMAAAODAAAAyvwAAOD8AAM3AAAAMPgAAicAAACJAAACzwAAAxz8AAHI/AACOvwAAAkAAAHS+AADsPwAAU8AAAKVAAABRwAAA8r4AAFnAAACXvwAAaL8AAHI/AACcQAAAeUAAAIm+AADVwAAAUMAAAPq+AACBPwAAbr8AAFFAAACcQAAAfMAAAKs/AABHPwAA1z8AAHdAAAB4wAAAtMAAAHC/AACdQAAAoUAAAN6/AADjQAAACkAAAHhAAACKwAAA8T8AAKa+AACBQA==", "world": 2, "sampling": [8263678241864472974, 0.6, 0, 1.0, 0.0], "position": 866850, "count": 64, "stats": [], "full": false, "token": 7} +{"logits": "AAD5QAAAekAAAE/AAAA3wAAAqUAAAN/AAAC9wAAAcUAAAI5AAADoQQAAlEEAADRBAAAXwQAAeb8AAPTAAADFwAAALkEAAJ3AAADdwAAA/MAAAH1AAADLPwAAdEAAAN7AAACKvwAAqj8AAAu/AAAowQAA5sAAABzBAAAzwAAAekEAAFjAAACswAAAHD4AAFxAAADZwAAAgz8AANM/AAAtQQAA18AAAM5AAAD4PwAACMEAAA7BAADzQAAAfT8AAKFAAAD5wAAA68AAAL4/AACvwAAAXUAAAArBAAB1QAAAzEAAAAFBAABTQAAA2UAAAGG/AAAIvwAAdD4AAK/AAAD1QA==", "world": 2, "sampling": [3581172677032365021, 1.0, 20, 0.95, 0.0], "position": 959186, "count": 28, "stats": [], "full": false, "token": 9} +{"logits": "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", "world": 1, "sampling": [3442466408928812906, 1.3, 0, 0.5, 0.0], "position": 449597, "count": 512, "stats": [[24.125, 2.794891142379392]], "full": false, "token": 398} diff --git a/zig/src/families/deepseek_v41/fixtures/widths-exl3-2p8.txt b/zig/src/families/deepseek_v41/fixtures/widths-exl3-2p8.txt new file mode 100644 index 0000000000..317ae6ed73 --- /dev/null +++ b/zig/src/families/deepseek_v41/fixtures/widths-exl3-2p8.txt @@ -0,0 +1,44 @@ +# expert widths of a 2.8 bpw EXL3 pack, world 2 rank 0: layer w1 w2 w3 shared_k2(w1,w2,w3) rank_bytes +0 4:384 4:384 4:384 10,10,10 1709752320 +1 4:384 4:384 4:384 10,10,10 1709752320 +2 4:384 4:383,6:1 4:384 10,10,10 1710489600 +3 4:384 4:384 4:384 10,10,10 1709752320 +4 4:384 4:383,6:1 4:384 10,10,10 1710489600 +5 4:384 4:384 4:384 10,10,10 1709752320 +6 4:384 4:384 4:384 8,8,8 1707540480 +7 4:384 4:382,6:2 4:384 10,10,10 1711226880 +8 4:384 4:376,6:8 4:384 12,12,12 1717862400 +9 4:382,6:2 4:369,6:14,8:1 4:382,6:2 12,12,12 1726709760 +10 4:376,6:8 4:360,6:23,8:1 4:376,6:8 12,12,12 1742192640 +11 4:382,6:2 4:364,6:20 4:382,6:2 12,12,12 1729658880 +12 4:382,6:2 4:373,6:10,8:1 4:382,6:2 12,12,12 1723760640 +13 4:384 4:380,6:4 4:384 12,12,12 1714913280 +14 4:384 4:384 4:384 10,10,10 1709752320 +15 4:384 4:384 4:384 10,10,10 1709752320 +16 4:384 4:384 4:384 10,10,10 1709752320 +17 4:384 4:384 4:384 8,8,8 1707540480 +18 4:384 4:384 4:384 6,6,6 1705328640 +19 4:71,6:222,8:90,10:1 4:34,6:126,8:217,10:7 4:71,6:222,8:90,10:1 12,12,12 2737520640 +20 4:41,6:162,8:166,10:15 4:18,6:69,8:221,10:76 4:41,6:162,8:166,10:15 12,12,12 3051601920 +21 4:18,6:104,8:216,10:46 4:5,6:38,8:182,10:159 4:18,6:104,8:216,10:46 12,12,12 3353886720 +22 4:13,6:69,8:226,10:76 4:3,6:25,8:145,10:211 4:13,6:69,8:226,10:76 12,12,12 3515351040 +23 4:24,6:55,8:220,10:85 4:21,6:31,8:139,10:193 4:24,6:55,8:220,10:85 12,12,12 3472588800 +24 4:14,6:117,8:221,10:32 4:6,6:34,8:213,10:131 4:14,6:117,8:221,10:32 12,12,12 3306700800 +25 4:28,6:175,8:167,10:14 4:5,6:66,8:237,10:76 4:28,6:175,8:167,10:14 12,12,12 3090677760 +26 4:31,6:239,8:108,10:6 4:8,6:116,8:228,10:32 4:31,6:239,8:108,10:6 12,12,12 2901934080 +27 4:54,6:199,8:126,10:5 4:20,6:113,8:219,10:32 4:54,6:199,8:126,10:5 12,12,12 2876129280 +28 4:109,6:213,8:59,10:3 4:39,6:178,8:146,10:21 4:109,6:213,8:59,10:3 12,12,12 2606284800 +29 4:116,6:219,8:47,10:2 4:47,6:189,8:136,10:12 4:116,6:219,8:47,10:2 12,12,12 2548776960 +30 4:164,6:192,8:28 4:76,6:217,8:88,10:3 4:164,6:192,8:28 12,12,12 2374041600 +31 4:180,6:177,8:26,10:1 4:84,6:213,8:84,10:3 4:180,6:177,8:26,10:1 12,12,12 2341601280 +32 4:188,6:172,8:24 4:84,6:205,8:92,10:3 4:188,6:172,8:24 12,12,12 2329804800 +33 4:187,6:175,8:22 4:92,6:217,8:72,10:3 4:187,6:175,8:22 12,12,12 2307686400 +34 4:208,6:157,8:19 4:122,6:207,8:53,10:2 4:208,6:157,8:19 12,12,12 2234695680 +35 4:65,6:186,8:121,10:12 4:36,6:97,8:207,10:44 4:65,6:186,8:121,10:12 12,12,12 2870231040 +36 6:147,8:205,10:32 6:73,8:207,10:104 6:147,8:205,10:32 12,12,12 3263938560 +37 6:91,8:222,10:71 6:47,8:158,10:179 6:91,8:222,10:71 12,12,12 3478487040 +38 6:50,8:129,10:205 6:32,8:61,10:291 6:50,8:129,10:205 12,12,12 3830169600 +39 6:48,8:103,10:233 6:38,8:42,10:304 6:48,8:103,10:233 12,12,12 3879567360 +40 8:128 8:128 8:128 8,8,8 1141309440 +41 8:128 8:128 8:128 8,8,8 1141309440 +42 8:128 8:128 8:128 8,8,8 1141309440 diff --git a/zig/src/families/deepseek_v41/forms.zig b/zig/src/families/deepseek_v41/forms.zig new file mode 100644 index 0000000000..ad5250d048 --- /dev/null +++ b/zig/src/families/deepseek_v41/forms.zig @@ -0,0 +1,93 @@ +//! The run-time weight forms built at load from the loaded weights: strips, lanes, bf16 mHC, expert tables. + +const std = @import("std"); +const cuda = @import("cuda"); +const load = @import("load.zig"); +const plan = @import("plan.zig"); +const prepare = @import("prepare.zig"); +const named = @import("named.zig"); + +pub const Source = struct { + d: *const cuda.Driver, + weights: *const load.Weights, + plan: *const plan.Plan, + + fn download(s: *const Source, a: std.mem.Allocator, name: []const u8) ![]u8 { + const t = s.weights.get(name) orelse return error.MissingWeight; + const host = try a.alloc(u8, t.len); + try cuda.DeviceBuffer.download(.{ .d = s.d, .ptr = t.ptr, .len = t.len }, 0, host); + return host; + } + + /// (K tiles, N tiles, int16 words a tile) of a group's trellis. + fn trellis(s: *const Source, w: []const u8) ![3]usize { + var b: [256]u8 = undefined; + const t = s.weights.get(try std.fmt.bufPrint(&b, "{s}.trellis", .{w})) orelse return error.MissingWeight; + if (t.rank != 3) return error.NotTrellis; + return .{ t.shape[0], t.shape[1], t.shape[2] }; + } +}; + +/// A persistent role's initial bytes when it is a weight form (null: state, scratch, tables the forward fills). +pub fn build(a: std.mem.Allocator, s: *const Source, role: []const u8) !?[]const u8 { + if (!std.mem.startsWith(u8, role, "s.")) return null; + const lanes = std.mem.endsWith(u8, role, ".lanes"); + if (lanes or std.mem.endsWith(u8, role, ".T")) { + const w = role[2 .. role.len - (if (lanes) ".lanes".len else ".T".len)]; + const tr = try s.download(a, try std.fmt.allocPrint(a, "{s}.trellis", .{w})); + const g = try s.trellis(w); + const st = try prepare.strips(a, tr, g[0], g[1], g[2]); + return if (lanes) try prepare.lanes(a, st, g[0], g[2] / 8) else st; + } + if (std.mem.endsWith(u8, role, ".fn16")) { + const src = try s.download(a, try std.fmt.allocPrint(a, "{s}.fn", .{role[2 .. role.len - 5]})); + const out = try a.alloc(u8, src.len / 2); + for (0..src.len / 4) |i| std.mem.writeInt(u16, out[2 * i ..][0..2], named.bf16Round(@bitCast(std.mem.readInt(u32, src[4 * i ..][0..4], .little))), .little); + return out; + } + if (!std.mem.startsWith(u8, role, "s.L")) return null; + const ex = std.mem.indexOf(u8, role, ".ex.") orelse return null; + const layer = role[2..ex]; + const what = role[ex + 4 ..]; + const proj: usize = switch (what[what.len - 1]) { + 'g' => 0, + 'u' => 2, + 'd' => 1, + else => return null, + }; + const projs = [_][]const u8{ "w1", "w2", "w3" }; + const w = projs[proj]; + if (std.mem.startsWith(u8, what, "suh") or std.mem.startsWith(u8, what, "svh")) { + const kind = what[0..3]; + const routed = try s.download(a, try std.fmt.allocPrint(a, "{s}.moe.{s}.{s}", .{ layer, w, kind })); + const shared = try s.download(a, try std.fmt.allocPrint(a, "{s}.moe.shared.0.{s}.{s}", .{ layer, w, kind })); + return try std.mem.concat(a, u8, &.{ routed, shared }); + } + const L = try std.fmt.parseInt(u32, layer[1..], 10); + const lp = for (s.plan.layers) |*l| { + if (l.index == L) break l; + } else return error.LayerNotPlanned; + const lay = &lp.experts[proj].layout; + const E = lay.words.len; + const shared = try std.fmt.allocPrint(a, "{s}.moe.shared.0.{s}", .{ layer, w }); + if (std.mem.startsWith(u8, what, "k2")) { + const out = try a.alloc(u8, 4 * (E + 1)); + for (lay.words, 0..) |words, i| std.mem.writeInt(i32, out[4 * i ..][0..4], @intCast(words / 8), .little); + std.mem.writeInt(i32, out[4 * E ..][0..4], @intCast((try s.trellis(shared))[2] / 8), .little); + return out; + } + if (std.mem.startsWith(u8, what, "tp")) { + // trellis address = ragged base + offset, shared expert last; a uniform stack (".trellis"): same offsets + const data = s.weights.get(try std.fmt.allocPrint(a, "{s}.moe.{s}.data", .{ layer, w })) orelse uniform: { + const t = s.weights.get(try std.fmt.allocPrint(a, "{s}.moe.{s}.trellis", .{ layer, w })) orelse return error.MissingWeight; + if (t.len != lay.trellisBytes()) return error.ExpertStackSize; + break :uniform t; + }; + const sh = s.weights.get(try std.fmt.allocPrint(a, "{s}.trellis", .{shared})) orelse return error.MissingWeight; + const out = try a.alloc(u8, 8 * (E + 1)); + for (0..E) |e| std.mem.writeInt(u64, out[8 * e ..][0..8], data.ptr + lay.byteOffset(e), .little); + std.mem.writeInt(u64, out[8 * E ..][0..8], sh.ptr, .little); + return out; + } + return null; +} diff --git a/zig/src/families/deepseek_v41/forms_inplace.zig b/zig/src/families/deepseek_v41/forms_inplace.zig new file mode 100644 index 0000000000..22b4454bbc --- /dev/null +++ b/zig/src/families/deepseek_v41/forms_inplace.zig @@ -0,0 +1,127 @@ +//! TF_DSV41_FORMS_INPLACE=1: the dense forms (s..lanes / .T) written over their trellis, as dense3.to_lanes does. + +const std = @import("std"); +const buffers = @import("buffers.zig"); +const prepare = @import("prepare.zig"); + +const log = std.log.scoped(.dsv41); + +pub fn enabled() bool { + const v = std.c.getenv("TF_DSV41_FORMS_INPLACE") orelse return false; + return std.mem.eql(u8, std.mem.trim(u8, std.mem.span(v), " "), "1"); +} + +pub const Bound = struct { roles: u32 = 0, bytes: u64 = 0 }; + +/// The source weight of a dense form role (`` of "s..lanes" / "s..T"), null for any other role. +pub fn sourceOf(role: []const u8) ?[]const u8 { + if (!std.mem.startsWith(u8, role, "s.")) return null; + for ([_][]const u8{ ".lanes", ".T" }) |suf| if (std.mem.endsWith(u8, role, suf)) return role[2 .. role.len - suf.len]; + return null; +} + +/// Binds `plans[0]`'s forms (r: run.Runner, weights: load.Weights) unless read, a source's two, or too large. +pub fn bind(gpa: std.mem.Allocator, r: anytype, weights: anytype, plans: []const *const buffers.Plan) !Bound { + // sources with more than one form role: each would overwrite the trellis the other is built from + var forms: std.StringHashMapUnmanaged(u32) = .empty; + defer forms.deinit(gpa); + for (plans[0].sizes.keys()) |role| if (sourceOf(role)) |w| { + const g = try forms.getOrPut(gpa, w); + g.value_ptr.* = if (g.found_existing) g.value_ptr.* + 1 else 1; + }; + var out: Bound = .{}; + var name: [192]u8 = undefined; + for (plans[0].sizes.keys(), plans[0].sizes.values()) |role, size| { + const w = sourceOf(role) orelse continue; + if (forms.get(w).? != 1 or r.addressOf(role) != null) continue; + const tr = std.fmt.bufPrint(&name, "{s}.trellis", .{w}) catch continue; + const t = weights.get(tr) orelse continue; + if (size > t.len) continue; + const read = for (plans) |p| { + if (p.weights.contains(tr)) break true; + } else false; + if (read) continue; + try r.external(role, t.ptr); + out.roles += 1; + out.bytes += t.len; + } + log.info("forms in place (TF_DSV41_FORMS_INPLACE): {d} dense forms over their trellis, {d:.2} GiB not allocated", .{ out.roles, @as(f64, @floatFromInt(out.bytes)) / (1 << 30) }); + return out; +} + +const testing = std.testing; + +/// The weights' and runner's parts `bind` uses (load.Weights.get, run.Runner.addressOf / external). +const Span = struct { ptr: u64, len: usize }; +const Weights = struct { + map: std.StringHashMapUnmanaged(Span) = .empty, + fn get(w: *const Weights, name: []const u8) ?Span { + return w.map.get(name); + } +}; +const Roles = struct { + gpa: std.mem.Allocator, + addrs: std.StringHashMapUnmanaged(u64) = .empty, + fn addressOf(r: *const Roles, role: []const u8) ?u64 { + return r.addrs.get(role); + } + fn external(r: *Roles, role: []const u8, addr: u64) !void { + try r.addrs.put(r.gpa, role, addr); + } +}; + +test "forms in place: each dense form bound to its own trellis; read, shared, larger or other roles keep their own" { + const gpa = testing.allocator; + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + const a = arena.allocator(); + var w: Weights = .{}; + defer w.map.deinit(gpa); + const ws = [_]struct { []const u8, u64, usize }{ + .{ "L0.attn.wq_b.trellis", 0x10000, 4096 }, + .{ "head.trellis", 0x20000, 1024 }, + .{ "L1.attn.wo_b.trellis", 0x30000, 512 }, + .{ "L2.attn.wq_a.trellis", 0x40000, 256 }, + .{ "L3.attn.wkv.trellis", 0x50000, 256 }, + }; + for (ws) |x| try w.map.put(gpa, x[0], .{ .ptr = x[1], .len = x[2] }); + var p: buffers.Plan = .{ .a = a }; + for ([_]struct { []const u8, u64 }{ + .{ "s.L0.attn.wq_b.lanes", 4096 }, // bound + .{ "s.head.T", 1000 }, // bound (smaller than its trellis) + .{ "s.L1.attn.wo_b.lanes", 1024 }, // larger than its trellis: own buffer + .{ "s.L2.attn.wq_a.lanes", 256 }, // its trellis is read by a 4K program: own buffer + .{ "s.L3.attn.wkv.lanes", 256 }, // two forms of one source: both their own + .{ "s.L3.attn.wkv.T", 256 }, + .{ "s.L4.attn.wq_b.lanes", 64 }, // no trellis on this rank: own buffer + .{ "s.L0.hc_attn.fn16", 64 }, // not a dense form + .{ "w.x", 64 }, + }) |x| try p.sizes.put(a, x[0], x[1]); + var k4: buffers.Plan = .{ .a = a }; + try k4.weights.put(a, "L2.attn.wq_a.trellis", {}); + var r: Roles = .{ .gpa = gpa }; + defer r.addrs.deinit(gpa); + const got = try bind(gpa, &r, &w, &.{ &p, &k4 }); + try testing.expectEqual(@as(u32, 2), got.roles); + try testing.expectEqual(@as(u64, 4096 + 1024), got.bytes); + try testing.expectEqual(@as(?u64, 0x10000), r.addressOf("s.L0.attn.wq_b.lanes")); + try testing.expectEqual(@as(?u64, 0x20000), r.addressOf("s.head.T")); + for ([_][]const u8{ "s.L1.attn.wo_b.lanes", "s.L2.attn.wq_a.lanes", "s.L3.attn.wkv.lanes", "s.L3.attn.wkv.T", "s.L4.attn.wq_b.lanes", "s.L0.hc_attn.fn16", "w.x" }) |role| + try testing.expectEqual(@as(?u64, null), r.addressOf(role)); + try testing.expectEqualStrings("L0.attn.wq_b", sourceOf("s.L0.attn.wq_b.lanes").?); + try testing.expectEqual(@as(?[]const u8, null), sourceOf("L.x.lanes")); +} + +test "forms in place: strips and lanes are the trellis's own length, so a form fits over it" { + const gpa = testing.allocator; + // kt 2 K tiles, nt 8 N tiles, 32 int16 words a tile (K2 4): one strip, one lanes group pair + const src = try gpa.alloc(u8, 2 * 8 * 32 * 2); + defer gpa.free(src); + for (src, 0..) |*b, i| b.* = @truncate(i *% 37 +% 11); + const st = try prepare.strips(gpa, src, 2, 8, 32); + defer gpa.free(st); + const ln = try prepare.lanes(gpa, st, 2, 4); + defer gpa.free(ln); + try testing.expectEqual(src.len, st.len); + try testing.expectEqual(src.len, ln.len); +} diff --git a/zig/src/families/deepseek_v41/forward.zig b/zig/src/families/deepseek_v41/forward.zig new file mode 100644 index 0000000000..c24d50127e --- /dev/null +++ b/zig/src/families/deepseek_v41/forward.zig @@ -0,0 +1,489 @@ +//! The decode forward: a slot's windows over the whole model, block.zig's program run by run.zig, TP exchanges. + +const std = @import("std"); +const cuda = @import("cuda"); +const tp = @import("tp"); +const calls = @import("block_calls.zig"); +const block = @import("block.zig"); +const buffers = @import("buffers.zig"); +const run = @import("run.zig"); +const Config = @import("config.zig").Config; +const eh = @import("engram_host.zig"); +const graphs = @import("graphs.zig"); +const hooks = @import("forward_hooks.zig"); +const ft = @import("forward_types.zig"); +const kvs = @import("kv_state.zig"); +const er = @import("engram_rows.zig"); +pub const kops = @import("dsv41_kernels").ops; +const ph = @import("phases.zig"); +const hs = @import("host_stage.zig"); +const fwd_sample = @import("forward_sample.zig"); +const fwd_follow = @import("forward_follow.zig"); +const fwd_glue = @import("forward_glue.zig"); +const fwd_engram = @import("forward_engram.zig"); +const fwd_graph = @import("forward_graph.zig"); + +pub const Error = error{ GlueNotBuilt, BadWindow }; + +/// One slot's position, Engram tail and pending window (what keep() commits). +pub const Slot = struct { + pos: u64 = 0, + /// the last max_ngram - 1 committed ids (Engram's lookback; fewer at the sequence start) + tail: [eh.max_ngram]u32 = undefined, + tail_len: usize = 0, + /// the window in flight: its first position, its rows + pending: ?struct { start: u64, n: u32 } = null, + /// CED replay (TF_DSV41_PREFILL=replay): the positions and ids of the stash ring "s.ced.*" (ced.zig) + ced: ft.Stash = .{}, +}; + +pub const Forward = struct { + gpa: std.mem.Allocator, + cfg: *const Config, + widths: *const block.Widths, + opts: block.Options, + runner: *run.Runner, + comm: tp.collective.Collective, + /// the window's calls by row count, emitted once (arena-owned) + arena: std.heap.ArenaAllocator, + programs: std.AutoHashMapUnmanaged(u32, []const calls.Call) = .empty, + slot: Slot = .{}, + /// pinned staging of the window's token ids, and their device copy (int64, the embedding step reads them) + ids_stage: hs.Stage = .{}, + dev_ids: ?cuda.DeviceBuffer = null, + /// Engram's hasher (recipe/dsv41_engram_host.py's tables) and the rows' source; none: the synthetic rows + engram: ?*const eh.Host = null, + gathered: ?cuda.DeviceBuffer = null, + rows_stage: [eh.max_layers]hs.Stage = @splat(.{}), // Engram's own pinned staging, one a layer (copies may overlap) + /// a bf16 [rows, hidden] send buffer for the exchanges whose partial is not bf16 already (embedding, MoE) + send: ?cuda.DeviceBuffer = null, + /// the window's token ids (the embedding step reads them; window() copies them into ids_own) + ids: []const u32 = &.{}, + /// TF_DSV41_IMAGES=native (vision_rows.zig): every rank keeps image positions out of Engram; rank 0's image rows + images: bool = false, + /// TF_DSV41_BIAS_VL loaded (every rank): image rows route with gate.bias_vl ("L.moe.bias_vl") + image_bias: bool = false, + vision: ?*anyopaque = null, // vision_rows.Rows + ids_own: std.ArrayList(u32) = .empty, + /// the backbone layers the forward runs (all of them unless a gate runs a prefix) + layers: ?[]const u32 = null, + /// TP: rank 0 leads (sends each forward operation to the others over the plan link); the others `follow` + link: ?*tp.planlink.PlanLink = null, + /// a follower's drafter (the leader's lanes ask its pass; every rank runs it) + drafter: ?Drafter = null, + /// each block's traced streams (block.Options.trace) as SHA-256 hex by layer, for the last window + trace: ?*std.AutoArrayHashMapUnmanaged(u32, [64]u8) = null, + /// the largest row bucket plan() sized the buffers for (a graphed window never has more rows) + planned_rows: u32 = 0, + graphed: ?*Graphed = null, // decode windows through CUDA graphs (TF_DSV41_GRAPHS=1); null until the knob is read + graphs_read: bool = false, + /// the graphs' default when TF_DSV41_GRAPHS is unset (model.zig: on for the served model; the gates' drivers: off) + graphs_default: bool = false, + /// Engram's local shards (TF_DSV41_ENGRAM_DIR, engram_rows.zig), opened on first use; unset: synthetic index rows + engram_rows: ?*er.Rows = null, + engram_opened: bool = false, + /// Engram read-ahead at the DSpark pass (TF_DSV41_ENGRAM_PREFETCH=0: rows read when the window needs them) + engram_prefetch: bool = true, + gate: ?*anyopaque = null, // TF_DSV41_ENGRAM_GATE: opened with the table (engram_gate.Gate) + gated: bool = false, // the window being issued armed the gate: its engram_rows steps launch the gate kernel + /// TF_DSV41_ROUND_GRAPH's `gate`: recorded after each gated window, the gate's arm waits for it (not the stream) + gated_end: ?cuda.Event = null, + /// TF_DSV41_BRANCHES / TF_DSV41_MHC_DEFER (branches.zig): the runner's side streams, owned here + branches: ?*anyopaque = null, // branches.Branches + /// TF_DSV41_PHASES (phases.zig): the window path's timers + phases: ?*ph.Phases = null, + /// greedy's pinned logits / pairs staging and device pairs, kept across windows + pick_host: ?cuda.HostBuffer = null, + pick_dev: ?cuda.DeviceBuffer = null, + greedy_gpu: ?bool = null, // TF_DSV41_GREEDY_GPU=1: each rank's best column on GPU, pairs copied back; null: unread + greedy_dev: ?cuda.DeviceBuffer = null, + /// the last GPU pick on the device (`pickDevice`), null when it was merged on the host + pick_last: ?PickDevice = null, + after_pick: ?AfterPick = null, // TF_DSV41_DRAFT_GRAPHS + TF_DSV41_SPEC_DRAFT: enqueued after the device pick + after_sample: ?AfterSample = null, + pick_done: ?cuda.Event = null, // the pick's copy-back event the host waits on + /// the prefill's state (forward_prefill.State, forward_prefill.st), made at engine open + prefill_state: ?*anyopaque = null, + /// rows of "w.logits" the last prefill left (its last segment's, or its tail's): greedy takes out[0..last_rows] + last_rows: u32 = 0, + /// rank 0 inside a prefill: its windows and keeps are the followers' own (they got the whole prompt), not sent + quiet: bool = false, + /// the KV families in the paged pool (null: the slot's contiguous buffers); opts.pool is its emitter view + kv: ?*kvs.Kv = null, + /// the session store (sessions_gpu.zig): the ids each keep commits, and the operations a follower runs + sess: ?Sess = null, + /// a workstream's plan-link operations (slots.zig 16-19), run by a follower in the leader's order + ext: ?Ext = null, + /// rank 0's clean stop of every rank (model.zig: the TP session's announceStop), run by `stop` + on_stop: ?struct { ctx: *anyopaque, run: *const fn (ctx: *anyopaque) void } = null, + pf_switch: ?PfSwitch = null, // several live slots: a multi-segment prefill run's slot switch; null: one slot + pf_stash: ?PfStash = null, // the CED stash ring made the current slot's before a stash step (swapped lazily) + /// the multi-segment prefill run in flight (forward_prefill.promptMulti; its glue reads it), null outside one + multi: ?*ft.Multi = null, + /// the keyed sampler (sampling_gpu.zig): a follower runs rank 0's sampled choices too (their gathers pair up) + sampler: ?Sampler = null, + grammar: ?Grammar = null, // TF_DSV41_GRAMMAR=1: grammar masks applied to "w.logits" before a pick + calib_marks: std.ArrayList(u64) = .empty, // TF_DSV41_CALIB=measure: a follower's clock at each op_calib_mark + + pub fn init(gpa: std.mem.Allocator, cfg: *const Config, widths: *const block.Widths, opts: block.Options, runner: *run.Runner, comm: tp.collective.Collective) Forward { + return .{ .gpa = gpa, .cfg = cfg, .widths = widths, .opts = opts, .runner = runner, .comm = comm, .arena = std.heap.ArenaAllocator.init(gpa) }; + } + + pub fn deinit(f: *Forward) void { + if (f.graphed) |g| { + g.deinit(); + f.gpa.destroy(g); + } + f.ids_stage.deinit(); + if (f.dev_ids) |*b| b.free(); + if (f.send) |*b| b.free(); + if (f.gathered) |*b| b.free(); + for (&f.rows_stage) |*x| x.deinit(); + if (f.prefill_state != null) hooks.set.prefill.?.deinit(f); + f.calib_marks.deinit(f.gpa); + if (f.gate) |x| hooks.set.gate.?.close(x); + if (f.engram_rows) |x| x.close(); + if (f.branches != null) hooks.set.branches.?.close(f); + if (f.pick_host) |*b| b.free(); + if (f.pick_dev) |*b| b.free(); + if (f.greedy_dev) |*b| b.free(); + if (f.pick_done) |*e| e.deinit(); + if (f.gated_end) |*e| e.deinit(); + f.programs.deinit(f.gpa); + f.ids_own.deinit(f.gpa); + f.arena.deinit(); + } + + /// The backbone's layers, then the head. + pub fn backbone(f: *Forward, a: std.mem.Allocator) ![]const u32 { + if (f.layers) |ls| return ls; + const ls = try a.alloc(u32, f.cfg.layers); + for (ls, 0..) |*l, i| l.* = @intCast(i); + return ls; + } + + /// The buffer plan of every row bucket the forward runs (bind the runner with it once, before the first window). + pub fn plan(f: *Forward, buckets: []const u32) !buffers.Plan { + var p: buffers.Plan = .{ .a = f.arena.allocator() }; + for (buckets) |n| try p.add(try f.program(n)); + for (buckets) |n| f.planned_rows = @max(f.planned_rows, n); + return p; + } + + /// The calls of an `n`-row window at the slot's last position (the largest every role gets; plans size from it). + pub fn program(f: *Forward, n: u32) ![]const calls.Call { + if (f.programs.get(n)) |cs| return cs; + const a = f.arena.allocator(); + // the emitter's positions only size the indexer's scores at the window's end; the largest the slot reaches + const cs = try block.emit(a, f.cfg, f.widths, f.opts, try f.backbone(a), n, f.opts.limit - n, true); + try f.programs.put(f.gpa, n, cs); + return cs; + } + + /// Adds the prefill program's roles (segments of up to `rows` rows) to `p` before bind; shared roles: the larger. + pub fn planPrefill(f: *Forward, p: *buffers.Plan, rows: u32) !void { + return (try hooks.prefill()).plan(f, p, rows); + } + + /// The next prompt piece's ids for the Engram read-ahead (TF_DSV41_PREFETCH_AHEAD), every rank; I/O only. + pub fn prefillAhead(f: *Forward, next: []const u32) !void { + const ahead = (try hooks.prefill()).ahead(f) orelse return; + if (next.len == 0) return; + if (f.leads()) { + const msg = try f.gpa.alloc(i64, 1 + next.len); + defer f.gpa.free(msg); + msg[0] = op_ahead; + for (next, msg[1..]) |t, *y| y.* = t; + try f.link.?.send(msg); + } + ahead.clearRetainingCapacity(); + try ahead.appendSlice(f.gpa, next); + } + + /// The prompt `ids` from the slot's position: segments kept whole, a tail of at most 16 rows as decode windows. + pub fn prefill(f: *Forward, ids: []const u32) !void { + if (f.slot.pending != null) return error.BadWindow; + if (ids.len == 0) return; + f.gated = false; // prompt segments read their rows on the old path + if (f.leads()) { + // [op, total, offset, ids...] frames (planlink takes up to 64M words a frame; a prompt is a few) + const frame: usize = 1 << 20; + const msg = try f.gpa.alloc(i64, 3 + @min(frame, ids.len)); + defer f.gpa.free(msg); + var at: usize = 0; + while (at < ids.len) : (at += frame) { + const m = @min(frame, ids.len - at); + msg[0] = op_prefill; + msg[1] = @intCast(ids.len); + msg[2] = @intCast(at); + for (ids[at .. at + m], msg[3 .. 3 + m]) |t, *y| y.* = t; + try f.link.?.send(msg[0 .. 3 + m]); + } + } + f.quiet = true; + defer f.quiet = false; + return (try hooks.prefill()).prompt(f, ids); + } + + /// One decode window: `ids` at the slot's position. This rank's logits stay in the runner's "w.logits". + pub fn window(f: *Forward, ids_in: []const u32) !void { + // the forward's own copy: keep() reads the ids (Engram's tail) after the caller's slice may be gone + if (ids_in.ptr != f.ids_own.items.ptr) { + f.ids_own.clearRetainingCapacity(); + try f.ids_own.appendSlice(f.gpa, ids_in); + } + const ids = f.ids_own.items; + if (ids.len == 0 or f.slot.pending != null) return error.BadWindow; + // no embed of a window fills image rows (graphed: never; eager: no keep or bias), so image ids fail here + if (hooks.set.vision) |v| try v.checkWindow(ids); + // the pool's pages for the window's positions, their table entries up before the launches (every rank alike) + if (f.kv) |kx| try kx.reserve(f.slot.pos + ids.len); + if (f.leads()) { + var msg: [2 + 128]i64 = undefined; + if (ids.len > 128) return error.BadWindow; + msg[0] = op_window; + msg[1] = @intCast(ids.len); + for (ids, 0..) |t, i| msg[2 + i] = t; + try f.link.?.send(msg[0 .. 2 + ids.len]); + } + const tf = ph.start(f.phases, .forward); + defer tf.stop(); + f.runner.rows = @intCast(ids.len); // l2pf: wider windows skip the prefetch sites + if (try f.graphedWindow(ids)) return f.gatedEnd(); + // the gate (its worker reads every layer at once) or every layer's reads in flight while the calls are emitted + f.gated = try f.armEngram(&.{.{ .ids = ids, .tail = f.slot.tail[0..f.slot.tail_len] }}, @intCast(ids.len)); + if (!f.gated) f.issueEngram(ids, f.slot.tail[0..f.slot.tail_len]); + // the window's calls at its position (the indexer's scores and top-k grow with the cache: NK = end / ratio) + var wa = std.heap.ArenaAllocator.init(f.gpa); + defer wa.deinit(); + const cs = try block.emit(wa.allocator(), f.cfg, f.widths, f.opts, try f.backbone(wa.allocator()), @intCast(ids.len), @intCast(f.slot.pos), true); + f.ids = ids; + f.slot.pending = .{ .start = f.slot.pos, .n = @intCast(ids.len) }; + f.runner.glue = .{ .ctx = f, .run = glueFn }; + try f.runner.timedWindow(cs); + try f.gatedEnd(); + } + + pub const greedy = fwd_sample.greedy; + pub const greedyScratch = fwd_sample.greedyScratch; + + /// The last window's pick on the device: `out` int64 [n + 3] = picks, then (chain) accepted, bonus, next position. + pub const PickDevice = struct { out: u64, n: u32, chain: bool, start: u64 = 0, host: ?[*]const i64 = null }; + + /// Work enqueued from the device pick, on every rank, after the pick's copy back and before the host waits for it. + pub const AfterPick = struct { ctx: *anyopaque, run: *const fn (ctx: *anyopaque, pick: PickDevice) anyerror!void }; + + /// Work enqueued from sampled device choices (rows [row0, row0 + n) into `chosen` int64 [n]), every rank. + pub const AfterSample = struct { ctx: *anyopaque, run: *const fn (ctx: *anyopaque, row0: u32, n: u32, chosen: u64) anyerror!void }; + + pub fn pickDevice(f: *const Forward) ?PickDevice { + return f.pick_last; + } + + /// Commits the pending window's first row + `accepted` drafts. + pub fn keep(f: *Forward, accepted: u32) !void { + const p = f.slot.pending orelse return error.BadWindow; + if (accepted >= p.n) return error.BadWindow; + try f.engramCheck(); // a gated window whose rows failed: its picks are not committed + if (f.leads()) try f.link.?.send(&.{ op_keep, accepted }); + // the ratio-2 KV sources' carry: fp32 of the accepted row of the window's compressor projection + const r = f.runner; + var nb: [64]u8 = undefined; + var la = std.heap.ArenaAllocator.init(f.gpa); + defer la.deinit(); + for (try f.backbone(la.allocator())) |L| if (f.cfg.isKvSource(L) and f.cfg.compressRatio(L) == 2) { + const w = 2 * @as(usize, f.cfg.head_dim); + const src = r.addressOf(try std.fmt.bufPrint(&nb, "w.L{d}.comp", .{L})) orelse return error.Unbound; + const dst = r.addressOf(try std.fmt.bufPrint(&nb, "s.L{d}.carry", .{L})) orelse return error.Unbound; + try r.kernels.others(r.stream).carry(src, w, 0, accepted, w, dst); + }; + // Engram's tail: (tail + the committed ids)[-(max_ngram - 1):] + if (f.engram) |h| { + const keep_n = h.max_ngram - 1; + var all: [2 * eh.max_ngram + 128]u32 = undefined; + var m: usize = 0; + for (f.slot.tail[0..f.slot.tail_len]) |t| { + all[m] = t; + m += 1; + } + const take = @min(accepted + 1, all.len - m); + for (f.ids[0..take]) |t| { + all[m] = t; + m += 1; + } + const k = @min(m, keep_n); + @memcpy(f.slot.tail[0..k], all[m - k .. m]); + f.slot.tail_len = k; + } + f.slot.pos = p.start + accepted + 1; + f.slot.pending = null; + if (f.sess) |x| try x.commit(x.ptr, f.ids[0 .. accepted + 1]); + } + + const op_window = fwd_follow.op_window; + const op_greedy = fwd_follow.op_greedy; + const op_keep = fwd_follow.op_keep; + const op_stop = fwd_follow.op_stop; + const op_ds_ingest = fwd_follow.op_ds_ingest; + const op_ds_propose = fwd_follow.op_ds_propose; + const op_prefill = fwd_follow.op_prefill; + const op_ahead = fwd_follow.op_ahead; + const op_sess_save = fwd_follow.op_sess_save; + const op_sess_restore = fwd_follow.op_sess_restore; + const op_release = fwd_follow.op_release; + const op_ds_reset = fwd_follow.op_ds_reset; + pub const op_sample = fwd_follow.op_sample; + pub const op_sample_many = fwd_follow.op_sample_many; + + /// The sampler's side of a follower (sampling_gpu.zig): rank 0's op_sample message, run on this rank. + pub const Sampler = struct { + ptr: *anyopaque, + follow: *const fn (ptr: *anyopaque, msg: []const i64) anyerror!void, + }; + + pub const sendSample = fwd_follow.sendSample; + const op_tree_drop = fwd_follow.op_tree_drop; + const op_ced_finish = fwd_follow.op_ced_finish; + const op_sess_prompt = fwd_follow.op_sess_prompt; + const op_sess_admit = fwd_follow.op_sess_admit; + const op_pf_multi = fwd_follow.op_pf_multi; + const op_ced_multi = fwd_follow.op_ced_multi; + pub const replayMulti = fwd_follow.replayMulti; + + /// A slot's views made current on this rank (slots.zig SlotSet.view), for a multi-segment run's segments. + pub const PfSwitch = struct { ctx: *anyopaque, run: *const fn (ctx: *anyopaque, slot: u32) anyerror!void }; + pub const PfStash = struct { ctx: *anyopaque, run: *const fn (ctx: *anyopaque) anyerror!void }; + + pub const prefillMulti = fwd_follow.prefillMulti; + pub const finishPrompt = fwd_follow.finishPrompt; + pub const op_grammar = fwd_follow.op_grammar; + + /// Structured output's side of the forward: `apply` before a pick reads "w.logits", `follow` an op_grammar message. + pub const Grammar = struct { + ptr: *anyopaque, + follow: *const fn (ptr: *anyopaque, msg: []const i64) anyerror!void, + apply: *const fn (ptr: *anyopaque, row0: u32, n: u32) anyerror!void, + }; + + /// A workstream's follower side: rank 0's message of an operation number this file does not know. + pub const Ext = struct { ptr: *anyopaque, run: *const fn (ptr: *anyopaque, msg: []const i64) anyerror!void }; + + /// The session store's side: `commit` hears every committed id; `save` / `restore` run in the leader's order. + pub const Sess = struct { + ptr: *anyopaque, + commit: *const fn (ptr: *anyopaque, ids: []const u32) anyerror!void, + save: *const fn (ptr: *anyopaque) anyerror!void, + /// `ids`: an NVMe entry's ids (the leader's prompt prefix: its tokens are not indexed) + restore: *const fn (ptr: *anyopaque, id: u32, ids: []const i64) anyerror!void, + /// CED replay's prompt snapshot: an entry at the slot's position, the slot keeps going (sessions_gpu.zig) + prompt: *const fn (ptr: *anyopaque) anyerror!void, + /// the slot empty: its pages back, the committed ids cleared + reset: *const fn (ptr: *anyopaque) anyerror!void, + /// a request's pool admission (op_sess_admit's message): the leader's spills evicted, its pages reserved + admit: ?*const fn (ptr: *anyopaque, msg: []const i64) anyerror!void = null, + }; + + pub const op_calib_mark = fwd_follow.op_calib_mark; + pub const op_calib_gather = fwd_follow.op_calib_gather; + pub const calibMark = fwd_follow.calibMark; + pub const calibGather = fwd_follow.calibGather; + pub const followCalib = fwd_follow.followCalib; + pub const drop = fwd_follow.drop; + pub const release = fwd_follow.release; + pub const sendRestore = fwd_follow.sendRestore; + pub const sendSess = fwd_follow.sendSess; + pub const sendSessAdmit = fwd_follow.sendSessAdmit; + + /// The drafter's operations a follower runs (dspark_gpu.zig): its collectives pair up with the leader's. + pub const Drafter = struct { + ptr: *anyopaque, + ingest: *const fn (ptr: *anyopaque, start: u64, row0: u32, n: u32, avail: u32) anyerror!void, + propose: *const fn (ptr: *anyopaque, anchor: u32, start: u64) anyerror!void, + /// the slot's draft context restarts (a new request): every rank's, or their candidates differ + reset: *const fn (ptr: *anyopaque) void, + }; + + pub const dsResetOp = fwd_follow.dsResetOp; + pub const sendDrafter = fwd_follow.sendDrafter; + pub const dsIngestOp = fwd_follow.dsIngestOp; + pub const dsProposeOp = fwd_follow.dsProposeOp; + + pub fn leads(f: *const Forward) bool { + return f.link != null and !f.quiet and f.comm.rank() == 0; + } + + pub const stop = fwd_follow.stop; + pub const follow = fwd_follow.follow; + const glueMissing = fwd_glue.glueMissing; + const selectionGlue = fwd_glue.selectionGlue; + const numel = fwd_glue.numel; + pub const sendBuffer = fwd_glue.sendBuffer; + pub const glueFn = fwd_glue.glueFn; + pub const engramRows = fwd_engram.engramRows; + pub const engramCols = fwd_engram.engramCols; + pub const fillEngramRows = fwd_engram.fillEngramRows; + pub const engramSource = fwd_engram.engramSource; + pub const armEngram = fwd_engram.armEngram; + pub const gatedEnd = fwd_engram.gatedEnd; + pub const engramCheck = fwd_engram.engramCheck; + pub const gateCopy = fwd_engram.gateCopy; + pub const issueEngram = fwd_engram.issueEngram; + pub const prefetchEngram = fwd_engram.prefetchEngram; + pub const engramGathered = fwd_graph.engramGathered; + pub const activeGraph = fwd_graph.activeGraph; + pub const graphState = fwd_graph.graphState; + const graphedWindow = fwd_graph.graphedWindow; + const graphBody = fwd_graph.graphBody; + pub const fingerprint = fwd_graph.fingerprint; +}; + +pub const Graphed = fwd_graph.Graphed; + +/// A world-2 peer for one GPU: the peer's half of every all-gather is zeros (only the forward's all-gather exists). +pub const ZeroPeer = struct { + d: *const cuda.Driver, + + const C = tp.collective; + + fn rank(_: *anyopaque) u32 { + return 0; + } + fn world(_: *anyopaque) u32 { + return 2; + } + fn allGather(ptr: *anyopaque, send: C.DevicePtr, recv: C.DevicePtr, count: usize, t: C.DType, stream: C.Stream) C.Error!void { + const z: *ZeroPeer = @ptrCast(@alignCast(ptr)); + const n = count * t.size(); + z.d.check(z.d.api.cuMemcpyDtoDAsync_v2(recv, send, n, stream), "cuMemcpyDtoDAsync") catch return error.BackendFailed; + z.d.check(z.d.api.cuMemsetD8Async(recv + n, 0, n, stream), "cuMemsetD8Async") catch return error.BackendFailed; + } + fn unsupported(_: *anyopaque, _: C.DevicePtr, _: C.DevicePtr, _: usize, _: C.DType, _: C.Op, _: C.Stream) C.Error!void { + return error.Unsupported; + } + fn exchange(_: *anyopaque, _: C.DevicePtr, _: C.DevicePtr, _: usize, _: C.DType, _: u32, _: C.Stream) C.Error!void { + return error.Unsupported; + } + fn p2p(_: *anyopaque, _: C.DevicePtr, _: usize, _: C.DType, _: u32, _: C.Stream) C.Error!void { + return error.Unsupported; + } + fn none(_: *anyopaque) C.Error!void {} + fn abort(_: *anyopaque) void {} + + const vtable: C.Collective.VTable = .{ + .kind = .host, + .rank = rank, + .world = world, + .all_reduce = unsupported, + .all_gather = allGather, + .exchange = exchange, + .send = p2p, + .recv = p2p, + .barrier = none, + .check = none, + .abort = abort, + }; + + pub fn collective(z: *ZeroPeer) C.Collective { + return .{ .ptr = z, .vtable = &vtable }; + } +}; diff --git a/zig/src/families/deepseek_v41/forward_engram.zig b/zig/src/families/deepseek_v41/forward_engram.zig new file mode 100644 index 0000000000..f95e58e2ae --- /dev/null +++ b/zig/src/families/deepseek_v41/forward_engram.zig @@ -0,0 +1,141 @@ +//! The forward's Engram rows: hashing, staging, the gate, read-ahead and the all-gather. + +const std = @import("std"); +const quiet = @import("builtin").is_test; // a test's stderr is the build runner's channel +const cuda = @import("cuda"); +const calls = @import("block_calls.zig"); +const run = @import("run.zig"); +const eh = @import("engram_host.zig"); +const er = @import("engram_rows.zig"); +const hooks = @import("forward_hooks.zig"); +const ft = @import("forward_types.zig"); +const ph = @import("phases.zig"); +const hs = @import("host_stage.zig"); +const fwd = @import("forward.zig"); +const fwd_glue = @import("forward_glue.zig"); +const Forward = fwd.Forward; +const glueMissing = fwd_glue.glueMissing; + +/// A window's Engram rows of layer L: hashes, this rank's heads' rows, the all-gather and the column reorder. +pub fn engramRows(f: *Forward, r: *run.Runner, c: *const calls.Call) !void { + const h = f.engram orelse return glueMissing("engram_rows (no Engram tables)"); + const L: u32 = @intCast(c.args[1].arg.i); + const n = f.ids.len; // a multi-segment run: every segment's rows (each with its slot's tail) + const W = f.comm.world(); + const cols = f.engramCols(h); // this rank's columns of a row + const bytes = 2 * n * cols; + if (f.gated) { + // the gate: the GPU waits for the worker's rows and copies them where the staged copy would land + const send = try f.sendBuffer(r, bytes); + try f.gateCopy(r, L, send, n); + if (f.gathered == null or f.gathered.?.len < W * bytes) { + if (f.gathered) |*g| g.free(); + f.gathered = try cuda.DeviceBuffer.alloc(r.d, W * bytes); + } + try f.comm.allGather(send, f.gathered.?.ptr, n * cols, .bf16, r.stream.handle); + return r.kernels.others(r.stream).gatherCols(f.gathered.?.ptr, W, n, cols, try r.tensorAddr(c.args[0].arg.t)); + } + // an earlier copy out of this layer's staging must have left: a segment waits for it, smaller ones the stream + const li = std.mem.indexOfScalar(u32, h.layer_ids, L) orelse return error.NotEngram; + if (n <= hs.chunk) try r.stream.synchronize(); + const stage = &f.rows_stage[li]; + const host = try stage.take(r.d, bytes); + try f.fillEngramRows(h, L, @alignCast(std.mem.bytesAsSlice(u16, host))); + const send = try f.sendBuffer(r, bytes); + const s = r.stream.handle; + try r.d.check(r.d.api.cuMemcpyHtoDAsync_v2(send, host.ptr, bytes, s), "cuMemcpyHtoDAsync"); + try stage.issued(r.d, r.stream); + if (f.gathered == null or f.gathered.?.len < W * bytes) { + if (f.gathered) |*g| g.free(); + f.gathered = try cuda.DeviceBuffer.alloc(r.d, W * bytes); + } + try f.comm.allGather(send, f.gathered.?.ptr, n * cols, .bf16, s); + return r.kernels.others(r.stream).gatherCols(f.gathered.?.ptr, W, n, cols, try r.tensorAddr(c.args[0].arg.t)); +} + +/// This rank's columns of an Engram row (its heads' share). +pub fn engramCols(f: *const Forward, h: *const eh.Host) usize { + return h.headShard(f.comm.rank(), f.comm.world())[1] * @as(usize, f.cfg.engram_head_dim); +} + +/// Layer L's rows of the window's ids (with the slot's tail): the hashes, this rank's heads' index rows. +pub fn fillEngramRows(f: *Forward, h: *const eh.Host, L: u32, out: []u16) !void { + const tw = ph.start(f.phases, .@"engram.wait"); + defer tw.stop(); + if (f.multi) |m| { + // a multi-segment run: each segment's rows after its own slot's tail, at its rows of the run + const cols = out.len / f.ids.len; + for (m.segs, m.row0, m.tails) |sg, r0, t| try h.windowRows(f.gpa, try f.engramSource(h), sg.ids, t.items(), L, f.comm.rank(), f.comm.world(), f.cfg.engram_head_dim, out[r0 * cols ..][0 .. sg.ids.len * cols]); + return; + } + return h.windowRows(f.gpa, try f.engramSource(h), f.ids, f.slot.tail[0..f.slot.tail_len], L, f.comm.rank(), f.comm.world(), f.cfg.engram_head_dim, out); +} + +/// The Engram rows' source, opened on first use (TF_DSV41_ENGRAM_DIR; TF_DSV41_ENGRAM_CACHE records a layer). +pub fn engramSource(f: *Forward, h: *const eh.Host) !?eh.Source { + if (!f.engram_opened) { + f.engram_opened = true; + if (std.c.getenv("TF_DSV41_ENGRAM_PREFETCH")) |v| f.engram_prefetch = !std.mem.eql(u8, std.mem.span(v), "0"); + if (std.c.getenv("TF_DSV41_ENGRAM_DIR")) |dir| { + const cap: u32 = if (std.c.getenv("TF_DSV41_ENGRAM_CACHE")) |v| try std.fmt.parseInt(u32, std.mem.span(v), 10) else 65536; + f.engram_rows = try er.Rows.open(f.gpa, std.mem.span(dir), f.comm.rank(), f.comm.world(), h.layer_ids, cap); + std.log.scoped(.dsv41).info("engram: rows from {s} (io_uring {s}, {d} records a layer cached)", .{ std.mem.span(dir), if (f.engram_rows.?.ring != null) "on" else "off", cap }); + } else if (!quiet) std.log.scoped(.dsv41).warn("engram: TF_DSV41_ENGRAM_DIR unset: synthetic index rows (the forward gate's), not the model's table", .{}); + try (try hooks.gate()).open(f, h); + } + if (f.gate) |g| return hooks.set.gate.?.source(g); + return if (f.engram_rows) |x| x.source() else null; +} + +/// TF_DSV41_ENGRAM_GATE: arms the gate for a window over `items`; false when off or too wide (rows staged instead). +pub fn armEngram(f: *Forward, items: []const ft.Item, fill: u32) !bool { + const h = f.engram orelse return false; + _ = try f.engramSource(h); // the table and the gate open on the first Engram window + const g = f.gate orelse return false; + const gh = hooks.set.gate.?; + if (!gh.eligible(g, fill)) return false; + const ts = ph.start(f.phases, .@"engram.wait"); + defer ts.stop(); + try gh.arm(g, items, fill); + return true; +} + +/// After a window's launches (one-slot or row): a gated window's end recorded for the next arm (`syncGatedEnd`). +pub fn gatedEnd(f: *Forward) !void { + if (!f.gated or f.gate == null or !(try (try hooks.roundGraph()).current()).gate) return; + if (f.gated_end == null) f.gated_end = try cuda.Event.init(f.runner.d, false); + try f.gated_end.?.record(f.runner.stream); +} + +/// The armed round's errors (a failed read, a GPU wait past the timeout); nothing when no window was gated. +pub fn engramCheck(f: *Forward) !void { + if (f.gate) |g| if (f.gated) try hooks.set.gate.?.check(g); +} + +/// The gate kernel of Engram layer L: `n` rows of the armed round's staging into `dst` once published. +pub fn gateCopy(f: *Forward, r: *run.Runner, L: u32, dst: u64, n: usize) !void { + return hooks.set.gate.?.copy(f, r, L, dst, n); +} + +/// Every Engram layer's reads of a window in flight at once (one read latency); off: TF_DSV41_ENGRAM_PREFETCH=0. +pub fn issueEngram(f: *Forward, ids: []const u32, tail: []const u32) void { + const h = f.engram orelse return; + const src = (f.engramSource(h) catch return) orelse return; + if (!f.engram_prefetch or h.layer_ids.len < 2) return; + h.prefetch(f.gpa, src, ids, tail, f.comm.rank(), f.comm.world()); +} + +/// The coming window's rows `ids` at `start` read ahead on this rank; a wrong guess costs a read, never a bit. +pub fn prefetchEngram(f: *Forward, start: u64, ids: []const u32) void { + const h = f.engram orelse return; + const src = (f.engramSource(h) catch return) orelse return; + if (!f.engram_prefetch) return; + const tp_ = ph.start(f.phases, .prefetch); + defer tp_.stop(); + var buf: [2 * eh.max_ngram + 128]u32 = undefined; + const pend: ?[]const u32 = if (f.slot.pending) |p| f.ids[0..p.n] else null; + const lb = eh.lookbackAt(f.slot.tail[0..f.slot.tail_len], f.slot.pos, pend, start, h.max_ngram - 1, &buf) orelse return; + // the gate's worker owns the table between windows: a warm job, not a read on this thread + if (f.gate) |g| return hooks.set.gate.?.warm(g, &.{.{ .ids = ids, .tail = lb }}); + h.prefetch(f.gpa, src, ids, lb, f.comm.rank(), f.comm.world()); +} diff --git a/zig/src/families/deepseek_v41/forward_follow.zig b/zig/src/families/deepseek_v41/forward_follow.zig new file mode 100644 index 0000000000..911863e899 --- /dev/null +++ b/zig/src/families/deepseek_v41/forward_follow.zig @@ -0,0 +1,349 @@ +//! The forward's plan link: the leader's operation messages and a follower's loop that runs them. + +const std = @import("std"); +const cuda = @import("cuda"); +const hooks = @import("forward_hooks.zig"); +const ft = @import("forward_types.zig"); +const ph = @import("phases.zig"); +const fwd = @import("forward.zig"); +const Forward = fwd.Forward; + +pub const op_window: i64 = 1; +pub const op_greedy: i64 = 2; +pub const op_keep: i64 = 3; +pub const op_stop: i64 = 4; +pub const op_ds_ingest: i64 = 5; +pub const op_ds_propose: i64 = 6; +pub const op_prefill: i64 = 7; +/// 60: [op, ids...] the next prompt piece's ids for the coming prefill's Engram read-ahead (prefillAhead) +pub const op_ahead: i64 = 60; +pub const op_sess_save: i64 = 8; +pub const op_sess_restore: i64 = 9; +pub const op_release: i64 = 10; +pub const op_ds_reset: i64 = 11; +/// 12: [op, row0, n, T, top_k, top_p, min_p] a keyed sample (ops 12-15: the keyed sampler) +pub const op_sample: i64 = 12; +/// 13: [op, n, (row0, rows, T, top_k, top_p, min_p, seed, pos0) a segment]: TF_DSV41_SAMP_BATCH's segments +pub const op_sample_many: i64 = 13; + +/// Rank 0: a sampled choice's message to the followers (before its gathers). +pub fn sendSample(f: *Forward, ints: []const i64) !void { + if (f.leads()) try f.link.?.send(ints); +} +pub const op_tree_drop: i64 = 20; +/// 44: [op, end] the CED decoder replay (finishPrompt); 45: a prompt snapshot (ops 44-47: CED replay) +pub const op_ced_finish: i64 = 44; +pub const op_sess_prompt: i64 = 45; +/// 48: [op, need, n, spilled entry ids...] a request's admission (ops 48-51: sessions over several slots) +pub const op_sess_admit: i64 = 48; + +/// 52: [op, segments, (slot, start, rows) a segment, ids...]; 53: [op, n, slots...] (ops 52-55: prefill runs) +pub const op_pf_multi: i64 = 52; +pub const op_ced_multi: i64 = 53; + +/// Several slots' CED decoder replays in one run (TF_DSV41_REPLAY_RUNS), every rank; `out`: tails, taps. +pub fn replayMulti(f: *Forward, slots: []const u32, out: []ft.Replayed) !void { + if (f.leads()) { + var msg: [2 + 16]i64 = undefined; + if (slots.len > 16) return error.BadWindow; + msg[0] = op_ced_multi; + msg[1] = @intCast(slots.len); + for (slots, msg[2 .. 2 + slots.len]) |s, *y| y.* = s; + try f.link.?.send(msg[0 .. 2 + slots.len]); + } + return (try hooks.prefill()).finishMulti(f, slots, out); +} + +/// Several slots' prompt segments in one run (TF_DSV41_PIECE_RUNS); the leader sends the run first. +pub fn prefillMulti(f: *Forward, segs: []const ft.MultiSeg) !void { + if (segs.len == 0) return; + f.gated = false; + if (f.leads()) { + var msg: std.ArrayList(i64) = .empty; + defer msg.deinit(f.gpa); + try msg.appendSlice(f.gpa, &.{ op_pf_multi, @intCast(segs.len) }); + for (segs) |sg| try msg.appendSlice(f.gpa, &.{ sg.slot, @intCast(sg.start), @intCast(sg.ids.len) }); + for (segs) |sg| for (sg.ids) |t| try msg.append(f.gpa, t); + try f.link.?.send(msg.items); + } + f.quiet = true; + defer f.quiet = false; + return (try hooks.prefill()).promptMulti(f, segs); +} + +/// CED (TF_DSV41_PREFILL=replay): the decoder replay over the prompt's prefilled tail, every rank; full: nothing. +pub fn finishPrompt(f: *Forward) !void { + const h = try hooks.prefill(); + if (!h.replaying(f)) return; + if (f.leads()) try f.link.?.send(&.{ op_ced_finish, @intCast(f.slot.pos) }); + return h.finish(f); +} +/// 28: [op, sub (bind / step / release), ...] (grammar_gpu.zig; ops 28-31) +pub const op_grammar: i64 = 28; + +/// 64: [op] every rank syncs its device and reads its clock; 65: [op, n] samples all-gathered (ops 64-67: calib) +pub const op_calib_mark: i64 = 64; +pub const op_calib_gather: i64 = 65; + +/// A timed run's start or end on every rank: the leader sends the mark, then this rank syncs and reads its clock. +pub fn calibMark(f: *Forward) !u64 { + if (f.leads()) try f.link.?.send(&.{op_calib_mark}); + const d = f.runner.d; + try d.check(d.api.cuCtxSynchronize(), "cuCtxSynchronize"); + return ph.nowNs(); +} + +/// Every rank's samples (`mine`, ns) into `all` [world][mine.len]; an unpaired follower sends -1s (refused). +pub fn calibGather(f: *Forward, mine: []const i64, all: []i64) !void { + const w: usize = f.comm.world(); + if (all.len != w * mine.len) return error.BadWindow; + if (f.leads()) try f.link.?.send(&.{ op_calib_gather, @intCast(mine.len) }); + if (w == 1 or mine.len == 0) return @memcpy(all[0..mine.len], mine); + const d = f.runner.d; + const bytes = 8 * mine.len; + var dev = try cuda.DeviceBuffer.alloc(d, bytes * (w + 1)); + defer dev.free(); + var host = try cuda.HostBuffer.alloc(d, bytes * (w + 1)); + defer host.free(); + const h = host.slice(i64); + @memcpy(h[0..mine.len], mine); + const st = f.runner.stream; + try dev.uploadAsync(0, std.mem.sliceAsBytes(h[0..mine.len]), st.handle); + try f.comm.allGather(dev.ptr, dev.ptr + bytes, mine.len, .i64, st.handle); + try dev.downloadAsync(bytes, std.mem.sliceAsBytes(h[mine.len .. mine.len * (w + 1)]), st.handle); + try st.synchronize(); + @memcpy(all, h[mine.len .. mine.len * (w + 1)]); +} + +/// A follower's op_calib_gather: its marks paired up (end - start) as its samples, gathered with the leader's. +pub fn followCalib(f: *Forward, n: usize) !void { + defer f.calib_marks.clearRetainingCapacity(); + const mine = try f.gpa.alloc(i64, n); + defer f.gpa.free(mine); + const marks = f.calib_marks.items; + if (marks.len == 2 * n) { + for (mine, 0..) |*x, k| x.* = @intCast(marks[2 * k + 1] - marks[2 * k]); + } else { + std.log.scoped(.dsv41).err("calibration: {d} marks on this rank for {d} timed runs; sending none", .{ marks.len, n }); + @memset(mine, -1); + } + const all = try f.gpa.alloc(i64, n * f.comm.world()); + defer f.gpa.free(all); + try f.calibGather(mine, all); +} + +/// Drops the pending window on every rank without committing a row (a tree chain whose branch lost). +pub fn drop(f: *Forward) !void { + if (f.slot.pending == null) return error.BadWindow; + if (f.leads()) try f.link.?.send(&.{op_tree_drop}); + f.slot.pending = null; +} + +/// Frees the slot for the next stream on every rank: position 0, no Engram tail, no pending window, pages back. +pub fn release(f: *Forward) !void { + if (f.leads()) try f.link.?.send(&.{op_release}); + if (f.sess) |x| return x.reset(x.ptr); + if (f.kv) |k| try k.release(); + f.slot = .{}; +} + +/// Rank 0: a restore to the followers, with the entry's ids when it is not in RAM ([op, id, ids...]). +pub fn sendRestore(f: *Forward, id: u32, ids: ?[]const u32) !void { + if (!f.leads()) return; + const extra = if (ids) |x| x.len else 0; + const msg = try f.gpa.alloc(i64, 2 + extra); + defer f.gpa.free(msg); + msg[0] = op_sess_restore; + msg[1] = id; + if (ids) |x| for (x, msg[2..]) |t, *y| { + y.* = t; + }; + try f.link.?.send(msg); +} + +/// Rank 0: a session operation to the followers (before running it). +pub fn sendSess(f: *Forward, op: enum { save, restore, prompt }, id: u32) !void { + if (f.leads()) try f.link.?.send(&.{ switch (op) { + .save => op_sess_save, + .restore => op_sess_restore, + .prompt => op_sess_prompt, + }, id }); +} + +/// Rank 0: a request's admission to the followers (before running it): its pages and the entries to evict first. +pub fn sendSessAdmit(f: *Forward, need: u32, spills: []const u32) !void { + if (!f.leads()) return; + var msg: std.ArrayList(i64) = .empty; + defer msg.deinit(f.gpa); + try msg.appendSlice(f.gpa, &.{ op_sess_admit, need, @intCast(spills.len) }); + for (spills) |id| try msg.append(f.gpa, id); + try f.link.?.send(msg.items); +} + +pub fn dsResetOp() [1]i64 { + return .{op_ds_reset}; +} + +/// Rank 0: a drafter operation to the followers (before running it). +pub fn sendDrafter(f: *Forward, ints: []const i64) !void { + if (f.leads()) try f.link.?.send(ints); + // the pass's anchor is the next window's row 0: its Engram rows are read while the pass runs + if (ints.len == 3 and ints[0] == op_ds_propose) f.prefetchEngram(@intCast(ints[2]), &.{@intCast(ints[1])}); +} + +pub fn dsIngestOp(start: u64, row0: u32, n: u32, avail: u32) [5]i64 { + return .{ op_ds_ingest, @intCast(start), row0, n, avail }; +} + +pub fn dsProposeOp(anchor: u32, start: u64) [3]i64 { + return .{ op_ds_propose, anchor, @intCast(start) }; +} + +/// Rank 0: the followers' loops end (after the last operation they were sent). +pub fn stop(f: *Forward) !void { + if (f.leads()) try f.link.?.send(&.{op_stop}); + // every rank's clean end, before rank 0 closes its sockets (tp.Session.announceStop: no fail-fast exit 70) + if (f.leads()) if (f.on_stop) |h| h.run(h.ctx); +} + +/// Ranks > 0: run the leader's operations in its order (collectives pair up) until `stop`. +pub fn follow(f: *Forward) !void { + var buf: std.ArrayList(i64) = .empty; + defer buf.deinit(f.gpa); + var ids: std.ArrayList(u32) = .empty; + defer ids.deinit(f.gpa); + var prompt: std.ArrayList(u32) = .empty; + defer prompt.deinit(f.gpa); + // greedy's rows: a window's, or a prefill's last segment (up to prefill_rows) + var picks: std.ArrayList(u32) = .empty; + defer picks.deinit(f.gpa); + while (true) { + const msg = try f.link.?.recvGrow(f.gpa, &buf); + if (msg.len == 0) return error.BadPlan; + const ended = followOp(f, msg, &ids, &prompt, &picks) catch |e| { + // Runner.guard failed the leader's same window before any launch: only its request fails + if (e != error.MissingTritonVariant) return e; + std.log.scoped(.dsv41).warn("rank {d}: operation {d} failed ({t}), as on rank 0: the next one", .{ f.comm.rank(), msg[0], e }); + continue; + }; + if (ended) return; + } +} + +/// One of the leader's operations (follow); true: `stop`. +fn followOp(f: *Forward, msg: []const i64, ids: *std.ArrayList(u32), prompt: *std.ArrayList(u32), picks: *std.ArrayList(u32)) !bool { + switch (msg[0]) { + op_window => { + const n: usize = @intCast(msg[1]); + ids.clearRetainingCapacity(); + for (msg[2 .. 2 + n]) |t| try ids.append(f.gpa, @intCast(t)); + try f.window(ids.items); + }, + op_greedy => { + try picks.resize(f.gpa, @intCast(msg[1])); + try f.greedy(picks.items); + }, + op_prefill => { + const total: usize = @intCast(msg[1]); + const at: usize = @intCast(msg[2]); + if (at == 0) prompt.clearRetainingCapacity(); + if (at != prompt.items.len) return error.BadPlan; + for (msg[3..]) |t| try prompt.append(f.gpa, @intCast(t)); + if (prompt.items.len > total) return error.BadPlan; + if (prompt.items.len == total) try f.prefill(prompt.items); + }, + op_ahead => { + var nx: std.ArrayList(u32) = .empty; + defer nx.deinit(f.gpa); + for (msg[1..]) |t| try nx.append(f.gpa, @intCast(t)); + try f.prefillAhead(nx.items); + }, + op_keep => try f.keep(@intCast(msg[1])), + op_release => try f.release(), + op_tree_drop => try f.drop(), + op_calib_mark => { + const d = f.runner.d; + try d.check(d.api.cuCtxSynchronize(), "cuCtxSynchronize"); + try f.calib_marks.append(f.gpa, ph.nowNs()); + }, + op_calib_gather => { + if (msg.len != 2 or msg[1] < 0) return error.BadPlan; + try f.followCalib(@intCast(msg[1])); + }, + op_stop => return true, + op_sess_save => { + const x = f.sess orelse return error.BadPlan; + try x.save(x.ptr); + }, + op_sess_restore => { + const x = f.sess orelse return error.BadPlan; + if (msg.len < 2) return error.BadPlan; + try x.restore(x.ptr, @intCast(msg[1]), msg[2..]); + }, + op_sess_prompt => { + const x = f.sess orelse return error.BadPlan; + try x.prompt(x.ptr); + }, + op_sess_admit => { + const x = f.sess orelse return error.BadPlan; + const run_ = x.admit orelse return error.BadPlan; + try run_(x.ptr, msg); + }, + op_pf_multi => { + const ns: usize = @intCast(msg[1]); + if (ns == 0 or ns > 16 or msg.len < 2 + 3 * ns) return error.BadPlan; + var segs: [16]ft.MultiSeg = undefined; + prompt.clearRetainingCapacity(); + for (msg[2 + 3 * ns ..]) |t| try prompt.append(f.gpa, @intCast(t)); + var at: usize = 0; + for (segs[0..ns], 0..) |*sg, j| { + const n: usize = @intCast(msg[4 + 3 * j]); + if (at + n > prompt.items.len) return error.BadPlan; + sg.* = .{ .slot = @intCast(msg[2 + 3 * j]), .start = @intCast(msg[3 + 3 * j]), .ids = prompt.items[at .. at + n] }; + at += n; + } + if (at != prompt.items.len) return error.BadPlan; + try f.prefillMulti(segs[0..ns]); + }, + op_ced_multi => { + const ns: usize = @intCast(msg[1]); + if (ns < 2 or ns > 16 or msg.len != 2 + ns) return error.BadPlan; + var slots: [16]u32 = undefined; + for (msg[2..], slots[0..ns]) |x, *y| y.* = @intCast(x); + var out: [16]ft.Replayed = undefined; + try f.replayMulti(slots[0..ns], &out); + }, + op_ced_finish => { + // the leader's slot ends where this one does, and both run replay (the same TF_DSV41_PREFILL) + const h = try hooks.prefill(); + if (!h.replaying(f) or msg.len < 2 or msg[1] != f.slot.pos) return error.BadPlan; + try h.finish(f); + }, + op_ds_ingest => { + const d = f.drafter orelse return error.BadPlan; + try d.ingest(d.ptr, @intCast(msg[1]), @intCast(msg[2]), @intCast(msg[3]), @intCast(msg[4])); + }, + op_ds_reset => { + const d = f.drafter orelse return error.BadPlan; + d.reset(d.ptr); + }, + op_ds_propose => { + const d = f.drafter orelse return error.BadPlan; + f.prefetchEngram(@intCast(msg[2]), &.{@intCast(msg[1])}); + try d.propose(d.ptr, @intCast(msg[1]), @intCast(msg[2])); + }, + op_sample, op_sample_many => { + const x = f.sampler orelse return error.BadPlan; + try x.follow(x.ptr, msg); + }, + op_grammar => { + const x = f.grammar orelse return error.BadPlan; + try x.follow(x.ptr, msg); + }, + else => { + const x = f.ext orelse return error.BadPlan; + try x.run(x.ptr, msg); + }, + } + return false; +} diff --git a/zig/src/families/deepseek_v41/forward_glue.zig b/zig/src/families/deepseek_v41/forward_glue.zig new file mode 100644 index 0000000000..880edbc2dd --- /dev/null +++ b/zig/src/families/deepseek_v41/forward_glue.zig @@ -0,0 +1,157 @@ +//! The decode window's glue steps: positions, embedding, exchanges, selections, traces. + +const std = @import("std"); +const quiet = @import("builtin").is_test; // a test's stderr is the build runner's channel +const cuda = @import("cuda"); +const calls = @import("block_calls.zig"); +const run = @import("run.zig"); +const hs = @import("host_stage.zig"); +const fwd = @import("forward.zig"); +const hooks = @import("forward_hooks.zig"); +const Error = fwd.Error; +const Forward = fwd.Forward; + +pub fn glueMissing(name: []const u8) Error { + if (!quiet) std.log.scoped(.dsv41).warn("glue step {s}: its kernel is not built yet (no glue kernel binds it)", .{name}); + return error.GlueNotBuilt; +} + +/// The materialised selection's glue steps ("topk", "counts", "block_pos"); false: not one of them. +pub fn selectionGlue(k: anytype, r: *run.Runner, c: *const calls.Call, step: []const u8) !bool { + const x = c.args; + if (std.mem.eql(u8, step, "topk")) { + const keys = x[0].arg.t; + const out = x[1].arg.t; + const count: usize = @intCast(x[2].arg.i); + if (out.shape[0] != keys.shape[0] or out.shape[1] != count) return error.BadGlue; + try k.topPositions(try r.tensorAddr(keys), @intCast(keys.stride[0]), @intCast(keys.shape[0]), @intCast(keys.shape[1]), count, try r.tensorAddr(out)); + return true; + } + if (std.mem.eql(u8, step, "counts")) { + const sel = x[0].arg.t; + try k.visibleCounts(try r.tensorAddr(sel), @intCast(sel.shape[1]), try r.tensorAddr(x[2].arg.t), @intCast(x[3].arg.i), @intCast(sel.shape[0]), try r.tensorAddr(x[1].arg.t)); + return true; + } + if (std.mem.eql(u8, step, "block_pos")) { + const v: i32 = @intCast(x[1].arg.i); + try r.d.check(r.d.api.cuMemsetD32Async(try r.tensorAddr(x[0].arg.t), @bitCast(v), 1, r.stream.handle), "cuMemsetD32Async"); + return true; + } + return false; +} + +pub fn numel(t: calls.Tensor) usize { + var n: usize = 1; + for (t.shape) |x| n *= @intCast(x); + return n; +} + +pub fn sendBuffer(f: *Forward, r: *run.Runner, bytes: usize) !u64 { + if (f.send == null or f.send.?.len < bytes) { + if (f.send) |*b| b.free(); + f.send = try cuda.DeviceBuffer.alloc(r.d, bytes); + } + return f.send.?.ptr; +} + +/// The decode window's glue (a prefill segment's handler hands it the steps both have). +pub fn glueFn(ctx: *anyopaque, r: *run.Runner, c: *const calls.Call) anyerror!void { + const f: *Forward = @ptrCast(@alignCast(ctx)); + const step = c.name["glue.".len..]; + if (std.mem.startsWith(u8, step, "kx_")) return (f.kv orelse return error.GlueNotBuilt).glue(r, c, step); + const s = r.stream.handle; + const k = r.kernels.others(r.stream); + if (try selectionGlue(k, r, c, step)) return; + const p = f.slot.pending.?; + const D: usize = f.cfg.hidden; + const W: usize = f.comm.world(); + const g = f.activeGraph(); + if (std.mem.eql(u8, step, "positions")) { + const t = [_]calls.Tensor{ c.args[0].arg.t, c.args[1].arg.t, c.args[2].arg.t }; + // graphed: the start staged in the statics (one graph serves every window of its key) + if (g) |gr| return k.positionsDev(gr.statics.startDev(), p.n, try r.tensorAddr(t[0]), try r.tensorAddr(t[1]), try r.tensorAddr(t[2])); + try k.positions(@intCast(p.start), p.n, try r.tensorAddr(t[0]), try r.tensorAddr(t[1]), try r.tensorAddr(t[2])); + // CED's decoder replay: every row's window starts at the pass's first row (replay.finish's ctx.lo) + if (c.args.len == 4) try r.d.check(r.d.api.cuMemsetD32Async(try r.tensorAddr(t[2]), @bitCast(@as(i32, @intCast(c.args[3].arg.i))), p.n, s), "cuMemsetD32Async"); + return; + } + if (g != null and std.mem.eql(u8, step, "embed")) { + // graphed: the ids staged before the launch; no host step inside the window + const n: usize = p.n; + const embed = r.weights.get("embed") orelse return error.MissingWeight; + const V: usize = f.cfg.vocab / W; + const rows = try f.sendBuffer(r, 2 * n * D); + try k.embedSend(g.?.statics.idsDev(), n, embed.ptr, @intCast(f.comm.rank() * V), V, D, rows); + const recv = try r.tensorAddr(c.args[1].arg.t); + try f.comm.allGather(rows, recv, n * D, .bf16, s); + return k.embedSum(recv, W, n, D, try r.tensorAddr(c.args[0].arg.t)); + } + if (std.mem.eql(u8, step, "embed")) { + // this rank's vocabulary rows (others +0), their all-sum (the window's first exchange), x4 into the streams + const n: usize = p.n; + if (f.dev_ids == null or f.dev_ids.?.len < 8 * n) { + if (f.dev_ids) |*b| b.free(); + f.dev_ids = try cuda.DeviceBuffer.alloc(r.d, @max(8 * n, 256)); + } + // the previous copy out of this staging finished: a segment waits for it alone, smaller windows the stream + if (n <= hs.chunk) try r.stream.synchronize(); + const h = try f.ids_stage.take(r.d, 8 * n); + for (f.ids, 0..) |id, i| std.mem.writeInt(i64, h[8 * i ..][0..8], id, .little); + try r.d.check(r.d.api.cuMemcpyHtoDAsync_v2(f.dev_ids.?.ptr, h.ptr, 8 * n, s), "cuMemcpyHtoDAsync"); + try f.ids_stage.issued(r.d, r.stream); + const embed = r.weights.get("embed") orelse return error.MissingWeight; + const V: usize = f.cfg.vocab / W; + const rows = try f.sendBuffer(r, 2 * n * D); + try k.embedSend(f.dev_ids.?.ptr, n, embed.ptr, @intCast(f.comm.rank() * V), V, D, rows); + if (f.vision) |vr| try hooks.set.vision.?.fill(vr, r.stream, f.ids, rows, D); // rank 0: the image positions' rows (vision.fill) + const recv = try r.tensorAddr(c.args[1].arg.t); + try f.comm.allGather(rows, recv, n * D, .bf16, s); + return k.embedSum(recv, W, n, D, try r.tensorAddr(c.args[0].arg.t)); + } + if (std.mem.eql(u8, step, "exchange")) { + // attention's bf16 partial gathered into the receive buffer, rank 0's first + const send = c.args[0].arg.t; + return f.comm.allGather(try r.tensorAddr(send), try r.tensorAddr(c.args[1].arg.t), numel(send), .bf16, s); + } + if (std.mem.eql(u8, step, "exchange_rows")) { + // TF_DSV41_PF_OVERLAP: piece rows all-gathered into "L.xrows", copied into place: same bytes + const send = c.args[0].arg.t; + const into = c.args[1].arg.t; + const r0: usize = @intCast(c.args[2].arg.i); + const n: usize = @intCast(into.shape[1]); + const m = numel(send) / D; + if (r0 + m > n or c.args.len < 4) return error.BadGlue; + const tmp = try r.tensorAddr(c.args[3].arg.t); + try f.comm.allGather(try r.tensorAddr(send), tmp, m * D, .bf16, s); + const base = try r.tensorAddr(into); + for (0..W) |q| try r.d.check(r.d.api.cuMemcpyDtoDAsync_v2(base + ((q * n + r0) * D) * 2, tmp + q * m * D * 2, m * D * 2, s), "cuMemcpyDtoDAsync"); + return; + } + if (std.mem.eql(u8, step, "exchange_f32")) { + // the MoE's fp32 partial rounded to bf16, then gathered + const part = c.args[0].arg.t; + const n = numel(part); + const half = try f.sendBuffer(r, 2 * n); + try k.castBf16(try r.tensorAddr(part), half, n); + return f.comm.allGather(half, try r.tensorAddr(c.args[1].arg.t), n, .bf16, s); + } + if (std.mem.eql(u8, step, "kit_weights")) + return k.kitWeights(try r.tensorAddr(c.args[0].arg.t), try r.tensorAddr(c.args[1].arg.t), numel(c.args[0].arg.t)); + if (std.mem.eql(u8, step, "kit_logits")) return k.kitLogits(try r.tensorAddr(c.args[0].arg.t), numel(c.args[0].arg.t)); + if (std.mem.eql(u8, step, "engram_rows")) { + if (g) |gr| return f.engramGathered(gr, r, c); + return f.engramRows(r, c); + } + if (std.mem.eql(u8, step, "trace")) { + const map = f.trace orelse return; + const t = c.args[0].arg.t; + const host = try f.gpa.alloc(u8, 2 * numel(t)); + defer f.gpa.free(host); + try r.stream.synchronize(); + try cuda.DeviceBuffer.download(.{ .d = r.d, .ptr = try r.tensorAddr(t), .len = host.len }, 0, host); + var sha: [32]u8 = undefined; + std.crypto.hash.sha2.Sha256.hash(host, &sha, .{}); + return map.put(f.gpa, @intCast(c.args[1].arg.i), std.fmt.bytesToHex(sha, .lower)); + } + return glueMissing(step); +} diff --git a/zig/src/families/deepseek_v41/forward_graph.zig b/zig/src/families/deepseek_v41/forward_graph.zig new file mode 100644 index 0000000000..6a2d1245f6 --- /dev/null +++ b/zig/src/families/deepseek_v41/forward_graph.zig @@ -0,0 +1,215 @@ +//! Decode windows through CUDA graphs (TF_DSV41_GRAPHS): keys, capture, replay, fingerprint. + +const std = @import("std"); +const cuda = @import("cuda"); +const calls = @import("block_calls.zig"); +const block = @import("block.zig"); +const run = @import("run.zig"); +const graphs = @import("graphs.zig"); +const hooks = @import("forward_hooks.zig"); +const ft = @import("forward_types.zig"); +const ph = @import("phases.zig"); +const fwd = @import("forward.zig"); +const fwd_glue = @import("forward_glue.zig"); +const Forward = fwd.Forward; +const glueFn = fwd_glue.glueFn; + +/// Graphed: layer L's staged rows (copied before the launch) all-gathered, then forward.gather_cols' order. +pub fn engramGathered(f: *Forward, g: *Graphed, r: *run.Runner, c: *const calls.Call) !void { + const L: u32 = @intCast(c.args[1].arg.i); + const st = g.stageOf(L) orelse return error.NotEngram; + const n = f.ids.len; + if (f.gated) try f.gateCopy(r, L, st.dev.ptr, n); + try f.comm.allGather(st.dev.ptr, f.gathered.?.ptr, n * st.cols, .bf16, r.stream.handle); + return r.kernels.others(r.stream).gatherCols(f.gathered.?.ptr, f.comm.world(), n, st.cols, try r.tensorAddr(c.args[0].arg.t)); +} + +pub fn activeGraph(f: *Forward) ?*Graphed { + const g = f.graphed orelse return null; + return if (g.active) g else null; +} + +/// The graphs' state, made on the first window when TF_DSV41_GRAPHS is set (both ranks must set it alike). +pub fn graphState(f: *Forward) !?*Graphed { + if (!f.graphs_read) { + f.graphs_read = true; + const s = try graphs.Settings.fromEnvOr(f.graphs_default); + if (s.on and f.planned_rows > 0) { + const g = try f.gpa.create(Graphed); + errdefer f.gpa.destroy(g); + try g.init(f, s); + f.graphed = g; + std.log.scoped(.dsv41).info("graphs: on, windows of up to {d} rows, context buckets of {d}", .{ g.rows_max, s.bucket }); + } + } + return f.graphed; +} + +/// The window through its key's graph (captured on first use); false: not graphable here, run eagerly. +pub fn graphedWindow(f: *Forward, ids: []const u32) !bool { + const g = (try f.graphState()) orelse return false; + // a traced window is never graphed, and its program is not the key's: never emitted or cached here + if (f.opts.trace) return false; + const n: u32 = @intCast(ids.len); + const limit: u64 = @intCast(f.opts.limit); + if (n > g.rows_max or f.slot.pos + n > limit) return false; + const key: graphs.Key = .{ .rows = n, .ctx = graphs.bucketOf(f.slot.pos + n - 1, g.settings.bucket, g.settings.grow) }; + const cs = try g.program(f, key); + if (graphs.audit(cs) != null) return false; + const r = f.runner; + f.ids = ids; + f.slot.pending = .{ .start = f.slot.pos, .n = n }; + // inputs: one copy of the ids and start, then each Engram layer's rows (hashed here, on the host) + { + const ts = ph.start(f.phases, .@"graph.stage"); + defer ts.stop(); + f.gated = try f.armEngram(&.{.{ .ids = ids, .tail = f.slot.tail[0..f.slot.tail_len] }}, n); + if (!f.gated) f.issueEngram(ids, f.slot.tail[0..f.slot.tail_len]); + try g.statics.stage(r.stream, ids, f.slot.pos); + if (f.engram) |h| if (!f.gated) for (g.stages.items) |*st| { + try f.fillEngramRows(h, st.layer, try st.begin(n)); + try st.commit(r.stream, n); + }; + } + g.active = true; + defer g.active = false; + r.glue = .{ .ctx = f, .run = glueFn }; + g.current = cs; + const t0 = ph.nowNs(); + const fp = f.fingerprint(g); + // TF_DSV41_ROUND_GRAPH=split: the head's graph, then the tail's, launched while the GPU runs the head + const at = if (g.split) |rg| hooks.set.round_graph.?.splitAt(cs, rg.head) else null; + var outcome: graphs.gc.Outcome = .replayed; + if (at) |i| { + g.span = .{ 0, i }; + const o1 = try g.cache.run(.{ .rows = key.rows, .ctx = key.ctx, .part = 1 }, r.stream, fp, .{ .ctx = f, .run = graphBody }); + g.span = .{ i, cs.len }; + outcome = try g.cache.run(.{ .rows = key.rows, .ctx = key.ctx, .part = 2 }, r.stream, fp, .{ .ctx = f, .run = graphBody }); + if (o1 == .captured) outcome = .captured; + } else { + g.span = .{ 0, cs.len }; + outcome = try g.cache.run(key, r.stream, fp, .{ .ctx = f, .run = graphBody }); + } + if (f.phases) |x| x.add(if (outcome == .captured) .@"graph.capture" else .@"graph.replay", ph.nowNs() - t0); + if (outcome == .captured and g.cache.stats.captured == 1) + std.log.scoped(.dsv41).info("graphs: first capture ({d} rows, context bucket {d})", .{ key.rows, key.ctx }); + return true; +} + +pub fn graphBody(ctx: *anyopaque, stream: cuda.Stream) anyerror!void { + const f: *Forward = @ptrCast(@alignCast(ctx)); + _ = stream; // the runner's stream: the one the cache captures + const g = f.graphed.?; + try f.runner.window(g.current[g.span[0]..g.span[1]]); +} + +/// The addresses a graphed window bakes in beyond the runner's bound roles: any change drops every graph. +pub fn fingerprint(f: *const Forward, g: *const Graphed) u64 { + var words: [64]u64 = undefined; + var m: usize = 0; + for (f.runner.owned.items) |b| { + if (m + 2 > words.len) break; + words[m] = b.ptr; + words[m + 1] = b.len; + m += 2; + } + for ([_]?cuda.DeviceBuffer{ f.runner.scratch, f.send, f.gathered, g.statics.dev }) |b| if (b) |x| { + if (m + 1 > words.len) break; + words[m] = x.ptr; + m += 1; + }; + for (g.stages.items) |st| if (m < words.len) { + words[m] = st.dev.ptr; + m += 1; + }; + return graphs.gc.fingerprintOf(words[0..m]); +} + +/// The graphed path's state: the cache, window statics, Engram stages, programs by key (emitted at the bucket's end). +pub const Graphed = struct { + gpa: std.mem.Allocator, + settings: graphs.Settings, + rows_max: u32, + engine: graphs.gc.CudaEngine, + agreement: graphs.Agreement, + cache: graphs.Cache, + statics: graphs.Statics, + stages: std.ArrayList(graphs.EngramStage) = .empty, + programs: std.AutoHashMapUnmanaged(graphs.Key, []const calls.Call) = .empty, + current: []const calls.Call = &.{}, + /// the calls of `current` the graph body issues (TF_DSV41_ROUND_GRAPH's `split`: the head's, then the tail's) + span: [2]usize = .{ 0, 0 }, + split: ?ft.RoundGraph = null, + active: bool = false, + + fn init(g: *Graphed, f: *Forward, s: graphs.Settings) !void { + const r = f.runner; + const rows = @min(s.rows_max, f.planned_rows); + g.* = .{ + .gpa = f.gpa, + .settings = s, + .rows_max = rows, + .engine = .{ .d = r.d }, + .agreement = try graphs.Agreement.init(r.d, f.comm, r.stream), + .cache = undefined, + .statics = undefined, + }; + errdefer g.agreement.deinit(); + g.statics = try graphs.Statics.init(r.d, rows); + errdefer g.statics.deinit(); + const rg = try (try hooks.roundGraph()).current(); + if (rg.split) g.split = rg; + // `split`: a window's head and tail graphs in one cache of twice the cap (a head is ~1/43 of a window) + g.cache = graphs.Cache.init(f.gpa, g.engine.engine(), .{ .on = true, .max = if (rg.split) 2 * s.max else s.max, .hold = s.floor_hold }); + g.cache.agree = g.agreement.agree(); + g.cache.room = graphs.room; + g.cache.meter = graphs.meter; + g.cache.name = "decode windows"; + graphs.rule = s.room; + // every buffer a graph bakes in, at the largest window: the exchanges' send, Engram's gathered rows + const D: usize = f.cfg.hidden; + const W: usize = f.comm.world(); + // at the planned rows, so an eager window of more rows than a graph takes moves nothing (the fingerprint) + const most: usize = f.planned_rows; + _ = try f.sendBuffer(r, 2 * most * D); + if (f.engram) |h| { + var la = std.heap.ArenaAllocator.init(f.gpa); + defer la.deinit(); + const cols = f.engramCols(h); + for (try f.backbone(la.allocator())) |L| if (std.mem.indexOfScalar(u32, h.layer_ids, L) != null) { + try g.stages.append(f.gpa, try graphs.EngramStage.init(r.d, L, rows, cols)); + }; + const bytes = W * 2 * most * cols; + if (f.gathered == null or f.gathered.?.len < bytes) { + if (f.gathered) |*b| b.free(); + f.gathered = try cuda.DeviceBuffer.alloc(r.d, bytes); + } + } + } + + pub fn deinit(g: *Graphed) void { + const cs = g.cache.stats; + std.log.scoped(.dsv41).info("graphs: {d} captured, {d} replayed, {d} eager, {d} keys held, {d} evicted, {d} dropped{s}", .{ cs.captured, cs.replayed, cs.eager, g.cache.count(), cs.evicted, cs.dropped, if (g.cache.failed) " (off after a failed capture)" else "" }); + g.cache.deinit(); + for (g.stages.items) |*st| st.deinit(); + g.stages.deinit(g.gpa); + g.programs.deinit(g.gpa); + g.statics.deinit(); + g.agreement.deinit(); + } + + fn stageOf(g: *Graphed, L: u32) ?*graphs.EngramStage { + for (g.stages.items) |*st| if (st.layer == L) return st; + return null; + } + + /// The key's calls, emitted once (forward's arena) at the start that puts the window's end at its bucket's end. + fn program(g: *Graphed, f: *Forward, key: graphs.Key) ![]const calls.Call { + if (g.programs.get(key)) |cs| return cs; + const a = f.arena.allocator(); + const start = graphs.emitStart(key.rows, key.ctx, g.settings, @intCast(f.opts.limit)); + const cs = try block.emit(a, f.cfg, f.widths, f.opts, try f.backbone(a), key.rows, start, true); + try g.programs.put(g.gpa, key, cs); + return cs; + } +}; diff --git a/zig/src/families/deepseek_v41/forward_hooks.zig b/zig/src/families/deepseek_v41/forward_hooks.zig new file mode 100644 index 0000000000..7742013989 --- /dev/null +++ b/zig/src/families/deepseek_v41/forward_hooks.zig @@ -0,0 +1,95 @@ +//! The forward's later-layer entry points (prefill, Engram gate, round graph, greedy, images, branches), set at open. +const std = @import("std"); +const cuda = @import("cuda"); +const calls = @import("block_calls.zig"); +const buffers = @import("buffers.zig"); +const run = @import("run.zig"); +const eh = @import("engram_host.zig"); +const ft = @import("forward_types.zig"); +const Forward = @import("forward.zig").Forward; + +/// forward_prefill.zig: the prompt's segments, the CED decoder replay and the prefill's state on the forward. +pub const Prefill = struct { + plan: *const fn (f: *Forward, p: *buffers.Plan, rows: u32) anyerror!void, + prompt: *const fn (f: *Forward, ids: []const u32) anyerror!void, + promptMulti: *const fn (f: *Forward, segs: []const ft.MultiSeg) anyerror!void, + finish: *const fn (f: *Forward) anyerror!void, + finishMulti: *const fn (f: *Forward, slots: []const u32, out: []ft.Replayed) anyerror!void, + ahead: *const fn (f: *Forward) ?*std.ArrayList(u32), // the read-ahead ids' list; null: TF_DSV41_PREFETCH_AHEAD off + replaying: *const fn (f: *const Forward) bool, // TF_DSV41_PREFILL=replay + deinit: *const fn (f: *Forward) void, +}; + +/// engram_gate_fwd.zig: TF_DSV41_ENGRAM_GATE's gate (`Forward.gate`) opened with the table. +pub const Gate = struct { + open: *const fn (f: *Forward, h: *const eh.Host) anyerror!void, + source: *const fn (g: *anyopaque) eh.Source, + eligible: *const fn (g: *anyopaque, rows: usize) bool, + arm: *const fn (g: *anyopaque, items: []const ft.Item, fill: u32) anyerror!void, + check: *const fn (g: *anyopaque) anyerror!void, + copy: *const fn (f: *Forward, r: *run.Runner, L: u32, dst: u64, n: usize) anyerror!void, + warm: *const fn (g: *anyopaque, items: []const ft.Item) void, + close: *const fn (g: *anyopaque) void, +}; + +/// round_graph.zig: TF_DSV41_ROUND_GRAPH's settings and a window's split point. +pub const RoundGraph = struct { + current: *const fn () anyerror!ft.RoundGraph, + splitAt: *const fn (cs: []const calls.Call, head: u32) ?usize, +}; + +/// greedy.zig: the host's best column of a row. +pub const Greedy = struct { argmax: *const fn (vals: []const f32) usize }; + +/// vision_rows.zig: rank 0's image rows (`Forward.vision`), the no-image window check and the prefill's image glue. +pub const Vision = struct { + fill: *const fn (v: *anyopaque, s: cuda.Stream, ids: []const u32, rows: u64, D: usize) anyerror!void, + checkWindow: *const fn (ids: []const u32) anyerror!void, + keep: *const fn (f: *Forward, r: *run.Runner, c: *const calls.Call) anyerror!void, // glue image_keep + split: *const fn (f: *Forward, r: *run.Runner, c: *const calls.Call, merge: bool) anyerror!void, // or image_merge +}; + +/// branches.zig: the runner's side streams (`Forward.branches`) released. +pub const Branches = struct { close: *const fn (f: *Forward) void }; + +pub const Hooks = struct { + prefill: ?*const Prefill = null, + gate: ?*const Gate = null, + round_graph: ?*const RoundGraph = null, + greedy: ?*const Greedy = null, + vision: ?*const Vision = null, + branches: ?*const Branches = null, +}; + +/// The table every forward uses; an object a later layer made (gate, images, branches) means its part is set. +pub var set: Hooks = .{}; + +/// Engine open (engine_hooks.zig) sets every part before the first forward runs. +pub fn install(h: Hooks) void { + set = h; +} + +fn missing(comptime name: []const u8) error{ForwardHookUnset} { + std.log.scoped(.dsv41).err("forward hook `" ++ name ++ "` unset: engine open calls engine_hooks.open first", .{}); + return error.ForwardHookUnset; +} + +pub fn prefill() !*const Prefill { + return set.prefill orelse missing("prefill"); +} + +pub fn gate() !*const Gate { + return set.gate orelse missing("gate"); +} + +pub fn roundGraph() !*const RoundGraph { + return set.round_graph orelse missing("round_graph"); +} + +pub fn greedy() !*const Greedy { + return set.greedy orelse missing("greedy"); +} + +pub fn vision() !*const Vision { + return set.vision orelse missing("vision"); +} diff --git a/zig/src/families/deepseek_v41/forward_prefill.zig b/zig/src/families/deepseek_v41/forward_prefill.zig new file mode 100644 index 0000000000..b22da614de --- /dev/null +++ b/zig/src/families/deepseek_v41/forward_prefill.zig @@ -0,0 +1,485 @@ +//! The forward's own prefill: a prompt in segments through block_prefill.zig's program, each committed as a keep. + +const std = @import("std"); +const quiet = @import("builtin").is_test; // a test's stderr is the build runner's channel +const cuda = @import("cuda"); +const calls = @import("block_calls.zig"); +const buffers = @import("buffers.zig"); +const run = @import("run.zig"); +const block = @import("block.zig"); +const block_prefill = @import("block_prefill.zig"); +const longpf = @import("longpf.zig"); +const image_opts = @import("image_opts.zig"); +const fwd = @import("forward.zig"); +const eh = @import("engram_host.zig"); +const ced = @import("ced.zig"); +const ft = @import("forward_types.zig"); +const graphs = @import("graphs.zig"); +const serve_knobs = @import("serve_knobs.zig"); +const pf_k4 = @import("forward_prefill_k4.zig"); +const pf_finish = @import("forward_prefill_finish.zig"); +const pf_glue = @import("forward_prefill_glue.zig"); +const Forward = fwd.Forward; + +pub const log = std.log.scoped(.dsv41); + +pub const Error = error{ NoExpertTable, NoK2Table, BadGlue, StashGap }; + +/// The prefill's own state on the forward (forward.zig: `prefill_state`). +pub const State = struct { + /// the persistent tables and grown buffers are in place (ready()) + ready: bool = false, + /// the prompt's own copy (a caller's slice may alias the window's ids) + ids: std.ArrayList(u32) = .empty, + /// gm_plan's width-masked picks (int32 [P]) + masked: ?cuda.DeviceBuffer = null, + /// the segment's layers and the MoE blocks seen: gm_ticket starts each layer's MoE, gm_plan reads its K2 table + layers: []const u32 = &.{}, + moe: usize = 0, + layer: u32 = 0, + /// TF_DSV41_PREFETCH_AHEAD: the next segment's Engram reads issued before this one runs + ahead: bool = false, + /// long prompts' buffers (longpf.zig) + long: longpf.State = .{}, + /// TF_DSV41_PF_TBO: micro-batch B's own long-prompt buffers, and the pair in flight + long_b: longpf.State = .{}, + tbo: ?*Pair = null, + /// TF_DSV41_PF_4K: 4,096-row segments in the prefill's own workspace (`K4`); the forward's plan stays 2,048 rows + k4: bool = false, + k4ws: K4 = .{}, + /// the prompt call may use the workspace (every rank's MemAvailable above the floor); false: 2,048-row segments + ws_ok: bool = true, + ws_fallbacks: u64 = 0, + agreement: ?graphs.Agreement = null, + /// the next prompt piece's ids (op 60): the call's last segment reads their Engram rows ahead; cleared by the call + next: std.ArrayList(u32) = .empty, + /// TF_DSV41_PREFILL: `.replay` = CED (segments are encoder passes, `finish` runs the decoder); `.full`: every layer + mode: serve_knobs.PrefillMode = .full, + /// the decoder replay's ids (the glue reads them through Forward.ids while it runs) + tail_ids: [ced.keep]u32 = undefined, + /// what "w.taps" holds after the prompt: positions [taps_at, taps_at + taps_rows) at rows 0 ..: the DSpark hand-off + taps_at: u64 = 0, + taps_rows: u32 = 0, + + pub fn deinit(s: *State, gpa: std.mem.Allocator) void { + s.long.deinit(gpa); + if (s.masked) |*b| b.free(); + s.ids.deinit(gpa); + s.next.deinit(gpa); + if (s.agreement) |*x| x.deinit(); + var it = s.k4ws.keep.keyIterator(); + while (it.next()) |k| gpa.free(k.*); + s.k4ws.keep.deinit(gpa); + } +}; + +/// The prefill's state on `f` (`Forward.prefill_state`, made by `attach` at engine open). +pub fn state(f: *const Forward) *State { + return @ptrCast(@alignCast(f.prefill_state.?)); +} + +/// Engine open: the forward's prefill state, released by Forward.deinit (forward_hooks.Prefill.deinit). +pub fn attach(f: *Forward) !void { + const s = try f.gpa.create(State); + s.* = .{}; + f.prefill_state = s; +} + +fn deinitFn(f: *Forward) void { + const s = state(f); + s.deinit(f.gpa); + f.gpa.destroy(s); + f.prefill_state = null; +} + +fn aheadFn(f: *Forward) ?*std.ArrayList(u32) { + const s = state(f); + return if (s.ahead) &s.next else null; +} + +fn replayingFn(f: *const Forward) bool { + return state(f).mode == .replay; +} + +fn planFn(f: *Forward, p: *buffers.Plan, rows: u32) anyerror!void { + return plan(f, p, rows); +} + +fn promptFn(f: *Forward, ids: []const u32) anyerror!void { + return prompt(f, ids); +} + +fn promptMultiFn(f: *Forward, segs: []const MultiSeg) anyerror!void { + return promptMulti(f, segs); +} + +fn finishFn(f: *Forward) anyerror!void { + return finish(f); +} + +fn finishMultiFn(f: *Forward, slots: []const u32, out: []Replayed) anyerror!void { + return finishMulti(f, slots, out); +} + +pub const hooks_table: @import("forward_hooks.zig").Prefill = .{ .plan = planFn, .prompt = promptFn, .promptMulti = promptMultiFn, .finish = finishFn, .finishMulti = finishMultiFn, .ahead = aheadFn, .replaying = replayingFn, .deinit = deinitFn }; + +/// Adds the prefill program's roles at `rows` (the largest segment) to `p`, at every start and segment size it uses. +pub fn plan(f: *Forward, p: *buffers.Plan, rows: u32) !void { + if (rows <= 16) return error.BadWindow; + var la = std.heap.ArenaAllocator.init(f.gpa); + defer la.deinit(); + const layers = try f.backbone(la.allocator()); + try planPrograms(f, p, rows, layers); + if (state(f).k4 or (state(f).mode == .replay and f.opts.pf_tbo)) { + const ws = &state(f).k4ws; + if (ws.plan == null) { + ws.plan = .{ .a = f.gpa }; + try planWorkspace(f, &ws.plan.?, rows, layers); + if (keepsShared(f)) { + const before = planBytes(&ws.plan.?); + try k4Trim(f.gpa, &ws.plan.?, p, &ws.keep); + log.info("prefill 4K: rank {d}: {d} roles shared with the forward (TF_DSV41_PF_WS_SHARE): workspace {d} -> {d} MiB", .{ f.comm.rank(), ws.keep.count(), before >> 20, planBytes(&ws.plan.?) >> 20 }); + } + } + } +} + +/// The forward's prefill programs at `rows` (`plan` without the workspace). +pub fn planPrograms(f: *Forward, p: *buffers.Plan, rows: u32, layers: []const u32) !void { + const sizes = [_]u32{ rows, 17, 16 }; + for (sizes[if (rows > 17) 0 else 1..]) |n| { + const top = try lastStart(f, layers, n, .whole); + for ([_]i64{ 0, top }) |start| { + var ea = std.heap.ArenaAllocator.init(f.gpa); + defer ea.deinit(); + // replay's segments run the encoder (no head): its whole programs plan no head, w.logits not 2,048 rows + const head = state(f).mode != .replay; + try p.add(try block_prefill.emitPrefill(ea.allocator(), f.cfg, f.widths, image_opts.planOptions(f), layers, n, start, head)); + } + } + if (state(f).mode == .replay) try planReplay(f, p, rows, layers); + // the multi-segment runs (TF_DSV41_PIECE_RUNS / _REPLAY_RUNS): their own roles and their rows' at `rows` + var ra = std.heap.ArenaAllocator.init(f.gpa); + defer ra.deinit(); + const replay = state(f).mode == .replay; + const runs = try block_prefill.planRuns(ra.allocator(), f.cfg, f.widths, image_opts.planOptions(f), if (replay) try encoderLayers(f, layers) else layers, if (replay) try decoderLayers(f, layers) else null, rows, if (replay) .encoder else .whole); + for (runs.items) |cs| try p.add(cs); +} + +/// TF_DSV41_PF_WS_SHARE=1 with TF_DSV41_PF_4K alone: a 4K segment reuses forward roles that are already large enough. +pub fn keepsShared(f: *const Forward) bool { + return state(f).k4 and !f.opts.pf_tbo and envOn("TF_DSV41_PF_WS_SHARE"); +} + +fn envOn(name: [:0]const u8) bool { + const v = std.mem.span(std.c.getenv(name) orelse return false); + return std.mem.eql(u8, v, "1"); +} + +pub const k4Trim = pf_k4.k4Trim; +const planWorkspace = pf_k4.planWorkspace; +pub const workspaceBytes = pf_k4.workspaceBytes; +const planBytes = pf_k4.planBytes; + +/// CED replay's programs: an encoder segment and the decoder pass at each row count where its paths change. +fn planReplay(f: *Forward, p: *buffers.Plan, rows: u32, layers: []const u32) !void { + const enc = try encoderLayers(f, layers); + const dec = try decoderLayers(f, layers); + var ea = std.heap.ArenaAllocator.init(f.gpa); + defer ea.deinit(); + try p.add(try block_prefill.emitReplay(ea.allocator(), f.cfg, f.widths, image_opts.planOptions(f), enc, rows, 0, .encoder)); + for ([_]u32{ 16, 17, 32, 33, 64, 65, ced.keep }) |m| { + if (m > rows) continue; + const top = try lastStart(f, dec, m, .decoder); + for ([_]i64{ 0, top }) |start| { + var da = std.heap.ArenaAllocator.init(f.gpa); + defer da.deinit(); + try p.add(try block_prefill.emitReplay(da.allocator(), f.cfg, f.widths, image_opts.planOptions(f), dec, m, start, .decoder)); + } + } +} + +/// The encoder pass's layers: the forward's up to the decoder's first (ced.decoderStart), inclusive. +pub fn encoderLayers(f: *Forward, layers: []const u32) ![]const u32 { + const d = try ced.decoderStart(f.cfg); + const i = std.mem.indexOfScalar(u32, layers, d) orelse return error.NoDecoder; + return layers[0 .. i + 1]; +} + +/// The decoder pass's layers: the forward's from the decoder's first on. +pub fn decoderLayers(f: *Forward, layers: []const u32) ![]const u32 { + const d = try ced.decoderStart(f.cfg); + const i = std.mem.indexOfScalar(u32, layers, d) orelse return error.NoDecoder; + return layers[i..]; +} + +/// Whether an `n`-row segment (`part` of it) at `start` is on the prefill path (block_prefill's emitter takes it). +fn emits(f: *Forward, o: block.Options, layers: []const u32, n: u32, start: i64, part: block_prefill.Part) !bool { + var ea = std.heap.ArenaAllocator.init(f.gpa); + defer ea.deinit(); + _ = block_prefill.emitReplay(ea.allocator(), f.cfg, f.widths, o, layers, n, start, part) catch |e| switch (e) { + error.Unsupported => return false, + else => return e, + }; + return true; +} + +/// The last start an `n`-row segment emits at; the decoder pass's is one lower. +fn lastStart(f: *Forward, layers: []const u32, n: u32, part: block_prefill.Part) !i64 { + return lastStartWith(f, f.opts, layers, n, part); +} + +/// `lastStart` with options `o` (TF_DSV41_PF_4K's segments: k4Options). +pub fn lastStartWith(f: *Forward, o: block.Options, layers: []const u32, n: u32, part: block_prefill.Part) !i64 { + var hi: i64 = o.limit - n - @intFromBool(part == .decoder); + if (hi < 0) return error.BadWindow; + if (try emits(f, o, layers, n, hi, part)) return hi; + if (!try emits(f, o, layers, n, 0, part)) { + if (!quiet) log.warn("prefill: a {d}-row segment is not on the prefill path at any position (block_prefill: Unsupported)", .{n}); + return error.Unsupported; + } + var lo: i64 = 0; + while (hi - lo > 1) { + const mid = lo + @divFloor(hi - lo, 2); + if (try emits(f, o, layers, n, mid, part)) lo = mid else hi = mid; + } + log.info("prefill: {d}-row segments emit up to start {d}", .{ n, lo }); + return lo; +} + +pub const K4 = pf_k4.K4; +pub const segmentRows = pf_k4.segmentRows; +const wsAgreed = pf_k4.wsAgreed; +pub const k4Options = pf_k4.k4Options; +pub const wsPair = pf_k4.wsPair; +const wsArg = pf_k4.wsArg; +const k4Acquire = pf_k4.k4Acquire; +const nowMs = pf_k4.nowMs; +const wsFloorGiB = pf_k4.wsFloorGiB; +const hardFloorGiB = pf_k4.hardFloorGiB; +const k4Release = pf_k4.k4Release; + +/// TF_DSV41_PF_TBO: one micro-batch's host side of a pair in flight: ids, window, Engram tail, MoE calls seen. +pub const Micro = struct { + ids: []const u32, + pending: @TypeOf(@as(fwd.Slot, undefined).pending), + tail: Tail, + moe: usize = 0, + layer: u32 = 0, +}; + +pub const Pair = struct { a: Micro, b: Micro }; + +/// The prompt `ids` from the slot's position (forward.prefill: the leader has sent it to the followers). +pub fn prompt(f: *Forward, ids_in: []const u32) !void { + const st = state(f); + if (f.slot.pending != null) return error.BadWindow; + if (ids_in.len == 0) return; + if (f.slot.pos + ids_in.len > @as(u64, @intCast(f.opts.limit))) return error.BadWindow; + if (ids_in.ptr != st.ids.items.ptr) { + st.ids.clearRetainingCapacity(); + try st.ids.appendSlice(f.gpa, ids_in); + } + const ids = st.ids.items; + st.taps_rows = 0; + try ready(f); + // TF_DSV41_PF_4K / _PF_TBO: the workspace for this call, decided on every rank alike (else 2,048-row segments) + st.ws_ok = true; + if (st.k4 or (st.mode == .replay and f.opts.pf_tbo)) st.ws_ok = try wsAgreed(f); + const seg: usize = segmentRows(f); + defer st.next.clearRetainingCapacity(); + var at: usize = 0; + while (at < ids.len) { + // TF_DSV41_PF_TBO: two segments at a time (the second at least tbo_min rows), else one + if (pairOf(f, ids.len - at)) |pr| { + const n = pr[0] + pr[1]; + if (st.ahead) ahead(f, ids, at, n, seg); + if (try segmentPair(f, ids[at .. at + pr[0]], ids[at + pr[0] .. at + n])) { + at += n; + continue; + } + } + const n = @min(seg, ids.len - at); + if (st.ahead) ahead(f, ids, at, n, seg); + try segment(f, ids[at .. at + n], at + n == ids.len and st.mode == .full); + at += n; + } +} + +/// TF_DSV41_PF_TBO's split of the `left` rows ahead into two segments; null when off, full mode, or under tbo_min. +fn pairOf(f: *const Forward, left: usize) ?[2]usize { + if (!f.opts.pf_tbo or state(f).mode != .replay or !state(f).ws_ok) return null; + return pairSplit(segmentRows(f), left); +} + +/// `pairOf`'s split of `left` rows at segments of `seg` rows (null: one segment at a time). +pub fn pairSplit(seg: usize, left: usize) ?[2]usize { + const min: usize = @intCast(block_prefill.tbo_min); + if (left >= 2 * seg) return .{ seg, seg }; + if (left < 2 * min) return null; + const na = @min(seg, (left / 2) / 16 * 16); + if (left - na > seg or na < min) return null; + return .{ na, left - na }; +} + +/// Two encoder segments as one two-stream program, committed once; false: refused, run the segments one at a time. +fn segmentPair(f: *Forward, ia: []const u32, ib: []const u32) !bool { + const st = state(f); + var sa = std.heap.ArenaAllocator.init(f.gpa); + defer sa.deinit(); + const a = sa.allocator(); + const layers = try encoderLayers(f, try f.backbone(a)); + const start = f.slot.pos; + const na: u32 = @intCast(ia.len); + const nb: u32 = @intCast(ib.len); + // under TF_DSV41_PF_4K a micro-batch past the forward's rows is a 4K one (both in the workspace: wsPair) + const big = st.k4 and @max(na, nb) > f.opts.prefill_rows; + const o0 = image_opts.segmentOptions(f, ia.ptr[0 .. na + nb]); + const o = if (big) k4Options(o0) else o0; + const plain = block_prefill.emitTbo(a, f.cfg, f.widths, o, layers, na, nb, @intCast(start)) catch |e| switch (e) { + error.Unsupported => return false, + else => return e, + }; + const cs = try wsPair(a, plain, big); + try k4Acquire(f); + if (f.kv) |kx| try kx.reserve(start + na + nb); + // the micro-batches' host sides: B's Engram tail is the slot's after A's ids (tailOf, as A's commit leaves it) + var pair: Pair = .{ .a = .{ .ids = ia, .pending = .{ .start = start, .n = na }, .tail = .{} }, .b = .{ .ids = ib, .pending = .{ .start = start + na, .n = nb }, .tail = .{} } }; + pair.a.tail.len = f.slot.tail_len; + @memcpy(pair.a.tail.ids[0..pair.a.tail.len], f.slot.tail[0..f.slot.tail_len]); + if (f.engram) |h| { + var s2 = f.slot; + tailOf(&s2, ia, h.max_ngram - 1); + pair.b.tail.len = s2.tail_len; + @memcpy(pair.b.tail.ids[0..s2.tail_len], s2.tail[0..s2.tail_len]); + } else pair.b.tail = pair.a.tail; + f.ids = ia; + f.slot.pending = pair.a.pending; + st.tbo = &pair; + defer st.tbo = null; + errdefer f.slot.pending = null; + st.layers = layers; + st.moe = 0; + f.runner.glue = .{ .ctx = f, .run = glueFn }; + // the pair's send / gathered buffers are the workspace's while it runs (as a 4K segment's) + std.mem.swap(?cuda.DeviceBuffer, &f.send, &st.k4ws.send); + std.mem.swap(?cuda.DeviceBuffer, &f.gathered, &st.k4ws.gathered); + defer { + std.mem.swap(?cuda.DeviceBuffer, &f.send, &st.k4ws.send); + std.mem.swap(?cuda.DeviceBuffer, &f.gathered, &st.k4ws.gathered); + } + try longpf.window(&st.long, f.gpa, f.runner, f.kv, start, cs); + if (f.runner.prof) |pr| pr.prefillRows(@intCast(na + nb), false); + // the commit: both micro-batches' rows as one segment, the carries from B's last rows (its roles in the workspace) + const both = ia.ptr[0 .. na + nb]; + f.ids = both; + f.slot.pending = .{ .start = start, .n = na + nb }; + try commitRoles(f, nb, 0, K4.tag ++ "b~", na + nb); + f.slot.ced.extend(start, both); + f.last_rows = 0; + st.taps_at = start; + st.taps_rows = 0; + return true; +} + +/// Read-ahead of the Engram rows after segment ids[at .. at + n] (next segment or piece); a wrong guess costs a read. +fn ahead(f: *Forward, ids: []const u32, at: usize, n: usize, seg: usize) void { + const st = state(f); + if (at + n < ids.len) return readAhead(f, ids, at, n, ids[at + n .. at + n + @min(seg, ids.len - at - n)]); + if (st.next.items.len > 0) readAhead(f, ids, at, n, st.next.items); +} + +fn readAhead(f: *Forward, ids: []const u32, at: usize, n: usize, next: []const u32) void { + const h = f.engram orelse return; + if (!f.engram_prefetch) return; + const src = (f.engramSource(h) catch return) orelse return; + const keep: usize = h.max_ngram - 1; + const b = at + n; + var buf: [2 * eh.max_ngram]u32 = undefined; + const lb: []const u32 = if (n >= keep) ids[b - keep .. b] else blk: { + const old = @min(f.slot.tail_len, keep - n); + @memcpy(buf[0..old], f.slot.tail[f.slot.tail_len - old .. f.slot.tail_len]); + @memcpy(buf[old .. old + n], ids[at..b]); + break :blk buf[0 .. old + n]; + }; + h.prefetch(f.gpa, src, next, lb, f.comm.rank(), f.comm.world()); +} + +/// One segment at the slot's position, run eagerly, then committed. +fn segment(f: *Forward, ids: []const u32, head: bool) !void { + const st = state(f); + const n: u32 = @intCast(ids.len); + var sa = std.heap.ArenaAllocator.init(f.gpa); + defer sa.deinit(); + const a = sa.allocator(); + // CED replay: the encoder pass (layers up to the decoder's first, its site and compressor, the stash; no head) + const replay = st.mode == .replay; + const layers = if (replay) try encoderLayers(f, try f.backbone(a)) else try f.backbone(a); + const start = f.slot.pos; + if (f.kv) |kx| try kx.reserve(start + n); + // TF_DSV41_PF_4K: a segment past the forward's rows runs in the workspace (its own roles: block_prefill.ownCalls) + const big = st.k4 and st.ws_ok and n > f.opts.prefill_rows; + const o = if (big) k4Options(image_opts.segmentOptions(f, ids)) else image_opts.segmentOptions(f, ids); + const emitted = if (replay) block_prefill.emitReplay(a, f.cfg, f.widths, o, layers, n, @intCast(start), .encoder) else block_prefill.emitPrefill(a, f.cfg, f.widths, o, layers, n, @intCast(start), head); + const plain = emitted catch |e| { + if (e == error.Unsupported) if (!quiet) log.warn("prefill: the {d}-row segment at {d} is not on the prefill path (block_prefill: Unsupported)", .{ n, start }); + return e; + }; + const cs = if (big) try block_prefill.ownCallsKeep(a, plain, K4.tag, &st.k4ws.keep) else plain; + if (big) try k4Acquire(f); + // the glue reads the ids (embedding, Engram) and the pending window (positions) + f.ids = ids; + f.slot.pending = .{ .start = start, .n = n }; + errdefer f.slot.pending = null; + st.layers = layers; + st.moe = 0; + f.runner.glue = .{ .ctx = f, .run = glueFn }; + // TF_DSV41_PF_4K: the segment's send / gathered buffers are the workspace's while it runs + if (big) { + std.mem.swap(?cuda.DeviceBuffer, &f.send, &st.k4ws.send); + std.mem.swap(?cuda.DeviceBuffer, &f.gathered, &st.k4ws.gathered); + } + defer if (big) { + std.mem.swap(?cuda.DeviceBuffer, &f.send, &st.k4ws.send); + std.mem.swap(?cuda.DeviceBuffer, &f.gathered, &st.k4ws.gathered); + }; + try longpf.window(&st.long, f.gpa, f.runner, f.kv, start, cs); + if (f.runner.prof) |pr| pr.prefillRows(@intCast(n), false); + if (big) try commitRoles(f, n, 0, K4.tag, n) else try commit(f, n); + // replay.encode: the stash's ids follow its rows (a segment that does not continue it starts it over) + if (replay) f.slot.ced.extend(start, ids); + f.last_rows = if (replay) 0 else n; + st.taps_at = start; + st.taps_rows = if (replay) 0 else n; +} + +pub const MultiSeg = ft.MultiSeg; + +pub const Tail = ft.Tail; + +pub const Multi = ft.Multi; + +pub const promptMulti = pf_finish.promptMulti; +pub const runnable = pf_finish.runnable; +pub const Replayed = pf_finish.Replayed; +pub const replayRunnable = pf_finish.replayRunnable; +pub const finishMulti = pf_finish.finishMulti; +const enterRun = pf_finish.enterRun; +const segGlue = pf_glue.segGlue; +const runPositions = pf_glue.runPositions; +pub const finish = pf_finish.finish; +const stashCopy = pf_glue.stashCopy; +const commit = pf_finish.commit; +const commitAt = pf_finish.commitAt; +const commitRoles = pf_finish.commitRoles; +const tailOf = pf_finish.tailOf; +const ready = pf_glue.ready; +const grow = pf_glue.grow; +const copy = pf_glue.copy; +const zero = pf_glue.zero; +const numel = pf_glue.numel; +const bytesOf = pf_glue.bytesOf; +const dim = pf_glue.dim; +const ld = pf_glue.ld; +const glueFn = pf_glue.glueFn; +const glueOne = pf_glue.glueOne; diff --git a/zig/src/families/deepseek_v41/forward_prefill_finish.zig b/zig/src/families/deepseek_v41/forward_prefill_finish.zig new file mode 100644 index 0000000000..0c9132244b --- /dev/null +++ b/zig/src/families/deepseek_v41/forward_prefill_finish.zig @@ -0,0 +1,263 @@ +//! Multi-segment prefill runs, the CED decoder replay and a segment's commit. + +const std = @import("std"); +const block_prefill = @import("block_prefill.zig"); +const longpf = @import("longpf.zig"); +const image_opts = @import("image_opts.zig"); +const fwd = @import("forward.zig"); +const ced = @import("ced.zig"); +const Forward = fwd.Forward; +const pf = @import("forward_prefill.zig"); +const pf_glue = @import("forward_prefill_glue.zig"); +const pf_k4 = @import("forward_prefill_k4.zig"); +const Multi = pf.Multi; +const MultiSeg = pf.MultiSeg; +const Tail = pf.Tail; +const copy = pf_glue.copy; +const decoderLayers = pf.decoderLayers; +const encoderLayers = pf.encoderLayers; +const glueFn = pf_glue.glueFn; +const k4Release = pf_k4.k4Release; +const log = pf.log; +const ready = pf_glue.ready; + +/// Several slots' prompt segments in one run, each committed as `segment` would; error.Unsupported: one at a time. +pub fn promptMulti(f: *Forward, segs: []const MultiSeg) !void { + const st = pf.state(f); + const sw = f.pf_switch orelse return error.Unsupported; + if (segs.len == 0 or segs.len > 16) return error.BadWindow; + var sa = std.heap.ArenaAllocator.init(f.gpa); + defer sa.deinit(); + const a = sa.allocator(); + const replay = st.mode == .replay; + const layers = if (replay) try encoderLayers(f, try f.backbone(a)) else try f.backbone(a); + const spans = try a.alloc(block_prefill.Span, segs.len); + const row0 = try a.alloc(usize, segs.len); + const tails = try a.alloc(Tail, segs.len); + var all: std.ArrayList(u32) = .empty; + for (segs, spans, row0, tails) |sg, *sp, *r0, *t| { + try sw.run(sw.ctx, sg.slot); + if (f.slot.pending != null or f.slot.pos != sg.start or sg.ids.len == 0) return error.BadWindow; + if (sg.start + sg.ids.len > @as(u64, @intCast(f.opts.limit))) return error.BadWindow; + sp.* = .{ .slot = sg.slot, .start = @intCast(sg.start), .n = @intCast(sg.ids.len) }; + r0.* = all.items.len; + t.len = f.slot.tail_len; + @memcpy(t.ids[0..t.len], f.slot.tail[0..t.len]); + try all.appendSlice(a, sg.ids); + } + if (image_opts.segmentOptions(f, all.items).image_rows != 0) return error.Unsupported; + const cs = try block_prefill.emitMulti(a, f.cfg, f.widths, f.opts, layers, spans, if (replay) .encoder else .whole); + try ready(f); + // the pages of every segment's positions, before any launch (each on its slot's table) + for (segs) |sg| { + try sw.run(sw.ctx, sg.slot); + if (f.kv) |kx| try kx.reserve(sg.start + sg.ids.len); + } + var m: Multi = .{ .segs = segs, .row0 = row0, .tails = tails, .ids = all.items }; + f.multi = &m; + defer f.multi = null; + try enterRun(f, &m); + st.layers = layers; + st.moe = 0; + st.taps_rows = 0; + f.runner.glue = .{ .ctx = f, .run = glueFn }; + try longpf.window(&st.long, f.gpa, f.runner, f.kv, segs[0].start, cs); + if (f.runner.prof) |pr| pr.prefillRows(@intCast(all.items.len), false); + // each slot keeps its segment (carries, Engram tail, position, stash ids) + for (segs, row0) |sg, r0| { + try sw.run(sw.ctx, sg.slot); + f.ids = sg.ids; + f.slot.pending = .{ .start = sg.start, .n = @intCast(sg.ids.len) }; + try commitAt(f, @intCast(sg.ids.len), r0); + if (replay) f.slot.ced.extend(sg.start, sg.ids); + } + f.last_rows = 0; +} + +/// Whether a multi-segment run takes `segs` (promptMulti's refusals), checked before the leader sends it. +pub fn runnable(f: *Forward, segs: []const MultiSeg) !bool { + if (f.pf_switch == null or segs.len < 2 or segs.len > 16) return false; + var sa = std.heap.ArenaAllocator.init(f.gpa); + defer sa.deinit(); + const a = sa.allocator(); + const replay = pf.state(f).mode == .replay; + const layers = if (replay) try encoderLayers(f, try f.backbone(a)) else try f.backbone(a); + const spans = try a.alloc(block_prefill.Span, segs.len); + var all: std.ArrayList(u32) = .empty; + for (segs, spans) |sg, *sp| { + sp.* = .{ .slot = sg.slot, .start = @intCast(sg.start), .n = @intCast(sg.ids.len) }; + try all.appendSlice(a, sg.ids); + } + if (image_opts.segmentOptions(f, all.items).image_rows != 0) return false; + _ = block_prefill.emitMulti(a, f.cfg, f.widths, f.opts, layers, spans, if (replay) .encoder else .whole) catch |e| switch (e) { + error.Unsupported => return false, + else => return e, + }; + return true; +} + +pub const Replayed = @import("forward_types.zig").Replayed; + +/// Whether a batched decoder replay takes `segs`, checked before the leader sends it (emitMulti's refusals). +pub fn replayRunnable(f: *Forward, segs: []const MultiSeg) !bool { + if (f.pf_switch == null or pf.state(f).mode != .replay or segs.len < 2 or segs.len > 16) return false; + var sa = std.heap.ArenaAllocator.init(f.gpa); + defer sa.deinit(); + const a = sa.allocator(); + const spans = try a.alloc(block_prefill.Span, segs.len); + var all: std.ArrayList(u32) = .empty; + for (segs, spans) |sg, *sp| { + sp.* = .{ .slot = sg.slot, .start = @intCast(sg.start), .n = @intCast(sg.ids.len) }; + try all.appendSlice(a, sg.ids); + } + if (image_opts.segmentOptions(f, all.items).image_rows != 0) return false; + _ = block_prefill.emitMulti(a, f.cfg, f.widths, f.opts, try decoderLayers(f, try f.backbone(a)), spans, .decoder) catch |e| switch (e) { + error.Unsupported => return false, + else => return e, + }; + return true; +} + +/// Several slots' CED decoder replays in one run; nothing is committed, each tail as its one-slot replay row for row. +pub fn finishMulti(f: *Forward, slots: []const u32, out: []Replayed) !void { + const st = pf.state(f); + const sw = f.pf_switch orelse return error.Unsupported; + if (st.mode != .replay or slots.len < 2 or slots.len > 16 or out.len < slots.len) return error.BadWindow; + var sa = std.heap.ArenaAllocator.init(f.gpa); + defer sa.deinit(); + const a = sa.allocator(); + const layers = try decoderLayers(f, try f.backbone(a)); + const segs = try a.alloc(MultiSeg, slots.len); + const spans = try a.alloc(block_prefill.Span, slots.len); + const row0 = try a.alloc(usize, slots.len); + const tails = try a.alloc(Tail, slots.len); + var all: std.ArrayList(u32) = .empty; + for (slots, segs, spans, row0, tails) |slot, *sg, *sp, *r0, *t| { + try sw.run(sw.ctx, slot); + if (f.slot.pending != null) return error.BadWindow; + const end = f.slot.pos; + const tl = ced.tailOf(end); + if (tl.m == 0) return error.BadWindow; + if (!f.slot.ced.covers(tl.r0, end)) { + log.err("CED replay of [{d}, {d}), but the stash holds [{d}, {d})", .{ tl.r0, end, f.slot.ced.lo(), f.slot.ced.hi }); + return error.StashGap; + } + const ids = try a.alloc(u32, tl.m); + f.slot.ced.tail(tl.r0, end, ids); + sg.* = .{ .slot = slot, .start = tl.r0, .ids = ids }; + sp.* = .{ .slot = slot, .start = @intCast(tl.r0), .n = @intCast(tl.m) }; + r0.* = all.items.len; + t.len = f.slot.tail_len; + @memcpy(t.ids[0..t.len], f.slot.tail[0..t.len]); + try all.appendSlice(a, ids); + } + const cs = try block_prefill.emitMulti(a, f.cfg, f.widths, image_opts.segmentOptions(f, all.items), layers, spans, .decoder); + try ready(f); + var m: Multi = .{ .segs = segs, .row0 = row0, .tails = tails, .ids = all.items }; + f.multi = &m; + defer f.multi = null; + try enterRun(f, &m); + st.layers = layers; + st.moe = 0; + st.taps_rows = 0; + f.runner.glue = .{ .ctx = f, .run = glueFn }; + try longpf.window(&st.long, f.gpa, f.runner, f.kv, segs[0].start, cs); + if (f.runner.prof) |pr| pr.prefillRows(@intCast(m.ids.len), true); + for (segs, row0, out[0..slots.len]) |sg, r0, *o| { + try sw.run(sw.ctx, sg.slot); + f.slot.pending = null; + o.* = .{ .slot = sg.slot, .at = sg.start, .rows = @intCast(sg.ids.len), .row0 = @intCast(r0) }; + } + f.last_rows = 0; + try k4Release(f); // TF_DSV41_PF_4K: these prompts are prefilled + try longpf.release(&st.long, f.runner); // TF_DSV41_STREAM_LAZY: their stream scratch too +} + +/// The glue's view of the whole run (Forward.multi): the first segment's slot, every row's ids, the run's rows pending. +pub fn enterRun(f: *Forward, m: *const Multi) !void { + const sw = f.pf_switch.?; + try sw.run(sw.ctx, m.segs[0].slot); + f.ids = m.ids; + f.slot.pending = .{ .start = m.segs[0].start, .n = @intCast(m.ids.len) }; +} + +/// CED's decoder replay: layers D .. over the prefilled tail [R0, end) from the stash; nothing is committed. +pub fn finish(f: *Forward) !void { + const st = pf.state(f); + if (st.mode != .replay) return; + if (f.slot.pending != null) return error.BadWindow; + const end = f.slot.pos; + const t = ced.tailOf(end); + if (t.m == 0) return; + if (!f.slot.ced.covers(t.r0, end)) { + log.err("CED replay of [{d}, {d}), but the stash holds [{d}, {d})", .{ t.r0, end, f.slot.ced.lo(), f.slot.ced.hi }); + return error.StashGap; + } + try ready(f); + var sa = std.heap.ArenaAllocator.init(f.gpa); + defer sa.deinit(); + const a = sa.allocator(); + const layers = try decoderLayers(f, try f.backbone(a)); + const ids = st.tail_ids[0..t.m]; + f.slot.ced.tail(t.r0, end, ids); + // the tail's image positions route with gate.bias_vl (replay._image); no decoder layer reads Engram + const cs = try block_prefill.emitReplay(a, f.cfg, f.widths, image_opts.segmentOptions(f, ids), layers, t.m, @intCast(t.r0), .decoder); + f.ids = ids; + f.slot.pending = .{ .start = t.r0, .n = t.m }; + defer f.slot.pending = null; + st.layers = layers; + st.moe = 0; + f.runner.glue = .{ .ctx = f, .run = glueFn }; + try longpf.window(&st.long, f.gpa, f.runner, f.kv, t.r0, cs); + if (f.runner.prof) |pr| pr.prefillRows(@intCast(t.m), true); + f.last_rows = 0; + // TF_DSV41_PF_4K: the prompt is prefilled, its workspace goes back (TF_DSV41_STREAM_LAZY: and its stream scratch) + try k4Release(f); + try longpf.release(&st.long, f.runner); + // the drafter's context rows: the tail's taps (replay.finish's, which slots.finish_prompt hands to `replayed`) + st.taps_at = t.r0; + st.taps_rows = t.m; +} + +/// Keep of all the pending segment's rows: the ratio-2 carries, the Engram tail, the position. +pub fn commit(f: *Forward, n: u32) !void { + return commitAt(f, n, 0); +} + +/// `commit` of a segment whose rows are rows [row0, row0 + n) of the run's projections. +pub fn commitAt(f: *Forward, n: u32, row0: usize) !void { + return commitRoles(f, n, row0, "", n); +} + +/// `commitAt` with carries from rows of "w.
L.proj" (TBO's B: "b~") ending at row `n`; the slot advances `adv`.
+pub fn commitRoles(f: *Forward, n: u32, row0: usize, comptime pre: []const u8, adv: u32) !void {
+    const p = f.slot.pending orelse return error.BadWindow;
+    const r = f.runner;
+    var nb: [64]u8 = undefined;
+    var la = std.heap.ArenaAllocator.init(f.gpa);
+    defer la.deinit();
+    const row: usize = 4 * 2 * @as(usize, f.cfg.head_dim); // fp32 [kv | gate]
+    for (try f.backbone(la.allocator())) |L| if (f.cfg.isKvSource(L) and f.cfg.compressRatio(L) == 2) {
+        // a role the 4K segment shares with the forward (K4.keep) is under its bare name
+        const src = r.addressOf(try std.fmt.bufPrint(&nb, "w." ++ pre ++ "L{d}.proj", .{L})) orelse r.addressOf(try std.fmt.bufPrint(&nb, "w.L{d}.proj", .{L})) orelse return error.Unbound;
+        const dst = r.addressOf(try std.fmt.bufPrint(&nb, "s.L{d}.carry", .{L})) orelse return error.Unbound;
+        try copy(r, dst, src + (row0 + n - 1) * row, row);
+    };
+    if (f.engram) |h| tailOf(&f.slot, f.ids[0..adv], h.max_ngram - 1);
+    f.slot.pos = p.start + adv;
+    f.slot.pending = null;
+    if (f.sess) |x| try x.commit(x.ptr, f.ids[0..adv]);
+}
+
+/// Engram's tail after committing `ids`: (tail + ids)[-keep:].
+pub fn tailOf(s: *fwd.Slot, ids: []const u32, keep: usize) void {
+    if (ids.len >= keep) {
+        @memcpy(s.tail[0..keep], ids[ids.len - keep ..]);
+        s.tail_len = keep;
+        return;
+    }
+    const old = @min(s.tail_len, keep - ids.len); // the old tail's last ones that stay
+    std.mem.copyForwards(u32, s.tail[0..old], s.tail[s.tail_len - old .. s.tail_len]);
+    @memcpy(s.tail[old .. old + ids.len], ids);
+    s.tail_len = old + ids.len;
+}
diff --git a/zig/src/families/deepseek_v41/forward_prefill_glue.zig b/zig/src/families/deepseek_v41/forward_prefill_glue.zig
new file mode 100644
index 0000000000..a88f5b398a
--- /dev/null
+++ b/zig/src/families/deepseek_v41/forward_prefill_glue.zig
@@ -0,0 +1,275 @@
+//! A prefill segment's glue steps and the tables and buffers they need once.
+
+const std = @import("std");
+const cuda = @import("cuda");
+const calls = @import("block_calls.zig");
+const buffers = @import("buffers.zig");
+const run = @import("run.zig");
+const longpf = @import("longpf.zig");
+const hooks = @import("forward_hooks.zig");
+const fwd = @import("forward.zig");
+const ced = @import("ced.zig");
+const Forward = fwd.Forward;
+const pf = @import("forward_prefill.zig");
+const pf_finish = @import("forward_prefill_finish.zig");
+const Tail = pf.Tail;
+const enterRun = pf_finish.enterRun;
+
+/// glue pf_seg: segment j's slot current, its rows pending, its positions; -1: the run's view again (enterRun).
+pub fn segGlue(f: *Forward, r: *run.Runner, c: *const calls.Call) !void {
+    const m = f.multi orelse return error.BadGlue;
+    const j = c.args[0].arg.i;
+    if (j < 0) return enterRun(f, m);
+    const sg = m.segs[@intCast(j)];
+    const sw = f.pf_switch.?;
+    try sw.run(sw.ctx, sg.slot);
+    f.ids = sg.ids;
+    f.slot.pending = .{ .start = sg.start, .n = @intCast(sg.ids.len) };
+    const k = r.kernels.others(r.stream);
+    const lo = try r.tensorAddr(c.args[3].arg.t);
+    try k.positions(@intCast(sg.start), @intCast(sg.ids.len), try r.tensorAddr(c.args[1].arg.t), try r.tensorAddr(c.args[2].arg.t), lo);
+    // a decoder replay: every row's window starts at the segment's first row (R0)
+    if (c.args[4].arg.b) try r.d.check(r.d.api.cuMemsetD32Async(lo, @bitCast(@as(i32, @intCast(sg.start))), sg.ids.len, r.stream.handle), "cuMemsetD32Async");
+}
+
+/// glue mpositions: every segment's positions into the run's "w.mpos64" (rope over the run's rows).
+pub fn runPositions(f: *Forward, r: *run.Runner, c: *const calls.Call) !void {
+    const m = f.multi orelse return error.BadGlue;
+    const k = r.kernels.others(r.stream);
+    const mpos = try r.tensorAddr(c.args[0].arg.t);
+    const pos = r.addressOf("w.pos") orelse return error.Unbound;
+    const lo = r.addressOf("w.lo") orelse return error.Unbound;
+    for (m.segs, m.row0) |sg, r0| try k.positions(@intCast(sg.start), @intCast(sg.ids.len), pos, mpos + 8 * r0, lo);
+}
+
+/// ced_stash (`store`): a segment's last rows into the ring at row p % ring; ced_load: ring rows back to rows 0 .. n.
+pub fn stashCopy(f: *Forward, r: *run.Runner, c: *const calls.Call, store: bool) !void {
+    const p = f.slot.pending orelse return error.BadGlue;
+    if (f.pf_stash) |h| try h.run(h.ctx); // the ring holds this slot's rows (slots.zig swaps it lazily)
+    const x = c.args;
+    if (x.len != 2 * ced.roles.len) return error.BadGlue;
+    const k: u64 = @min(p.n, ced.ring);
+    const a0: u64 = p.start + p.n - k;
+    var sp: [2]ced.Span = undefined;
+    for (0..ced.roles.len) |j| {
+        const t = x[j].arg.t;
+        const rg = x[ced.roles.len + j].arg.t;
+        const row = dim(t, 1) * buffers.dtSize(t.dt);
+        if (dim(rg, 1) * buffers.dtSize(rg.dt) != row or ld(t) != dim(t, 1) or dim(rg, 0) != ced.ring) return error.BadGlue;
+        const ta = try r.tensorAddr(t);
+        const ra = try r.tensorAddr(rg);
+        for (ced.spans(a0, a0 + k, &sp)) |s| {
+            const tr = ta + (a0 - p.start + s.at) * row;
+            const rr = ra + @as(u64, s.row) * row;
+            if (store) try copy(r, rr, tr, s.n * row) else try copy(r, tr, rr, s.n * row);
+        }
+    }
+}
+
+/// Once: the tables no loader form builds and the buffers a segment's exchanges / Engram rows need.
+pub fn ready(f: *Forward) !void {
+    const st = pf.state(f);
+    if (st.ready) return;
+    const r = f.runner;
+    // exl3.prefill's Hadamard: H[i, j] = (-1)^popcount(i & j), bf16
+    if (r.addressOf("s.pf.had")) |p| {
+        const h = try f.gpa.alloc(u16, 128 * 128);
+        defer f.gpa.free(h);
+        for (0..128) |i| for (0..128) |j| {
+            h[i * 128 + j] = if (@popCount(i & j) & 1 == 1) 0xBF80 else 0x3F80;
+        };
+        try cuda.DeviceBuffer.upload(.{ .d = r.d, .ptr = p, .len = h.len * 2 }, 0, std.mem.sliceAsBytes(h));
+    }
+    // x3gm's pointer tables: the routed experts' trellis addresses (forms' tables hold the shared expert's last)
+    var nb: [64]u8 = undefined;
+    var la = std.heap.ArenaAllocator.init(f.gpa);
+    defer la.deinit();
+    for (try f.backbone(la.allocator())) |L| for ([_][2][]const u8{ .{ "tg", "tp_g" }, .{ "tu", "tp_u" }, .{ "td", "tp_d" } }) |t| {
+        const dst = r.addressOf(try std.fmt.bufPrint(&nb, "s.L{d}.gm.{s}", .{ L, t[0] })) orelse continue;
+        const src = r.addressOf(try std.fmt.bufPrint(&nb, "s.L{d}.ex.{s}", .{ L, t[1] })) orelse return error.NoExpertTable;
+        try copy(r, dst, src, 8 * @as(usize, f.cfg.expertsOf(L).count));
+    };
+    // x3gm v2's width tables (gm2pf.zig, x3gm.Ragged.k2g / k2d): the routed experts' K2s, then a 0 sentinel
+    for (try f.backbone(la.allocator())) |L| for ([_][2][]const u8{ .{ "k2g", "k2_g" }, .{ "k2d", "k2_d" } }) |t| {
+        const dst = r.addressOf(try std.fmt.bufPrint(&nb, "s.L{d}.gm.{s}", .{ L, t[0] })) orelse continue;
+        const src = r.addressOf(try std.fmt.bufPrint(&nb, "s.L{d}.ex.{s}", .{ L, t[1] })) orelse return error.NoK2Table;
+        const Et: usize = f.cfg.expertsOf(L).count;
+        try copy(r, dst, src, 4 * Et);
+        try zero(r, dst + 4 * Et, 4);
+    };
+    // a segment's exchange send and Engram rows at their largest (decode's would grow them on the first segment)
+    const rows: usize = @intCast(f.opts.prefill_rows);
+    const W: usize = f.comm.world();
+    _ = try f.sendBuffer(r, 2 * rows * f.cfg.hidden);
+    if (f.engram) |h| {
+        const cols = h.headShard(f.comm.rank(), f.comm.world())[1] * @as(usize, f.cfg.engram_head_dim);
+        _ = try grow(&f.gathered, r.d, W * 2 * rows * cols);
+        for (0..h.layer_ids.len) |li| _ = try f.rows_stage[li].take(r.d, 2 * rows * cols);
+    }
+    _ = try f.ids_stage.take(r.d, 8 * rows);
+    try r.stream.synchronize();
+    st.ready = true;
+}
+
+pub fn grow(b: *?cuda.DeviceBuffer, d: *const cuda.Driver, bytes: usize) !u64 {
+    if (b.* == null or b.*.?.len < bytes) {
+        if (b.*) |*x| x.free();
+        b.* = try cuda.DeviceBuffer.alloc(d, @max(bytes, 256));
+    }
+    return b.*.?.ptr;
+}
+
+pub fn copy(r: *run.Runner, dst: u64, src: u64, bytes: usize) !void {
+    if (bytes == 0) return;
+    try r.d.check(r.d.api.cuMemcpyDtoDAsync_v2(dst, src, bytes, r.stream.handle), "cuMemcpyDtoDAsync");
+}
+
+pub fn zero(r: *run.Runner, dst: u64, bytes: usize) !void {
+    if (bytes == 0) return;
+    try r.d.check(r.d.api.cuMemsetD8Async(dst, 0, bytes, r.stream.handle), "cuMemsetD8Async");
+}
+
+pub fn numel(t: calls.Tensor) usize {
+    var n: usize = 1;
+    for (t.shape) |x| n *= @intCast(x);
+    return n;
+}
+
+pub fn bytesOf(t: calls.Tensor) usize {
+    return numel(t) * buffers.dtSize(t.dt);
+}
+
+pub fn dim(t: calls.Tensor, i: usize) usize {
+    return @intCast(t.shape[i]);
+}
+
+pub fn ld(t: calls.Tensor) usize {
+    return @intCast(t.stride[0]);
+}
+
+/// A prefill segment's glue steps; the ones a decode window has too are the decode glue's.
+pub fn glueFn(ctx: *anyopaque, r: *run.Runner, c: *const calls.Call) anyerror!void {
+    const f: *Forward = @ptrCast(@alignCast(ctx));
+    const st = pf.state(f);
+    const pr = st.tbo orelse return glueOne(ctx, r, c);
+    // TF_DSV41_PF_TBO: the step's micro-batch (B's are the side stream's) on the forward while it runs
+    const m = if (c.side) &pr.b else &pr.a;
+    const keep_ids = f.ids;
+    const keep_pending = f.slot.pending;
+    var keep_tail: Tail = .{ .len = f.slot.tail_len };
+    @memcpy(keep_tail.ids[0..keep_tail.len], f.slot.tail[0..f.slot.tail_len]);
+    f.ids = m.ids;
+    f.slot.pending = m.pending;
+    f.slot.tail_len = m.tail.len;
+    @memcpy(f.slot.tail[0..m.tail.len], m.tail.items());
+    st.moe = m.moe;
+    st.layer = m.layer;
+    if (c.side) std.mem.swap(longpf.State, &st.long, &st.long_b);
+    defer {
+        if (c.side) std.mem.swap(longpf.State, &st.long, &st.long_b);
+        m.moe = st.moe;
+        m.layer = st.layer;
+        f.ids = keep_ids;
+        f.slot.pending = keep_pending;
+        f.slot.tail_len = keep_tail.len;
+        @memcpy(f.slot.tail[0..keep_tail.len], keep_tail.items());
+    }
+    return glueOne(ctx, r, c);
+}
+
+pub fn glueOne(ctx: *anyopaque, r: *run.Runner, c: *const calls.Call) anyerror!void {
+    const f: *Forward = @ptrCast(@alignCast(ctx));
+    const st = pf.state(f);
+    const step = c.name["glue.".len..];
+    const k = r.kernels.others(r.stream);
+    const x = c.args;
+    const eql = std.mem.eql;
+    if (std.mem.startsWith(u8, step, "kx_")) return (f.kv orelse return error.BadGlue).glue(r, c, step);
+    if (eql(u8, step, "proj_carry")) {
+        // torch.cat([the slot's carry, the projection]): position start - 1's row, then the segment's
+        const carry = x[0].arg.t;
+        const buf = try r.tensorAddr(x[2].arg.t);
+        try copy(r, buf, try r.tensorAddr(carry), bytesOf(carry));
+        return copy(r, buf + bytesOf(carry), try r.tensorAddr(x[1].arg.t), bytesOf(x[1].arg.t));
+    }
+    if (eql(u8, step, "stream_merge")) return longpf.merge(&st.long, f.gpa, r, c);
+    // TF_DSV41_PF_TBO: A's ratio-2 carry into the slot at its layer's end; the pair's closing join has nothing to run
+    if (eql(u8, step, "copy_rows")) return copy(r, try r.tensorAddr(x[1].arg.t), try r.tensorAddr(x[0].arg.t), bytesOf(x[0].arg.t));
+    if (eql(u8, step, "tbo_join")) return;
+    if (eql(u8, step, "pf_seg")) return segGlue(f, r, c);
+    if (eql(u8, step, "mpositions")) return runPositions(f, r, c);
+    if (eql(u8, step, "ced_stash") or eql(u8, step, "ced_load")) return stashCopy(f, r, c, eql(u8, step, "ced_stash"));
+    if (eql(u8, step, "zeros")) return zero(r, try r.tensorAddr(x[0].arg.t), bytesOf(x[0].arg.t));
+    if (eql(u8, step, "image_keep")) return (try hooks.vision()).keep(f, r, c);
+    if (eql(u8, step, "image_split") or eql(u8, step, "image_merge")) return (try hooks.vision()).split(f, r, c, eql(u8, step, "image_merge"));
+    if (eql(u8, step, "gm_ticket_again")) return zero(r, try r.tensorAddr(x[0].arg.t), bytesOf(x[0].arg.t)); // vision.moe's text call: the same layer
+    if (eql(u8, step, "cand_keys")) {
+        const blocks = x[0].arg.t;
+        return k.candKeys(try r.tensorAddr(blocks), ld(blocks), dim(blocks, 0), dim(blocks, 1), @intCast(x[2].arg.i), try r.tensorAddr(x[1].arg.t));
+    }
+    if (eql(u8, step, "proj")) {
+        // blocks._projection: f32(kv), or ratio 2 [f32(kv) | f32(gate)]
+        const kv = x[0].arg.t;
+        const gate: u64 = if (x.len == 3) try r.tensorAddr(x[1].arg.t) else 0;
+        return k.widenCat(try r.tensorAddr(kv), gate, dim(kv, 0), dim(kv, 1), try r.tensorAddr(x[x.len - 1].arg.t));
+    }
+    if (eql(u8, step, "ix_w")) return k.f64Bf16F32(try r.tensorAddr(x[0].arg.t), try r.tensorAddr(x[1].arg.t), numel(x[0].arg.t));
+    if (eql(u8, step, "stage_in") or eql(u8, step, "stage_out")) {
+        // blocks.stage_rows(dst, dst_ring, src, src_ring, lo, hi), a tensor at a time (values, scales)
+        const dst_ring: usize = @intCast(x[2].arg.i);
+        const src_ring: usize = @intCast(x[5].arg.i);
+        const lo: u64 = @intCast(x[6].arg.i);
+        const hi: u64 = @intCast(x[7].arg.i);
+        for (0..2) |j| {
+            const dst = x[j].arg.t;
+            const src = x[3 + j].arg.t;
+            const row = dim(dst, 1) * buffers.dtSize(dst.dt);
+            if (dim(src, 1) * buffers.dtSize(src.dt) != row) return error.BadGlue;
+            try k.ringCopy(try r.tensorAddr(src), src_ring, try r.tensorAddr(dst), dst_ring, row, lo, hi);
+        }
+        return;
+    }
+    if (eql(u8, step, "gm_picks")) {
+        const pick = x[0].arg.t;
+        return k.gmPicks(try r.tensorAddr(pick), try r.tensorAddr(x[1].arg.t), dim(pick, 1), dim(x[2].arg.t, 1), dim(pick, 0), try r.tensorAddr(x[2].arg.t), try r.tensorAddr(x[3].arg.t));
+    }
+    if (eql(u8, step, "gm_ticket")) {
+        // a layer's MoE starts here: the layer gm_plan's K2 tables are of
+        if (st.moe >= st.layers.len) return error.BadGlue;
+        st.layer = st.layers[st.moe];
+        st.moe += 1;
+        return zero(r, try r.tensorAddr(x[0].arg.t), bytesOf(x[0].arg.t));
+    }
+    if (eql(u8, step, "gm_plan")) {
+        // x3gm._run_ragged: the picks of one width (the others past the table), then x3gm.plan's passes
+        const pk = x[0].arg.t;
+        const P = numel(pk);
+        const k2: u32 = @intCast(x[6].arg.i);
+        const down = x[7].arg.b;
+        const bm: usize = @intCast(x[8].arg.i);
+        const Et: usize = @intCast(x[9].arg.i);
+        // k2 0 (x3gm._run_v2's one plan, gm2pf.zig): every pick, no width mask
+        if (k2 == 0) return r.kernels.experts(r.stream).gmPlan(try r.tensorAddr(pk), P, Et, bm, try r.tensorAddr(x[1].arg.t), try r.tensorAddr(x[2].arg.t), try r.tensorAddr(x[3].arg.t), try r.tensorAddr(x[4].arg.t), try r.tensorAddr(x[5].arg.t));
+        var nb: [64]u8 = undefined;
+        // forms' "s.L.ex.k2_{g,d}": each routed expert's K2 (gate = up), the shared expert's past Et (never read)
+        const tab = r.addressOf(try std.fmt.bufPrint(&nb, "s.L{d}.ex.k2_{s}", .{ st.layer, if (down) "d" else "g" })) orelse return error.NoK2Table;
+        const masked = try grow(&st.masked, r.d, 4 * P);
+        try k.widthMask(try r.tensorAddr(pk), P, tab, Et, k2, masked);
+        const t = [5]u64{ try r.tensorAddr(x[1].arg.t), try r.tensorAddr(x[2].arg.t), try r.tensorAddr(x[3].arg.t), try r.tensorAddr(x[4].arg.t), try r.tensorAddr(x[5].arg.t) };
+        return r.kernels.experts(r.stream).gmPlan(masked, P, Et, bm, t[0], t[1], t[2], t[3], t[4]);
+    }
+    if (eql(u8, step, "swiglu")) {
+        // the shared expert's silu(min(g, limit)) * clamp(u, +-limit), fp32
+        const g = x[0].arg.t;
+        const u = x[1].arg.t;
+        const act = x[2].arg.t;
+        return k.swiglu(try r.tensorAddr(g), ld(g), try r.tensorAddr(u), ld(u), try r.tensorAddr(act), ld(act), dim(g, 0), dim(g, 1), @floatCast(x[3].arg.f));
+    }
+    if (eql(u8, step, "moe_sum")) {
+        // bf16(routed + shared): one RNE of the fp32 sum; no shared expert: the routed partial's cast
+        const routed = x[0].arg.t;
+        if (x.len == 3) return k.addBf16(try r.tensorAddr(routed), try r.tensorAddr(x[1].arg.t), try r.tensorAddr(x[2].arg.t), numel(routed));
+        return k.castBf16(try r.tensorAddr(routed), try r.tensorAddr(x[1].arg.t), numel(routed));
+    }
+    return Forward.glueFn(ctx, r, c);
+}
diff --git a/zig/src/families/deepseek_v41/forward_prefill_k4.zig b/zig/src/families/deepseek_v41/forward_prefill_k4.zig
new file mode 100644
index 0000000000..ad4a275cd2
--- /dev/null
+++ b/zig/src/families/deepseek_v41/forward_prefill_k4.zig
@@ -0,0 +1,246 @@
+//! The prefill's own workspace (TF_DSV41_PF_4K / _PF_TBO): plan, agreement, acquire, release.
+
+const std = @import("std");
+const quiet = @import("builtin").is_test; // a test's stderr is the build runner's channel
+const cuda = @import("cuda");
+const calls = @import("block_calls.zig");
+const buffers = @import("buffers.zig");
+const block = @import("block.zig");
+const block_prefill = @import("block_prefill.zig");
+const image_opts = @import("image_opts.zig");
+const fwd = @import("forward.zig");
+const graphs = @import("graphs.zig");
+const serve_knobs = @import("serve_knobs.zig");
+const Forward = fwd.Forward;
+const pf = @import("forward_prefill.zig");
+const encoderLayers = pf.encoderLayers;
+const keepsShared = pf.keepsShared;
+const lastStartWith = pf.lastStartWith;
+const log = pf.log;
+const planPrograms = pf.planPrograms;
+
+/// Moves workspace roles that `forward` holds at least as large into `keep`, so the 4K program names the forward's.
+pub fn k4Trim(gpa: std.mem.Allocator, wp: *buffers.Plan, forward: *const buffers.Plan, keep: *block_prefill.Keep) !void {
+    var i: usize = 0;
+    while (i < wp.sizes.count()) {
+        const k = wp.sizes.keys()[i];
+        const v = wp.sizes.values()[i];
+        const at = std.mem.indexOf(u8, k, K4.tag) orelse {
+            i += 1;
+            continue;
+        };
+        const bare = try std.mem.concat(gpa, u8, &.{ k[0..at], k[at + K4.tag.len ..] });
+        const have = forward.sizes.get(bare) orelse 0;
+        if (have < v or keep.contains(bare)) {
+            gpa.free(bare);
+            if (have >= v) wp.sizes.orderedRemoveAt(i) else i += 1;
+            continue;
+        }
+        try keep.put(gpa, bare, {});
+        wp.sizes.orderedRemoveAt(i);
+    }
+}
+
+/// The workspace programs: TF_DSV41_PF_4K's 4,096-row encoder segment and TF_DSV41_PF_TBO's pair, at 0 and last start.
+pub fn planWorkspace(f: *Forward, wp: *buffers.Plan, rows: u32, layers: []const u32) !void {
+    const replay = pf.state(f).mode == .replay;
+    if (pf.state(f).k4) {
+        const enc = try encoderLayers(f, layers);
+        const o4 = k4Options(image_opts.planOptions(f));
+        const top4 = try lastStartWith(f, o4, enc, K4.rows, .encoder);
+        for ([_]i64{ 0, top4 }) |start| {
+            var ka = std.heap.ArenaAllocator.init(f.gpa);
+            defer ka.deinit();
+            const cs = try block_prefill.emitReplay(ka.allocator(), f.cfg, f.widths, o4, enc, K4.rows, start, .encoder);
+            try wp.add(try block_prefill.ownCalls(ka.allocator(), cs, K4.tag));
+        }
+    }
+    if (replay and f.opts.pf_tbo and rows >= 2 * block_prefill.tbo_min) {
+        const enc = try encoderLayers(f, layers);
+        const k4 = pf.state(f).k4;
+        const op = if (k4) k4Options(image_opts.planOptions(f)) else image_opts.planOptions(f);
+        const seg: u32 = if (k4) K4.rows else rows;
+        const top = @max(0, try lastStartWith(f, op, enc, seg, .encoder) - seg); // B at the last start a segment takes
+        for ([_]i64{ 0, top }) |start| {
+            var ta = std.heap.ArenaAllocator.init(f.gpa);
+            defer ta.deinit();
+            const cs = try block_prefill.emitTbo(ta.allocator(), f.cfg, f.widths, op, enc, seg, seg, start);
+            try wp.add(try wsPair(ta.allocator(), cs, k4));
+        }
+    }
+}
+
+/// The bytes the workspace allocates at a long prompt; 0 when neither TF_DSV41_PF_4K nor TF_DSV41_PF_TBO is on.
+pub fn workspaceBytes(f: *Forward, rows: u32) !u64 {
+    if (!(pf.state(f).k4 or (pf.state(f).mode == .replay and f.opts.pf_tbo))) return 0;
+    var la = std.heap.ArenaAllocator.init(f.gpa);
+    defer la.deinit();
+    var wp: buffers.Plan = .{ .a = la.allocator() };
+    const layers = try f.backbone(la.allocator());
+    try planWorkspace(f, &wp, rows, layers);
+    if (keepsShared(f)) {
+        // the same trim `plan` makes: the roles the forward's prefill programs hold already
+        var fp: buffers.Plan = .{ .a = la.allocator() };
+        try planPrograms(f, &fp, rows, layers);
+        var keep: block_prefill.Keep = .empty;
+        try k4Trim(la.allocator(), &wp, &fp, &keep);
+    }
+    return planBytes(&wp);
+}
+
+pub fn planBytes(wp: *const buffers.Plan) u64 {
+    var total: u64 = 0;
+    for (wp.sizes.keys(), wp.sizes.values()) |k, v| {
+        if (std.mem.indexOf(u8, k, K4.tag) == null) continue;
+        total += std.mem.alignForward(u64, v, 256);
+    }
+    return total;
+}
+
+/// TF_DSV41_PF_4K's workspace: a 4K segment's roles in their own namespace, allocated per long prompt, then released.
+pub const K4 = struct {
+    pub const rows: u32 = serve_knobs.pf4k_rows;
+    pub const tag = "k4~";
+    plan: ?buffers.Plan = null,
+    /// the bare roles a 4K segment shares with the forward (k4Trim under TF_DSV41_PF_WS_SHARE=1; else none)
+    keep: block_prefill.Keep = .empty,
+    buf: ?cuda.DeviceBuffer = null,
+    bytes: u64 = 0,
+    allocs: u64 = 0,
+    releases: u64 = 0,
+    /// the exchange send buffer and Engram rows while a 4K segment runs; the forward's own are never regrown
+    send: ?cuda.DeviceBuffer = null,
+    gathered: ?cuda.DeviceBuffer = null,
+};
+
+/// The rows a prompt segment holds: 4,096 under TF_DSV41_PF_4K, else the forward's (the round planner's rows too).
+pub fn segmentRows(f: *const Forward) usize {
+    const st = pf.state(f);
+    return if (st.k4 and st.ws_ok) K4.rows else @intCast(f.opts.prefill_rows);
+}
+
+/// Whether this prompt call may use the workspace, agreed by every rank; no: 2,048-row segments, same bits, logged.
+pub fn wsAgreed(f: *Forward) !bool {
+    const st = pf.state(f);
+    const ws = &st.k4ws;
+    if (ws.buf != null) return true;
+    const pl = ws.plan orelse return false;
+    const bytes = planBytes(&pl);
+    const hard = wsFloorGiB();
+    const avail = graphs.memAvailableGiB();
+    const left = if (avail) |v| v - @as(f64, @floatFromInt(bytes)) / (1 << 30) else hard;
+    if (st.agreement == null) st.agreement = try graphs.Agreement.init(f.runner.d, f.comm, f.runner.stream);
+    const ok = try st.agreement.?.agree().all(st.agreement.?.agree().ctx, left >= hard);
+    if (!ok) {
+        st.ws_fallbacks += 1;
+        if (st.ws_fallbacks <= 4 or st.ws_fallbacks % 64 == 0)
+            if (!quiet) log.warn("prefill workspace: {d} MiB does not fit on every rank (rank {d}: {d:.2} GiB MemAvailable leaves {d:.2}, floor {d:.1} GiB, TF_DSV41_PF_WS_FLOOR_GIB): this prompt piece runs 2,048-row segments (fallback {d})", .{ bytes >> 20, f.comm.rank(), avail orelse -1, left, hard, st.ws_fallbacks });
+    }
+    return ok;
+}
+
+/// The options a 4,096-row segment emits with: the forward's at 4,096 rows with x3gm one block a segment.
+pub fn k4Options(o: block.Options) block.Options {
+    var x = o;
+    x.prefill_rows = K4.rows;
+    x.pf4k = true;
+    return x;
+}
+
+/// A TF_DSV41_PF_TBO pair's calls with private roles in the workspace (B's always, A's too at 4K): no forward bytes.
+pub fn wsPair(a: std.mem.Allocator, cs: []const calls.Call, all: bool) ![]calls.Call {
+    if (all) return block_prefill.ownCalls(a, cs, K4.tag);
+    const out = try a.alloc(calls.Call, cs.len);
+    for (cs, out) |c, *x| {
+        x.* = c;
+        const args = try a.alloc(calls.Named, c.args.len);
+        for (c.args, args) |y, *z| z.* = .{ .name = y.name, .arg = try wsArg(a, y.arg) };
+        x.args = args;
+    }
+    return out;
+}
+
+pub fn wsArg(a: std.mem.Allocator, x: calls.Arg) !calls.Arg {
+    switch (x) {
+        .t, .opaque_table => |t| {
+            if (t.role != .buf or std.mem.indexOf(u8, t.role.buf, "b~") == null) return x;
+            var u = t;
+            u.role = .{ .buf = try block_prefill.ownRole(a, t.role.buf, K4.tag) };
+            return if (x == .t) .{ .t = u } else .{ .opaque_table = u };
+        },
+        .list => |l| {
+            const o = try a.alloc(calls.Arg, l.len);
+            for (l, o) |y, *z| z.* = try wsArg(a, y);
+            return .{ .list = o };
+        },
+        else => return x,
+    }
+}
+
+/// The workspace's roles bound to one fresh zeroed allocation (the first 4K segment of a prompt).
+pub fn k4Acquire(f: *Forward) !void {
+    const ws = &pf.state(f).k4ws;
+    if (ws.buf != null) return;
+    const pl = ws.plan orelse return error.Unbound;
+    const total = planBytes(&pl);
+    // the room was agreed first (wsAgreed): every rank fits the workspace above the hard floor
+    const t0 = nowMs();
+    const b = cuda.DeviceBuffer.alloc(f.runner.d, @max(total, 256)) catch |e| {
+        log.err("prefill workspace: allocating {d} MiB failed ({t})", .{ total >> 20, e });
+        return e;
+    };
+    errdefer {
+        var x = b;
+        x.free();
+    }
+    try b.fill8(0, null);
+    var at: u64 = 0;
+    for (pl.sizes.keys(), pl.sizes.values()) |k, v| {
+        if (std.mem.indexOf(u8, k, K4.tag) == null) continue;
+        try f.runner.external(k, b.ptr + at);
+        at += std.mem.alignForward(u64, v, 256);
+    }
+    ws.buf = b;
+    ws.bytes = total;
+    ws.allocs += 1;
+    // the allocation's cost a prompt (the GB10 maps host pages for it): outside the prefill profile's segments
+    try f.runner.stream.synchronize();
+    const ms = nowMs() - t0;
+    if (ws.allocs <= 4 or ws.allocs % 64 == 0) log.info("prefill 4K: rank {d}: workspace {d} MiB allocated and zeroed in {d:.1} ms (prompt {d}), held while the prompt prefills", .{ f.comm.rank(), total >> 20, ms, ws.allocs });
+}
+
+pub fn nowMs() f64 {
+    var ts: std.c.timespec = undefined;
+    _ = std.c.clock_gettime(.MONOTONIC, &ts);
+    return @as(f64, @floatFromInt(ts.sec)) * 1e3 + @as(f64, @floatFromInt(ts.nsec)) / 1e6;
+}
+
+/// TF_DSV41_PF_WS_FLOOR_GIB: MemAvailable the workspace must leave on every rank (default TF_DSV41_FLOOR_HARD_GIB).
+pub fn wsFloorGiB() f64 {
+    const v = std.c.getenv("TF_DSV41_PF_WS_FLOOR_GIB") orelse return hardFloorGiB();
+    return std.fmt.parseFloat(f64, std.mem.span(v)) catch hardFloorGiB();
+}
+
+/// TF_DSV41_FLOOR_HARD_GIB (4, the boot check's).
+pub fn hardFloorGiB() f64 {
+    const v = std.c.getenv("TF_DSV41_FLOOR_HARD_GIB") orelse return 4.0;
+    return std.fmt.parseFloat(f64, std.mem.span(v)) catch 4.0;
+}
+
+/// The workspace back (a prompt's CED finish: no 4K segment runs until the next prompt's first).
+pub fn k4Release(f: *Forward) !void {
+    const ws = &pf.state(f).k4ws;
+    var b = ws.buf orelse return;
+    try f.runner.stream.synchronize();
+    const before = graphs.memAvailableGiB();
+    b.free();
+    ws.buf = null;
+    // the prefill -> decode handoff: the row graphs' memory floor right after the free (first prompts, then every 64th)
+    ws.releases += 1;
+    if (ws.releases <= 4 or ws.releases % 64 == 0)
+        log.info("prefill 4K: rank {d}: workspace {d} MiB freed at the prompt's end (release {d}); MemAvailable {d:.2} -> {d:.2} GiB", .{ f.comm.rank(), ws.bytes >> 20, ws.releases, before orelse -1, graphs.memAvailableGiB() orelse -1 });
+    if (ws.send) |*x| x.free();
+    if (ws.gathered) |*x| x.free();
+    ws.send = null;
+    ws.gathered = null;
+}
diff --git a/zig/src/families/deepseek_v41/forward_sample.zig b/zig/src/families/deepseek_v41/forward_sample.zig
new file mode 100644
index 0000000000..69b5e1e39e
--- /dev/null
+++ b/zig/src/families/deepseek_v41/forward_sample.zig
@@ -0,0 +1,118 @@
+//! The forward's greedy pick: each rank's best column (host or GPU), all-gathered, lower rank wins.
+
+const std = @import("std");
+const cuda = @import("cuda");
+const run = @import("run.zig");
+const hooks = @import("forward_hooks.zig");
+const ph = @import("phases.zig");
+const fwd = @import("forward.zig");
+const fwd_follow = @import("forward_follow.zig");
+const Forward = fwd.Forward;
+const kops = fwd.kops;
+const op_greedy = fwd_follow.op_greedy;
+
+/// Greedy choices of the last window's rows: each rank's max (lowest column), all-gathered (lower rank wins).
+pub fn greedy(f: *Forward, out: []u32) !void {
+    if (f.leads()) try f.link.?.send(&.{ op_greedy, @intCast(out.len) });
+    const tc = ph.start(f.phases, .candidates);
+    defer tc.stop();
+    const r = f.runner;
+    const n = out.len;
+    const W: usize = f.comm.world();
+    const V: usize = f.cfg.vocab / W;
+    // pinned: the logits [n, V], then the pairs [2n] and every rank's [W][2n] (reused, no allocation a window)
+    const lb = std.mem.alignForward(usize, 4 * n * V, 8);
+    const need = lb + 16 * n * (W + 1);
+    if (f.pick_host == null or f.pick_host.?.bytes.len < need) {
+        if (f.pick_host) |*b| b.free();
+        f.pick_host = try cuda.HostBuffer.alloc(r.d, @max(need, 1 << 20));
+    }
+    if (f.pick_dev == null or f.pick_dev.?.len < 16 * n * (W + 1)) {
+        if (f.pick_dev) |*b| b.free();
+        f.pick_dev = try cuda.DeviceBuffer.alloc(r.d, @max(16 * n * (W + 1), 4096));
+    }
+    const hb = f.pick_host.?.bytes;
+    const host: []f32 = @alignCast(std.mem.bytesAsSlice(f32, hb[0 .. 4 * n * V]));
+    const pairs: []f64 = @alignCast(std.mem.bytesAsSlice(f64, hb[lb..need]));
+    const dev = f.pick_dev.?;
+    const logits = r.addressOf("w.logits") orelse return error.Unbound;
+    if (f.grammar) |g| try g.apply(g.ptr, 0, @intCast(n));
+    if (f.greedy_gpu == null) f.greedy_gpu = if (std.c.getenv("TF_DSV41_GREEDY_GPU")) |v| std.mem.eql(u8, std.mem.span(v), "1") else false;
+    if (f.greedy_gpu.? and V <= 1 << 16) {
+        // the device pick: topk_keys' best column a row, [n] fp32 values + [n] int64 columns back, not logits
+        const vals_dev = try f.greedyScratch(r, n);
+        const cols_dev = vals_dev + std.mem.alignForward(u64, 4 * n, 16);
+        const k = r.kernels.others(r.stream);
+        try k.topKeys(logits, V, n, V, 1, vals_dev, cols_dev);
+        f.pick_last = null;
+        if (k.f.pick_pack != null and k.f.pick_merge != null and n <= kops.glue.pick_max_rows) {
+            // the pairs, their exchange and the merge on the device; one copy back (glue.cu pick_*)
+            const ids_dev = cols_dev + 8 * n;
+            const out_dev = ids_dev + 8 * n;
+            try k.pickPack(vals_dev, cols_dev, n, f.comm.rank() * V, dev.ptr);
+            try f.comm.allGather(dev.ptr, dev.ptr + 16 * n, 2 * n, .f64, r.stream.handle);
+            const chain = if (f.slot.pending) |p| p.n == n and f.ids.len >= n else false;
+            const start: i64 = if (f.slot.pending) |p| @intCast(p.start) else 0;
+            const ih: []i64 = @alignCast(std.mem.bytesAsSlice(i64, hb[0 .. 8 * n]));
+            if (chain) {
+                for (ih, f.ids[0..n]) |*x, t| x.* = t;
+                try r.d.check(r.d.api.cuMemcpyHtoDAsync_v2(ids_dev, ih.ptr, 8 * n, r.stream.handle), "cuMemcpyHtoDAsync");
+            }
+            try k.pickMerge(dev.ptr + 16 * n, W, n, if (chain) ids_dev else 0, start, out_dev);
+            const oh: []i64 = @alignCast(std.mem.bytesAsSlice(i64, hb[std.mem.alignForward(usize, 8 * n, 16)..][0 .. 8 * (n + 3)]));
+            try r.d.check(r.d.api.cuMemcpyDtoHAsync_v2(oh.ptr, out_dev, 8 * (n + 3), r.stream.handle), "cuMemcpyDtoHAsync");
+            f.pick_last = .{ .out = out_dev, .n = @intCast(n), .chain = chain, .start = @intCast(start), .host = oh.ptr };
+            if (f.after_pick) |h| {
+                // work from the device pick (speculative DSpark pass) follows the copy back; the host waits on it
+                if (f.pick_done == null) f.pick_done = try cuda.Event.init(r.d, false);
+                try f.pick_done.?.record(r.stream);
+                try h.run(h.ctx, f.pick_last.?);
+                try f.pick_done.?.synchronize();
+            } else try r.stream.synchronize();
+            for (out, oh[0..n]) |*o, x| o.* = @intCast(x);
+            return;
+        }
+        const vh: []f32 = @alignCast(std.mem.bytesAsSlice(f32, hb[0 .. 4 * n]));
+        const ch: []i64 = @alignCast(std.mem.bytesAsSlice(i64, hb[std.mem.alignForward(usize, 4 * n, 16)..][0 .. 8 * n]));
+        try r.d.check(r.d.api.cuMemcpyDtoHAsync_v2(vh.ptr, vals_dev, 4 * n, r.stream.handle), "cuMemcpyDtoHAsync");
+        try r.d.check(r.d.api.cuMemcpyDtoHAsync_v2(ch.ptr, cols_dev, 8 * n, r.stream.handle), "cuMemcpyDtoHAsync");
+        try r.stream.synchronize();
+        for (0..n) |row| {
+            pairs[2 * row] = vh[row];
+            pairs[2 * row + 1] = @floatFromInt(@as(usize, @intCast(ch[row])) + f.comm.rank() * V);
+        }
+    } else {
+        try r.d.check(r.d.api.cuMemcpyDtoHAsync_v2(host.ptr, logits, 4 * n * V, r.stream.handle), "cuMemcpyDtoHAsync");
+        try r.stream.synchronize();
+        const argmax = (try hooks.greedy()).argmax;
+        for (0..n) |row| {
+            const vals = host[row * V ..][0..V];
+            const best = argmax(vals);
+            pairs[2 * row] = vals[best];
+            pairs[2 * row + 1] = @floatFromInt(best + f.comm.rank() * V);
+        }
+    }
+    try dev.uploadAsync(0, std.mem.sliceAsBytes(pairs[0 .. 2 * n]), r.stream.handle);
+    try f.comm.allGather(dev.ptr, dev.ptr + 16 * n, 2 * n, .f64, r.stream.handle);
+    const all = pairs[2 * n ..];
+    try dev.downloadAsync(16 * n, std.mem.sliceAsBytes(all), r.stream.handle);
+    try r.stream.synchronize();
+    for (out, 0..) |*o, row| {
+        var bk: usize = 0;
+        for (1..W) |k| if (all[2 * (k * n + row)] > all[2 * (bk * n + row)]) {
+            bk = k;
+        };
+        o.* = @intFromFloat(all[2 * (bk * n + row) + 1]);
+    }
+}
+
+/// The GPU pick's device scratch: [n] fp32 values, [n] int64 columns, [n] int64 window ids, [n + 3] int64 out.
+pub fn greedyScratch(f: *Forward, r: *run.Runner, n: usize) !u64 {
+    const bytes = std.mem.alignForward(usize, 4 * n, 16) + 8 * n + 8 * n + 8 * (n + 3);
+    if (f.greedy_dev == null or f.greedy_dev.?.len < bytes) {
+        if (f.greedy_dev) |*b| b.free();
+        f.greedy_dev = null;
+        f.greedy_dev = try cuda.DeviceBuffer.alloc(r.d, @max(bytes, 4096));
+    }
+    return f.greedy_dev.?.ptr;
+}
diff --git a/zig/src/families/deepseek_v41/forward_types.zig b/zig/src/families/deepseek_v41/forward_types.zig
new file mode 100644
index 0000000000..d77adf3c09
--- /dev/null
+++ b/zig/src/families/deepseek_v41/forward_types.zig
@@ -0,0 +1,71 @@
+//! Plain types the forward shares with its later layers (CED stash, multi-segment runs, gate items, round graph).
+const std = @import("std");
+const eh = @import("engram_host.zig");
+
+/// the decoder's rows over a prompt's end, its last token (the verify row) included
+pub const replay_rows: u32 = 128;
+/// the stash rows a slot needs (the prefilled part of the tail)
+pub const keep: u32 = replay_rows - 1;
+/// the stash ring's rows (position p at row p % ring): at least `keep`, a power of two
+pub const ring: u32 = 128;
+
+/// A slot's stash bookkeeping (rows on the device, "s.ced.*" row p % ring): positions [max(from, hi - keep), hi), ids.
+pub const Stash = struct {
+    from: u64 = 0,
+    hi: u64 = 0,
+    ids: [ring]u32 = @splat(0),
+
+    /// A segment at `start`; a gap starts the stash over there.
+    pub fn extend(s: *Stash, start: u64, ids: []const u32) void {
+        if (s.hi != start) s.from = start;
+        for (ids, 0..) |t, i| s.ids[@intCast((start + i) % ring)] = t;
+        s.hi = start + ids.len;
+    }
+
+    /// The first position held.
+    pub fn lo(s: *const Stash) u64 {
+        return @max(s.from, s.hi -| keep);
+    }
+
+    /// The replay check: the stash ends at `end` and holds [r0, end).
+    pub fn covers(s: *const Stash, r0: u64, end: u64) bool {
+        return s.hi == end and s.lo() <= r0 and end - r0 <= keep;
+    }
+
+    /// The ids of positions [r0, end) (`covers` first).
+    pub fn tail(s: *const Stash, r0: u64, end: u64, out: []u32) void {
+        for (out[0..@intCast(end - r0)], 0..) |*o, i| o.* = s.ids[@intCast((r0 + i) % ring)];
+    }
+};
+
+/// One segment of a multi-segment run: a slot's prompt ids from its position `start`.
+pub const MultiSeg = struct { slot: u32, start: u64, ids: []const u32 };
+
+/// An Engram tail (a slot's last ids before the run).
+pub const Tail = struct {
+    ids: [eh.max_ngram]u32 = undefined,
+    len: usize = 0,
+
+    pub fn items(t: *const Tail) []const u32 {
+        return t.ids[0..t.len];
+    }
+};
+
+/// The run in flight: its segments, each one's first row and Engram tail at the start, every segment's ids in order.
+pub const Multi = struct { segs: []const MultiSeg, row0: []const usize, tails: []const Tail, ids: []const u32 };
+
+/// One slot's decoder replay in a batched run: the tail's first position and rows, its first row in the run.
+pub const Replayed = struct { slot: u32, at: u64, rows: u32, row0: u32 };
+
+/// A window's part on one slot: its ids and the slot's Engram tail (lookback) before them.
+pub const Item = struct { ids: []const u32, tail: []const u32 };
+
+/// TF_DSV41_ROUND_GRAPH (round_graph.zig): the settings read once.
+pub const RoundGraph = struct {
+    split: bool = false,
+    gate: bool = false,
+    /// layers in the head graph
+    head: u32 = 1,
+};
+
+pub const stream_min: i64 = 4096; // TF_DSV41_INDEX_STREAM_MIN (block.zig: a decode window past it would stream)
diff --git a/zig/src/families/deepseek_v41/gm2pf.zig b/zig/src/families/deepseek_v41/gm2pf.zig
new file mode 100644
index 0000000000..18ddb093cf
--- /dev/null
+++ b/zig/src/families/deepseek_v41/gm2pf.zig
@@ -0,0 +1,163 @@
+//! x3gm v2 on prefill (TF_DSV41_GM_V2): a segment's routed experts through gm2_kernel, same bits as gm_kernel.
+
+const std = @import("std");
+const quiet = @import("builtin").is_test; // a test's stderr is the build runner's channel
+const calls = @import("block_calls.zig");
+const knob_types = @import("knob_types.zig");
+const block = @import("block.zig");
+const E = block.Emitter;
+const Arg = calls.Arg;
+
+const Mode = knob_types.GmMode;
+
+/// x3gm.MAX_WIDTHS: the ticket's down half starts there
+pub const max_widths: i64 = 7;
+/// x3gm.BM: members a pass of gm2_kernel's tiles (V2_GU 0 / 1, V2_DN 0)
+pub const bm: i64 = 64;
+/// x3gm.tuned2(shx False) without TF_DSV41_GM_CFG
+pub const cfg_gu: i64 = 1;
+/// TF_DSV41_GM_GU2: gate / up at x3gm.cu's GU2 (128-member passes, kernels_exl3.gu2_cfg) over a plan of bm2
+pub const cfg_gu2: i64 = 2;
+pub const bm2: i64 = 128;
+/// GU2 runs where passes fill: a 4,096-row segment's block (~64 members an expert)
+pub const gu2_rows: i64 = 4096;
+
+fn gu2On(e: *const E, r: Routed) bool {
+    _ = r;
+    return e.o.gm_gu2 and e.n >= gu2_rows;
+}
+pub const cfg_dn: i64 = 0;
+/// x3gm.K2S: the widths gm2_kernel dispatches (x3gm.supported refuses a block with any other)
+pub const k2s: u32 = (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 10);
+
+pub const Get = *const fn ([]const u8) ?[]const u8;
+
+/// TF_DSV41_GM_V2 by x3gm.config's rule: `(env or "0").strip()` one of "0", "1", "split", else refused.
+pub fn mode(get: Get) !Mode {
+    const raw = std.mem.trim(u8, get("TF_DSV41_GM_V2") orelse "", " \t");
+    if (raw.len == 0 or std.mem.eql(u8, raw, "0")) return .off;
+    if (std.mem.eql(u8, raw, "1")) return .one;
+    if (std.mem.eql(u8, raw, "split")) return .split;
+    if (!quiet) std.log.warn("TF_DSV41_GM_V2={s}: expected 0, 1 or split", .{raw});
+    return error.BadKnob;
+}
+
+/// The mode a capture's prefill ran, read from its launches: no `gateup2` is off, one a rotation `one`, more `split`.
+pub fn ofLaunches(names: anytype) Mode {
+    var rot: usize = 0;
+    var gu2: usize = 0;
+    while (names.next()) |n| {
+        if (std.mem.eql(u8, n, "tf_dsv41_x3gm_v1.rot")) rot += 1;
+        if (std.mem.eql(u8, n, "tf_dsv41_x3gm_v1.gateup2")) gu2 += 1;
+    }
+    if (gu2 == 0) return .off;
+    return if (gu2 > rot) .split else .one;
+}
+
+/// One MoE call's routed experts after the rotation and the ticket's zeroing (block_prefill.zig's moeRows).
+pub const Routed = struct {
+    L: u32,
+    /// "L.moe": the routed stacks' weight prefix
+    pre: []const u8,
+    /// routed pairs (rows x topk), routed experts, hidden, this rank's expert width
+    P: i64,
+    Et: i64,
+    D: i64,
+    I: i64,
+    /// the routed picks [rows, topk] (gm_picks), the rotated inputs, Xd, Y, the ticket [2 x max_widths]
+    pk: Arg,
+    xg: Arg,
+    xu: Arg,
+    xd: Arg,
+    y: Arg,
+    ticket: Arg,
+    limit: f64,
+    /// gate/up and down widths present (Widths.gm; bit k2): split's launches; `one` checks them when known
+    widths: ?[2]u32,
+};
+
+/// The calls of x3gm._run_v2's block for `m` (not off).
+pub fn emit(e: *E, m: Mode, r: Routed) !void {
+    std.debug.assert(m != .off);
+    if (r.widths) |w| if ((w[0] | w[1]) & ~k2s != 0 or w[0] == 0 or w[1] == 0) return error.Unsupported; // x3gm.supported: "width"
+    const L = r.L;
+    const tmax = @divFloor(r.P, bm) + r.Et + 1;
+    const tab = [3]Arg{
+        try e.buf("s.L{d}.gm.tg", .{L}, .i64, &.{r.Et}),
+        try e.buf("s.L{d}.gm.tu", .{L}, .i64, &.{r.Et}),
+        try e.buf("s.L{d}.gm.td", .{L}, .i64, &.{r.Et}),
+    };
+    const k2g = try e.buf("s.L{d}.gm.k2g", .{L}, .i32, &.{r.Et + 1});
+    const k2d = try e.buf("s.L{d}.gm.k2d", .{L}, .i32, &.{r.Et + 1});
+    if (m == .one) {
+        const p = try planOf(e, r, "g", 0, 0, false, tmax, bm);
+        if (gu2On(e, r)) {
+            // TF_DSV41_GM_GU2: 128-member passes over their own plan (same stable expert order as down's plan)
+            const h = try planOf(e, r, "h", 0, 0, false, @divFloor(r.P, bm2) + r.Et + 1, bm2);
+            try gateupCfg(e, r, tab, k2g, h, 0, cfg_gu2);
+        } else try gateup(e, r, tab, k2g, p, 0);
+        return down(e, r, tab[2], k2d, p, 0);
+    }
+    const w = r.widths orelse return error.NoWidth;
+    for (0..2) |dn| {
+        var j: i64 = 0;
+        for (0..32) |kk| {
+            if (w[dn] >> @intCast(kk) & 1 == 0) continue;
+            const p = try planOf(e, r, if (dn == 1) "d" else "g", j, @intCast(kk), dn == 1, tmax, bm);
+            if (dn == 0) try gateup(e, r, tab, k2g, p, j) else try down(e, r, tab[2], k2d, p, j);
+            j += 1;
+        }
+    }
+}
+
+/// x3gm.plan's five tensors of one launch (k2 0: every pick in one plan; else that width's picks first).
+fn planOf(e: *E, r: Routed, tag: []const u8, j: i64, k2: i64, dn: bool, tmax: i64, pass: i64) ![5]Arg {
+    const p = [5]Arg{
+        try e.buf("L.gm.{s}{d}.order", .{ tag, j }, .i32, &.{r.P}),
+        try e.buf("L.gm.{s}{d}.pe", .{ tag, j }, .i32, &.{tmax}),
+        try e.buf("L.gm.{s}{d}.poff", .{ tag, j }, .i32, &.{tmax}),
+        try e.buf("L.gm.{s}{d}.pcnt", .{ tag, j }, .i32, &.{tmax}),
+        try e.buf("L.gm.{s}{d}.npass", .{ tag, j }, .i32, &.{1}),
+    };
+    try e.glue("gm_plan", &.{ r.pk, p[0], p[1], p[2], p[3], p[4], .{ .i = k2 }, .{ .b = dn }, .{ .i = pass }, .{ .i = r.Et } });
+    return p;
+}
+
+fn ticketAt(e: *E, r: Routed, i: i64) !Arg {
+    return e.view(r.ticket.t.role, .i32, &.{1}, &.{1}, i * 4);
+}
+
+/// x3gm.cpp gateup2 (inputs, tables, plan, ticket, scales, xd, K, N, shx, cfg, use_ticket, limit, tables)
+fn gateup(e: *E, r: Routed, tab: [3]Arg, k2g: Arg, p: [5]Arg, j: i64) !void {
+    return gateupCfg(e, r, tab, k2g, p, j, cfg_gu);
+}
+
+fn gateupCfg(e: *E, r: Routed, tab: [3]Arg, k2g: Arg, p: [5]Arg, j: i64, cfg: i64) !void {
+    try e.ext("tf_dsv41_x3gm_v1.gateup2", &.{
+        r.xg,                                                        r.xu,
+        tab[0],                                                      tab[1],
+        k2g,                                                         p[0],
+        p[1],                                                        p[2],
+        p[3],                                                        p[4],
+        try ticketAt(e, r, j),                                       try e.weight("{s}.w1.svh", .{r.pre}, .f16, &.{ r.Et, r.I }),
+        try e.weight("{s}.w3.svh", .{r.pre}, .f16, &.{ r.Et, r.I }), try e.weight("{s}.w2.suh", .{r.pre}, .f16, &.{ r.Et, r.I }),
+        r.xd,                                                        .{ .i = r.D },
+        .{ .i = r.I },                                               .{ .b = false },
+        .{ .i = cfg },                                               .{ .b = true },
+        .{ .f = r.limit },                                           .{ .b = true },
+    });
+}
+
+/// x3gm.cpp down2(xd, td, k2e, order, pe, poff, pcnt, npass, ticket, svh_d, y, K, N, cfg, use_ticket, tables)
+fn down(e: *E, r: Routed, td: Arg, k2d: Arg, p: [5]Arg, j: i64) !void {
+    try e.ext("tf_dsv41_x3gm_v1.down2", &.{
+        r.xd,                               td,
+        k2d,                                p[0],
+        p[1],                               p[2],
+        p[3],                               p[4],
+        try ticketAt(e, r, max_widths + j), try e.weight("{s}.w2.svh", .{r.pre}, .f16, &.{ r.Et, r.D }),
+        r.y,                                .{ .i = r.I },
+        .{ .i = r.D },                      .{ .i = cfg_dn },
+        .{ .b = true },                     .{ .b = true },
+    });
+}
diff --git a/zig/src/families/deepseek_v41/gm2pf_test.zig b/zig/src/families/deepseek_v41/gm2pf_test.zig
new file mode 100644
index 0000000000..1f93ed5277
--- /dev/null
+++ b/zig/src/families/deepseek_v41/gm2pf_test.zig
@@ -0,0 +1,191 @@
+//! Host tests of x3gm v2 on prefill (`zig build test-dsv41-gm2pf`): knob reader, capture mode, emitter off/one/split.
+const std = @import("std");
+const testing = std.testing;
+const calls = @import("block_calls.zig");
+const block = @import("block.zig");
+const bp = @import("block_prefill.zig");
+const check = @import("calls_check.zig");
+const gm2pf = @import("gm2pf.zig");
+const knob_types = @import("knob_types.zig");
+const Config = @import("config.zig").Config;
+
+const Fake = struct {
+    var pairs: []const [2][]const u8 = &.{};
+    fn get(name: []const u8) ?[]const u8 {
+        for (pairs) |p| if (std.mem.eql(u8, p[0], name)) return p[1];
+        return null;
+    }
+};
+
+test "TF_DSV41_GM_V2: x3gm.config's modes (unset / empty / 0 off, 1 one, split), anything else refused" {
+    const cases = [_]struct { v: ?[]const u8, m: knob_types.GmMode }{
+        .{ .v = null, .m = .off }, .{ .v = "", .m = .off }, .{ .v = "0", .m = .off }, .{ .v = " 1 ", .m = .one }, .{ .v = "split", .m = .split },
+    };
+    for (cases) |c| {
+        Fake.pairs = if (c.v) |v| &.{.{ "TF_DSV41_GM_V2", v }} else &.{};
+        try testing.expectEqual(c.m, try gm2pf.mode(&Fake.get));
+    }
+    for ([_][]const u8{ "2", "on", "one" }) |v| {
+        Fake.pairs = &.{.{ "TF_DSV41_GM_V2", v }};
+        try testing.expectError(error.BadKnob, gm2pf.mode(&Fake.get));
+    }
+    Fake.pairs = &.{};
+}
+
+const Names = struct {
+    list: []const []const u8,
+    i: usize = 0,
+    pub fn next(it: *Names) ?[]const u8 {
+        if (it.i == it.list.len) return null;
+        it.i += 1;
+        return it.list[it.i - 1];
+    }
+};
+
+test "a capture's mode from its launches: v1, one gateup2 a rotation, several" {
+    const rot = "tf_dsv41_x3gm_v1.rot";
+    const g1 = "tf_dsv41_x3gm_v1.gateup";
+    const g2 = "tf_dsv41_x3gm_v1.gateup2";
+    var v1: Names = .{ .list = &.{ rot, g1, g1, "tf_dsv41_x3gm_v1.down", rot, g1 } };
+    try testing.expectEqual(knob_types.GmMode.off, gm2pf.ofLaunches(&v1));
+    var one: Names = .{ .list = &.{ rot, g2, "tf_dsv41_x3gm_v1.down2", rot, g2, "tf_dsv41_x3gm_v1.down2" } };
+    try testing.expectEqual(knob_types.GmMode.one, gm2pf.ofLaunches(&one));
+    var split: Names = .{ .list = &.{ rot, g2, g2, "tf_dsv41_x3gm_v1.down2", "tf_dsv41_x3gm_v1.down2" } };
+    try testing.expectEqual(knob_types.GmMode.split, gm2pf.ofLaunches(&split));
+}
+
+const gu_widths: u32 = (1 << 3) | (1 << 4) | (1 << 6);
+const dn_widths: u32 = (1 << 4) | (1 << 10);
+
+fn widths(a: std.mem.Allocator, gm: bool) !block.Widths {
+    var w: block.Widths = .{};
+    try check.parseDense(a, &w, @embedFile("fixtures/dense-k2.txt"));
+    try check.parseExperts(a, &w, @embedFile("fixtures/expert-k2.txt"));
+    if (gm) for (0..40) |L| try w.gm.put(a, @intCast(L), .{ gu_widths, dn_widths });
+    return w;
+}
+
+/// The routed launches and their plans: what v2 replaces.
+fn isRouted(c: calls.Call) bool {
+    const n = c.name;
+    for ([_][]const u8{ "glue.gm_plan", "tf_dsv41_x3gm_v1.gateup", "tf_dsv41_x3gm_v1.down", "tf_dsv41_x3gm_v1.gateup2", "tf_dsv41_x3gm_v1.down2" }) |r|
+        if (std.mem.eql(u8, n, r)) return true;
+    return false;
+}
+
+fn role(x: calls.Named) []const u8 {
+    return switch (x.arg.t.role) {
+        .buf => |b| b,
+        .weight => |w| w,
+        .empty => "",
+    };
+}
+
+fn count(cs: []const calls.Call, name: []const u8) usize {
+    var n: usize = 0;
+    for (cs) |c| n += @intFromBool(std.mem.eql(u8, c.name, name));
+    return n;
+}
+
+test "TF_DSV41_GM_V2 on the prefill emitter: v2's launches in place of v1's, every other call the same" {
+    var arena = std.heap.ArenaAllocator.init(testing.allocator);
+    defer arena.deinit();
+    const aa = arena.allocator();
+    const cfg: Config = .{};
+    const w = try widths(aa, true);
+    const bare = try widths(aa, false);
+    const layers = [_]u32{ 1, 2, 3 };
+    for ([_]i64{ 2048, 300, 12, 1 }) |n| {
+        const off = try bp.emitPrefill(aa, &cfg, &w, .{}, &layers, n, 0, true);
+        const moes = count(off, "tf_dsv41_x3gm_v1.rot");
+        try testing.expect(moes > 0);
+        try testing.expectEqual(moes * @popCount(gu_widths), count(off, "tf_dsv41_x3gm_v1.gateup"));
+        for ([_]knob_types.GmMode{ .one, .split }) |m| {
+            const on = try bp.emitPrefill(aa, &cfg, &w, .{ .gm_v2 = m }, &layers, n, 0, true);
+            // every call that is not a routed launch or its plan: the same, in the same order
+            var i: usize = 0;
+            var j: usize = 0;
+            while (true) {
+                while (i < off.len and isRouted(off[i])) i += 1;
+                while (j < on.len and isRouted(on[j])) j += 1;
+                if (i == off.len or j == on.len) break;
+                try testing.expectEqualStrings(off[i].name, on[j].name);
+                try testing.expectEqual(off[i].args.len, on[j].args.len);
+                i += 1;
+                j += 1;
+            }
+            try testing.expectEqual(off.len, i);
+            try testing.expectEqual(on.len, j);
+            try testing.expectEqual(@as(usize, 0), count(on, "tf_dsv41_x3gm_v1.gateup") + count(on, "tf_dsv41_x3gm_v1.down"));
+            const per: [2]usize = if (m == .one) .{ 1, 1 } else .{ @popCount(gu_widths), @popCount(dn_widths) };
+            try testing.expectEqual(moes * per[0], count(on, "tf_dsv41_x3gm_v1.gateup2"));
+            try testing.expectEqual(moes * per[1], count(on, "tf_dsv41_x3gm_v1.down2"));
+            try testing.expectEqual(moes * (per[0] + per[1]) - (if (m == .one) moes else 0), count(on, "glue.gm_plan"));
+            try launches(on, m);
+        }
+        // `one` needs no widths (each expert's is in its table); v1 and split do
+        _ = try bp.emitPrefill(aa, &cfg, &bare, .{ .gm_v2 = .one }, &layers, n, 0, true);
+        try testing.expectError(error.NoWidth, bp.emitPrefill(aa, &cfg, &bare, .{}, &layers, n, 0, true));
+        try testing.expectError(error.NoWidth, bp.emitPrefill(aa, &cfg, &bare, .{ .gm_v2 = .split }, &layers, n, 0, true));
+    }
+    // x3gm.supported: a width gm2_kernel does not dispatch (2, 9) refuses the block
+    var odd = try widths(aa, false);
+    for (0..40) |L| try odd.gm.put(aa, @intCast(L), .{ (1 << 2) | (1 << 4), 1 << 4 });
+    try testing.expectError(error.Unsupported, bp.emitPrefill(aa, &cfg, &odd, .{ .gm_v2 = .one }, &layers, 300, 0, true));
+    for (0..40) |L| try odd.gm.put(aa, @intCast(L), .{ 1 << 4, 1 << 9 });
+    try testing.expectError(error.Unsupported, bp.emitPrefill(aa, &cfg, &odd, .{ .gm_v2 = .split }, &layers, 300, 0, true));
+}
+
+/// Each gateup2 / down2 against x3gm.cpp's signatures: width tables, ticket halves, shared plan (one), widths (split).
+fn launches(cs: []const calls.Call, m: knob_types.GmMode) !void {
+    var last_plan: ?[]const calls.Named = null;
+    var gu_plan: [5][]const u8 = undefined;
+    var jg: i64 = 0;
+    var jd: i64 = 0;
+    for (cs) |c| {
+        if (std.mem.eql(u8, c.name, "tf_dsv41_x3gm_v1.rot")) {
+            jg = 0;
+            jd = 0;
+        }
+        if (std.mem.eql(u8, c.name, "glue.gm_plan")) {
+            try testing.expectEqual(@as(usize, 10), c.args.len);
+            const k2 = c.args[6].arg.i;
+            if (m == .one) try testing.expectEqual(@as(i64, 0), k2) else try testing.expect(k2 > 0);
+            try testing.expectEqual(gm2pf.bm, c.args[8].arg.i);
+            last_plan = c.args;
+        }
+        const gu = std.mem.eql(u8, c.name, "tf_dsv41_x3gm_v1.gateup2");
+        const dn = std.mem.eql(u8, c.name, "tf_dsv41_x3gm_v1.down2");
+        if (!gu and !dn) continue;
+        const p = last_plan.?;
+        const a = c.args;
+        try testing.expectEqual(@as(usize, if (gu) 22 else 16), a.len);
+        const k2e = a[if (gu) 4 else 2].arg.t;
+        try testing.expectEqualStrings(if (gu) "k2g" else "k2d", role(a[if (gu) 4 else 2])[role(a[if (gu) 4 else 2]).len - 3 ..]);
+        try testing.expectEqual(calls.Dt.i32, k2e.dt);
+        try testing.expectEqual(a[if (gu) 2 else 1].arg.t.shape[0] + 1, k2e.shape[0]);
+        const first: usize = if (gu) 5 else 3;
+        for (0..5) |q| try testing.expectEqualStrings(role(p[1 + q]), role(a[first + q]));
+        if (gu) {
+            for (0..5) |q| gu_plan[q] = role(a[first + q]);
+            try testing.expectEqual(gm2pf.cfg_gu, a[18].arg.i);
+            try testing.expect(!a[17].arg.b and a[19].arg.b and a[21].arg.b);
+        } else {
+            try testing.expectEqual(gm2pf.cfg_dn, a[13].arg.i);
+            try testing.expect(a[14].arg.b and a[15].arg.b);
+            if (m == .one) for (0..5) |q| try testing.expectEqualStrings(gu_plan[q], role(a[first + q]));
+        }
+        // the ticket: gate/up from 0, down from MAX_WIDTHS, one a launch
+        const t = a[if (gu) 10 else 8].arg.t;
+        try testing.expectEqualStrings("L.gm.ticket", role(a[if (gu) 10 else 8]));
+        if (gu) {
+            try testing.expectEqual(jg * 4, t.offset);
+            jg += 1;
+            if (m == .split) try testing.expect(!p[7].arg.b);
+        } else {
+            try testing.expectEqual((gm2pf.max_widths + jd) * 4, t.offset);
+            jd += 1;
+            if (m == .split) try testing.expect(p[7].arg.b);
+        }
+    }
+}
diff --git a/zig/src/families/deepseek_v41/graphs.zig b/zig/src/families/deepseek_v41/graphs.zig
new file mode 100644
index 0000000000..c0c88a911a
--- /dev/null
+++ b/zig/src/families/deepseek_v41/graphs.zig
@@ -0,0 +1,390 @@
+//! CUDA graphs for decode windows over cuda/graph_cache.zig, keyed by rows and context bucket; bits equal eager.
+
+const std = @import("std");
+const cuda = @import("cuda");
+const tp = @import("tp");
+const calls = @import("block_calls.zig");
+
+pub const gc = cuda.graph_cache;
+
+/// the graphed row counts (a window's rows are exact; this caps them)
+pub const row_buckets = [_]u32{ 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 24, 32, 48, 64 };
+/// context buckets 0-15 are `size` tokens each; past them each ends `grow` x further
+pub const linear = 16;
+
+pub const Settings = struct {
+    on: bool = false,
+    max: u32 = 48,
+    bucket: u32 = 2048,
+    grow: f64 = 1.25,
+    rows_max: u32 = 64,
+    room: Room = .{},
+    /// TF_DSV41_GRAPH_FLOOR=hold (default `shed`): under the floor a miss runs eagerly and held graphs stay
+    floor_hold: bool = false,
+
+    pub fn fromEnv() !Settings {
+        return fromEnvOr(false);
+    }
+
+    /// The knobs; graphs `on_default` when TF_DSV41_GRAPHS is unset (every rank must agree).
+    pub fn fromEnvOr(on_default: bool) !Settings {
+        var s: Settings = .{ .on = on_default };
+        if (std.c.getenv("TF_DSV41_GRAPHS")) |v| s.on = !std.mem.eql(u8, std.mem.span(v), "0");
+        if (std.c.getenv("TF_DSV41_GRAPHS_MAX")) |v| s.max = try std.fmt.parseInt(u32, std.mem.span(v), 10);
+        if (std.c.getenv("TF_DSV41_GRAPH_BUCKET")) |v| s.bucket = try std.fmt.parseInt(u32, std.mem.span(v), 10);
+        if (std.c.getenv("TF_DSV41_GRAPH_BUCKET_GROW")) |v| s.grow = try std.fmt.parseFloat(f64, std.mem.span(v));
+        if (std.c.getenv("TF_DSV41_GRAPH_ROWS_MAX")) |v| s.rows_max = try std.fmt.parseInt(u32, std.mem.span(v), 10);
+        s.room = try Room.fromEnv();
+        if (std.c.getenv("TF_DSV41_GRAPH_FLOOR")) |v| {
+            const m = std.mem.span(v);
+            if (std.mem.eql(u8, m, "hold")) s.floor_hold = true else if (!(m.len == 0 or std.mem.eql(u8, m, "shed"))) return error.BadGraphFloor;
+        }
+        if (s.bucket == 0) return error.BadGraphBucket;
+        return s;
+    }
+};
+
+/// The end (exclusive) of bucket `b`: `size` steps, then x `grow` (truncated) rounded up to `size`, at least one step.
+pub fn endOf(b: u32, size: u64, grow: f64) u64 {
+    var e: u64 = size * @min(b + 1, linear);
+    var i: u32 = linear;
+    while (i <= b) : (i += 1) {
+        const nxt: u64 = if (grow > 1) blk: {
+            const prod: u64 = @intFromFloat(@trunc(@as(f64, @floatFromInt(e)) * grow));
+            break :blk ((prod + size - 1) / size) * size;
+        } else e + size;
+        e = @max(nxt, e + size);
+    }
+    return e;
+}
+
+/// The context bucket of a window whose last position is `last` (the first bucket ending past it).
+pub fn bucketOf(last: u64, size: u64, grow: f64) u32 {
+    var b: u32 = 0;
+    while (endOf(b, size, grow) <= last) b += 1;
+    return b;
+}
+
+/// The last position bucket `b`'s graphs score keys for.
+pub fn bucketEnd(b: u32, size: u64, limit: u64, grow: f64) u64 {
+    return @min(endOf(b, size, grow) - 1, limit - 1);
+}
+
+/// `part`: 0 a whole window; 1 / 2 its head / tail graph (TF_DSV41_ROUND_GRAPH=split, round_graph.zig)
+pub const Key = struct { rows: u32, ctx: u32, part: u8 = 0 };
+
+pub const Cache = gc.Cache(Key);
+
+/// The window's start for emitting a graphed program: its end lands at the bucket's end (the indexer's NK).
+pub fn emitStart(n: u32, b: u32, s: Settings, limit: u64) i64 {
+    return @as(i64, @intCast(bucketEnd(b, s.bucket, limit, s.grow))) + 1 - n;
+}
+
+/// Why a window's program cannot be captured (null: it can): a glue step that reads the device on the host.
+pub fn audit(cs: []const calls.Call) ?[]const u8 {
+    for (cs) |c| if (c.glue and std.mem.eql(u8, c.name, "glue.trace")) return "glue.trace downloads the streams";
+    return null;
+}
+
+/// One more graph fits when its price leaves the hard floor and a 2K segment's floor (a fixed 5 GiB kept ~1 GiB).
+pub const Room = struct {
+    /// TF_DSV41_GRAPH_FLOOR_GIB set: MemAvailable at least this instead
+    floor_gib: ?f64 = null,
+    /// TF_DSV41_GRAPH_COST_MIB: a capture's price
+    cost_gib: f64 = 64.0 / 1024.0,
+    /// max(TF_DSV41_FLOOR_HARD_GIB, TF_DSV41_PF_WS_FLOOR_GIB + a 2K segment's host transient)
+    keep_gib: f64 = 4.0,
+
+    /// a 2K segment's own run-time memory: its buffers are the boot plan's, so only Engram's bulk-read batches
+    pub const prefill_2k_gib: f64 = 64.0 / 1024.0;
+
+    pub fn fromEnv() !Room {
+        var r: Room = .{};
+        if (std.c.getenv("TF_DSV41_GRAPH_FLOOR_GIB")) |v| r.floor_gib = try std.fmt.parseFloat(f64, std.mem.span(v));
+        if (std.c.getenv("TF_DSV41_GRAPH_COST_MIB")) |v| r.cost_gib = try std.fmt.parseFloat(f64, std.mem.span(v)) / 1024.0;
+        const hard = gib("TF_DSV41_FLOOR_HARD_GIB", 4.0);
+        r.keep_gib = @max(hard, gib("TF_DSV41_PF_WS_FLOOR_GIB", hard) + prefill_2k_gib);
+        return r;
+    }
+
+    pub fn fits(r: Room, avail_gib: f64) bool {
+        if (r.floor_gib) |f| return avail_gib >= f;
+        return avail_gib - r.cost_gib >= r.keep_gib;
+    }
+};
+
+fn gib(name: [*:0]const u8, default: f64) f64 {
+    const v = std.c.getenv(name) orelse return default;
+    return std.fmt.parseFloat(f64, std.mem.span(v)) catch default;
+}
+
+/// The rule every graph cache asks (`room`): set once from the environment by the first cache made.
+pub var rule: ?Room = null;
+
+/// This rank's room for one more graph (true where /proc/meminfo cannot be read).
+pub fn room() bool {
+    const r = rule orelse blk: {
+        rule = Room.fromEnv() catch Room{};
+        break :blk rule.?;
+    };
+    return r.fits(memAvailableGiB() orelse return true);
+}
+
+/// MemAvailable now, in GiB, for a capture's measured cost (null: unknown).
+pub fn meter() ?f64 {
+    return memAvailableGiB();
+}
+
+/// MemAvailable in GiB (null where /proc/meminfo cannot be read): the prefill's handoff log.
+pub fn memAvailableGiB() ?f64 {
+    var buf: [4096]u8 = undefined;
+    const fd = std.c.open("/proc/meminfo", .{ .ACCMODE = .RDONLY });
+    if (fd < 0) return null;
+    defer _ = std.c.close(fd);
+    const n = std.c.read(fd, &buf, buf.len);
+    if (n <= 0) return null;
+    return memAvailableOf(buf[0..@intCast(n)]);
+}
+
+fn memAvailableOf(text: []const u8) ?f64 {
+    var it = std.mem.splitScalar(u8, text, '\n');
+    while (it.next()) |line| {
+        if (!std.mem.startsWith(u8, line, "MemAvailable:")) continue;
+        var f = std.mem.tokenizeScalar(u8, line["MemAvailable:".len..], ' ');
+        const kb = std.fmt.parseInt(u64, f.next() orelse return null, 10) catch return null;
+        return @as(f64, @floatFromInt(kb)) / (1 << 20);
+    }
+    return null;
+}
+
+/// Every rank's verdict over the Collective (one all-gathered i32 a rank, outside any capture): true when all agree.
+pub const Agreement = struct {
+    comm: tp.collective.Collective,
+    stream: cuda.Stream,
+    dev: cuda.DeviceBuffer,
+    host: cuda.HostBuffer,
+
+    pub fn init(d: *const cuda.Driver, comm: tp.collective.Collective, stream: cuda.Stream) !Agreement {
+        const w = comm.world();
+        const dev = try cuda.DeviceBuffer.alloc(d, 4 * (w + 1));
+        errdefer {
+            var b = dev;
+            b.free();
+        }
+        return .{ .comm = comm, .stream = stream, .dev = dev, .host = try cuda.HostBuffer.alloc(d, 4 * (w + 1)) };
+    }
+
+    pub fn deinit(a: *Agreement) void {
+        a.dev.free();
+        a.host.free();
+    }
+
+    pub fn agree(a: *Agreement) gc.Agree {
+        return .{ .ctx = a, .all = all };
+    }
+
+    fn all(ctx: *anyopaque, ok: bool) anyerror!bool {
+        const a: *Agreement = @ptrCast(@alignCast(ctx));
+        const w = a.comm.world();
+        const h = a.host.slice(i32);
+        h[0] = @intFromBool(ok);
+        try a.dev.uploadAsync(0, std.mem.sliceAsBytes(h[0..1]), a.stream.handle);
+        try a.comm.allGather(a.dev.ptr, a.dev.ptr + 4, 1, .i32, a.stream.handle);
+        try a.dev.downloadAsync(4, std.mem.sliceAsBytes(h[1 .. 1 + w]), a.stream.handle);
+        try a.stream.synchronize();
+        for (h[1 .. 1 + w]) |v| if (v != 1) return false;
+        return true;
+    }
+};
+
+/// A graphed window's inputs (ids int64 [rows_max], start int32), copied from two alternating pinned staging halves.
+pub const Statics = struct {
+    pub const ids_off = 0;
+
+    rows_max: u32,
+    dev: cuda.DeviceBuffer,
+    host: [2]cuda.HostBuffer,
+    copied: [2]cuda.Event,
+    half: u1 = 0,
+
+    pub fn bytes(rows_max: u32) usize {
+        return 8 * @as(usize, rows_max) + 256;
+    }
+
+    pub fn startOff(rows_max: u32) usize {
+        return 8 * @as(usize, rows_max);
+    }
+
+    pub fn init(d: *const cuda.Driver, rows_max: u32) !Statics {
+        const n = bytes(rows_max);
+        var s: Statics = .{ .rows_max = rows_max, .dev = try cuda.DeviceBuffer.alloc(d, n), .host = undefined, .copied = undefined };
+        try s.dev.fill8(0, null);
+        for (&s.host, &s.copied) |*h, *e| {
+            h.* = try cuda.HostBuffer.alloc(d, n);
+            e.* = try cuda.Event.init(d, false);
+            try e.record(.{ .d = d, .handle = null }); // a first wait returns at once
+        }
+        return s;
+    }
+
+    pub fn deinit(s: *Statics) void {
+        s.dev.free();
+        for (&s.host, &s.copied) |*h, *e| {
+            h.free();
+            e.deinit();
+        }
+    }
+
+    pub fn idsDev(s: *const Statics) u64 {
+        return s.dev.ptr + ids_off;
+    }
+
+    pub fn startDev(s: *const Statics) u64 {
+        return s.dev.ptr + startOff(s.rows_max);
+    }
+
+    /// The window's ids and first position, copied on `stream` ahead of its graph.
+    pub fn stage(s: *Statics, stream: cuda.Stream, ids: []const u32, start: u64) !void {
+        if (ids.len > s.rows_max) return error.TooManyRows;
+        const h = s.half;
+        try s.copied[h].synchronize();
+        const b = s.host[h].bytes;
+        for (ids, 0..) |id, i| std.mem.writeInt(i64, b[8 * i ..][0..8], id, .little);
+        std.mem.writeInt(i32, b[startOff(s.rows_max)..][0..4], @intCast(start), .little);
+        try s.dev.uploadAsync(0, b[0 .. 8 * ids.len], stream.handle);
+        try s.dev.uploadAsync(startOff(s.rows_max), b[startOff(s.rows_max)..][0..4], stream.handle);
+        try s.copied[h].record(stream);
+        s.half = h +% 1;
+    }
+};
+
+/// One Engram layer's staged rows: this rank's bf16 [rows_max, cols], copied before the window like Statics'.
+pub const EngramStage = struct {
+    layer: u32,
+    cols: usize,
+    dev: cuda.DeviceBuffer,
+    host: [2]cuda.HostBuffer,
+    copied: [2]cuda.Event,
+    half: u1 = 0,
+
+    pub fn init(d: *const cuda.Driver, layer: u32, rows_max: u32, cols: usize) !EngramStage {
+        const n = 2 * @as(usize, rows_max) * cols;
+        var e: EngramStage = .{ .layer = layer, .cols = cols, .dev = try cuda.DeviceBuffer.alloc(d, n), .host = undefined, .copied = undefined };
+        for (&e.host, &e.copied) |*h, *ev| {
+            h.* = try cuda.HostBuffer.alloc(d, n);
+            ev.* = try cuda.Event.init(d, false);
+            try ev.record(.{ .d = d, .handle = null });
+        }
+        return e;
+    }
+
+    pub fn deinit(e: *EngramStage) void {
+        e.dev.free();
+        for (&e.host, &e.copied) |*h, *ev| {
+            h.free();
+            ev.deinit();
+        }
+    }
+
+    /// The pinned half to fill with `n` rows (its previous copy has finished).
+    pub fn begin(e: *EngramStage, n: usize) ![]u16 {
+        try e.copied[e.half].synchronize();
+        return @alignCast(std.mem.bytesAsSlice(u16, e.host[e.half].bytes[0 .. 2 * n * e.cols]));
+    }
+
+    /// Copies the filled half's `n` rows on `stream`.
+    pub fn commit(e: *EngramStage, stream: cuda.Stream, n: usize) !void {
+        const h = e.half;
+        try e.dev.uploadAsync(0, e.host[h].bytes[0 .. 2 * n * e.cols], stream.handle);
+        try e.copied[h].record(stream);
+        e.half = h +% 1;
+    }
+};
+
+// -- host tests ---------------------------------------------------------------------------------------------------
+
+test "graphs: context buckets as Python's (2,048 steps to 32K, then 1.25x rounded up to 2,048)" {
+    // _ends(2048, 1.25, ...) = 2048, 4096, ..., 32768, 40960, 51200, 65536, 81920, 102400, 129024, ...
+    const want = [_]u64{ 2048, 4096, 6144, 8192, 10240, 12288, 14336, 16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768, 40960, 51200, 65536, 81920, 102400, 129024 };
+    for (want, 0..) |e, b| try std.testing.expectEqual(e, endOf(@intCast(b), 2048, 1.25));
+    try std.testing.expectEqual(@as(u32, 0), bucketOf(0, 2048, 1.25));
+    try std.testing.expectEqual(@as(u32, 0), bucketOf(2047, 2048, 1.25));
+    try std.testing.expectEqual(@as(u32, 1), bucketOf(2048, 2048, 1.25));
+    try std.testing.expectEqual(@as(u32, 16), bucketOf(32768, 2048, 1.25));
+    try std.testing.expectEqual(@as(u32, 17), bucketOf(40960, 2048, 1.25));
+    try std.testing.expectEqual(@as(u64, 2047), bucketEnd(0, 2048, 1 << 20, 1.25));
+    try std.testing.expectEqual(@as(u64, 3999), bucketEnd(1, 2048, 4000, 1.25)); // capped at limit - 1
+    // grow 1: equal steps throughout
+    try std.testing.expectEqual(@as(u64, 2048 * 20), endOf(19, 2048, 1.0));
+}
+
+test "graphs: emitted start puts the window's end at the bucket's end" {
+    const s: Settings = .{};
+    // a 5-row window in bucket 0: rows end at 2047, so end = start + n = 2048, NK = 2048 / ratio
+    try std.testing.expectEqual(@as(i64, 2043), emitStart(5, 0, s, 1 << 20));
+}
+
+test "graphs: MemAvailable in GiB" {
+    try std.testing.expectEqual(@as(?f64, 6.0), memAvailableOf("MemTotal: 1 kB\nMemAvailable:   6291456 kB\n"));
+    try std.testing.expectEqual(@as(?f64, null), memAvailableOf("MemTotal: 1 kB\n"));
+}
+
+test "graphs: one more graph fits when its price leaves the hard floor and a 2K segment's floor and transient" {
+    // served: TF_DSV41_FLOOR_HARD_GIB 4, TF_DSV41_PF_WS_FLOOR_GIB 3.5 -> keep max(4, 3.5 + 1/16) = 4
+    const served: Room = .{ .keep_gib = @max(4.0, 3.5 + Room.prefill_2k_gib) };
+    try std.testing.expect(served.fits(4.66)); // rank 1 after traffic
+    try std.testing.expect(!served.fits(4.03));
+    // a prefill floor above the hard floor raises what graphs must leave
+    const high: Room = .{ .keep_gib = @max(4.0, 4.5 + Room.prefill_2k_gib) };
+    try std.testing.expect(high.fits(4.66) and !high.fits(4.6));
+    // TF_DSV41_GRAPH_FLOOR_GIB: the fixed floor of before
+    const fixed: Room = .{ .floor_gib = 5.0 };
+    try std.testing.expect(!fixed.fits(4.66) and fixed.fits(5.1));
+}
+
+/// A test rank's MemAvailable (GiB): what `testRoom` and `testMeter` read; a capture takes `cost` of it.
+var test_avail: f64 = 0;
+
+fn testRoom() bool {
+    const r: Room = .{ .keep_gib = @max(4.0, 3.5 + Room.prefill_2k_gib) };
+    return r.fits(test_avail);
+}
+
+fn testMeter() ?f64 {
+    return test_avail;
+}
+
+const TestEngine = struct {
+    slot: u8 = 0,
+    fn engine(e: *TestEngine) gc.Engine {
+        return .{ .ctx = e, .capture = capture, .launch = launch, .free = free };
+    }
+    fn capture(ctx: *anyopaque, _: cuda.Stream, _: gc.Body) anyerror!*anyopaque {
+        test_avail -= 15.0 / 1024.0; // a graph's driver memory (~15 MiB)
+        return &@as(*TestEngine, @ptrCast(@alignCast(ctx))).slot;
+    }
+    fn launch(_: *anyopaque, _: *anyopaque, _: cuda.Stream) anyerror!void {}
+    fn free(_: *anyopaque, _: *anyopaque) void {}
+};
+
+fn noBody(_: *anyopaque, _: cuda.Stream) anyerror!void {}
+
+test "graphs: rank 1 at 4.66 GiB captures, at 4.03 defers (eager, held graphs replay), and captures again once back" {
+    var e: TestEngine = .{};
+    var c = gc.Cache(u32).init(std.testing.allocator, e.engine(), .{ .max = 48, .hold = true });
+    defer c.deinit();
+    c.room = testRoom;
+    c.meter = testMeter;
+    const body: gc.Body = .{ .ctx = &e, .run = noBody };
+    const stream: cuda.Stream = .{ .d = undefined, .handle = null };
+    test_avail = 4.66;
+    try std.testing.expectEqual(gc.Outcome.captured, try c.run(1, stream, 7, body));
+    try std.testing.expectApproxEqAbs(@as(f64, 15), c.stats.capture_mib, 0.01);
+    test_avail = 4.03;
+    try std.testing.expectEqual(gc.Outcome.eager, try c.run(2, stream, 7, body));
+    try std.testing.expectEqual(gc.Outcome.replayed, try c.run(1, stream, 7, body));
+    try std.testing.expectEqual(@as(u64, 1), c.stats.eager);
+    test_avail = 4.66;
+    try std.testing.expectEqual(gc.Outcome.captured, try c.run(2, stream, 7, body));
+    try std.testing.expectEqual(@as(usize, 2), c.count());
+}
diff --git a/zig/src/families/deepseek_v41/greedy.zig b/zig/src/families/deepseek_v41/greedy.zig
new file mode 100644
index 0000000000..15a1115ad8
--- /dev/null
+++ b/zig/src/families/deepseek_v41/greedy.zig
@@ -0,0 +1,69 @@
+//! Greedy's host step: a row's largest logit by `x > best`, the lowest column on ties (vectorized, same pick).
+
+const std = @import("std");
+
+const lanes = 16;
+const V = @Vector(lanes, f32);
+
+/// The column the scalar loop `best = 0; for i: if (x[i] > x[best]) best = i` returns.
+pub fn argmax(vals: []const f32) usize {
+    if (vals.len == 0 or std.math.isNan(vals[0])) return 0;
+    var acc: V = @splat(-std.math.inf(f32));
+    var i: usize = 0;
+    while (i + lanes <= vals.len) : (i += lanes) {
+        const x: V = vals[i..][0..lanes].*;
+        acc = @select(f32, x > acc, x, acc);
+    }
+    var m: f32 = -std.math.inf(f32);
+    const lane: [lanes]f32 = acc;
+    for (lane) |x| m = if (x > m) x else m;
+    for (vals[i..]) |x| m = if (x > m) x else m;
+    // vals[0] is not NaN, so the maximum is a value of the row (-inf at least): its first column
+    const want: V = @splat(m);
+    i = 0;
+    while (i + lanes <= vals.len) : (i += lanes) {
+        const x: V = vals[i..][0..lanes].*;
+        if (@reduce(.Or, x == want)) for (vals[i..][0..lanes], i..) |y, k| if (y == m) return k;
+    }
+    for (vals[i..], i..) |x, k| if (x == m) return k;
+    unreachable;
+}
+
+fn scalar(vals: []const f32) usize {
+    var best: usize = 0;
+    for (vals, 0..) |x, i| if (x > vals[best]) {
+        best = i;
+    };
+    return best;
+}
+
+test "greedy argmax: the scalar loop's column on ties, signed zeros, infinities and NaN" {
+    const nan = std.math.nan(f32);
+    const inf = std.math.inf(f32);
+    const cases = [_][]const f32{
+        &.{ 1, 3, 3, 2 },
+        &.{ -0.0, 0.0, -0.0 },
+        &.{ 0.0, -0.0 },
+        &.{ -inf, -inf, -inf },
+        &.{ nan, 5, 7 },
+        &.{ 2, nan, 7, nan },
+        &.{ -inf, nan, nan },
+        &.{ inf, inf },
+    };
+    for (cases) |c| try std.testing.expectEqual(scalar(c), argmax(c));
+    // random rows of a rank's vocabulary width with planted ties, zeros and NaNs, past the vector width's tail
+    var prng = std.Random.DefaultPrng.init(4101);
+    const r = prng.random();
+    const row = try std.testing.allocator.alloc(f32, 64_647);
+    defer std.testing.allocator.free(row);
+    for (0..200) |t| {
+        for (row) |*x| x.* = @floatFromInt(r.intRangeAtMost(i32, -400, 400));
+        for (0..(t % 7)) |_| row[r.uintLessThan(usize, row.len)] = nan;
+        if (t % 5 == 0) row[r.uintLessThan(usize, row.len)] = 401;
+        if (t % 5 == 0) row[r.uintLessThan(usize, row.len)] = 401;
+        if (t % 11 == 0) @memset(row, if (t % 2 == 0) 0.0 else -0.0);
+        if (t % 13 == 0) row[0] = nan;
+        try std.testing.expectEqual(scalar(row), argmax(row));
+        try std.testing.expectEqual(scalar(row[0 .. 1 + t]), argmax(row[0 .. 1 + t]));
+    }
+}
diff --git a/zig/src/families/deepseek_v41/host_stage.zig b/zig/src/families/deepseek_v41/host_stage.zig
new file mode 100644
index 0000000000..baf0bdac81
--- /dev/null
+++ b/zig/src/families/deepseek_v41/host_stage.zig
@@ -0,0 +1,41 @@
+//! Pinned host staging a prompt segment rewrites without draining the stream (waits only on its last copy's event).
+
+const std = @import("std");
+const cuda = @import("cuda");
+
+/// forward.CHUNK: windows of more rows stage through pinned memory without a stream sync
+pub const chunk: usize = 128;
+
+pub const Stage = struct {
+    buf: ?cuda.HostBuffer = null,
+    ev: ?cuda.Event = null,
+    /// a copy out of `buf` was issued and `ev` recorded after it
+    pending: bool = false,
+
+    /// `bytes` of the buffer to write: the copy that last read it has finished; grown when too small.
+    pub fn take(s: *Stage, d: *const cuda.Driver, bytes: usize) ![]u8 {
+        if (s.pending) {
+            try s.ev.?.synchronize();
+            s.pending = false;
+        }
+        if (s.buf == null or s.buf.?.bytes.len < bytes) {
+            if (s.buf) |*b| b.free();
+            s.buf = null;
+            s.buf = try cuda.HostBuffer.alloc(d, @max(bytes, 4096));
+        }
+        return s.buf.?.bytes[0..bytes];
+    }
+
+    /// The copy out of the buffer is on `stream`: the next `take` waits for it alone.
+    pub fn issued(s: *Stage, d: *const cuda.Driver, stream: cuda.Stream) !void {
+        if (s.ev == null) s.ev = try cuda.Event.init(d, false);
+        try s.ev.?.record(stream);
+        s.pending = true;
+    }
+
+    pub fn deinit(s: *Stage) void {
+        if (s.ev) |*e| e.deinit();
+        if (s.buf) |*b| b.free();
+        s.* = .{};
+    }
+};
diff --git a/zig/src/families/deepseek_v41/host_test.zig b/zig/src/families/deepseek_v41/host_test.zig
new file mode 100644
index 0000000000..1212ed3518
--- /dev/null
+++ b/zig/src/families/deepseek_v41/host_test.zig
@@ -0,0 +1,330 @@
+//! Host tests of the loader side: release config, synthetic pack, 2.8 bpw widths, header dumps (TF_DSV41_HEADERS).
+
+const std = @import("std");
+const testing = std.testing;
+const config = @import("config.zig");
+const Config = config.Config;
+const exl3 = @import("exl3.zig");
+const Pack = @import("pack.zig").Pack;
+const plan = @import("plan.zig");
+const named = @import("named.zig");
+
+test "the release config.json: topology, routing and rope" {
+    const c = try Config.parse(testing.allocator, @embedFile("fixtures/config.json"));
+    try testing.expectEqual(@as(u32, 129280), c.vocab);
+    try testing.expectEqual(@as(u32, 43), c.allLayers());
+    try testing.expectEqual(@as(f32, 1e-20), c.eps);
+    try testing.expect(c.rope.yarn and c.rope.factor == 16 and c.rope.original_max_positions == 65536);
+    try testing.expectEqual(@as(f64, 160000), c.rope.compress_theta);
+    try testing.expectEqual(config.Mode.swa, c.mode(1));
+    try testing.expectEqual(config.Mode.full, c.mode(2));
+    try testing.expectEqual(config.Mode.reuse, c.mode(3));
+    try testing.expectEqual(config.Mode.full, c.mode(20));
+    try testing.expectEqual(config.Mode.reindex, c.mode(24));
+    try testing.expectEqual(config.Mode.swa, c.mode(41));
+    try testing.expectEqual(@as(?u32, 14), c.kvSource(19));
+    try testing.expectEqual(@as(?u32, 20), c.kvSource(39));
+    try testing.expectEqual(@as(?u32, 36), c.indexSource(39));
+    try testing.expect(c.isCandidateSource(20) and c.usesCandidates(24) and !c.usesCandidates(20));
+    try testing.expectEqual(@as(u32, 128), c.expertsOf(40).count);
+    try testing.expectEqual(@as(u32, 3), c.expertsOf(40).topk);
+    try testing.expectEqual(@as(u32, 24), c.hashCols());
+    try testing.expect(c.isEngram(14) and !c.isEngram(2));
+    try testing.expectEqual(@as(u32, 1), c.eos);
+    // the release's values equal the Config defaults
+    const d: Config = .{};
+    try testing.expectEqualSlices(u32, d.compress_ratios.items(), c.compress_ratios.items());
+    try testing.expectEqualSlices(u64, d.engram_rows.items(), c.engram_rows.items());
+    try testing.expectError(error.UnsupportedScoring, Config.parse(testing.allocator, "{\"scoring_func\": \"sigmoid\"}"));
+    try testing.expectError(error.BadConfig, Config.parse(testing.allocator, "{\"compress_ratios\": [0, 3]}"));
+    try config.tiny().validate();
+}
+
+/// Header JSON of a pack for `c`, named as the release packs are (their index, pattern by pattern), header only.
+fn tinyPack(a: std.mem.Allocator, c: *const Config, extra: []const u8, skip: []const u8) ![]u8 {
+    var n: u64 = 0;
+    return tinyPackLen(a, c, extra, skip, &n);
+}
+
+/// `tinyPack`, and the data bytes its header describes.
+fn tinyPackLen(a: std.mem.Allocator, c: *const Config, extra: []const u8, skip: []const u8, data_len: *u64) ![]u8 {
+    var w: std.Io.Writer.Allocating = .init(a);
+    errdefer w.deinit();
+    var at: u64 = 0;
+    var first = true;
+    const T = struct {
+        fn put(wr: *std.Io.Writer, pos: *u64, f: *bool, sk: []const u8, name: []const u8, dtype: []const u8, shape: []const u64) !void {
+            if (sk.len > 0 and std.mem.eql(u8, name, sk)) return;
+            var n: u64 = if (std.mem.eql(u8, dtype, "F32") or std.mem.eql(u8, dtype, "I32")) 4 else 2;
+            for (shape) |d| n *= d;
+            try wr.print("{s}\"{s}\":{{\"dtype\":\"{s}\",\"shape\":[", .{ if (f.*) "" else ",", name, dtype });
+            for (shape, 0..) |d, i| try wr.print("{s}{d}", .{ if (i == 0) "" else ",", d });
+            try wr.print("],\"data_offsets\":[{d},{d}]}}", .{ pos.*, pos.* + n });
+            pos.* += n;
+            f.* = false;
+        }
+        fn group(wr: *std.Io.Writer, pos: *u64, f: *bool, sk: []const u8, p: []const u8, k: u64, n: u64, words: u64) !void {
+            var b: [160]u8 = undefined;
+            try put(wr, pos, f, sk, try std.fmt.bufPrint(&b, "{s}.trellis", .{p}), "I16", &.{ k / 16, n / 16, words });
+            try put(wr, pos, f, sk, try std.fmt.bufPrint(&b, "{s}.suh", .{p}), "F16", &.{k});
+            try put(wr, pos, f, sk, try std.fmt.bufPrint(&b, "{s}.svh", .{p}), "F16", &.{n});
+            try put(wr, pos, f, sk, try std.fmt.bufPrint(&b, "{s}.mul1", .{p}), "I32", &.{});
+        }
+    };
+    const wr = &w.writer;
+    try wr.writeAll("{");
+    const D: u64 = c.hidden;
+    var nb: [160]u8 = undefined;
+    var l: u32 = 0;
+    while (l < c.allLayers()) : (l += 1) {
+        var pb: [32]u8 = undefined;
+        const p = if (l < c.layers) try std.fmt.bufPrint(&pb, "layers.{d}", .{l}) else try std.fmt.bufPrint(&pb, "mtp.{d}", .{l - c.layers});
+        const E: u64 = if (l < c.layers) c.experts else c.dspark_experts;
+        inline for (.{ "attn_norm.weight", "ffn_norm.weight" }) |s| try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}." ++ s, .{p}), "BF16", &.{D});
+        inline for (.{ "attn", "ffn" }) |s| {
+            try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.hc_" ++ s ++ "_fn", .{p}), "F32", &.{ 24, 4 * D });
+            try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.hc_" ++ s ++ "_base", .{p}), "F32", &.{24});
+            try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.hc_" ++ s ++ "_scale", .{p}), "F32", &.{3});
+        }
+        try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.attn.q_norm.weight", .{p}), "BF16", &.{c.q_lora});
+        try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.attn.kv_norm.weight", .{p}), "BF16", &.{c.head_dim});
+        try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.attn.attn_sink", .{p}), "F32", &.{c.heads});
+        try T.group(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.attn.wq_a", .{p}), D, c.q_lora, 64);
+        try T.group(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.attn.wkv", .{p}), D, c.head_dim, 80);
+        try T.group(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.attn.wq_b", .{p}), c.q_lora, c.heads * c.head_dim, 64);
+        var g: u32 = 0;
+        while (g < c.o_groups) : (g += 1) try T.group(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.attn.wo_a.slice.{d}", .{ p, g }), c.heads / c.o_groups * c.head_dim, c.o_lora, 64);
+        try T.group(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.attn.wo_b", .{p}), c.o_groups * c.o_lora, D, 64);
+        if (l < c.layers and c.kv_sources.contains(l)) {
+            try T.group(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.attn.compressor.wkv", .{p}), D, c.head_dim, 80);
+            try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.attn.compressor.wkv.weight", .{p}), "BF16", &.{ c.head_dim, D });
+            if (c.compressRatio(l) == 2) {
+                try T.group(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.attn.compressor.wgate", .{p}), D, c.head_dim, 80);
+                try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.attn.compressor.wgate.weight", .{p}), "BF16", &.{ c.head_dim, D });
+            }
+            try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.attn.compressor.norm.weight", .{p}), "BF16", &.{c.head_dim});
+            try T.group(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.attn.indexer.wk", .{p}), c.head_dim, c.index_dim, 128);
+            try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.attn.indexer.wk.weight", .{p}), "BF16", &.{ c.index_dim, c.head_dim });
+            try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.attn.indexer.k_norm.weight", .{p}), "BF16", &.{c.index_dim});
+        }
+        if (l < c.layers and c.index_sources.contains(l)) {
+            try T.group(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.attn.indexer.wq_b", .{p}), c.q_lora, c.index_heads * c.index_dim, 80);
+            try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.attn.indexer.weights_proj.weight", .{p}), "BF16", &.{ c.index_heads, D });
+        }
+        if (c.engram_layers.contains(l)) {
+            try T.group(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.engram.wkv", .{p}), c.hashCols() * c.engram_head_dim, (c.engram_max_ngram + 1) * D, 64);
+            try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.engram.q_weight", .{p}), "BF16", &.{ c.hc_mult, D });
+            try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.engram.k_weight", .{p}), "BF16", &.{ c.hc_mult, D });
+        }
+        try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.ffn.gate.weight", .{p}), "BF16", &.{ E, D });
+        try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.ffn.gate.bias", .{p}), "F32", &.{E});
+        try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.ffn.gate.bias_vl", .{p}), "F32", &.{E});
+        inline for (.{ "w1", "w2", "w3" }) |m| {
+            const kn: [2]u64 = if (comptime std.mem.eql(u8, m, "w2")) .{ c.expert_width, D } else .{ D, c.expert_width };
+            try T.group(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.ffn.shared_experts." ++ m, .{p}), kn[0], kn[1], 80);
+            var e: u64 = 0;
+            while (e < E) : (e += 1) {
+                // block 2: expert 1's down projection at 3 bits, expert 3's gate at 3 bits (a gate/up width mismatch)
+                const words: u64 = if (l == 2 and ((e == 1 and comptime std.mem.eql(u8, m, "w2")) or (e == 3 and comptime std.mem.eql(u8, m, "w1")))) 48 else 32;
+                try T.group(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "{s}.ffn.experts.{d}." ++ m, .{ p, e }), kn[0], kn[1], words);
+            }
+        }
+    }
+    try T.put(wr, &at, &first, skip, "embed.weight", "BF16", &.{ c.vocab, D });
+    try T.put(wr, &at, &first, skip, "norm.weight", "BF16", &.{D});
+    try T.group(wr, &at, &first, skip, "head", D, c.vocab, 96);
+    const last = c.mtp_layers - 1;
+    try T.group(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "mtp.{d}.main_proj", .{last}), 3 * D, D, 64);
+    inline for (.{ "main_norm", "norm" }) |s| try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "mtp.{d}." ++ s ++ ".weight", .{last}), "BF16", &.{D});
+    inline for (.{ "embed", "head" }) |s| try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "mtp.{d}.markov_head." ++ s ++ ".weight", .{last}), "BF16", &.{ c.vocab, c.dspark_markov_rank });
+    try T.put(wr, &at, &first, skip, try std.fmt.bufPrint(&nb, "mtp.{d}.confidence_head.proj.weight", .{last}), "F32", &.{ 1, D + c.dspark_markov_rank });
+    try T.put(wr, &at, &first, skip, "vision.norm.weight", "BF16", &.{D});
+    try T.put(wr, &at, &first, skip, "image_start", "BF16", &.{D});
+    if (extra.len > 0) try T.put(wr, &at, &first, skip, extra, "BF16", &.{4});
+    try wr.writeAll("}");
+    data_len.* = at;
+    return w.toOwnedSlice();
+}
+
+fn tinyPlan(a: std.mem.Allocator, c: *const Config, extra: []const u8, skip: []const u8, rank: u32) !plan.Plan {
+    return tinyPlanOf(a, c, c, extra, skip, rank);
+}
+
+/// A pack written for `stored`, planned with `c`.
+fn tinyPlanOf(a: std.mem.Allocator, c: *const Config, stored: *const Config, extra: []const u8, skip: []const u8, rank: u32) !plan.Plan {
+    const json = try tinyPack(a, stored, extra, skip);
+    defer a.free(json);
+    var p = Pack.init(a);
+    defer p.deinit();
+    try p.addHeader("model.safetensors", json, std.math.maxInt(u32));
+    return plan.build(a, c, &p, .{ .rank = rank, .world = 2 });
+}
+
+test "a synthetic pack: both ranks plan every block, the parts tile the full tensors, nothing is left" {
+    const a = testing.allocator;
+    const c = config.tiny();
+    var r0 = try tinyPlan(a, &c, "", "", 0);
+    defer r0.deinit();
+    var r1 = try tinyPlan(a, &c, "", "", 1);
+    defer r1.deinit();
+    try testing.expectEqual(@as(usize, 0), r0.leftovers.len);
+    try testing.expectEqual(@as(usize, 10), r0.layers.len);
+    const l2 = &r0.layers[2];
+    try testing.expectEqual(config.Mode.full, l2.mode);
+    try testing.expect(l2.experts[1].layout.kind == .ragged and l2.experts[0].layout.kind == .ragged and l2.experts[2].layout.kind == .stack);
+    try testing.expectEqual(@as(u32, 1), l2.gate_up_mismatch);
+    try testing.expectEqual(@as(u32, 2), l2.experts[1].layout.widths());
+    try testing.expect(r0.layers[3].experts[1].layout.kind == .stack);
+    try testing.expectEqual(@as(usize, 4), r0.layers[8].experts[0].layout.count());
+    // every split part on the two ranks adds up to the stored tensor
+    for (r0.layers, r1.layers) |x, y| for (x.groups, y.groups) |g0, g1| {
+        if (g0.split == .whole) {
+            try testing.expectEqual(g0.full.trellisBytes(), g0.part.trellis.bytes());
+        } else try testing.expectEqual(g0.full.trellisBytes(), g0.part.trellis.bytes() + g1.part.trellis.bytes());
+    };
+    try testing.expectEqual(r0.head.?.full.trellisBytes(), r0.head.?.part.trellis.bytes() + r1.head.?.part.trellis.bytes());
+    try testing.expectEqualStrings("mtp.1.main_proj", r0.dspark.?.main_proj.prefix);
+    try testing.expect(r0.dspark.?.confidence != null);
+    try testing.expectEqual(r0.bytes(), r1.bytes());
+    // the embedding: half the vocabulary rows on each rank
+    try testing.expectEqual(@as(u64, 128 * 256 * 2), r0.top[0].bytes);
+}
+
+test "a synthetic pack: unknown names are leftovers, missing ones and wrong shapes are refused" {
+    const a = testing.allocator;
+    const c = config.tiny();
+    var p = try tinyPlan(a, &c, "layers.3.attn.mystery", "", 0);
+    defer p.deinit();
+    try testing.expectEqual(@as(usize, 1), p.leftovers.len);
+    try testing.expectEqualStrings("layers.3.attn.mystery", p.leftovers[0]);
+    try testing.expectError(error.MissingTensor, tinyPlan(a, &c, "", "layers.5.ffn.experts.6.w3.svh", 0));
+    try testing.expectError(error.MissingTensor, tinyPlan(a, &c, "", "layers.4.attn.indexer.k_norm.weight", 1));
+    var wide = c;
+    wide.q_lora = 48;
+    try testing.expectError(error.UnexpectedShape, tinyPlanOf(a, &wide, &c, "", "", 0));
+}
+
+test "a synthetic pack on disk: every planned tensor named in the reference loader's form" {
+    const a = testing.allocator;
+    const io = testing.io;
+    const c = config.tiny();
+    var n: u64 = 0;
+    const json = try tinyPackLen(a, &c, "", "", &n);
+    defer a.free(json);
+    var tmp = testing.tmpDir(.{});
+    defer tmp.cleanup();
+    {
+        var f = try tmp.dir.createFile(io, "model.safetensors", .{});
+        defer f.close(io);
+        var len: [8]u8 = undefined;
+        std.mem.writeInt(u64, &len, json.len, .little);
+        try f.writePositionalAll(io, &len, 0);
+        try f.writePositionalAll(io, json, 8);
+        try f.setLength(io, 8 + json.len + n);
+    }
+    const dir = try tmp.dir.realPathFileAlloc(io, ".", a);
+    defer a.free(dir);
+    var p = try Pack.open(a, io, dir);
+    defer p.deinit();
+    var pl = try plan.build(a, &c, &p, .{ .rank = 1, .world = 2 });
+    defer pl.deinit();
+    var b: named.Builder = .{ .gpa = a, .io = io, .pack = &p };
+    defer b.deinit();
+    try b.all(&pl, c.o_groups);
+    try testing.expectEqual(named.Kind.i16, b.find("L2.moe.w2.data").?.kind);
+    try testing.expect(b.find("L3.moe.w2.trellis") != null and b.find("L3.moe.w2.data") == null);
+    try testing.expectEqualSlices(usize, &.{ 8, 8, 16, 32 }, b.find("L3.moe.w2.trellis").?.shape[0..4]);
+    const qk = b.find("L1.engram.qk").?;
+    try testing.expect(qk.kind == .f32 and qk.bytes.len == 4 * c.hc_mult * c.hidden);
+    try testing.expectEqual(named.Kind.bf16, b.find("L0.moe.gate").?.kind);
+    try testing.expectEqual(named.Kind.f32, b.find("L2.attn.ix_wp").?.kind);
+    // rank 1 of 2: half the heads' sinks, half the vocabulary rows, wo_a's one local group
+    try testing.expectEqual(@as(usize, 4 * c.heads / 2), b.find("L4.attn.sink").?.bytes.len);
+    try testing.expectEqual(@as(usize, 2 * c.vocab / 2 * c.hidden), b.find("embed").?.bytes.len);
+    try testing.expect(b.find("L4.attn.wo_a.0.svh") != null and b.find("L4.attn.wo_a.1.svh") == null);
+    try testing.expect(b.find("head.trellis") != null and b.find("norm") != null);
+}
+
+/// One fixture histogram ("4:383,6:1") as rank-0 parts of a TP=2 projection with full shape `kn`.
+fn parts(a: std.mem.Allocator, text: []const u8, kn: [2]u32, split: exl3.Split) ![]exl3.Part {
+    var out: std.ArrayList(exl3.Part) = .empty;
+    errdefer out.deinit(a);
+    var it = std.mem.tokenizeScalar(u8, text, ',');
+    while (it.next()) |item| {
+        const colon = std.mem.indexOfScalar(u8, item, ':') orelse return error.BadFixture;
+        const k2 = try std.fmt.parseInt(u32, item[0..colon], 10);
+        const n = try std.fmt.parseInt(u32, item[colon + 1 ..], 10);
+        const g = try exl3.Group.fromShape(&.{ kn[0] / 16, kn[1] / 16, 8 * k2 });
+        try out.appendNTimes(a, try g.part(split, 0, 2), n);
+    }
+    return out.toOwnedSlice(a);
+}
+
+test "the 2.8 bpw EXL3 pack: rank 0's routed + shared trellis bytes of every block match the reference loader's listing" {
+    const a = testing.allocator;
+    const c: Config = .{};
+    var lines = std.mem.tokenizeScalar(u8, @embedFile("fixtures/widths-exl3-2p8.txt"), '\n');
+    var blocks: u32 = 0;
+    var ragged: u32 = 0;
+    while (lines.next()) |line| {
+        if (line[0] == '#') continue;
+        var f = std.mem.tokenizeScalar(u8, line, ' ');
+        const layer = try std.fmt.parseInt(u32, f.next().?, 10);
+        var total: u64 = 0;
+        const projs = [_]struct { kn: [2]u32, split: exl3.Split }{ .{ .kn = .{ c.hidden, c.expert_width }, .split = .col }, .{ .kn = .{ c.expert_width, c.hidden }, .split = .row }, .{ .kn = .{ c.hidden, c.expert_width }, .split = .col } };
+        for (projs) |pj| {
+            const ps = try parts(a, f.next().?, pj.kn, pj.split);
+            defer a.free(ps);
+            try testing.expectEqual(@as(usize, c.expertsOf(layer).count), ps.len);
+            var lay = try exl3.Layout.of(a, ps);
+            defer lay.deinit(a);
+            ragged += @intFromBool(lay.kind == .ragged);
+            total += lay.trellisBytes();
+        }
+        var sh = std.mem.tokenizeScalar(u8, f.next().?, ',');
+        for (projs) |pj| {
+            const g = try exl3.Group.fromShape(&.{ pj.kn[0] / 16, pj.kn[1] / 16, 8 * try std.fmt.parseInt(u32, sh.next().?, 10) });
+            total += (try g.part(pj.split, 0, 2)).trellis.bytes();
+        }
+        try testing.expectEqual(try std.fmt.parseInt(u64, f.next().?, 10), total);
+        blocks += 1;
+    }
+    try testing.expectEqual(@as(u32, 43), blocks);
+    try testing.expect(ragged > 40);
+}
+
+test "real pack headers (TF_DSV41_HEADERS: model.safetensors.index.json and .hdr dumps): planned blocks" {
+    const dir = testing.environ.getPosix("TF_DSV41_HEADERS") orelse return error.SkipZigTest;
+    const a = testing.allocator;
+    const io = testing.io;
+    var p = Pack.init(a);
+    defer p.deinit();
+    const cwd = std.Io.Dir.cwd();
+    const index = try std.fs.path.join(a, &.{ dir, "model.safetensors.index.json" });
+    defer a.free(index);
+    const text = try cwd.readFileAlloc(io, index, a, .limited(1 << 28));
+    defer a.free(text);
+    try p.useIndex(text);
+    var d = try cwd.openDir(io, dir, .{ .iterate = true });
+    defer d.close(io);
+    var it = d.iterate();
+    var blocks: std.ArrayList(u32) = .empty;
+    defer blocks.deinit(a);
+    while (try it.next(io)) |e| {
+        if (!std.mem.endsWith(u8, e.name, ".hdr")) continue;
+        const bytes = try d.readFileAlloc(io, e.name, a, .limited(1 << 28));
+        defer a.free(bytes);
+        const shard = e.name[0 .. e.name.len - 4];
+        try p.addHeaderDump(shard, bytes);
+        if (std.mem.startsWith(u8, shard, "layer-")) try blocks.append(a, try std.fmt.parseInt(u32, shard[6..8], 10));
+    }
+    const c: Config = .{};
+    for ([_]u32{ 0, 1 }) |rank| {
+        var pl = try plan.build(a, &c, &p, .{ .rank = rank, .world = 2, .blocks = blocks.items });
+        defer pl.deinit();
+        for (pl.layers) |*l| {
+            const h = l.experts[1].layout.histogram();
+            std.debug.print("rank {d} {s} {t}: down {t} K2 4/6/8 = {d}/{d}/{d}, gate/up mismatches {d}, experts {d} B, block {d} B\n", .{ rank, l.prefix, l.mode, l.experts[1].layout.kind, h[4], h[6], h[8], l.gate_up_mismatch, l.expertBytes(), l.bytes() });
+        }
+    }
+}
diff --git a/zig/src/families/deepseek_v41/image_opts.zig b/zig/src/families/deepseek_v41/image_opts.zig
new file mode 100644
index 0000000000..f325d920cb
--- /dev/null
+++ b/zig/src/families/deepseek_v41/image_opts.zig
@@ -0,0 +1,68 @@
+//! The prefill's block options for image prompts: pure functions of the forward's image settings and a segment's ids.
+const std = @import("std");
+const block = @import("block.zig");
+const Forward = @import("forward.zig").Forward;
+
+/// The first virtual id of an image position; text ids sit below it.
+pub const VBASE: u32 = 1 << 24;
+
+pub fn isVid(t: u32) bool {
+    return t >= VBASE;
+}
+
+/// Whether a window's ids hold an image position.
+pub fn hasImage(ids: []const u32) bool {
+    for (ids) |t| if (isVid(t)) return true;
+    return false;
+}
+
+/// The plan's options: the keep buffer when images are on, both MoE calls at full rows with the image bias.
+pub fn planWith(o: block.Options, images: bool, bias: bool) block.Options {
+    var p = o;
+    p.image_keep = images;
+    if (images and bias) p.image_rows = -1;
+    return p;
+}
+
+/// The options the buffer plan sizes with.
+pub fn planOptions(f: *const Forward) block.Options {
+    return planWith(f.opts, f.images, f.image_bias);
+}
+
+/// A segment's options: the keep step when it holds an image position, its image rows under the image bias.
+pub fn segmentWith(base: block.Options, images: bool, bias: bool, ids: []const u32) block.Options {
+    var o = base;
+    o.image_keep = images and hasImage(ids);
+    if (o.image_keep and bias) {
+        var n: i64 = 0;
+        for (ids) |t| n += @intFromBool(isVid(t));
+        o.image_rows = n;
+    }
+    return o;
+}
+
+/// A segment's options on the forward `f`.
+pub fn segmentOptions(f: *const Forward, ids: []const u32) block.Options {
+    return segmentWith(f.opts, f.images, f.image_bias, ids);
+}
+
+test "image options: text segments keep the forward's options, image ones the keep step and their rows" {
+    const vid = VBASE + 7;
+    try std.testing.expect(!hasImage(&.{ 1, 2, VBASE - 1 }));
+    try std.testing.expect(hasImage(&.{ 1, vid }));
+    const o: block.Options = .{};
+    const off = planWith(o, false, true);
+    try std.testing.expect(!off.image_keep);
+    try std.testing.expectEqual(o.image_rows, off.image_rows);
+    const on = planWith(o, true, false);
+    try std.testing.expect(on.image_keep);
+    try std.testing.expectEqual(o.image_rows, on.image_rows);
+    try std.testing.expectEqual(@as(i64, -1), planWith(o, true, true).image_rows);
+    const ids = [_]u32{ 5, vid, vid + 1, 9 };
+    try std.testing.expectEqual(o, segmentWith(o, false, true, &ids)); // images off: the forward's options
+    try std.testing.expectEqual(o, segmentWith(o, true, true, &.{ 5, 9 })); // a text segment
+    const kept = segmentWith(o, true, false, &ids);
+    try std.testing.expect(kept.image_keep);
+    try std.testing.expectEqual(o.image_rows, kept.image_rows);
+    try std.testing.expectEqual(@as(i64, 2), segmentWith(o, true, true, &ids).image_rows);
+}
diff --git a/zig/src/families/deepseek_v41/kernels.zig b/zig/src/families/deepseek_v41/kernels.zig
new file mode 100644
index 0000000000..38f446cfb8
--- /dev/null
+++ b/zig/src/families/deepseek_v41/kernels.zig
@@ -0,0 +1,156 @@
+//! DeepSeek-V4.1's CUDA kernels (zig/kernels/cuda/deepseek_v41) as embedded fatbins, and their launches.
+
+const std = @import("std");
+const cuda = @import("cuda");
+const options = @import("dsv41_kernel_options");
+
+pub const exl3 = @import("kernels_exl3.zig");
+pub const dense = @import("kernels_dense.zig");
+pub const kv_glue = @import("kernels_kv_glue.zig");
+pub const replay = @import("kernels_replay.zig");
+pub const ops = @import("kernels_ops.zig");
+/// sampling.cu (its own module, loaded by the keyed sampler)
+pub const sampling = @import("sampling_kernels.zig");
+
+fn Blob(comptime import_name: []const u8) type {
+    return struct {
+        pub const bytes align(16) = @embedFile(import_name).*;
+    };
+}
+
+/// False in host-only builds (no nvcc, no prebuilt fatbins); every image is then empty.
+pub const available = options.with_kernels;
+
+/// The fatbins, by the names zig/build/cuda.zig gives them (dsv41_).
+pub const images = struct {
+    pub const exl3_experts: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_exl3_experts").bytes else &.{};
+    pub const x3ld: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_x3ld").bytes else &.{};
+    pub const x3pf: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_x3pf").bytes else &.{};
+    pub const x3gm: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_x3gm").bytes else &.{};
+    pub const linear: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_linear").bytes else &.{};
+    pub const x3seg: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_x3seg").bytes else &.{};
+    pub const dense3: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_dense3").bytes else &.{};
+    pub const attn: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_attn").bytes else &.{};
+    pub const topk: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_topk").bytes else &.{};
+    pub const mhc: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_mhc").bytes else &.{};
+    pub const mhc_pf: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_mhc_pf").bytes else &.{};
+    pub const router_gemv: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_router_gemv").bytes else &.{};
+    pub const pfdense_k8: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_pfdense_k8").bytes else &.{};
+    pub const pfdense_k10: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_pfdense_k10").bytes else &.{};
+    pub const pfdense_k12: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_pfdense_k12").bytes else &.{};
+    pub const pfdense_k16: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_pfdense_k16").bytes else &.{};
+    pub const engram_gate: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_engram_gate").bytes else &.{};
+    pub const l2pace: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_l2pace").bytes else &.{};
+    pub const l2pf: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_l2pf").bytes else &.{};
+    pub const x3gm_plan: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_x3gm_plan").bytes else &.{};
+    pub const topk_keys: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_topk_keys").bytes else &.{};
+    pub const pointwise: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_pointwise").bytes else &.{};
+    pub const kvsplit: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_kvsplit").bytes else &.{};
+    pub const glue: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_glue").bytes else &.{};
+    pub const x3gm3: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_x3gm3").bytes else &.{};
+    pub const pfglue: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_pfglue").bytes else &.{};
+    pub const kv_glue: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_kv_glue").bytes else &.{};
+    pub const router_glue: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_router_glue").bytes else &.{};
+    pub const x3ld_epi: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_x3ld_epi").bytes else &.{};
+    pub const topk_b: []const u8 = if (available) &Blob("dsv41_fatbin_dsv41_topk_b").bytes else &.{};
+};
+
+/// lin2.cu (ours, TF_DSV41_LIN2): optional (zig/build/cuda.zig), an empty image when the build has none.
+const Lin2Blob = struct {
+    pub const bytes align(16) = @embedFile("dsv41_fatbin_dsv41_lin2").*;
+};
+pub const lin2_image: []const u8 = &Lin2Blob.bytes;
+
+fn env(name: [:0]const u8) ?[]const u8 {
+    const v = std.c.getenv(name) orelse return null;
+    return std.mem.span(v);
+}
+
+pub const Kernels = struct {
+    mods: [30]cuda.Module,
+    kv_glue: kv_glue.Functions,
+    exl3: exl3.Functions,
+    dense: dense.Functions,
+    ops: ops.Functions,
+    sms: usize,
+
+    /// Loads every module and resolves every instance the launches can use.
+    pub fn load(ctx: *const cuda.Context) !Kernels {
+        if (!available) return error.BuiltWithoutKernels;
+        const d = ctx.d;
+        var k: Kernels = undefined;
+        const list = [_][]const u8{ images.exl3_experts, images.x3ld, images.x3pf, images.x3gm, images.linear, images.x3seg, images.dense3, images.attn, images.topk, images.mhc, images.mhc_pf, images.router_gemv, images.pfdense_k8, images.pfdense_k10, images.pfdense_k12, images.pfdense_k16, images.engram_gate, images.l2pace, images.l2pf, images.x3gm_plan, images.topk_keys, images.pointwise, images.kvsplit, images.glue, images.x3gm3, images.pfglue, images.router_glue, images.kv_glue, images.x3ld_epi, images.topk_b };
+        comptime std.debug.assert(list.len == @typeInfo(@FieldType(Kernels, "mods")).array.len);
+        var loaded: usize = 0;
+        errdefer for (k.mods[0..loaded]) |*m| m.unload();
+        for (list, 0..) |img, i| {
+            k.mods[i] = try cuda.Module.load(d, img);
+            loaded += 1;
+        }
+        // the per-block shared memory opt-in (sm_120 / sm_121: 101,376 B): instances above it are refused one by one
+        const optin: u32 = @intCast(try ctx.attribute(.max_shared_memory_per_block_optin));
+        k.kv_glue = try kv_glue.Functions.resolve(k.mods[27]);
+        k.exl3 = try exl3.Functions.resolve(k.mods[0], k.mods[1], k.mods[2], k.mods[3], k.mods[19], k.mods[24], k.mods[26], k.mods[28], optin);
+        k.dense = try dense.Functions.resolve(k.mods[4], k.mods[5], k.mods[6]);
+        const m = k.mods;
+        k.ops = try ops.Functions.resolve(m[7], m[8], m[9], m[10], m[11], .{ m[12], m[13], m[14], m[15] }, m[16], m[17], m[18], m[20], m[21], m[22], m[23], m[25], m[29], optin);
+        k.sms = @intCast(try ctx.attribute(.multiprocessor_count));
+        try k.loadLin2(d, blk: {
+            var lm = try dense.lin2ModeWk(env("TF_DSV41_LIN2"), env("TF_DSV41_LIN2_PF"), env("TF_DSV41_LIN2_WK"));
+            lm.cluster = try dense.lin2Cluster(env("TF_DSV41_LIN2_CLUSTER"));
+            break :blk lm;
+        });
+        return k;
+    }
+
+    /// TF_DSV41_LIN2: lin2.cu's module and instances (dense.Ops); refused when the build has no lin2 fatbin.
+    pub fn loadLin2(k: *Kernels, d: *const cuda.Driver, mode: dense.Lin2Mode) !void {
+        if (!mode.lanes and !mode.linear and !mode.cluster) return;
+        if (lin2_image.len == 0) return error.Lin2KernelNotBuilt;
+        var m = try cuda.Module.load(d, lin2_image);
+        errdefer m.unload();
+        k.dense.lin2 = try dense.Lin2.resolve(m, mode);
+        std.log.scoped(.dsv41).info("lin2: 17-32-row {s} launches as two-tile variants (one pass over the weights){s}{s}", .{ if (mode.lanes and mode.linear) "dense3 and linear.cu" else if (mode.lanes) "dense3" else "linear.cu", if (mode.linear and !mode.pf) ", linear2 without the k-step prefetch" else "", if (mode.lanes and !mode.wk8) ", lanes2 at 4-warp CTAs only" else if (mode.lanes and !mode.wk4) ", lanes2 at 8-warp CTAs only" else "" });
+        if (mode.cluster) std.log.scoped(.dsv41).info("lin2: dense3's split-K launches of up to 32 rows reduce their partials in a cluster's shared memory (TF_DSV41_LIN2_CLUSTER)", .{});
+    }
+
+    pub fn deinit(k: *Kernels) void {
+        if (k.dense.lin2) |*l| l.module.unload();
+        for (&k.mods) |*m| m.unload();
+    }
+
+    /// The routed-expert launches on one stream.
+    pub fn experts(k: *const Kernels, s: cuda.Stream) exl3.Ops {
+        return .{ .f = &k.exl3, .s = s, .sms = k.sms };
+    }
+
+    /// Attention, top-k, mHC, router, pfdense, Engram gate and L2 launches on one stream.
+    pub fn others(k: *const Kernels, s: cuda.Stream) ops.Ops {
+        return .{ .f = &k.ops, .s = s };
+    }
+
+    pub fn kvStore(k: *const Kernels, s: cuda.Stream) kv_glue.Ops {
+        return .{ .f = &k.kv_glue, .stream = s };
+    }
+
+    /// The dense-linear launches on one stream.
+    pub fn linears(k: *const Kernels, s: cuda.Stream) dense.Ops {
+        return .{ .f = &k.dense, .s = s };
+    }
+};
+
+test {
+    std.testing.refAllDecls(kv_glue);
+    std.testing.refAllDecls(kv_glue.Ops);
+    std.testing.refAllDecls(exl3);
+    std.testing.refAllDecls(exl3.Ops);
+    std.testing.refAllDecls(exl3.Functions);
+    std.testing.refAllDecls(dense);
+    std.testing.refAllDecls(dense.Ops);
+    std.testing.refAllDecls(dense.Functions);
+    std.testing.refAllDecls(Kernels);
+    std.testing.refAllDecls(replay);
+    std.testing.refAllDecls(ops);
+    std.testing.refAllDecls(ops.Ops);
+    std.testing.refAllDecls(ops.Functions);
+}
diff --git a/zig/src/families/deepseek_v41/kernels_dense.zig b/zig/src/families/deepseek_v41/kernels_dense.zig
new file mode 100644
index 0000000000..b31bec7ebc
--- /dev/null
+++ b/zig/src/families/deepseek_v41/kernels_dense.zig
@@ -0,0 +1,555 @@
+//! DeepSeek-V4.1's dense EXL3 linears (mul1): linear.cu, x3seg segment tables and dense3's "lanes" layout.
+
+const std = @import("std");
+const cuda = @import("cuda");
+const lin2 = @import("kernels_lin2.zig");
+
+pub const DType = enum(c_int) { f16 = 0, bf16 = 1, f32 = 2 };
+
+pub fn dtypeSize(t: DType) usize {
+    return switch (t) {
+        .f16, .bf16 => 2,
+        .f32 => 4,
+    };
+}
+
+// -- Plans and tables (pure: host tests cover them) -------------------------------------------------------------
+
+/// linear.plan: (K splits, warps a program) for a K x N layer, the shape's alone (every row count one reduction).
+pub fn plan(k: usize, n: usize) [2]usize {
+    const blocks = 192;
+    const min_tiles = 8;
+    const kt = k / 16;
+    const nb = n / 128;
+    var sk: usize = 1;
+    var wk: usize = if (nb >= 64) 8 else 4;
+    if (nb < 64) {
+        while (nb * sk < blocks and sk < 64) sk *= 2;
+    }
+    while ((wk > 4 or sk > 1) and (kt % (sk * wk) != 0 or kt / (sk * wk) < min_tiles)) {
+        if (wk > 4) {
+            wk = 4;
+        } else if (sk > 1) {
+            sk /= 2;
+        } else break;
+    }
+    return .{ sk, wk };
+}
+
+/// Words between k tiles and between 128-column blocks of a matrix in the "strips" layout (both dense layouts).
+pub fn strides(k: usize, k2: u32) [2]i64 {
+    const tw: i64 = 4 * @as(i64, k2);
+    return .{ 8 * tw, @as(i64, @intCast(k / 16)) * 8 * tw };
+}
+
+pub const max_seg = 8; // x3seg.cu MAXSEG
+pub const rows_max = 128; // fused_proj.ROWS / dense3.ROWS: rows a launch
+pub const x3seg_k2s = [_]u32{ 4, 6, 8, 10, 12, 16 };
+pub const dense3_k2s = [_]u32{ 4, 6, 8, 10, 12 };
+
+/// fused_proj.Seg: one projection as the kernel sees it, and where its output goes (buffer `out`, column `col`).
+pub const Seg = struct {
+    k: usize,
+    n: usize,
+    k2: u32,
+    sk: usize,
+    wk: usize,
+    out: usize = 0,
+    col: usize = 0,
+
+    pub fn programs(s: Seg) usize {
+        return (s.n / 128) * s.sk;
+    }
+};
+
+pub const Launch = struct {
+    k2: u32,
+    wk: usize,
+    segs: [max_seg]usize = undefined, // indices into the call's segments
+    count: usize = 0,
+    programs: usize = 0,
+    p0: [max_seg]usize = undefined, // first program of each segment in this launch
+};
+
+/// fused_proj.Layout: scratch offsets (fp16 rotated inputs, Z floats, counter ints) and the launches at `m` rows.
+pub const Layout = struct {
+    m: usize,
+    xh_off: [64]usize = undefined,
+    z_off: [64]usize = undefined,
+    c_off: [64]usize = undefined,
+    xh_total: usize = 0,
+    z_total: usize = 0,
+    c_total: usize = 0,
+    launches: [16]Launch = undefined,
+    n_launches: usize = 0,
+};
+
+pub const Error = error{ Shape, Unsupported, TooMany };
+
+/// fused_proj.layout: one launch a width in first-appearance order, up to 8 segments; WK = the segments' largest.
+pub fn layout(segs: []const Seg, m: usize) Error!Layout {
+    if (m < 1 or m > rows_max) return error.Shape;
+    if (segs.len > 64) return error.TooMany;
+    var lay: Layout = .{ .m = m };
+    for (segs, 0..) |s, i| {
+        if (s.k % 128 != 0 or s.n % 128 != 0 or s.sk < 1 or (s.wk != 4 and s.wk != 8) or (s.k / 16) % (s.sk * s.wk) != 0) return error.Shape;
+        lay.xh_off[i] = lay.xh_total;
+        lay.z_off[i] = lay.z_total;
+        lay.c_off[i] = lay.c_total;
+        lay.xh_total += m * s.k;
+        if (s.sk > 1) lay.z_total += s.sk * m * s.n;
+        lay.c_total += 8 * (s.n / 128);
+    }
+    var seen: [64]u32 = undefined;
+    var nseen: usize = 0;
+    for (segs) |s| {
+        if (std.mem.indexOfScalar(u32, seen[0..nseen], s.k2) == null) {
+            seen[nseen] = s.k2;
+            nseen += 1;
+        }
+    }
+    for (seen[0..nseen]) |k2| {
+        var l: Launch = .{ .k2 = k2, .wk = 0 };
+        for (segs, 0..) |s, i| {
+            if (s.k2 != k2) continue;
+            if (l.count == max_seg) {
+                if (lay.n_launches == lay.launches.len) return error.TooMany;
+                lay.launches[lay.n_launches] = l;
+                lay.n_launches += 1;
+                l = .{ .k2 = k2, .wk = 0 };
+            }
+            l.segs[l.count] = i;
+            l.p0[l.count] = l.programs;
+            l.count += 1;
+            l.programs += s.programs();
+            l.wk = @max(l.wk, s.wk);
+        }
+        if (lay.n_launches == lay.launches.len) return error.TooMany;
+        lay.launches[lay.n_launches] = l;
+        lay.n_launches += 1;
+    }
+    return lay;
+}
+
+/// dense3.group_steps: k steps a load group (whole 16-byte chunks a lane).
+pub fn groupSteps(k2: u32) usize {
+    return if (k2 == 16) 1 else 2;
+}
+
+/// x3seg.cu rot table entry (by value); R1c: with `nw` set the input is rmsnorm-normed in the kernel (inv_k, eps).
+pub const RotSeg = extern struct { x: u64, suh: u64, xh: u64, ld: i64, K: c_int, x_dtype: c_int, nw: u64 = 0, nw_dtype: c_int = 0, inv_k: f32 = 0, eps: f32 = 0 };
+pub const RotTable = extern struct { s: [max_seg]RotSeg, n: c_int };
+pub const LinSeg = extern struct {
+    xh: u64,
+    T: u64,
+    stride_k: i64,
+    stride_nb: i64,
+    svh: u64,
+    y: u64,
+    Z: u64,
+    counters: u64,
+    K: c_int,
+    N: c_int,
+    SK: c_int,
+    wk: c_int,
+    p0: c_int,
+    ld: c_int,
+};
+pub const LinTable = extern struct { s: [max_seg]LinSeg, n: c_int };
+
+comptime {
+    std.debug.assert(@sizeOf(RotSeg) == 64 and @offsetOf(RotSeg, "nw") == 40 and @sizeOf(RotTable) == 520);
+    std.debug.assert(@sizeOf(LinSeg) == 88 and @sizeOf(LinTable) == 712);
+}
+
+/// One segment's buffers for a linear launch: matrix words, svh, output `y` (row stride `ld` elements).
+pub const SegBufs = struct { T: u64, svh: u64, y: u64, ld: usize };
+
+/// One segment's rotation input: rows at x + row * ld, dtype, suh [K]; `nw`: folded q_norm weight (0 for none).
+pub const RotIn = struct { x: u64, ld: usize, dtype: DType, suh: u64, nw: u64 = 0, nw_dtype: DType = .bf16 };
+
+/// fp32(1 / K): the binding's `static_cast(1.0 / K)`.
+pub fn invK(k: usize) f32 {
+    return @floatCast(1.0 / @as(f64, @floatFromInt(k)));
+}
+
+/// fused_proj._block's rot_in tables: one launch per 8 segments (`first` = 0, 8, ...), rows at their xh offsets.
+pub fn rotTable(lay: Layout, segs: []const Seg, ins: []const RotIn, first: usize, xh: u64, eps: f32) RotTable {
+    var t = std.mem.zeroes(RotTable);
+    const end = @min(segs.len, first + max_seg);
+    t.n = @intCast(end - first);
+    for (first..end, 0..) |i, j| {
+        t.s[j] = .{ .x = ins[i].x, .suh = ins[i].suh, .xh = xh + 2 * lay.xh_off[i], .ld = @intCast(ins[i].ld), .K = @intCast(segs[i].k), .x_dtype = @backingInt(ins[i].dtype) };
+        if (ins[i].nw != 0) {
+            t.s[j].nw = ins[i].nw;
+            t.s[j].nw_dtype = @backingInt(ins[i].nw_dtype);
+            t.s[j].inv_k = invK(segs[i].k);
+            t.s[j].eps = eps;
+        }
+    }
+    return t;
+}
+
+/// dsv41_x3seg_linear_cuda / dsv41_dense3_linear_cuda's table of one launch.
+pub fn linTable(lay: Layout, l: Launch, segs: []const Seg, bufs: []const SegBufs, y_dtype: DType, xh: u64, z: u64, counters: u64) LinTable {
+    var t = std.mem.zeroes(LinTable);
+    t.n = @intCast(l.count);
+    const esz = dtypeSize(y_dtype);
+    for (l.segs[0..l.count], 0..) |i, j| {
+        const s = segs[i];
+        const st = strides(s.k, s.k2);
+        t.s[j] = .{
+            .xh = xh + 2 * lay.xh_off[i],
+            .T = bufs[i].T,
+            .stride_k = st[0],
+            .stride_nb = st[1],
+            .svh = bufs[i].svh,
+            .y = bufs[i].y + s.col * esz,
+            .Z = if (s.sk > 1) z + 4 * lay.z_off[i] else 0,
+            .counters = counters + 4 * lay.c_off[i],
+            .K = @intCast(s.k),
+            .N = @intCast(s.n),
+            .SK = @intCast(s.sk),
+            .wk = @intCast(s.wk),
+            .p0 = @intCast(l.p0[j]),
+            .ld = @intCast(bufs[i].ld),
+        };
+    }
+    return t;
+}
+
+// -- Symbols ----------------------------------------------------------------------------------------------------
+
+pub const linear_k2s = [_]u32{ 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16 };
+
+fn linearSymbol(comptime k2: u32, comptime wk: u32) [:0]const u8 {
+    return std.fmt.comptimePrint("_ZN14tf_exl3_linear13linear_kernelILi{d}ELi2ELi{d}EEEvPK6__halfPKjxxS3_S3_PviPfPiiiiiPKi", .{ k2, wk });
+}
+fn unpackSymbol(comptime k2: u32) [:0]const u8 {
+    return std.fmt.comptimePrint("_ZN14tf_exl3_linear13unpack_kernelILi{d}ELi2EEEvPKjP6__halfill", .{k2});
+}
+fn segSymbol(comptime k2: u32, comptime wk: u32, comptime mb: u32) [:0]const u8 {
+    return std.fmt.comptimePrint("_ZN11dsv41_x3seg17seg_linear_kernelILi{d}ELi2ELi{d}ELi{d}EEEvNS_8LinTableEii", .{ k2, wk, mb });
+}
+fn lanesSymbol(comptime k2: u32, comptime wk: u32, comptime mb: u32) [:0]const u8 {
+    return std.fmt.comptimePrint("_ZN12dsv41_dense319lanes_linear_kernelILi{d}ELi2ELi{d}ELi{d}EEEvN11dsv41_x3seg8LinTableEii", .{ k2, wk, mb });
+}
+fn lanesUnpackSymbol(comptime k2: u32) [:0]const u8 {
+    return std.fmt.comptimePrint("_ZN12dsv41_dense319lanes_unpack_kernelILi{d}ELi2EEEvPKjP6__halfill", .{k2});
+}
+
+pub const lanesc_cfgs = lin2.lanesc_cfgs;
+pub const Lin2Mode = lin2.Lin2Mode;
+pub const lin2Cluster = lin2.lin2Cluster;
+pub const clusterSplits = lin2.clusterSplits;
+pub const lin2Mode = lin2.lin2Mode;
+pub const lin2ModeWk = lin2.lin2ModeWk;
+pub const twoTile = lin2.twoTile;
+pub const Lin2 = lin2.Lin2;
+
+/// x3seg's (WK, MINB) instances (TF_SEG_WIDTH: WK 8 takes any minb) and dense3's (TF_D3_WIDTH).
+pub const seg_cfgs = [_][2]u32{ .{ 4, 3 }, .{ 4, 2 }, .{ 8, 1 } };
+pub const lanes_cfgs = [_][2]u32{ .{ 4, 3 }, .{ 4, 4 }, .{ 8, 1 }, .{ 8, 2 } };
+
+pub const Functions = struct {
+    rot_in: cuda.Function,
+    linear: [linear_k2s.len][3]cuda.Function, // WK 2, 4, 8
+    unpack: [linear_k2s.len]cuda.Function,
+    seg_rot_in: cuda.Function,
+    seg: [x3seg_k2s.len][seg_cfgs.len]cuda.Function,
+    lanes: [dense3_k2s.len][lanes_cfgs.len]cuda.Function,
+    lanes_unpack: [dense3_k2s.len]cuda.Function,
+    to_lanes: cuda.Function,
+    to_strips: cuda.Function,
+    /// TF_DSV41_LIN2: the two-tile variants, set by Kernels.load when the knob asks for them
+    lin2: ?Lin2 = null,
+
+    pub fn resolve(linear: cuda.Module, x3seg: cuda.Module, dense3: cuda.Module) !Functions {
+        var f: Functions = undefined;
+        f.lin2 = null;
+        f.rot_in = try linear.function("_ZN14tf_exl3_linear13rot_in_kernelEPKviPK6__halfPS2_i");
+        inline for (linear_k2s, 0..) |k2, i| {
+            inline for (.{ 2, 4, 8 }, 0..) |wk, j| f.linear[i][j] = try linear.function(linearSymbol(k2, wk));
+            f.unpack[i] = try linear.function(unpackSymbol(k2));
+        }
+        f.seg_rot_in = try x3seg.function("_ZN11dsv41_x3seg17seg_rot_in_kernelENS_8RotTableE");
+        inline for (x3seg_k2s, 0..) |k2, i| inline for (seg_cfgs, 0..) |c, j| {
+            f.seg[i][j] = try x3seg.function(segSymbol(k2, c[0], c[1]));
+        };
+        inline for (dense3_k2s, 0..) |k2, i| {
+            inline for (lanes_cfgs, 0..) |c, j| f.lanes[i][j] = try dense3.function(lanesSymbol(k2, c[0], c[1]));
+            f.lanes_unpack[i] = try dense3.function(lanesUnpackSymbol(k2));
+        }
+        f.to_lanes = try dense3.function("_ZN12dsv41_dense315to_lanes_kernelEPKjPjxii");
+        f.to_strips = try dense3.function("_ZN12dsv41_dense316to_strips_kernelEPKjPjxii");
+        return f;
+    }
+};
+
+fn dim(x: usize) u32 {
+    return @intCast(x);
+}
+
+fn int(x: usize) c_int {
+    return @intCast(x);
+}
+
+fn go(f: cuda.Function, s: cuda.Stream, grid: [3]usize, block: usize, shared: usize, pdl: bool, a: *cuda.Args) !void {
+    try cuda.launch.launch(f, .{ .grid = .{ .x = dim(grid[0]), .y = dim(grid[1]), .z = dim(grid[2]) }, .block = .{ .x = dim(block) }, .shared = dim(shared), .pdl = pdl }, s, a);
+}
+
+/// WK * min(M, 8) * 128 floats: the linear kernels' reduction buffer (at most 32 KiB: no opt-in needed).
+fn redSmem(wk: usize, m: usize) usize {
+    return wk * @min(m, 8) * 128 * 4;
+}
+
+pub const Ops = struct {
+    f: *const Functions,
+    s: cuda.Stream,
+
+    /// exl3_rot_in_cuda: xh [M, K] fp16 = (x * suh) H / sqrt 128 by 128-blocks.
+    pub fn rotIn(o: Ops, x: u64, x_dtype: DType, suh: u64, xh: u64, M: usize, K: usize) !void {
+        var a: cuda.Args = .{};
+        a.add(x);
+        a.add(@backingInt(x_dtype));
+        a.add(suh);
+        a.add(xh);
+        a.add(int(K));
+        try go(o.f.rot_in, o.s, .{ (K / 128 + 3) / 4, M, 1 }, 128, 0, false, &a);
+    }
+
+    /// exl3_linear_cuda (mul1): y [M, N] from xh [M, K]; `z` fp32 [SK, M, N] when SK > 1; `skip` a device flag or 0.
+    pub fn linear(o: Ops, xh: u64, T: u64, stride_k: i64, stride_nb: i64, svh: u64, bias: u64, y: u64, y_dtype: DType, z: u64, counters: u64, M: usize, K: usize, N: usize, k2: u32, SK: usize, WK: usize, skip: u64) !void {
+        const ki = std.mem.indexOfScalar(u32, &linear_k2s, k2) orelse return error.Unsupported;
+        const wi: usize = switch (WK) {
+            2 => 0,
+            4 => 1,
+            8 => 2,
+            else => return error.Unsupported,
+        };
+        if (SK == 0 or (K / 16) % (SK * WK) != 0) return error.Shape;
+        if (SK > 1 and z == 0) return error.Shape;
+        if (M < 1 or M > rows_max) return error.Shape;
+        var a: cuda.Args = .{};
+        for ([_]u64{ xh, T }) |v| a.add(v);
+        a.add(stride_k);
+        a.add(stride_nb);
+        for ([_]u64{ svh, bias, y }) |v| a.add(v);
+        a.add(@backingInt(y_dtype));
+        for ([_]u64{ z, counters }) |v| a.add(v);
+        for ([_]usize{ M, K, N, SK }) |v| a.add(int(v));
+        a.add(skip);
+        var f = o.f.linear[ki][wi];
+        if (o.f.lin2) |l2| if (l2.mode.linear and twoTile(M)) {
+            f = l2.linear[ki][wi]; // TF_DSV41_LIN2: the same programs, one pass over the weights
+        };
+        try go(f, o.s, .{ N / 128, SK, 1 }, WK * 32, redSmem(WK, M), false, &a);
+    }
+
+    /// exl3_unpack_cuda (mul1): W_q [K, N] fp16.
+    pub fn unpack(o: Ops, T: u64, W: u64, K: usize, N: usize, stride_k: i64, stride_nb: i64, k2: u32) !void {
+        const ki = std.mem.indexOfScalar(u32, &linear_k2s, k2) orelse return error.Unsupported;
+        var a: cuda.Args = .{};
+        a.add(T);
+        a.add(W);
+        a.add(int(N));
+        a.add(stride_k);
+        a.add(stride_nb);
+        try go(o.f.unpack[ki], o.s, .{ N / 16, K / 16, 1 }, 32, 0, false, &a);
+    }
+
+    /// dsv41_x3seg_rot_in_cuda: up to 8 (input, suh) pairs into one xh (each at its xh offset), one launch.
+    pub fn segRotIn(o: Ops, tab: RotTable, M: usize, pdl: bool) !void {
+        if (tab.n < 1 or tab.n > max_seg) return error.Shape;
+        for (tab.s[0..@intCast(tab.n)]) |s| {
+            if (s.nw != 0 and s.x_dtype != @backingInt(DType.bf16)) return error.Shape; // the fold takes bf16 rows
+        }
+        var kmax: usize = 0;
+        for (tab.s[0..@intCast(tab.n)]) |s| kmax = @max(kmax, @as(usize, @intCast(s.K)));
+        var a: cuda.Args = .{};
+        a.add(tab);
+        try go(o.f.seg_rot_in, o.s, .{ (kmax / 128 + 3) / 4, M, @intCast(tab.n) }, 128, 0, pdl, &a);
+    }
+
+    /// dsv41_x3seg_linear_cuda: one launch of a layout (words in the strips layout).
+    pub fn segLinear(o: Ops, tab: LinTable, y_dtype: DType, M: usize, l: Launch, minb: u32, pdl: bool) !void {
+        try o.segLinearRaw(tab, y_dtype, M, l.programs, l.k2, l.wk, minb, pdl);
+    }
+
+    /// segLinear with the binding's own (programs, K2, WK, minb): WK 8 takes any minb.
+    pub fn segLinearRaw(o: Ops, tab: LinTable, y_dtype: DType, M: usize, programs: usize, k2: u32, wk: usize, minb: u32, pdl: bool) !void {
+        const ki = std.mem.indexOfScalar(u32, &x3seg_k2s, k2) orelse return error.Unsupported;
+        const ci: usize = if (wk == 8) 2 else if (wk == 4 and minb == 3) 0 else if (wk == 4 and minb == 2) 1 else return error.Unsupported;
+        try o.tableLaunch(o.f.seg[ki][ci], tab, y_dtype, M, programs, wk, pdl);
+    }
+
+    /// dsv41_dense3_linear_cuda: the same programs on words in the lanes layout.
+    pub fn lanesLinear(o: Ops, tab: LinTable, y_dtype: DType, M: usize, l: Launch, minb: u32, pdl: bool) !void {
+        try o.lanesLinearRaw(tab, y_dtype, M, l.programs, l.k2, l.wk, minb, pdl);
+    }
+
+    /// lanesLinear with the binding's own arguments; each segment's k steps must split into whole load groups.
+    pub fn lanesLinearRaw(o: Ops, tab: LinTable, y_dtype: DType, M: usize, programs: usize, k2: u32, wk: usize, minb: u32, pdl: bool) !void {
+        const ki = std.mem.indexOfScalar(u32, &dense3_k2s, k2) orelse return error.Unsupported;
+        const want = [2]u32{ @intCast(wk), minb };
+        var ci: ?usize = null;
+        for (lanes_cfgs, 0..) |c, j| if (std.mem.eql(u32, &c, &want)) {
+            ci = j;
+        };
+        for (tab.s[0..@intCast(tab.n)]) |s| {
+            const per_warp: usize = @intCast(@divTrunc(@divTrunc(@divTrunc(s.K, 16), s.SK), s.wk));
+            if (per_warp % groupSteps(k2) != 0) return error.Shape;
+        }
+        const cfg = ci orelse return error.Unsupported;
+        var f = o.f.lanes[ki][cfg];
+        const width_ok = if (o.f.lin2) |l2| (if (wk == 8) l2.mode.wk8 else l2.mode.wk4) else false;
+        // TF_DSV41_LIN2_CLUSTER: a column block's split programs in one cluster, partials reduced in shared memory
+        if (o.f.lin2) |l2| if (l2.mode.cluster) if (clusterSplits(tab, M)) |sk| {
+            const two = twoTile(M);
+            if (!two or (l2.mode.lanes and width_ok)) {
+                const j: usize = (if (two) @as(usize, 2) else 0) + @intFromBool(wk == 8);
+                const t: usize = if (two) 2 else 1;
+                if (tab.n < 1 or tab.n > max_seg) return error.Shape;
+                var a: cuda.Args = .{};
+                a.add(tab);
+                a.add(@backingInt(y_dtype));
+                a.add(int(M));
+                const shared = redSmem(wk, M) + 16 * t * 128 * 4;
+                return cuda.launch.launch(l2.lanesc[ki][j], .{ .grid = .{ .x = dim(programs), .y = 1, .z = 1 }, .block = .{ .x = dim(wk * 32) }, .shared = dim(shared), .cluster = .{ .x = sk, .y = 1, .z = 1 }, .pdl = pdl }, o.s, &a);
+            }
+        };
+        if (o.f.lin2) |l2| if (l2.mode.lanes and twoTile(M) and width_ok) {
+            f = l2.lanes[ki][if (wk == 8) 1 else 0]; // TF_DSV41_LIN2: the same programs, one pass over the weights
+        };
+        try o.tableLaunch(f, tab, y_dtype, M, programs, wk, pdl);
+    }
+
+    fn tableLaunch(o: Ops, f: cuda.Function, tab: LinTable, y_dtype: DType, M: usize, programs: usize, wk: usize, pdl: bool) !void {
+        if (M < 1 or M > rows_max or tab.n < 1 or tab.n > max_seg) return error.Shape;
+        var a: cuda.Args = .{};
+        a.add(tab);
+        a.add(@backingInt(y_dtype));
+        a.add(int(M));
+        try go(f, o.s, .{ programs, 1, 1 }, wk * 32, redSmem(wk, M), pdl, &a);
+    }
+
+    /// dsv41_dense3_relayout_cuda: int32 words of one matrix ([N/128, K/16, 8, 4 K2]) to the lanes layout or back.
+    pub fn relayout(o: Ops, src: u64, dst: u64, words: usize, k2: u32, K: usize, lanes: bool) !void {
+        var a: cuda.Args = .{};
+        a.add(src);
+        a.add(dst);
+        a.add(@as(i64, @intCast(words)));
+        a.add(@as(c_int, @intCast(k2)));
+        a.add(int(K / 16));
+        try go(if (lanes) o.f.to_lanes else o.f.to_strips, o.s, .{ (words + 255) / 256, 1, 1 }, 256, 0, false, &a);
+    }
+
+    /// dsv41_dense3_unpack_cuda: W_q [K, N] fp16 from lanes-layout words.
+    pub fn lanesUnpack(o: Ops, T: u64, W: u64, K: usize, N: usize, stride_k: i64, stride_nb: i64, k2: u32) !void {
+        const ki = std.mem.indexOfScalar(u32, &dense3_k2s, k2) orelse return error.Unsupported;
+        var a: cuda.Args = .{};
+        a.add(T);
+        a.add(W);
+        a.add(int(N));
+        a.add(stride_k);
+        a.add(stride_nb);
+        try go(o.f.lanes_unpack[ki], o.s, .{ N / 16, K / 16, 1 }, 32, 0, false, &a);
+    }
+};
+
+test "linear.plan as upstream" {
+    // wide layers: 8 warps, no split; narrow ones split K until 8 k tiles a warp would not hold
+    try std.testing.expectEqual([2]usize{ 1, 8 }, plan(1024, 128 * 64));
+    try std.testing.expectEqual([2]usize{ 8, 4 }, plan(4096, 1024));
+    try std.testing.expectEqual([2]usize{ 1, 4 }, plan(512, 4096));
+}
+
+test "rot_in tables: one launch a group of 8, xh offsets from the layout" {
+    var segs: [10]Seg = undefined;
+    var ins: [10]RotIn = undefined;
+    for (&segs, &ins, 0..) |*s, *r, i| {
+        s.* = .{ .k = 1024, .n = 256, .k2 = 10, .sk = 1, .wk = 4 };
+        r.* = .{ .x = 0x1000 * (i + 1), .ld = 1024, .dtype = .bf16, .suh = 0x100 * (i + 1) };
+    }
+    const lay = try layout(&segs, 2);
+    const t0 = rotTable(lay, &segs, &ins, 0, 0x10000, 0);
+    const t1 = rotTable(lay, &segs, &ins, 8, 0x10000, 0);
+    try std.testing.expectEqual(@as(c_int, 8), t0.n);
+    try std.testing.expectEqual(@as(c_int, 2), t1.n);
+    try std.testing.expectEqual(@as(u64, 0x10000 + 2 * 8 * 2 * 1024), t1.s[0].xh);
+    try std.testing.expectEqual(@as(c_int, @backingInt(DType.bf16)), t1.s[1].x_dtype);
+}
+
+test "fused_proj.layout: offsets, one launch a width, 8 segments a launch" {
+    const segs = [_]Seg{
+        .{ .k = 4096, .n = 1024, .k2 = 10, .sk = 4, .wk = 4 },
+        .{ .k = 4096, .n = 512, .k2 = 12, .sk = 8, .wk = 4, .out = 1 },
+        .{ .k = 4096, .n = 256, .k2 = 10, .sk = 8, .wk = 8, .out = 0, .col = 1024 },
+    };
+    const lay = try layout(&segs, 3);
+    try std.testing.expectEqual(@as(usize, 3 * 4096 * 3), lay.xh_total);
+    try std.testing.expectEqual(@as(usize, 4 * 3 * 1024 + 8 * 3 * 512 + 8 * 3 * 256), lay.z_total);
+    try std.testing.expectEqual(@as(usize, 8 * (8 + 4 + 2)), lay.c_total);
+    try std.testing.expectEqual(@as(usize, 2), lay.n_launches);
+    const l0 = lay.launches[0];
+    try std.testing.expectEqual(@as(u32, 10), l0.k2);
+    try std.testing.expectEqual(@as(usize, 8), l0.wk);
+    try std.testing.expectEqualSlices(usize, &.{ 0, 2 }, l0.segs[0..l0.count]);
+    try std.testing.expectEqualSlices(usize, &.{ 0, 32 }, l0.p0[0..l0.count]);
+    try std.testing.expectEqual(@as(usize, 32 + 16), l0.programs);
+    try std.testing.expectError(error.Shape, layout(&segs, 129));
+}
+
+test "TF_DSV41_LIN2 reader: unset / 0 / off none, 1 both, lanes / linear one, _PF 1 / 0, anything else refused" {
+    const t = std.testing;
+    for ([_]?[]const u8{ null, "", "0", "off" }) |v| {
+        const m = try lin2Mode(v, null);
+        try t.expect(!m.lanes and !m.linear and m.pf);
+    }
+    try t.expectEqual(Lin2Mode{ .lanes = true, .linear = true }, try lin2Mode("1", null));
+    try t.expectEqual(Lin2Mode{ .lanes = true }, try lin2Mode(" lanes ", "1"));
+    try t.expectEqual(Lin2Mode{ .linear = true, .pf = false }, try lin2Mode("linear", "0"));
+    for ([_][]const u8{ "2", "head", "lanes,linear" }) |v| try t.expectError(error.BadLin2, lin2Mode(v, null));
+    try t.expectError(error.BadLin2, lin2Mode("1", "on"));
+    // TF_DSV41_LIN2_WK: lanes2 at one CTA width only
+    const four = try lin2ModeWk("lanes", null, "4");
+    try t.expect(four.lanes and four.wk4 and !four.wk8);
+    const eight = try lin2ModeWk("1", "0", " 8 ");
+    try t.expect(eight.lanes and eight.linear and !eight.pf and !eight.wk4 and eight.wk8);
+    const both = try lin2ModeWk("1", null, null);
+    try t.expect(both.wk4 and both.wk8);
+    for ([_][]const u8{ "2", "48", "all" }) |w| try t.expectError(error.BadLin2, lin2ModeWk("1", null, w));
+}
+
+test "TF_DSV41_LIN2: only the original's two-pass launches (17-32 rows) take the variant" {
+    const t = std.testing;
+    for (1..17) |m| try t.expect(!twoTile(m));
+    for (17..33) |m| try t.expect(twoTile(m));
+    for (33..129) |m| try t.expect(!twoTile(m));
+}
+
+test "TF_DSV41_LIN2_CLUSTER: the reader, and the launches the cluster variant takes" {
+    const t = std.testing;
+    for ([_]?[]const u8{ null, "", "0" }) |v| try t.expect(!try lin2Cluster(v));
+    try t.expect(try lin2Cluster(" 1 "));
+    for ([_][]const u8{ "on", "2", "yes" }) |v| try t.expectError(error.BadLin2, lin2Cluster(v));
+    var tab = std.mem.zeroes(LinTable);
+    tab.n = 2;
+    tab.s[0] = .{ .xh = 0, .T = 0, .stride_k = 0, .stride_nb = 0, .svh = 0, .y = 0, .Z = 0, .counters = 0, .K = 4096, .N = 512, .SK = 4, .wk = 4, .p0 = 0, .ld = 512 };
+    tab.s[1] = tab.s[0];
+    tab.s[1].p0 = 16; // 4 column blocks x 4 splits
+    try t.expectEqual(@as(?u32, 4), clusterSplits(tab, 1));
+    try t.expectEqual(@as(?u32, 4), clusterSplits(tab, 32));
+    try t.expectEqual(@as(?u32, null), clusterSplits(tab, 33)); // past two row tiles
+    tab.s[1].SK = 2; // a launch whose segments split differently
+    try t.expectEqual(@as(?u32, null), clusterSplits(tab, 8));
+    tab.s[1].SK = 4;
+    tab.s[1].p0 = 18; // a segment not starting on a cluster boundary
+    try t.expectEqual(@as(?u32, null), clusterSplits(tab, 8));
+    tab.s[1].p0 = 16;
+    tab.s[0].SK = 1;
+    tab.s[1].SK = 1; // no split: no partials to reduce
+    try t.expectEqual(@as(?u32, null), clusterSplits(tab, 8));
+    for (&tab.s) |*x| x.SK = 16; // past the portable cluster size
+    try t.expectEqual(@as(?u32, null), clusterSplits(tab, 8));
+}
diff --git a/zig/src/families/deepseek_v41/kernels_exl3.zig b/zig/src/families/deepseek_v41/kernels_exl3.zig
new file mode 100644
index 0000000000..bb25a603b8
--- /dev/null
+++ b/zig/src/families/deepseek_v41/kernels_exl3.zig
@@ -0,0 +1,534 @@
+//! DeepSeek-V4.1's routed EXL3 expert kernels: x3ld, x3pf, x3gm and the grouped kernel with its helpers.
+
+const std = @import("std");
+const cuda = @import("cuda");
+const gmk = @import("kernels_x3gm.zig");
+
+// -- Instances (the copies' footers hold the matching explicit instantiations) ----------------------------------
+
+/// The (lo, hi) K2 ranges x3ld / x3pf are built for, tried in order (expert_loads.RANGES / expert_prefill.RANGES).
+pub const ranges = [_][2]u32{ .{ 8, 8 }, .{ 2, 10 }, .{ 2, 12 } };
+/// Upstream's TF_RANGES: (8, 8), (2, 10), else (2, 16).
+pub const upstream_ranges = [_][2]u32{ .{ 8, 8 }, .{ 2, 10 }, .{ 2, 16 } };
+
+/// dispatch_ranges: the first instance whose range holds [lo, hi], or null (the binding's "unsupported").
+pub fn rangeIndex(lo: u32, hi: u32) ?usize {
+    if (lo == 8 and hi == 8) return 0;
+    if (lo >= 2 and hi <= 10) return 1;
+    if (lo >= 2 and hi <= 12) return 2;
+    return null;
+}
+
+/// grouped_launch's TF_RANGES (always an instance: 2..16 catches the rest).
+pub fn upstreamRangeIndex(lo: u32, hi: u32) usize {
+    if (lo == 8 and hi == 8) return 0;
+    if (lo >= 2 and hi <= 10) return 1;
+    return 2;
+}
+
+/// x3ld's (nt, pd) settings (expert_loads.CFGS); probe 3 (timing only) has the first two.
+pub const ld_cfgs = [_][2]u32{ .{ 8, 1 }, .{ 8, 2 }, .{ 4, 2 } };
+/// x3pf's (nt, mtl) settings.
+pub const pf_cfgs = [_][2]u32{ .{ 4, 4 }, .{ 8, 2 } };
+/// upstream grouped_kernel's (nt, warps, pf) settings.
+pub const grouped_cfgs = [_][3]u32{ .{ 8, 4, 1 }, .{ 8, 4, 2 }, .{ 4, 4, 2 } };
+
+fn cfgIndex(comptime n: usize, table: []const [n]u32, want: [n]u32) ?usize {
+    for (table, 0..) |c, i| if (std.mem.eql(u32, &c, &want)) return i;
+    return null;
+}
+
+const tail16 = "PK6__halfS3_PKlS5_PKiS7_S7_S7_S7_Pfiiiiii"; // (X0, X1, TP0, TP1, K2_0, K2_1, uids, ucount, members, Z, 6 ints)
+
+pub fn ldSymbol(comptime nt: u32, comptime pd: u32, comptime lo: u32, comptime hi: u32, comptime probe: u32) [:0]const u8 {
+    return std.fmt.comptimePrint("_ZN10dsv41_x3ld9ld_kernelILi2ELi{d}ELi{d}ELi{d}ELi{d}ELi{d}EEEv" ++ tail16, .{ nt, pd, lo, hi, probe });
+}
+
+/// TF_DSV41_X3LD_EPI (x3ld_epi.cu): x3ld (nt 8) with the gate/up (`gu`) or down_combine (`dn` fp32, `dnb` bf16) tail.
+pub const EpiKind = enum { gu, dn, dnb };
+pub const epi_pds = [_]u32{ 1, 2 };
+
+pub fn epiSymbol(comptime kind: EpiKind, comptime pd: u32, comptime lo: u32, comptime hi: u32) [:0]const u8 {
+    return std.fmt.comptimePrint("dsv41_x3ld_epi_{s}_{d}_{d}_{d}", .{ @tagName(kind), pd, lo, hi });
+}
+
+pub fn pfSymbol(comptime nt: u32, comptime mtl: u32, comptime lo: u32, comptime hi: u32) [:0]const u8 {
+    return std.fmt.comptimePrint("_ZN10dsv41_x3pf9pf_kernelILi2ELi{d}ELi{d}ELi{d}ELi{d}EEEv" ++ tail16, .{ nt, mtl, lo, hi });
+}
+
+pub fn groupedSymbol(comptime nt: u32, comptime w: u32, comptime pf: u32, comptime lo: u32, comptime hi: u32) [:0]const u8 {
+    return std.fmt.comptimePrint("_ZN8tf_exl3x14grouped_kernelILi2ELi{d}ELi{d}ELi{d}ELi{d}ELi{d}EEEv" ++ tail16, .{ nt, w, pf, lo, hi });
+}
+
+pub fn dequantSymbol(comptime k2: u32) [:0]const u8 {
+    return std.fmt.comptimePrint("_ZN8tf_exl3x14dequant_kernelILi2ELi{d}EEEvPKjP6__halfii", .{k2});
+}
+
+const up_sym = struct {
+    const group = "_ZN15tf_exl3_experts12group_kernelEPKiPiS2_S2_iiii";
+    const rot_in_bf16 = "_ZN15tf_exl3_experts13rot_in_kernelI13__nv_bfloat16EEvPKT_iPKiPK6__halfS9_PS7_SA_iii";
+    const rot_in_f16 = "_ZN15tf_exl3_experts13rot_in_kernelI6__halfEEvPKT_iPKiPKS1_S8_PS1_S9_iii";
+    const gateup_epilogue = "_ZN15tf_exl3_experts22gateup_epilogue_kernelEPKfPKiPK6__halfS6_S6_PS4_iiiifi";
+    const down_epilogue = "_ZN15tf_exl3_experts20down_epilogue_kernelEPKfPKiPK6__halfPfiiii";
+    const combine = "_ZN15tf_exl3_experts14combine_kernelEPKfS1_Pfii";
+    // templated on the output (R1: fp32, or bf16 rounded as stored = fp32 then .to(bf16))
+    const down_combine_f32 = "_ZN15tf_exl3_experts19down_combine_kernelIfEEvPKfPKiPK6__halfPfS2_PT_iiiii";
+    const down_combine_bf16 = "_ZN15tf_exl3_experts19down_combine_kernelI13__nv_bfloat16EEvPKfPKiPK6__halfPfS3_PT_iiiii";
+};
+
+pub const gm_k2s = gmk.gm_k2s;
+pub const gu_tiles = gmk.gu_tiles;
+pub const dn_tiles = gmk.dn_tiles;
+pub const gm_smem_max = gmk.gm_smem_max;
+pub const gmK2Index = gmk.gmK2Index;
+pub const GmShape = gmk.GmShape;
+pub const v2_gu = gmk.v2_gu;
+pub const gu2_cfg = gmk.gu2_cfg;
+pub const v2_dn = gmk.v2_dn;
+pub const gm2Shape = gmk.gm2Shape;
+pub const V3 = gmk.V3;
+pub const v3_variants = gmk.v3_variants;
+pub const gm3Symbol = gmk.gm3Symbol;
+pub const v3Of = gmk.v3Of;
+pub const gmV3 = gmk.gmV3;
+pub const v3_default = gmk.v3_default;
+pub const gmTuned2 = gmk.gmTuned2;
+pub const gm2WidthsOk = gmk.gm2WidthsOk;
+pub const gm2GuSymbol = gmk.gm2GuSymbol;
+pub const gm2DnSymbol = gmk.gm2DnSymbol;
+pub const gmSmemBytes = gmk.gmSmemBytes;
+pub const GmKind = gmk.GmKind;
+pub const plan_max_experts = gmk.plan_max_experts;
+pub const planPasses = gmk.planPasses;
+pub const gmFits = gmk.gmFits;
+pub const gmMembers = gmk.gmMembers;
+pub const TuneRow = gmk.TuneRow;
+pub const tune_default = gmk.tune_default;
+pub const gmTuned = gmk.gmTuned;
+pub const gmGuSymbol = gmk.gmGuSymbol;
+pub const gmDnSymbol = gmk.gmDnSymbol;
+pub const gmDequantSymbol = gmk.gmDequantSymbol;
+pub const GmArgs = gmk.GmArgs;
+pub const gmCheck = gmk.gmCheck;
+
+/// TF_DSV41_PF_TBO_GM_SMS (under TF_DSV41_PF_TBO): the SMs x3gm's persistent grid covers at most; 0: every SM
+pub var gm_sms_cap: usize = 0;
+
+const gm_sym = struct {
+    const rot_bf16_1 = "_ZN10dsv41_x3gm10rot_kernelI13__nv_bfloat16Li1EEEvPKT_iPKiPK6__halfS9_PS7_SA_iii";
+    const rot_bf16_2 = "_ZN10dsv41_x3gm10rot_kernelI13__nv_bfloat16Li2EEEvPKT_iPKiPK6__halfS9_PS7_SA_iii";
+    const rot_f16_1 = "_ZN10dsv41_x3gm10rot_kernelI6__halfLi1EEEvPKT_iPKiPKS1_S8_PS1_S9_iii";
+    const rot_f16_2 = "_ZN10dsv41_x3gm10rot_kernelI6__halfLi2EEEvPKT_iPKiPKS1_S8_PS1_S9_iii";
+};
+
+// -- Loaded functions -------------------------------------------------------------------------------------------
+
+pub const Gm = struct { f: cuda.Function, threads: u32, smem: u32, per_sm: u32 };
+
+pub const Functions = struct {
+    ld: [2][ld_cfgs.len][ranges.len]?cuda.Function, // [probe 0 / 3][cfg][range]
+    pf: [pf_cfgs.len][ranges.len]cuda.Function,
+    grouped: [grouped_cfgs.len][upstream_ranges.len]cuda.Function,
+    dequant: [15]cuda.Function, // K2 2..16
+    group: cuda.Function,
+    group_rot: cuda.Function,
+    rot_in: [2]cuda.Function, // bf16, fp16 input
+    gateup_epilogue: cuda.Function,
+    down_epilogue: cuda.Function,
+    combine: cuda.Function,
+    down_combine: [2]cuda.Function, // fp32, bf16 out
+    gm_gu: [gm_k2s.len][2][gu_tiles.len]Gm, // [k2][xm - 1][cfg]
+    gm_dn: [gm_k2s.len][dn_tiles.len]Gm,
+    gm_rot: [2][2]cuda.Function, // [bf16, fp16][mats - 1]
+    gm_dequant: [gm_k2s.len]cuda.Function,
+    gm_plan: cuda.Function,
+    gm2_gu: [2][v2_gu.len]Gm, // [xm - 1][cfg]
+    gm2_dn: Gm,
+    /// gm2_kernel at GU2 (128-member gate / up, two inputs): x3gm3.cu's instance (TF_DSV41_GM_GU2)
+    gm2_gu2: Gm,
+    gm3: [v3_variants.len]Gm,
+    epi: [3][epi_pds.len][ranges.len]cuda.Function, // [EpiKind][pd - 1][range]
+
+    /// Resolves every instance (a wrong name fails here, not on a rare path) and opts x3gm into its shared memory.
+    pub fn resolve(experts: cuda.Module, x3ld: cuda.Module, x3pf: cuda.Module, x3gm: cuda.Module, x3gm_plan: cuda.Module, x3gm3: cuda.Module, router_glue: cuda.Module, x3ld_epi: cuda.Module, optin: u32) !Functions {
+        var f: Functions = undefined;
+        inline for (ld_cfgs, 0..) |c, ci| inline for (ranges, 0..) |r, ri| {
+            f.ld[0][ci][ri] = try x3ld.function(ldSymbol(c[0], c[1], r[0], r[1], 0));
+            f.ld[1][ci][ri] = if (ci < 2) try x3ld.function(ldSymbol(c[0], c[1], r[0], r[1], 3)) else null;
+        };
+        inline for (pf_cfgs, 0..) |c, ci| inline for (ranges, 0..) |r, ri| {
+            f.pf[ci][ri] = try x3pf.function(pfSymbol(c[0], c[1], r[0], r[1]));
+        };
+        inline for (.{ EpiKind.gu, EpiKind.dn, EpiKind.dnb }, 0..) |kind, ki| inline for (epi_pds, 0..) |pd, pi| inline for (ranges, 0..) |r, ri| {
+            f.epi[ki][pi][ri] = try x3ld_epi.function(epiSymbol(kind, pd, r[0], r[1]));
+        };
+        inline for (grouped_cfgs, 0..) |c, ci| inline for (upstream_ranges, 0..) |r, ri| {
+            f.grouped[ci][ri] = try experts.function(groupedSymbol(c[0], c[1], c[2], r[0], r[1]));
+        };
+        inline for (0..15) |i| f.dequant[i] = try experts.function(dequantSymbol(i + 2));
+        f.group = try experts.function(up_sym.group);
+        f.group_rot = try router_glue.function("router_group_rot");
+        f.rot_in = .{ try experts.function(up_sym.rot_in_bf16), try experts.function(up_sym.rot_in_f16) };
+        f.gateup_epilogue = try experts.function(up_sym.gateup_epilogue);
+        f.down_epilogue = try experts.function(up_sym.down_epilogue);
+        f.combine = try experts.function(up_sym.combine);
+        f.down_combine = .{ try experts.function(up_sym.down_combine_f32), try experts.function(up_sym.down_combine_bf16) };
+        inline for (gm_k2s, 0..) |k2, ki| {
+            inline for (.{ 1, 2 }) |xm| inline for (0..gu_tiles.len) |c| {
+                f.gm_gu[ki][xm - 1][c] = try gmResolve(x3gm, gmGuSymbol(xm, k2, c), GmShape.of(2, xm, k2, gu_tiles[c]), optin);
+            };
+            inline for (0..dn_tiles.len) |c| f.gm_dn[ki][c] = try gmResolve(x3gm, gmDnSymbol(k2, c), GmShape.of(1, 1, k2, dn_tiles[c]), optin);
+            f.gm_dequant[ki] = try x3gm.function(gmDequantSymbol(k2));
+        }
+        inline for (.{ 1, 2 }) |xm| inline for (v2_gu, 0..) |c, ci| {
+            f.gm2_gu[xm - 1][ci] = try gmResolve(x3gm, gm2GuSymbol(xm, c), gm2Shape(2, xm, gu_tiles[c]), optin);
+        };
+        f.gm2_dn = try gmResolve(x3gm, gm2DnSymbol(0), gm2Shape(1, 1, dn_tiles[0]), optin);
+        f.gm2_gu2 = try gmResolve(x3gm3, gm2GuSymbol(2, gu2_cfg), gm2Shape(2, 2, gu_tiles[gu2_cfg]), optin);
+        inline for (v3_variants, 0..) |v, i| f.gm3[i] = try gmResolve(x3gm3, gm3Symbol(v), gm2Shape(v.mats, v.xm, v.t), optin);
+        f.gm_plan = try x3gm_plan.function("_ZN15dsv41_x3gm_plan11plan_kernelEPKiiiiPiS2_S2_S2_S2_");
+        f.gm_rot = .{ .{ try x3gm.function(gm_sym.rot_bf16_1), try x3gm.function(gm_sym.rot_bf16_2) }, .{ try x3gm.function(gm_sym.rot_f16_1), try x3gm.function(gm_sym.rot_f16_2) } };
+        return f;
+    }
+};
+
+/// Opts `f` into `bytes` of dynamic shared memory if it fits the opt-in limit; false refuses the instance.
+pub fn fitShared(f: cuda.Function, name: []const u8, bytes: u32, optin: u32) !bool {
+    const static: u32 = @intCast(try f.attribute(.shared_size_bytes));
+    if (static + bytes > optin) {
+        std.log.info("dsv41 kernel refused on this GPU ({d} + {d} B shared > {d}): {s}", .{ static, bytes, optin, name });
+        return false;
+    }
+    if (bytes > 0) try f.allowDynamicShared(bytes); // set whatever the size
+    return true;
+}
+
+/// x3gm.cu launch<>'s first call: the shared memory attribute, then the CTAs an SM it allows (0: does not fit).
+fn gmResolve(m: cuda.Module, name: [:0]const u8, shape: GmShape, optin: u32) !Gm {
+    const f = try m.function(name);
+    const ok = try fitShared(f, name, shape.smem, optin);
+    return .{ .f = f, .threads = shape.threads, .smem = shape.smem, .per_sm = if (ok) try f.occupancy(shape.threads, shape.smem) else 0 };
+}
+
+// -- Launches ---------------------------------------------------------------------------------------------------
+
+pub const Input = enum { bf16, f16 };
+
+pub fn int(x: usize) c_int {
+    return @intCast(x);
+}
+
+fn dim(x: usize) u32 {
+    return @intCast(x);
+}
+
+pub fn go(f: cuda.Function, s: cuda.Stream, grid: [3]usize, block: usize, shared: usize, pdl: bool, a: *cuda.Args) !void {
+    try cuda.launch.launch(f, .{ .grid = .{ .x = dim(grid[0]), .y = dim(grid[1]), .z = dim(grid[2]) }, .block = .{ .x = dim(block) }, .shared = dim(shared), .pdl = pdl }, s, a);
+}
+
+/// The arguments every grouped decode kernel (upstream's grouped_kernel, x3ld, x3pf) takes, plus the grid's sizes.
+pub const Grouped = struct {
+    x0: u64, // fp16 [P, K] rotated inputs of matrix 0 (and of matrix 1 in x1)
+    x1: u64,
+    tp0: u64, // int64 [E]: each expert's trellis words
+    tp1: u64,
+    k2_0: u64, // int32 [E]: each expert's K2
+    k2_1: u64,
+    uids: u64, // int32 [nexp]: distinct experts (group_kernel)
+    ucount: u64, // int32 [1]
+    members: u64, // int32 [nexp, maxm]: row * 32 + slot codes, -1 after the last
+    z: u64, // fp32 [mats, SK, P, N]
+    mats: usize,
+    K: usize,
+    N: usize,
+    P: usize,
+    SK: usize,
+    slots: usize,
+    maxm: usize, // members.size(1)
+    nexp: usize, // uids.size(0)
+    lo: u32, // the widths' range over the table
+    hi: u32,
+
+    fn add(g: Grouped, a: *cuda.Args) void {
+        for ([_]u64{ g.x0, g.x1, g.tp0, g.tp1, g.k2_0, g.k2_1, g.uids, g.ucount, g.members, g.z }) |v| a.add(v);
+        for ([_]usize{ g.K, g.N, g.P, g.SK, g.maxm, g.slots }) |v| a.add(int(v));
+    }
+};
+
+pub const Error = error{ Unsupported, Shape };
+
+/// dsv41_x3ld_grouped_cuda's checks: mul1, even splits, the ring depth a divisor of a warp's k steps.
+pub fn x3ldCheck(g: Grouped, nt: u32, pd: u32, probe: u32) Error!struct { cfg: usize, range: usize } {
+    const cfg = cfgIndex(2, &ld_cfgs, .{ nt, pd }) orelse return error.Unsupported;
+    if (probe != 0 and (probe != 3 or cfg >= 2)) return error.Unsupported;
+    const range = rangeIndex(g.lo, g.hi) orelse return error.Unsupported;
+    if (g.SK == 0 or g.K % (16 * g.SK * 4) != 0 or g.N % (16 * nt) != 0) return error.Shape;
+    const per_warp = g.K / (16 * g.SK * 4);
+    if (per_warp < pd or per_warp % pd != 0) return error.Shape;
+    return .{ .cfg = cfg, .range = range };
+}
+
+/// dsv41_x3pf_grouped_cuda's checks.
+pub fn x3pfCheck(g: Grouped, nt: u32, mtl: u32) Error!struct { cfg: usize, range: usize } {
+    const cfg = cfgIndex(2, &pf_cfgs, .{ nt, mtl }) orelse return error.Unsupported;
+    const range = rangeIndex(g.lo, g.hi) orelse return error.Unsupported;
+    if (g.SK == 0 or g.K % (16 * g.SK * 4) != 0 or g.N % (16 * nt) != 0) return error.Shape;
+    return .{ .cfg = cfg, .range = range };
+}
+
+/// x3ld_epi.cu's Epi (by value): the epilogue's operands, 0 where a kind does not use one.
+pub const Epi = extern struct {
+    pick: u64 = 0,
+    sv0: u64 = 0, // gate/up: svh_g; down: svh_d
+    sv1: u64 = 0, // gate/up: svh_u
+    sd: u64 = 0, // gate/up: suh_d
+    xd: u64 = 0, // gate/up: Xd
+    y: u64 = 0, // down: y
+    wts: u64 = 0, // down: the combine's weights
+    out: u64 = 0, // down: L.moe (fp32 or bf16)
+    ticket: u64 = 0, // int32, zero between launches
+    E: c_int = 0,
+    limit: f32 = 0,
+    act_mode: c_int = 0,
+};
+
+comptime {
+    std.debug.assert(@sizeOf(Epi) == 88 and @offsetOf(Epi, "E") == 72);
+}
+
+/// The ticket words a fused launch counts on (gate/up: expert x member tile x 128 columns; down: row x 128 columns).
+pub fn epiTicketWords(g: Grouped, kind: EpiKind) usize {
+    return if (kind == .gu) g.nexp * ((g.maxm + 15) / 16) * (g.N / 128) else (g.P / g.slots) * (g.N / 128);
+}
+
+/// TF_DSV41_X3LD_EPI's checks: x3ld's at nt 8 and pd 1 / 2, plus the fused tails' shapes (down: one split).
+pub fn x3ldEpiCheck(g: Grouped, kind: EpiKind, pd: u32) Error!struct { pd: usize, range: usize } {
+    const c = try x3ldCheck(g, 8, pd, 0);
+    const pi = std.mem.indexOfScalar(u32, &epi_pds, pd) orelse return error.Unsupported;
+    if (g.N % 128 != 0 or g.slots == 0 or g.slots > 32 or g.P % g.slots != 0) return error.Shape;
+    switch (kind) {
+        .gu => if (g.mats != 2) return error.Shape,
+        .dn, .dnb => if (g.mats != 1 or g.SK != 1) return error.Shape,
+    }
+    return .{ .pd = pi, .range = c.range };
+}
+
+pub const Ops = struct {
+    f: *const Functions,
+    s: cuda.Stream,
+    sms: usize,
+
+    /// x3ld (expert_loads.grouped): Z of every (mat, split, live member row, column), upstream's bits.
+    pub fn x3ld(o: Ops, g: Grouped, nt: u32, pd: u32, probe: u32, pdl: bool) !void {
+        const c = try x3ldCheck(g, nt, pd, probe);
+        const mt = (g.maxm + 15) / 16;
+        var a: cuda.Args = .{};
+        g.add(&a);
+        try go(o.f.ld[if (probe == 3) 1 else 0][c.cfg][c.range].?, o.s, .{ g.nexp, g.N / (16 * nt), g.mats * g.SK * mt }, 128, 0, pdl, &a);
+    }
+
+    /// TF_DSV41_X3LD_EPI: x3ld (nt 8) with the gateup_epilogue (`gu`) or down_combine (`dn` / `dnb`) in its tail.
+    pub fn x3ldEpi(o: Ops, g: Grouped, kind: EpiKind, pd: u32, epi: Epi) !void {
+        const c = try x3ldEpiCheck(g, kind, pd);
+        if (epi.ticket == 0 or epi.pick == 0) return error.Shape;
+        const mt = (g.maxm + 15) / 16;
+        var a: cuda.Args = .{};
+        g.add(&a);
+        a.add(epi);
+        try go(o.f.epi[@backingInt(kind)][c.pd][c.range], o.s, .{ g.nexp, g.N / 128, g.mats * g.SK * mt }, 128, 0, false, &a);
+    }
+
+    /// x3pf (expert_prefill.grouped): the same Z, MTL member tiles a program.
+    pub fn x3pf(o: Ops, g: Grouped, nt: u32, mtl: u32) !void {
+        const c = try x3pfCheck(g, nt, mtl);
+        const mg = (g.maxm + 16 * mtl - 1) / (16 * mtl);
+        var a: cuda.Args = .{};
+        g.add(&a);
+        try go(o.f.pf[c.cfg][c.range], o.s, .{ g.nexp, g.N / (16 * nt), g.mats * g.SK * mg }, 128, 0, false, &a);
+    }
+
+    /// Upstream's grouped kernel (exl3x_grouped_cuda at cb 2): the reference x3ld and x3pf keep the bits of.
+    pub fn grouped(o: Ops, g: Grouped, nt: u32, warps: u32, pf: u32) !void {
+        const cfg = cfgIndex(3, &grouped_cfgs, .{ nt, warps, pf }) orelse return error.Unsupported;
+        if (g.SK == 0 or g.K % (16 * g.SK * warps) != 0 or g.N % (16 * nt) != 0) return error.Shape;
+        const mt = (g.maxm + 15) / 16;
+        var a: cuda.Args = .{};
+        g.add(&a);
+        try go(o.f.grouped[cfg][upstreamRangeIndex(g.lo, g.hi)], o.s, .{ g.nexp, g.N / (16 * nt), g.mats * g.SK * mt }, warps * 32, 0, false, &a);
+    }
+
+    /// exl3x_group_cuda: distinct experts of `pick` [R, slots] in id order and their members, one 1,024-thread block.
+    pub fn group(o: Ops, pick: u64, uids: u64, ucount: u64, members: u64, R: usize, slots: usize, E: usize, maxm: usize) !void {
+        if (E > 1024 * 4 or slots > 32) return error.Shape;
+        const smem = R * slots * 4;
+        if (smem > 48 * 1024) return error.Shape; // the binding never opts in to more
+        var a: cuda.Args = .{};
+        for ([_]u64{ pick, uids, ucount, members }) |v| a.add(v);
+        for ([_]usize{ R, slots, E, maxm }) |v| a.add(int(v));
+        try go(o.f.group, o.s, .{ 1, 1, 1 }, 1024, smem, false, &a);
+    }
+
+    /// exl3x_rot_in_cuda: Xh = fp16((x * suh) H / sqrt 128) for gate and up of every routed slot.
+    pub fn rotIn(o: Ops, input: Input, x: u64, x_stride: usize, pick: u64, suh0: u64, suh1: u64, out0: u64, out1: u64, rows: usize, K: usize, slots: usize, E: usize) !void {
+        var a: cuda.Args = .{};
+        a.add(x);
+        a.add(int(x_stride));
+        for ([_]u64{ pick, suh0, suh1, out0, out1 }) |v| a.add(v);
+        for ([_]usize{ K, slots, E }) |v| a.add(int(v));
+        try go(o.f.rot_in[@backingInt(input)], o.s, .{ rows * slots, K / 128, 2 }, 32, 0, false, &a);
+    }
+
+    /// Independent grouping/rotation CTAs share a launch, with no inter-CTA handoff. Invalid picks leave Xh untouched.
+    pub fn groupRotIn(o: Ops, x: u64, x_stride: usize, pick: u64, suh0: u64, suh1: u64, out0: u64, out1: u64, uids: u64, ucount: u64, members: u64, rows: usize, K: usize, slots: usize, E: usize, maxm: usize) !void {
+        if (rows < 1 or rows > 64 or K != 5120 or slots < 1 or slots > 9 or E < 1 or E > 512 or x_stride < K or maxm < 1) return error.Shape;
+        var a: cuda.Args = .{};
+        a.add(x);
+        a.add(int(x_stride));
+        for ([_]u64{ pick, suh0, suh1, out0, out1, uids, ucount, members }) |v| a.add(v);
+        for ([_]usize{ rows, K, slots, E, maxm }) |v| a.add(int(v));
+        const tiles = rows * slots * (K / 128) * 2;
+        try go(o.f.group_rot, o.s, .{ 1 + (tiles + 31) / 32, 1, 1 }, 1024, 0, false, &a);
+    }
+
+    /// exl3x_gateup_epilogue_cuda: splits summed, rotated, * svh, SwiGLU (act_mode 0 bf16 roundings, 1 fp32), Xd.
+    pub fn gateupEpilogue(o: Ops, z: u64, pick: u64, svh_g: u64, svh_u: u64, suh_d: u64, xd: u64, rows: usize, P: usize, N: usize, SK: usize, slots: usize, E: usize, limit: f32, act_mode: c_int) !void {
+        var a: cuda.Args = .{};
+        for ([_]u64{ z, pick, svh_g, svh_u, suh_d, xd }) |v| a.add(v);
+        for ([_]usize{ P, N, SK, E }) |v| a.add(int(v));
+        a.add(limit);
+        a.add(act_mode);
+        try go(o.f.gateup_epilogue, o.s, .{ rows * slots, N / 128, 1 }, 32, 0, false, &a);
+    }
+
+    /// exl3x_down_epilogue_cuda: Y = (splits summed) H * svh_d, fp32.
+    pub fn downEpilogue(o: Ops, z: u64, pick: u64, svh_d: u64, y: u64, rows: usize, P: usize, D: usize, SK: usize, slots: usize, E: usize) !void {
+        var a: cuda.Args = .{};
+        for ([_]u64{ z, pick, svh_d, y }) |v| a.add(v);
+        for ([_]usize{ P, D, SK, E }) |v| a.add(int(v));
+        try go(o.f.down_epilogue, o.s, .{ rows * slots, D / 128, 1 }, 32, 0, false, &a);
+    }
+
+    /// exl3x_combine_cuda: out[r] = fma chain over slots in order of wts[r][k] * y[r * slots + k].
+    pub fn combine(o: Ops, y: u64, wts: u64, out: u64, rows: usize, D: usize, slots: usize) !void {
+        var a: cuda.Args = .{};
+        for ([_]u64{ y, wts, out }) |v| a.add(v);
+        for ([_]usize{ D, slots }) |v| a.add(int(v));
+        try go(o.f.combine, o.s, .{ rows, (D + 255) / 256, 1 }, 256, 0, false, &a);
+    }
+
+    /// exl3x_down_combine_cuda: down_epilogue then combine in one launch, same bits; `out_bf16`: bf16 out (RNE).
+    pub fn downCombine(o: Ops, z: u64, pick: u64, svh_d: u64, y: u64, wts: u64, out: u64, rows: usize, P: usize, D: usize, SK: usize, slots: usize, E: usize, out_bf16: bool) !void {
+        if (slots > 32) return error.Shape;
+        var a: cuda.Args = .{};
+        for ([_]u64{ z, pick, svh_d, y, wts, out }) |v| a.add(v);
+        for ([_]usize{ P, D, SK, E, slots }) |v| a.add(int(v));
+        try go(o.f.down_combine[@intFromBool(out_bf16)], o.s, .{ rows, D / 128, 1 }, 32 * slots, 0, false, &a);
+    }
+
+    /// exl3x_dequant_cuda at cb 2: W_q [K, N] fp16 through the grouped kernels' lane decode (tests).
+    pub fn dequant(o: Ops, t: u64, out: u64, K: usize, N: usize, k2: u32) !void {
+        if (k2 < 2 or k2 > 16) return error.Unsupported;
+        var a: cuda.Args = .{};
+        a.add(t);
+        a.add(out);
+        a.add(int(K));
+        a.add(int(N));
+        try go(o.f.dequant[k2 - 2], o.s, .{ K / 16, N / 16, 1 }, 32, 0, false, &a);
+    }
+
+    pub const gmRot = gmk.gmRot;
+    pub const gmGateup = gmk.gmGateup;
+    pub const gmDown = gmk.gmDown;
+    pub const gm2Gateup = gmk.gm2Gateup;
+    pub const gm2Down = gmk.gm2Down;
+    pub const gm3Gateup = gmk.gm3Gateup;
+    pub const gm3Down = gmk.gm3Down;
+    pub const gmPlan = gmk.gmPlan;
+    pub const gmDequant = gmk.gmDequant;
+
+    pub fn gmLaunch(o: Ops, g: Gm, args: GmArgs, ticket: bool) !void {
+        if (g.per_sm == 0) return error.Unsupported; // "the configuration does not fit an SM"
+        var b = args;
+        if (!ticket) b.ticket = 0;
+        var a: cuda.Args = .{};
+        a.add(b);
+        // TF_DSV41_PF_TBO_GM_SMS: persistent CTAs on at most that many SMs; which CTA runs an item changes no bit
+        const sms = if (gm_sms_cap > 0) @min(o.sms, gm_sms_cap) else o.sms;
+        try go(g.f, o.s, .{ sms * g.per_sm, 1, 1 }, g.threads, g.smem, false, &a);
+    }
+};
+
+test "ranges as dispatch_ranges and TF_RANGES pick them" {
+    try std.testing.expectEqual(@as(?usize, 0), rangeIndex(8, 8));
+    try std.testing.expectEqual(@as(?usize, 1), rangeIndex(4, 10));
+    try std.testing.expectEqual(@as(?usize, 1), rangeIndex(8, 10));
+    try std.testing.expectEqual(@as(?usize, 2), rangeIndex(4, 12));
+    try std.testing.expectEqual(@as(?usize, null), rangeIndex(4, 16));
+    try std.testing.expectEqual(@as(?usize, null), rangeIndex(1, 4));
+    try std.testing.expectEqual(@as(usize, 2), upstreamRangeIndex(4, 16));
+}
+
+test "x3ld checks mirror the binding's" {
+    const g: Grouped = .{ .x0 = 0, .x1 = 0, .tp0 = 0, .tp1 = 0, .k2_0 = 0, .k2_1 = 0, .uids = 0, .ucount = 0, .members = 0, .z = 0, .mats = 2, .K = 4096, .N = 1024, .P = 64, .SK = 1, .slots = 8, .maxm = 64, .nexp = 64, .lo = 4, .hi = 12 };
+    const c = try x3ldCheck(g, 8, 2, 0);
+    try std.testing.expectEqual(@as(usize, 1), c.cfg);
+    try std.testing.expectEqual(@as(usize, 2), c.range);
+    try std.testing.expectError(error.Unsupported, x3ldCheck(g, 4, 2, 3));
+    try std.testing.expectError(error.Unsupported, x3ldCheck(g, 4, 1, 0));
+    var bad = g;
+    bad.K = 4096 + 16; // not 16 x SK x 4 warps
+    try std.testing.expectError(error.Shape, x3ldCheck(bad, 8, 1, 0));
+    bad = g;
+    bad.K = 16 * 4 * 3; // three k steps a warp: not a multiple of pd 2
+    try std.testing.expectError(error.Shape, x3ldCheck(bad, 8, 2, 0));
+}
+
+test "x3gm v2 shapes and tuning" {
+    // gate/up cfg 0, one input: K2 10's ring cut from 3 to 2 stages keeps two CTAs an SM; the widest width sets SMEM
+    const s = gm2Shape(2, 1, gu_tiles[0]);
+    try std.testing.expectEqual(@as(u32, 256), s.threads);
+    try std.testing.expect(2 * (@as(usize, s.smem) + 1536) <= 102400);
+    try std.testing.expectEqual([2]usize{ 0, 0 }, gmTuned2(null, null, true));
+    try std.testing.expectEqual([2]usize{ 1, 0 }, gmTuned2(3, 2, false));
+    try std.testing.expect(gm2WidthsOk(&.{ 3, 10, 6 }));
+    try std.testing.expect(!gm2WidthsOk(&.{ 2, 6 }));
+}
+
+test "x3gm v2 at GU2 (TF_DSV41_GM_GU2, x3gm3.cu's instance): 512 threads, every width's ring in sm_121's opt-in memory" {
+    const s = gm2Shape(2, 2, gu_tiles[gu2_cfg]);
+    try std.testing.expectEqual(@as(u32, 512), s.threads);
+    try std.testing.expect(s.smem > 0 and s.smem + 4 * 8 * gu_tiles[gu2_cfg][1] + 4 <= gm_smem_max);
+    try std.testing.expectEqualStrings("_ZN10dsv41_x3gm10gm2_kernelILi2ELi2ELi1ELi16ELi2ELi4ELi4EEEvNS_4ArgsE", gm2GuSymbol(2, gu2_cfg));
+}
+
+test "x3gm shapes, fits and tuning as the reference tuner" {
+    // G6 gate/up cfg 0 at K2 6, one input: 3 x (64 x 64 x 2 + 2 x 4 x 8 x 24 x 4) = 49,152 + 4,608 ... (smem_bytes)
+    const s = GmShape.of(2, 1, 6, gu_tiles[0]);
+    try std.testing.expectEqual(@as(u32, 256), s.threads);
+    try std.testing.expectEqual(@as(u32, 3 * (64 * 64 * 2 + 2 * 4 * 8 * 24 * 4)), s.smem);
+    try std.testing.expect(gmFits(.gu, 0, 10, 1));
+    try std.testing.expect(!gmFits(.gu, 3, 10, 2));
+    try std.testing.expectEqual(@as(u32, 128), gmMembers(.dn, 2));
+    // 2,048 rows x 6 slots over 384 experts (32 members): G6's (0 / 1, 0)
+    try std.testing.expectEqual([2]usize{ 0, 0 }, gmTuned(&tune_default, null, null, 2048, 6, 384, true, 6, 6));
+    try std.testing.expectEqual([2]usize{ 1, 0 }, gmTuned(&tune_default, null, null, 2048, 6, 384, false, 6, 6));
+    // 4,096 rows (64 members): the 4K entry, cfg 3 / 2 when it fits the width
+    try std.testing.expectEqual([2]usize{ 3, 2 }, gmTuned(&tune_default, null, null, 4096, 6, 384, true, 6, 6));
+    try std.testing.expectEqual([2]usize{ 2, 2 }, gmTuned(&tune_default, null, null, 4096, 6, 384, false, 6, 6));
+    try std.testing.expectEqual([2]usize{ 5, 4 }, gmTuned(&tune_default, 5, 4, 4096, 6, 384, false, 6, 6));
+}
+
+test "x3ld_epi checks" {
+    const g: Grouped = .{ .x0 = 0, .x1 = 0, .tp0 = 0, .tp1 = 0, .k2_0 = 0, .k2_1 = 0, .uids = 0, .ucount = 0, .members = 0, .z = 0, .mats = 2, .K = 5120, .N = 1152, .P = 28, .SK = 4, .slots = 7, .maxm = 4, .nexp = 28, .lo = 4, .hi = 10 };
+    _ = try x3ldEpiCheck(g, .gu, 1);
+    try std.testing.expectError(error.Shape, x3ldEpiCheck(g, .dn, 1));
+    var d = g;
+    d.mats = 1;
+    d.K = 1152;
+    d.N = 5120;
+    d.SK = 1;
+    _ = try x3ldEpiCheck(d, .dnb, 1);
+    try std.testing.expectEqual(@as(usize, 4 * 40), epiTicketWords(d, .dn));
+    try std.testing.expectEqual(@as(usize, 28 * 1 * 9), epiTicketWords(g, .gu));
+    try std.testing.expectEqualStrings("dsv41_x3ld_epi_dnb_2_2_10", epiSymbol(.dnb, 2, 2, 10));
+}
diff --git a/zig/src/families/deepseek_v41/kernels_glue_ops.zig b/zig/src/families/deepseek_v41/kernels_glue_ops.zig
new file mode 100644
index 0000000000..76d1a1f6af
--- /dev/null
+++ b/zig/src/families/deepseek_v41/kernels_glue_ops.zig
@@ -0,0 +1,332 @@
+//! DeepSeek-V4.1 glue launches: the window's and the prefill segment's torch glue as kernels_ops.Ops methods.
+
+const std = @import("std");
+const cuda = @import("cuda");
+const ops = @import("kernels_ops.zig");
+
+const Ops = ops.Ops;
+const glue = ops.glue;
+const pfglue = ops.pfglue;
+
+pub fn glueGo(o: Ops, kind: glue.Kind, n: u64, args: *cuda.Args) !void {
+    try o.go(o.f.glue[@backingInt(kind)], .{ glue.grid(n), 1, 1 }, 256, 0, null, args);
+}
+
+/// Embedding send: rows [n, D] bf16 = embed[ids - vocab_lo] when in this rank's slice [0, V), else +0.
+pub fn embedSend(o: Ops, ids: u64, n: usize, embed: u64, vocab_lo: i64, V: usize, D: usize, rows: u64) !void {
+    if (n == 0) return;
+    var a: cuda.Args = .{};
+    a.add(ids);
+    a.add(@as(c_int, @intCast(n)));
+    a.add(embed);
+    a.add(vocab_lo);
+    a.add(@as(c_int, @intCast(V)));
+    a.add(@as(c_int, @intCast(D)));
+    a.add(rows);
+    try o.glueGo(.embed_send, @as(u64, n) * D, &a);
+}
+
+/// Embedding sum: recv [W, n, D] bf16 -> out [n, 4 D] bf16 (fp32 adds in rank order, one rounding).
+pub fn embedSum(o: Ops, recv: u64, W: usize, n: usize, D: usize, out: u64) !void {
+    if (n == 0) return;
+    if (W < 1) return error.Shape;
+    var a: cuda.Args = .{};
+    a.add(recv);
+    for ([_]usize{ W, n, D }) |v| a.add(@as(c_int, @intCast(v)));
+    a.add(out);
+    try o.glueGo(.embed_sum, @as(u64, n) * D, &a);
+}
+
+/// y bf16 = x.to(bf16) over n elements (the MoE partial into the exchange).
+pub fn castBf16(o: Ops, x: u64, y: u64, n: usize) !void {
+    if (n == 0) return;
+    var a: cuda.Args = .{};
+    a.add(x);
+    a.add(y);
+    a.add(@as(i64, @intCast(n)));
+    try o.glueGo(.cast_bf16, n, &a);
+}
+
+/// pick.kit_weights: y = x.to(fp16).to(fp32) over n elements (y may be x).
+pub fn kitWeights(o: Ops, x: u64, y: u64, n: usize) !void {
+    if (n == 0) return;
+    var a: cuda.Args = .{};
+    a.add(x);
+    a.add(y);
+    a.add(@as(i64, @intCast(n)));
+    try o.glueGo(.kit_weights, n, &a);
+}
+
+/// pick.kit_logits in place: x = x.to(bf16).to(fp32) over n elements.
+pub fn kitLogits(o: Ops, x: u64, n: usize) !void {
+    if (n == 0) return;
+    var a: cuda.Args = .{};
+    a.add(x);
+    a.add(@as(i64, @intCast(n)));
+    try o.glueGo(.kit_logits, n, &a);
+}
+
+/// Win.make's tensors: pos int32 [1] = start, pos64 int64 [n] = start + i, lo int32 [n] = 0.
+pub fn positions(o: Ops, start: i32, n: usize, pos: u64, pos64: u64, lo: u64) !void {
+    var a: cuda.Args = .{};
+    a.add(start);
+    a.add(@as(c_int, @intCast(n)));
+    a.add(pos);
+    a.add(pos64);
+    a.add(lo);
+    try o.go(o.f.glue[@backingInt(glue.Kind.positions)], .{ (@max(n, 1) + 255) / 256, 1, 1 }, 256, 0, null, &a);
+}
+
+/// DSpark's candidates (glue.cu ds_cands): each row's best k as packed [rows][2 k] fp32 (values, then ids).
+pub fn dsCandidates(o: Ops, lg: u64, ld: usize, rows: usize, V: usize, k: usize, id0: u32, out: u64) !void {
+    const f = o.f.ds_cands orelse return error.Unsupported;
+    if (k < 1 or k > glue.ds_cands_max or V < 1) return error.Shape;
+    if (rows == 0) return;
+    var a: cuda.Args = .{};
+    a.add(lg);
+    a.add(@as(i64, @intCast(ld)));
+    a.add(@as(i32, @intCast(V)));
+    a.add(@as(i32, @intCast(k)));
+    a.add(@as(i32, @intCast(id0)));
+    a.add(out);
+    try o.go(f, .{ rows, 1, 1 }, 1024, 0, null, &a);
+}
+
+/// DSpark's one-slot statics from the device pick `pick` into "w.ds.i64" / "w.ds.i32" at offsets `off`.
+pub fn dsStage(o: Ops, pick: u64, nw: usize, s64: u64, s32: u64, n: usize, W: usize, ring: usize, valid: u64, noise: u32, off: [6]i64) !void {
+    const f = o.f.ds_stage orelse return error.Unsupported;
+    var a: cuda.Args = .{};
+    a.add(pick);
+    a.add(@as(i32, @intCast(nw)));
+    a.add(s64);
+    a.add(s32);
+    a.add(@as(i32, @intCast(n)));
+    a.add(@as(i32, @intCast(W)));
+    a.add(@as(i32, @intCast(ring)));
+    a.add(@as(i64, @intCast(valid)));
+    a.add(@as(i32, @intCast(noise)));
+    for (off) |x| a.add(@as(i32, @intCast(x)));
+    try o.go(f, .{ 1, 1, 1 }, 256, 0, null, &a);
+}
+
+/// A row window's slots' (accepted, bonus, next position) into `acc` [k][3] from `picks`, `ids` and `segs`.
+pub fn dsAcceptRows(o: Ops, picks: u64, ids: u64, segs: u64, k: usize, acc: u64) !void {
+    const f = o.f.ds_accept_rows orelse return error.Unsupported;
+    if (k < 1 or k > 32) return error.Shape;
+    var a: cuda.Args = .{};
+    a.add(picks);
+    a.add(ids);
+    a.add(segs);
+    a.add(@as(i32, @intCast(k)));
+    a.add(acc);
+    try o.go(f, .{ 1, 1, 1 }, 32, 0, null, &a);
+}
+
+/// A several-slot pass's position-dependent statics for `g` members `mem` [g][3], from acc's bonus and position.
+pub fn dsStageRows(o: Ops, acc: u64, mem: u64, g: usize, s64: u64, s32: u64, n: usize, W: usize, ring: usize, noise: u32, off: [6]i64) !void {
+    const f = o.f.ds_stage_rows orelse return error.Unsupported;
+    var a: cuda.Args = .{};
+    a.add(acc);
+    a.add(mem);
+    a.add(@as(i32, @intCast(g)));
+    a.add(s64);
+    a.add(s32);
+    a.add(@as(i32, @intCast(n)));
+    a.add(@as(i32, @intCast(W)));
+    a.add(@as(i32, @intCast(ring)));
+    a.add(@as(i32, @intCast(noise)));
+    for (off) |x| a.add(@as(i32, @intCast(x)));
+    try o.go(f, .{ 1, 1, 1 }, 256, 0, null, &a);
+}
+
+/// vsample.choose over gathered candidates `g` for `n` rows: tokens into `out` int64 [n] (-1: a nucleus row).
+pub fn vsChoose(o: Ops, g: u64, W: usize, n: usize, k: usize, count: usize, seed: u64, pos0: u64, top_k: u32, temp: f64, top_p: f64, min_p: f64, out: u64) !void {
+    const f = o.f.vs_choose orelse return error.Unsupported;
+    if (W * k > glue.vs_max or n == 0) return error.Shape;
+    var a: cuda.Args = .{};
+    a.add(g);
+    a.add(@as(i32, @intCast(W)));
+    a.add(@as(i32, @intCast(n)));
+    a.add(@as(i32, @intCast(k)));
+    a.add(@as(i32, @intCast(count)));
+    a.add(seed);
+    a.add(@as(i64, @bitCast(pos0))); // the kernel draws at pos0 + r in int64: a pos0 below 0 (a row-shifted start) wraps back
+    a.add(@as(i32, @intCast(top_k)));
+    a.add(temp);
+    a.add(top_p);
+    a.add(min_p);
+    a.add(out);
+    try o.go(f, .{ n, 1, 1 }, 256, 0, null, &a);
+}
+
+/// bf16 [n] -> fp32 [n], exact (the DSpark pass's q before _rope).
+pub fn widenF32(o: Ops, src: u64, dst: u64, n: usize) !void {
+    if (n == 0) return;
+    var a: cuda.Args = .{};
+    a.add(src);
+    a.add(dst);
+    a.add(@as(i64, @intCast(n)));
+    try o.glueGo(.widen_f32, n, &a);
+}
+
+pub fn pfGo(o: Ops, kind: pfglue.Kind, n: u64, args: *cuda.Args) !void {
+    try o.go(o.f.pfglue[@backingInt(kind)], .{ glue.grid(n), 1, 1 }, 256, 0, null, args);
+}
+
+/// Each of R rows' `count` largest int64 keys as ascending positions, -1 padded: out int32 [R, count].
+pub fn topPositions(o: Ops, keys: u64, ld: usize, R: usize, n: usize, count: usize, out: u64) !void {
+    if (count < 1 or count > pfglue.max_count) return error.Shape;
+    if (R == 0) return;
+    var a: cuda.Args = .{};
+    a.add(keys);
+    a.add(@as(i64, @intCast(ld)));
+    a.add(@as(c_int, @intCast(n)));
+    a.add(@as(c_int, @intCast(count)));
+    a.add(out);
+    try o.go(o.f.pfglue[@backingInt(pfglue.Kind.top_positions)], .{ R, 1, 1 }, 1024, 0, null, &a);
+}
+
+/// index.candidate_keys: blocks int32 [R, nb] (row stride ld) -> out int32 [R, nb * bs].
+pub fn candKeys(o: Ops, blocks: u64, ld: usize, R: usize, nb: usize, bs: usize, out: u64) !void {
+    if (R * nb == 0) return;
+    var a: cuda.Args = .{};
+    a.add(blocks);
+    a.add(@as(i64, @intCast(ld)));
+    for ([_]usize{ R, nb, bs }) |v| a.add(@as(c_int, @intCast(v)));
+    a.add(out);
+    try o.pfGo(.cand_keys, R * nb * bs, &a);
+}
+
+/// out int32 [R] = #(sel >= 0 and sel < (pos + 1) / ratio), sel int32 [R, K], pos int64 [R].
+pub fn visibleCounts(o: Ops, sel: u64, K: usize, pos: u64, ratio: u32, R: usize, out: u64) !void {
+    if (R == 0) return;
+    if (ratio < 1) return error.Shape;
+    var a: cuda.Args = .{};
+    a.add(sel);
+    a.add(@as(c_int, @intCast(K)));
+    a.add(pos);
+    a.add(@as(c_int, @intCast(ratio)));
+    a.add(@as(c_int, @intCast(R)));
+    a.add(out);
+    try o.go(o.f.pfglue[@backingInt(pfglue.Kind.counts)], .{ (R + 7) / 8, 1, 1 }, 256, 0, null, &a);
+}
+
+/// Routed picks: pk int32 [n, routed] = pick[:, :routed], w6 fp32 = f32(f16(wts[:, :routed])) (shared slot after).
+pub fn gmPicks(o: Ops, pick: u64, wts: u64, ld: usize, routed: usize, n: usize, pk: u64, w6: u64) !void {
+    if (n * routed == 0) return;
+    var a: cuda.Args = .{};
+    a.add(pick);
+    a.add(wts);
+    for ([_]usize{ ld, routed, n }) |v| a.add(@as(c_int, @intCast(v)));
+    a.add(pk);
+    a.add(w6);
+    try o.pfGo(.gm_picks, n * routed, &a);
+}
+
+/// One width's picks: out[p] = k2tab[pick[p]] == k2 ? pick[p] : E (the input of gmPlan).
+pub fn widthMask(o: Ops, pick: u64, P: usize, k2tab: u64, E: usize, k2: u32, out: u64) !void {
+    if (P == 0) return;
+    var a: cuda.Args = .{};
+    a.add(pick);
+    a.add(@as(i64, @intCast(P)));
+    a.add(k2tab);
+    a.add(@as(c_int, @intCast(E)));
+    a.add(@as(c_int, @intCast(k2)));
+    a.add(out);
+    try o.pfGo(.width_mask, P, &a);
+}
+
+/// The indexer head weights' casts: y fp32 = f32(bf16(f32(x fp64))), n elements (glue step ix_w).
+pub fn f64Bf16F32(o: Ops, x: u64, y: u64, n: usize) !void {
+    if (n == 0) return;
+    var a: cuda.Args = .{};
+    a.add(x);
+    a.add(y);
+    a.add(@as(i64, @intCast(n)));
+    try o.pfGo(.f64_bf16_f32, n, &a);
+}
+
+/// blocks._projection's fp32: out [n, hd] = f32(kv), or (gate != 0, ratio 2) [n, 2 hd] = [f32(kv) | f32(gate)].
+pub fn widenCat(o: Ops, kv: u64, gate: u64, n: usize, hd: usize, out: u64) !void {
+    if (n == 0) return;
+    var a: cuda.Args = .{};
+    a.add(kv);
+    a.add(gate);
+    a.add(@as(c_int, @intCast(n)));
+    a.add(@as(c_int, @intCast(hd)));
+    a.add(out);
+    try o.pfGo(.widen_cat, n * hd * @as(usize, if (gate != 0) 2 else 1), &a);
+}
+
+/// Stage rows of one tensor: dst row p % dst_ring = src row p % src_ring for p in [lo, hi).
+pub fn ringCopy(o: Ops, src: u64, src_ring: usize, dst: u64, dst_ring: usize, row_bytes: usize, lo: u64, hi: u64) !void {
+    if (hi <= lo) return;
+    var a: cuda.Args = .{};
+    a.add(src);
+    a.add(@as(c_int, @intCast(src_ring)));
+    a.add(dst);
+    a.add(@as(c_int, @intCast(dst_ring)));
+    a.add(@as(i64, @intCast(row_bytes)));
+    a.add(@as(i64, @intCast(lo)));
+    a.add(@as(i64, @intCast(hi)));
+    try o.pfGo(.ring_copy, (hi - lo) * (row_bytes / 4 + 1), &a);
+}
+
+/// positions with `start` read from the device (int32 at `start_dev`): a graphed window's form, the same values.
+pub fn positionsDev(o: Ops, start_dev: u64, n: usize, pos: u64, pos64: u64, lo: u64) !void {
+    var a: cuda.Args = .{};
+    a.add(start_dev);
+    a.add(@as(c_int, @intCast(n)));
+    a.add(pos);
+    a.add(pos64);
+    a.add(lo);
+    try o.go(o.f.glue[@backingInt(glue.Kind.positions_dev)], .{ (@max(n, 1) + 255) / 256, 1, 1 }, 256, 0, null, &a);
+}
+
+/// forward.gather_cols after the all-gather: recv [W, n, c] -> out [n, W c] (2-byte elements, rank order).
+pub fn gatherCols(o: Ops, recv: u64, W: usize, n: usize, c: usize, out: u64) !void {
+    if (n == 0 or c == 0) return;
+    var a: cuda.Args = .{};
+    a.add(recv);
+    for ([_]usize{ W, n, c }) |v| a.add(@as(c_int, @intCast(v)));
+    a.add(out);
+    try o.glueGo(.gather_cols, @as(u64, W) * n * c, &a);
+}
+
+/// glue.cu pick_pack: f64 (value, column + id0) pairs [n][2] from topk_keys' k-1 vals fp32 [n] / cols int64 [n].
+pub fn pickPack(o: Ops, vals: u64, cols: u64, n: usize, id0: u64, pairs: u64) !void {
+    const f = o.f.pick_pack orelse return error.NoPickKernel;
+    var a: cuda.Args = .{};
+    a.add(vals);
+    a.add(cols);
+    a.add(@as(c_int, @intCast(n)));
+    a.add(@as(i64, @intCast(id0)));
+    a.add(pairs);
+    try o.go(f, .{ glue.grid(n), 1, 1 }, 256, 0, null, &a);
+}
+
+/// glue.cu pick_merge: out int64 [n + 3] = each row's pick over the ranks, then accepted rows, bonus, next start.
+pub fn pickMerge(o: Ops, all: u64, W: usize, n: usize, ids: u64, start: i64, out: u64) !void {
+    const f = o.f.pick_merge orelse return error.NoPickKernel;
+    if (n == 0 or n > glue.pick_max_rows) return error.Shape;
+    var a: cuda.Args = .{};
+    a.add(all);
+    a.add(@as(c_int, @intCast(W)));
+    a.add(@as(c_int, @intCast(n)));
+    a.add(ids);
+    a.add(start);
+    a.add(out);
+    try o.go(f, .{ 1, 1, 1 }, @intCast(@max(32, std.mem.alignForward(usize, n, 32))), 0, null, &a);
+}
+
+/// The compressor's carry: carry fp32 [cols] = bf16 row of src; row *accepted (device int32) if `accepted` != 0.
+pub fn carry(o: Ops, src: u64, ld: usize, accepted: u64, row: usize, cols: usize, out: u64) !void {
+    var a: cuda.Args = .{};
+    a.add(src);
+    a.add(@as(i64, @intCast(ld)));
+    a.add(accepted);
+    a.add(@as(c_int, @intCast(row)));
+    a.add(@as(c_int, @intCast(cols)));
+    a.add(out);
+    try o.glueGo(.carry, cols, &a);
+}
diff --git a/zig/src/families/deepseek_v41/kernels_kv_glue.zig b/zig/src/families/deepseek_v41/kernels_kv_glue.zig
new file mode 100644
index 0000000000..47b71369f8
--- /dev/null
+++ b/zig/src/families/deepseek_v41/kernels_kv_glue.zig
@@ -0,0 +1,46 @@
+//! KV-path RMS + SWA RoPE/FP8 store. This owns only the norm feeding the SWA producer.
+const std = @import("std");
+const cuda = @import("cuda");
+
+/// Mirrors kv_glue.cu; norm_out is an optional contiguous bf16 debug output, not used in production.
+pub const Args = extern struct {
+    x: u64 = 0,
+    w: u64 = 0,
+    cs: u64 = 0,
+    v: u64 = 0,
+    s: u64 = 0,
+    pos: u64 = 0,
+    sl: u64 = 0,
+    norm_out: u64 = 0,
+    x_stride: i64 = 512,
+    cs_stride: i64 = 64,
+    v_stride: i64 = 576,
+    s_stride: i64 = 8,
+    inv_k: f32 = 1.0 / 512.0,
+    eps: f32 = 1e-20,
+    ring: c_int = 256,
+    rows: c_int = 0,
+};
+comptime {
+    std.debug.assert(@sizeOf(Args) == 112 and @offsetOf(Args, "inv_k") == 96 and @offsetOf(Args, "ring") == 104);
+}
+
+pub const Functions = struct {
+    norm_store: cuda.Function,
+    pub fn resolve(m: cuda.Module) !Functions {
+        return .{ .norm_store = try m.function("kv_norm_store") };
+    }
+};
+
+pub const Ops = struct {
+    f: *const Functions,
+    stream: cuda.Stream,
+    pub fn normStore(o: Ops, a: Args, n: usize) !void {
+        if (n == 0 or n > 64 or a.x_stride < 512 or a.cs_stride < 64 or a.v_stride < 576 or a.s_stride < 8 or
+            a.ring <= 0 or (a.ring & (a.ring - 1)) != 0 or a.inv_k != 1.0 / 512.0 or
+            (a.rows != 0 and a.rows != 1) or (a.rows == 1 and a.sl == 0)) return error.Shape;
+        var args: cuda.Args = .{};
+        args.add(a);
+        try cuda.launch.launch(o.f.norm_store, .{ .grid = .{ .x = @intCast(n) }, .block = .{ .x = 128 } }, o.stream, &args);
+    }
+};
diff --git a/zig/src/families/deepseek_v41/kernels_lin2.zig b/zig/src/families/deepseek_v41/kernels_lin2.zig
new file mode 100644
index 0000000000..db68e18af7
--- /dev/null
+++ b/zig/src/families/deepseek_v41/kernels_lin2.zig
@@ -0,0 +1,106 @@
+//! TF_DSV41_LIN2 (lin2.cu): the dense linears' one-pass 17-32-row variants, their symbols, mode readers and functions.
+
+const std = @import("std");
+const cuda = @import("cuda");
+const dense = @import("kernels_dense.zig");
+
+const LinTable = dense.LinTable;
+const dense3_k2s = dense.dense3_k2s;
+const linear_k2s = dense.linear_k2s;
+
+// TF_DSV41_LIN2 (ours, lin2.cu): the 17-32-row programs of the dense linears in one pass over the weights
+fn lanes2Symbol(comptime k2: u32, comptime wk: u32, comptime mb: u32) [:0]const u8 {
+    return std.fmt.comptimePrint("_ZN10dsv41_lin213lanes2_kernelILi{d}ELi2ELi{d}ELi{d}EEEvN11dsv41_x3seg8LinTableEii", .{ k2, wk, mb });
+}
+fn lanescSymbol(comptime k2: u32, comptime wk: u32, comptime mb: u32, comptime t: u32) [:0]const u8 {
+    return std.fmt.comptimePrint("_ZN10dsv41_lin213lanesc_kernelILi{d}ELi2ELi{d}ELi{d}ELi{d}EEEvN11dsv41_x3seg8LinTableEii", .{ k2, wk, mb, t });
+}
+/// lanesc's instances in lin2.cu's order (WK, MINB, T): a 4-warp and an 8-warp CTA at T 1 and at T 2
+pub const lanesc_cfgs = [_][3]u32{ .{ 4, 3, 1 }, .{ 8, 1, 1 }, .{ 4, 2, 2 }, .{ 8, 1, 2 } };
+fn linear2Symbol(comptime k2: u32, comptime wk: u32, comptime pf: bool) [:0]const u8 {
+    return std.fmt.comptimePrint("_ZN10dsv41_lin214linear2_kernelILi{d}ELi2ELi{d}ELb{d}EEEvPK6__halfPKjxxS3_S3_PviPfPiiiiiPKi", .{ k2, wk, @intFromBool(pf) });
+}
+
+/// TF_DSV41_LIN2[_PF|_WK|_CLUSTER]: lin2.cu's one-pass 17-32-row variants of the dense linears; bad values refused.
+pub const Lin2Mode = struct { lanes: bool = false, linear: bool = false, pf: bool = true, wk4: bool = true, wk8: bool = true, cluster: bool = false };
+
+/// TF_DSV41_LIN2_CLUSTER's value: unset / empty / 0 off, 1 on, anything else refused.
+pub fn lin2Cluster(v: ?[]const u8) !bool {
+    const t = std.mem.trim(u8, v orelse "", " \t\r\n");
+    if (t.len == 0 or std.mem.eql(u8, t, "0")) return false;
+    if (std.mem.eql(u8, t, "1")) return true;
+    return error.BadLin2;
+}
+
+/// The split count (2-8) shared by every segment of a dense3 launch of at most 32 rows, else null.
+pub fn clusterSplits(tab: LinTable, m: usize) ?u32 {
+    if (m < 1 or m > 32 or tab.n < 1) return null;
+    const sk = tab.s[0].SK;
+    if (sk < 2 or sk > 8) return null;
+    for (tab.s[0..@intCast(tab.n)]) |x| if (x.SK != sk or @mod(x.p0, sk) != 0) return null;
+    return @intCast(sk);
+}
+
+pub fn lin2Mode(v: ?[]const u8, pf: ?[]const u8) !Lin2Mode {
+    return lin2ModeWk(v, pf, null);
+}
+
+pub fn lin2ModeWk(v: ?[]const u8, pf: ?[]const u8, wk: ?[]const u8) !Lin2Mode {
+    var m: Lin2Mode = .{};
+    const t = std.mem.trim(u8, v orelse "", " \t\r\n");
+    if (t.len == 0 or std.mem.eql(u8, t, "0") or std.ascii.eqlIgnoreCase(t, "off")) {
+        m = .{};
+    } else if (std.mem.eql(u8, t, "1") or std.ascii.eqlIgnoreCase(t, "on")) {
+        m = .{ .lanes = true, .linear = true };
+    } else if (std.ascii.eqlIgnoreCase(t, "lanes")) {
+        m = .{ .lanes = true };
+    } else if (std.ascii.eqlIgnoreCase(t, "linear")) {
+        m = .{ .linear = true };
+    } else return error.BadLin2;
+    const p = std.mem.trim(u8, pf orelse "", " \t\r\n");
+    if (p.len == 0 or std.mem.eql(u8, p, "1")) {
+        m.pf = true;
+    } else if (std.mem.eql(u8, p, "0")) {
+        m.pf = false;
+    } else return error.BadLin2;
+    const w = std.mem.trim(u8, wk orelse "", " \t\r\n");
+    if (w.len == 0) {
+        m.wk4 = true;
+        m.wk8 = true;
+    } else if (std.mem.eql(u8, w, "4")) {
+        m.wk8 = false;
+    } else if (std.mem.eql(u8, w, "8")) {
+        m.wk4 = false;
+    } else return error.BadLin2;
+    return m;
+}
+
+/// A launch of `m` rows the two-tile variant takes: the original's two passes (rows 17-32).
+pub fn twoTile(m: usize) bool {
+    return m > 16 and m <= 32;
+}
+
+/// lin2.cu's instances: lanes2 at dense3's widths (4- and 8-warp CTAs); linear2 with or without k-step prefetch.
+pub const Lin2 = struct {
+    module: cuda.Module,
+    mode: Lin2Mode,
+    lanes: [dense3_k2s.len][2]cuda.Function, // WK 4, 8
+    linear: [linear_k2s.len][3]cuda.Function, // WK 2, 4, 8
+    /// TF_DSV41_LIN2_CLUSTER: lanesc at lanesc_cfgs (resolved only with the knob: an older lin2 fatbin still loads)
+    lanesc: [dense3_k2s.len][lanesc_cfgs.len]cuda.Function = undefined,
+
+    pub fn resolve(module: cuda.Module, mode: Lin2Mode) !Lin2 {
+        var l: Lin2 = .{ .module = module, .mode = mode, .lanes = undefined, .linear = undefined };
+        inline for (dense3_k2s, 0..) |k2, i| {
+            l.lanes[i][0] = try module.function(lanes2Symbol(k2, 4, 2));
+            l.lanes[i][1] = try module.function(lanes2Symbol(k2, 8, 1));
+        }
+        inline for (linear_k2s, 0..) |k2, i| inline for (.{ 2, 4, 8 }, 0..) |wk, j| {
+            l.linear[i][j] = try module.function(if (mode.pf) linear2Symbol(k2, wk, true) else linear2Symbol(k2, wk, false));
+        };
+        if (mode.cluster) inline for (dense3_k2s, 0..) |k2, i| inline for (lanesc_cfgs, 0..) |c, j| {
+            l.lanesc[i][j] = try module.function(lanescSymbol(k2, c[0], c[1], c[2]));
+        };
+        return l;
+    }
+};
diff --git a/zig/src/families/deepseek_v41/kernels_ops.zig b/zig/src/families/deepseek_v41/kernels_ops.zig
new file mode 100644
index 0000000000..b2dff0b03a
--- /dev/null
+++ b/zig/src/families/deepseek_v41/kernels_ops.zig
@@ -0,0 +1,590 @@
+//! DeepSeek-V4.1's other CUDA kernels (attention, top-k, mHC, router, prefill GEMM, glue): args and launches.
+
+const std = @import("std");
+const cuda = @import("cuda");
+const shapes = @import("kernels_shapes.zig");
+const glue_ops = @import("kernels_glue_ops.zig");
+const fitShared = @import("kernels_exl3.zig").fitShared;
+
+fn dim(x: usize) u32 {
+    return @intCast(x);
+}
+
+pub const Error = shapes.Error;
+pub const attn = shapes.attn;
+pub const topk = shapes.topk;
+pub const mhc = shapes.mhc;
+pub const mhc_pf = shapes.mhc_pf;
+pub const rg = shapes.rg;
+pub const pfd = shapes.pfd;
+
+// -- Loaded functions and launches ------------------------------------------------------------------------------
+
+pub const Functions = struct {
+    attn: [8]cuda.Function,
+    attn_split: [2][8]cuda.Function, // [P 1, P 4][instance]
+    topk: cuda.Function,
+    /// topk_b.cu's row-bounded twin (tf_dsv41_topk_b_v1.topk)
+    topk_b: cuda.Function,
+    mhc: [2][mhc.instances.len]cuda.Function, // [tail][instance]
+    mhc_coef: cuda.Function,
+    mhc_pf: [mhc_pf.instances.len]cuda.Function,
+    gemv: [rg.rts.len][rg.wide.len]cuda.Function,
+    narrow: [rg.rts.len][rg.narrow.len]cuda.Function,
+    pfd: [pfd.k2s.len][2][pfd.cfgs.len]?cuda.Function, // [k2][lanes][cfg]; K2 16 has no lanes kernels
+    pfd_dequant: [pfd.k2s.len][2]?cuda.Function,
+    gate_copy: cuda.Function,
+    paced: cuda.Function,
+    segments: [2]cuda.Function, // bulk false / true
+    topk_keys: cuda.Function,
+    swiglu: cuda.Function,
+    add: cuda.Function,
+    add_bf16: cuda.Function,
+    kv_dense: cuda.Function,
+    /// kvsplit's compact pack (TF_DSV41_KV_SPLIT_COMPACT); null in a fatbin built before it
+    kv_pack: ?cuda.Function = null,
+    /// kvsplit's dedup steps (TF_DSV41_KV_SPLIT_DEDUP: mark, flags, pack, tok); null in a fatbin built before them
+    kv_dedup: ?[4]cuda.Function = null,
+    kv_hist: ?[2]cuda.Function = null,
+    kv_gather: cuda.Function,
+    glue: [10]cuda.Function, // glue.syms order
+    /// glue.cu's DSpark candidates (TF_DSV41_DRAFT_GRAPHS); null in a fatbin built before it (the host's candidates)
+    ds_cands: ?cuda.Function = null,
+    /// glue.cu's device pick (TF_DSV41_GREEDY_GPU); null in a fatbin built before it (the pairs merged on the host)
+    pick_pack: ?cuda.Function = null,
+    pick_merge: ?cuda.Function = null,
+    /// glue.cu's DSpark statics from the device pick (the speculative pass's device start); optional, as ds_cands
+    ds_stage: ?cuda.Function = null,
+    /// glue.cu's row-window accept and several-slot statics (the slot passes' device start); optional
+    ds_accept_rows: ?cuda.Function = null,
+    ds_stage_rows: ?cuda.Function = null,
+    /// glue.cu's device keyed choice (vsample.choose; the sampled rounds' device start); optional
+    vs_choose: ?cuda.Function = null,
+    pfglue: [pfglue.syms.len]cuda.Function, // pfglue.syms order
+
+    /// `pf`: the four pfdense modules (K2 8, 10, 12, 16).
+    pub fn resolve(m_attn: cuda.Module, m_topk: cuda.Module, m_mhc: cuda.Module, m_mhc_pf: cuda.Module, m_rg: cuda.Module, pf: [4]cuda.Module, m_gate: cuda.Module, m_pace: cuda.Module, m_l2pf: cuda.Module, m_keys: cuda.Module, m_pw: cuda.Module, m_kv: cuda.Module, m_glue: cuda.Module, m_pfglue: cuda.Module, m_topk_b: cuda.Module, optin: u32) !Functions {
+        var f: Functions = undefined;
+        inline for (0..8) |i| f.attn[i] = try m_attn.function(attn.symbol(if (i >= 4) 4 else 2, (i & 2) != 0, (i & 1) != 0));
+        inline for (f.attn, 0..) |fun, i| {
+            if (!try fitShared(fun, attn.symbol(if (i >= 4) 4 else 2, (i & 2) != 0, (i & 1) != 0), attn.SMEM, optin)) return error.SharedMemoryTooSmall;
+        }
+        inline for (.{ 1, 4 }, 0..) |p, pi| inline for (0..8) |i| {
+            const name = attn.splitSymbol(if (i >= 4) 4 else 2, (i & 2) != 0, (i & 1) != 0, p);
+            f.attn_split[pi][i] = try m_attn.function(name);
+            if (!try fitShared(f.attn_split[pi][i], name, attn.splitSmem(p), optin)) return error.SharedMemoryTooSmall;
+        };
+        f.topk = try m_topk.function(topk.symbol);
+        if (!try fitShared(f.topk, topk.symbol, topk.smem(topk.MAXEPT), optin)) return error.SharedMemoryTooSmall;
+        f.topk_b = try m_topk_b.function(topk.symbol_b);
+        if (!try fitShared(f.topk_b, topk.symbol_b, topk.smem(topk.MAXEPT), optin)) return error.SharedMemoryTooSmall;
+        inline for (.{ false, true }, 0..) |tail, ti| inline for (0..mhc.instances.len) |i| {
+            f.mhc[ti][i] = try m_mhc.function(mhc.symbol(i, tail));
+        };
+        f.mhc_coef = try m_mhc.function(mhc.coef_symbol);
+        inline for (0..mhc_pf.instances.len) |i| {
+            f.mhc_pf[i] = try m_mhc_pf.function(mhc_pf.symbol(i));
+            if (!try fitShared(f.mhc_pf[i], mhc_pf.symbol(i), mhc_pf.smem(mhc_pf.instances[i].fn32), optin)) return error.SharedMemoryTooSmall;
+        }
+        inline for (rg.rts, 0..) |rt, i| {
+            inline for (rg.wide, 0..) |c, j| f.gemv[i][j] = try m_rg.function(rg.gemvSymbol(rt, c[0], c[1]));
+            inline for (rg.narrow, 0..) |nw, j| f.narrow[i][j] = try m_rg.function(rg.narrowSymbol(rt, nw));
+        }
+        inline for (pfd.k2s, 0..) |k2, ki| inline for (.{ false, true }, 0..) |lanes, li| {
+            inline for (0..pfd.cfgs.len) |c| {
+                if (lanes and k2 == 16) {
+                    f.pfd[ki][li][c] = null;
+                } else {
+                    const fun = try pf[ki].function(pfd.symbol(k2, lanes, c));
+                    f.pfd[ki][li][c] = if (try fitShared(fun, pfd.symbol(k2, lanes, c), pfd.smem(c, k2), optin)) fun else null;
+                }
+            }
+            f.pfd_dequant[ki][li] = if (lanes and k2 == 16) null else try pf[ki].function(pfd.dequantSymbol(k2, lanes));
+        };
+        f.gate_copy = try m_gate.function("_ZN17dsv41_engram_gate16gate_copy_kernelEPxiiPKhxPhxxi");
+        f.paced = try m_pace.function("_ZN12dsv41_l2pace12paced_kernelEPKlijmm");
+        f.segments = .{ try m_l2pf.function("_ZN6tfl2pf15segments_kernelILb0EEEvPKlij"), try m_l2pf.function("_ZN6tfl2pf15segments_kernelILb1EEEvPKlij") };
+        f.topk_keys = try m_keys.function("_ZN15dsv41_topk_keys11topk_kernelEPKfxiiPfPx");
+        f.swiglu = try m_pw.function("_ZN15dsv41_pointwise13swiglu_kernelEPKfxS1_xPfxif");
+        f.add = try m_pw.function("_ZN15dsv41_pointwise10add_kernelEPfPKfx");
+        f.add_bf16 = try m_pw.function("_ZN15dsv41_pointwise15add_bf16_kernelEPKfS1_P13__nv_bfloat16x");
+        f.kv_dense = try m_kv.function("_ZN13dsv41_kvsplit12dense_kernelENS_9DenseArgsE");
+        f.kv_gather = try m_kv.function("_ZN13dsv41_kvsplit13gather_kernelEPKhjPKjjPh");
+        f.kv_pack = m_kv.function("_ZN13dsv41_kvsplit11pack_kernelENS_8PackArgsE") catch null;
+        f.kv_dedup = kvsplit.dedupFns(m_kv) catch null;
+        f.kv_hist = kvsplit.histFns(m_kv) catch null;
+        inline for (glue.syms, 0..) |name, i| f.glue[i] = try m_glue.function(name);
+        f.ds_cands = m_glue.function(glue.ds_cands_sym) catch null;
+        f.pick_pack = m_glue.function(glue.pick_pack_sym) catch null;
+        f.pick_merge = m_glue.function(glue.pick_merge_sym) catch null;
+        f.ds_stage = m_glue.function(glue.ds_stage_sym) catch null;
+        f.ds_accept_rows = m_glue.function(glue.ds_accept_rows_sym) catch null;
+        f.ds_stage_rows = m_glue.function(glue.ds_stage_rows_sym) catch null;
+        f.vs_choose = m_glue.function(glue.vs_choose_sym) catch null;
+        inline for (pfglue.syms, 0..) |name, i| f.pfglue[i] = try m_pfglue.function(name);
+        return f;
+    }
+};
+
+/// kvsplit.cu: split KV's exchange copies (kv/split.zig's DenseArgs, the same layout; HostKernels the reference).
+pub const kvsplit = struct {
+    pub const DenseArgs = extern struct {
+        sel: u64,
+        rows: u32,
+        k: u32,
+        table: u64,
+        pts: u32,
+        rslot: u64,
+        psh: u32,
+        base: u64,
+        row_bytes: u32,
+        world: u32,
+        send: u64,
+        tok: u64,
+    };
+
+    /// kv/split.zig's PackArgs (DenseArgs' fields at the same offsets, rank in psh's padding, lens after tok).
+    pub const PackArgs = extern struct {
+        sel: u64,
+        rows: u32,
+        k: u32,
+        table: u64,
+        pts: u32,
+        rslot: u64,
+        psh: u32,
+        rank: u32,
+        base: u64,
+        row_bytes: u32,
+        world: u32,
+        send: u64,
+        tok: u64,
+        lens: u64,
+    };
+
+    /// kv/split.zig's DedupArgs: PackArgs, then the map and the scratch.
+    pub const DedupArgs = extern struct {
+        p: PackArgs,
+        map: u64,
+        first: u64,
+        place: u64,
+        bcnt: u64,
+    };
+
+    const dedup_syms = [4][:0]const u8{
+        "_ZN13dsv41_kvsplit17dedup_mark_kernelENS_9DedupArgsE",
+        "_ZN13dsv41_kvsplit18dedup_flags_kernelENS_9DedupArgsE",
+        "_ZN13dsv41_kvsplit17dedup_pack_kernelENS_9DedupArgsE",
+        "_ZN13dsv41_kvsplit16dedup_tok_kernelENS_9DedupArgsE",
+    };
+
+    fn dedupFns(m: cuda.Module) ![4]cuda.Function {
+        var out: [4]cuda.Function = undefined;
+        for (dedup_syms, &out) |name, *f| f.* = try m.function(name);
+        return out;
+    }
+
+    /// kv/split.zig's HistArgs (the history union's gather and tokens).
+    pub const HistArgs = extern struct {
+        sel: u64,
+        rows: u32,
+        k: u32,
+        table: u64,
+        pages: u32,
+        psh: u32,
+        rank: u32,
+        world: u32,
+        m: u32,
+        row_bytes: u32,
+        base: u64,
+        send: u64,
+        tok: u64,
+    };
+
+    const hist_syms = [2][:0]const u8{ "_ZN13dsv41_kvsplit18hist_gather_kernelENS_8HistArgsE", "_ZN13dsv41_kvsplit15hist_tok_kernelENS_8HistArgsE" };
+
+    fn histFns(m: cuda.Module) ![2]cuda.Function {
+        return .{ try m.function(hist_syms[0]), try m.function(hist_syms[1]) };
+    }
+
+    comptime {
+        std.debug.assert(@sizeOf(DedupArgs) == 120 and @offsetOf(DedupArgs, "map") == 88 and @offsetOf(DedupArgs, "bcnt") == 112);
+        std.debug.assert(@sizeOf(HistArgs) == 72 and @offsetOf(HistArgs, "m") == 40 and @offsetOf(HistArgs, "base") == 48);
+        std.debug.assert(@sizeOf(DenseArgs) == 80 and @offsetOf(DenseArgs, "rslot") == 32 and @offsetOf(DenseArgs, "send") == 64);
+        std.debug.assert(@sizeOf(PackArgs) == 88 and @offsetOf(PackArgs, "rank") == 44 and @offsetOf(PackArgs, "lens") == 80);
+    }
+
+    fn grid(words: u64) usize {
+        return @intCast(@max(1, @min((words + 255) / 256, 4096)));
+    }
+};
+
+/// glue.cu: the window's torch glue, torch's arithmetic bit for bit.
+pub const glue = struct {
+    pub const syms = [_][:0]const u8{
+        "_ZN10dsv41_glue17embed_send_kernelEPKxiPKtxiiPt",
+        "_ZN10dsv41_glue16embed_sum_kernelEPKtiiiPt",
+        "_ZN10dsv41_glue16cast_bf16_kernelEPKfPtx",
+        "_ZN10dsv41_glue18kit_weights_kernelEPKfPfx",
+        "_ZN10dsv41_glue17kit_logits_kernelEPfx",
+        "_ZN10dsv41_glue16positions_kernelEiiPiPxS0_",
+        "_ZN10dsv41_glue18gather_cols_kernelEPKtiiiPt",
+        "_ZN10dsv41_glue12carry_kernelEPKtxPKiiiPf",
+        "_ZN10dsv41_glue20positions_dev_kernelEPKiiPiPxS2_",
+        "_ZN10dsv41_glue16widen_f32_kernelEPKtPfx",
+    };
+    pub const Kind = enum(u4) { embed_send, embed_sum, cast_bf16, kit_weights, kit_logits, positions, gather_cols, carry, positions_dev, widen_f32 };
+    /// DSpark's candidates (optional: resolved apart, so an older fatbin still loads)
+    pub const ds_cands_sym: [:0]const u8 = "_ZN10dsv41_glue15ds_cands_kernelEPKfxiiiPf";
+    /// the most candidates a rank (the kernel's shared sort)
+    pub const ds_cands_max = 128;
+    /// DSpark's statics from the device pick (optional, as ds_cands)
+    pub const ds_stage_sym: [:0]const u8 = "_ZN10dsv41_glue15ds_stage_kernelEPKxiPxPiiiixiiiiiii";
+    pub const ds_accept_rows_sym: [:0]const u8 = "_ZN10dsv41_glue21ds_accept_rows_kernelEPKxS1_S1_iPx";
+    pub const ds_stage_rows_sym: [:0]const u8 = "_ZN10dsv41_glue20ds_stage_rows_kernelEPKxS1_iPxPiiiiiiiiiii";
+    pub const vs_choose_sym: [:0]const u8 = "_ZN10dsv41_glue16vs_choose_kernelEPKfiiiiyxidddPx";
+    /// the candidates vs_choose merges at most (W k)
+    pub const vs_max = 2048;
+    /// the device pick (optional, as ds_cands)
+    pub const pick_pack_sym: [:0]const u8 = "_ZN10dsv41_glue16pick_pack_kernelEPKfPKxixPd";
+    pub const pick_merge_sym: [:0]const u8 = "_ZN10dsv41_glue17pick_merge_kernelEPKdiiPKxxPx";
+    /// the most rows pick_merge takes (one block)
+    pub const pick_max_rows = 1024;
+
+    pub fn grid(n: u64) usize {
+        return @intCast(@max(1, @min((n + 255) / 256, 4096)));
+    }
+};
+
+/// prefill_glue.cu: the prefill segment's torch glue (block_prefill.zig's glue steps), torch's integers and casts.
+pub const pfglue = struct {
+    pub const syms = [_][:0]const u8{
+        "_ZN12dsv41_pfglue20top_positions_kernelEPKxxiiPi",
+        "_ZN12dsv41_pfglue16cand_keys_kernelEPKixiiiPi",
+        "_ZN12dsv41_pfglue13counts_kernelEPKiiPKxiiPi",
+        "_ZN12dsv41_pfglue15gm_picks_kernelEPKiPKfiiiPiPf",
+        "_ZN12dsv41_pfglue17width_mask_kernelEPKixS1_iiPi",
+        "_ZN12dsv41_pfglue19f64_bf16_f32_kernelEPKdPfx",
+        "_ZN12dsv41_pfglue16widen_cat_kernelEPKtS1_iiPf",
+        "_ZN12dsv41_pfglue16ring_copy_kernelEPKhiPhixxx",
+    };
+    pub const Kind = enum(u3) { top_positions, cand_keys, counts, gm_picks, width_mask, f64_bf16_f32, widen_cat, ring_copy };
+    /// index.top_positions' largest `count` (TOPK 512, TOPK_BLOCKS 2,048)
+    pub const max_count = 2048;
+};
+
+/// topk_keys.cu: pick.top's bounds (C columns a row within pick.COLS, k within the shared sort).
+pub const topk_keys = struct {
+    pub const max_cols = 1 << 16;
+    pub const max_k = 1024;
+};
+
+pub const Ops = struct {
+    f: *const Functions,
+    s: cuda.Stream,
+
+    pub fn go(o: Ops, f: cuda.Function, grid: [3]usize, block: usize, shared: u32, cluster: ?cuda.Dim3, a: *cuda.Args) !void {
+        try cuda.launch.launch(f, .{ .grid = .{ .x = dim(grid[0]), .y = dim(grid[1]), .z = dim(grid[2]) }, .block = .{ .x = dim(block) }, .shared = shared, .cluster = cluster }, o.s, a);
+    }
+
+    /// dsv41_attn_run_cuda: `rows` slots table given, `has_hi` window ends given, `kw` Triton k width, `split` R1 CTAs.
+    pub fn attention(o: Ops, a: attn.Args, kw: u32, rows: bool, has_hi: bool, split: u32) !void {
+        const i = try attn.instance(a, kw, rows, has_hi);
+        if (a.qrope != 0 and split == 0) return error.Unsupported; // "q's RoPE folds into the split kernel only"
+        var args: cuda.Args = .{};
+        args.add(a);
+        const hb: usize = @intCast(@divTrunc(a.H, attn.BMQ));
+        const R: usize = @intCast(a.R);
+        switch (split) {
+            0 => try o.go(o.f.attn[i], .{ attn.NCH, hb, R }, attn.NT, attn.SMEM, null, &args),
+            1 => try o.go(o.f.attn_split[0][i], .{ attn.NCH, hb, R }, attn.NT, attn.splitSmem(1), null, &args),
+            4 => try o.go(o.f.attn_split[1][i], .{ attn.NCH * 4, hb, R }, attn.NT, attn.splitSmem(4), .{ .x = 4 }, &args),
+            else => return error.Unsupported,
+        }
+    }
+
+    /// dsv41_topk_run_cuda: `jobs` 1-2 selections, R rows, CL CTAs a cluster, ept entries a thread.
+    pub fn topK(o: Ops, a: topk.Args, jobs: usize, R: usize, CL: usize, ept: usize) !void {
+        return o.topKOf(o.f.topk, a, jobs, R, CL, ept);
+    }
+
+    /// topk_b.cu (TF_DSV41_INDEX_BOUND): topK's launch of the row-bounded twin (the same args and bytes).
+    pub fn topKB(o: Ops, a: topk.Args, jobs: usize, R: usize, CL: usize, ept: usize) !void {
+        return o.topKOf(o.f.topk_b, a, jobs, R, CL, ept);
+    }
+
+    fn topKOf(o: Ops, f: cuda.Function, a: topk.Args, jobs: usize, R: usize, CL: usize, ept: usize) !void {
+        if (R < 1 or jobs < 1 or jobs > 2 or CL < 1 or CL > topk.MAXCL or ept < 1 or ept > topk.MAXEPT) return error.Shape;
+        var args: cuda.Args = .{};
+        args.add(a);
+        try o.go(f, .{ CL, R, jobs }, topk.NT, topk.smem(ept), .{ .x = dim(CL) }, &args);
+    }
+
+    /// dsv41_mhc_run_cuda (`fn32`: fn is fp32); `a.spin` >= 0 R1's parallel tail, `a.defer` 1 leaves coefs to mhcCoef.
+    pub fn mhcBoundary(o: Ops, a: mhc.Args, mode: u32, fn32: bool) !void {
+        const i = try mhc.instance(a, mode, fn32);
+        var b = a;
+        if (b.spin < 0) b.@"defer" = 0; // as the binding: defer only with the tail
+        var args: cuda.Args = .{};
+        args.add(b);
+        try o.go(o.f.mhc[@intFromBool(b.spin >= 0)][i], .{ mhc.NB, if (mhc.instances[i].all4) 1 else 4, 1 }, mhc.NT, @intCast(@as(u32, @intCast(a.R)) * mhc.ROWB), null, &args);
+    }
+
+    /// dsv41_mhc_coef_cuda (R1): the next site's coefficients of a deferred boundary, a CTA a row.
+    pub fn mhcCoef(o: Ops, a: mhc.Args) !void {
+        if (a.R < 1 or a.R > mhc.COEF_ROWS) return error.Shape;
+        var args: cuda.Args = .{};
+        args.add(a);
+        try o.go(o.f.mhc_coef, .{ @intCast(a.R), 1, 1 }, mhc.NT, 0, null, &args);
+    }
+
+    /// dsv41_mhc_pf_run_cuda: `tap` = a tap buffer is given.
+    pub fn mhcSite(o: Ops, a: mhc_pf.Args, mode: u32, fn32: bool, tap: bool) !void {
+        const i = try mhc_pf.instance(a, mode, fn32, tap);
+        var args: cuda.Args = .{};
+        args.add(a);
+        const R: usize = @intCast(a.R);
+        try o.go(o.f.mhc_pf[i], .{ mhc_pf.NB, (R + mhc_pf.BM - 1) / mhc_pf.BM, 1 }, mhc_pf.NT, mhc_pf.smem(mhc_pf.instances[i].fn32), null, &args);
+    }
+
+    /// dsv41_rg_route_cuda: (rt, nw, ew) a wide instance, or ew 0 the narrow kernel (default carveout).
+    pub fn route(o: Ops, a: rg.Args, rt: u32, nw: u32, ew: u32, lg_len: usize, cnt_len: usize) !void {
+        try rg.check(a, rt, nw, ew, lg_len, cnt_len);
+        if (a.R == 0) return;
+        const ri = std.mem.indexOfScalar(u32, &rg.rts, rt).?;
+        const E: usize = @intCast(a.E);
+        const R: usize = @intCast(a.R);
+        var args: cuda.Args = .{};
+        args.add(a);
+        if (ew == 0) {
+            const ni = std.mem.indexOfScalar(u32, &rg.narrow, nw).?;
+            return o.go(o.f.narrow[ri][ni], .{ (E + nw - 1) / nw, (R + rt - 1) / rt, 1 }, nw * 32, 0, null, &args);
+        }
+        var wi: usize = 0;
+        for (rg.wide, 0..) |c, j| if (c[0] == nw and c[1] == ew) {
+            wi = j;
+        };
+        try o.go(o.f.gemv[ri][wi], .{ (E + nw * ew - 1) / (nw * ew), (R + rt - 1) / rt, 1 }, nw * 32, 0, null, &args);
+    }
+
+    /// pfdense.cpp gemm: out [M, N] = xh [M, K] (rotated, fp16) through the trellis decoded in the kernel.
+    pub fn pfdGemm(o: Ops, a: pfd.Args, k2: u32, lanes: bool, cfg: usize, words: usize) !void {
+        try pfd.check(a, k2, lanes, cfg, words);
+        const ki = std.mem.indexOfScalar(u32, &pfd.k2s, k2).?;
+        const f = o.f.pfd[ki][@intFromBool(lanes)][cfg] orelse return error.Unsupported;
+        const bm = pfd.cfgs[cfg][0];
+        const M: usize = @intCast(a.M);
+        const N: usize = @intCast(a.N);
+        var args: cuda.Args = .{};
+        args.add(a);
+        try o.go(f, .{ ((M + bm - 1) / bm) * (N >> 7), 1, 1 }, pfd.threads(cfg), pfd.smem(cfg, k2), null, &args);
+    }
+
+    /// pfdense.cpp dequant: W_q [K, N] fp16 through the kernel's decode (tests).
+    pub fn pfdDequant(o: Ops, T: u64, stride_k: i64, stride_nb: i64, out: u64, K: usize, N: usize, k2: u32, lanes: bool) !void {
+        const ki = std.mem.indexOfScalar(u32, &pfd.k2s, k2) orelse return error.Unsupported;
+        const f = o.f.pfd_dequant[ki][@intFromBool(lanes)] orelse return error.Unsupported;
+        var args: cuda.Args = .{};
+        args.add(T);
+        args.add(stride_k);
+        args.add(stride_nb);
+        args.add(out);
+        args.add(@as(c_int, @intCast(N)));
+        try o.go(f, .{ K / 32, N / 128, 1 }, 128, 0, null, &args);
+    }
+
+    /// dsv41_engram_gate_copy: wait (bounded) for the host's ready word, then copy the selected buffer.
+    pub fn engramGate(o: Ops, ctl: u64, ready: c_int, err_slot: c_int, base: u64, stride: i64, dst: u64, nbytes: i64, timeout_ns: i64, vec: bool) !void {
+        const units: i64 = if (vec) (nbytes + 15) >> 4 else nbytes;
+        const blocks: usize = @intCast(std.math.clamp(@divTrunc(units + 255, 256), 1, 64));
+        var args: cuda.Args = .{};
+        args.add(ctl);
+        args.add(ready);
+        args.add(err_slot);
+        args.add(base);
+        args.add(stride);
+        args.add(dst);
+        args.add(nbytes);
+        args.add(timeout_ns);
+        args.add(@as(c_int, @intFromBool(vec)));
+        try o.go(o.f.gate_copy, .{ blocks, 1, 1 }, 256, 0, null, &args);
+    }
+
+    /// dsv41_l2pace launch_paced: (address, bytes) table prefetched to L2 at a paced rate on `ctas` warps.
+    pub fn l2Paced(o: Ops, table: u64, n: usize, ctas: usize, chunk: u32, ns_per_piece: u64, delay_ns: u64) !void {
+        var args: cuda.Args = .{};
+        args.add(table);
+        args.add(@as(c_int, @intCast(n)));
+        args.add(chunk);
+        args.add(ns_per_piece);
+        args.add(delay_ns);
+        try o.go(o.f.paced, .{ ctas, 1, 1 }, 32, 0, null, &args);
+    }
+
+    /// pick.top (topk_keys.cu): each row's best k of bf16-valued fp32 logits, value desc then column asc.
+    pub fn topKeys(o: Ops, lg: u64, ld: usize, rows: usize, C: usize, k: usize, vals: u64, cols: u64) !void {
+        if (rows < 1 or C < 1 or C > topk_keys.max_cols or k < 1 or k > @min(C, topk_keys.max_k)) return error.Shape;
+        var args: cuda.Args = .{};
+        args.add(lg);
+        args.add(@as(i64, @intCast(ld)));
+        args.add(@as(c_int, @intCast(C)));
+        args.add(@as(c_int, @intCast(k)));
+        args.add(vals);
+        args.add(cols);
+        try o.go(o.f.topk_keys, .{ rows, 1, 1 }, 1024, 0, null, &args);
+    }
+
+    /// The shared expert's pointwise part: act = silu(min(g, limit)) * clamp(u, +-limit) (fp32, strides in elements).
+    pub fn swiglu(o: Ops, g: u64, ldg: usize, u: u64, ldu: usize, act: u64, lda: usize, rows: usize, n: usize, limit: f32) !void {
+        var args: cuda.Args = .{};
+        args.add(g);
+        args.add(@as(i64, @intCast(ldg)));
+        args.add(u);
+        args.add(@as(i64, @intCast(ldu)));
+        args.add(act);
+        args.add(@as(i64, @intCast(lda)));
+        args.add(@as(c_int, @intCast(n)));
+        args.add(limit);
+        try o.go(o.f.swiglu, .{ @min((n + 255) / 256, 64), rows, 1 }, 256, 0, null, &args);
+    }
+
+    /// out += b over n fp32 elements (prefill_moe.forward: the routed partial plus the shared expert's).
+    pub fn addInto(o: Ops, out: u64, b: u64, n: usize) !void {
+        var args: cuda.Args = .{};
+        args.add(out);
+        args.add(b);
+        args.add(@as(i64, @intCast(n)));
+        try o.go(o.f.add, .{ @min((n + 255) / 256, 4096), 1, 1 }, 256, 0, null, &args);
+    }
+
+    /// y = bf16_rn(a + b) over n elements (torch.add(out, shared, out=bf16): TF_DSV41_PF_COPIES).
+    pub fn addBf16(o: Ops, a: u64, b: u64, y: u64, n: usize) !void {
+        var args: cuda.Args = .{};
+        args.add(a);
+        args.add(b);
+        args.add(y);
+        args.add(@as(i64, @intCast(n)));
+        try o.go(o.f.add_bf16, .{ @min((n + 255) / 256, 4096), 1, 1 }, 256, 0, null, &args);
+    }
+
+    /// kvsplit dense: a split family's selected rows packed for the all-gather (`a` @bitCast from kv/split.zig).
+    pub fn kvDense(o: Ops, a: kvsplit.DenseArgs) !void {
+        if (a.row_bytes == 0 or a.row_bytes % 8 != 0 or a.k == 0 or a.world == 0 or a.psh > 30) return error.Shape;
+        const words = @as(u64, a.rows) * a.k * (a.row_bytes / 8);
+        if (words == 0) return;
+        var args: cuda.Args = .{};
+        args.add(a);
+        try o.go(o.f.kv_dense, .{ kvsplit.grid(words), 1, 1 }, 256, 0, null, &args);
+    }
+
+    /// kvsplit pack (compact): this rank's owned selected rows first, lens[W] each owner's bytes; block 256.
+    pub fn kvPack(o: Ops, a: kvsplit.PackArgs) !void {
+        const f = o.f.kv_pack orelse return error.Unsupported;
+        if (a.row_bytes == 0 or a.row_bytes % 8 != 0 or a.k == 0 or a.world == 0 or a.world > 8 or a.psh > 30) return error.Shape;
+        const n = @as(u64, a.rows) * a.k;
+        if (n == 0) return;
+        var args: cuda.Args = .{};
+        args.add(a);
+        try o.go(f, .{ @intCast((n + 255) / 256), 1, 1 }, 256, 0, null, &args);
+    }
+
+    /// kvsplit dedup: the pack over each slot's unique rows (mark, flags, pack, tok: four launches of 256 threads).
+    pub fn kvDedup(o: Ops, d: kvsplit.DedupArgs) !void {
+        const fs = o.f.kv_dedup orelse return error.Unsupported;
+        const a = d.p;
+        if (a.row_bytes == 0 or a.row_bytes % 8 != 0 or a.k == 0 or a.world == 0 or a.world > 8 or a.psh > 30) return error.Shape;
+        const n = @as(u64, a.rows) * a.k;
+        if (n == 0) return;
+        const blocks: usize = @intCast((n + 255) / 256);
+        for (fs, [4]usize{ kvsplit.grid(n), blocks, blocks, kvsplit.grid(n) }) |f, g| {
+            var args: cuda.Args = .{};
+            args.add(d);
+            try o.go(f, .{ g, 1, 1 }, 256, 0, null, &args);
+        }
+    }
+
+    /// kvsplit hist_gather + hist_tok: the history union's M rows of this rank and the selection's tokens.
+    pub fn kvHistory(o: Ops, a: kvsplit.HistArgs) !void {
+        const fs = o.f.kv_hist orelse return error.Unsupported;
+        if (a.row_bytes == 0 or a.row_bytes % 8 != 0 or a.k == 0 or a.world == 0 or a.world > 8 or a.psh > 30) return error.Shape;
+        const words = @as(u64, a.m) * (a.row_bytes / 8);
+        const n = @as(u64, a.rows) * a.k;
+        for (fs, [2]u64{ words, n }) |f, w| {
+            if (w == 0) continue;
+            var args: cuda.Args = .{};
+            args.add(a);
+            try o.go(f, .{ kvsplit.grid(w), 1, 1 }, 256, 0, null, &args);
+        }
+    }
+
+    /// kvsplit gather: send[i] = base row phys[i] (uint32 [n]).
+    pub fn kvGather(o: Ops, base: u64, row_bytes: u32, phys: u64, n: u32, send: u64) !void {
+        if (row_bytes == 0 or row_bytes % 8 != 0) return error.Shape;
+        const words = @as(u64, n) * (row_bytes / 8);
+        if (words == 0) return;
+        var args: cuda.Args = .{};
+        args.add(base);
+        args.add(row_bytes);
+        args.add(phys);
+        args.add(n);
+        args.add(send);
+        try o.go(o.f.kv_gather, .{ kvsplit.grid(words), 1, 1 }, 256, 0, null, &args);
+    }
+
+    pub const glueGo = glue_ops.glueGo;
+    pub const embedSend = glue_ops.embedSend;
+    pub const embedSum = glue_ops.embedSum;
+    pub const castBf16 = glue_ops.castBf16;
+    pub const kitWeights = glue_ops.kitWeights;
+    pub const kitLogits = glue_ops.kitLogits;
+    pub const positions = glue_ops.positions;
+    pub const dsCandidates = glue_ops.dsCandidates;
+    pub const dsStage = glue_ops.dsStage;
+    pub const dsAcceptRows = glue_ops.dsAcceptRows;
+    pub const dsStageRows = glue_ops.dsStageRows;
+    pub const vsChoose = glue_ops.vsChoose;
+    pub const widenF32 = glue_ops.widenF32;
+    pub const pfGo = glue_ops.pfGo;
+    pub const topPositions = glue_ops.topPositions;
+    pub const candKeys = glue_ops.candKeys;
+    pub const visibleCounts = glue_ops.visibleCounts;
+    pub const gmPicks = glue_ops.gmPicks;
+    pub const widthMask = glue_ops.widthMask;
+    pub const f64Bf16F32 = glue_ops.f64Bf16F32;
+    pub const widenCat = glue_ops.widenCat;
+    pub const ringCopy = glue_ops.ringCopy;
+    pub const positionsDev = glue_ops.positionsDev;
+    pub const gatherCols = glue_ops.gatherCols;
+    pub const pickPack = glue_ops.pickPack;
+    pub const pickMerge = glue_ops.pickMerge;
+    pub const carry = glue_ops.carry;
+
+    /// tfl2pf launch_segments: (address, bytes) table prefetched to L2 (bulk: cp.async.bulk.prefetch).
+    pub fn l2Segments(o: Ops, table: u64, n: usize, grid: usize, threads: usize, chunk: u32, bulk: bool) !void {
+        var args: cuda.Args = .{};
+        args.add(table);
+        args.add(@as(c_int, @intCast(n)));
+        args.add(chunk);
+        try o.go(o.f.segments[@intFromBool(bulk)], .{ grid, 1, 1 }, threads, 0, null, &args);
+    }
+};
+
+test "pfdense shared memory and heuristic as the reference" {
+    // cfg 0 (128, 2, 4, 2, 3) at K2 10: 3 x (128 x 32 x 2 + 2 x 8 x 40 x 4) + 2 x 8192 = 3 x 10752 + 16384
+    try std.testing.expectEqual(@as(u32, 3 * 10752 + 16384), pfd.smem(0, 10));
+    try std.testing.expectEqual(@as(u32, 256), pfd.threads(0));
+    // a 2,048-row [5120 -> 1024] call on 48 SMs: 16 x 8 = 128 CTAs of BM 128
+    const h = pfd.heuristic(5120, 1024, 10, 2048, 48);
+    try std.testing.expectEqual(@as(usize, 8), h[1]);
+}
+
+test "mhc instances follow dispatch" {
+    var a: mhc.Args = .{ .R = 3 };
+    try std.testing.expectEqual(@as(usize, 6), try mhc.instance(a, 3, true));
+    try std.testing.expectEqual(@as(usize, 3), try mhc.instance(a, 1, true));
+    a.R = 17;
+    try std.testing.expectError(error.Shape, mhc.instance(a, 0, false));
+    const p: mhc_pf.Args = .{ .R = 40 };
+    try std.testing.expectEqual(@as(usize, 5), try mhc_pf.instance(p, 1, true, false));
+    try std.testing.expectError(error.Shape, mhc_pf.instance(p, 1, false, true));
+    try std.testing.expectEqual(@as(u32, 47872), mhc_pf.smem(false));
+}
diff --git a/zig/src/families/deepseek_v41/kernels_replay.zig b/zig/src/families/deepseek_v41/kernels_replay.zig
new file mode 100644
index 0000000000..ffeb282502
--- /dev/null
+++ b/zig/src/families/deepseek_v41/kernels_replay.zig
@@ -0,0 +1,232 @@
+//! Replays captured extension calls through the Zig launches, so block replay compares every .cu kernel's outputs.
+
+const std = @import("std");
+const cuda = @import("cuda");
+const kernels = @import("kernels.zig");
+const exl3 = @import("kernels_exl3.zig");
+const dense = @import("kernels_dense.zig");
+const ops = @import("kernels_ops.zig");
+const replay_experts = @import("kernels_replay_experts.zig");
+const replay_ops = @import("kernels_replay_ops.zig");
+
+const Kernels = kernels.Kernels;
+
+pub const DType = enum { f16, bf16, f32, f64, i8, u8, i16, i32, i64, bool };
+pub const Tensor = struct { ptr: u64, dtype: DType, shape: []const i64, stride: []const i64 }; // element strides, ptr already at storage_offset
+pub const Arg = union(enum) { tensor: Tensor, int: i64, float: f64, boolean: bool, none, list: []const Arg };
+pub const Kw = struct { name: []const u8, value: Arg };
+
+/// Device bytes a call may use beyond its arguments (cleared per call by the caller).
+pub const Scratch = struct {
+    ptr: u64,
+    len: usize,
+    used: usize = 0,
+
+    pub fn take(s: *Scratch, n: usize) !u64 {
+        const at = std.mem.alignForward(usize, s.used, 256);
+        if (at + n > s.len) return error.ScratchTooSmall;
+        s.used = at + n;
+        return s.ptr + at;
+    }
+};
+
+pub const Error = error{ MissingArgument, WrongArgument, Unsupported };
+
+/// The call's arguments by pybind position, or by keyword when passed so (`names` lists the binding's parameters).
+pub const Call = struct {
+    args: []const Arg,
+    kwargs: []const Kw,
+    names: []const []const u8,
+
+    fn get(c: Call, i: usize) Error!Arg {
+        if (i < c.args.len) return c.args[i];
+        for (c.kwargs) |kw| if (std.mem.eql(u8, kw.name, c.names[i])) return kw.value;
+        return error.MissingArgument;
+    }
+
+    pub fn opt(c: Call, i: usize) Error!?Arg {
+        const a = c.get(i) catch |e| switch (e) {
+            error.MissingArgument => return null,
+            else => return e,
+        };
+        return if (a == .none) null else a;
+    }
+
+    pub fn tensor(c: Call, i: usize) Error!Tensor {
+        return switch (try c.get(i)) {
+            .tensor => |t| t,
+            else => error.WrongArgument,
+        };
+    }
+
+    /// An optional tensor's address (None or an empty tensor: 0, as the bindings pass nullptr).
+    pub fn ptrOrNull(c: Call, i: usize) Error!u64 {
+        const a = (try c.opt(i)) orelse return 0;
+        return switch (a) {
+            .tensor => |t| if (numel(t) == 0) 0 else t.ptr,
+            else => error.WrongArgument,
+        };
+    }
+
+    pub fn ptr(c: Call, i: usize) Error!u64 {
+        return (try c.tensor(i)).ptr;
+    }
+
+    pub fn int(c: Call, i: usize) Error!i64 {
+        return switch (try c.get(i)) {
+            .int => |v| v,
+            .boolean => |b| @intFromBool(b),
+            else => error.WrongArgument,
+        };
+    }
+
+    pub fn usz(c: Call, i: usize) Error!usize {
+        const v = try c.int(i);
+        if (v < 0) return error.WrongArgument;
+        return @intCast(v);
+    }
+
+    pub fn u(c: Call, i: usize) Error!u32 {
+        return @intCast(try c.usz(i));
+    }
+
+    pub fn float(c: Call, i: usize) Error!f64 {
+        return switch (try c.get(i)) {
+            .float => |v| v,
+            .int => |v| @floatFromInt(v),
+            else => error.WrongArgument,
+        };
+    }
+
+    pub fn boolean(c: Call, i: usize, default: bool) Error!bool {
+        const a = (try c.opt(i)) orelse return default;
+        return switch (a) {
+            .boolean => |b| b,
+            .int => |v| v != 0,
+            else => error.WrongArgument,
+        };
+    }
+
+    pub fn list(c: Call, i: usize) Error![]const Arg {
+        return switch (try c.get(i)) {
+            .list => |l| l,
+            else => error.WrongArgument,
+        };
+    }
+};
+
+pub fn numel(t: Tensor) i64 {
+    var n: i64 = 1;
+    for (t.shape) |d| n *= d;
+    return n;
+}
+
+pub fn dim(t: Tensor, i: usize) Error!usize {
+    if (i >= t.shape.len) return error.WrongArgument;
+    return @intCast(t.shape[i]);
+}
+
+pub fn ioType(t: DType) Error!dense.DType {
+    return switch (t) {
+        .f16 => .f16,
+        .bf16 => .bf16,
+        .f32 => .f32,
+        else => error.WrongArgument,
+    };
+}
+
+pub fn input(t: DType) Error!exl3.Input {
+    return switch (t) {
+        .bf16 => .bf16,
+        .f16 => .f16,
+        else => error.WrongArgument,
+    };
+}
+
+pub fn elem(t: DType) usize {
+    return switch (t) {
+        .i8, .u8, .bool => 1,
+        .f16, .bf16, .i16 => 2,
+        .f32, .i32 => 4,
+        .f64, .i64 => 8,
+    };
+}
+
+pub fn is(a: []const u8, b: []const u8) bool {
+    return std.mem.eql(u8, a, b);
+}
+
+/// One captured extension call re-issued on `s`; false when there is no binding for it (reported as skipped).
+pub fn call(k: *const Kernels, s: cuda.Stream, ext: []const u8, func: []const u8, args: []const Arg, kwargs: []const Kw, scratch: *Scratch) !bool {
+    _ = scratch;
+    if (is(ext, "tf_dsv41_kv_glue_v1") and is(func, "norm_store")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "x", "w", "cs", "v", "s", "pos", "sl", "x_stride", "cs_stride", "v_stride", "s_stride", "inv_k", "eps", "ring", "rows" } };
+        const x = try c.tensor(0);
+        const rows = try c.boolean(14, false);
+        if (x.dtype != .bf16 or try dim(x, 1) != 512 or (try c.tensor(1)).dtype != .f32 or
+            (try c.tensor(2)).dtype != .f32 or (try c.tensor(5)).dtype != (if (rows) DType.i64 else DType.i32) or
+            (rows and (try c.tensor(6)).dtype != .i64)) return error.WrongArgument;
+        try k.kvStore(s).normStore(.{
+            .x = x.ptr,
+            .w = try c.ptr(1),
+            .cs = try c.ptr(2),
+            .v = try c.ptr(3),
+            .s = try c.ptr(4),
+            .pos = try c.ptr(5),
+            .sl = try c.ptrOrNull(6),
+            .x_stride = try c.int(7),
+            .cs_stride = try c.int(8),
+            .v_stride = try c.int(9),
+            .s_stride = try c.int(10),
+            .inv_k = @floatCast(try c.float(11)),
+            .eps = @floatCast(try c.float(12)),
+            .ring = @intCast(try c.int(13)),
+            .rows = @intFromBool(rows),
+        }, try dim(x, 0));
+        return true;
+    }
+    if (is(ext, "tensorfold_exl3_linear_v4")) return linearExt(k.linears(s), func, args, kwargs);
+    if (is(ext, "tf_dsv41_x3seg_v3")) return segExt(k.linears(s), func, args, kwargs, false);
+    if (is(ext, "tf_dsv41_dense3_v1")) return segExt(k.linears(s), func, args, kwargs, true);
+    if (is(ext, "tensorfold_exl3_experts_v1")) return expertsExt(k.experts(s), func, args, kwargs);
+    if (is(ext, "tf_dsv41_x3ld_v1") or is(ext, "tf_dsv41_x3pf_v1")) return groupedExt(k.experts(s), ext, func, args, kwargs);
+    if (is(ext, "tf_dsv41_x3gm_v1")) return x3gmExt(k.experts(s), func, args, kwargs);
+    if (is(ext, "tf_dsv41_x3ld_epi_v1")) return x3ldEpiExt(k.experts(s), func, args, kwargs);
+    if (is(ext, "tf_dsv41_attn_cuda_v1")) return attnExt(k.others(s), func, args, kwargs);
+    if (is(ext, "tf_dsv41_topk_b_v1") and is(func, "topk")) return topkCall(k.others(s), args, kwargs, true);
+    if (is(ext, "tf_dsv41_mhc_cuda_v1") and is(func, "run")) return mhcRun(k.others(s), args, kwargs);
+    if (is(ext, "tf_dsv41_mhc_cuda_v1") and is(func, "coef")) return mhcCoef(k.others(s), args, kwargs);
+    if (is(ext, "tf_dsv41_mhc_pf_v1") and is(func, "run")) return mhcPfRun(k.others(s), args, kwargs);
+    if (is(ext, "tf_dsv41_router_gemv_v2") and is(func, "route")) return route(k.others(s), args, kwargs);
+    if (is(ext, "tf_dsv41_pfdense_v1")) return pfdenseExt(k.others(s), func, args, kwargs);
+    if (is(ext, "tf_dsv41_l2pace_v1") and is(func, "paced")) return paced(k.others(s), args, kwargs);
+    if (is(ext, "tf_dsv41_l2pf_v1") and is(func, "segments")) return segments(k.others(s), args, kwargs);
+    return false; // engram_gate.gate_copy reads host-mapped words a capture cannot relocate: not replayed
+}
+
+const linearExt = replay_experts.linearExt;
+const segExt = replay_experts.segExt;
+const expertsExt = replay_experts.expertsExt;
+const groupedExt = replay_experts.groupedExt;
+const x3gmExt = replay_experts.x3gmExt;
+const x3ldEpiExt = replay_experts.x3ldEpiExt;
+const attnExt = replay_ops.attnExt;
+const topkCall = replay_ops.topkCall;
+const mhcRun = replay_ops.mhcRun;
+const mhcCoef = replay_ops.mhcCoef;
+const mhcPfRun = replay_ops.mhcPfRun;
+const route = replay_ops.route;
+const pfdenseExt = replay_ops.pfdenseExt;
+const paced = replay_ops.paced;
+const segments = replay_ops.segments;
+
+test "arguments by position and keyword" {
+    const shape = [_]i64{ 2, 3 };
+    const t: Tensor = .{ .ptr = 64, .dtype = .bf16, .shape = &shape, .stride = &.{ 3, 1 } };
+    const c: Call = .{ .args = &.{ .{ .tensor = t }, .{ .int = 7 } }, .kwargs = &.{.{ .name = "pdl", .value = .{ .boolean = true } }}, .names = &.{ "x", "n", "z", "pdl" } };
+    try std.testing.expectEqual(@as(u64, 64), try c.ptr(0));
+    try std.testing.expectEqual(@as(usize, 7), try c.usz(1));
+    try std.testing.expectEqual(@as(u64, 0), try c.ptrOrNull(2));
+    try std.testing.expect(try c.boolean(3, false));
+    try std.testing.expectError(error.WrongArgument, c.tensor(1));
+}
diff --git a/zig/src/families/deepseek_v41/kernels_replay_experts.zig b/zig/src/families/deepseek_v41/kernels_replay_experts.zig
new file mode 100644
index 0000000000..0d470ebcfd
--- /dev/null
+++ b/zig/src/families/deepseek_v41/kernels_replay_experts.zig
@@ -0,0 +1,325 @@
+//! Replays the EXL3 linear and routed-expert extension calls (linear, x3seg, dense3, experts, x3ld/x3pf, x3gm).
+
+const std = @import("std");
+const exl3 = @import("kernels_exl3.zig");
+const dense = @import("kernels_dense.zig");
+const replay = @import("kernels_replay.zig");
+
+const Call = replay.Call;
+const Arg = replay.Arg;
+const Kw = replay.Kw;
+const numel = replay.numel;
+const dim = replay.dim;
+const ioType = replay.ioType;
+const input = replay.input;
+const elem = replay.elem;
+const is = replay.is;
+
+// tensorfold_exl3_linear_v4 (linear.cpp)
+pub fn linearExt(o: dense.Ops, func: []const u8, args: []const Arg, kwargs: []const Kw) !bool {
+    if (is(func, "rot_in")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "x", "suh", "xh" } };
+        const x = try c.tensor(0);
+        try o.rotIn(x.ptr, try ioType(x.dtype), try c.ptr(1), try c.ptr(2), try dim(x, 0), try dim(x, 1));
+        return true;
+    }
+    if (is(func, "linear") or is(func, "linear_skip")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "xh", "T", "stride_k", "stride_nb", "svh", "bias", "y", "Z", "counters", "K2", "cb", "SK", "WK", "skip" } };
+        if (try c.int(10) != 2) return false; // mul1 only is built (every DeepSeek-V4.1 matrix)
+        const xh = try c.tensor(0);
+        const y = try c.tensor(6);
+        const skip: u64 = if (is(func, "linear_skip")) try c.ptrOrNull(13) else 0;
+        try o.linear(xh.ptr, try c.ptr(1), try c.int(2), try c.int(3), try c.ptr(4), try c.ptrOrNull(5), y.ptr, try ioType(y.dtype), try c.ptrOrNull(7), try c.ptr(8), try dim(xh, 0), try dim(xh, 1), try dim(y, 1), try c.u(9), try c.usz(11), try c.usz(12), skip);
+        return true;
+    }
+    if (is(func, "unpack")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "T", "W", "stride_k", "stride_nb", "K2", "cb" } };
+        if (try c.int(5) != 2) return false;
+        const w = try c.tensor(1);
+        try o.unpack(try c.ptr(0), w.ptr, try dim(w, 0), try dim(w, 1), try c.int(2), try c.int(3), try c.u(4));
+        return true;
+    }
+    return false;
+}
+
+// tf_dsv41_x3seg_v3 / tf_dsv41_dense3_v1 (x3seg.cpp, dense3.cpp): the tables built as dsv41_*_linear_cuda builds them
+pub fn segExt(o: dense.Ops, func: []const u8, args: []const Arg, kwargs: []const Kw, lanes: bool) !bool {
+    if (is(func, "rot_in") and !lanes) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "xs", "suh", "xh", "xh_off", "pdl", "nw", "eps" } };
+        const nws: []const Arg = if (try c.opt(5)) |v| switch (v) {
+            .list => |l| l,
+            else => return error.WrongArgument,
+        } else &.{};
+        const eps: f32 = if ((try c.opt(6)) != null) @floatCast(try c.float(6)) else 0;
+        const xs = try c.list(0);
+        const suh = try c.list(1);
+        const offs = try c.list(3);
+        const xh = try c.ptr(2);
+        if (xs.len < 1 or xs.len > dense.max_seg or suh.len != xs.len or offs.len != xs.len) return error.WrongArgument;
+        var tab = std.mem.zeroes(dense.RotTable);
+        tab.n = @intCast(xs.len);
+        var M: usize = 0;
+        for (xs, suh, offs, 0..) |xa, sa, oa, i| {
+            if (xa != .tensor or sa != .tensor or oa != .int) return error.WrongArgument;
+            const x = xa.tensor;
+            if (i == 0) M = try dim(x, 0);
+            tab.s[i] = .{ .x = x.ptr, .suh = sa.tensor.ptr, .xh = xh + 2 * @as(u64, @intCast(oa.int)), .ld = x.stride[0], .K = @intCast(try dim(x, 1)), .x_dtype = @backingInt(try ioType(x.dtype)) };
+            if (i < nws.len) { // R1c: the folded norm (an empty weight: none)
+                const w = switch (nws[i]) {
+                    .tensor => |t| t,
+                    else => return error.WrongArgument,
+                };
+                if (numel(w) != 0) {
+                    tab.s[i].nw = w.ptr;
+                    tab.s[i].nw_dtype = @backingInt(try ioType(w.dtype));
+                    tab.s[i].inv_k = dense.invK(try dim(x, 1));
+                    tab.s[i].eps = eps;
+                }
+            }
+        }
+        try o.segRotIn(tab, M, try c.boolean(4, false));
+        return true;
+    }
+    if (is(func, "linear")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "xh", "T", "svh", "ys", "Z", "counters", "meta", "programs", "K2", "WK", "minb", "pdl" } };
+        const T = try c.list(1);
+        const svh = try c.list(2);
+        const ys = try c.list(3);
+        const meta = try c.list(6);
+        const n = T.len;
+        if (n < 1 or n > dense.max_seg or svh.len != n or ys.len != n or meta.len != 11 * n) return error.WrongArgument;
+        const xh = try c.ptr(0);
+        const z = try c.ptrOrNull(4);
+        const counters = try c.ptr(5);
+        if (ys[0] != .tensor) return error.WrongArgument;
+        const y0 = ys[0].tensor;
+        const M = try dim(y0, 0);
+        const y_dtype = try ioType(y0.dtype);
+        var tab = std.mem.zeroes(dense.LinTable);
+        tab.n = @intCast(n);
+        for (0..n) |i| {
+            var m: [11]i64 = undefined;
+            for (&m, meta[11 * i ..][0..11]) |*v, a| v.* = switch (a) {
+                .int => |x| x,
+                else => return error.WrongArgument,
+            };
+            if (T[i] != .tensor or svh[i] != .tensor or ys[i] != .tensor) return error.WrongArgument;
+            const y = ys[i].tensor;
+            tab.s[i] = .{
+                .xh = xh + 2 * @as(u64, @intCast(m[6])),
+                .T = T[i].tensor.ptr,
+                .stride_k = m[4],
+                .stride_nb = m[5],
+                .svh = svh[i].tensor.ptr,
+                .y = y.ptr + @as(u64, @intCast(m[9])) * elem(y.dtype),
+                .Z = if (m[2] > 1) z + 4 * @as(u64, @intCast(m[7])) else 0,
+                .counters = counters + 4 * @as(u64, @intCast(m[8])),
+                .K = @intCast(m[0]),
+                .N = @intCast(m[1]),
+                .SK = @intCast(m[2]),
+                .wk = @intCast(m[3]),
+                .p0 = @intCast(m[10]),
+                .ld = @intCast(y.stride[0]),
+            };
+        }
+        const programs = try c.usz(7);
+        const k2 = try c.u(8);
+        const wk = try c.usz(9);
+        const minb = try c.u(10);
+        const pdl = try c.boolean(11, false);
+        if (lanes) try o.lanesLinearRaw(tab, y_dtype, M, programs, k2, wk, minb, pdl) else try o.segLinearRaw(tab, y_dtype, M, programs, k2, wk, minb, pdl);
+        return true;
+    }
+    if (is(func, "relayout") and lanes) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "src", "dst", "K2", "K", "lanes" } };
+        const src = try c.tensor(0);
+        try o.relayout(src.ptr, try c.ptr(1), @intCast(numel(src)), try c.u(2), try c.usz(3), try c.boolean(4, true));
+        return true;
+    }
+    if (is(func, "unpack") and lanes) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "T", "W", "stride_k", "stride_nb", "K2" } };
+        const w = try c.tensor(1);
+        try o.lanesUnpack(try c.ptr(0), w.ptr, try dim(w, 0), try dim(w, 1), try c.int(2), try c.int(3), try c.u(4));
+        return true;
+    }
+    return false;
+}
+
+const grouped_names = [_][]const u8{ "X0", "X1", "TP0", "TP1", "B0", "B1", "uids", "ucount", "members", "Z", "mats", "K", "N", "P", "SK", "slots", "cb" };
+
+fn groupedArgs(c: Call, lo_i: usize) !exl3.Grouped {
+    const uids = try c.tensor(6);
+    const members = try c.tensor(8);
+    return .{
+        .x0 = try c.ptr(0),
+        .x1 = try c.ptr(1),
+        .tp0 = try c.ptr(2),
+        .tp1 = try c.ptr(3),
+        .k2_0 = try c.ptr(4),
+        .k2_1 = try c.ptr(5),
+        .uids = uids.ptr,
+        .ucount = try c.ptr(7),
+        .members = members.ptr,
+        .z = try c.ptr(9),
+        .mats = try c.usz(10),
+        .K = try c.usz(11),
+        .N = try c.usz(12),
+        .P = try c.usz(13),
+        .SK = try c.usz(14),
+        .slots = try c.usz(15),
+        .maxm = try dim(members, 1),
+        .nexp = try dim(uids, 0),
+        .lo = try c.u(lo_i),
+        .hi = try c.u(lo_i + 1),
+    };
+}
+
+// tensorfold_exl3_experts_v1 (experts.cpp)
+pub fn expertsExt(o: exl3.Ops, func: []const u8, args: []const Arg, kwargs: []const Kw) !bool {
+    if (is(func, "group_rot_in")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "x", "x_stride", "pick", "suh0", "suh1", "out0", "out1", "rows", "K", "slots", "E", "uids", "ucount", "members" } };
+        const x = try c.tensor(0);
+        if (x.dtype != .bf16) return error.WrongArgument;
+        const members = try c.tensor(13);
+        try o.groupRotIn(x.ptr, try c.usz(1), try c.ptr(2), try c.ptr(3), try c.ptr(4), try c.ptr(5), try c.ptr(6), try c.ptr(11), try c.ptr(12), members.ptr, try c.usz(7), try c.usz(8), try c.usz(9), try c.usz(10), try dim(members, 1));
+        return true;
+    }
+    if (is(func, "grouped")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &(grouped_names ++ [_][]const u8{ "nt", "warps", "pf", "lo", "hi" }) };
+        if (try c.int(16) != 2) return false; // cb 2 (mul1) instances only
+        try o.grouped(try groupedArgs(c, 20), try c.u(17), try c.u(18), try c.u(19));
+        return true;
+    }
+    if (is(func, "group")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "pick", "uids", "ucount", "members", "R", "slots", "E" } };
+        const members = try c.tensor(3);
+        try o.group(try c.ptr(0), try c.ptr(1), try c.ptr(2), members.ptr, try c.usz(4), try c.usz(5), try c.usz(6), try dim(members, 1));
+        return true;
+    }
+    if (is(func, "rot_in")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "x", "x_stride", "pick", "suh0", "suh1", "out0", "out1", "rows", "K", "slots", "E" } };
+        const x = try c.tensor(0);
+        try o.rotIn(try input(x.dtype), x.ptr, try c.usz(1), try c.ptr(2), try c.ptr(3), try c.ptr(4), try c.ptr(5), try c.ptr(6), try c.usz(7), try c.usz(8), try c.usz(9), try c.usz(10));
+        return true;
+    }
+    if (is(func, "gateup_epilogue")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "Z", "pick", "svh_g", "svh_u", "suh_d", "xd", "rows", "P", "N", "SK", "slots", "E", "limit", "act_mode" } };
+        try o.gateupEpilogue(try c.ptr(0), try c.ptr(1), try c.ptr(2), try c.ptr(3), try c.ptr(4), try c.ptr(5), try c.usz(6), try c.usz(7), try c.usz(8), try c.usz(9), try c.usz(10), try c.usz(11), @floatCast(try c.float(12)), @intCast(try c.int(13)));
+        return true;
+    }
+    if (is(func, "down_epilogue")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "Z", "pick", "svh_d", "y", "rows", "P", "D", "SK", "slots", "E" } };
+        try o.downEpilogue(try c.ptr(0), try c.ptr(1), try c.ptr(2), try c.ptr(3), try c.usz(4), try c.usz(5), try c.usz(6), try c.usz(7), try c.usz(8), try c.usz(9));
+        return true;
+    }
+    if (is(func, "combine")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "y", "wts", "out", "rows", "D", "slots" } };
+        try o.combine(try c.ptr(0), try c.ptr(1), try c.ptr(2), try c.usz(3), try c.usz(4), try c.usz(5));
+        return true;
+    }
+    if (is(func, "down_combine")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "Z", "pick", "svh_d", "y", "wts", "out", "rows", "P", "D", "SK", "slots", "E" } };
+        const out = try c.tensor(5);
+        if (out.dtype != .f32 and out.dtype != .bf16) return error.WrongArgument;
+        try o.downCombine(try c.ptr(0), try c.ptr(1), try c.ptr(2), try c.ptr(3), try c.ptr(4), out.ptr, try c.usz(6), try c.usz(7), try c.usz(8), try c.usz(9), try c.usz(10), try c.usz(11), out.dtype == .bf16);
+        return true;
+    }
+    if (is(func, "dequant")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "T", "out", "k2", "cb" } };
+        if (try c.int(3) != 2) return false;
+        const t = try c.tensor(0);
+        try o.dequant(t.ptr, try c.ptr(1), 16 * try dim(t, 0), 16 * try dim(t, 1), try c.u(2));
+        return true;
+    }
+    return false;
+}
+
+// tf_dsv41_x3ld_v1.grouped (..., cb, nt, pd, probe, lo, hi, pdl) / tf_dsv41_x3pf_v1.grouped (..., cb, nt, mtl, lo, hi)
+pub fn groupedExt(o: exl3.Ops, ext: []const u8, func: []const u8, args: []const Arg, kwargs: []const Kw) !bool {
+    if (!is(func, "grouped")) return false;
+    if (is(ext, "tf_dsv41_x3ld_v1")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &(grouped_names ++ [_][]const u8{ "nt", "pd", "probe", "lo", "hi", "pdl" }) };
+        if (try c.int(16) != 2) return error.Unsupported; // the binding refuses it too
+        try o.x3ld(try groupedArgs(c, 20), try c.u(17), try c.u(18), try c.u(19), try c.boolean(22, false));
+        return true;
+    }
+    const c: Call = .{ .args = args, .kwargs = kwargs, .names = &(grouped_names ++ [_][]const u8{ "nt", "mtl", "lo", "hi" }) };
+    if (try c.int(16) != 2) return error.Unsupported;
+    try o.x3pf(try groupedArgs(c, 19), try c.u(17), try c.u(18));
+    return true;
+}
+
+// tf_dsv41_x3ld_epi_v1 (TF_DSV41_X3LD_EPI): x3ld.grouped's arguments to hi, then the epilogue's, then the ticket words
+pub fn x3ldEpiExt(o: exl3.Ops, func: []const u8, args: []const Arg, kwargs: []const Kw) !bool {
+    const base = grouped_names ++ [_][]const u8{ "nt", "pd", "lo", "hi", "pick" };
+    if (is(func, "gateup")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &(base ++ [_][]const u8{ "svh_g", "svh_u", "suh_d", "xd", "E", "limit", "act_mode", "ticket" }) };
+        if (try c.int(16) != 2 or try c.int(17) != 8) return error.Unsupported;
+        try o.x3ldEpi(try groupedArgs(c, 19), .gu, try c.u(18), .{
+            .pick = try c.ptr(21),
+            .sv0 = try c.ptr(22),
+            .sv1 = try c.ptr(23),
+            .sd = try c.ptr(24),
+            .xd = try c.ptr(25),
+            .E = @intCast(try c.int(26)),
+            .limit = @floatCast(try c.float(27)),
+            .act_mode = @intCast(try c.int(28)),
+            .ticket = try c.ptr(29),
+        });
+        return true;
+    }
+    if (is(func, "down")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &(base ++ [_][]const u8{ "svh_d", "y", "wts", "out", "E", "ticket" }) };
+        if (try c.int(16) != 2 or try c.int(17) != 8) return error.Unsupported;
+        const out = try c.tensor(25);
+        if (out.dtype != .f32 and out.dtype != .bf16) return error.WrongArgument;
+        try o.x3ldEpi(try groupedArgs(c, 19), if (out.dtype == .bf16) .dnb else .dn, try c.u(18), .{
+            .pick = try c.ptr(21),
+            .sv0 = try c.ptr(22),
+            .y = try c.ptr(23),
+            .wts = try c.ptr(24),
+            .out = out.ptr,
+            .E = @intCast(try c.int(26)),
+            .ticket = try c.ptr(27),
+        });
+        return true;
+    }
+    return false;
+}
+
+// tf_dsv41_x3gm_v1 (x3gm.cpp)
+pub fn x3gmExt(o: exl3.Ops, func: []const u8, args: []const Arg, kwargs: []const Kw) !bool {
+    if (is(func, "gateup")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "xg", "xu", "tg", "tu", "order", "pe", "poff", "pcnt", "npass", "ticket", "svh_g", "svh_u", "suh_d", "xd", "K", "N", "k2", "shx", "cfg", "use_ticket", "limit", "tables" } };
+        try o.gmGateup(try c.ptr(0), try c.ptr(1), try c.ptr(2), try c.ptr(3), try c.ptr(4), try c.ptr(5), try c.ptr(6), try c.ptr(7), try c.ptr(8), try c.ptr(9), try c.ptr(10), try c.ptr(11), try c.ptr(12), try c.ptr(13), try c.usz(14), try c.usz(15), try c.u(16), try c.boolean(17, false), try c.usz(18), try c.boolean(19, true), @floatCast(try c.float(20)), try c.boolean(21, false));
+        return true;
+    }
+    if (is(func, "down")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "xd", "td", "order", "pe", "poff", "pcnt", "npass", "ticket", "svh_d", "y", "K", "N", "k2", "cfg", "use_ticket", "tables" } };
+        try o.gmDown(try c.ptr(0), try c.ptr(1), try c.ptr(2), try c.ptr(3), try c.ptr(4), try c.ptr(5), try c.ptr(6), try c.ptr(7), try c.ptr(8), try c.ptr(9), try c.usz(10), try c.usz(11), try c.u(12), try c.usz(13), try c.boolean(14, true), try c.boolean(15, false));
+        return true;
+    }
+    if (is(func, "gateup2")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "xg", "xu", "tg", "tu", "k2e", "order", "pe", "poff", "pcnt", "npass", "ticket", "svh_g", "svh_u", "suh_d", "xd", "K", "N", "shx", "cfg", "use_ticket", "limit", "tables" } };
+        try o.gm2Gateup(try c.ptr(0), try c.ptr(1), try c.ptr(2), try c.ptr(3), try c.ptr(4), try c.ptr(5), try c.ptr(6), try c.ptr(7), try c.ptr(8), try c.ptr(9), try c.ptr(10), try c.ptr(11), try c.ptr(12), try c.ptr(13), try c.ptr(14), try c.usz(15), try c.usz(16), try c.boolean(17, false), try c.usz(18), try c.boolean(19, true), @floatCast(try c.float(20)), try c.boolean(21, false));
+        return true;
+    }
+    if (is(func, "down2")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "xd", "td", "k2e", "order", "pe", "poff", "pcnt", "npass", "ticket", "svh_d", "y", "K", "N", "cfg", "use_ticket", "tables" } };
+        try o.gm2Down(try c.ptr(0), try c.ptr(1), try c.ptr(2), try c.ptr(3), try c.ptr(4), try c.ptr(5), try c.ptr(6), try c.ptr(7), try c.ptr(8), try c.ptr(9), try c.ptr(10), try c.usz(11), try c.usz(12), try c.usz(13), try c.boolean(14, true), try c.boolean(15, false));
+        return true;
+    }
+    if (is(func, "rot")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "x", "x_stride", "pick", "suh0", "suh1", "out0", "out1", "K", "slots", "pairs", "mats" } };
+        const x = try c.tensor(0);
+        try o.gmRot(try input(x.dtype), x.ptr, try c.usz(1), try c.ptr(2), try c.ptr(3), try c.ptr(4), try c.ptr(5), try c.ptr(6), try c.usz(7), try c.usz(8), try c.usz(9), try c.usz(10));
+        return true;
+    }
+    if (is(func, "dequant")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "t", "out", "k2" } };
+        const out = try c.tensor(1);
+        try o.gmDequant(try c.ptr(0), out.ptr, try dim(out, 0), try dim(out, 1), try c.u(2));
+        return true;
+    }
+    return false;
+}
diff --git a/zig/src/families/deepseek_v41/kernels_replay_ops.zig b/zig/src/families/deepseek_v41/kernels_replay_ops.zig
new file mode 100644
index 0000000000..8be2b1bdf1
--- /dev/null
+++ b/zig/src/families/deepseek_v41/kernels_replay_ops.zig
@@ -0,0 +1,308 @@
+//! Replays the other kernels' extension calls: attention, top-k, mHC, router, prefill dense GEMM and L2 prefetch.
+
+const ops = @import("kernels_ops.zig");
+const replay = @import("kernels_replay.zig");
+
+const Call = replay.Call;
+const Tensor = replay.Tensor;
+const Arg = replay.Arg;
+const Kw = replay.Kw;
+const numel = replay.numel;
+const dim = replay.dim;
+const is = replay.is;
+
+/// The bindings' `tp(t)`: an empty tensor is nullptr.
+fn tp(t: Tensor) u64 {
+    return if (numel(t) == 0) 0 else t.ptr;
+}
+
+fn stride0(t: Tensor) i64 {
+    return if (numel(t) == 0 or t.stride.len == 0) 0 else t.stride[0];
+}
+
+// tf_dsv41_attn_cuda_v1 (csa2/attn_cuda.cpp): attn and topk as dsv41_attn_run_cuda / dsv41_topk_run_cuda fill Args
+pub fn attnExt(o: ops.Ops, func: []const u8, args: []const Arg, kwargs: []const Kw) !bool {
+    if (is(func, "attn")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "q", "cv", "csc", "tok", "cnt", "sv", "ssc", "lo", "hi", "pos", "sl", "pt", "sink", "cs", "po", "pm", "pl", "out", "ticket", "ring", "psh", "pts", "kw", "split", "qrope" } };
+        const q = try c.tensor(0);
+        const cv = try c.tensor(1);
+        const csc = try c.tensor(2);
+        const tok = try c.tensor(3);
+        const sv = try c.tensor(5);
+        const ssc = try c.tensor(6);
+        const hi = try c.tensor(8);
+        const sl = try c.tensor(10);
+        const cs = try c.tensor(13);
+        const a: ops.attn.Args = .{
+            .q = tp(q),
+            .has_comp = @intFromBool(numel(cv) != 0),
+            .cv = tp(cv),
+            .csc = tp(csc),
+            .cvs = stride0(cv),
+            .css = stride0(csc),
+            .tok = tp(tok),
+            .ts = stride0(tok),
+            .cnt = tp(try c.tensor(4)),
+            .sv = tp(sv),
+            .ssc = tp(ssc),
+            .svs = sv.stride[0],
+            .sss = ssc.stride[0],
+            .lo = tp(try c.tensor(7)),
+            .hi = tp(hi),
+            .pos = (try c.tensor(9)).ptr,
+            .sl = tp(sl),
+            .pt = tp(try c.tensor(11)),
+            .pts = try c.int(21),
+            .psh = @intCast(try c.int(20)),
+            .sink = tp(try c.tensor(12)),
+            .cs = tp(cs),
+            .csst = cs.stride[0],
+            .po = tp(try c.tensor(14)),
+            .pm = tp(try c.tensor(15)),
+            .pl = tp(try c.tensor(16)),
+            .out = tp(try c.tensor(17)),
+            .ticket = tp(try c.tensor(18)),
+            .R = @intCast(try dim(q, 0)),
+            .H = @intCast(try dim(q, 1)),
+            .ring = @intCast(try c.int(19)),
+        };
+        const split: u32 = if (try c.opt(23)) |v| switch (v) {
+            .int => |x| @intCast(x),
+            else => return error.WrongArgument,
+        } else 0; // captures before R1 have no split: today's launch
+        var b = a;
+        const qrope: i64 = if (try c.opt(24)) |v| switch (v) {
+            .int => |n| n,
+            else => return error.WrongArgument,
+        } else 0;
+        b.qrope = @intFromBool(qrope != 0 and split > 0);
+        try o.attention(b, try c.u(22), numel(sl) > 0, numel(hi) > 0, split);
+        return true;
+    }
+    if (is(func, "topk")) return topkCall(o, args, kwargs, false);
+    return false;
+}
+
+/// tf_dsv41_attn_cuda_v1.topk, or with `bounded` tf_dsv41_topk_b_v1.topk (TF_DSV41_INDEX_BOUND, same arguments)
+pub fn topkCall(o: ops.Ops, args: []const Arg, kwargs: []const Kw, bounded: bool) !bool {
+    const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "s0", "c0", "o0", "n0", "nk0", "k0", "m0", "e0", "s1", "c1", "o1", "n1", "nk1", "k1", "m1", "e1", "jobs", "pos", "ratio", "bs", "R", "CL" } };
+    const jobs = try c.usz(16);
+    const pos = try c.tensor(17);
+    var a: ops.topk.Args = .{ .job = undefined, .pos = pos.ptr, .pos64 = @intFromBool(pos.dtype == .i64), .ratio = @intCast(try c.int(18)), .bs = @intCast(try c.int(19)) };
+    a.job[0] = try topkJob(c, 0);
+    a.job[1] = if (jobs > 1) try topkJob(c, 8) else a.job[0];
+    const e0 = try c.usz(7);
+    const ept = if (jobs > 1) @max(e0, try c.usz(15)) else e0;
+    if (bounded) try o.topKB(a, jobs, try c.usz(20), try c.usz(21), ept) else try o.topK(a, jobs, try c.usz(20), try c.usz(21), ept);
+    return true;
+}
+
+fn topkJob(c: Call, at: usize) !ops.topk.Job {
+    const sc = try c.tensor(at);
+    const cand = try c.tensor(at + 1);
+    const out = try c.tensor(at + 2);
+    return .{ .s = sc.ptr, .ss = sc.stride[0], .cand = tp(cand), .cs = stride0(cand), .out = out.ptr, .os = out.stride[0], .cnt = tp(try c.tensor(at + 3)), .nk = @intCast(try c.int(at + 4)), .k = @intCast(try c.int(at + 5)), .mode = @intCast(try c.int(at + 6)), .ept = @intCast(try c.int(at + 7)) };
+}
+
+// tf_dsv41_mhc_cuda_v1.run (mhc_cuda.cpp / dsv41_mhc_run_cuda)
+pub fn mhcRun(o: ops.Ops, args: []const Arg, kwargs: []const Kw) !bool {
+    const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "x", "xout", "g", "post", "comb", "pre", "fn", "base", "scale", "part", "c", "tap", "nw", "out", "opre", "opost", "ocomb", "cnt", "R", "mode", "eps", "hc_eps", "post_alpha", "iters", "spin", "defer" } };
+    const x = try c.tensor(0);
+    const g = try c.tensor(2);
+    const f = try c.tensor(6);
+    const tap = try c.tensor(11);
+    const nw = try c.tensor(12);
+    const a: ops.mhc.Args = .{
+        .x = tp(x),
+        .xs = x.stride[0],
+        .xout = tp(try c.tensor(1)),
+        .g = tp(g),
+        .gr = stride0(g),
+        .world = if (numel(g) != 0) @intCast(try dim(g, 0)) else 1,
+        .gbf16 = @intFromBool(g.dtype == .bf16),
+        .post = tp(try c.tensor(3)),
+        .comb = tp(try c.tensor(4)),
+        .pre = tp(try c.tensor(5)),
+        .@"fn" = tp(f),
+        .base = tp(try c.tensor(7)),
+        .scale = tp(try c.tensor(8)),
+        .part = tp(try c.tensor(9)),
+        .c = tp(try c.tensor(10)),
+        .tap = tp(tap),
+        .ts = stride0(tap),
+        .nw = nw.ptr,
+        .nwbf16 = @intFromBool(nw.dtype == .bf16),
+        .out = tp(try c.tensor(13)),
+        .opre = tp(try c.tensor(14)),
+        .opost = tp(try c.tensor(15)),
+        .ocomb = tp(try c.tensor(16)),
+        .cnt = tp(try c.tensor(17)),
+        .R = @intCast(try c.int(18)),
+        .eps = @floatCast(try c.float(20)),
+        .hc_eps = @floatCast(try c.float(21)),
+        .post_alpha = @floatCast(try c.float(22)),
+        .iters = @intCast(try c.int(23)),
+    };
+    // R1: spin < 0 (or no spin: a capture before R1) is today's finish; defer only with the tail
+    var b = a;
+    b.spin = if (try c.opt(24)) |v| switch (v) {
+        .int => |n| n,
+        else => return error.WrongArgument,
+    } else -1;
+    b.@"defer" = @intFromBool(b.spin >= 0 and (if (try c.opt(25)) |v| switch (v) {
+        .int => |n| n != 0,
+        .boolean => |t| t,
+        else => return error.WrongArgument,
+    } else false));
+    try o.mhcBoundary(b, try c.u(19), numel(f) != 0 and f.dtype == .f32);
+    return true;
+}
+
+// tf_dsv41_mhc_cuda_v1.coef (R1: dsv41_mhc_coef_cuda): a deferred boundary's coefficients
+pub fn mhcCoef(o: ops.Ops, args: []const Arg, kwargs: []const Kw) !bool {
+    const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "part", "base", "scale", "opre", "opost", "ocomb", "R", "eps", "hc_eps", "post_alpha", "iters" } };
+    const a: ops.mhc.Args = .{
+        .part = try c.ptr(0),
+        .base = try c.ptr(1),
+        .scale = try c.ptr(2),
+        .opre = try c.ptr(3),
+        .opost = try c.ptr(4),
+        .ocomb = try c.ptr(5),
+        .R = @intCast(try c.int(6)),
+        .eps = @floatCast(try c.float(7)),
+        .hc_eps = @floatCast(try c.float(8)),
+        .post_alpha = @floatCast(try c.float(9)),
+        .iters = @intCast(try c.int(10)),
+    };
+    try o.mhcCoef(a);
+    return true;
+}
+
+// tf_dsv41_mhc_pf_v1.run (mhc_pf.cpp / dsv41_mhc_pf_run_cuda)
+pub fn mhcPfRun(o: ops.Ops, args: []const Arg, kwargs: []const Kw) !bool {
+    const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "x", "xout", "g", "post", "comb", "pre", "fn", "part", "c", "tap", "R", "mode" } };
+    const x = try c.tensor(0);
+    const g = try c.tensor(2);
+    const f = try c.tensor(6);
+    const tap = try c.tensor(9);
+    const a: ops.mhc_pf.Args = .{
+        .x = tp(x),
+        .xs = x.stride[0],
+        .xout = tp(try c.tensor(1)),
+        .g = tp(g),
+        .gr = stride0(g),
+        .world = if (numel(g) != 0) @intCast(try dim(g, 0)) else 1,
+        .gbf16 = @intFromBool(g.dtype == .bf16),
+        .post = tp(try c.tensor(3)),
+        .comb = tp(try c.tensor(4)),
+        .pre = tp(try c.tensor(5)),
+        .@"fn" = f.ptr,
+        .part = tp(try c.tensor(7)),
+        .c = tp(try c.tensor(8)),
+        .tap = tp(tap),
+        .ts = stride0(tap),
+        .R = @intCast(try c.int(10)),
+    };
+    try o.mhcSite(a, try c.u(11), f.dtype == .f32, numel(tap) > 0);
+    return true;
+}
+
+/// route's `prune` (R1c): a list of 7 floats, absent or empty = off.
+fn pruneArg(c: Call, i: usize) !ops.rg.Prune {
+    const l = (try c.opt(i)) orelse return .{};
+    const items = switch (l) {
+        .list => |x| x,
+        else => return error.WrongArgument,
+    };
+    var v: [7]f64 = undefined;
+    if (items.len != 0 and items.len != 7) return error.WrongArgument;
+    for (items, 0..) |it, j| v[j] = switch (it) {
+        .float => |f| f,
+        .int => |n| @floatFromInt(n),
+        else => return error.WrongArgument,
+    };
+    return ops.rg.pruneOf(v[0..items.len]);
+}
+
+// tf_dsv41_router_gemv_v2.route (router_gemv.cpp / dsv41_rg_route_cuda)
+pub fn route(o: ops.Ops, args: []const Arg, kwargs: []const Kw) !bool {
+    const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "X", "W", "B", "pick", "wts", "LG", "cnt", "K", "slots", "scale", "rt", "nw", "ew", "select", "kit", "gid", "gcnt", "gmem", "GE", "group", "prune" } };
+    const x = try c.tensor(0);
+    const w = try c.tensor(1);
+    const lg = try c.tensor(5);
+    const cnt = try c.tensor(6);
+    const group = try c.boolean(19, false);
+    const gmem = try c.tensor(17);
+    if (try dim(x, 1) != ops.rg.D or try dim(w, 1) != ops.rg.D) return error.WrongArgument;
+    const a: ops.rg.Args = .{
+        .x = x.ptr,
+        .xs = x.stride[0],
+        .w = w.ptr,
+        .bias = try c.ptr(2),
+        .pick = try c.ptr(3),
+        .wts = try c.ptr(4),
+        .lg = lg.ptr,
+        .cnt = cnt.ptr,
+        .R = @intCast(try dim(x, 0)),
+        .E = @intCast(try dim(w, 0)),
+        .K = @intCast(try c.int(7)),
+        .slots = @intCast(try c.int(8)),
+        .select = @intFromBool(try c.boolean(13, false)),
+        .scale = @floatCast(try c.float(9)),
+        .kit = @intFromBool(try c.boolean(14, false)),
+        .group = @intFromBool(group),
+        .gid = if (group) try c.ptr(15) else 0,
+        .gcnt = if (group) try c.ptr(16) else 0,
+        .gmem = if (group) gmem.ptr else 0,
+        .GE = @intCast(try c.int(18)),
+        .maxm = if (group) @intCast(try dim(gmem, 1)) else 0,
+        .prune = try pruneArg(c, 20),
+    };
+    try o.route(a, try c.u(10), try c.u(11), try c.u(12), @intCast(numel(lg)), @intCast(numel(cnt)));
+    return true;
+}
+
+// tf_dsv41_pfdense_v1 (pfdense.cpp)
+pub fn pfdenseExt(o: ops.Ops, func: []const u8, args: []const Arg, kwargs: []const Kw) !bool {
+    if (is(func, "gemm")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "xh", "T", "stride_k", "stride_nb", "svh", "bias", "out", "K2", "lanes", "cfg", "group" } };
+        const xh = try c.tensor(0);
+        const T = try c.tensor(1);
+        const out = try c.tensor(6);
+        const ot: ops.pfd.OutType = switch (out.dtype) {
+            .f32 => .f32,
+            .bf16 => .bf16,
+            .f16 => .f16,
+            else => return error.WrongArgument,
+        };
+        const a: ops.pfd.Args = .{ .xh = xh.ptr, .T = T.ptr, .stride_k = try c.int(2), .stride_nb = try c.int(3), .svh = try c.ptr(4), .bias = try c.ptrOrNull(5), .out = out.ptr, .o_stride = out.stride[0], .M = @intCast(try dim(xh, 0)), .K = @intCast(try dim(xh, 1)), .N = @intCast(try dim(out, 1)), .group = @intCast(try c.int(10)), .out_type = @backingInt(ot) };
+        try o.pfdGemm(a, try c.u(7), try c.boolean(8, false), try c.usz(9), @intCast(numel(T)));
+        return true;
+    }
+    if (is(func, "dequant")) {
+        const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "T", "W", "stride_k", "stride_nb", "K2", "lanes" } };
+        const w = try c.tensor(1);
+        try o.pfdDequant(try c.ptr(0), try c.int(2), try c.int(3), w.ptr, try dim(w, 0), try dim(w, 1), try c.u(4), try c.boolean(5, false));
+        return true;
+    }
+    return false;
+}
+
+// tf_dsv41_l2pace_v1.paced / tf_dsv41_l2pf_v1.segments: L2 prefetch only (no outputs; replayed for timing parity)
+pub fn paced(o: ops.Ops, args: []const Arg, kwargs: []const Kw) !bool {
+    const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "table", "ctas", "chunk", "ns_per_piece", "delay_ns" } };
+    const t = try c.tensor(0);
+    const n = try dim(t, 0);
+    if (n == 0) return true;
+    try o.l2Paced(t.ptr, n, try c.usz(1), try c.u(2), @intCast(try c.int(3)), @intCast(try c.int(4)));
+    return true;
+}
+
+pub fn segments(o: ops.Ops, args: []const Arg, kwargs: []const Kw) !bool {
+    const c: Call = .{ .args = args, .kwargs = kwargs, .names = &.{ "table", "grid", "threads", "chunk", "bulk" } };
+    const t = try c.tensor(0);
+    const n = try dim(t, 0);
+    if (n == 0) return true;
+    try o.l2Segments(t.ptr, n, try c.usz(1), try c.usz(2), try c.u(3), try c.boolean(4, false));
+    return true;
+}
diff --git a/zig/src/families/deepseek_v41/kernels_shapes.zig b/zig/src/families/deepseek_v41/kernels_shapes.zig
new file mode 100644
index 0000000000..6c7bd6c135
--- /dev/null
+++ b/zig/src/families/deepseek_v41/kernels_shapes.zig
@@ -0,0 +1,407 @@
+//! DeepSeek-V4.1 kernel shapes: attention, top-k, mHC, router and prefill GEMM args, symbols and checks.
+
+const std = @import("std");
+
+pub const Error = error{ Shape, Unsupported };
+
+// -- attn_cuda.cu: dsv41_attn -----------------------------------------------------------------------------------
+
+pub const attn = struct {
+    pub const NCH = 5;
+    pub const BMQ = 16;
+    pub const WINDOW = 128;
+    pub const NT = 256;
+    pub const SMEM: u32 = 98816;
+
+    pub const Args = extern struct {
+        q: u64 = 0,
+        cv: u64 = 0,
+        csc: u64 = 0,
+        cvs: i64 = 0,
+        css: i64 = 0,
+        tok: u64 = 0,
+        ts: i64 = 0,
+        cnt: u64 = 0,
+        sv: u64 = 0,
+        ssc: u64 = 0,
+        svs: i64 = 0,
+        sss: i64 = 0,
+        lo: u64 = 0,
+        hi: u64 = 0,
+        pos: u64 = 0,
+        sl: u64 = 0,
+        pt: u64 = 0,
+        pts: i64 = 0,
+        psh: c_int = 0,
+        sink: u64 = 0,
+        cs: u64 = 0,
+        csst: i64 = 0,
+        po: u64 = 0,
+        pm: u64 = 0,
+        pl: u64 = 0,
+        out: u64 = 0,
+        ticket: u64 = 0,
+        R: c_int = 0,
+        H: c_int = 0,
+        ring: c_int = 0,
+        has_comp: c_int = 0,
+        qrope: c_int = 0, // R1c (split_kernel only): q arrives un-rotated and is RoPE'd as it loads
+    };
+
+    comptime {
+        std.debug.assert(@offsetOf(Args, "psh") == 144 and @offsetOf(Args, "sink") == 152 and @offsetOf(Args, "qrope") == 232 and @sizeOf(Args) == 240);
+    }
+
+    pub fn symbol(comptime kw: u32, comptime rows: bool, comptime hi: bool) [:0]const u8 {
+        return std.fmt.comptimePrint("_ZN10dsv41_attn11attn_kernelILi{d}ELb{d}ELb{d}EEEvNS_4ArgsE", .{ kw, @intFromBool(rows), @intFromBool(hi) });
+    }
+
+    /// R1 split_kernel: P CTAs a chunk, a cluster of P (P 4); the same bits as attn_kernel.
+    pub fn splitSymbol(comptime kw: u32, comptime rows: bool, comptime hi: bool, comptime p: u32) [:0]const u8 {
+        return std.fmt.comptimePrint("_ZN10dsv41_attn12split_kernelILi{d}ELb{d}ELb{d}ELi{d}EEEvNS_4ArgsE", .{ kw, @intFromBool(rows), @intFromBool(hi), p });
+    }
+
+    /// Split

::SMEM: P 1 the same as attn_kernel's, P 4 the S rows' B pairs beside the PV slice. + pub fn splitSmem(p: u32) u32 { + return if (p == 4) 100864 else SMEM; + } + + /// The ticket's ranks a (row, head tile) once any launch splits: the ticket buffer must hold R H / 16 * MAXP. + pub const MAXP = 4; + + /// attn_cuda.split(R) (TF_DSV41_ATTN_SPLIT on): P 4 up to `split_rows` rows (2), P 1 above. + pub fn splitFor(rows: usize, split_rows: usize) u32 { + return if (rows <= split_rows) 4 else 1; + } + + /// dispatch(): the instance index (kw 4 | rows | has_hi), after its checks. + pub fn instance(a: Args, kw: u32, rows: bool, has_hi: bool) Error!usize { + if (a.R < 1 or @rem(a.H, BMQ) != 0 or a.ring < WINDOW or (a.ring & (a.ring - 1)) != 0) return error.Shape; + return @as(usize, if (kw == 4) 4 else 0) + @as(usize, if (rows) 2 else 0) + @intFromBool(has_hi); + } +}; + +// topk_cuda.cu: dsv41_topk +pub const topk = struct { + pub const NT = 512; + pub const MAXCL = 8; + pub const MAXEPT = 31; + + pub const Job = extern struct { s: u64 = 0, ss: i64 = 0, cand: u64 = 0, cs: i64 = 0, out: u64 = 0, os: i64 = 0, cnt: u64 = 0, nk: c_int = 0, k: c_int = 0, mode: c_int = 0, ept: c_int = 0 }; + pub const Args = extern struct { job: [2]Job, pos: u64 = 0, pos64: c_int = 0, ratio: c_int = 0, bs: c_int = 0 }; + + comptime { + std.debug.assert(@sizeOf(Job) == 72 and @offsetOf(Args, "pos") == 144 and @sizeOf(Args) == 168); + } + + pub const symbol = "_ZN10dsv41_topk11topk_kernelENS_4ArgsE"; + /// topk_b.cu (ours, TF_DSV41_INDEX_BOUND): topk_kernel with each row stopped at its own visible end, the same Args + pub const symbol_b = "_ZN10dsv41_topk13topk_b_kernelENS_4ArgsE"; + + pub fn smem(ept: usize) u32 { + return @intCast(NT * ept * 4); + } +}; + +// -- mhc_cuda.cu: dsv41_mhc (decode boundary, <= 16 rows) ------------------------------------------------------- + +pub const mhc = struct { + pub const NB = 40; + pub const NT = 128; + pub const MAXR = 16; + pub const ROWB = 128; + /// coef_kernel is a CTA a row: TF_DSV41_MHC_PFDEC launches it past MAXR, up to a decode window's 64 rows + pub const COEF_ROWS = 64; + + pub const Args = extern struct { + x: u64 = 0, + xs: i64 = 0, + xout: u64 = 0, + g: u64 = 0, + gr: i64 = 0, + world: c_int = 1, + gbf16: c_int = 0, + post: u64 = 0, + comb: u64 = 0, + pre: u64 = 0, + @"fn": u64 = 0, + base: u64 = 0, + scale: u64 = 0, + part: u64 = 0, + c: u64 = 0, + tap: u64 = 0, + ts: i64 = 0, + nw: u64 = 0, + nwbf16: c_int = 0, + out: u64 = 0, + opre: u64 = 0, + opost: u64 = 0, + ocomb: u64 = 0, + cnt: u64 = 0, + R: c_int = 0, + eps: f32 = 0, + hc_eps: f32 = 0, + post_alpha: f32 = 0, + iters: c_int = 0, + spin: i64 = -1, // R1 tail: SM cycles a CTA waits for the last arrival (the binding's -1: today's finish) + @"defer": c_int = 0, // R1 tail: the coefficient items run in coef_kernel on a side stream + }; + + comptime { + std.debug.assert(@offsetOf(Args, "post") == 48 and @offsetOf(Args, "nwbf16") == 136 and @offsetOf(Args, "R") == 184 and @offsetOf(Args, "spin") == 208 and @sizeOf(Args) == 224); + } + + pub const coef_symbol = "_ZN9dsv41_mhc11coef_kernelENS_4ArgsE"; + + /// (POST, COLL, MIX, ALL4, FN32) of dispatch's cases, by mode * 2 + fn32 (mode 3 ignores fn32). + pub const instances = [_]struct { post: bool, coll: u32, mix: bool, all4: bool, fn32: bool }{ + .{ .post = true, .coll = 2, .mix = true, .all4 = false, .fn32 = false }, + .{ .post = true, .coll = 2, .mix = true, .all4 = false, .fn32 = true }, + .{ .post = false, .coll = 1, .mix = true, .all4 = false, .fn32 = false }, + .{ .post = false, .coll = 1, .mix = true, .all4 = false, .fn32 = true }, + .{ .post = false, .coll = 2, .mix = true, .all4 = false, .fn32 = false }, + .{ .post = false, .coll = 2, .mix = true, .all4 = false, .fn32 = true }, + .{ .post = true, .coll = 2, .mix = false, .all4 = true, .fn32 = false }, + }; + + /// boundary_kernel (TAIL: R1's parallel tail, default false: still mangled) + pub fn symbol(comptime i: usize, comptime tail: bool) [:0]const u8 { + const s = instances[i]; + return std.fmt.comptimePrint("_ZN9dsv41_mhc15boundary_kernelILb{d}ELi{d}ELb{d}ELb{d}ELb{d}ELb{d}EEEvNS_4ArgsE", .{ @intFromBool(s.post), s.coll, @intFromBool(s.mix), @intFromBool(s.all4), @intFromBool(s.fn32), @intFromBool(tail) }); + } + + /// dispatch(): mode 0 boundary, 1 site from stream 0, 2 site with a carried pre-mix, 3 final. + pub fn instance(a: Args, mode: u32, fn32: bool) Error!usize { + if (a.R < 1 or a.R > MAXR or a.world < 1 or a.world > 8) return error.Shape; + if (mode == 3) return 6; + if (mode > 2) return error.Unsupported; + return mode * 2 + @intFromBool(fn32); + } +}; + +// mhc_pf.cu: dsv41_mhc_pf (prefill site, 16 rows a CTA) +pub const mhc_pf = struct { + pub const NB = 40; + pub const NT = 128; + pub const BM = 16; + pub const MAXW = 8; + + pub const Args = extern struct { + x: u64 = 0, + xs: i64 = 0, + xout: u64 = 0, + g: u64 = 0, + gr: i64 = 0, + world: c_int = 1, + gbf16: c_int = 0, + post: u64 = 0, + comb: u64 = 0, + pre: u64 = 0, + @"fn": u64 = 0, + part: u64 = 0, + c: u64 = 0, + tap: u64 = 0, + ts: i64 = 0, + R: c_int = 0, + }; + + comptime { + std.debug.assert(@offsetOf(Args, "R") == 112 and @sizeOf(Args) == 120); + } + + /// Lay::BYTES: staged streams, the collapsed rows and fn. + pub fn smem(fn32: bool) u32 { + const pb = 128 + 8; + const pf = 128 + 4; + const xs = 4 * BM * pb * 2; + const cs = BM * pb * 2; + const fnb: u32 = if (fn32) 4 * 24 * pf * 4 else 4 * 24 * pb * 2; + return xs + cs + fnb; + } + + /// (POST, COLL, FN32, TAP) by dispatch's case (mode * 4 + fn32 * 2 + tap). + pub const instances = [_]struct { case: u32, post: bool, coll: u32, fn32: bool, tap: bool }{ + .{ .case = 0, .post = true, .coll = 2, .fn32 = false, .tap = false }, + .{ .case = 1, .post = true, .coll = 2, .fn32 = false, .tap = true }, + .{ .case = 2, .post = true, .coll = 2, .fn32 = true, .tap = false }, + .{ .case = 3, .post = true, .coll = 2, .fn32 = true, .tap = true }, + .{ .case = 4, .post = false, .coll = 1, .fn32 = false, .tap = false }, + .{ .case = 6, .post = false, .coll = 1, .fn32 = true, .tap = false }, + .{ .case = 8, .post = false, .coll = 2, .fn32 = false, .tap = false }, + .{ .case = 10, .post = false, .coll = 2, .fn32 = true, .tap = false }, + }; + + pub fn symbol(comptime i: usize) [:0]const u8 { + const s = instances[i]; + return std.fmt.comptimePrint("_ZN12dsv41_mhc_pf11site_kernelILb{d}ELi{d}ELb{d}ELb{d}EEEvNS_4ArgsE", .{ @intFromBool(s.post), s.coll, @intFromBool(s.fn32), @intFromBool(s.tap) }); + } + + pub fn instance(a: Args, mode: u32, fn32: bool, tap: bool) Error!usize { + if (a.R < 1 or a.world < 1 or a.world > MAXW or (tap and mode != 0)) return error.Shape; + const case = mode * 4 + @as(u32, if (fn32) 2 else 0) + @intFromBool(tap); + for (instances, 0..) |s, i| if (s.case == case) return i; + return error.Unsupported; + } +}; + +// -- router_gemv.cu: dsv41_rg ----------------------------------------------------------------------------------- + +pub const rg = struct { + pub const D = 5120; + pub const EPL = 16; + pub const KMAX = 8; + pub const GE_MAX = 512; + pub const rts = [_]u32{ 1, 2, 4, 8, 16 }; + pub const wide = [_][2]u32{ .{ 8, 1 }, .{ 4, 1 }, .{ 8, 2 }, .{ 4, 2 } }; + pub const narrow = [_]u32{ 1, 2, 4, 8 }; + + pub const Args = extern struct { + x: u64 = 0, + xs: i64 = 0, + w: u64 = 0, + bias: u64 = 0, + pick: u64 = 0, + wts: u64 = 0, + lg: u64 = 0, + cnt: u64 = 0, + R: c_int = 0, + E: c_int = 0, + K: c_int = 0, + slots: c_int = 0, + select: c_int = 0, + scale: f32 = 0, + kit: c_int = 0, + gid: u64 = 0, + gcnt: u64 = 0, + gmem: u64 = 0, + GE: c_int = 0, + maxm: c_int = 0, + group: c_int = 0, + prune: Prune = .{}, // R1c: the prune rule folded into the selection (on 0: off) + }; + + /// router_gemv.cu Prune: [min_w, topp, scale, min_k, orig, drop, dup] (prune.fold_args) in fp32 / int, on = 1. + pub const Prune = extern struct { min_w: f32 = 0, topp: f32 = 0, scale: f32 = 0, min_k: c_int = 0, orig: c_int = 0, drop: c_int = 0, dup: c_int = 0, on: c_int = 0 }; + + /// The binding's prune vector (7 doubles, empty: off) as the kernel's struct (fp32 thresholds, as it rounds them). + pub fn pruneOf(v: []const f64) error{Shape}!Prune { + if (v.len == 0) return .{}; + if (v.len != 7) return error.Shape; + return .{ .min_w = @floatCast(v[0]), .topp = @floatCast(v[1]), .scale = @floatCast(v[2]), .min_k = @intFromFloat(v[3]), .orig = @intFromFloat(v[4]), .drop = @intFromFloat(v[5]), .dup = @intFromFloat(v[6]), .on = 1 }; + } + + comptime { + std.debug.assert(@sizeOf(Prune) == 32); + std.debug.assert(@offsetOf(Args, "gid") == 96 and @offsetOf(Args, "group") == 128 and @offsetOf(Args, "prune") == 132 and @sizeOf(Args) == 168); + } + + pub fn gemvSymbol(comptime rt: u32, comptime nw: u32, comptime ew: u32) [:0]const u8 { + return std.fmt.comptimePrint("_ZN8dsv41_rg11gemv_kernelILi{d}ELi{d}ELi{d}EEEvNS_4ArgsE", .{ rt, nw, ew }); + } + + pub fn narrowSymbol(comptime rt: u32, comptime nw: u32) [:0]const u8 { + return std.fmt.comptimePrint("_ZN8dsv41_rg13narrow_kernelILi{d}ELi{d}EEEvNS_4ArgsE", .{ rt, nw }); + } + + /// dsv41_rg_route_cuda's checks (row stride / alignment of x and the buffer sizes are the caller's). + pub fn check(a: Args, rt: u32, nw: u32, ew: u32, lg_len: usize, cnt_len: usize) Error!void { + const E: usize = @intCast(a.E); + const R: usize = @intCast(a.R); + if (E < 1 or E > 32 * EPL or a.K < 1 or a.K > KMAX or a.K > a.E or a.slots < a.K or a.slots > a.K + 1) return error.Shape; + if (lg_len < R * E) return error.Shape; + if (a.select != 0 and cnt_len < (R + rt - 1) / rt) return error.Shape; + const is_wide = (nw == 4 or nw == 8) and (ew == 1 or ew == 2); + const is_narrow = ew == 0 and (nw == 1 or nw == 2 or nw == 4 or nw == 8); + if (std.mem.indexOfScalar(u32, &rts, rt) == null or !(is_wide or is_narrow)) return error.Unsupported; + if (a.group != 0 and (a.select == 0 or !is_narrow or R > rt or a.GE < 1 or a.GE > GE_MAX)) return error.Shape; + } +}; + +// -- pfdense.cuh: dsv41_pfd (fused prefill dense GEMM, one fatbin a width) -------------------------------------- + +pub const pfd = struct { + pub const k2s = [_]u32{ 8, 10, 12, 16 }; + /// DSV41_PFD_CFGS: (BM, WM, WN, KS, NST) by id. + pub const cfgs = [_][5]u32{ .{ 128, 2, 4, 2, 3 }, .{ 128, 2, 4, 2, 4 }, .{ 128, 4, 2, 2, 3 }, .{ 64, 2, 2, 2, 4 }, .{ 64, 1, 4, 2, 4 }, .{ 128, 2, 4, 2, 2 }, .{ 32, 1, 4, 2, 4 }, .{ 256, 4, 2, 2, 3 } }; + pub const OutType = enum(c_int) { f16 = 0, bf16 = 1, f32 = 2 }; + + pub const Args = extern struct { + xh: u64 = 0, + T: u64 = 0, + stride_k: i64 = 0, + stride_nb: i64 = 0, + svh: u64 = 0, + bias: u64 = 0, + out: u64 = 0, + o_stride: i64 = 0, + M: c_int = 0, + K: c_int = 0, + N: c_int = 0, + group: c_int = 0, + out_type: c_int = 0, + }; + + comptime { + std.debug.assert(@offsetOf(Args, "M") == 64 and @sizeOf(Args) == 88); + } + + pub fn threads(cfg: usize) u32 { + return cfgs[cfg][1] * cfgs[cfg][2] * 32; + } + + /// Lay::SMEM (the layout does not depend on LANES). + pub fn smem(cfg: usize, k2: u32) u32 { + const bm, const wm, _, const ks, const nst = cfgs[cfg]; + const a_bytes = bm * (16 * ks) * 2; + const w_bytes = ks * 8 * (4 * k2) * 4; + const ring = nst * (a_bytes + w_bytes); + const main = ring + 2 * (ks * 8 * 32 * 16); + const epi = (bm / wm) * 128 * 2 * 2; + return @max(main, epi); + } + + pub fn symbol(comptime k2: u32, comptime lanes: bool, comptime cfg: usize) [:0]const u8 { + const c = cfgs[cfg]; + return std.fmt.comptimePrint("_ZN9dsv41_pfd10pfd_kernelILi{d}ELb{d}ENS_3CfgILi{d}ELi{d}ELi{d}ELi{d}ELi{d}EEEEEvNS_4ArgsE", .{ k2, @intFromBool(lanes), c[0], c[1], c[2], c[3], c[4] }); + } + + pub fn dequantSymbol(comptime k2: u32, comptime lanes: bool) [:0]const u8 { + return std.fmt.comptimePrint("_ZN9dsv41_pfd18pfd_dequant_kernelILi{d}ELb{d}EEEvPKjxxP6__halfi", .{ k2, @intFromBool(lanes) }); + } + + /// pfdense.cpp gemm's checks on the shapes and strides (dtype / alignment are the caller's). + pub fn check(a: Args, k2: u32, lanes: bool, cfg: usize, words: usize) Error!void { + if (std.mem.indexOfScalar(u32, &k2s, k2) == null or (lanes and k2 == 16)) return error.Unsupported; + if (cfg >= cfgs.len) return error.Unsupported; + const ks: i64 = cfgs[cfg][3]; + const K: i64 = a.K; + const N: i64 = a.N; + if (a.M < 1 or @rem(K, 128) != 0 or K < 128 or @rem(K, 16 * ks) != 0) return error.Shape; + if (@rem(N, 128) != 0 or N < 128 or @rem(a.o_stride, 2) != 0 or a.o_stride < N) return error.Shape; + const tw: i64 = 4 * @as(i64, k2); + if (@rem(a.stride_k, 4) != 0 or @rem(a.stride_nb, 4) != 0 or a.stride_k < 8 * tw or a.stride_nb < 8 * tw) return error.Shape; + const last = (@divTrunc(K, 16) - 1) * a.stride_k + (@divTrunc(N, 128) - 1) * a.stride_nb + 8 * tw; + if (last > @as(i64, @intCast(words))) return error.Shape; + if (a.group < 1 or a.group > 1024) return error.Shape; + } + + /// pfdense.heuristic (no sweep table): the largest row tile whose last wave is not mostly empty. + pub fn heuristic(k: usize, n: usize, k2: u32, m: usize, sms: usize) [2]usize { + const nb = n / 128; + var best: ?usize = null; + var best_eff: f64 = -1.0; + for ([_]usize{ 0, 3, 6 }) |cfg| { + if (k % (16 * cfgs[cfg][3]) != 0) continue; + const bm = cfgs[cfg][0]; + const blocks: usize = if (2 * (smem(cfg, k2) + 1024) <= 102400) 2 else 1; + const ctas = ((m + bm - 1) / bm) * nb; + const slots = sms * blocks; + const eff = @as(f64, @floatFromInt(ctas)) / @as(f64, @floatFromInt(((ctas + slots - 1) / slots) * slots)); + if (eff > best_eff + 0.10) { + best = cfg; + best_eff = eff; + } + } + return .{ best orelse 0, 8 }; + } +}; diff --git a/zig/src/families/deepseek_v41/kernels_x3gm.zig b/zig/src/families/deepseek_v41/kernels_x3gm.zig new file mode 100644 index 0000000000..0578fc17ad --- /dev/null +++ b/zig/src/families/deepseek_v41/kernels_x3gm.zig @@ -0,0 +1,354 @@ +//! DeepSeek-V4.1's x3gm routed-expert GEMM: width and tile tables, v2/v3 shapes and tuning, args and launches. + +const std = @import("std"); +const cuda = @import("cuda"); +const exl3 = @import("kernels_exl3.zig"); + +const Ops = exl3.Ops; +const Input = exl3.Input; +const Error = exl3.Error; +const int = exl3.int; +const go = exl3.go; + +/// x3gm's widths (x3gm.K2S) and tile tables (MTL, NG, KS, NSA, NGR) by configuration id (x3gm.cu GM_GU* / GM_DN*). +pub const gm_k2s = [_]u32{ 3, 4, 5, 6, 7, 8, 10 }; +pub const gu_tiles = [_][5]u32{ .{ 2, 8, 4, 3, 4 }, .{ 2, 8, 2, 4, 4 }, .{ 1, 16, 2, 4, 4 }, .{ 1, 16, 4, 3, 4 }, .{ 2, 16, 2, 3, 4 }, .{ 2, 8, 2, 6, 4 } }; +pub const dn_tiles = [_][5]u32{ .{ 2, 8, 4, 4, 8 }, .{ 1, 8, 4, 4, 8 }, .{ 1, 16, 4, 4, 8 }, .{ 1, 16, 2, 6, 8 }, .{ 2, 16, 4, 3, 16 } }; +/// sm_121's opt-in dynamic shared memory a CTA (x3gm.SMEM_MAX). +pub const gm_smem_max: usize = 101376; + +pub fn gmK2Index(k2: u32) ?usize { + return std.mem.indexOfScalar(u32, &gm_k2s, k2); +} + +/// x3gm.cu Cfg: threads and dynamic shared memory of one instance. +pub const GmShape = struct { + threads: u32, + smem: u32, + + pub fn of(mats: u32, xm: u32, k2: u32, t: [5]u32) GmShape { + const mtl, const ng, const ks, const nsa, const ngr = t; + const bm = ng * 8; + const w = mats * (8 / mtl); + const a_bytes: usize = @as(usize, xm) * bm * (ks * 16) * 2; + const w_bytes: usize = @as(usize, mats) * ks * 8 * (4 * k2) * 4; + const ring = nsa * (a_bytes + w_bytes); + const epi: usize = @as(usize, mats) * (ngr * 8) * 132 * 4; + return .{ .threads = w * 32, .smem = @intCast(@max(ring, epi)) }; + } +}; + +/// x3gm v2 (gm2_kernel): the tiles it is built at (V2_GU / V2_DN). +pub const v2_gu = [_]usize{ 0, 1 }; +/// gate / up's 128-member tile (gu_tiles[2], NG 16) that x3gm3.cu builds gm2_kernel at, two inputs only +pub const gu2_cfg: usize = 2; +pub const v2_dn = [_]usize{0}; + +/// x3gm.cu Smem2: the widest width's dynamic shared memory; rings cut (Ring2) to keep 2 CTAs an SM. +pub fn gm2Shape(mats: u32, xm: u32, t: [5]u32) GmShape { + const mtl, const ng, const ks, const nsa, const ngr = t; + const four = GmShape.of(mats, xm, 4, t); + const two_ctas = 2 * (@as(usize, four.smem) + 1536) <= 102400 and four.threads <= 256 and mtl * ng <= 16; // Cfg::MINB == 2 + var smem: u32 = 0; + for (gm_k2s) |k2| { + const stage: usize = @as(usize, xm) * (ng * 8) * (ks * 16) * 2 + @as(usize, mats) * ks * 8 * (4 * k2) * 4; + var ring: u32 = nsa; + if (two_ctas) { + while (ring > 2 and 2 * (stage * ring + 1536) > 102400) ring -= 1; + } + smem = @max(smem, GmShape.of(mats, xm, k2, .{ mtl, ng, ks, ring, ngr }).smem); + } + return .{ .threads = four.threads, .smem = smem }; +} + +/// x3gm v3 (x3gm3.cu's gm3_kernel): v2 plus next-item read-ahead and LEAN reads; variants in its instance order. +pub const V3 = struct { mats: u32, xm: u32, t: [5]u32, lean: u32 }; +pub const v3_variants = [_]V3{ + .{ .mats = 2, .xm = 2, .t = .{ 2, 8, 2, 4, 4 }, .lean = 0 }, // v2's GU1 + claim-ahead + .{ .mats = 2, .xm = 2, .t = .{ 2, 8, 2, 4, 4 }, .lean = 1 }, + .{ .mats = 2, .xm = 2, .t = .{ 1, 8, 2, 4, 4 }, .lean = 1 }, // 16-warp CTAs + .{ .mats = 2, .xm = 2, .t = .{ 1, 8, 4, 3, 4 }, .lean = 1 }, + .{ .mats = 2, .xm = 1, .t = .{ 2, 8, 4, 3, 4 }, .lean = 1 }, // v2's GU0 (one input) + claim-ahead + LEAN + .{ .mats = 2, .xm = 1, .t = .{ 1, 8, 4, 3, 4 }, .lean = 1 }, + .{ .mats = 1, .xm = 1, .t = .{ 2, 8, 4, 4, 8 }, .lean = 0 }, // v2's DN0 + claim-ahead + .{ .mats = 1, .xm = 1, .t = .{ 2, 8, 4, 4, 8 }, .lean = 1 }, + .{ .mats = 1, .xm = 1, .t = .{ 1, 8, 4, 4, 8 }, .lean = 0 }, // 8-warp CTAs: 16 warps an SM + .{ .mats = 1, .xm = 1, .t = .{ 1, 8, 4, 4, 8 }, .lean = 1 }, + .{ .mats = 1, .xm = 1, .t = .{ 1, 8, 2, 6, 8 }, .lean = 1 }, +}; + +pub fn gm3Symbol(comptime v: V3) [:0]const u8 { + return std.fmt.comptimePrint("_ZN10dsv41_x3gm10gm3_kernelILi{d}ELi{d}ELi{d}ELi{d}ELi{d}ELi{d}ELi{d}ELi{d}EEEvNS_4ArgsE", .{ v.mats, v.xm, v.t[0], v.t[1], v.t[2], v.t[3], v.t[4], v.lean }); +} + +/// v3's variants for one projection: gate / up at one or two inputs (xm), or down. +pub fn v3Of(mats: u32, xm: u32, out: []usize) []usize { + var n: usize = 0; + for (v3_variants, 0..) |v, i| if (v.mats == mats and v.xm == xm) { + out[n] = i; + n += 1; + }; + return out[0..n]; +} + +/// TF_DSV41_GM_V3=1: prefill's x3gm through v3 (v3_default's variants); unset or 0: v2 (default; v3 is bit-identical). +pub fn gmV3() bool { + const v = std.c.getenv("TF_DSV41_GM_V3") orelse return false; + return !std.mem.eql(u8, std.mem.span(v), "0"); +} + +/// v3's default variant of a projection (from per-shape timing). +pub const v3_default: struct { gu: [2]usize, dn: usize } = .{ .gu = .{ 4, 1 }, .dn = 9 }; + +/// x3gm.tuned2: (gate/up cfg, down cfg) of a v2 block: forced ids where built, else 0 / 1 by rotated inputs; down 0. +pub fn gmTuned2(force_gu: ?usize, force_dn: ?usize, shx: bool) [2]usize { + const gu = if (force_gu) |f| (if (std.mem.indexOfScalar(usize, &v2_gu, f) != null) f else null) else null; + const dn = if (force_dn) |f| (if (std.mem.indexOfScalar(usize, &v2_dn, f) != null) f else null) else null; + return .{ gu orelse (if (shx) 0 else 1), dn orelse 0 }; +} + +/// The host check before a v2 launch: every expert's width is one gm2_kernel dispatches. +pub fn gm2WidthsOk(widths: []const u32) bool { + for (widths) |w| if (gmK2Index(w) == null) return false; + return true; +} + +pub fn gm2GuSymbol(comptime xm: u32, comptime cfg: usize) [:0]const u8 { + const t = gu_tiles[cfg]; + return std.fmt.comptimePrint("_ZN10dsv41_x3gm10gm2_kernelILi2ELi{d}ELi{d}ELi{d}ELi{d}ELi{d}ELi{d}EEEvNS_4ArgsE", .{ xm, t[0], t[1], t[2], t[3], t[4] }); +} + +pub fn gm2DnSymbol(comptime cfg: usize) [:0]const u8 { + const t = dn_tiles[cfg]; + return std.fmt.comptimePrint("_ZN10dsv41_x3gm10gm2_kernelILi1ELi1ELi{d}ELi{d}ELi{d}ELi{d}ELi{d}EEEvNS_4ArgsE", .{ t[0], t[1], t[2], t[3], t[4] }); +} + +/// x3gm.smem_bytes: Cfg::SMEM plus the static rows / item words. +pub fn gmSmemBytes(t: [5]u32, mats: u32, xm: u32, k2: u32) usize { + return @as(usize, GmShape.of(mats, xm, k2, t).smem) + 4 * 8 * t[1] + 4; +} + +pub const GmKind = enum { gu, dn }; + +/// x3gm_plan.cu takes E < 512 experts. +pub const plan_max_experts = 512; + +/// x3gm.plan's table length: P / bm + E + 1 passes at most. +pub fn planPasses(pairs: usize, experts: usize, bm: usize) usize { + return pairs / bm + experts + 1; +} + +/// x3gm.fits: whether a configuration builds a CTA at this width (gate/up: `xm` rotated inputs) on sm_121. +pub fn gmFits(kind: GmKind, cfg: usize, k2: u32, xm: u32) bool { + return switch (kind) { + .gu => cfg < gu_tiles.len and gmSmemBytes(gu_tiles[cfg], 2, xm, k2) <= gm_smem_max, + .dn => cfg < dn_tiles.len and gmSmemBytes(dn_tiles[cfg], 1, 1, k2) <= gm_smem_max, + }; +} + +/// x3gm.bm_of: members a pass of a configuration. +pub fn gmMembers(kind: GmKind, cfg: usize) u32 { + return 8 * (if (kind == .gu) gu_tiles[cfg] else dn_tiles[cfg])[1]; +} + +/// x3gm.TUNE row: from `members` an expert on average, gate/up `gu` (null: by shx; else one / two inputs), down `dn`. +pub const TuneRow = struct { members: f64, gu: ?[2]u32, dn: u32 }; +pub const tune_default = [_]TuneRow{ .{ .members = 0, .gu = null, .dn = 0 }, .{ .members = 48, .gu = .{ 3, 2 }, .dn = 2 } }; + +/// x3gm.tuned: (gate/up cfg, down cfg) of a block of `rows` rows; `force_gu` / `force_dn` are TF_DSV41_GM_CFG's ids. +pub fn gmTuned(table: []const TuneRow, force_gu: ?usize, force_dn: ?usize, rows: usize, slots: usize, experts: usize, shx: bool, k2gu: u32, k2d: u32) [2]usize { + const m = @as(f64, @floatFromInt(rows * slots)) / @as(f64, @floatFromInt(@max(1, experts))); + var ent = table[0]; + for (table) |r| { + if (m > r.members or r.members == 0) ent = r; + } + var gu: usize = if (ent.gu) |p| (if (shx) p[0] else p[1]) else (if (shx) 0 else 1); + if (force_gu) |f| { + gu = f; + } else if (!gmFits(.gu, gu, k2gu, if (shx) 1 else 2)) { + gu = if (shx) 0 else 1; + } + var dn: usize = ent.dn; + if (force_dn) |f| { + dn = f; + } else if (!gmFits(.dn, dn, k2d, 1)) { + dn = 0; + } + return .{ gu, dn }; +} + +pub fn gmGuSymbol(comptime xm: u32, comptime k2: u32, comptime cfg: usize) [:0]const u8 { + const t = gu_tiles[cfg]; + return std.fmt.comptimePrint("_ZN10dsv41_x3gm9gm_kernelILi2ELi{d}ELi{d}ELi{d}ELi{d}ELi{d}ELi{d}ELi{d}EEEvNS_4ArgsE", .{ xm, k2, t[0], t[1], t[2], t[3], t[4] }); +} + +pub fn gmDnSymbol(comptime k2: u32, comptime cfg: usize) [:0]const u8 { + const t = dn_tiles[cfg]; + return std.fmt.comptimePrint("_ZN10dsv41_x3gm9gm_kernelILi1ELi1ELi{d}ELi{d}ELi{d}ELi{d}ELi{d}ELi{d}EEEvNS_4ArgsE", .{ k2, t[0], t[1], t[2], t[3], t[4] }); +} + +pub fn gmDequantSymbol(comptime k2: u32) [:0]const u8 { + return std.fmt.comptimePrint("_ZN10dsv41_x3gm14dequant_kernelILi{d}EEEvPKjP6__halfi", .{k2}); +} + +/// x3gm.cu's Args, passed by value. +pub const GmArgs = extern struct { + x0: u64 = 0, + x1: u64 = 0, + t0: u64 = 0, + t1: u64 = 0, + tp0: u64 = 0, + tp1: u64 = 0, + k2e: u64 = 0, // int32 [E] each expert's K2 (gm2_kernel; gm_kernel ignores it) + order: u64 = 0, + pe: u64 = 0, + poff: u64 = 0, + pcnt: u64 = 0, + npass: u64 = 0, + ticket: u64 = 0, + sv0: u64 = 0, + sv1: u64 = 0, + sd: u64 = 0, + xd: u64 = 0, + y: u64 = 0, + K: c_int = 0, + N: c_int = 0, + limit: f32 = 0, +}; + +comptime { + std.debug.assert(@sizeOf(GmArgs) == 160 and @offsetOf(GmArgs, "k2e") == 48 and @offsetOf(GmArgs, "K") == 144 and @offsetOf(GmArgs, "limit") == 152); +} + +/// x3gm's per-launch checks (launch<>): K in whole stages, N in 128-column Hadamard blocks. +pub fn gmCheck(kind: GmKind, cfg: usize, K: usize, N: usize) Error!void { + const ks = (if (kind == .gu) gu_tiles[cfg] else dn_tiles[cfg])[2]; + if ((K / 16) % ks != 0 or N % 128 != 0) return error.Shape; +} + +/// dsv41_x3gm_rot_cuda: out_m[p] = fp16((x[p / slots] * suh_m[e]) H / sqrt 128) for `mats` matrices. +pub fn gmRot(o: Ops, input: Input, x: u64, x_stride: usize, pick: u64, suh0: u64, suh1: u64, out0: u64, out1: u64, K: usize, slots: usize, pairs: usize, mats: usize) !void { + if (mats != 1 and mats != 2) return error.Unsupported; + const items = pairs * (K / 128); + var a: cuda.Args = .{}; + a.add(x); + a.add(int(x_stride)); + for ([_]u64{ pick, suh0, suh1, out0, out1 }) |v| a.add(v); + for ([_]usize{ K, slots, items }) |v| a.add(int(v)); + try go(o.f.gm_rot[@backingInt(input)][mats - 1], o.s, .{ (items + 7) / 8, 1, 1 }, 256, 0, false, &a); +} + +/// dsv41_x3gm_gateup_cuda: Xd of every pass's members; `tables`: tg / tu are int64 pointer tables (ragged). +pub fn gmGateup(o: Ops, xg: u64, xu: u64, tg: u64, tu: u64, order: u64, pe: u64, poff: u64, pcnt: u64, npass: u64, ticket: u64, svh_g: u64, svh_u: u64, suh_d: u64, xd: u64, K: usize, N: usize, k2: u32, shx: bool, cfg: usize, use_ticket: bool, limit: f32, tables: bool) !void { + const ki = gmK2Index(k2) orelse return error.Unsupported; + if (cfg >= gu_tiles.len) return error.Unsupported; + try gmCheck(.gu, cfg, K, N); + var a: GmArgs = .{ .x0 = xg, .x1 = xu, .order = order, .pe = pe, .poff = poff, .pcnt = pcnt, .npass = npass, .ticket = ticket, .sv0 = svh_g, .sv1 = svh_u, .sd = suh_d, .xd = xd, .K = int(K), .N = int(N), .limit = limit }; + if (tables) { + a.tp0 = tg; + a.tp1 = tu; + } else { + a.t0 = tg; + a.t1 = tu; + } + try o.gmLaunch(o.f.gm_gu[ki][if (shx) 0 else 1][cfg], a, use_ticket); +} + +/// dsv41_x3gm_down_cuda: Y (fp32) of every pass's members. +pub fn gmDown(o: Ops, xd: u64, td: u64, order: u64, pe: u64, poff: u64, pcnt: u64, npass: u64, ticket: u64, svh_d: u64, y: u64, K: usize, N: usize, k2: u32, cfg: usize, use_ticket: bool, tables: bool) !void { + const ki = gmK2Index(k2) orelse return error.Unsupported; + if (cfg >= dn_tiles.len) return error.Unsupported; + try gmCheck(.dn, cfg, K, N); + var a: GmArgs = .{ .x0 = xd, .x1 = xd, .order = order, .pe = pe, .poff = poff, .pcnt = pcnt, .npass = npass, .ticket = ticket, .sv0 = svh_d, .y = y, .K = int(K), .N = int(N) }; + if (tables) { + a.tp0 = td; + a.tp1 = td; + } else { + a.t0 = td; + a.t1 = td; + } + try o.gmLaunch(o.f.gm_dn[ki][cfg], a, use_ticket); +} + +/// dsv41_x3gm_gateup2_cuda (x3gm v2): Xd of every pass of every width in one launch; `tables`: ragged pointers. +pub fn gm2Gateup(o: Ops, xg: u64, xu: u64, tg: u64, tu: u64, k2e: u64, order: u64, pe: u64, poff: u64, pcnt: u64, npass: u64, ticket: u64, svh_g: u64, svh_u: u64, suh_d: u64, xd: u64, K: usize, N: usize, shx: bool, cfg: usize, use_ticket: bool, limit: f32, tables: bool) !void { + const gu2 = cfg == gu2_cfg and !shx; + const ci = std.mem.indexOfScalar(usize, &v2_gu, cfg) orelse if (gu2) 0 else return error.Unsupported; + try gmCheck(.gu, cfg, K, N); + var a: GmArgs = .{ .x0 = xg, .x1 = xu, .k2e = k2e, .order = order, .pe = pe, .poff = poff, .pcnt = pcnt, .npass = npass, .ticket = ticket, .sv0 = svh_g, .sv1 = svh_u, .sd = suh_d, .xd = xd, .K = int(K), .N = int(N), .limit = limit }; + if (tables) { + a.tp0 = tg; + a.tp1 = tu; + } else { + a.t0 = tg; + a.t1 = tu; + } + try o.gmLaunch(if (gu2) o.f.gm2_gu2 else o.f.gm2_gu[if (shx) 0 else 1][ci], a, use_ticket); +} + +/// dsv41_x3gm_down2_cuda (x3gm v2): Y of every pass of every width in one launch; `k2e` the experts' down widths. +pub fn gm2Down(o: Ops, xd: u64, td: u64, k2e: u64, order: u64, pe: u64, poff: u64, pcnt: u64, npass: u64, ticket: u64, svh_d: u64, y: u64, K: usize, N: usize, cfg: usize, use_ticket: bool, tables: bool) !void { + if (std.mem.indexOfScalar(usize, &v2_dn, cfg) == null) return error.Unsupported; + try gmCheck(.dn, cfg, K, N); + var a: GmArgs = .{ .x0 = xd, .x1 = xd, .k2e = k2e, .order = order, .pe = pe, .poff = poff, .pcnt = pcnt, .npass = npass, .ticket = ticket, .sv0 = svh_d, .y = y, .K = int(K), .N = int(N) }; + if (tables) { + a.tp0 = td; + a.tp1 = td; + } else { + a.t0 = td; + a.t1 = td; + } + try o.gmLaunch(o.f.gm2_dn, a, use_ticket); +} + +/// x3gm v3 gate/up (gm3_kernel), variant `v` of v3_variants (mats 2, its xm): gm2Gateup's arguments and bits. +pub fn gm3Gateup(o: Ops, v: usize, xg: u64, xu: u64, tg: u64, tu: u64, k2e: u64, order: u64, pe: u64, poff: u64, pcnt: u64, npass: u64, ticket: u64, svh_g: u64, svh_u: u64, suh_d: u64, xd: u64, K: usize, N: usize, shx: bool, use_ticket: bool, limit: f32, tables: bool) !void { + const vv = v3_variants[v]; + if (vv.mats != 2 or vv.xm != @as(u32, if (shx) 1 else 2)) return error.Unsupported; + if ((K / 16) % vv.t[2] != 0 or N % 128 != 0) return error.Shape; + var a: GmArgs = .{ .x0 = xg, .x1 = xu, .k2e = k2e, .order = order, .pe = pe, .poff = poff, .pcnt = pcnt, .npass = npass, .ticket = ticket, .sv0 = svh_g, .sv1 = svh_u, .sd = suh_d, .xd = xd, .K = int(K), .N = int(N), .limit = limit }; + if (tables) { + a.tp0 = tg; + a.tp1 = tu; + } else { + a.t0 = tg; + a.t1 = tu; + } + try o.gmLaunch(o.f.gm3[v], a, use_ticket); +} + +/// x3gm v3 down (gm3_kernel), variant `v` (mats 1): gm2Down's arguments and bits. +pub fn gm3Down(o: Ops, v: usize, xd: u64, td: u64, k2e: u64, order: u64, pe: u64, poff: u64, pcnt: u64, npass: u64, ticket: u64, svh_d: u64, y: u64, K: usize, N: usize, use_ticket: bool, tables: bool) !void { + const vv = v3_variants[v]; + if (vv.mats != 1) return error.Unsupported; + if ((K / 16) % vv.t[2] != 0 or N % 128 != 0) return error.Shape; + var a: GmArgs = .{ .x0 = xd, .x1 = xd, .k2e = k2e, .order = order, .pe = pe, .poff = poff, .pcnt = pcnt, .npass = npass, .ticket = ticket, .sv0 = svh_d, .y = y, .K = int(K), .N = int(N) }; + if (tables) { + a.tp0 = td; + a.tp1 = td; + } else { + a.t0 = td; + a.t1 = td; + } + try o.gmLaunch(o.f.gm3[v], a, use_ticket); +} + +/// x3gm.plan on the device (x3gm_plan.cu): order [pairs], pe / poff / pcnt [planPasses], npass [1] in `bm` members. +pub fn gmPlan(o: Ops, pick: u64, pairs: usize, E: usize, bm: usize, order: u64, pe: u64, poff: u64, pcnt: u64, npass: u64) !void { + if (E < 1 or E >= plan_max_experts or bm < 1 or pairs < 1) return error.Shape; + var a: cuda.Args = .{}; + a.add(pick); + for ([_]usize{ pairs, E, bm }) |v| a.add(int(v)); + for ([_]u64{ order, pe, poff, pcnt, npass }) |v| a.add(v); + try go(o.f.gm_plan, o.s, .{ 1, 1, 1 }, 512, 0, false, &a); +} + +/// dsv41_x3gm_dequant_cuda: W_q [K, N] fp16 through the gm kernels' decode_a (tests). +pub fn gmDequant(o: Ops, t: u64, out: u64, K: usize, N: usize, k2: u32) !void { + const ki = gmK2Index(k2) orelse return error.Unsupported; + var a: cuda.Args = .{}; + a.add(t); + a.add(out); + a.add(int(N)); + try go(o.f.gm_dequant[ki], o.s, .{ K / 16, N / 16, 1 }, 32, 0, false, &a); +} diff --git a/zig/src/families/deepseek_v41/knob_types.zig b/zig/src/families/deepseek_v41/knob_types.zig new file mode 100644 index 0000000000..e1cb6d4eaa --- /dev/null +++ b/zig/src/families/deepseek_v41/knob_types.zig @@ -0,0 +1,78 @@ +//! Option types the block and forward options carry: the fused dense prefill GEMM's choices, the x3gm v2 mode. +const std = @import("std"); +const dk = @import("dsv41_kernels"); + +const pfd = dk.ops.pfd; + +/// TF_DSV41_GM_V2 (gm2pf.zig): prefill's routed experts on x3gm v2; off: v1. The same bits either way. +pub const GmMode = enum { off, one, split }; + +/// The SMs the dense prefill heuristic prices waves on, on every GPU (the pick never changes a bit). +pub const pfd_sms: usize = 48; + +/// The fused dense prefill GEMM's choices (pfdense.pick): an override, the JSON table, else the heuristic. +pub const PfDense = struct { + entries: []const Entry = &.{}, + /// TF_DSV41_PF_DENSE_CFG: (id, group) for every shape + override: ?[2]u32 = null, + + pub const Entry = struct { k: u32, n: u32, cfg: u32, group: u32 = 8, small: ?u32 = null, small_rows: u32 = 0 }; + + /// pfdense.why_not's shape and width rules (the codebook is mul1 for every dense group of our packs). + pub fn eligible(k: i64, n: i64, k2: u32, lanes: bool) bool { + const widths: []const u32 = if (lanes) &.{ 8, 10, 12 } else &.{ 8, 10, 12, 16 }; + if (std.mem.indexOfScalar(u32, widths, k2) == null) return false; + return k > 0 and n > 0 and @rem(k, 128) == 0 and @rem(n, 128) == 0; + } + + /// (cfg, group) for an [m, k] @ [k, n] call: override, table, else heuristic (also when a stage does not divide K). + pub fn pick(p: *const PfDense, k: usize, n: usize, k2: u32, m: usize) [2]u32 { + var got: ?[2]u32 = p.override; + if (got == null) for (p.entries) |e| { + if (e.k != k or e.n != n) continue; + const c = if (e.small != null and m <= e.small_rows) e.small.? else e.cfg; + got = .{ c, e.group }; + break; + }; + if (got) |g| if (g[0] < pfd.cfgs.len and k % (16 * pfd.cfgs[g[0]][3]) == 0) return g; + const h = pfd.heuristic(k, n, k2, m, pfd_sms); + return .{ @intCast(h[0]), @intCast(h[1]) }; + } + + /// pfdense._table_file: `{"K,N": {"cfg": id, "group": g, "small": id, "small_rows": r}}`. + pub fn parseTable(a: std.mem.Allocator, json: []const u8) ![]const Entry { + const parsed = std.json.parseFromSliceLeaky(std.json.Value, a, json, .{}) catch return error.BadTable; + if (parsed != .object) return error.BadTable; + const out = try a.alloc(Entry, parsed.object.count()); + var it = parsed.object.iterator(); + var i: usize = 0; + while (it.next()) |kv| : (i += 1) { + var ks = std.mem.tokenizeScalar(u8, kv.key_ptr.*, ','); + const k = std.fmt.parseInt(u32, std.mem.trim(u8, ks.next() orelse return error.BadTable, " "), 10) catch return error.BadTable; + const n = std.fmt.parseInt(u32, std.mem.trim(u8, ks.next() orelse return error.BadTable, " "), 10) catch return error.BadTable; + if (kv.value_ptr.* != .object) return error.BadTable; + const o = kv.value_ptr.object; + out[i] = .{ .k = k, .n = n, .cfg = try field(o, "cfg") orelse return error.BadTable }; + if (try field(o, "group")) |g| out[i].group = g; + out[i].small = try field(o, "small"); + out[i].small_rows = (try field(o, "small_rows")) orelse 0; + } + return out; + } + + fn field(o: std.json.ObjectMap, name: []const u8) !?u32 { + const v = o.get(name) orelse return null; + return switch (v) { + .integer => |x| std.math.cast(u32, x) orelse error.BadTable, + else => error.BadTable, + }; + } + + pub fn parseOverride(raw: []const u8) ![2]u32 { + var it = std.mem.tokenizeScalar(u8, raw, ','); + const id = std.fmt.parseInt(u32, std.mem.trim(u8, it.next() orelse return error.BadKnob, " "), 10) catch return error.BadKnob; + const g: u32 = if (it.next()) |x| std.fmt.parseInt(u32, std.mem.trim(u8, x, " "), 10) catch return error.BadKnob else 8; + if (it.next() != null or id >= pfd.cfgs.len or g < 1 or g > 1024) return error.BadKnob; + return .{ id, g }; + } +}; diff --git a/zig/src/families/deepseek_v41/kv/bench.zig b/zig/src/families/deepseek_v41/kv/bench.zig new file mode 100644 index 0000000000..08eae03862 --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/bench.zig @@ -0,0 +1,188 @@ +//! tf-kv-bench DIR [tokens] [lanes] [staging MiB]: pool page ops, a 1M-token session parked/restored on NVMe. + +const std = @import("std"); +const sessions = @import("sessions"); +const layout = @import("layout.zig"); +const Io = std.Io; + +fn now(io: Io) i96 { + return Io.Clock.awake.now(io).toNanoseconds(); +} + +fn secs(a: i96, b: i96) f64 { + return @as(f64, @floatFromInt(b - a)) / 1e9; +} + +/// Bounded state of a realistic size (rings, taps: ~11 MiB) held in host memory. +const Blob = struct { + gpa: std.mem.Allocator, + bytes: []u8, + const vt: sessions.store.Bounded.VTable = .{ .nbytes = nbytes, .blobs = blobs, .release = release }; + fn make(gpa: std.mem.Allocator) !sessions.store.Bounded { + const b = try gpa.create(Blob); + b.* = .{ .gpa = gpa, .bytes = try gpa.alloc(u8, 11 << 20) }; + for (b.bytes, 0..) |*x, i| x.* = @truncate(i *% 2654435761 >> 13); + return .{ .ptr = b, .vtable = &vt }; + } + fn nbytes(p: *anyopaque) u64 { + return @as(*Blob, @ptrCast(@alignCast(p))).bytes.len; + } + fn blobs(p: *anyopaque, gpa: std.mem.Allocator) anyerror![]sessions.disk.Blob { + const b: *Blob = @ptrCast(@alignCast(p)); + const out = try gpa.alloc(sessions.disk.Blob, 1); + out[0] = .{ .name = try gpa.dupe(u8, "bounded"), .bytes = try gpa.dupe(u8, b.bytes) }; + return out; + } + fn release(p: *anyopaque) void { + const b: *Blob = @ptrCast(@alignCast(p)); + b.gpa.free(b.bytes); + b.gpa.destroy(b); + } +}; + +fn pageOps(gpa: std.mem.Allocator, io: Io, out: *Io.Writer) !void { + var l = try layout.Layout.fromConfig(layout.Release{}, .{ .split = true }); + for ([_]u64{ 2_240_000, 9_000_000 }) |tokens| { + var pool = try sessions.Pool.init(gpa, l.pool(), tokens / 512 * 512, 2, 0); + defer pool.deinit(); + const cap = pool.npages / 4 / 16 * 16 * 256; + const slots = [_]*sessions.Slot{ try pool.newSlot(cap), try pool.newSlot(cap), try pool.newSlot(cap), try pool.newSlot(cap) }; + var ops: u64 = 0; + const t0 = now(io); + for (0..20) |_| { + for (slots) |s| { + var pos: u64 = 0; + while (pos < cap) : (pos += 4096) { // a 16-page prefill segment at a time + try s.ensure(pos + 4096); + ops += 16; + } + } + for (slots) |s| { + pool.share(s.mapped()); // a session entry keeps the slot's pages + ops += s.len; + _ = try s.truncate(s.tokens() / 2); // a rollback + ops += s.len; + _ = try s.releaseAll(); + } + for (slots) |s| { + try pool.drop(s.pages[0 .. cap / 256]); // the entries go + ops += cap / 256; + } + } + const dt = secs(t0, now(io)); + try out.print("page ops, pool {d} pages ({d} tokens), split over 2: {d:.1} ns a page op ({d} ops, {d:.3} s); available() {d}\n", .{ pool.npages, pool.npages * 256, dt * 1e9 / @as(f64, @floatFromInt(ops)), ops, dt, pool.available() }); + } +} + +fn prefixOps(gpa: std.mem.Allocator, io: Io, out: *Io.Writer) !void { + const n = 1 << 20; + const ids = try gpa.alloc(i32, n); + defer gpa.free(ids); + for (ids, 0..) |*t, i| t.* = @intCast(i *% 7919 % 129280); + const d = try gpa.alloc(sessions.prefix.Digest, n / 256); + defer gpa.free(d); + const t0 = now(io); + sessions.prefix.chain(1, ids, 256, d); + const t1 = now(io); + var x = sessions.prefix.Index(u64).init(gpa, 256); + defer x.deinit(); + for (0..64) |k| _ = try x.insert(1, ids[0 .. n - 1 - k * 9000], d, k % 2 == 0, k); + const t2 = now(io); + var hits: u64 = 0; + for (0..1000) |_| hits += @intFromBool(x.find(1, ids, d, .any) != null); + const t3 = now(io); + try out.print("prefix: chain digests {d:.2} GB/s of ids ({d:.1} ms a 1M prompt); 64 entries of ~1M inserted in {d:.1} ms; find over them {d:.1} us ({d} hits)\n", .{ 4.0 * n / secs(t0, t1) / 1e9, secs(t0, t1) * 1e3, secs(t1, t2) * 1e3, secs(t2, t3) * 1e6 / 1000, hits }); +} + +fn session(gpa: std.mem.Allocator, io: Io, out: *Io.Writer, dir: []const u8, tokens: u64, world: u32, rank: u32, lanes: u32, staging_mib: u32) !void { + var l = try layout.Layout.fromConfig(layout.Release{}, .{ .split = world > 1 }); + const pool_tokens = (2 * tokens + 4096) / 512 * 512 + 512; + var pool = try sessions.Pool.init(gpa, l.pool(), pool_tokens, world, rank); + defer pool.deinit(); + var host = try sessions.HostPool.init(gpa, &pool); + defer host.deinit(); + var compat: [16]u8 = @splat(0); + compat[0] = @intCast(world); + const d = try sessions.Disk.open(gpa, io, .{ .root = dir, .compat = compat, .rank = rank, .world = world, .lanes = lanes, .staging = staging_mib / 8, .min_tokens = 1 }); + defer d.close(); + var st = try sessions.Store.init(gpa, io, &pool, host.store(), d, .{}); + defer st.deinit(); + const a = try pool.newSlot(tokens + 16384); + try a.ensure(tokens); + for (a.mapped()) |pg| for (l.families(), 0..) |f, fi| { + if (f.split and !pool.owns(pg)) continue; + const v = host.pageOf(@intCast(fi), pool.familyPage(f, pg)); + for (std.mem.bytesAsSlice(u64, v), 0..) |*w, i| w.* = (@as(u64, pg) << 40) ^ (i *% 0x9E3779B97F4A7C15) ^ fi; + }; + const ids = try gpa.alloc(i32, tokens); + defer gpa.free(ids); + for (ids, 0..) |*t, i| t.* = @intCast(i % 129280); + const id = (try st.save(a, try Blob.make(gpa), ids, 1, 0)).?; + const src_pages = try gpa.dupe(u32, a.mapped()); + defer gpa.free(src_pages); + _ = try a.releaseAll(); + const t0 = now(io); + const j = (try st.beginPark(id)).?; + _ = try st.settle(j); + const t1 = now(io); + const file = d.used; + const hash0 = d.lanes.hash_ns.load(.monotonic); + const io0 = d.lanes.io_ns.load(.monotonic); + // restore onto other pages: the source pages are free again, so take a pad first + const pad = try pool.newSlot(tokens + 16384); + try pad.ensure(4096); + const b = try pool.newSlot(tokens + 16384); + const t2 = now(io); + const rj = try st.beginRestore(id, b); + var got = (try st.settle(rj)).?; + got.deinit(gpa); + const t3 = now(io); + // the restored rows equal the parked ones, owned rows of every family + var bad: u64 = 0; + var cmp: u64 = 0; + for (b.mapped(), 0..) |pg, k| for (l.families(), 0..) |f, fi| { + if (f.split and !pool.owns(pg)) continue; + const v = host.pageOf(@intCast(fi), pool.familyPage(f, pg)); + const sp = src_pages[k]; + for (std.mem.bytesAsSlice(u64, v), 0..) |w, i| bad += @intFromBool(w != ((@as(u64, sp) << 40) ^ (i *% 0x9E3779B97F4A7C15) ^ fi)); + cmp += v.len; + }; + const gb = @as(f64, @floatFromInt(file)) / 1e9; + try out.print("{d} tokens, world {d} rank {d}: file {d:.3} GB ({d:.0} B a token); park {d:.3} s ({d:.2} GB/s), restore {d:.3} s ({d:.2} GB/s); lanes {d}: hash {d:.2} s, I/O {d:.2} s summed; staging {d} MiB; restored bytes compared {d:.2} GB, mismatches {d}\n", .{ tokens, world, rank, gb, @as(f64, @floatFromInt(file)) / @as(f64, @floatFromInt(tokens)), secs(t0, t1), gb / secs(t0, t1), secs(t2, t3), gb / secs(t2, t3), lanes, @as(f64, @floatFromInt(d.lanes.hash_ns.load(.monotonic))) / 1e9, @as(f64, @floatFromInt(d.lanes.io_ns.load(.monotonic))) / 1e9, staging_mib, @as(f64, @floatFromInt(cmp)) / 1e9, bad }); + _ = hash0; + _ = io0; + // the next turn (+8K tokens) parked as a delta on the restored file + try b.ensure(tokens + 8192); + const ids2 = try gpa.alloc(i32, tokens + 8192); + defer gpa.free(ids2); + for (ids2, 0..) |*t, i| t.* = @intCast(i % 129280); + const id2 = (try st.save(b, try Blob.make(gpa), ids2, 1, 0)).?; + _ = try b.releaseAll(); + const before = d.used; + const t4 = now(io); + _ = try st.settle((try st.beginPark(id2)).?); + const t5 = now(io); + try out.print(" next turn +8192 tokens parked as a delta: {d:.1} MB written in {d:.3} s (a whole file would be {d:.3} GB)\n", .{ @as(f64, @floatFromInt(d.used - before)) / 1e6, secs(t4, t5), gb * @as(f64, @floatFromInt(tokens + 8192)) / @as(f64, @floatFromInt(tokens)) }); + if (bad != 0) return error.RestoreMismatch; +} + +pub fn main(init: std.process.Init) !void { + const gpa = init.gpa; + const io = init.io; + const args = try init.minimal.args.toSlice(init.arena.allocator()); + if (args.len < 2) return error.Usage; + const dir = args[1]; + const tokens = if (args.len > 2) try std.fmt.parseInt(u64, args[2], 10) else 1 << 20; + const lanes = if (args.len > 3) try std.fmt.parseInt(u32, args[3], 10) else 4; + const staging = if (args.len > 4) try std.fmt.parseInt(u32, args[4], 10) else 128; + var buf: [4096]u8 = undefined; + var w = Io.File.stdout().writer(io, &buf); + const out = &w.interface; + try pageOps(gpa, io, out); + try prefixOps(gpa, io, out); + try out.flush(); + try session(gpa, io, out, dir, tokens, 1, 0, lanes, staging); + try out.flush(); + try session(gpa, io, out, dir, tokens, 2, 0, lanes, staging); + try out.flush(); +} diff --git a/zig/src/families/deepseek_v41/kv/budget.zig b/zig/src/families/deepseek_v41/kv/budget.zig new file mode 100644 index 0000000000..65b70dd786 --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/budget.zig @@ -0,0 +1,517 @@ +//! The KV side of the priced memory floor: boot pool size, prompt and NVMe resume prices, and admission fit. + +const std = @import("std"); +const sessions = @import("sessions"); + +pub const GiB: f64 = @floatFromInt(@as(u64, 1) << 30); + +/// A slot's bounded state outside the pool: 43 SWA rings x 256 x 584, 3 ratio-2 carries, Engram lookback, DSpark taps. +pub const slot_bytes: u64 = 43 * 256 * 584 + 3 * 1024 * 4 + 3 * 4 + 3 * 5120 * 2 * 128; + +pub const Knobs = struct { + /// TF_DSV41_PREFILL_ROWS and TF_DSV41_INDEX_BUDGET_MIB + prefill_rows: u32 = 2048, + index_budget_mib: u32 = 256, + /// fitted prefill price terms + pf_chunk_gib: f64 = 0.533, + pf_host_gib: f64 = 0.6, + pf_other_gib: f64 = 0.0, + pf_index_gib: f64 = 1.067, + index_positions: u64 = 32768, + /// split KV's union receive cap (TF_DSV41_KV_SPLIT_UNION_MIB); 0 when unsplit + union_cap: u64 = 0, + /// the NVMe tier's staging set: a streamed park's or restore's whole host transient + staging_bytes: u64 = 128 << 20, + hard_gib: f64 = 4.0, + target_gib: f64 = 5.0, +}; + +/// A rank's boot budget in GiB: what is allocated before the pool, and what must stay free. +pub const Boot = struct { + available: f64 = 112.0, + weights: f64, + slots: u32 = 4, + /// the planned workspace (graphs 1.2, prefill 1.3, select 0.25, drafting 0.35, slack 0.9) + workspace: f64 = 4.0, + runtime: f64 = 1.0, + /// the RAM tier's bounded state (TF_DSV41_SESSION_RAM_MIB) + session_ram: f64 = 0.25, + + pub fn fixed(b: Boot, k: Knobs) f64 { + const rings = @as(f64, @floatFromInt(b.slots * slot_bytes)) / GiB; + const staging = @as(f64, @floatFromInt(k.staging_bytes)) / GiB; + const unions = 1.5 * @as(f64, @floatFromInt(k.union_cap)) / GiB; + return b.weights + rings + b.workspace + b.runtime + b.session_ram + staging + unions; + } + + /// The floor left with a pool of pool_bytes: under the hard floor the boot refuses. + pub fn floor(b: Boot, k: Knobs, pool_bytes: u64) f64 { + return b.available - b.fixed(k) - @as(f64, @floatFromInt(pool_bytes)) / GiB; + } + + /// The most pool tokens keeping the hard floor, in whole pages, a multiple of world pages (split residues). + pub fn poolTokens(b: Boot, k: Knobs, layout: sessions.Layout, world: u32) u64 { + const spare = b.available - b.fixed(k) - k.hard_gib; + if (spare <= 0) return 0; + const per_page: f64 = @floatFromInt(layout.page); + const page_bytes = layout.bytesPerToken(world) * per_page; + const pages: u64 = @intFromFloat(@floor(spare * GiB / page_bytes)); + return pages / world * world * layout.page; + } +}; + +/// A prompt's prefill transient in bytes: once (the window's buffers) and index (grows to index_positions). +pub const Price = struct { + once: u64 = 0, + index: u64 = 0, + start: u64 = 0, + end: u64 = 0, + + pub fn total(p: Price) u64 { + return p.once + p.index; + } + + /// What has not materialised at `done` tokens (the floor's reservation for a prompt still prefilling). + pub fn outstanding(p: Price, done: u64) u64 { + if (p.total() == 0) return 0; + const once = if (done <= p.start) p.once else 0; + const span = p.end - p.start; + const left = p.end -| @max(done, p.start); + return once + if (span > 0) @as(u64, @intFromFloat(@as(f64, @floatFromInt(p.index)) * @as(f64, @floatFromInt(left)) / @as(f64, @floatFromInt(span)))) else 0; + } +}; + +/// Prefilling prompt tokens [cached, n - 1), plus split KV's union buffers (receive cap + half for the send). +pub fn prefillPrice(k: Knobs, n: u64, cached: u64) Price { + const todo = (n -| 1) -| cached; + if (todo == 0) return .{}; + const w = @as(f64, @floatFromInt(@min(k.prefill_rows, todo))) / 2048.0; + var once = (k.pf_chunk_gib + k.pf_host_gib) * w + k.pf_other_gib; + once += 1.5 * @as(f64, @floatFromInt(k.union_cap)) / GiB; + const end = @min(n - 1, k.index_positions); + const share = @as(f64, @floatFromInt(end -| cached)) / @as(f64, @floatFromInt(k.index_positions)); + const index = k.pf_index_gib * @as(f64, @floatFromInt(k.index_budget_mib)) / 256.0 * share; + return .{ .once = @intFromFloat(once * GiB), .index = @intFromFloat(index * GiB), .start = cached, .end = @max(cached, end) }; +} + +/// An NVMe resume's host transient: the staging set (streamed), or nothing when the bytes are already in host memory. +pub fn restorePrice(k: Knobs, from_disk: bool) u64 { + return if (from_disk) k.staging_bytes else 0; +} + +pub const Verdict = enum { admit, wait_pages, wait_memory }; + +pub const Ask = struct { + /// pages the request needs reserved (prompt + reply + slack) less those a RAM resume shares + pages: u32, + /// the prompt's price, an NVMe resume's price, and what admitted prompts have not materialised yet + price: u64, + restore: u64 = 0, + outstanding: u64 = 0, +}; + +/// May a request start: pages from any residue, and every rank's `usable` less the transient at the hard floor. +pub fn admit(k: Knobs, pool: *const sessions.Pool, ask: Ask, usable: []const u64) struct { verdict: Verdict, rank: u32 } { + if (pool.available() < ask.pages) return .{ .verdict = .wait_pages, .rank = 0 }; + const want = ask.price + ask.restore + ask.outstanding + @as(u64, @intFromFloat(k.hard_gib * GiB)); + var tight: u32 = 0; + for (usable, 0..) |u, r| if (u < usable[tight]) { + tight = @intCast(r); + }; + if (usable[tight] < want) return .{ .verdict = .wait_memory, .rank = tight }; + return .{ .verdict = .admit, .rank = tight }; +} + +// -- the measured boot check ---------------------------------------------------------------------------------------- + +/// A rank's measured GiB terms: boot growth past the check less the pool, and the worst serving dip below idle. +pub const Serve = struct { + growth: f64, + dip: f64, + + /// Measured: rank 0 (head) 1.9 / 4.9, every other rank 1.7 / 4.2. + pub fn measured(rank: u32) Serve { + return if (rank == 0) .{ .growth = 1.9, .dip = 4.9 } else .{ .growth = 1.7, .dip = 4.2 }; + } + + /// The rank's terms overridden by TF_DSV41_BOOT_GROWTH_GIB / TF_DSV41_SERVE_DIP_GIB (null or blank: measured). + pub fn withOverrides(rank: u32, growth: ?[]const u8, dip: ?[]const u8) !Serve { + const m = measured(rank); + return .{ .growth = try gibEnv(growth, m.growth), .dip = try gibEnv(dip, m.dip) }; + } + + pub fn fromEnv(rank: u32) !Serve { + return withOverrides(rank, env(growth_env), env(dip_env)); + } +}; + +/// Zig's dip below idle (TF_DSV41_SERVE_DIP_GIB unset): 2 x 1M then 4 x 740K measured 2.48 (rank 1), 1.1 (rank 0). +pub const zig_dip_gib: f64 = 3.0; + +/// Zig's terms before the pool: TF_DSV41_BOOT_GROWTH_GIB (unset 0: measured at boot's end), TF_DSV41_SERVE_DIP_GIB. +pub fn zigFromEnv() !Serve { + return .{ .growth = try gibEnv(env(growth_env), 0), .dip = try gibEnv(env(dip_env), zig_dip_gib) }; +} + +/// The measured check at the end of boot (GiB): whether the worst point keeps the floor, and what the boot took. +pub const AfterBoot = struct { + ok: bool, + /// what the boot took after the check besides the pool (plan, forms, rows, drafter, sampler) + growth: f64, + /// the worst serving point: idle less the dip + worst: f64, + /// the pool's tokens that would leave the worst point at the hard floor (0: none) + largest: u64, +}; + +/// The measured check: idle - post-boot reserve - dip >= the hard floor (growth = at the check - pool - now, logged). +pub fn afterBoot(at_check: f64, pool_gib: f64, idle: f64, reserve: f64, dip: f64, hard: f64, pool_tokens: u64, bytes_a_token: f64) AfterBoot { + const worst = idle - reserve - dip; + const room_tokens = (worst - hard) * GiB / @max(1.0, bytes_a_token); + const largest = @as(f64, @floatFromInt(pool_tokens)) + room_tokens; + return .{ .ok = worst >= hard, .growth = at_check - pool_gib - idle, .worst = worst, .largest = if (largest > 0) @intFromFloat(@floor(largest)) else 0 }; +} + +/// TF_DSV41_POST_BOOT_GIB (unset 0): post-boot one-time cost (graphs, held sessions; ~1.5 GiB measured), not dip. +pub const reserve_env = "TF_DSV41_POST_BOOT_GIB"; + +pub fn reserveFromEnv() !f64 { + return gibEnv(env(reserve_env), 0); +} + +pub const growth_env = "TF_DSV41_BOOT_GROWTH_GIB"; +pub const dip_env = "TF_DSV41_SERVE_DIP_GIB"; +pub const measured_env = "TF_DSV41_BOOT_MEASURED"; + +fn env(name: [*:0]const u8) ?[]const u8 { + return if (std.c.getenv(name)) |v| std.mem.span(v) else null; +} + +/// A GiB knob: blank is the default, a negative figure an error. +fn gibEnv(raw: ?[]const u8, default: f64) !f64 { + const s = std.mem.trim(u8, raw orelse "", " \t\r\n"); + if (s.len == 0) return default; + const v = std.fmt.parseFloat(f64, s) catch return error.BadGiBEnv; + if (v < 0) return error.BadGiBEnv; + return v; +} + +/// TF_DSV41_BOOT_MEASURED: 0 / off / no / false, 1 / on / yes / true, anything else auto. +pub const Mode = enum { auto, on, off }; + +pub fn parseMode(raw: ?[]const u8) Mode { + var buf: [8]u8 = undefined; + const s = std.mem.trim(u8, raw orelse "", " \t\r\n"); + if (s.len > buf.len) return .auto; + const l = std.ascii.lowerString(&buf, s); + const offs = [_][]const u8{ "0", "off", "no", "false" }; + const ons = [_][]const u8{ "1", "on", "yes", "true" }; + for (offs) |o| if (std.mem.eql(u8, l, o)) return .off; + for (ons) |o| if (std.mem.eql(u8, l, o)) return .on; + return .auto; +} + +pub fn modeFromEnv() Mode { + return parseMode(env(measured_env)); +} + +/// The measured check runs if forced, else where the GPU is integrated or its memory within 15 % of host MemTotal. +pub fn measuredApplies(mode: Mode, integrated: bool, device_total: u64, host_total: u64) bool { + if (mode == .off) return false; + const d: f64 = @floatFromInt(device_total); + const h: f64 = @floatFromInt(host_total); + const shared = integrated or (h > 0 and @abs(d - h) < 0.15 * h); + return shared or mode == .on; +} + +/// /proc/meminfo's MemTotal and MemAvailable in bytes. +pub const MemInfo = struct { total: u64, available: u64 }; + +pub fn parseMeminfo(text: []const u8) !MemInfo { + var m: MemInfo = .{ .total = 0, .available = 0 }; + var seen: u2 = 0; + var it = std.mem.splitScalar(u8, text, '\n'); + while (it.next()) |line| { + const dst = if (std.mem.startsWith(u8, line, "MemTotal:")) &m.total else if (std.mem.startsWith(u8, line, "MemAvailable:")) &m.available else continue; + var f = std.mem.tokenizeScalar(u8, line[std.mem.indexOfScalar(u8, line, ':').? + 1 ..], ' '); + dst.* = 1024 * (std.fmt.parseInt(u64, f.next() orelse return error.BadMeminfo, 10) catch return error.BadMeminfo); + seen += 1; + } + if (seen < 2) return error.BadMeminfo; + return m; +} + +pub fn meminfo() !MemInfo { + var buf: [8192]u8 = undefined; + const fd = std.c.open("/proc/meminfo", .{ .ACCMODE = .RDONLY }); + if (fd < 0) return error.NoMeminfo; + defer _ = std.c.close(fd); + const n = std.c.read(fd, &buf, buf.len); + if (n <= 0) return error.NoMeminfo; + return parseMeminfo(buf[0..@intCast(n)]); +} + +/// MemAvailable now in bytes: what the engine measures at the check (after the weights, before the pool). +pub fn memAvailable() !u64 { + return (try meminfo()).available; +} + +/// A rank's pool bytes for `tokens` (whole rows a family, the split families' rows / W). +pub fn poolBytes(layout: sessions.Layout, world: u32, tokens: u64) u64 { + var b: u64 = 0; + for (layout.families) |f| b += @as(u64, f.row_bytes) * (tokens / f.ratio) / (if (f.split and world > 1) world else 1); + return b; +} + +/// Bytes a token: a 1M-token pool's bytes / 2^20 (1,790 replicated, 1,060 split over two ranks). +pub fn tokenBytes(layout: sessions.Layout, world: u32) f64 { + return @as(f64, @floatFromInt(poolBytes(layout, world, 1 << 20))) / @as(f64, 1 << 20); +} + +/// What the check measured: MemAvailable at the check (GiB) and the rank's terms. +pub const Measured = struct { + rank: u32, + avail_gib: f64, + terms: Serve, +}; + +pub const Result = struct { + rank: u32, + ok: bool, + /// the predicted worst MemAvailable while serving (GiB) + worst: f64, + avail_gib: f64, + pool_tokens: u64, + pool_gib: f64, + bytes_a_token: f64, + split: bool, + growth: f64, + dip: f64, + hard: f64, + /// the largest pool that fits (room / bytes a token, floored), and the same in whole pages of every residue + largest: u64, + largest_paged: u64, + + /// The refusal (or the report on a pass). + pub fn message(r: Result, buf: []u8) []const u8 { + var a: [32]u8 = undefined; + var b: [32]u8 = undefined; + var c: [32]u8 = undefined; + if (r.ok) return std.fmt.bufPrint(buf, "rank {d} pool {s} tokens = {d:.2} GiB ({d:.0} B a token{s}); predicted worst MemAvailable {d:.2} GiB (hard floor {s})", .{ + r.rank, grouped(&a, r.pool_tokens), r.pool_gib, r.bytes_a_token, if (r.split) ", split KV" else "", r.worst, pyFloat(&b, r.hard), + }) catch buf; + return std.fmt.bufPrint(buf, "rank {d}: a {d:.2} GiB pool leaves {d:.2} GiB at the worst measured serving point (MemAvailable {d:.2} now - boot growth {d:.2} - serving dip {d:.2}), under the {s} GiB hard floor (TF_DSV41_FLOOR_HARD_GIB); the largest pool that fits: {s} tokens ({s} in whole pages; TF_DSV41_POOL_TOKENS; " ++ growth_env ++ " / " ++ dip_env ++ " override the measured terms)", .{ + r.rank, r.pool_gib, r.worst, r.avail_gib, r.growth, r.dip, pyFloat(&a, r.hard), grouped(&b, r.largest), grouped(&c, r.largest_paged), + }) catch buf; + } +}; + +/// The largest pool the terms leave room for: max(0, avail - growth - dip - hard) GiB / bytes a token, floored. +pub fn largestPool(avail_gib: f64, terms: Serve, hard_gib: f64, bytes_a_token: f64) u64 { + const room = @max(0.0, avail_gib - terms.growth - terms.dip - hard_gib); + return @intFromFloat(@floor(room * GiB / bytes_a_token)); +} + +/// The predicted worst MemAvailable (GiB) with a pool of pool_gib. +pub fn worstAt(avail_gib: f64, pool_gib: f64, terms: Serve) f64 { + return avail_gib - pool_gib - terms.growth - terms.dip; +} + +/// Load-time check: MemAvailable - pool (split-aware) - growth - worst dip >= hard floor; after Boot, if it applies. +pub fn serveCheck(k: Knobs, m: Measured, layout: sessions.Layout, world: u32, pool_tokens: u64) Result { + const per = tokenBytes(layout, world); + const pool_gib = @as(f64, @floatFromInt(poolBytes(layout, world, pool_tokens))) / GiB; + const worst = worstAt(m.avail_gib, pool_gib, m.terms); + var split = false; + for (layout.families) |f| split = split or (f.split and world > 1); + const largest = largestPool(m.avail_gib, m.terms, k.hard_gib, per); + const step = @as(u64, layout.page) * (if (split) world else 1); + return .{ + .rank = m.rank, + .ok = worst >= k.hard_gib, + .worst = worst, + .avail_gib = m.avail_gib, + .pool_tokens = pool_tokens, + .pool_gib = pool_gib, + .bytes_a_token = per, + .split = split, + .growth = m.terms.growth, + .dip = m.terms.dip, + .hard = k.hard_gib, + .largest = largest, + .largest_paged = largest / step * step, + }; +} + +/// n with thousands separators. +fn grouped(buf: *[32]u8, n: u64) []const u8 { + var tmp: [24]u8 = undefined; + const d = std.fmt.bufPrint(&tmp, "{d}", .{n}) catch unreachable; + var o: usize = 0; + for (d, 0..) |ch, i| { + if (i > 0 and (d.len - i) % 3 == 0) { + buf[o] = ','; + o += 1; + } + buf[o] = ch; + o += 1; + } + return buf[0..o]; +} + +/// A float as the refusal message prints the hard floor (4.0, 4.5). +fn pyFloat(buf: *[32]u8, v: f64) []const u8 { + const s = std.fmt.bufPrint(buf, "{d}", .{v}) catch unreachable; + if (std.mem.indexOfAny(u8, s, ".en") != null) return s; + buf[s.len] = '.'; + buf[s.len + 1] = '0'; + return buf[0 .. s.len + 2]; +} + +const layout_mod = @import("layout.zig"); + +test "the boot budget: the reference's pool cap, its second form's, and split KV's pool from the same memory" { + var l = try layout_mod.Layout.fromConfig(layout_mod.Release{}, .{}); + const k: Knobs = .{ .staging_bytes = 0 }; + // a 99.48 GiB-weights boot refuses pools above 3.23 GiB = 1.94M tokens (111.75 - 99.48 - 4 slots - 4.0 - 1.0 - 4) + const boot: Boot = .{ .weights = 99.48 }; + const t = boot.poolTokens(k, l.pool(), 1); + try std.testing.expect(t > 1_930_000 and t < 1_945_000); + try std.testing.expect(boot.floor(k, t * 1790) >= 4.0); + // split KV over two ranks holds 1,790 / 1,060 times the tokens (less the union buffers it prices) + var s = try layout_mod.Layout.fromConfig(layout_mod.Release{}, .{ .split = true }); + const ks: Knobs = .{ .staging_bytes = 0, .union_cap = 128 << 20 }; + const ts = boot.poolTokens(ks, s.pool(), 2); + const ratio = @as(f64, @floatFromInt(ts)) / @as(f64, @floatFromInt(t)); + const spare = boot.available - boot.fixed(k) - k.hard_gib; + const want = (spare - 0.1875) / spare * 1790.0 / 1060.0; // 1.59: the union's 192 MiB comes off first + try std.testing.expectApproxEqAbs(want, ratio, 1e-3); + try std.testing.expectEqual(@as(u64, 0), ts % (2 * 256)); +} + +test "prefill and restore prices, admission against both ranks and residues" { + const k: Knobs = .{}; + const p = prefillPrice(k, 1_000_000, 0); // a 1M prompt is priced as a 32K one: 1.133 + 1.067 GiB at 2,048 rows + const p32 = prefillPrice(k, 32_769, 0); + try std.testing.expectEqual(p.total(), p32.total()); + try std.testing.expectApproxEqAbs(@as(f64, 1.133 + 1.067), @as(f64, @floatFromInt(p.total())) / GiB, 1e-6); + try std.testing.expectEqual(@as(u64, 0), prefillPrice(k, 100, 99).total()); + try std.testing.expectEqual(p.index / 2, p.outstanding(16384) - 0); + const ks: Knobs = .{ .union_cap = 128 << 20 }; + try std.testing.expectEqual(@as(u64, 192 << 20), prefillPrice(ks, 5000, 0).once - prefillPrice(k, 5000, 0).once); + const fams = [_]sessions.Family{.{ .name = "comp", .ratio = 1, .row_bytes = 584, .split = true }}; + var pool = try sessions.Pool.init(std.testing.allocator, .{ .families = &fams }, 16 * 256, 2, 0); + defer pool.deinit(); + const g: u64 = 1 << 30; + const ask: Ask = .{ .pages = 10, .price = p.total(), .restore = restorePrice(k, true) }; + try std.testing.expectEqual(Verdict.admit, admit(k, &pool, ask, &.{ 9 * g, 8 * g }).verdict); + const r = admit(k, &pool, ask, &.{ 9 * g, 6 * g }); // rank 1 binds: 6 - 2.2 - 0.125 < 4 + try std.testing.expect(r.verdict == .wait_memory and r.rank == 1); + try std.testing.expectEqual(Verdict.wait_pages, admit(k, &pool, .{ .pages = 17, .price = 0 }, &.{ 9 * g, 9 * g }).verdict); +} + +test "the measured boot check: the reference's served and middle-profile goldens" { + var rl = try layout_mod.Layout.fromConfig(layout_mod.Release{}, .{}); + var sl = try layout_mod.Layout.fromConfig(layout_mod.Release{}, .{ .split = true }); + const rep = rl.pool(); + const spl = sl.pool(); + try std.testing.expectEqual(@as(f64, 1790), tokenBytes(rep, 2)); + try std.testing.expectEqual(@as(f64, 1060), tokenBytes(spl, 2)); + const k: Knobs = .{}; + const at_check = [2]f64{ 14.0, 12.9 }; // MemAvailable at load_check, head / worker + var buf: [512]u8 = undefined; + for (0..2) |r| { + const m: Measured = .{ .rank = @intCast(r), .avail_gib = at_check[r], .terms = Serve.measured(@intCast(r)) }; + const served = serveCheck(k, m, rep, 2, 1_400_000); // replicated 1.4M + const mid = serveCheck(k, m, spl, 2, 3_000_000); // the middle profile: split 3.0M + try std.testing.expect(served.ok and mid.ok); + try std.testing.expectApproxEqAbs(@as(f64, if (r == 0) 4.86610562801361 else 4.666105628013612), served.worst, 1e-12); + try std.testing.expectApproxEqAbs(@as(f64, if (r == 0) 4.238394212722778 else 4.038394212722779), mid.worst, 1e-12); + try std.testing.expect(!serveCheck(k, m, rep, 2, 2_200_000).ok); // replicated 2.2M + try std.testing.expect(!serveCheck(k, m, spl, 2, 3_500_000).ok); // split 3.5M, which the planned budget accepts + } + const w: Measured = .{ .rank = 1, .avail_gib = 12.9, .terms = Serve.measured(1) }; + const refused = serveCheck(k, w, spl, 2, 3_500_000); + try std.testing.expectApproxEqAbs(@as(f64, 3.5447932481765747), refused.worst, 1e-12); + try std.testing.expectEqual(@as(u64, 3_038_891), refused.largest); // int(3.0 GiB / 1,060) + try std.testing.expectEqual(@as(u64, 3_038_720), refused.largest_paged); // 5,935 x 512 + try std.testing.expect(serveCheck(k, w, spl, 2, refused.largest_paged).ok); + try std.testing.expect(!serveCheck(k, w, spl, 2, refused.largest_paged + 512).ok); + const msg = refused.message(&buf); + try std.testing.expect(std.mem.indexOf(u8, msg, "rank 1: a 3.46 GiB pool leaves 3.54 GiB at the worst measured serving point (MemAvailable 12.90 now - boot growth 1.70 - serving dip 4.20), under the 4.0 GiB hard floor") != null); + try std.testing.expect(std.mem.indexOf(u8, msg, "the largest pool that fits: 3,038,891 tokens") != null); + const rr = serveCheck(k, w, rep, 2, 2_200_000); + try std.testing.expectEqual(@as(u64, 1_799_567), rr.largest); + try std.testing.expectEqual(@as(u64, 1_799_424), rr.largest_paged); + try std.testing.expect(std.mem.indexOf(u8, rr.message(&buf), "hard floor") != null); + const ok = serveCheck(k, w, spl, 2, 3_000_000).message(&buf); + try std.testing.expectEqualStrings("rank 1 pool 3,000,000 tokens = 2.96 GiB (1060 B a token, split KV); predicted worst MemAvailable 4.04 GiB (hard floor 4.0)", ok); + // a nothing-fits rank: no room, no pool + try std.testing.expectEqual(@as(u64, 0), largestPool(8.0, Serve.measured(1), 4.0, 1060)); +} + +test "the measured check's knobs: growth / dip overrides, TF_DSV41_BOOT_MEASURED, MemAvailable" { + var sl = try layout_mod.Layout.fromConfig(layout_mod.Release{}, .{ .split = true }); + const spl = sl.pool(); + const k: Knobs = .{}; + // TF_DSV41_SERVE_DIP_GIB=3.0 (a profile measured with a smaller dip): split 3.5M passes on the worker + const t = try Serve.withOverrides(1, null, "3.0"); + try std.testing.expectEqual(Serve{ .growth = 1.7, .dip = 3.0 }, t); + try std.testing.expect(serveCheck(k, .{ .rank = 1, .avail_gib = 12.9, .terms = t }, spl, 2, 3_500_000).ok); + // worst = 12.9 - 2.0 - 1.7 - 3.0 (rank 1, 1060 bytes a token, hard 4) + try std.testing.expectApproxEqAbs(@as(f64, 12.9 - 2.0 - 1.7 - 3.0), worstAt(12.9, 2.0, t), 1e-12); + try std.testing.expectEqual(Serve{ .growth = 2.5, .dip = 4.9 }, try Serve.withOverrides(0, " 2.5 ", "")); + try std.testing.expectEqual(Serve.measured(7), try Serve.withOverrides(7, null, null)); // any rank past 0: the worker's + try std.testing.expectError(error.BadGiBEnv, Serve.withOverrides(1, "-1", null)); + try std.testing.expectError(error.BadGiBEnv, Serve.withOverrides(1, null, "lots")); + // when it applies: forced, integrated, or device memory within 15 % of the host's + try std.testing.expectEqual(Mode.off, parseMode(" OFF")); + try std.testing.expectEqual(Mode.on, parseMode("True")); + try std.testing.expectEqual(Mode.auto, parseMode(null)); + try std.testing.expectEqual(Mode.auto, parseMode("maybe")); + const g: u64 = 1 << 30; + try std.testing.expect(measuredApplies(.auto, true, 0, 121 * g)); + try std.testing.expect(measuredApplies(.auto, false, 119 * g, 121 * g)); // GB10 without the integrated flag + try std.testing.expect(!measuredApplies(.auto, false, 96 * g, 1024 * g)); // a discrete card + try std.testing.expect(measuredApplies(.on, false, 96 * g, 1024 * g)); + try std.testing.expect(!measuredApplies(.off, true, 121 * g, 121 * g)); + const mi = try parseMeminfo("MemTotal: 126877032 kB\nMemFree: 1000 kB\nMemAvailable: 13526528 kB\n"); + try std.testing.expectEqual(MemInfo{ .total = 126877032 * 1024, .available = 13526528 * 1024 }, mi); + try std.testing.expectError(error.BadMeminfo, parseMeminfo("MemTotal: 1 kB\n")); + if (@import("builtin").os.tag == .linux) try std.testing.expect(try memAvailable() > 0); +} + +test "the Zig check: measured at the end of boot, a measured rank 1 passes, a pool 1 GiB past the largest refuses" { + var sl = try layout_mod.Layout.fromConfig(layout_mod.Release{}, .{ .split = true }); + const spl = sl.pool(); + const k: Knobs = .{}; + const per = tokenBytes(spl, 2); + const pool = @as(f64, @floatFromInt(poolBytes(spl, 2, 3_000_000))) / GiB; + // a rank 1 before the pool: the reference's terms refused it (6.07 - 2.10 workspace < 4); Zig's pass it + const terms: Serve = .{ .growth = 0, .dip = zig_dip_gib }; + const pre = serveCheck(k, .{ .rank = 1, .avail_gib = 14.94, .terms = terms }, spl, 2, 3_000_000); + try std.testing.expect(pre.ok); + // after the boot: a measured rank 1 idle 8.70 - 3.0 dip = 5.70, over the 4.0 floor + const ok = afterBoot(14.94, pool, 8.70, 0, zig_dip_gib, k.hard_gib, 3_000_000, per); + try std.testing.expect(ok.ok); + try std.testing.expectApproxEqAbs(@as(f64, 14.94 - pool - 8.70), ok.growth, 1e-9); + try std.testing.expectApproxEqAbs(@as(f64, 5.70), ok.worst, 1e-9); + // the largest pool lands the worst point on the floor; 1 GiB more pool (1 GiB less idle) refuses + const more = @as(u64, @intFromFloat(GiB / per)); + try std.testing.expect(ok.largest > 3_000_000); + const edge = ok.largest - 3_000_000; + const over = afterBoot(14.94, pool + (@as(f64, @floatFromInt(edge)) * per / GiB) + 1.0, 8.70 - (@as(f64, @floatFromInt(edge)) * per / GiB) - 1.0, 0, zig_dip_gib, k.hard_gib, ok.largest + more, per); + try std.testing.expect(!over.ok); + try std.testing.expectApproxEqAbs(@as(f64, 3.0), over.worst, 1e-6); // the floor less the GiB + try std.testing.expect(over.largest < ok.largest + more); + // an earlier boot's idle (6.39, before the trims) refuses at the measured dip: 2.48 would have left 3.91 + try std.testing.expect(!afterBoot(14.94, pool, 6.39, 0, zig_dip_gib, k.hard_gib, 3_000_000, per).ok); + try std.testing.expectEqual(Serve{ .growth = 0, .dip = zig_dip_gib }, try zigFromEnv()); + // the reserve apart: a measured rank 1 keeps the floor (8.76 - 1.5 - 3.0 = 4.26); 7.33 idle (4.2M untrimmed) not + const r = afterBoot(14.94, pool, 8.76, 1.5, zig_dip_gib, k.hard_gib, 3_000_000, per); + try std.testing.expect(r.ok); + try std.testing.expectApproxEqAbs(@as(f64, 4.26), r.worst, 1e-9); + try std.testing.expectApproxEqAbs(@as(f64, 14.94 - pool - 8.76), r.growth, 1e-9); // growth from the true idle + try std.testing.expect(!afterBoot(14.94, pool, 7.33, 1.5, zig_dip_gib, k.hard_gib, 3_000_000, per).ok); + try std.testing.expectEqual(@as(f64, 0), try reserveFromEnv()); +} diff --git a/zig/src/families/deepseek_v41/kv/device.zig b/zig/src/families/deepseek_v41/kv/device.zig new file mode 100644 index 0000000000..dd6c5160af --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/device.zig @@ -0,0 +1,181 @@ +//! The pool on the GPU: one device tensor a family, stacked [slots, pts] int32 page tables, the sessions' PageStore. + +const std = @import("std"); +const cuda = @import("cuda"); +const sessions = @import("sessions"); +const Pool = sessions.Pool; +const PageStore = sessions.PageStore; + +/// A family as the kernels bind it (attn / kv_store / index): rows, their scales, the page table, rows-a-page shift. +pub const View = struct { + cv: u64, + csc: u64, + cvs: i64, + css: i64, + pt: u64, + pts: i64, + psh: c_int, +}; + +const fences = 8; + +pub const DevicePool = struct { + gpa: std.mem.Allocator, + d: *const cuda.Driver, + ctx: *const cuda.Context, + pool: *Pool, + tensors: []cuda.DeviceBuffer, + /// [slots, pts] int32: the physical tables and the split families' local tables + table: cuda.DeviceBuffer, + local: cuda.DeviceBuffer, + host_table: []i32, + host_local: []i32, + pts: u32, + slots: u32, + io: cuda.Stream, + events: [fences]cuda.Event, + compute: cuda.Stream, + next_fence: u64 = 0, + + /// Allocates every family's tensor (zeroed) and the tables for `slots` slots of up to `max_pages` pages. + pub fn init(gpa: std.mem.Allocator, d: *const cuda.Driver, ctx: *const cuda.Context, pool: *Pool, compute: cuda.Stream, slots: u32, max_pages: u32) !DevicePool { + const fams = pool.layout.families; + const t = try gpa.alloc(cuda.DeviceBuffer, fams.len); + errdefer gpa.free(t); + var made: usize = 0; + errdefer for (t[0..made]) |*b| b.free(); + for (fams, 0..) |f, i| { + t[i] = try cuda.DeviceBuffer.alloc(d, @as(usize, pool.familyTensorPages(f)) * f.pageBytes(pool.page)); + made += 1; + try t[i].fill8(0, compute.handle); + } + const n = @as(usize, slots) * max_pages; + var table = try cuda.DeviceBuffer.alloc(d, n * 4); + errdefer table.free(); + try table.fill32(pool.null_page, compute.handle); + var local = try cuda.DeviceBuffer.alloc(d, n * 4); + errdefer local.free(); + try local.fill32(pool.local_null, compute.handle); + const ht = try gpa.alloc(i32, n); + errdefer gpa.free(ht); + @memset(ht, @intCast(pool.null_page)); + const hl = try gpa.alloc(i32, n); + errdefer gpa.free(hl); + @memset(hl, @intCast(pool.local_null)); + var io = try cuda.Stream.init(d, true); + errdefer io.deinit(); + var ev: [fences]cuda.Event = undefined; + for (&ev) |*e| e.* = try cuda.Event.init(d, false); + // fills ran on the compute stream (a legacy-stream memset races later uploads): sync before any use + try compute.synchronize(); + return .{ .gpa = gpa, .d = d, .ctx = ctx, .pool = pool, .tensors = t, .table = table, .local = local, .host_table = ht, .host_local = hl, .pts = max_pages, .slots = slots, .io = io, .events = ev, .compute = compute }; + } + + pub fn deinit(p: *DevicePool) void { + for (p.tensors) |*b| b.free(); + p.gpa.free(p.tensors); + p.table.free(); + p.local.free(); + p.gpa.free(p.host_table); + p.gpa.free(p.host_local); + for (&p.events) |*e| e.deinit(); + p.io.deinit(); + p.* = undefined; + } + + /// Uploads the table entries each slot changed since the last sync, on the compute stream. + pub fn syncTables(p: *DevicePool) !void { + for (p.pool.slots.items) |s| { + if (s.id >= p.slots) return error.TooManySlots; + const r = s.takeDirty() orelse continue; + const row = @as(usize, s.id) * p.pts; + for (r.lo..r.hi) |i| { + p.host_table[row + i] = @intCast(s.tableAt(@intCast(i))); + p.host_local[row + i] = @intCast(s.localTableAt(@intCast(i))); + } + const a = row + r.lo; + const n = r.hi - r.lo; + try p.table.uploadAsync(a * 4, std.mem.sliceAsBytes(p.host_table[a..][0..n]), p.compute.handle); + try p.local.uploadAsync(a * 4, std.mem.sliceAsBytes(p.host_local[a..][0..n]), p.compute.handle); + } + } + + /// Family fam as the kernels take it for one slot (row mode: slot 0's row with pts, the stacked table). + pub fn view(p: *const DevicePool, fam: u32, slot: u32) View { + const f = p.pool.layout.families[fam]; + const split = f.split and p.pool.world > 1; + const tab = if (split) p.local else p.table; + const rows = f.pageRows(p.pool.page); + const values: u64 = if (std.mem.startsWith(u8, f.name, "comp") and f.row_bytes == 584) 576 else f.row_bytes; + return .{ .cv = p.tensors[fam].ptr, .csc = p.tensors[fam].ptr + values, .cvs = f.row_bytes, .css = f.row_bytes, .pt = tab.ptr + @as(u64, slot) * p.pts * 4, .pts = p.pts, .psh = @intCast(std.math.log2_int(u32, rows)) }; + } + + pub fn store(p: *DevicePool) PageStore { + return .{ .ptr = p, .vtable = &vtable }; + } + + const vtable: PageStore.VTable = .{ .read = read, .write = write, .copyPage = copyPage, .fence = fence, .waitFence = waitFence, .publish = publish }; + + /// Consecutive family pages as runs: one copy a run (a chunk of a long entry is mostly one or two runs). + fn runs(pages: []const u32, i: usize) usize { + var j = i + 1; + while (j < pages.len and pages[j] == pages[j - 1] + 1) j += 1; + return j; + } + + fn read(ptr: *anyopaque, fam: u32, pages: []const u32, dst: []u8) anyerror!void { + const p: *DevicePool = @ptrCast(@alignCast(ptr)); + try p.ctx.makeCurrent(); + const pb: usize = @intCast(p.pool.layout.families[fam].pageBytes(p.pool.page)); + if (dst.len != pages.len * pb) return error.BadLength; + var i: usize = 0; + while (i < pages.len) { + const j = runs(pages, i); + try p.tensors[fam].downloadAsync(pages[i] * pb, dst[i * pb .. j * pb], p.io.handle); + i = j; + } + try p.io.synchronize(); + } + + fn write(ptr: *anyopaque, fam: u32, pages: []const u32, src: []const u8) anyerror!void { + const p: *DevicePool = @ptrCast(@alignCast(ptr)); + try p.ctx.makeCurrent(); + const pb: usize = @intCast(p.pool.layout.families[fam].pageBytes(p.pool.page)); + if (src.len != pages.len * pb) return error.BadLength; + var i: usize = 0; + while (i < pages.len) { + const j = runs(pages, i); + try p.tensors[fam].uploadAsync(pages[i] * pb, src[i * pb .. j * pb], p.io.handle); + i = j; + } + try p.io.synchronize(); // the staging buffer goes back to the lanes after this + } + + fn copyPage(ptr: *anyopaque, fam: u32, src: u32, dst: u32) anyerror!void { + const p: *DevicePool = @ptrCast(@alignCast(ptr)); + const pb: usize = @intCast(p.pool.layout.families[fam].pageBytes(p.pool.page)); + const b = p.tensors[fam]; + try b.copyFrom(dst * pb, b.ptr + src * pb, pb, p.compute.handle); + } + + fn fence(ptr: *anyopaque) anyerror!u64 { + const p: *DevicePool = @ptrCast(@alignCast(ptr)); + const t = p.next_fence; + p.next_fence += 1; + try p.events[t % fences].record(p.compute); + return t; + } + + fn waitFence(ptr: *anyopaque, token: u64) anyerror!void { + const p: *DevicePool = @ptrCast(@alignCast(ptr)); + try p.ctx.makeCurrent(); + // the slot holds this fence's record or a later one: waiting for it waits at least as long + try p.events[token % fences].synchronize(); + } + + fn publish(ptr: *anyopaque) anyerror!void { + const p: *DevicePool = @ptrCast(@alignCast(ptr)); + try p.ctx.makeCurrent(); + try p.io.synchronize(); + } +}; diff --git a/zig/src/families/deepseek_v41/kv/gpu_main.zig b/zig/src/families/deepseek_v41/kv/gpu_main.zig new file mode 100644 index 0000000000..59ef0242c9 --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/gpu_main.zig @@ -0,0 +1,164 @@ +//! tf-kv-gpu-test DIR [tokens]: device tables, NVMe park/restore, RAM copy-on-write, views; exit 0: bytes equal. + +const std = @import("std"); +const cuda = @import("cuda"); +const sessions = @import("sessions"); +const layout = @import("layout.zig"); +const DevicePool = @import("device.zig").DevicePool; +const Io = std.Io; + +fn now(io: Io) i96 { + return Io.Clock.awake.now(io).toNanoseconds(); +} + +fn secs(a: i96, b: i96) f64 { + return @as(f64, @floatFromInt(b - a)) / 1e9; +} + +/// The bytes logical page k of family fam holds (8-byte words), whatever physical page it sits on. +fn word(k: u64, fam: u64, i: u64) u64 { + return (k << 40) ^ (i *% 0x9E3779B97F4A7C15) ^ (fam << 32); +} + +const Bounded = struct { + gpa: std.mem.Allocator, + const vt: sessions.store.Bounded.VTable = .{ .nbytes = nbytes, .blobs = blobs, .release = release }; + fn make(gpa: std.mem.Allocator) !sessions.store.Bounded { + const b = try gpa.create(Bounded); + b.* = .{ .gpa = gpa }; + return .{ .ptr = b, .vtable = &vt }; + } + fn nbytes(_: *anyopaque) u64 { + return 4096; + } + fn blobs(_: *anyopaque, gpa: std.mem.Allocator) anyerror![]sessions.disk.Blob { + const out = try gpa.alloc(sessions.disk.Blob, 1); + const bytes = try gpa.alloc(u8, 4096); + @memset(bytes, 0x5a); + out[0] = .{ .name = try gpa.dupe(u8, "rings"), .bytes = bytes }; + return out; + } + fn release(p: *anyopaque) void { + const b: *Bounded = @ptrCast(@alignCast(p)); + b.gpa.destroy(b); + } +}; + +/// Every owned page of a slot read back from the device and compared with word(); returns mismatching words. +fn compare(gpa: std.mem.Allocator, pool: *sessions.Pool, dp: *DevicePool, s: *const sessions.Slot, fams: []const sessions.Family) !u64 { + var bad: u64 = 0; + const st = dp.store(); + for (fams, 0..) |f, fi| { + const pb = f.pageBytes(pool.page); + const buf = try gpa.alloc(u8, pb); + defer gpa.free(buf); + for (s.mapped(), 0..) |pg, k| { + if (f.split and !pool.owns(pg)) continue; + try st.read(@intCast(fi), &.{pool.familyPage(f, pg)}, buf); + for (std.mem.bytesAsSlice(u64, buf), 0..) |w, i| bad += @intFromBool(w != word(k, fi, i)); + } + } + return bad; +} + +fn run(gpa: std.mem.Allocator, io: Io, out: *Io.Writer, d: *const cuda.Driver, ctx: *const cuda.Context, dir: []const u8, tokens: u64, world: u32) !u64 { + var l = try layout.Layout.fromConfig(layout.Release{}, .{ .split = world > 1 }); + const fams = l.families(); + const pool_tokens = (2 * tokens + 65536) / 512 * 512; + var pool = try sessions.Pool.init(gpa, l.pool(), pool_tokens, world, 0); + defer pool.deinit(); + var compute = try cuda.Stream.init(d, true); + defer compute.deinit(); + const max_pages = sessions.pool.pagesFor(tokens + 16384, 256); + var dp = try DevicePool.init(gpa, d, ctx, &pool, compute, 4, max_pages); + defer dp.deinit(); + var compat: [16]u8 = @splat(0); + compat[0] = @intCast(world); + compat[1] = 0x6b; + const disk = try sessions.Disk.open(gpa, io, .{ .root = dir, .compat = compat, .rank = 0, .world = world, .min_tokens = 1 }); + defer disk.close(); + var store = try sessions.Store.init(gpa, io, &pool, dp.store(), disk, .{ .async_io = true }); + defer store.deinit(); + var bad: u64 = 0; + + // a slot's rows, written page by page through the page store (what kv_store would write) + const a = try pool.newSlot(tokens + 16384); + try a.ensure(tokens); + for (fams, 0..) |f, fi| { + const pb = f.pageBytes(pool.page); + const buf = try gpa.alloc(u8, pb); + defer gpa.free(buf); + for (a.mapped(), 0..) |pg, k| { + if (f.split and !pool.owns(pg)) continue; + for (std.mem.bytesAsSlice(u64, buf), 0..) |*w, i| w.* = word(k, fi, i); + try dp.store().write(@intCast(fi), &.{pool.familyPage(f, pg)}, buf); + } + } + // page tables: the dirty range uploaded, read back equal to the host book + try dp.syncTables(); + try compute.synchronize(); + const tab = try gpa.alloc(i32, max_pages); + defer gpa.free(tab); + try dp.table.download(0, std.mem.sliceAsBytes(tab)); + for (tab, 0..) |v, i| bad += @intFromBool(v != @as(i32, @intCast(a.tableAt(@intCast(i))))); + try dp.local.download(0, std.mem.sliceAsBytes(tab)); + for (tab, 0..) |v, i| bad += @intFromBool(v != @as(i32, @intCast(a.localTableAt(@intCast(i))))); + const v20 = dp.view(l.compOf(20).?, 0); + const v2 = dp.view(l.compOf(2).?, 1); + if (v20.psh != 8 or v2.psh != 7 or v20.csc != v20.cv + 576 or v2.pt != (if (world > 1) dp.local.ptr else dp.table.ptr) + @as(u64, max_pages) * 4) bad += 1; + + // save, park on the tier thread behind the fence, restore onto other pages, compare + const ids = try gpa.alloc(i32, tokens); + defer gpa.free(ids); + for (ids, 0..) |*t, i| t.* = @intCast(i % 129280); + const id = (try store.save(a, try Bounded.make(gpa), ids, 1, 0)).?; + _ = try a.releaseAll(); + const t0 = now(io); + _ = try store.settle((try store.beginPark(id)).?); + const t1 = now(io); + const pad = try pool.newSlot(65536); + try pad.ensure(32768); // the restore lands on other pages + const b = try pool.newSlot(tokens + 16384); + const t2 = now(io); + var got = (try store.settle(try store.beginRestore(id, b))).?; + got.deinit(gpa); + const t3 = now(io); + bad += try compare(gpa, &pool, &dp, b, fams); + // a RAM resume of a partial entry: full pages shared, the last copied on the compute stream + const short = tokens / 2 + 100; + const id2 = (try store.save(b, try Bounded.make(gpa), ids[0..short], 1, 0)).?; + const c = try pool.newSlot(tokens + 16384); + _ = try store.restoreRam(id2, c); + try compute.synchronize(); + bad += try compare(gpa, &pool, &dp, c, fams); + const gb = @as(f64, @floatFromInt(disk.used)) / 1e9; + try out.print("world {d} rank 0, {d} tokens: file {d:.3} GB; park {d:.3} s ({d:.2} GB/s), restore {d:.3} s ({d:.2} GB/s) through device memory; mismatches {d}\n", .{ world, tokens, gb, secs(t0, t1), gb / secs(t0, t1), secs(t2, t3), gb / secs(t2, t3), bad }); + return bad; +} + +pub fn main(init: std.process.Init) !u8 { + const gpa = init.gpa; + const io = init.io; + const args = try init.minimal.args.toSlice(init.arena.allocator()); + if (args.len < 2) { + std.debug.print("usage: tf-kv-gpu-test DIR [tokens]\n", .{}); + return 2; + } + const tokens = if (args.len > 2) try std.fmt.parseInt(u64, args[2], 10) else 1 << 20; + var driver = try cuda.Driver.open(); + defer driver.close(); + var ctx = try cuda.Context.init(&driver, 0); + defer ctx.deinit(); + var name: [256]u8 = undefined; + var buf: [4096]u8 = undefined; + var w = Io.File.stdout().writer(io, &buf); + const out = &w.interface; + try out.print("device: {s}\n", .{try ctx.name(&name)}); + var bad: u64 = 0; + bad += try run(gpa, io, out, &driver, &ctx, args[1], tokens, 1); + try out.flush(); + bad += try run(gpa, io, out, &driver, &ctx, args[1], tokens, 2); + try out.print("{s}\n", .{if (bad == 0) "PASS" else "FAIL"}); + try out.flush(); + return if (bad == 0) 0 else 1; +} diff --git a/zig/src/families/deepseek_v41/kv/kv.zig b/zig/src/families/deepseek_v41/kv/kv.zig new file mode 100644 index 0000000000..9ac3ba073e --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/kv.zig @@ -0,0 +1,27 @@ +//! DeepSeek-V4.1 KV memory: families, split KV exchange, priced floor, device pool (neutral parts: zig/src/sessions). + +pub const sessions = @import("sessions"); +pub const layout = @import("layout.zig"); +pub const Layout = layout.Layout; +pub const split = @import("split.zig"); +pub const budget = @import("budget.zig"); +pub const device = @import("device.zig"); +pub const DevicePool = device.DevicePool; +pub const sess = @import("sess.zig"); +pub const sched = @import("sched.zig"); +pub const progress = @import("progress.zig"); + +test { + _ = layout; + _ = split; + _ = budget; + _ = device; + _ = @import("split_test.zig"); + _ = @import("union_plan_test.zig"); + _ = @import("split_roce_test.zig"); + _ = sess; + _ = @import("session_slots_test.zig"); + _ = @import("resend_test.zig"); + _ = sched; + _ = progress; +} diff --git a/zig/src/families/deepseek_v41/kv/layout.zig b/zig/src/families/deepseek_v41/kv/layout.zig new file mode 100644 index 0000000000..5235748c1a --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/layout.zig @@ -0,0 +1,129 @@ +//! DeepSeek-V4.1's paged cache families: comp.L (compressed KV) and index_k.L (index keys) per KV source layer. + +const std = @import("std"); +const sessions = @import("sessions"); +const Family = sessions.Family; + +pub const KvFormat = enum { fp8, fp4 }; +pub const IndexFormat = enum { fp8, bf16 }; + +pub fn compRowBytes(f: KvFormat) u32 { + return switch (f) { + .fp8 => 584, + .fp4 => 384, + }; +} + +pub fn indexRowBytes(f: IndexFormat) u32 { + return switch (f) { + .fp8 => 132, + .bf16 => 256, + }; +} + +pub const Options = struct { + kv: KvFormat = .fp8, + index: IndexFormat = .fp8, + /// TF_DSV41_KV_SPLIT: the comp families sharded by logical page over the TP ranks + split: bool = false, + page: u32 = 256, +}; + +pub const max_sources = 8; + +pub const Layout = struct { + opts: Options = .{}, + n: u32 = 0, + fams: [2 * max_sources]Family = undefined, + names: [2 * max_sources][16]u8 = undefined, + /// the KV source layers, in order + sources: [max_sources]u32 = undefined, + nsources: u32 = 0, + + /// The families of every KV source layer of a config (duck-typed: `layers`, `isKvSource`, `compressRatio`). + pub fn fromConfig(cfg: anytype, opts: Options) !Layout { + var l: Layout = .{ .opts = opts }; + var layer: u32 = 0; + while (layer < cfg.layers) : (layer += 1) { + if (!cfg.isKvSource(layer)) continue; + try l.add(layer, cfg.compressRatio(layer)); + } + if (l.nsources == 0) return error.NoKvSources; + return l; + } + + /// One KV source layer at a compression ratio (1 or 2). + pub fn add(l: *Layout, layer: u32, ratio: u32) !void { + if (l.nsources == max_sources) return error.TooManySources; + if (ratio == 0 or l.opts.page % ratio != 0) return error.BadRatio; + l.sources[l.nsources] = layer; + l.nsources += 1; + l.push("comp", layer, .{ .name = "", .ratio = ratio, .row_bytes = compRowBytes(l.opts.kv), .split = l.opts.split }); + l.push("index_k", layer, .{ .name = "", .ratio = ratio, .row_bytes = indexRowBytes(l.opts.index) }); + } + + fn push(l: *Layout, kind: []const u8, layer: u32, f: Family) void { + const name = std.fmt.bufPrint(&l.names[l.n], "{s}.{d}", .{ kind, layer }) catch unreachable; + l.fams[l.n] = f; + l.fams[l.n].name = name; + l.n += 1; + } + + /// Rebinds the names after the value moved (the families name into this value's own storage). + pub fn families(l: *Layout) []const Family { + for (l.fams[0..l.n], 0..) |*f, i| f.name = l.names[i][0..f.name.len]; + return l.fams[0..l.n]; + } + + /// The pool's layout (sessions.Layout) over these families. + pub fn pool(l: *Layout) sessions.Layout { + return .{ .families = l.families(), .page = l.opts.page }; + } + + /// The family index of comp.L / index_k.L. + pub fn compOf(l: *const Layout, layer: u32) ?u32 { + for (l.sources[0..l.nsources], 0..) |s, i| if (s == layer) return @intCast(2 * i); + return null; + } + + pub fn indexOf(l: *const Layout, layer: u32) ?u32 { + return if (l.compOf(layer)) |c| c + 1 else null; + } + + /// A rank's pool bytes a token (world: the TP ranks the split families shard over). + pub fn bytesPerToken(l: *Layout, world: u32) f64 { + return l.pool().bytesPerToken(if (l.opts.split) world else 1); + } +}; + +/// The release topology (config.json's kv_source_layer_ids and compress_ratios), for tests and offline pricing. +pub const Release = struct { + layers: u32 = 43, + pub fn isKvSource(_: Release, layer: u32) bool { + return layer == 2 or layer == 8 or layer == 14 or layer == 20; + } + pub fn compressRatio(_: Release, layer: u32) u32 { + return if (layer < 2 or layer >= 40) 0 else if (layer < 20) 2 else 1; + } +}; + +test "the release's families: 1,790 B a token replicated, 1,060 split, the FP4 and bf16 variants" { + var l = try Layout.fromConfig(Release{}, .{}); + try std.testing.expectEqual(@as(u32, 8), l.n); + try std.testing.expectEqualStrings("comp.2", l.families()[0].name); + try std.testing.expectEqualStrings("index_k.20", l.families()[7].name); + try std.testing.expectEqual(@as(u32, 1), l.families()[6].ratio); + try std.testing.expectApproxEqAbs(@as(f64, 1790), l.bytesPerToken(2), 1e-9); + var s = try Layout.fromConfig(Release{}, .{ .split = true }); + try std.testing.expectApproxEqAbs(@as(f64, 1060), s.bytesPerToken(2), 1e-9); + try std.testing.expectApproxEqAbs(@as(f64, 1790), s.bytesPerToken(1), 1e-9); + var f4 = try Layout.fromConfig(Release{}, .{ .kv = .fp4, .split = true }); + try std.testing.expectApproxEqAbs(@as(f64, 810), f4.bytesPerToken(2), 1e-9); // A + FP4 comp + var bf = try Layout.fromConfig(Release{}, .{ .index = .bf16 }); + try std.testing.expectApproxEqAbs(@as(f64, 2100), bf.bytesPerToken(1), 1e-9); + try std.testing.expectEqual(@as(?u32, 6), l.compOf(20)); + try std.testing.expectEqual(@as(?u32, 3), l.indexOf(8)); + const moved = l; + var m = moved; + try std.testing.expectEqualStrings("comp.14", m.families()[4].name); +} diff --git a/zig/src/families/deepseek_v41/kv/progress.zig b/zig/src/families/deepseek_v41/kv/progress.zig new file mode 100644 index 0000000000..2d6b737494 --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/progress.zig @@ -0,0 +1,96 @@ +//! When the round planner's log lines are due: admission waits and long prefills, first and then every `every_s`. +const std = @import("std"); + +/// Seconds between a request's repeated lines. +pub const every_s: f64 = 30; +/// Prompts with fewer tokens to prefill than this get no progress lines. +pub const long_tokens: u64 = 16384; + +/// Requests the planner keeps queued: since when, and when their wait was last logged. +pub const Waits = struct { + map: std.AutoArrayHashMapUnmanaged(usize, [2]f64) = .empty, + + pub fn deinit(w: *Waits, gpa: std.mem.Allocator) void { + w.map.deinit(gpa); + } + + /// `key` waits at `now`: the seconds it has waited when a line is due (its first wait, then every `every_s`). + pub fn waiting(w: *Waits, gpa: std.mem.Allocator, key: usize, now: f64) !?f64 { + const g = try w.map.getOrPut(gpa, key); + if (!g.found_existing) { + g.value_ptr.* = .{ now, now }; + return 0; + } + if (now - g.value_ptr[1] < every_s) return null; + g.value_ptr[1] = now; + return now - g.value_ptr[0]; + } + + /// `key` was admitted or left: the seconds it waited, if it waited at all. + pub fn done(w: *Waits, key: usize, now: f64) ?f64 { + const kv = w.map.fetchSwapRemove(key) orelse return null; + return now - kv.value[0]; + } +}; + +/// A long prompt's prefill: its length, where it started, and its last logged point. +const Run = struct { n: u64, from: u64, start: f64, at: f64, done: u64 }; + +pub const Line = struct { done: u64, n: u64, seconds: f64, rate: f64 }; + +pub const Prefills = struct { + map: std.AutoArrayHashMapUnmanaged(usize, Run) = .empty, + + pub fn deinit(p: *Prefills, gpa: std.mem.Allocator) void { + p.map.deinit(gpa); + } + + /// `key`'s prompt holds `done` of `n` tokens at `now`: a line when one is due (tokens a second since the last). + pub fn at(p: *Prefills, gpa: std.mem.Allocator, key: usize, n: u64, done: u64, now: f64) !?Line { + const g = try p.map.getOrPut(gpa, key); + // a damaged saved entry restarts its prompt from 0: a new run + if (!g.found_existing or done < g.value_ptr.done) g.value_ptr.* = .{ .n = n, .from = done, .start = now, .at = now, .done = done }; + const r = g.value_ptr; + if (r.n -| r.from < long_tokens or now - r.at < every_s) return null; + const line: Line = .{ .done = done, .n = n, .seconds = now - r.start, .rate = @as(f64, @floatFromInt(done - r.done)) / (now - r.at) }; + r.at = now; + r.done = done; + return line; + } + + /// `key`'s prompt is in (or it left): the whole prefill's line for a long prompt. + pub fn end(p: *Prefills, key: usize, done: u64, now: f64) ?Line { + const kv = p.map.fetchSwapRemove(key) orelse return null; + const r = kv.value; + if (r.n -| r.from < long_tokens or now <= r.start) return null; + return .{ .done = done, .n = r.n, .seconds = now - r.start, .rate = @as(f64, @floatFromInt(done -| r.from)) / (now - r.start) }; + } +}; + +test "progress: a wait's lines come first and then every 30 s; a long prefill's lines carry its rate" { + const gpa = std.testing.allocator; + var w: Waits = .{}; + defer w.deinit(gpa); + try std.testing.expectEqual(@as(?f64, 0), try w.waiting(gpa, 7, 100)); + try std.testing.expectEqual(@as(?f64, null), try w.waiting(gpa, 7, 110)); + try std.testing.expectEqual(@as(?f64, 30), try w.waiting(gpa, 7, 130)); + try std.testing.expectEqual(@as(?f64, 45), w.done(7, 145)); + try std.testing.expectEqual(@as(?f64, null), w.done(7, 146)); // logged once + var p: Prefills = .{}; + defer p.deinit(gpa); + // a 183K prompt from 0: a line every 30 s with the rows since the last + try std.testing.expectEqual(@as(?Line, null), try p.at(gpa, 1, 183154, 0, 0)); + try std.testing.expectEqual(@as(?Line, null), try p.at(gpa, 1, 183154, 8192, 10)); + const l = (try p.at(gpa, 1, 183154, 73728, 30)).?; + try std.testing.expectEqual(@as(u64, 73728), l.done); + try std.testing.expectApproxEqAbs(@as(f64, 2457.6), l.rate, 0.01); + try std.testing.expectApproxEqAbs(@as(f64, 2442.04), p.end(1, 183153, 75).?.rate, 0.01); + // a short prompt (or a resumed one with little left) gets none + try std.testing.expectEqual(@as(?Line, null), try p.at(gpa, 2, 9000, 0, 0)); + try std.testing.expectEqual(@as(?Line, null), try p.at(gpa, 2, 9000, 8999, 40)); + try std.testing.expectEqual(@as(?Line, null), p.end(2, 8999, 41)); + // a restart from 0 (a damaged entry) is a new run, not a negative rate + _ = try p.at(gpa, 3, 100000, 40000, 0); + try std.testing.expectEqual(@as(?Line, null), try p.at(gpa, 3, 100000, 0, 31)); + try std.testing.expectEqual(@as(u64, 61440), (try p.at(gpa, 3, 100000, 61440, 61)).?.done); +} diff --git a/zig/src/families/deepseek_v41/kv/resend_test.zig b/zig/src/families/deepseek_v41/kv/resend_test.zig new file mode 100644 index 0000000000..93a9651b51 --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/resend_test.zig @@ -0,0 +1,122 @@ +//! 2 x 1M, 4 x 740K push the first chat to NVMe; its resend takes the prompt as history. + +const std = @import("std"); +const testing = std.testing; +const sessions = @import("sessions"); +const sess = @import("sess.zig"); + +const Pool = sessions.Pool; +const Store = sessions.Store; +const Bounded = sessions.store.Bounded; + +/// The pool's families, a few bytes a row: the served token counts and page, its pages small enough for a host test +const fams = [_]sessions.Family{ + .{ .name = "comp.4", .ratio = 4, .row_bytes = 8 }, + .{ .name = "index_k.4", .ratio = 4, .row_bytes = 4 }, +}; + +const Blob = struct { + gpa: std.mem.Allocator, + pos: u64, + const vt: Bounded.VTable = .{ .nbytes = nbytes, .blobs = blobs, .release = release }; + fn make(gpa: std.mem.Allocator, pos: u64) !Bounded { + const b = try gpa.create(Blob); + b.* = .{ .gpa = gpa, .pos = pos }; + return .{ .ptr = b, .vtable = &vt }; + } + fn nbytes(_: *anyopaque) u64 { + return 64 << 20; // the bounded state of a long chat (rings, carries, stash) + } + fn blobs(p: *anyopaque, gpa: std.mem.Allocator) anyerror![]sessions.disk.Blob { + const b: *Blob = @ptrCast(@alignCast(p)); + const out = try gpa.alloc(sessions.disk.Blob, 1); + out[0] = .{ .name = try gpa.dupe(u8, "slot"), .bytes = try gpa.dupe(u8, std.mem.asBytes(&b.pos)) }; + return out; + } + fn release(p: *anyopaque) void { + const b: *Blob = @ptrCast(@alignCast(p)); + b.gpa.destroy(b); + } +}; + +fn chat(ids: []i32, salt: i32) void { + for (ids, 0..) |*t, i| t.* = salt * 7_000_000 + @as(i32, @intCast(i)); +} + +/// A turn under the served admission: spills parked, pages reserved, the prompt saved, the slot free. +fn turn(gpa: std.mem.Allocator, st: *Store, slot: *sessions.Slot, ids: []const i32, tag: u32) !void { + var spills: std.ArrayList(u32) = .empty; + defer spills.deinit(gpa); + const plan = try sess.admit(st, null, ids.len, 512, slot.capacity, &.{}, 0, &spills); + try testing.expect(!plan.wait); + for (spills.items) |id| _ = try st.evict(id); + try st.settleAll(); + slot.reserve(plan.need); + try slot.ensure(ids.len); + _ = try st.save(slot, try Blob.make(gpa, ids.len), ids, tag, 0); + _ = try slot.releaseAll(); + try st.settleAll(); +} + +test "a 1M chat pushed to NVMe by 4 x 740K resumes with the prompt's ids as its history (a served NotInRam)" { + const gpa = testing.allocator; + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + try tmp.dir.createDirPath(testing.io, "tier"); + const root = try tmp.dir.realPathFileAlloc(testing.io, "tier", gpa); + defer gpa.free(root); + const page = 256; + var pool = try Pool.init(gpa, .{ .families = &fams, .page = page }, 11_718 * page, 1, 0); // a 3.0M pool + defer pool.deinit(); + var host = try sessions.HostPool.init(gpa, &pool); + defer host.deinit(); + const disk = try sessions.Disk.open(gpa, testing.io, .{ .root = root, .compat = @splat(3), .min_tokens = 1024, .chunk = 1 << 20, .staging = 4, .lanes = 2, .direct = false }); + defer disk.close(); + var st = try Store.init(gpa, testing.io, &pool, host.store(), disk, .{ .ram_bytes = 256 << 20, .skip_covered = false, .delta = false }); + defer st.deinit(); + var slots: [4]*sessions.Slot = undefined; + for (&slots) |*s| s.* = try pool.newSlot(1 << 20); + const tag = 7; + const first = try gpa.alloc(i32, 1_000_000); + defer gpa.free(first); + chat(first, 1); + const second = try gpa.alloc(i32, 1_000_000); + defer gpa.free(second); + chat(second, 2); + try turn(gpa, &st, slots[0], first, tag); + try turn(gpa, &st, slots[1], second, tag); + const long = try gpa.alloc(i32, 740_000); + defer gpa.free(long); + for (0..4) |k| { + chat(long, @intCast(3 + k)); + try turn(gpa, &st, slots[k], long, tag); + } + try testing.expect(st.stats.parks >= 1); + // the first chat's prompt again, plus a new turn: found on NVMe, its tokens no longer indexed + const again = try gpa.alloc(i32, 1_000_100); + defer gpa.free(again); + @memcpy(again[0..first.len], first); + for (again[first.len..], 0..) |*t, i| t.* = 900 + @as(i32, @intCast(i)); + const hit = (try st.find(tag, again)).?; + try testing.expect(!hit.ram and st.entry(hit.id).extra.file); + try testing.expectEqual(@as(u64, first.len), hit.pos); + const hist = try gpa.alloc(i32, hit.pos); + defer gpa.free(hist); + try testing.expectError(error.NotInRam, st.index.idsOf(hit.id, hist)); // what the restore read before + // the admission for it: the hit held, the coldest RAM entries spilled, the pages reserved, then the restore + var spills: std.ArrayList(u32) = .empty; + defer spills.deinit(gpa); + const plan = try sess.admit(&st, hit, again.len, 512, slots[0].capacity, &.{hit.id}, 0, &spills); + try testing.expect(!plan.wait and spills.items.len > 0); + for (spills.items) |id| _ = try st.evict(id); + try st.settleAll(); + slots[0].reserve(plan.need); + const j = try st.beginRestore(hit.id, slots[0]); + var got = (try st.settle(j)).?; + defer got.deinit(gpa); + try st.historyOf(hit.id, again, hist); + try testing.expectEqualSlices(i32, first, hist); + // the leader's ids are the entry's: another prompt's are refused, and none at all is the old failure + try testing.expectError(error.NotTheEntry, st.historyOf(hit.id, second, hist)); + try testing.expectError(error.NotInRam, st.historyOf(hit.id, null, hist)); +} diff --git a/zig/src/families/deepseek_v41/kv/sched.zig b/zig/src/families/deepseek_v41/kv/sched.zig new file mode 100644 index 0000000000..7468d1684e --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/sched.zig @@ -0,0 +1,596 @@ +//! The served engine's round planner (rank 0): which queued request starts and which prompt rows run between rounds. + +const std = @import("std"); +const sess = @import("sess.zig"); + +pub const grid: u64 = 16; +pub const GiB: f64 = @floatFromInt(@as(u64, 1) << 30); + +/// End of a slot's next piece from `done` toward `target` in `budget` rows: `target` if it fits, else last grid point. +pub fn pieceEnd(done: u64, target: u64, budget: u64, g: u64) u64 { + if (target - done <= budget) return target; + var end = (done + budget) / g * g; + if (end <= done) end = @min(target, (done / g + 1) * g); + return end; +} + +/// The last grid point strictly before a prompt's end. +pub fn snapshotPoint(n: u64) u64 { + return if (n > 0) (n - 1) / grid * grid else 0; +} + +/// A piece of t s is followed by >= t (1 - share) / share s (and a round) of decode; none decoding: no wait. +pub const Fairness = struct { + share: f64 = 0.5, + debt: f64 = 0.0, + rounds: u64 = 1, + + pub fn allow(f: *const Fairness, decoding: bool) bool { + if (!decoding) return true; + return f.rounds >= 1 and f.debt <= 0.0; + } + + pub fn after(f: *Fairness, piece_s: f64, round_s: f64, prefilling: bool) void { + if (piece_s > 0.0) { + f.debt = @max(f.debt, 0.0) + piece_s * (1.0 - f.share) / f.share; + f.rounds = 0; + } + if (round_s > 0.0) { + f.debt -= round_s; + f.rounds += 1; + } + if (!prefilling) { + f.debt = 0.0; + f.rounds = 1; + } + } +}; + +/// Prefill price knobs (TF_DSV41_FLOOR_PF_*_GIB, TF_DSV41_INDEX_BUDGET_MIB, TF_DSV41_FLOOR_PRICE). +pub const PriceKnobs = struct { + on: bool = true, + chunk_gib: f64 = 0.533, + index_gib: f64 = 1.067, + host_gib: f64 = 0.6, + other_gib: f64 = 0.0, + index_positions: u64 = 32768, + budget_mib: u64 = 256, +}; + +/// A prompt's prefill transient in bytes: `once` with its first piece, `index` below `index_positions`, start..end. +pub const Price = struct { + once: i64 = 0, + index: i64 = 0, + start: u64 = 0, + end: u64 = 0, + + pub fn total(p: Price) i64 { + return p.once + p.index; + } + + /// What has not materialised once the prompt's state holds `done` tokens. + pub fn outstanding(p: Price, done: u64) i64 { + if (p.total() == 0) return 0; + const once: i64 = if (done <= p.start) p.once else 0; + const span = p.end - p.start; + const left = p.end -| @max(done, p.start); + // int(index * left / span): the exact quotient is >= 1/span from the next integer, so truncation matches + const idx: i64 = if (span > 0) @intCast(@divTrunc(@as(i128, p.index) * left, span)) else 0; + return once + idx; + } +}; + +/// The transient of prefilling tokens [cached, n - 1) in windows of `rows`. +pub fn prefillPrice(k: PriceKnobs, n: u64, cached: u64, rows: u64) Price { + if (!k.on) return .{}; + if (n < 1 + cached + 1) return .{}; // todo = n - 1 - cached <= 0 + const todo = n - 1 - cached; + const w = @as(f64, @floatFromInt(@min(rows, todo))) / 2048.0; + const once = (k.chunk_gib + k.host_gib) * w + k.other_gib; + const start = cached; + const end = @min(n - 1, k.index_positions); + const share = @as(f64, @floatFromInt(end -| start)) / @as(f64, @floatFromInt(k.index_positions)); + const index = k.index_gib * @as(f64, @floatFromInt(k.budget_mib)) / 256.0 * share; + return .{ .once = @intFromFloat(once * GiB), .index = @intFromFloat(index * GiB), .start = start, .end = @max(start, end) }; +} + +/// Bytes the next prefill round takes at `rows`: one window's `once` + each prompt's index growth (`left`: (n, done)). +pub fn roundNeed(k: PriceKnobs, rows: u64, left: []const [2]u64) i64 { + if (left.len == 0) return 0; + const once = prefillPrice(k, rows + 1, 0, rows).once; + var index: i64 = 0; + for (left) |x| index += prefillPrice(k, x[0], x[1], rows).index; + return once + index; +} + +/// Adaptive rows: TF_DSV41_PREFILL_ADAPT (on), _GIB (floor target), _UP_GIB (to step back up), _MIN (smallest rows). +pub const Adapt = struct { + gib: f64 = 5.0, + up_gib: f64 = 0.5, + min_rows: u64 = 512, + + /// The rows a round may take, largest first: `full`, its half, ... down to `min_rows` (on the grid). + pub fn steps(a: Adapt, full: u64, out: *[16]u64) []const u64 { + out[0] = full; + var n: usize = 1; + var r = full; + while (r / 2 >= a.min_rows and n < out.len) { + r = @max(grid, r / 2 / grid * grid); + out[n] = r; + n += 1; + } + return out[0..n]; + } + + /// The largest step whose need leaves `usable` at or above `gib` (+ `up_gib` above `current`); else the smallest. + pub fn choose(a: Adapt, k: PriceKnobs, full: u64, usable: i64, left: []const [2]u64, current: ?u64) u64 { + var buf: [16]u64 = undefined; + const st = a.steps(full, &buf); + for (st) |r| { + const keep = a.gib + (if (current != null and r > current.?) a.up_gib else 0.0); + if (@as(f64, @floatFromInt(usable - roundNeed(k, r, left))) >= keep * GiB) return r; + } + return st[st.len - 1]; + } +}; + +/// The /proc/meminfo lines the floor reads (bytes). +pub const Meminfo = struct { free: i64, available: i64, dirty: i64 = 0, writeback: i64 = 0, mapped: i64 = 0 }; + +/// The rank-0 memory floor: MemAvailable kept above the hard floor; `usable` = MemFree + reclaimable page cache. +pub const Floor = struct { + target_gib: f64 = 5.0, + hard_gib: f64 = 4.0, + /// TF_DSV41_ADMIT_FREE_FLOOR_GIB: immediately free on top (default 0) + free_gib: f64 = 0.0, + refuse_s: f64 = 30.0, // TF_DSV41_FLOOR_REFUSE_S: nothing live this long -> refused + keep_gib: f64 = 2.0, // TF_DSV41_ADMIT_CACHE_KEEP_GB: page cache never counted as available + last: i64 = 0, // the usable bytes of the last check (the refusal's test) + + pub fn usable(f: *const Floor, mi: Meminfo) i64 { + const keep: i64 = @intFromFloat(f.keep_gib * GiB); + const credit = @max(0, mi.available - mi.free - (mi.dirty + mi.writeback) - @max(mi.mapped, keep)); + return mi.free + credit; + } + + /// Something new may start: usable less `need` stays at or above the hard floor, and MemFree at `free_gib`. + pub fn check(f: *Floor, mi: Meminfo, need: i64) bool { + const u = f.usable(mi); + f.last = u; + const want = need + @as(i64, @intFromFloat(f.hard_gib * GiB)); + return u >= want and mi.free >= @as(i64, @intFromFloat(f.free_gib * GiB)); + } + + pub fn fitsIdle(f: *const Floor, need: i64) bool { + return @as(f64, @floatFromInt(f.last - need)) >= f.hard_gib * GiB; + } +}; + +pub const Settings = struct { + rows: u64 = 2048, // TF_DSV41_PREFILL_ROWS: rows of prompt a round (the forward's segment) + short: u64 = 512, // TF_DSV41_BATCH_SHORT: prompts with at most this many tokens left go every round + share: f64 = 0.5, // TF_DSV41_PREFILL_SHARE + long_prompt: u64 = 32768, // TF_DSV41_LONG_PROMPT / TF_DSV41_PREFILL_CONCURRENT (0: no limit) + concurrent: u64 = 1, + session_min: u64 = 64, // shorter prompts are not snapshotted + sessions: bool = true, // a session store is open (prompt snapshots at the replay point) + adapt: ?Adapt = null, // adaptive rows (on whenever the floor is): null = the fixed rows + floor: ?Floor = null, // the memory floor (null: no memory source) + price: PriceKnobs = .{}, + + pub fn fromEnv() !Settings { + var s: Settings = .{}; + if (envInt("TF_DSV41_PREFILL_ROWS")) |v| s.rows = v; + if (envInt("TF_DSV41_BATCH_SHORT")) |v| s.short = v; + if (envFloat("TF_DSV41_PREFILL_SHARE")) |v| s.share = v; + if (envInt("TF_DSV41_LONG_PROMPT")) |v| s.long_prompt = v; + if (envInt("TF_DSV41_PREFILL_CONCURRENT")) |v| s.concurrent = v; + if (!(s.share > 0.0 and s.share <= 1.0) or s.long_prompt < 1) return error.BadSettings; + if (envInt("TF_DSV41_INDEX_BUDGET_MIB")) |v| s.price.budget_mib = v; + if (envFloat("TF_DSV41_FLOOR_PF_CHUNK_GIB")) |v| s.price.chunk_gib = v; + if (envFloat("TF_DSV41_FLOOR_PF_INDEX_GIB")) |v| s.price.index_gib = v; + if (envFloat("TF_DSV41_FLOOR_PF_HOST_GIB")) |v| s.price.host_gib = v; + if (envFloat("TF_DSV41_FLOOR_PF_OTHER_GIB")) |v| s.price.other_gib = v; + if (std.c.getenv("TF_DSV41_FLOOR_PRICE")) |v| s.price.on = !std.mem.eql(u8, std.mem.trim(u8, std.mem.span(v), " "), "0"); + return s; + } + + /// The floor and adaptive rows from the environment. + pub fn withFloorFromEnv(s: *Settings) !void { + var f: Floor = .{}; + if (envFloat("TF_DSV41_FLOOR_GIB")) |v| f.target_gib = v; + if (envFloat("TF_DSV41_FLOOR_HARD_GIB")) |v| f.hard_gib = v; + if (!(f.hard_gib > 0 and f.hard_gib <= f.target_gib)) return error.BadSettings; + if (envFloat("TF_DSV41_ADMIT_FREE_FLOOR_GIB")) |v| f.free_gib = v; + if (envFloat("TF_DSV41_FLOOR_REFUSE_S")) |v| f.refuse_s = v; + if (envFloat("TF_DSV41_ADMIT_CACHE_KEEP_GB")) |v| f.keep_gib = v; + s.floor = f; + const off = if (std.c.getenv("TF_DSV41_PREFILL_ADAPT")) |v| std.mem.eql(u8, std.mem.trim(u8, std.mem.span(v), " "), "0") else false; + if (off) return; + var a: Adapt = .{ .gib = f.target_gib }; + if (envFloat("TF_DSV41_PREFILL_ADAPT_GIB")) |v| a.gib = v; + if (envFloat("TF_DSV41_PREFILL_ADAPT_UP_GIB")) |v| a.up_gib = v; + if (envInt("TF_DSV41_PREFILL_ADAPT_MIN")) |v| a.min_rows = v; + if (a.min_rows < 16) return error.BadSettings; + s.adapt = a; + } +}; + +fn envInt(name: [*:0]const u8) ?u64 { + const v = std.c.getenv(name) orelse return null; + const t = std.mem.trim(u8, std.mem.span(v), " "); + if (t.len == 0) return null; + return std.fmt.parseInt(u64, t, 10) catch null; +} + +fn envFloat(name: [*:0]const u8) ?f64 { + const v = std.c.getenv(name) orelse return null; + const t = std.mem.trim(u8, std.mem.span(v), " "); + if (t.len == 0) return null; + return std.fmt.parseFloat(f64, t) catch null; +} + +/// A queued request as the planner sees it (`key`: the host's handle for it). +pub const Job = struct { key: usize, n: u64, max_new: u64, submitted: f64 }; + +/// The session store's side of an admission (sessions_gpu.zig on the GPU path; the host gate's store in the test). +pub const Store = struct { + ctx: *anyopaque, + vtable: *const VTable, + + pub const Hit = struct { id: u32, pos: u64, ram: bool }; + + pub const VTable = struct { + /// The longest saved entry that strictly prefixes the job's prompt (null: none). + find: *const fn (ctx: *anyopaque, job: usize) anyerror!?Hit, + /// The pool decision for the job (`sess.admit`: `keep` entries never spilled, `extra` pages already taken). + pool: *const fn (ctx: *anyopaque, job: usize, hit: ?Hit, keep: []const u32, extra: u32, spills: *std.ArrayList(u32)) anyerror!sess.Plan, + }; +}; + +/// An admitted request's plan: its pages and resumed entry (spills in `Round.spills`, evicted first on every rank). +pub const Admission = struct { need: u32, hit: ?Store.Hit }; + +pub const Verdict = union(enum) { + admit: Admission, // start it (its admission); the host begins the stream, then `started` + wait, // it and every request behind it stay queued this round (FIFO) + skip, // it stays queued; the next one may start (a long prefill in flight) + refuse: Refusal, // it cannot run on this server: the request fails with these words +}; + +pub const Refusal = enum { too_large, memory }; + +/// One request in the slots: its prompt's progress. +pub const Seq = struct { + key: usize, + n: u64, + submitted: f64, + done: u64, // prompt tokens in the slot's state (prefill progress) + cached: u64 = 0, // the tokens its saved prefix gave it at admission + save_at: ?u64, // the replay point still to snapshot at + decoding: bool = false, + long: bool = false, + price: ?Price = null, + /// its NVMe entry failed to restore: this round's plan stands, its pieces do not run, it starts over after it + damaged: bool = false, +}; + +/// Rows [start, end) of `key`'s prompt; `save`: snapshot after; `run`: false for a damaged entry's planned piece. +pub const Piece = struct { key: usize, start: u64, end: u64, save: bool, run: bool = true }; + +pub const Counts = struct { + long_waits: u64 = 0, + mem_waits: u64 = 0, + pool_waits: u64 = 0, + mem_refused: u64 = 0, + rows_down: u64 = 0, + rows_up: u64 = 0, + rows: [8][2]u64 = @splat(.{ 0, 0 }), // rounds at each adaptive step (rows_): (rows, count) + + fn rowsAt(c: *Counts, r: u64) void { + for (&c.rows) |*x| { + if (x[0] == r) { + x[1] += 1; + return; + } + if (x[0] == 0) { + x.* = .{ r, 1 }; + return; + } + } + } +}; + +/// One admission pass's running state. +pub const Round = struct { + longs: u64 = 0, + owed: i64 = 0, + /// entries resumed by this round's earlier admissions (never spilled for a later one) + used: std.ArrayList(u32) = .empty, + /// pages this round's earlier admissions take (`new_pages`: they run after the whole pass is decided) + new_pages: u32 = 0, + admitted: u32 = 0, + /// the round's spills (plan.spills: every rank evicts them, in this order, before the admissions run) + spills: std.ArrayList(u32) = .empty, + one: std.ArrayList(u32) = .empty, + + pub fn deinit(r: *Round, gpa: std.mem.Allocator) void { + r.used.deinit(gpa); + r.spills.deinit(gpa); + r.one.deinit(gpa); + } +}; + +pub const Planner = struct { + gpa: std.mem.Allocator, + s: Settings, + fair: Fairness, + seqs: std.ArrayList(Seq) = .empty, // the requests in the slots, in admission order + rows_now: ?u64 = null, // the rows of the last prefill round (adaptive rows) + counts: Counts = .{}, + mem_since: std.AutoHashMapUnmanaged(usize, f64) = .empty, // a request the floor holds back: since when (seconds) + + pub fn init(gpa: std.mem.Allocator, s: Settings) Planner { + return .{ .gpa = gpa, .s = s, .fair = .{ .share = s.share } }; + } + + pub fn deinit(p: *Planner) void { + p.seqs.deinit(p.gpa); + p.mem_since.deinit(p.gpa); + } + + fn find(p: *Planner, key: usize) ?*Seq { + for (p.seqs.items) |*x| if (x.key == key) return x; + return null; + } + + pub fn seq(p: *Planner, key: usize) ?*Seq { + return p.find(key); + } + + /// The rows admission prices a prompt's transient at: the smallest adaptive step, else `rows`. + fn priceRows(p: *const Planner) u64 { + const a = p.s.adapt orelse return p.s.rows; + var buf: [16]u64 = undefined; + const st = a.steps(p.s.rows, &buf); + return st[st.len - 1]; + } + + /// An admission pass starts (the host then asks `admit` for each queued request in its order while slots are free). + pub fn beginAdmit(p: *Planner) Round { + var r: Round = .{}; + for (p.seqs.items) |x| { + if (x.long and !x.decoding) r.longs += 1; + if (x.price) |pr| if (!x.decoding) { + r.owed += pr.outstanding(x.done); + }; + } + return r; + } + + /// Start, hold (with those behind), skip or refuse one queued request; `now`: seconds, `mem` when a floor is set. + pub fn admit(p: *Planner, r: *Round, job: Job, store: ?Store, now: f64, mem: ?Meminfo) !Verdict { + const hit: ?Store.Hit = if (store) |st| try st.vtable.find(st.ctx, job.key) else null; + const cached: u64 = if (hit) |h| h.pos else 0; + const long = job.n - cached > p.s.long_prompt; + if (long and p.s.concurrent > 0 and r.longs >= p.s.concurrent) { + p.counts.long_waits += 1; + return .skip; + } + var price: ?Price = null; + if (p.s.floor) |*f| if (mem) |mi| { + const pr = prefillPrice(p.s.price, job.n, cached, p.priceRows()); + price = pr; + if (!f.check(mi, r.owed + pr.total())) { + p.counts.mem_waits += 1; + if (try p.neverFits(job, pr, r, now)) return .{ .refuse = .memory }; + return .wait; + } + _ = p.mem_since.remove(job.key); + }; + var pp: sess.Plan = .{ .need = 0, .want = 0 }; + if (store) |st| { + r.one.clearRetainingCapacity(); + const at = r.used.items.len; + if (hit) |h| try r.used.append(p.gpa, h.id); + defer r.used.shrinkRetainingCapacity(at); + pp = st.vtable.pool(st.ctx, job.key, hit, r.used.items, r.new_pages, &r.one) catch |e| switch (e) { + error.RequestTooLarge => return .{ .refuse = .too_large }, + else => return e, + }; + // running requests free pages as they end; one round's admissions spill once + if (pp.wait or (r.one.items.len > 0 and r.spills.items.len > 0)) { + p.counts.pool_waits += 1; + return .wait; + } + try r.spills.appendSlice(p.gpa, r.one.items); + r.new_pages = pp.want; + } + if (hit) |h| try r.used.append(p.gpa, h.id); + r.longs += @intFromBool(long); + if (price) |pr| r.owed += pr.total(); + r.admitted += 1; + try p.seqs.append(p.gpa, .{ .key = job.key, .n = job.n, .submitted = job.submitted, .done = cached, .cached = cached, .save_at = p.savePoint(job.n, cached), .long = long, .price = price }); + return .{ .admit = .{ .need = pp.need, .hit = hit } }; + } + + fn savePoint(p: *const Planner, n: u64, cached: u64) ?u64 { + if (!p.s.sessions) return null; + const s = snapshotPoint(n); + return if (s > cached and s >= p.s.session_min) s else null; + } + + /// Refused only when nothing is live and it waited `refuse_s` with its price still past usable less the hard floor. + fn neverFits(p: *Planner, job: Job, pr: Price, r: *const Round, now: f64) !bool { + const f = &p.s.floor.?; + _ = r; + if (p.seqs.items.len > 0) { + _ = p.mem_since.remove(job.key); + return false; + } + const g = try p.mem_since.getOrPut(p.gpa, job.key); + if (!g.found_existing) g.value_ptr.* = now; + if (now - g.value_ptr.* < f.refuse_s or f.fitsIdle(pr.total())) return false; + _ = p.mem_since.remove(job.key); + p.counts.mem_refused += 1; + return true; + } + + /// A prompt whose client left: the grid point to keep its rows at for a retry, or null (nothing new, or saved). + pub fn leaving(p: *const Planner, key: usize) ?u64 { + for (p.seqs.items) |x| if (x.key == key) { + if (x.decoding or x.damaged or x.save_at == null) return null; + if (x.done % grid != 0 or x.done < p.s.session_min or x.done <= x.cached) return null; + return x.done; + }; + return null; + } + + /// The admitted request's entry failed to restore: the plan stands, its pieces do not run, then it prefills from 0. + pub fn damaged(p: *Planner, key: usize) void { + if (p.find(key)) |x| x.damaged = true; + } + + /// This round's pieces (`out`, in order) and the requests whose prompts are in (`finals`); `mem` for adaptive rows. + pub fn plan(p: *Planner, mem: ?Meminfo, out: *std.ArrayList(Piece), finals: *std.ArrayList(usize)) !void { + out.clearRetainingCapacity(); + finals.clearRetainingCapacity(); + var decoding = false; + for (p.seqs.items) |x| decoding = decoding or x.decoding; + const allow = p.fair.allow(decoding); + var waiting: std.ArrayList(*Seq) = .empty; + defer waiting.deinit(p.gpa); + for (p.seqs.items) |*x| if (!x.decoding and x.done < x.n - 1) try waiting.append(p.gpa, x); + var budget: u64 = if (waiting.items.len > 0) try p.roundRows(waiting.items, mem) else p.s.rows; + std.mem.sort(*Seq, waiting.items, {}, struct { + fn less(_: void, a: *Seq, b: *Seq) bool { + const la = a.n - 1 - a.done; + const lb = b.n - 1 - b.done; + return la < lb or (la == lb and a.submitted < b.submitted); + } + }.less); + for (waiting.items) |x| { + const left = x.n - 1 - x.done; + if (budget == 0 or (left > p.s.short and !allow)) continue; + const target = if (x.save_at) |s| (if (x.done < s) s else x.n - 1) else x.n - 1; + const end = pieceEnd(x.done, target, budget, grid); + const save = x.save_at != null and end == x.save_at.?; + try out.append(p.gpa, .{ .key = x.key, .start = x.done, .end = end, .save = save, .run = !x.damaged }); + // a piece is at least one grid step and may overrun the budget: saturating to 0 ends the round + budget -|= end - x.done; + x.done = end; + if (save) x.save_at = null; + } + for (p.seqs.items) |*x| if (!x.decoding and x.done == x.n - 1) { + if (!x.damaged) try finals.append(p.gpa, x.key); + x.decoding = true; + }; + } + + /// Full rows while usable memory covers the round's priced transient with `adapt.gib` spare, else a smaller step. + fn roundRows(p: *Planner, waiting: []const *Seq, mem: ?Meminfo) !u64 { + const a = p.s.adapt orelse return p.s.rows; + const f = p.s.floor orelse return p.s.rows; + const mi = mem orelse return p.s.rows; + var left: std.ArrayList([2]u64) = .empty; + defer left.deinit(p.gpa); + for (waiting) |x| try left.append(p.gpa, .{ x.n, x.done }); + const rows = a.choose(p.s.price, p.s.rows, f.usable(mi), left.items, p.rows_now); + if (p.rows_now) |cur| if (rows != cur) { + if (rows < cur) p.counts.rows_down += 1 else p.counts.rows_up += 1; + std.log.scoped(.dsv41).info("prefill rows {d} -> {d} (usable {d:.2} GiB, the round's transient at {d} rows {d:.2} GiB, keep {d})", .{ cur, rows, @as(f64, @floatFromInt(f.usable(mi))) / GiB, rows, @as(f64, @floatFromInt(roundNeed(p.s.price, rows, left.items))) / GiB, a.gib }); + }; + p.counts.rowsAt(rows); + p.rows_now = rows; + return rows; + } + + /// A request left the slots (finished, cancelled, failed). + pub fn ended(p: *Planner, key: usize) void { + _ = p.mem_since.remove(key); + for (p.seqs.items, 0..) |x, i| if (x.key == key) { + _ = p.seqs.orderedRemove(i); + return; + }; + } + + /// A queued request left the queue without starting (cancelled, refused). + pub fn dropped(p: *Planner, key: usize) void { + _ = p.mem_since.remove(key); + } + + /// After the round (damaged reset, then fair share): the pieces' and decode's seconds; `ended` goes first. + pub fn after(p: *Planner, piece_s: f64, decode_s: f64) void { + for (p.seqs.items) |*x| if (x.damaged) { + x.damaged = false; + x.done = 0; + x.decoding = false; + x.long = x.n > p.s.long_prompt; + x.save_at = if (p.s.sessions) snapshotPoint(x.n) else null; + }; + var prefilling = false; + for (p.seqs.items) |x| prefilling = prefilling or !x.decoding; + p.fair.after(piece_s, decode_s, prefilling); + } +}; + +test "piece_end, snapshot_point and the fair share as plan.py / sessions.py / batchplan.py" { + try std.testing.expectEqual(@as(u64, 300), pieceEnd(0, 300, 2048, 16)); + try std.testing.expectEqual(@as(u64, 2048), pieceEnd(0, 9000, 2048, 16)); + try std.testing.expectEqual(@as(u64, 2032), pieceEnd(5, 9000, 2040, 16)); + try std.testing.expectEqual(@as(u64, 16), pieceEnd(3, 9000, 4, 16)); // at least one grid step + try std.testing.expectEqual(@as(u64, 10), pieceEnd(3, 10, 4, 16)); + try std.testing.expectEqual(@as(u64, 9488), snapshotPoint(9500)); + try std.testing.expectEqual(@as(u64, 0), snapshotPoint(1)); + var f: Fairness = .{}; + try std.testing.expect(f.allow(true)); + f.after(0.25, 0.0625, true); + try std.testing.expect(!f.allow(true) and f.allow(false)); + f.after(0, 0.0625, true); + f.after(0, 0.0625, true); + f.after(0, 0.0625, true); + try std.testing.expect(f.allow(true)); + var buf: [16]u64 = undefined; + try std.testing.expectEqualSlices(u64, &.{ 2048, 1024, 512 }, (Adapt{}).steps(2048, &buf)); +} + +test "a round whose last piece rounds up past the budget left ends the round (batch.py: budget <= 0)" { + // a piece rounding up past the budget left ends the round (an unsigned budget once overflowed here) + var p = Planner.init(std.testing.allocator, .{}); + defer p.deinit(); + try p.seqs.append(p.gpa, .{ .key = 1, .n = 2045, .submitted = 1, .done = 0, .save_at = null }); + try p.seqs.append(p.gpa, .{ .key = 2, .n = 9000, .submitted = 2, .done = 3, .save_at = null }); + try p.seqs.append(p.gpa, .{ .key = 3, .n = 9500, .submitted = 3, .done = 0, .save_at = null }); + var out: std.ArrayList(Piece) = .empty; + defer out.deinit(p.gpa); + var finals: std.ArrayList(usize) = .empty; + defer finals.deinit(p.gpa); + try p.plan(null, &out, &finals); + try std.testing.expectEqual(@as(usize, 2), out.items.len); + try std.testing.expectEqual(Piece{ .key = 1, .start = 0, .end = 2044, .save = false }, out.items[0]); + try std.testing.expectEqual(Piece{ .key = 2, .start = 3, .end = 16, .save = false }, out.items[1]); + try std.testing.expectEqualSlices(usize, &.{1}, finals.items); +} + +test "a prompt that leaves mid-prefill is kept at its last grid point, if that is new and before its replay point" { + var p = Planner.init(std.testing.allocator, .{}); + defer p.deinit(); + try p.seqs.append(p.gpa, .{ .key = 1, .n = 9000, .submitted = 0, .done = 4096, .cached = 0, .save_at = snapshotPoint(9000) }); + try p.seqs.append(p.gpa, .{ .key = 2, .n = 9000, .submitted = 0, .done = 4096, .cached = 4096, .save_at = snapshotPoint(9000) }); + try p.seqs.append(p.gpa, .{ .key = 3, .n = 9000, .submitted = 0, .done = 4100, .cached = 0, .save_at = snapshotPoint(9000) }); + try p.seqs.append(p.gpa, .{ .key = 4, .n = 9000, .submitted = 0, .done = 8992, .cached = 0, .save_at = null }); + try p.seqs.append(p.gpa, .{ .key = 5, .n = 9000, .submitted = 0, .done = 48, .cached = 0, .save_at = snapshotPoint(9000) }); + try p.seqs.append(p.gpa, .{ .key = 6, .n = 9000, .submitted = 0, .done = 4096, .cached = 0, .save_at = snapshotPoint(9000), .decoding = true }); + try std.testing.expectEqual(@as(?u64, 4096), p.leaving(1)); + try std.testing.expectEqual(@as(?u64, null), p.leaving(2)); // nothing past what it resumed from + try std.testing.expectEqual(@as(?u64, null), p.leaving(3)); // off the grid + try std.testing.expectEqual(@as(?u64, null), p.leaving(4)); // its replay point is saved already + try std.testing.expectEqual(@as(?u64, null), p.leaving(5)); // under session_min + try std.testing.expectEqual(@as(?u64, null), p.leaving(6)); // its prompt is in + try std.testing.expectEqual(@as(?u64, null), p.leaving(99)); +} + +test { + _ = @import("sched_test.zig"); + _ = @import("sched_crash_test.zig"); +} diff --git a/zig/src/families/deepseek_v41/kv/sched_crash_test.zig b/zig/src/families/deepseek_v41/kv/sched_crash_test.zig new file mode 100644 index 0000000000..16aa494c12 --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/sched_crash_test.zig @@ -0,0 +1,54 @@ +//! Regression: budget under one grid step, then a piece_end rounding up past it, ends the round (no overflow). +const std = @import("std"); +const sched = @import("sched.zig"); + +fn seqOf(key: usize, n: u64, done: u64, t: f64) sched.Seq { + return .{ .key = key, .n = n, .submitted = t, .done = done, .save_at = null }; +} + +fn planOnce(p: *sched.Planner) !struct { pieces: []sched.Piece, finals: []usize } { + var pieces: std.ArrayList(sched.Piece) = .empty; + var finals: std.ArrayList(usize) = .empty; + try p.plan(null, &pieces, &finals); + return .{ .pieces = try pieces.toOwnedSlice(p.gpa), .finals = try finals.toOwnedSlice(p.gpa) }; +} + +test "a prompt mid-prefill and two arrivals leave the round 5 rows: the next piece is one grid step, the rest skip" { + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + for ([_]u64{ 4096, 2048, 1024 }) |rows| { + var p = sched.Planner.init(arena.allocator(), .{ .rows = rows, .short = 512, .sessions = false }); + // A: mid-prefill, 2040 left; B: a short arrival (299 left); C: a long arrival + try p.seqs.append(p.gpa, seqOf(1, 4096 + 2041, 4096, 0)); + try p.seqs.append(p.gpa, seqOf(2, 300, 0, 2)); + try p.seqs.append(p.gpa, seqOf(3, 150_000, 0, 2.8)); + const r = try planOnce(&p); // B whole, A to its last grid point within the budget, then 5 (or fewer) rows left + var taken: u64 = 0; + for (r.pieces) |pc| taken += pc.end - pc.start; + // the overshoot is at most one grid step less one row + try std.testing.expect(taken <= rows + sched.grid - 1); + try std.testing.expectEqual(@as(u64, 2), r.pieces[0].key); + } +} + +test "the smallest repro: 2040 of 2048 rows to one prompt, a second prompt's minimum grid step (16) exceeds the 8 left" { + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + var p = sched.Planner.init(arena.allocator(), .{ .rows = 2048, .sessions = false }); + try p.seqs.append(p.gpa, seqOf(1, 2041, 0, 0)); + try p.seqs.append(p.gpa, seqOf(2, 9000, 0, 1)); + const r = try planOnce(&p); + try std.testing.expectEqual(@as(usize, 2), r.pieces.len); + try std.testing.expectEqual(@as(u64, 16), r.pieces[1].end); // budget 8 -> -8, then skips +} + +test "edge cases: n 1 and cached n - 1 are finals at once; nothing is planned for them" { + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + var p = sched.Planner.init(arena.allocator(), .{ .rows = 2048, .sessions = false }); + try p.seqs.append(p.gpa, seqOf(1, 1, 0, 0)); + try p.seqs.append(p.gpa, seqOf(2, 5000, 4999, 1)); // the longest strict-prefix hit + const r = try planOnce(&p); + try std.testing.expectEqual(@as(usize, 0), r.pieces.len); + try std.testing.expectEqualSlices(usize, &.{ 1, 2 }, r.finals); +} diff --git a/zig/src/families/deepseek_v41/kv/sched_golden.json b/zig/src/families/deepseek_v41/kv/sched_golden.json new file mode 100644 index 0000000000..656663ec3a --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/sched_golden.json @@ -0,0 +1,4998 @@ +{ + "python": "8474f31", + "settings": { + "rows": 2048, + "short": 512, + "share": 0.5, + "long_prompt": 6000, + "concurrent": 1, + "session_min": 64, + "adapt_gib": 4.5, + "adapt_up_gib": 0.5, + "adapt_min": 512, + "floor_gib": 5.0, + "hard_gib": 4.0, + "refuse_s": 30.0, + "cache_keep_gib": 2.0, + "index_budget_mib": 256 + }, + "tag": 272, + "page": 256, + "pages": 72, + "slots": 4, + "capacity": 16384, + "slack": 64, + "ram_bytes": 3670016, + "disk_min": 1024, + "bounded_bytes": 1048576, + "window_s": 0.0625, + "jobs": [ + { + "id": 1, + "chat": 1, + "n": 1500, + "max_tokens": 24, + "background": false, + "submitted": 1000.0 + }, + { + "id": 2, + "chat": 2, + "n": 300, + "max_tokens": 40, + "background": false, + "submitted": 1000.0 + }, + { + "id": 3, + "chat": 3, + "n": 9000, + "max_tokens": 16, + "background": false, + "submitted": 1001.0 + }, + { + "id": 4, + "chat": 4, + "n": 7000, + "max_tokens": 12, + "background": false, + "submitted": 1002.0 + }, + { + "id": 5, + "chat": 5, + "n": 200, + "max_tokens": 20, + "background": true, + "submitted": 1002.0 + }, + { + "id": 6, + "chat": 1, + "n": 2100, + "max_tokens": 30, + "background": false, + "submitted": 1004.0 + }, + { + "id": 7, + "chat": 6, + "n": 5000, + "max_tokens": 3000, + "background": false, + "submitted": 1006.0 + }, + { + "id": 8, + "chat": 2, + "n": 1200, + "max_tokens": 10, + "background": false, + "submitted": 1009.0 + }, + { + "id": 9, + "chat": 3, + "n": 9500, + "max_tokens": 10, + "background": false, + "submitted": 1014.0 + }, + { + "id": 10, + "chat": 7, + "n": 3000, + "max_tokens": 10, + "background": false, + "submitted": 1016.0 + }, + { + "id": 11, + "chat": 8, + "n": 600, + "max_tokens": 10, + "background": false, + "submitted": 1016.0 + } + ], + "rounds": [ + { + "round": 0, + "clock": 1000.0, + "free_gib": 24.0, + "arrive": [ + 1, + 2 + ], + "cancel": [], + "damage": false, + "admits": [ + { + "id": 1, + "tier": "none", + "cached": 0, + "need": 7, + "damaged": false + }, + { + "id": 2, + "tier": "none", + "cached": 0, + "need": 2, + "damaged": false + } + ], + "pieces": [ + [ + 2, + 0, + 288 + ], + [ + 1, + 0, + 1488 + ] + ], + "ran": [ + [ + 2, + 0, + 288 + ], + [ + 1, + 0, + 1488 + ] + ], + "skipped": [], + "saves": [ + 2, + 1 + ], + "finals": [], + "windows": [], + "ended": [], + "rows": 2048, + "piece_s": 0.216796875, + "window_s": 0.0, + "counts": { + "rows_2048": 1 + }, + "queue": [], + "ram": [ + 288, + 1488 + ], + "disk": [], + "available": 63, + "debt": 0.216796875, + "fair_rounds": 0 + }, + { + "round": 1, + "clock": 1001.0, + "free_gib": 24.0, + "arrive": [ + 3 + ], + "cancel": [], + "damage": false, + "admits": [ + { + "id": 3, + "tier": "none", + "cached": 0, + "need": 36, + "damaged": false + } + ], + "pieces": [ + [ + 1, + 1488, + 1499 + ], + [ + 2, + 288, + 299 + ], + [ + 3, + 0, + 2016 + ] + ], + "ran": [ + [ + 1, + 1488, + 1499 + ], + [ + 2, + 288, + 299 + ], + [ + 3, + 0, + 2016 + ] + ], + "skipped": [], + "saves": [], + "finals": [ + 1, + 2 + ], + "windows": [ + 1, + 2 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.248779296875, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [], + "ram": [ + 288, + 1488 + ], + "disk": [], + "available": 27, + "debt": 0.403076171875, + "fair_rounds": 1 + }, + { + "round": 2, + "clock": 1002.0, + "free_gib": 24.0, + "arrive": [ + 4, + 5 + ], + "cancel": [], + "damage": false, + "admits": [ + { + "id": 5, + "tier": "none", + "cached": 0, + "need": 2, + "damaged": false + } + ], + "pieces": [ + [ + 5, + 0, + 192 + ] + ], + "ran": [ + [ + 5, + 0, + 192 + ] + ], + "skipped": [], + "saves": [ + 5 + ], + "finals": [], + "windows": [ + 1, + 2 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0234375, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "long_waits": 1 + }, + "queue": [ + 4 + ], + "ram": [ + 192, + 288, + 1488 + ], + "disk": [], + "available": 25, + "debt": 0.364013671875, + "fair_rounds": 1 + }, + { + "round": 3, + "clock": 1003.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 5, + 192, + 199 + ] + ], + "ran": [ + [ + 5, + 192, + 199 + ] + ], + "skipped": [], + "saves": [], + "finals": [ + 5 + ], + "windows": [ + 1, + 2, + 5 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0008544921875, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [ + 4 + ], + "ram": [ + 192, + 288, + 1488 + ], + "disk": [], + "available": 25, + "debt": 0.3023681640625, + "fair_rounds": 1 + }, + { + "round": 4, + "clock": 1004.0, + "free_gib": 24.0, + "arrive": [ + 6 + ], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 5 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [ + 4, + 6 + ], + "ram": [ + 192, + 288, + 1488 + ], + "disk": [], + "available": 25, + "debt": 0.2398681640625, + "fair_rounds": 2 + }, + { + "round": 5, + "clock": 1005.0, + "free_gib": 4.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 5 + ], + "ended": [], + "rows": 512, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_down": 1, + "rows_512": 1 + }, + "queue": [ + 4, + 6 + ], + "ram": [ + 192, + 288, + 1488 + ], + "disk": [], + "available": 25, + "debt": 0.1773681640625, + "fair_rounds": 3 + }, + { + "round": 6, + "clock": 1006.0, + "free_gib": 4.0, + "arrive": [ + 7 + ], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 5 + ], + "ended": [], + "rows": 512, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_512": 1 + }, + "queue": [ + 4, + 6, + 7 + ], + "ram": [ + 192, + 288, + 1488 + ], + "disk": [], + "available": 25, + "debt": 0.1148681640625, + "fair_rounds": 4 + }, + { + "round": 7, + "clock": 1007.0, + "free_gib": 3.2, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 5 + ], + "ended": [], + "rows": 512, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_512": 1 + }, + "queue": [ + 4, + 6, + 7 + ], + "ram": [ + 192, + 288, + 1488 + ], + "disk": [], + "available": 25, + "debt": 0.0523681640625, + "fair_rounds": 5 + }, + { + "round": 8, + "clock": 1008.0, + "free_gib": 2.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 5 + ], + "ended": [], + "rows": 512, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_512": 1 + }, + "queue": [ + 4, + 6, + 7 + ], + "ram": [ + 192, + 288, + 1488 + ], + "disk": [], + "available": 25, + "debt": -0.0101318359375, + "fair_rounds": 6 + }, + { + "round": 9, + "clock": 1009.0, + "free_gib": 2.0, + "arrive": [ + 8 + ], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 3, + 2016, + 2528 + ] + ], + "ran": [ + [ + 3, + 2016, + 2528 + ] + ], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 5 + ], + "ended": [], + "rows": 512, + "piece_s": 0.0625, + "window_s": 0.0625, + "counts": { + "rows_512": 1 + }, + "queue": [ + 4, + 6, + 7, + 8 + ], + "ram": [ + 192, + 288, + 1488 + ], + "disk": [], + "available": 25, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 10, + "clock": 1010.0, + "free_gib": 2.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 3, + 2528, + 3040 + ] + ], + "ran": [ + [ + 3, + 2528, + 3040 + ] + ], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 5 + ], + "ended": [], + "rows": 512, + "piece_s": 0.0625, + "window_s": 0.0625, + "counts": { + "rows_512": 1 + }, + "queue": [ + 4, + 6, + 7, + 8 + ], + "ram": [ + 192, + 288, + 1488 + ], + "disk": [], + "available": 25, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 11, + "clock": 1011.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [ + 4 + ], + "damage": false, + "admits": [], + "pieces": [ + [ + 3, + 3040, + 5088 + ] + ], + "ran": [ + [ + 3, + 3040, + 5088 + ] + ], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 5 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.25, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "rows_up": 1 + }, + "queue": [ + 6, + 7, + 8 + ], + "ram": [ + 192, + 288, + 1488 + ], + "disk": [], + "available": 25, + "debt": 0.1875, + "fair_rounds": 1 + }, + { + "round": 12, + "clock": 1012.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 5 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [ + 6, + 7, + 8 + ], + "ram": [ + 192, + 288, + 1488 + ], + "disk": [], + "available": 25, + "debt": 0.125, + "fair_rounds": 2 + }, + { + "round": 13, + "clock": 1013.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 5 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [ + 6, + 7, + 8 + ], + "ram": [ + 192, + 288, + 1488 + ], + "disk": [], + "available": 25, + "debt": 0.0625, + "fair_rounds": 3 + }, + { + "round": 14, + "clock": 1014.0, + "free_gib": 24.0, + "arrive": [ + 9 + ], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 5 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [ + 6, + 7, + 8, + 9 + ], + "ram": [ + 192, + 288, + 1488 + ], + "disk": [], + "available": 25, + "debt": 0.0, + "fair_rounds": 4 + }, + { + "round": 15, + "clock": 1015.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 3, + 5088, + 7136 + ] + ], + "ran": [ + [ + 3, + 5088, + 7136 + ] + ], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 5 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.25, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [ + 6, + 7, + 8, + 9 + ], + "ram": [ + 192, + 288, + 1488 + ], + "disk": [], + "available": 25, + "debt": 0.1875, + "fair_rounds": 1 + }, + { + "round": 16, + "clock": 1016.0, + "free_gib": 24.0, + "arrive": [ + 10, + 11 + ], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 5 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [ + 6, + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 192, + 288, + 1488 + ], + "disk": [], + "available": 25, + "debt": 0.125, + "fair_rounds": 2 + }, + { + "round": 17, + "clock": 1017.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 5 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [ + 6, + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 192, + 288, + 1488 + ], + "disk": [], + "available": 25, + "debt": 0.0625, + "fair_rounds": 3 + }, + { + "round": 18, + "clock": 1018.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 5 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [ + 6, + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 192, + 288, + 1488 + ], + "disk": [], + "available": 25, + "debt": 0.0, + "fair_rounds": 4 + }, + { + "round": 19, + "clock": 1019.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 3, + 7136, + 8992 + ] + ], + "ran": [ + [ + 3, + 7136, + 8992 + ] + ], + "skipped": [], + "saves": [ + 3 + ], + "finals": [], + "windows": [ + 1, + 2, + 5 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.2265625, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [ + 6, + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 192, + 1488, + 8992 + ], + "disk": [], + "available": 25, + "debt": 0.1640625, + "fair_rounds": 1 + }, + { + "round": 20, + "clock": 1020.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 3, + 8992, + 8999 + ] + ], + "ran": [ + [ + 3, + 8992, + 8999 + ] + ], + "skipped": [], + "saves": [], + "finals": [ + 3 + ], + "windows": [ + 1, + 2, + 3, + 5 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0008544921875, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [ + 6, + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 192, + 1488, + 8992 + ], + "disk": [], + "available": 25, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 21, + "clock": 1021.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 3, + 5 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": {}, + "queue": [ + 6, + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 192, + 1488, + 8992 + ], + "disk": [], + "available": 25, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 22, + "clock": 1022.0, + "free_gib": 2.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 3, + 5 + ], + "ended": [ + 5 + ], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": {}, + "queue": [ + 6, + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 192, + 1488, + 8992 + ], + "disk": [], + "available": 25, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 23, + "clock": 1023.0, + "free_gib": 2.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 3 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "mem_waits": 1 + }, + "queue": [ + 6, + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 192, + 1488, + 8992 + ], + "disk": [], + "available": 26, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 24, + "clock": 1024.0, + "free_gib": 2.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 1, + 2, + 3 + ], + "ended": [ + 1 + ], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "mem_waits": 1 + }, + "queue": [ + 6, + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 192, + 1488, + 8992 + ], + "disk": [], + "available": 26, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 25, + "clock": 1025.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [ + { + "id": 6, + "tier": "ram", + "cached": 1488, + "need": 9, + "damaged": false + } + ], + "pieces": [ + [ + 6, + 1488, + 2096 + ] + ], + "ran": [ + [ + 6, + 1488, + 2096 + ] + ], + "skipped": [], + "saves": [ + 6 + ], + "finals": [], + "windows": [ + 2, + 3 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.07421875, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 1488, + 2096, + 8992 + ], + "disk": [], + "available": 24, + "debt": 0.01171875, + "fair_rounds": 1 + }, + { + "round": 26, + "clock": 1026.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 6, + 2096, + 2099 + ] + ], + "ran": [ + [ + 6, + 2096, + 2099 + ] + ], + "skipped": [], + "saves": [], + "finals": [ + 6 + ], + "windows": [ + 2, + 3, + 6 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0003662109375, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 1488, + 2096, + 8992 + ], + "disk": [], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 27, + "clock": 1027.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 2, + 3, + 6 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 1488, + 2096, + 8992 + ], + "disk": [], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 28, + "clock": 1028.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 2, + 3, + 6 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 1488, + 2096, + 8992 + ], + "disk": [], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 29, + "clock": 1029.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 2, + 3, + 6 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 1488, + 2096, + 8992 + ], + "disk": [], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 30, + "clock": 1030.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 2, + 3, + 6 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 1488, + 2096, + 8992 + ], + "disk": [], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 31, + "clock": 1031.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 2, + 3, + 6 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 1488, + 2096, + 8992 + ], + "disk": [], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 32, + "clock": 1032.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 2, + 3, + 6 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 1488, + 2096, + 8992 + ], + "disk": [], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 33, + "clock": 1033.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 2, + 3, + 6 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 1488, + 2096, + 8992 + ], + "disk": [], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 34, + "clock": 1034.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 2, + 3, + 6 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 1488, + 2096, + 8992 + ], + "disk": [], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 35, + "clock": 1035.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 2, + 3, + 6 + ], + "ended": [ + 3 + ], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 1488, + 2096, + 8992 + ], + "disk": [], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 36, + "clock": 1036.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 2, + 6 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 7, + 8, + 9, + 10, + 11 + ], + "ram": [ + 1488, + 2096, + 8992 + ], + "disk": [], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 37, + "clock": 1037.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [ + { + "id": 7, + "tier": "none", + "cached": 0, + "need": 32, + "damaged": false + } + ], + "pieces": [ + [ + 7, + 0, + 2048 + ] + ], + "ran": [ + [ + 7, + 0, + 2048 + ] + ], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 2, + 6 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.25, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 8, + 9, + 10, + 11 + ], + "ram": [ + 1488, + 2096 + ], + "disk": [ + 8992 + ], + "available": 28, + "debt": 0.1875, + "fair_rounds": 1 + }, + { + "round": 38, + "clock": 1038.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [ + { + "id": 8, + "tier": "none", + "cached": 0, + "need": 5, + "damaged": false + } + ], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 2, + 6 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1488, + 2096 + ], + "disk": [ + 8992 + ], + "available": 23, + "debt": 0.125, + "fair_rounds": 2 + }, + { + "round": 39, + "clock": 1039.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 2, + 6 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1488, + 2096 + ], + "disk": [ + 8992 + ], + "available": 23, + "debt": 0.0625, + "fair_rounds": 3 + }, + { + "round": 40, + "clock": 1040.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 2, + 6 + ], + "ended": [ + 2 + ], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1488, + 2096 + ], + "disk": [ + 8992 + ], + "available": 23, + "debt": 0.0, + "fair_rounds": 4 + }, + { + "round": 41, + "clock": 1041.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 8, + 0, + 1184 + ], + [ + 7, + 2048, + 2912 + ] + ], + "ran": [ + [ + 8, + 0, + 1184 + ], + [ + 7, + 2048, + 2912 + ] + ], + "skipped": [], + "saves": [ + 8 + ], + "finals": [], + "windows": [ + 6 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.25, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1184, + 1488, + 2096 + ], + "disk": [ + 8992 + ], + "available": 25, + "debt": 0.1875, + "fair_rounds": 1 + }, + { + "round": 42, + "clock": 1042.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 8, + 1184, + 1199 + ] + ], + "ran": [ + [ + 8, + 1184, + 1199 + ] + ], + "skipped": [], + "saves": [], + "finals": [ + 8 + ], + "windows": [ + 6, + 8 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0018310546875, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1184, + 1488, + 2096 + ], + "disk": [ + 8992 + ], + "available": 25, + "debt": 0.1268310546875, + "fair_rounds": 1 + }, + { + "round": 43, + "clock": 1043.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 6, + 8 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1184, + 1488, + 2096 + ], + "disk": [ + 8992 + ], + "available": 25, + "debt": 0.0643310546875, + "fair_rounds": 2 + }, + { + "round": 44, + "clock": 1044.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 6, + 8 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1184, + 1488, + 2096 + ], + "disk": [ + 8992 + ], + "available": 25, + "debt": 0.0018310546875, + "fair_rounds": 3 + }, + { + "round": 45, + "clock": 1045.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": true, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 6, + 8 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1184, + 1488, + 2096 + ], + "disk": [ + 8992 + ], + "available": 25, + "debt": -0.0606689453125, + "fair_rounds": 4 + }, + { + "round": 46, + "clock": 1046.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 7, + 2912, + 4960 + ] + ], + "ran": [ + [ + 7, + 2912, + 4960 + ] + ], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 6, + 8 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.25, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1184, + 1488, + 2096 + ], + "disk": [ + 8992 + ], + "available": 25, + "debt": 0.1875, + "fair_rounds": 1 + }, + { + "round": 47, + "clock": 1047.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 7, + 4960, + 4992 + ] + ], + "ran": [ + [ + 7, + 4960, + 4992 + ] + ], + "skipped": [], + "saves": [ + 7 + ], + "finals": [], + "windows": [ + 6, + 8 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.00390625, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1184, + 2096, + 4992 + ], + "disk": [ + 1488, + 8992 + ], + "available": 26, + "debt": 0.12890625, + "fair_rounds": 1 + }, + { + "round": 48, + "clock": 1048.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 7, + 4992, + 4999 + ] + ], + "ran": [ + [ + 7, + 4992, + 4999 + ] + ], + "skipped": [], + "saves": [], + "finals": [ + 7 + ], + "windows": [ + 6, + 7, + 8 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0008544921875, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1184, + 2096, + 4992 + ], + "disk": [ + 1488, + 8992 + ], + "available": 26, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 49, + "clock": 1049.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 6, + 7, + 8 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1184, + 2096, + 4992 + ], + "disk": [ + 1488, + 8992 + ], + "available": 26, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 50, + "clock": 1050.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 6, + 7, + 8 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1184, + 2096, + 4992 + ], + "disk": [ + 1488, + 8992 + ], + "available": 26, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 51, + "clock": 1051.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 6, + 7, + 8 + ], + "ended": [ + 8 + ], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1184, + 2096, + 4992 + ], + "disk": [ + 1488, + 8992 + ], + "available": 26, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 52, + "clock": 1052.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 6, + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1184, + 2096, + 4992 + ], + "disk": [ + 1488, + 8992 + ], + "available": 26, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 53, + "clock": 1053.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 6, + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1184, + 2096, + 4992 + ], + "disk": [ + 1488, + 8992 + ], + "available": 26, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 54, + "clock": 1054.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 6, + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1184, + 2096, + 4992 + ], + "disk": [ + 1488, + 8992 + ], + "available": 26, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 55, + "clock": 1055.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 6, + 7 + ], + "ended": [ + 6 + ], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1184, + 2096, + 4992 + ], + "disk": [ + 1488, + 8992 + ], + "available": 26, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 56, + "clock": 1056.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 9, + 10, + 11 + ], + "ram": [ + 1184, + 2096, + 4992 + ], + "disk": [ + 1488, + 8992 + ], + "available": 26, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 57, + "clock": 1057.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [ + { + "id": 9, + "tier": "disk", + "cached": 8992, + "need": 38, + "damaged": true + } + ], + "pieces": [ + [ + 9, + 8992, + 9488 + ] + ], + "ran": [], + "skipped": [ + 9 + ], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": -0.0625, + "fair_rounds": 2 + }, + { + "round": 58, + "clock": 1058.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 9, + 0, + 2048 + ] + ], + "ran": [ + [ + 9, + 0, + 2048 + ] + ], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.25, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.1875, + "fair_rounds": 1 + }, + { + "round": 59, + "clock": 1059.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.125, + "fair_rounds": 2 + }, + { + "round": 60, + "clock": 1060.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0625, + "fair_rounds": 3 + }, + { + "round": 61, + "clock": 1061.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0, + "fair_rounds": 4 + }, + { + "round": 62, + "clock": 1062.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 9, + 2048, + 4096 + ] + ], + "ran": [ + [ + 9, + 2048, + 4096 + ] + ], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.25, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.1875, + "fair_rounds": 1 + }, + { + "round": 63, + "clock": 1063.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.125, + "fair_rounds": 2 + }, + { + "round": 64, + "clock": 1064.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0625, + "fair_rounds": 3 + }, + { + "round": 65, + "clock": 1065.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0, + "fair_rounds": 4 + }, + { + "round": 66, + "clock": 1066.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 9, + 4096, + 6144 + ] + ], + "ran": [ + [ + 9, + 4096, + 6144 + ] + ], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.25, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.1875, + "fair_rounds": 1 + }, + { + "round": 67, + "clock": 1067.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.125, + "fair_rounds": 2 + }, + { + "round": 68, + "clock": 1068.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0625, + "fair_rounds": 3 + }, + { + "round": 69, + "clock": 1069.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0, + "fair_rounds": 4 + }, + { + "round": 70, + "clock": 1070.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 9, + 6144, + 8192 + ] + ], + "ran": [ + [ + 9, + 6144, + 8192 + ] + ], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.25, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.1875, + "fair_rounds": 1 + }, + { + "round": 71, + "clock": 1071.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.125, + "fair_rounds": 2 + }, + { + "round": 72, + "clock": 1072.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0625, + "fair_rounds": 3 + }, + { + "round": 73, + "clock": 1073.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0, + "fair_rounds": 4 + }, + { + "round": 74, + "clock": 1074.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 9, + 8192, + 9488 + ] + ], + "ran": [ + [ + 9, + 8192, + 9488 + ] + ], + "skipped": [], + "saves": [ + 9 + ], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.158203125, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992, + 9488 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.095703125, + "fair_rounds": 1 + }, + { + "round": 75, + "clock": 1075.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 9, + 9488, + 9499 + ] + ], + "ran": [ + [ + 9, + 9488, + 9499 + ] + ], + "skipped": [], + "saves": [], + "finals": [ + 9 + ], + "windows": [ + 7, + 9 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0013427734375, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992, + 9488 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 76, + "clock": 1076.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7, + 9 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992, + 9488 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 77, + "clock": 1077.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7, + 9 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992, + 9488 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 78, + "clock": 1078.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7, + 9 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992, + 9488 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 79, + "clock": 1079.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7, + 9 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992, + 9488 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 80, + "clock": 1080.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7, + 9 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992, + 9488 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 81, + "clock": 1081.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7, + 9 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992, + 9488 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 82, + "clock": 1082.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7, + 9 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992, + 9488 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 83, + "clock": 1083.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7, + 9 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992, + 9488 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 84, + "clock": 1084.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7, + 9 + ], + "ended": [ + 9 + ], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992, + 9488 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 85, + "clock": 1085.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "pool_waits": 1 + }, + "queue": [ + 10, + 11 + ], + "ram": [ + 4992, + 9488 + ], + "disk": [ + 1184, + 1488, + 2096 + ], + "available": 2, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 86, + "clock": 1086.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [ + { + "id": 10, + "tier": "none", + "cached": 0, + "need": 13, + "damaged": false + } + ], + "pieces": [ + [ + 10, + 0, + 2048 + ] + ], + "ran": [ + [ + 10, + 0, + 2048 + ] + ], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.25, + "window_s": 0.0625, + "counts": { + "rows_2048": 1, + "pool_waits": 1 + }, + "queue": [ + 11 + ], + "ram": [], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 27, + "debt": 0.1875, + "fair_rounds": 1 + }, + { + "round": 87, + "clock": 1087.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [ + { + "id": 11, + "tier": "none", + "cached": 0, + "need": 3, + "damaged": false + } + ], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [], + "ram": [], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 24, + "debt": 0.125, + "fair_rounds": 2 + }, + { + "round": 88, + "clock": 1088.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [], + "ram": [], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 24, + "debt": 0.0625, + "fair_rounds": 3 + }, + { + "round": 89, + "clock": 1089.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [], + "ram": [], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 24, + "debt": 0.0, + "fair_rounds": 4 + }, + { + "round": 90, + "clock": 1090.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 11, + 0, + 592 + ], + [ + 10, + 2048, + 2992 + ] + ], + "ran": [ + [ + 11, + 0, + 592 + ], + [ + 10, + 2048, + 2992 + ] + ], + "skipped": [], + "saves": [ + 11, + 10 + ], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.1875, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [], + "ram": [ + 592, + 2992 + ], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 24, + "debt": 0.125, + "fair_rounds": 1 + }, + { + "round": 91, + "clock": 1091.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [ + [ + 10, + 2992, + 2999 + ], + [ + 11, + 592, + 599 + ] + ], + "ran": [ + [ + 10, + 2992, + 2999 + ], + [ + 11, + 592, + 599 + ] + ], + "skipped": [], + "saves": [], + "finals": [ + 10, + 11 + ], + "windows": [ + 7, + 10, + 11 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.001708984375, + "window_s": 0.0625, + "counts": { + "rows_2048": 1 + }, + "queue": [], + "ram": [ + 592, + 2992 + ], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 92, + "clock": 1092.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7, + 10, + 11 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": {}, + "queue": [], + "ram": [ + 592, + 2992 + ], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 93, + "clock": 1093.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7, + 10, + 11 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": {}, + "queue": [], + "ram": [ + 592, + 2992 + ], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 94, + "clock": 1094.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7, + 10, + 11 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": {}, + "queue": [], + "ram": [ + 592, + 2992 + ], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 95, + "clock": 1095.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7, + 10, + 11 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": {}, + "queue": [], + "ram": [ + 592, + 2992 + ], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 96, + "clock": 1096.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7, + 10, + 11 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": {}, + "queue": [], + "ram": [ + 592, + 2992 + ], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 97, + "clock": 1097.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7, + 10, + 11 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": {}, + "queue": [], + "ram": [ + 592, + 2992 + ], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 98, + "clock": 1098.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7, + 10, + 11 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": {}, + "queue": [], + "ram": [ + 592, + 2992 + ], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 99, + "clock": 1099.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7, + 10, + 11 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": {}, + "queue": [], + "ram": [ + 592, + 2992 + ], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 100, + "clock": 1100.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7, + 10, + 11 + ], + "ended": [ + 10, + 11 + ], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": {}, + "queue": [], + "ram": [ + 592, + 2992 + ], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 24, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 101, + "clock": 1101.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": {}, + "queue": [], + "ram": [ + 592, + 2992 + ], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 25, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 102, + "clock": 1102.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": {}, + "queue": [], + "ram": [ + 592, + 2992 + ], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 25, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 103, + "clock": 1103.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": {}, + "queue": [], + "ram": [ + 592, + 2992 + ], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 25, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 104, + "clock": 1104.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [ + 7 + ], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0625, + "counts": {}, + "queue": [], + "ram": [ + 592, + 2992 + ], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 25, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 105, + "clock": 1105.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [ + 7 + ], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0, + "counts": {}, + "queue": [], + "ram": [ + 592, + 2992 + ], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 57, + "debt": 0.0, + "fair_rounds": 1 + }, + { + "round": 106, + "clock": 1106.0, + "free_gib": 24.0, + "arrive": [], + "cancel": [], + "damage": false, + "admits": [], + "pieces": [], + "ran": [], + "skipped": [], + "saves": [], + "finals": [], + "windows": [], + "ended": [], + "rows": 2048, + "piece_s": 0.0, + "window_s": 0.0, + "counts": {}, + "queue": [], + "ram": [ + 592, + 2992 + ], + "disk": [ + 1184, + 1488, + 2096, + 4992, + 9488 + ], + "available": 57, + "debt": 0.0, + "fair_rounds": 1 + } + ], + "counts": { + "rows_2048": 54, + "long_waits": 1, + "rows_down": 1, + "rows_512": 6, + "rows_up": 1, + "mem_waits": 2, + "pool_waits": 59 + } +} diff --git a/zig/src/families/deepseek_v41/kv/sched_test.zig b/zig/src/families/deepseek_v41/kv/sched_test.zig new file mode 100644 index 0000000000..390323ad9d --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/sched_test.zig @@ -0,0 +1,436 @@ +//! The prefill scheduler against sched_golden.json: the Zig planner, store, pool and tier decide every round alike. + +const std = @import("std"); +const testing = std.testing; +const sessions = @import("sessions"); +const sess = @import("sess.zig"); +const sched = @import("sched.zig"); + +const Pool = sessions.Pool; +const Slot = sessions.Slot; +const Family = sessions.Family; +const Store = sessions.Store; +const Bounded = sessions.store.Bounded; +const Blob = sessions.disk.Blob; + +const golden_json = @embedFile("sched_golden.json"); + +const SettingsIn = struct { rows: u64, short: u64, share: f64, long_prompt: u64, concurrent: u64, session_min: u64, adapt_gib: f64, adapt_up_gib: f64, adapt_min: u64, floor_gib: f64, hard_gib: f64, refuse_s: f64, cache_keep_gib: f64, index_budget_mib: u64 }; +const JobIn = struct { id: u32, chat: u32, n: u64, max_tokens: u64, background: bool, submitted: f64 }; +const AdmitIn = struct { id: u32, tier: []const u8, cached: u64, need: u32, damaged: bool }; +const RoundIn = struct { + round: u32, + clock: f64, + free_gib: f64, + arrive: []const u32, + cancel: []const u32, + damage: bool, + admits: []const AdmitIn, + pieces: []const [3]u64, + ran: []const [3]u64, + skipped: []const u32, + saves: []const u32, + finals: []const u32, + windows: []const u32, + ended: []const u32, + rows: ?u64, + piece_s: f64, + window_s: f64, + counts: std.json.ArrayHashMap(u64), + queue: []const u32, + ram: []const u64, + disk: []const u64, + available: u32, + debt: f64, + fair_rounds: u64, +}; +const Golden = struct { + settings: SettingsIn, + tag: u32, + page: u32, + pages: u32, + slots: u32, + capacity: u64, + slack: u64, + ram_bytes: u64, + disk_min: u64, + bounded_bytes: u64, + window_s: f64, + jobs: []const JobIn, + rounds: []const RoundIn, + counts: std.json.ArrayHashMap(u64), +}; + +const fams = [_]Family{ + .{ .name = "comp.2", .ratio = 2, .row_bytes = 584 }, + .{ .name = "comp.20", .ratio = 1, .row_bytes = 584 }, +}; + +/// A snapshot's bounded state: a fixed RAM measure (golden.py's), one blob. +const Fixed = struct { + gpa: std.mem.Allocator, + pos: u64, + bytes: u64, + + const vt: Bounded.VTable = .{ .nbytes = nbytes, .blobs = blobs, .release = release }; + + fn make(gpa: std.mem.Allocator, pos: u64, bytes: u64) !Bounded { + const h = try gpa.create(Fixed); + h.* = .{ .gpa = gpa, .pos = pos, .bytes = bytes }; + return .{ .ptr = h, .vtable = &vt }; + } + fn nbytes(p: *anyopaque) u64 { + const h: *Fixed = @ptrCast(@alignCast(p)); + return h.bytes; + } + fn blobs(p: *anyopaque, gpa: std.mem.Allocator) anyerror![]Blob { + const h: *Fixed = @ptrCast(@alignCast(p)); + var b: [8]u8 = undefined; + std.mem.writeInt(u64, &b, h.pos, .little); + const out = try gpa.alloc(Blob, 1); + out[0] = .{ .name = try gpa.dupe(u8, "slot"), .bytes = try gpa.dupe(u8, &b) }; + return out; + } + fn release(p: *anyopaque) void { + const h: *Fixed = @ptrCast(@alignCast(p)); + h.gpa.destroy(h); + } +}; + +fn chatIds(out: []i32, chat: u32) void { + for (out, 0..) |*t, i| t.* = @intCast(chat * 100_000 + i); +} + +const World = struct { + gpa: std.mem.Allocator, + a: std.mem.Allocator, + g: Golden, + pool: Pool, + host: sessions.HostPool, + disk: *sessions.Disk, + store: Store, + root: []const u8, + slots: [4]*Slot = undefined, + /// each slot's request (job id) and a release the next round runs + owner: [4]?u32 = @splat(null), + releasing: [4]bool = @splat(false), + /// each request's decode position and reply tokens + pos: std.AutoHashMapUnmanaged(u32, u64) = .empty, + out: std.AutoHashMapUnmanaged(u32, u64) = .empty, + + fn job(w: *const World, id: u32) JobIn { + for (w.g.jobs) |j| if (j.id == id) return j; + unreachable; + } + + fn ids(w: *World, id: u32) ![]i32 { + const j = w.job(id); + const x = try w.a.alloc(i32, @intCast(j.n)); + chatIds(x, j.chat); + return x; + } + + fn slotOf(w: *const World, id: u32) ?usize { + for (w.owner, 0..) |o, i| if (o == id) return i; + return null; + } + + fn findFn(ctx: *anyopaque, key: usize) anyerror!?sched.Store.Hit { + const w: *World = @ptrCast(@alignCast(ctx)); + const id: u32 = @intCast(key); + const h = (try w.store.find(w.g.tag, try w.ids(id))) orelse return null; + if (h.pos >= w.job(id).n) return null; + return .{ .id = h.id, .pos = h.pos, .ram = h.ram }; + } + + fn poolFn(ctx: *anyopaque, key: usize, hit: ?sched.Store.Hit, keep: []const u32, extra: u32, spills: *std.ArrayList(u32)) anyerror!sess.Plan { + const w: *World = @ptrCast(@alignCast(ctx)); + const j = w.job(@intCast(key)); + const h: ?sessions.store.Hit = if (hit) |x| .{ .id = x.id, .pos = x.pos, .ram = x.ram } else null; + return sess.admit(&w.store, h, j.n, j.max_tokens, w.g.capacity, keep, extra, spills); + } + + fn storeOf(w: *World) sched.Store { + return .{ .ctx = w, .vtable = &.{ .find = findFn, .pool = poolFn } }; + } + + /// Every NVMe file damaged (a byte run in the middle of the file: its pages' data). + fn damage(w: *World) !void { + // the tier's files: //rank (disk.zig `open`) + const compat: [8]u8 = @splat(7); + const sub = try std.fmt.allocPrint(w.a, "{s}/{x}/rank0", .{ w.root, &compat }); + var dir = try std.Io.Dir.cwd().openDir(testing.io, sub, .{ .iterate = true }); + defer dir.close(testing.io); + var it = dir.iterate(); + while (try it.next(testing.io)) |e| { + if (!std.mem.endsWith(u8, e.name, ".tfs")) continue; + var f = try dir.openFile(testing.io, e.name, .{ .mode = .read_write }); + defer f.close(testing.io); + const size = (try f.stat(testing.io)).size; + var junk: [32]u8 = @splat(0xff); + try f.writePositionalAll(testing.io, &junk, size / 2); + } + } + + fn lengths(w: *World, disk: bool) ![]u64 { + var l: std.ArrayList(u64) = .empty; + for (w.store.index.entries.items) |e| if (e.live) { + if (if (disk) e.extra.file else e.ram) try l.append(w.a, e.pos); + }; + std.mem.sort(u64, l.items, {}, std.sort.asc(u64)); + return l.items; + } +}; + +fn expectIds(label: []const u8, round: u32, want: []const u32, got: []const u32) !void { + errdefer std.debug.print("round {d} {s}: reference {any}, ours {any}\n", .{ round, label, want, got }); + try testing.expectEqualSlices(u32, want, got); +} + +fn sortedIds(a: std.mem.Allocator, l: []const u32) ![]u32 { + const x = try a.dupe(u32, l); + std.mem.sort(u32, x, {}, std.sort.asc(u32)); + return x; +} + +test "the round planner decides as the reference batcher: admissions, waits, pieces, snapshots, finals, damaged entries, rows" { + const gpa = testing.allocator; + var parsed = try std.json.parseFromSlice(Golden, gpa, golden_json, .{ .ignore_unknown_fields = true }); + defer parsed.deinit(); + const g = parsed.value; + try testing.expectEqual(@as(u64, sess.slack), g.slack); + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + try tmp.dir.createDirPath(testing.io, "tier"); + const root = try tmp.dir.realPathFileAlloc(testing.io, "tier", gpa); + defer gpa.free(root); + + var w: World = .{ .gpa = gpa, .a = arena.allocator(), .g = g, .pool = undefined, .host = undefined, .disk = undefined, .store = undefined, .root = root }; + defer w.pos.deinit(gpa); + defer w.out.deinit(gpa); + w.pool = try Pool.init(gpa, .{ .families = &fams, .page = g.page }, @as(u64, g.pages) * g.page, 1, 0); + defer w.pool.deinit(); + w.host = try sessions.HostPool.init(gpa, &w.pool); + defer w.host.deinit(); + w.disk = try sessions.Disk.open(gpa, testing.io, .{ .root = root, .compat = @splat(7), .min_tokens = g.disk_min, .chunk = 1 << 20, .staging = 4, .lanes = 2, .direct = false }); + defer w.disk.close(); + w.store = try Store.init(gpa, testing.io, &w.pool, w.host.store(), w.disk, .{ .ram_bytes = g.ram_bytes, .skip_covered = false, .delta = false }); + defer w.store.deinit(); + for (&w.slots) |*s| s.* = try w.pool.newSlot(g.capacity); + + const S = g.settings; + var p = sched.Planner.init(gpa, .{ + .rows = S.rows, + .short = S.short, + .share = S.share, + .long_prompt = S.long_prompt, + .concurrent = S.concurrent, + .session_min = S.session_min, + .sessions = true, + .adapt = .{ .gib = S.adapt_gib, .up_gib = S.adapt_up_gib, .min_rows = S.adapt_min }, + .floor = .{ .target_gib = S.floor_gib, .hard_gib = S.hard_gib, .refuse_s = S.refuse_s, .keep_gib = S.cache_keep_gib }, + .price = .{ .budget_mib = S.index_budget_mib }, + }); + defer p.deinit(); + var queue: std.ArrayList(u32) = .empty; + defer queue.deinit(gpa); + var pieces: std.ArrayList(sched.Piece) = .empty; + defer pieces.deinit(gpa); + var finals: std.ArrayList(usize) = .empty; + defer finals.deinit(gpa); + var seen = struct { damaged: u32 = 0, pool_waits: u64 = 0, mem_waits: u64 = 0, long_waits: u64 = 0, rows_down: u64 = 0, snapshots: u32 = 0 }{}; + + for (g.rounds) |rd| { + const a = w.a; + const r = rd.round; + errdefer std.debug.print("round {d} failed\n", .{r}); + const mi: sched.Meminfo = .{ .free = @intFromFloat(rd.free_gib * sched.GiB), .available = @intFromFloat((rd.free_gib + 3) * sched.GiB), .mapped = 1 << 30 }; + // arrivals: foreground before background, each in arrival order (lane_host.zig's queue) + for (rd.arrive) |id| { + var at = queue.items.len; + if (!w.job(id).background) while (at > 0 and w.job(queue.items[at - 1]).background) { + at -= 1; + }; + try queue.insert(gpa, at, id); + } + if (rd.damage) try w.damage(); + // cancellations: a queued request leaves the queue; a running one ends (its slot's release runs this round) + for (rd.cancel) |id| { + if (std.mem.indexOfScalar(u32, queue.items, id)) |i| { + _ = queue.orderedRemove(i); + p.dropped(id); + } else if (w.slotOf(id)) |s| { + p.ended(id); + w.owner[s] = null; + w.releasing[s] = true; + } + } + // the admission pass, decided whole before any of it runs + const Adm = struct { id: u32, slot: usize, adm: sched.Admission }; + var adms: std.ArrayList(Adm) = .empty; + var round = p.beginAdmit(); + defer round.deinit(gpa); + var free: usize = 0; + for (w.owner) |o| free += @intFromBool(o == null); + var i: usize = 0; + while (i < queue.items.len and free > 0) { + const id = queue.items[i]; + const j = w.job(id); + const v = try p.admit(&round, .{ .key = id, .n = j.n, .max_new = j.max_tokens, .submitted = j.submitted }, w.storeOf(), rd.clock, mi); + switch (v) { + .admit => |adm| { + const s = for (w.owner, 0..) |o, k| { + if (o == null) break k; + } else unreachable; + w.owner[s] = id; + free -= 1; + try adms.append(a, .{ .id = id, .slot = s, .adm = adm }); + _ = queue.orderedRemove(i); + }, + .wait => break, + .skip => i += 1, + .refuse => { + _ = queue.orderedRemove(i); + p.dropped(id); + }, + } + } + // the round runs: releases, the round's spills, then each admission (reserve, restore; a damaged entry: none) + for (&w.releasing, 0..) |*x, s| if (x.*) { + _ = try w.slots[s].releaseAll(); + x.* = false; + }; + for (round.spills.items) |id| _ = try w.store.evict(id); + try w.store.settleAll(); + try testing.expectEqual(rd.admits.len, adms.items.len); + for (adms.items, rd.admits) |x, want| { + const s = w.slots[x.slot]; + try testing.expectEqual(@as(u32, 0), s.len); + s.reserve(x.adm.need); + var tier: []const u8 = "none"; + var bad = false; + if (x.adm.hit) |h| { + tier = if (h.ram) "ram" else "disk"; + if (h.ram) { + _ = try w.store.restoreRam(h.id, s); + } else { + const ok = blk: { + const job_ = w.store.beginRestore(h.id, s) catch break :blk false; + var got = (w.store.settle(job_) catch break :blk false) orelse break :blk false; + got.deinit(gpa); + break :blk true; + }; + if (!ok) { + _ = try s.truncate(0); + try w.store.forget(h.id); + p.damaged(x.id); + bad = true; + seen.damaged += 1; + } + } + } + errdefer std.debug.print("round {d} admit {d}: tier {s} cached {d} need {d} damaged {}; reference {d} {s} {d} {d} {}\n", .{ r, x.id, tier, if (x.adm.hit) |h| h.pos else 0, x.adm.need, bad, want.id, want.tier, want.cached, want.need, want.damaged }); + try testing.expectEqual(want.id, x.id); + try testing.expectEqualStrings(want.tier, tier); + try testing.expectEqual(want.cached, if (x.adm.hit) |h| h.pos else 0); + try testing.expectEqual(want.need, x.adm.need); + try testing.expectEqual(want.damaged, bad); + try testing.expectEqual(x.adm.need, s.quota.?); + } + // the pieces, run unless their entry was damaged, each snapshot right after its piece + try p.plan(mi, &pieces, &finals); + var planned: std.ArrayList([3]u64) = .empty; + var ran: std.ArrayList([3]u64) = .empty; + var saves: std.ArrayList(u32) = .empty; + var rows: u64 = 0; + for (pieces.items) |pc| { + try planned.append(a, .{ pc.key, pc.start, pc.end }); + if (!pc.run) continue; + try ran.append(a, .{ pc.key, pc.start, pc.end }); + rows += pc.end - pc.start; + const id: u32 = @intCast(pc.key); + const s = w.slots[w.slotOf(id).?]; + try s.ensure(pc.end); + if (pc.save and pc.end >= S.session_min) { + const x = try w.ids(id); + _ = try w.store.save(s, try Fixed.make(gpa, pc.end, g.bounded_bytes), x[0..@intCast(pc.end)], g.tag, 1); + try w.store.settleAll(); + try saves.append(a, id); + seen.snapshots += 1; + } + } + { + errdefer std.debug.print("round {d} pieces: reference {any} (ran {any}), ours {any} (ran {any})\n", .{ r, rd.pieces, rd.ran, planned.items, ran.items }); + try testing.expectEqual(rd.pieces.len, planned.items.len); + for (rd.pieces, planned.items) |x, y| try testing.expectEqualSlices(u64, &x, &y); + try testing.expectEqual(rd.ran.len, ran.items.len); + for (rd.ran, ran.items) |x, y| try testing.expectEqualSlices(u64, &x, &y); + } + try expectIds("saves", r, rd.saves, saves.items); + var fin: std.ArrayList(u32) = .empty; + for (finals.items) |k| { + try fin.append(a, @intCast(k)); + try w.pos.put(gpa, @intCast(k), w.job(@intCast(k)).n - 1); + try w.out.put(gpa, @intCast(k), 0); + } + try expectIds("finals", r, try sortedIds(a, rd.finals), try sortedIds(a, fin.items)); + // the windows: every decoding request a token (no drafts), its pages for the row + var wins: std.ArrayList(u32) = .empty; + for (p.seqs.items) |x| if (x.decoding and !x.damaged) try wins.append(a, @intCast(x.key)); + const wsorted = try sortedIds(a, wins.items); + try expectIds("windows", r, rd.windows, wsorted); + var ended: std.ArrayList(u32) = .empty; + for (wins.items) |id| { + const s = w.slots[w.slotOf(id).?]; + const at = w.pos.getPtr(id).?; + try s.ensure(at.* + 1); + at.* += 1; + const o = w.out.getPtr(id).?; + o.* += 1; + if (o.* >= w.job(id).max_tokens) try ended.append(a, id); + } + try expectIds("ended", r, try sortedIds(a, rd.ended), try sortedIds(a, ended.items)); + for (ended.items) |id| { + const s = w.slotOf(id).?; + p.ended(id); + w.owner[s] = null; + w.releasing[s] = true; // released at the next round's start + } + const piece_s = @as(f64, @floatFromInt(rows)) / 8192.0; + try testing.expectEqual(rd.piece_s, piece_s); + p.after(piece_s, if (wins.items.len > 0) g.window_s else 0.0); + { + const ram_l = try w.lengths(false); + const disk_l = try w.lengths(true); + errdefer std.debug.print("round {d}: rows {?d} (reference {?d}), debt {d} (reference {d}), fair rounds {d} (reference {d}), queue {any} (reference {any}), available {d} (reference {d}), RAM {any} (reference {any}), NVMe {any} (reference {any})\n", .{ r, p.rows_now, rd.rows, p.fair.debt, rd.debt, p.fair.rounds, rd.fair_rounds, queue.items, rd.queue, w.pool.available(), rd.available, ram_l, rd.ram, disk_l, rd.disk }); + try testing.expectEqual(rd.rows, p.rows_now); + try testing.expectEqual(rd.debt, p.fair.debt); + try testing.expectEqual(rd.fair_rounds, p.fair.rounds); + try testing.expectEqualSlices(u32, rd.queue, queue.items); + try testing.expectEqual(rd.available, w.pool.available()); + try testing.expectEqualSlices(u64, rd.ram, ram_l); + try testing.expectEqualSlices(u64, rd.disk, disk_l); + } + } + // the golden's counters over the whole run + const c = p.counts; + const want = g.counts.map; + try testing.expectEqual(want.get("long_waits") orelse 0, c.long_waits); + try testing.expectEqual(want.get("mem_waits") orelse 0, c.mem_waits); + try testing.expectEqual(want.get("pool_waits") orelse 0, c.pool_waits); + try testing.expectEqual(want.get("mem_refused") orelse 0, c.mem_refused); + try testing.expectEqual(want.get("rows_down") orelse 0, c.rows_down); + try testing.expectEqual(want.get("rows_up") orelse 0, c.rows_up); + for (c.rows) |x| if (x[0] != 0) { + var nb: [32]u8 = undefined; + try testing.expectEqual(want.get(try std.fmt.bufPrint(&nb, "rows_{d}", .{x[0]})) orelse 0, x[1]); + }; + // the golden exercises what it claims + seen.pool_waits = c.pool_waits; + seen.mem_waits = c.mem_waits; + seen.long_waits = c.long_waits; + seen.rows_down = c.rows_down; + try testing.expect(seen.damaged == 1 and seen.pool_waits > 0 and seen.mem_waits > 0 and seen.long_waits > 0 and seen.rows_down > 0 and seen.snapshots >= 8); +} diff --git a/zig/src/families/deepseek_v41/kv/sess.zig b/zig/src/families/deepseek_v41/kv/sess.zig new file mode 100644 index 0000000000..4dd5f49ed0 --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/sess.zig @@ -0,0 +1,102 @@ +//! Session rules over several live slots: pool admission (`admit`), the RAM tier's `Measure`, `dsRows` / `stashRows`. + +const std = @import("std"); +const sessions = @import("sessions"); + +/// Positions past prompt + max_tokens a request may write (verify / draft windows) +pub const slack: u64 = 64; + +pub const Error = error{RequestTooLarge}; + +/// A request's admission: `need` pages for its slot, `want` of them new; `wait`: no fit even with every RAM entry gone. +pub const Plan = struct { need: u32, want: u32, wait: bool = false }; + +/// Pool admission of `prompt` + `max_new` on a `capacity` slot: pages, and the leader-ordered `spills` to evict first. +pub fn admit(store: *sessions.Store, hit: ?sessions.store.Hit, prompt: u64, max_new: u64, capacity: u64, held: []const u32, extra: u32, spills: *std.ArrayList(u32)) !Plan { + spills.clearRetainingCapacity(); + const pool = store.pool; + const need = pool.needPages(prompt, max_new, capacity, slack); + if (need > pool.npages) return error.RequestTooLarge; + // a RAM hit's full pages are shared, not new + const shared: u32 = if (hit) |h| (if (h.ram) @intCast(h.pos / pool.page) else 0) else 0; + const want = need - @min(need, shared) + extra; + if (pool.available() >= want) return .{ .need = need, .want = want }; + if (!try store.planSpill(want, held, spills)) { + spills.clearRetainingCapacity(); + return .{ .need = need, .want = want, .wait = true }; + } + return .{ .need = need, .want = want }; +} + +/// The snapshot point (last 16-grid point before n) if past `cached` and >= 64 tokens; same rule as ced.savePoint. +pub fn savePoint(n: u64, cached: u64) ?u64 { + const s: u64 = if (n > 0) (n - 1) / 16 * 16 else 0; + return if (s > cached and s >= 64) s else null; +} + +/// An SWA / DSpark ring row as the snapshot holds it (Records raw: 576 values + 8 scales) +pub const row_bytes: u64 = 584; +/// Engram's lookback ids (int64) +pub const lookback: u64 = 3; +/// the stash rows a slot keeps (REPLAY - 1) +pub const keep: u64 = 127; + +/// What a slot's snapshot holds, as sizes (the RAM tier's measure of an entry). +pub const Measure = struct { + /// SWA rings in the snapshot: replay mode the layers below the decoder, full mode every one + rings: u64, + /// the SWA window (cfg.sliding_window: the rings' rows and the drafter's) + window: u64, + /// ratio-2 kv sources (their float32 [2 x head_dim] carries) + carries: u64, + head_dim: u64, + hidden: u64, + hc: u64, + /// the drafter's blocks (0: no drafter, nothing of DSpark in the snapshot) + ds_blocks: u64 = 0, + /// CED replay: the stash's rows ride along + replay: bool, + + /// A replay stash row: ced_x [hc x D], ced_out [D] bf16, ced_pre / ced_post [4], ced_comb [16] f32, its id + fn stashRow(m: Measure) u64 { + return 2 * m.hc * m.hidden + 2 * m.hidden + 4 * (4 + 4 + 16) + 8; + } + + /// the entry's bytes at `pos`, with `stash_rows` (`stashRows`) and `ds_rows` (`dsRows`) + pub fn bytes(m: Measure, pos: u64, stash_rows: u64, ds_rows: u64) u64 { + var n: u64 = 0; + // swa_values / swa_scales: [rings, rows, 576 | 8] uint8 (none below one row) + n += m.rings * @min(pos, m.window) * row_bytes; + // the stash (replay: ced_lo int64, then its rows) + if (m.replay) n += 8 + stash_rows * m.stashRow(); + // carry: [carries, 2 x head_dim] float32 + if (m.carries > 0) n += m.carries * 2 * m.head_dim * 4; + // lookback [3] and rings (the layer ids) int64 + n += 8 * lookback + 8 * m.rings; + // dspark_values / dspark_scales: [blocks, rows, 576 | 8] uint8 + if (m.ds_blocks > 0) n += m.ds_blocks * ds_rows * row_bytes; + return n; + } +}; + +/// The drafter snapshot's rows: [max(0, pos - window, valid), pos); `valid` 0 after a reset, pos - n after a restore. +pub fn dsRows(pos: u64, valid: u64, window: u64) u64 { + return pos -| @max(pos -| window, valid); +} + +/// The CED stash's rows at `pos` for a stash started at `from`: the last KEEP of them. +pub fn stashRows(pos: u64, from: u64) u64 { + return pos -| @max(from, pos -| keep); +} + +test "the measure is the reference's Bounded.nbytes (golden.py: 96 and >= 128 positions)" { + const m: Measure = .{ .rings = 20, .window = 128, .carries = 3, .head_dim = 512, .hidden = 5120, .hc = 4, .ds_blocks = 3, .replay = true }; + try std.testing.expectEqual(@as(u64, 6_227_136), m.bytes(96, stashRows(96, 0), dsRows(96, 0, 128))); + try std.testing.expectEqual(@as(u64, 8_247_384), m.bytes(1488, stashRows(1488, 0), dsRows(1488, 0, 128))); + // a restored slot: the drafter's context from the restored rows on, the stash from its restored first row + try std.testing.expectEqual(@as(u64, 128), dsRows(1696, 1488 - 128, 128)); + try std.testing.expectEqual(@as(u64, 0), dsRows(1488, 1488, 128)); + try std.testing.expectEqual(@as(u64, 40), dsRows(1528, 1488, 128)); + try std.testing.expectEqual(@as(u64, 127), stashRows(1696, 1488 - 127)); + try std.testing.expectEqual(@as(u64, 10), stashRows(10, 0)); +} diff --git a/zig/src/families/deepseek_v41/kv/session_slots_golden.json b/zig/src/families/deepseek_v41/kv/session_slots_golden.json new file mode 100644 index 0000000000..feb60ce017 --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/session_slots_golden.json @@ -0,0 +1,686 @@ +{ + "python": "8474f31", + "tag": 272, + "page": 256, + "pages": 28, + "slots": 4, + "capacity": 4096, + "slack": 64, + "ram_bytes": 37113228, + "disk_min": 1024, + "measure": { + "layers": 40, + "decoder_start": 20, + "window": 128, + "carries": 3, + "head_dim": 512, + "hidden": 5120, + "hc": 4, + "ds_blocks": 3, + "keep": 127, + "row_bytes": 584, + "lookback": 3 + }, + "stats": { + "saves": 12, + "hits_ram": 2, + "hits_disk": 5, + "parks": 5, + "drops": 3, + "dups": 0 + }, + "steps": [ + { + "op": "admit", + "label": "c1 t1", + "slot": 0, + "chat": 1, + "branch": 0, + "n": 1500, + "max_new": 200, + "reply": 40, + "tier": "none", + "cached": 0, + "need": 7, + "want": 7, + "spills": [], + "saved": { + "at": 1488, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b0@1488" + ], + "disk": [], + "available": 21, + "free": 21 + }, + { + "op": "admit", + "label": "c6 t1 (under one window)", + "slot": 3, + "chat": 6, + "branch": 0, + "n": 100, + "max_new": 50, + "reply": 10, + "tier": "none", + "cached": 0, + "need": 1, + "want": 1, + "spills": [], + "saved": { + "at": 96, + "nbytes": 6227136, + "ds_rows": 96, + "dup": false + }, + "ram": [ + "c1b0@1488", + "c6b0@96" + ], + "disk": [], + "available": 20, + "free": 20 + }, + { + "op": "finish", + "slot": 3, + "ram": [ + "c1b0@1488", + "c6b0@96" + ], + "disk": [], + "available": 20, + "free": 20 + }, + { + "op": "admit", + "label": "c2 t1 (short: dropped on trim)", + "slot": 1, + "chat": 2, + "branch": 0, + "n": 700, + "max_new": 100, + "reply": 30, + "tier": "none", + "cached": 0, + "need": 4, + "want": 4, + "spills": [], + "saved": { + "at": 688, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b0@1488", + "c2b0@688", + "c6b0@96" + ], + "disk": [], + "available": 16, + "free": 17 + }, + { + "op": "admit", + "label": "c3 t1", + "slot": 2, + "chat": 3, + "branch": 0, + "n": 1300, + "max_new": 200, + "reply": 50, + "tier": "none", + "cached": 0, + "need": 7, + "want": 7, + "spills": [], + "saved": { + "at": 1296, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b0@1488", + "c2b0@688", + "c3b0@1296", + "c6b0@96" + ], + "disk": [], + "available": 9, + "free": 11 + }, + { + "op": "finish", + "slot": 1, + "ram": [ + "c1b0@1488", + "c2b0@688", + "c3b0@1296", + "c6b0@96" + ], + "disk": [], + "available": 10, + "free": 11 + }, + { + "op": "admit", + "label": "c4 t1", + "slot": 1, + "chat": 4, + "branch": 0, + "n": 1100, + "max_new": 300, + "reply": 20, + "tier": "none", + "cached": 0, + "need": 6, + "want": 6, + "spills": [], + "saved": { + "at": 1088, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c2b0@688", + "c3b0@1296", + "c4b0@1088", + "c6b0@96" + ], + "disk": [ + "c1b0@1488" + ], + "available": 4, + "free": 6 + }, + { + "op": "finish", + "slot": 0, + "ram": [ + "c2b0@688", + "c3b0@1296", + "c4b0@1088", + "c6b0@96" + ], + "disk": [ + "c1b0@1488" + ], + "available": 11, + "free": 13 + }, + { + "op": "finish", + "slot": 2, + "ram": [ + "c2b0@688", + "c3b0@1296", + "c4b0@1088", + "c6b0@96" + ], + "disk": [ + "c1b0@1488" + ], + "available": 12, + "free": 13 + }, + { + "op": "admit", + "label": "c1 t2", + "slot": 0, + "chat": 1, + "branch": 0, + "n": 1700, + "max_new": 200, + "reply": 30, + "tier": "disk", + "cached": 1488, + "need": 8, + "want": 8, + "spills": [], + "saved": { + "at": 1696, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b0@1696", + "c2b0@688", + "c3b0@1296", + "c4b0@1088" + ], + "disk": [ + "c1b0@1488" + ], + "available": 5, + "free": 7 + }, + { + "op": "admit", + "label": "c2 t2", + "slot": 2, + "chat": 2, + "branch": 0, + "n": 900, + "max_new": 100, + "reply": 20, + "tier": "ram", + "cached": 688, + "need": 5, + "want": 3, + "spills": [], + "saved": { + "at": 896, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b0@1696", + "c2b0@688", + "c2b0@896", + "c4b0@1088" + ], + "disk": [ + "c1b0@1488", + "c3b0@1296" + ], + "available": 8, + "free": 11 + }, + { + "op": "probe", + "chat": 5, + "n": 2000, + "max_new": 3000, + "need": 16, + "want": 16, + "wait": true, + "ram": [ + "c1b0@1696", + "c2b0@688", + "c2b0@896", + "c4b0@1088" + ], + "disk": [ + "c1b0@1488", + "c3b0@1296" + ], + "available": 8, + "free": 11 + }, + { + "op": "finish", + "slot": 1, + "ram": [ + "c1b0@1696", + "c2b0@688", + "c2b0@896", + "c4b0@1088" + ], + "disk": [ + "c1b0@1488", + "c3b0@1296" + ], + "available": 9, + "free": 11 + }, + { + "op": "admit", + "label": "c3 t2", + "slot": 1, + "chat": 3, + "branch": 0, + "n": 1600, + "max_new": 200, + "reply": 20, + "tier": "disk", + "cached": 1296, + "need": 8, + "want": 8, + "spills": [], + "saved": { + "at": 1584, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b0@1696", + "c2b0@688", + "c2b0@896", + "c3b0@1584" + ], + "disk": [ + "c1b0@1488", + "c3b0@1296", + "c4b0@1088" + ], + "available": 6, + "free": 9 + }, + { + "op": "admit", + "label": "c3 t2 resend", + "slot": 3, + "chat": 3, + "branch": 0, + "n": 1600, + "max_new": 100, + "reply": 10, + "tier": "ram", + "cached": 1584, + "need": 7, + "want": 1, + "spills": [], + "saved": null, + "ram": [ + "c1b0@1696", + "c2b0@688", + "c2b0@896", + "c3b0@1584" + ], + "disk": [ + "c1b0@1488", + "c3b0@1296", + "c4b0@1088" + ], + "available": 5, + "free": 8 + }, + { + "op": "finish", + "slot": 3, + "ram": [ + "c1b0@1696", + "c2b0@688", + "c2b0@896", + "c3b0@1584" + ], + "disk": [ + "c1b0@1488", + "c3b0@1296", + "c4b0@1088" + ], + "available": 6, + "free": 9 + }, + { + "op": "finish", + "slot": 0, + "ram": [ + "c1b0@1696", + "c2b0@688", + "c2b0@896", + "c3b0@1584" + ], + "disk": [ + "c1b0@1488", + "c3b0@1296", + "c4b0@1088" + ], + "available": 7, + "free": 9 + }, + { + "op": "finish", + "slot": 2, + "ram": [ + "c1b0@1696", + "c2b0@688", + "c2b0@896", + "c3b0@1584" + ], + "disk": [ + "c1b0@1488", + "c3b0@1296", + "c4b0@1088" + ], + "available": 8, + "free": 9 + }, + { + "op": "admit", + "label": "c1 t2 regenerate", + "slot": 0, + "chat": 1, + "branch": 1, + "n": 1700, + "max_new": 200, + "reply": 30, + "tier": "disk", + "cached": 1488, + "need": 8, + "want": 8, + "spills": [], + "saved": { + "at": 1696, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b1@1696", + "c2b0@688", + "c2b0@896", + "c3b0@1584" + ], + "disk": [ + "c1b0@1488", + "c1b0@1696", + "c3b0@1296", + "c4b0@1088" + ], + "available": 7, + "free": 9 + }, + { + "op": "admit", + "label": "c4 t2 (big quota: spills)", + "slot": 2, + "chat": 4, + "branch": 0, + "n": 1400, + "max_new": 1200, + "reply": 20, + "tier": "disk", + "cached": 1088, + "need": 11, + "want": 11, + "spills": [ + "c2b0@688", + "c2b0@896" + ], + "saved": { + "at": 1392, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b1@1696", + "c3b0@1584", + "c4b0@1392" + ], + "disk": [ + "c1b0@1488", + "c1b0@1696", + "c3b0@1296", + "c4b0@1088" + ], + "available": 1, + "free": 8 + }, + { + "op": "finish", + "slot": 1, + "ram": [ + "c1b1@1696", + "c3b0@1584", + "c4b0@1392" + ], + "disk": [ + "c1b0@1488", + "c1b0@1696", + "c3b0@1296", + "c4b0@1088" + ], + "available": 2, + "free": 8 + }, + { + "op": "admit", + "label": "c1 t3", + "slot": 1, + "chat": 1, + "branch": 0, + "n": 2000, + "max_new": 200, + "reply": 10, + "tier": "disk", + "cached": 1696, + "need": 9, + "want": 9, + "spills": [ + "c3b0@1584" + ], + "saved": { + "at": 1984, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b0@1984", + "c1b1@1696", + "c4b0@1392" + ], + "disk": [ + "c1b0@1488", + "c1b0@1696", + "c3b0@1296", + "c3b0@1584", + "c4b0@1088" + ], + "available": 0, + "free": 7 + }, + { + "op": "finish", + "slot": 0, + "ram": [ + "c1b0@1984", + "c1b1@1696", + "c4b0@1392" + ], + "disk": [ + "c1b0@1488", + "c1b0@1696", + "c3b0@1296", + "c3b0@1584", + "c4b0@1088" + ], + "available": 1, + "free": 7 + }, + { + "op": "finish", + "slot": 2, + "ram": [ + "c1b0@1984", + "c1b1@1696", + "c4b0@1392" + ], + "disk": [ + "c1b0@1488", + "c1b0@1696", + "c3b0@1296", + "c3b0@1584", + "c4b0@1088" + ], + "available": 6, + "free": 7 + }, + { + "op": "admit", + "label": "c2 t3", + "slot": 3, + "chat": 2, + "branch": 0, + "n": 1200, + "max_new": 100, + "reply": 10, + "tier": "none", + "cached": 0, + "need": 6, + "want": 6, + "spills": [], + "saved": { + "at": 1184, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b0@1984", + "c1b1@1696", + "c2b0@1184", + "c4b0@1392" + ], + "disk": [ + "c1b0@1488", + "c1b0@1696", + "c3b0@1296", + "c3b0@1584", + "c4b0@1088" + ], + "available": 0, + "free": 2 + }, + { + "op": "finish", + "slot": 1, + "ram": [ + "c1b0@1984", + "c1b1@1696", + "c2b0@1184", + "c4b0@1392" + ], + "disk": [ + "c1b0@1488", + "c1b0@1696", + "c3b0@1296", + "c3b0@1584", + "c4b0@1088" + ], + "available": 1, + "free": 2 + }, + { + "op": "finish", + "slot": 3, + "ram": [ + "c1b0@1984", + "c1b1@1696", + "c2b0@1184", + "c4b0@1392" + ], + "disk": [ + "c1b0@1488", + "c1b0@1696", + "c3b0@1296", + "c3b0@1584", + "c4b0@1088" + ], + "available": 2, + "free": 2 + } + ] +} diff --git a/zig/src/families/deepseek_v41/kv/session_slots_split_golden.json b/zig/src/families/deepseek_v41/kv/session_slots_split_golden.json new file mode 100644 index 0000000000..91670161c6 --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/session_slots_split_golden.json @@ -0,0 +1,686 @@ +{ + "python": "kvsplit split 2 rank 0", + "tag": 272, + "page": 256, + "pages": 30, + "slots": 4, + "capacity": 4096, + "slack": 64, + "ram_bytes": 37113228, + "disk_min": 1024, + "measure": { + "layers": 40, + "decoder_start": 20, + "window": 128, + "carries": 3, + "head_dim": 512, + "hidden": 5120, + "hc": 4, + "ds_blocks": 3, + "keep": 127, + "row_bytes": 584, + "lookback": 3 + }, + "stats": { + "saves": 12, + "hits_ram": 2, + "hits_disk": 5, + "parks": 5, + "drops": 3, + "dups": 0 + }, + "steps": [ + { + "op": "admit", + "label": "c1 t1", + "slot": 0, + "chat": 1, + "branch": 0, + "n": 1500, + "max_new": 200, + "reply": 40, + "tier": "none", + "cached": 0, + "need": 7, + "want": 7, + "spills": [], + "saved": { + "at": 1488, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b0@1488" + ], + "disk": [], + "available": 22, + "free": 23 + }, + { + "op": "admit", + "label": "c6 t1 (under one window)", + "slot": 3, + "chat": 6, + "branch": 0, + "n": 100, + "max_new": 50, + "reply": 10, + "tier": "none", + "cached": 0, + "need": 1, + "want": 1, + "spills": [], + "saved": { + "at": 96, + "nbytes": 6227136, + "ds_rows": 96, + "dup": false + }, + "ram": [ + "c1b0@1488", + "c6b0@96" + ], + "disk": [], + "available": 20, + "free": 22 + }, + { + "op": "finish", + "slot": 3, + "ram": [ + "c1b0@1488", + "c6b0@96" + ], + "disk": [], + "available": 20, + "free": 22 + }, + { + "op": "admit", + "label": "c2 t1 (short: dropped on trim)", + "slot": 1, + "chat": 2, + "branch": 0, + "n": 700, + "max_new": 100, + "reply": 30, + "tier": "none", + "cached": 0, + "need": 4, + "want": 4, + "spills": [], + "saved": { + "at": 688, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b0@1488", + "c2b0@688", + "c6b0@96" + ], + "disk": [], + "available": 16, + "free": 19 + }, + { + "op": "admit", + "label": "c3 t1", + "slot": 2, + "chat": 3, + "branch": 0, + "n": 1300, + "max_new": 200, + "reply": 50, + "tier": "none", + "cached": 0, + "need": 7, + "want": 7, + "spills": [], + "saved": { + "at": 1296, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b0@1488", + "c2b0@688", + "c3b0@1296", + "c6b0@96" + ], + "disk": [], + "available": 8, + "free": 13 + }, + { + "op": "finish", + "slot": 1, + "ram": [ + "c1b0@1488", + "c2b0@688", + "c3b0@1296", + "c6b0@96" + ], + "disk": [], + "available": 8, + "free": 13 + }, + { + "op": "admit", + "label": "c4 t1", + "slot": 1, + "chat": 4, + "branch": 0, + "n": 1100, + "max_new": 300, + "reply": 20, + "tier": "none", + "cached": 0, + "need": 6, + "want": 6, + "spills": [], + "saved": { + "at": 1088, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c2b0@688", + "c3b0@1296", + "c4b0@1088", + "c6b0@96" + ], + "disk": [ + "c1b0@1488" + ], + "available": 2, + "free": 8 + }, + { + "op": "finish", + "slot": 0, + "ram": [ + "c2b0@688", + "c3b0@1296", + "c4b0@1088", + "c6b0@96" + ], + "disk": [ + "c1b0@1488" + ], + "available": 10, + "free": 15 + }, + { + "op": "finish", + "slot": 2, + "ram": [ + "c2b0@688", + "c3b0@1296", + "c4b0@1088", + "c6b0@96" + ], + "disk": [ + "c1b0@1488" + ], + "available": 12, + "free": 15 + }, + { + "op": "admit", + "label": "c1 t2", + "slot": 0, + "chat": 1, + "branch": 0, + "n": 1700, + "max_new": 200, + "reply": 30, + "tier": "disk", + "cached": 1488, + "need": 8, + "want": 8, + "spills": [], + "saved": { + "at": 1696, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b0@1696", + "c2b0@688", + "c3b0@1296", + "c4b0@1088" + ], + "disk": [ + "c1b0@1488" + ], + "available": 6, + "free": 9 + }, + { + "op": "admit", + "label": "c2 t2", + "slot": 2, + "chat": 2, + "branch": 0, + "n": 900, + "max_new": 100, + "reply": 20, + "tier": "ram", + "cached": 688, + "need": 5, + "want": 3, + "spills": [], + "saved": { + "at": 896, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b0@1696", + "c2b0@688", + "c2b0@896", + "c4b0@1088" + ], + "disk": [ + "c1b0@1488", + "c3b0@1296" + ], + "available": 8, + "free": 13 + }, + { + "op": "probe", + "chat": 5, + "n": 2000, + "max_new": 3000, + "need": 16, + "want": 16, + "wait": true, + "ram": [ + "c1b0@1696", + "c2b0@688", + "c2b0@896", + "c4b0@1088" + ], + "disk": [ + "c1b0@1488", + "c3b0@1296" + ], + "available": 8, + "free": 13 + }, + { + "op": "finish", + "slot": 1, + "ram": [ + "c1b0@1696", + "c2b0@688", + "c2b0@896", + "c4b0@1088" + ], + "disk": [ + "c1b0@1488", + "c3b0@1296" + ], + "available": 8, + "free": 13 + }, + { + "op": "admit", + "label": "c3 t2", + "slot": 1, + "chat": 3, + "branch": 0, + "n": 1600, + "max_new": 200, + "reply": 20, + "tier": "disk", + "cached": 1296, + "need": 8, + "want": 8, + "spills": [], + "saved": { + "at": 1584, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b0@1696", + "c2b0@688", + "c2b0@896", + "c3b0@1584" + ], + "disk": [ + "c1b0@1488", + "c3b0@1296", + "c4b0@1088" + ], + "available": 6, + "free": 11 + }, + { + "op": "admit", + "label": "c3 t2 resend", + "slot": 3, + "chat": 3, + "branch": 0, + "n": 1600, + "max_new": 100, + "reply": 10, + "tier": "ram", + "cached": 1584, + "need": 7, + "want": 1, + "spills": [], + "saved": null, + "ram": [ + "c1b0@1696", + "c2b0@688", + "c2b0@896", + "c3b0@1584" + ], + "disk": [ + "c1b0@1488", + "c3b0@1296", + "c4b0@1088" + ], + "available": 4, + "free": 10 + }, + { + "op": "finish", + "slot": 3, + "ram": [ + "c1b0@1696", + "c2b0@688", + "c2b0@896", + "c3b0@1584" + ], + "disk": [ + "c1b0@1488", + "c3b0@1296", + "c4b0@1088" + ], + "available": 6, + "free": 11 + }, + { + "op": "finish", + "slot": 0, + "ram": [ + "c1b0@1696", + "c2b0@688", + "c2b0@896", + "c3b0@1584" + ], + "disk": [ + "c1b0@1488", + "c3b0@1296", + "c4b0@1088" + ], + "available": 6, + "free": 11 + }, + { + "op": "finish", + "slot": 2, + "ram": [ + "c1b0@1696", + "c2b0@688", + "c2b0@896", + "c3b0@1584" + ], + "disk": [ + "c1b0@1488", + "c3b0@1296", + "c4b0@1088" + ], + "available": 8, + "free": 11 + }, + { + "op": "admit", + "label": "c1 t2 regenerate", + "slot": 0, + "chat": 1, + "branch": 1, + "n": 1700, + "max_new": 200, + "reply": 30, + "tier": "disk", + "cached": 1488, + "need": 8, + "want": 8, + "spills": [], + "saved": { + "at": 1696, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b1@1696", + "c2b0@688", + "c2b0@896", + "c3b0@1584" + ], + "disk": [ + "c1b0@1488", + "c1b0@1696", + "c3b0@1296", + "c4b0@1088" + ], + "available": 8, + "free": 11 + }, + { + "op": "admit", + "label": "c4 t2 (big quota: spills)", + "slot": 2, + "chat": 4, + "branch": 0, + "n": 1400, + "max_new": 1200, + "reply": 20, + "tier": "disk", + "cached": 1088, + "need": 11, + "want": 11, + "spills": [ + "c2b0@688", + "c2b0@896" + ], + "saved": { + "at": 1392, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b1@1696", + "c3b0@1584", + "c4b0@1392" + ], + "disk": [ + "c1b0@1488", + "c1b0@1696", + "c3b0@1296", + "c4b0@1088" + ], + "available": 2, + "free": 10 + }, + { + "op": "finish", + "slot": 1, + "ram": [ + "c1b1@1696", + "c3b0@1584", + "c4b0@1392" + ], + "disk": [ + "c1b0@1488", + "c1b0@1696", + "c3b0@1296", + "c4b0@1088" + ], + "available": 2, + "free": 10 + }, + { + "op": "admit", + "label": "c1 t3", + "slot": 1, + "chat": 1, + "branch": 0, + "n": 2000, + "max_new": 200, + "reply": 10, + "tier": "disk", + "cached": 1696, + "need": 9, + "want": 9, + "spills": [ + "c3b0@1584" + ], + "saved": { + "at": 1984, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b0@1984", + "c1b1@1696", + "c4b0@1392" + ], + "disk": [ + "c1b0@1488", + "c1b0@1696", + "c3b0@1296", + "c3b0@1584", + "c4b0@1088" + ], + "available": 0, + "free": 9 + }, + { + "op": "finish", + "slot": 0, + "ram": [ + "c1b0@1984", + "c1b1@1696", + "c4b0@1392" + ], + "disk": [ + "c1b0@1488", + "c1b0@1696", + "c3b0@1296", + "c3b0@1584", + "c4b0@1088" + ], + "available": 0, + "free": 9 + }, + { + "op": "finish", + "slot": 2, + "ram": [ + "c1b0@1984", + "c1b1@1696", + "c4b0@1392" + ], + "disk": [ + "c1b0@1488", + "c1b0@1696", + "c3b0@1296", + "c3b0@1584", + "c4b0@1088" + ], + "available": 6, + "free": 9 + }, + { + "op": "admit", + "label": "c2 t3", + "slot": 3, + "chat": 2, + "branch": 0, + "n": 1200, + "max_new": 100, + "reply": 10, + "tier": "none", + "cached": 0, + "need": 6, + "want": 6, + "spills": [], + "saved": { + "at": 1184, + "nbytes": 8247384, + "ds_rows": 128, + "dup": false + }, + "ram": [ + "c1b0@1984", + "c1b1@1696", + "c2b0@1184", + "c4b0@1392" + ], + "disk": [ + "c1b0@1488", + "c1b0@1696", + "c3b0@1296", + "c3b0@1584", + "c4b0@1088" + ], + "available": 0, + "free": 4 + }, + { + "op": "finish", + "slot": 1, + "ram": [ + "c1b0@1984", + "c1b1@1696", + "c2b0@1184", + "c4b0@1392" + ], + "disk": [ + "c1b0@1488", + "c1b0@1696", + "c3b0@1296", + "c3b0@1584", + "c4b0@1088" + ], + "available": 2, + "free": 4 + }, + { + "op": "finish", + "slot": 3, + "ram": [ + "c1b0@1984", + "c1b1@1696", + "c2b0@1184", + "c4b0@1392" + ], + "disk": [ + "c1b0@1488", + "c1b0@1696", + "c3b0@1296", + "c3b0@1584", + "c4b0@1088" + ], + "available": 2, + "free": 4 + } + ] +} diff --git a/zig/src/families/deepseek_v41/kv/session_slots_test.zig b/zig/src/families/deepseek_v41/kv/session_slots_test.zig new file mode 100644 index 0000000000..bd75ea2039 --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/session_slots_test.zig @@ -0,0 +1,353 @@ +//! Sessions over 4 live slots against session_slots_golden.json: each step decides alike, each resume checked by row. + +const std = @import("std"); +const testing = std.testing; +const sessions = @import("sessions"); +const sess = @import("sess.zig"); + +const Pool = sessions.Pool; +const Slot = sessions.Slot; +const Family = sessions.Family; +const Store = sessions.Store; +const Bounded = sessions.store.Bounded; +const Blob = sessions.disk.Blob; + +const golden_json = @embedFile("session_slots_golden.json"); +/// The split store and pool (2 ranks: their decisions alike) on 30 pages (golden.py --split 2 --pages 30) +const split_golden_json = @embedFile("session_slots_split_golden.json"); + +const Saved = struct { at: u64, nbytes: u64, ds_rows: u64, dup: bool }; +const Step = struct { + op: []const u8, + label: []const u8 = "", + slot: u32 = 0, + chat: u32 = 0, + branch: u32 = 0, + n: u64 = 0, + max_new: u64 = 0, + reply: u64 = 0, + tier: []const u8 = "", + cached: u64 = 0, + need: u32 = 0, + want: u32 = 0, + spills: []const []const u8 = &.{}, + saved: ?Saved = null, + wait: ?bool = null, + ram: []const []const u8, + disk: []const []const u8, + available: u32, + free: u32, +}; +const MeasureIn = struct { layers: u64, decoder_start: u64, window: u64, carries: u64, head_dim: u64, hidden: u64, hc: u64, ds_blocks: u64, keep: u64, row_bytes: u64, lookback: u64 }; +const Golden = struct { + tag: u32, + page: u32, + pages: u32, + slots: u32, + capacity: u64, + slack: u64, + ram_bytes: u64, + disk_min: u64, + measure: MeasureIn, + steps: []const Step, +}; + +/// The pool's families as the test stores them (any sizes: the decisions are in pages; the rows are checked) +const fams = [_]Family{ + .{ .name = "comp.2", .ratio = 2, .row_bytes = 584 }, + .{ .name = "index_k.2", .ratio = 2, .row_bytes = 132 }, + .{ .name = "comp.20", .ratio = 1, .row_bytes = 584 }, +}; +/// TF_DSV41_KV_SPLIT=1: the comp rows of a page live on the rank that owns it, the indexer's on every rank +const split_fams = [_]Family{ + .{ .name = "comp.2", .ratio = 2, .row_bytes = 584, .split = true }, + .{ .name = "index_k.2", .ratio = 2, .row_bytes = 132 }, + .{ .name = "comp.20", .ratio = 1, .row_bytes = 584, .split = true }, +}; + +/// A snapshot's bounded state in host memory: the drafter rows and stash start at `pos`, with the golden's RAM measure. +const HostBounded = struct { + gpa: std.mem.Allocator, + pos: u64, + ds_rows: u64, + stash_from: u64, + measure: u64, + + const vt: Bounded.VTable = .{ .nbytes = nbytes, .blobs = blobs, .release = release }; + + fn make(gpa: std.mem.Allocator, b: HostBounded) !Bounded { + const h = try gpa.create(HostBounded); + h.* = b; + h.gpa = gpa; + return .{ .ptr = h, .vtable = &vt }; + } + fn nbytes(p: *anyopaque) u64 { + const h: *HostBounded = @ptrCast(@alignCast(p)); + return h.measure; + } + fn blobs(p: *anyopaque, gpa: std.mem.Allocator) anyerror![]Blob { + const h: *HostBounded = @ptrCast(@alignCast(p)); + var b: [24]u8 = undefined; + std.mem.writeInt(u64, b[0..8], h.pos, .little); + std.mem.writeInt(u64, b[8..16], h.ds_rows, .little); + std.mem.writeInt(u64, b[16..24], h.stash_from, .little); + const out = try gpa.alloc(Blob, 1); + out[0] = .{ .name = try gpa.dupe(u8, "slot"), .bytes = try gpa.dupe(u8, &b) }; + return out; + } + fn release(p: *anyopaque) void { + const h: *HostBounded = @ptrCast(@alignCast(p)); + h.gpa.destroy(h); + } +}; + +/// A chat's ids (golden.py chat_ids): its own stream; a branch rewrites its last 100 tokens. +fn chatIds(out: []i32, chat: u32, branch: u32) void { + const n = out.len; + for (out, 0..) |*t, i| t.* = @intCast(chat * 100_000 + i); + if (branch > 0) { + const cut = n - 100; + for (out[cut..], 0..) |*t, i| t.* = @intCast(chat * 100_000 + 50_000 + branch * 1000 + i); + } +} + +/// A row's bytes: a function of the id at its first position (what a fresh prefill of the same prefix writes there). +fn rowByte(id: i32, pos: u64, fi: usize, i: usize) u8 { + return @truncate(@as(u64, @bitCast(@as(i64, id))) *% 31 +% pos *% 3 +% i *% 7 +% fi); +} + +const World = struct { + gpa: std.mem.Allocator, + g: Golden, + fams: []const Family, + pool: Pool, + host: sessions.HostPool, + disk: *sessions.Disk, + store: Store, + slots: [4]*Slot = undefined, + /// per slot: drafter.valid and the stash's first position (sessions_gpu.zig keeps the same) + valid: [4]u64 = @splat(0), + stash_from: [4]u64 = @splat(0), + names: std.AutoHashMapUnmanaged(u32, []const u8) = .empty, + arena: std.heap.ArenaAllocator, + + fn measure(w: *const World) sess.Measure { + const m = w.g.measure; + return .{ .rings = m.decoder_start, .window = m.window, .carries = m.carries, .head_dim = m.head_dim, .hidden = m.hidden, .hc = m.hc, .ds_blocks = m.ds_blocks, .replay = true }; + } + + /// Writes the rows of positions [from, to) of the slot's pages (each family's rows that start there). + fn write(w: *World, s: *const Slot, ids: []const i32, from: u64, to: u64) void { + const page = w.pool.page; + for (w.fams, 0..) |f, fi| { + var p = from - from % f.ratio + (if (from % f.ratio != 0) f.ratio else 0); + while (p < to) : (p += f.ratio) { + const pg = s.mapped()[@intCast(p / page)]; + if (f.split and !w.pool.owns(pg)) continue; + const v = w.host.pageOf(@intCast(fi), w.pool.familyPage(f, pg)); + const at: usize = @intCast((p % page) / f.ratio * f.row_bytes); + for (v[at..][0..f.row_bytes], 0..) |*b, i| b.* = rowByte(ids[@intCast(p)], p, fi, i); + } + } + } + + /// Every row below `pos` of the slot's pages holds what a fresh prefill of `ids` writes there. + fn check(w: *World, s: *const Slot, ids: []const i32, pos: u64) !void { + const page = w.pool.page; + for (w.fams, 0..) |f, fi| { + var p: u64 = 0; + while (p + f.ratio <= pos) : (p += f.ratio) { + const pg = s.mapped()[@intCast(p / page)]; + if (f.split and !w.pool.owns(pg)) continue; + const v = w.host.pageOf(@intCast(fi), w.pool.familyPage(f, pg)); + const at: usize = @intCast((p % page) / f.ratio * f.row_bytes); + for (v[at..][0..f.row_bytes], 0..) |b, i| if (b != rowByte(ids[@intCast(p)], p, fi, i)) { + std.debug.print("row at position {d} (family {s}) is not the prompt's\n", .{ p, f.name }); + return error.WrongRow; + }; + } + } + } + + fn nameOf(w: *World, id: u32) []const u8 { + return w.names.get(id) orelse "?"; + } + + fn sortNames(list: [][]const u8) void { + std.mem.sort([]const u8, list, {}, struct { + fn less(_: void, a: []const u8, b: []const u8) bool { + return std.mem.order(u8, a, b) == .lt; + } + }.less); + } + + /// The RAM tier's entries, the NVMe tier's files, the pool's pages == the golden's after this step. + fn expectState(w: *World, st: *const Step) !void { + const a = w.arena.allocator(); + var ram: std.ArrayList([]const u8) = .empty; + var disk: std.ArrayList([]const u8) = .empty; + for (w.store.index.entries.items, 0..) |e, id| if (e.live) { + if (e.ram) try ram.append(a, w.nameOf(@intCast(id))); + if (e.extra.file) try disk.append(a, w.nameOf(@intCast(id))); + }; + sortNames(ram.items); + sortNames(disk.items); + const j = struct { + fn of(x: std.mem.Allocator, l: []const []const u8) []const u8 { + return std.mem.join(x, " ", l) catch "?"; + } + }.of; + errdefer std.debug.print("step '{s}' {s} slot {d}: RAM {s} (reference {s}), NVMe {s} (reference {s}), available {d} (reference {d}), free {d} (reference {d})\n", .{ st.op, st.label, st.slot, j(a, ram.items), j(a, st.ram), j(a, disk.items), j(a, st.disk), w.pool.available(), st.available, w.pool.freePages(), st.free }); + try testing.expectEqual(st.ram.len, ram.items.len); + for (st.ram, ram.items) |x, y| try testing.expectEqualStrings(x, y); + try testing.expectEqual(st.disk.len, disk.items.len); + for (st.disk, disk.items) |x, y| try testing.expectEqualStrings(x, y); + try testing.expectEqual(st.available, w.pool.available()); + try testing.expectEqual(st.free, w.pool.freePages()); + } + + fn admit(w: *World, st: *const Step) !void { + const a = w.arena.allocator(); + const ids = try a.alloc(i32, @intCast(st.n)); + chatIds(ids, st.chat, st.branch); + const hit = try w.store.find(w.g.tag, ids); + var spills: std.ArrayList(u32) = .empty; + defer spills.deinit(w.gpa); + const keep: []const u32 = if (hit) |h| &.{h.id} else &.{}; + const plan = try sess.admit(&w.store, hit, st.n, st.max_new, w.g.capacity, keep, 0, &spills); + if (std.mem.eql(u8, st.op, "probe")) { + try testing.expectEqual(st.wait.?, plan.wait); + try testing.expectEqual(st.need, plan.need); + try testing.expectEqual(st.want, plan.want); + return; + } + errdefer std.debug.print("admit '{s}': tier {s} cached {d} need {d} want {d} spills {d}\n", .{ st.label, st.tier, st.cached, plan.need, plan.want, spills.items.len }); + try testing.expect(!plan.wait); + try testing.expectEqual(st.need, plan.need); + try testing.expectEqual(st.want, plan.want); + try testing.expectEqual(st.spills.len, spills.items.len); + var names: std.ArrayList([]const u8) = .empty; + for (spills.items) |id| try names.append(a, w.nameOf(id)); + sortNames(names.items); + for (st.spills, names.items) |x, y| try testing.expectEqualStrings(x, y); + // the leader's spills in its order, then the slot's reservation (reserve, reset, restore) + for (spills.items) |id| _ = try w.store.evict(id); + try w.store.settleAll(); + const s = w.slots[st.slot]; + try testing.expectEqual(@as(u32, 0), s.len); + s.reserve(plan.need); + w.valid[st.slot] = 0; + w.stash_from[st.slot] = 0; + var cached: u64 = 0; + var tier: []const u8 = "none"; + if (hit) |h| { + if (h.ram) { + const b = try w.store.restoreRam(h.id, s); + const hb: *HostBounded = @ptrCast(@alignCast(b.ptr)); + try testing.expectEqual(h.pos, hb.pos); + w.valid[st.slot] = hb.pos - hb.ds_rows; + w.stash_from[st.slot] = hb.stash_from; + tier = "ram"; + } else { + const j = try w.store.beginRestore(h.id, s); + var got = (try w.store.settle(j)).?; + defer got.deinit(w.gpa); + const b = got.blobs[0].bytes; + try testing.expectEqual(h.pos, std.mem.readInt(u64, b[0..8], .little)); + w.valid[st.slot] = h.pos - std.mem.readInt(u64, b[8..16], .little); + w.stash_from[st.slot] = std.mem.readInt(u64, b[16..24], .little); + tier = "disk"; + } + cached = h.pos; + try w.check(s, ids, cached); + } + try testing.expectEqualStrings(st.tier, tier); + try testing.expectEqual(st.cached, cached); + // the prompt to its replay point, the snapshot there, the rest of it, the reply + var at = cached; + if (sess.savePoint(st.n, cached)) |save_at| { + try s.ensure(save_at); + w.write(s, ids, at, save_at); + at = save_at; + const ds = sess.dsRows(save_at, w.valid[st.slot], w.g.measure.window); + const stash = sess.stashRows(save_at, w.stash_from[st.slot]); + const m = w.measure().bytes(save_at, stash, ds); + const want = st.saved orelse return error.ReferenceSavedNothing; + try testing.expectEqual(want.at, save_at); + try testing.expectEqual(want.ds_rows, ds); + try testing.expectEqual(want.nbytes, m); + const b = try HostBounded.make(w.gpa, .{ .gpa = w.gpa, .pos = save_at, .ds_rows = ds, .stash_from = save_at - stash, .measure = m }); + const id = try w.store.save(s, b, ids[0..@intCast(save_at)], w.g.tag, 1); + try testing.expectEqual(want.dup, id == null); + if (id) |x| try w.names.put(w.gpa, x, try std.fmt.allocPrint(a, "c{d}b{d}@{d}", .{ st.chat, st.branch, save_at })); + try w.store.settleAll(); + } else try testing.expect(st.saved == null); + try s.ensure(st.n - 1); + w.write(s, ids, at, st.n - 1); + try s.ensure(st.n + st.reply); + } +}; + +/// The golden's steps on one rank of `world`; every step must decide as the golden did (replicated or split alike). +fn replay(gpa: std.mem.Allocator, g: Golden, families: []const Family, world: u32, rank: u32) !void { + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + try tmp.dir.createDirPath(testing.io, "tier"); + const root = try tmp.dir.realPathFileAlloc(testing.io, "tier", gpa); + defer gpa.free(root); + + var w: World = .{ .gpa = gpa, .g = g, .fams = families, .pool = undefined, .host = undefined, .disk = undefined, .store = undefined, .arena = .init(gpa) }; + defer w.arena.deinit(); + defer w.names.deinit(gpa); + w.pool = try Pool.init(gpa, .{ .families = families, .page = g.page }, @as(u64, g.pages) * g.page, world, rank); + defer w.pool.deinit(); + w.host = try sessions.HostPool.init(gpa, &w.pool); + defer w.host.deinit(); + w.disk = try sessions.Disk.open(gpa, testing.io, .{ .root = root, .compat = @splat(9), .rank = rank, .world = world, .min_tokens = g.disk_min, .chunk = 1 << 20, .staging = 4, .lanes = 2, .direct = false }); + defer w.disk.close(); + // the golden's store: its RAM budget, no residency budget, no covered-entry or delta rules + w.store = try Store.init(gpa, testing.io, &w.pool, w.host.store(), w.disk, .{ .ram_bytes = g.ram_bytes, .skip_covered = false, .delta = false }); + defer w.store.deinit(); + for (&w.slots) |*s| s.* = try w.pool.newSlot(g.capacity); + try testing.expectEqual(@as(usize, g.slots), w.slots.len); + var spills: u32 = 0; + var hits: [2]u32 = .{ 0, 0 }; + for (g.steps) |*st| { + if (std.mem.eql(u8, st.op, "admit") or std.mem.eql(u8, st.op, "probe")) { + try w.admit(st); + spills += @intCast(st.spills.len); + if (std.mem.eql(u8, st.tier, "ram")) hits[0] += 1; + if (std.mem.eql(u8, st.tier, "disk")) hits[1] += 1; + } else if (std.mem.eql(u8, st.op, "finish")) { + _ = try w.slots[st.slot].releaseAll(); + } else return error.UnknownStep; + try w.expectState(st); + } + // the golden exercises what it claims: spills, both tiers' resumes, parks and drops + try testing.expect(spills >= 2 and hits[0] >= 2 and hits[1] >= 3); + try testing.expect(w.store.stats.parks >= 3 and w.store.stats.drops >= 2); +} + +fn golden(gpa: std.mem.Allocator, text: []const u8) !std.json.Parsed(Golden) { + const parsed = try std.json.parseFromSlice(Golden, gpa, text, .{ .ignore_unknown_fields = true }); + errdefer parsed.deinit(); + const g = parsed.value; + try testing.expectEqual(@as(u64, sess.slack), g.slack); + try testing.expectEqual(sess.row_bytes, g.measure.row_bytes); + try testing.expectEqual(sess.keep, g.measure.keep); + try testing.expectEqual(sess.lookback, g.measure.lookback); + return parsed; +} + +test "4 slots, interleaved resumes, spills, RAM trims and NVMe parks decide as the reference's store, pool and tier" { + const parsed = try golden(testing.allocator, golden_json); + defer parsed.deinit(); + try replay(testing.allocator, parsed.value, &fams, 1, 0); +} + +test "split KV over 2 ranks with 4 slots: each rank decides as kvsplit's store and pool, restoring only its own rows" { + const parsed = try golden(testing.allocator, split_golden_json); + defer parsed.deinit(); + try replay(testing.allocator, parsed.value, &split_fams, 2, 0); + try replay(testing.allocator, parsed.value, &split_fams, 2, 1); +} diff --git a/zig/src/families/deepseek_v41/kv/split.zig b/zig/src/families/deepseek_v41/kv/split.zig new file mode 100644 index 0000000000..b02e0c82da --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/split.zig @@ -0,0 +1,488 @@ +//! Split KV's row exchange: each rank sends its owned selected comp rows, one all-gather; bit-equal to replicated. + +const std = @import("std"); +const tp = @import("tp"); +const DevicePtr = tp.collective.DevicePtr; +const Stream = tp.collective.Stream; + +/// One selection's dense gather: [R, K] logical comp rows -> this rank's owned rows into send, and remapped tokens. +pub const DenseArgs = extern struct { + /// int32 [R, K] logical rows (-1: none) + sel: u64, + rows: u32, + k: u32, + /// int32 split page tables (Slot.localTableAt): one [pages] table, or row mode's stacked [S, pts] + table: u64, + pts: u32, + /// int32 [R]: each row's slot in the stacked tables (0: every row reads table row 0) + rslot: u64, + /// log2 of rows a page + psh: u32, + /// the family's local tensor and its row bytes + base: u64, + row_bytes: u32, + world: u32, + /// out: [R x K, row bytes] and int32 [R, K] + send: u64, + tok: u64, +}; + +/// One selection's packed gather (`Exchange.densePacked`): DenseArgs' fields at the same offsets, plus rank and lens. +pub const PackArgs = extern struct { + /// int32 [R, K] logical rows (-1: none) + sel: u64, + rows: u32, + k: u32, + /// int32 split page tables, as DenseArgs + table: u64, + pts: u32, + /// int32 [R] or 0, as DenseArgs + rslot: u64, + psh: u32, + /// this rank (sends the entries whose owner it is) + rank: u32, + base: u64, + row_bytes: u32, + world: u32, + /// out: [R x K, row bytes] (own rows first c_rank, list order), int32 [R, K] tokens, int32 [world] count x rb + send: u64, + tok: u64, + lens: u64, + + pub fn of(a: DenseArgs, rank: u32, lens: u64) PackArgs { + return .{ .sel = a.sel, .rows = a.rows, .k = a.k, .table = a.table, .pts = a.pts, .rslot = a.rslot, .psh = a.psh, .rank = rank, .base = a.base, .row_bytes = a.row_bytes, .world = a.world, .send = a.send, .tok = a.tok, .lens = lens }; + } + + pub fn dense(p: PackArgs) DenseArgs { + return .{ .sel = p.sel, .rows = p.rows, .k = p.k, .table = p.table, .pts = p.pts, .rslot = p.rslot, .psh = p.psh, .base = p.base, .row_bytes = p.row_bytes, .world = p.world, .send = p.send, .tok = p.tok }; + } +}; + +/// TF_DSV41_KV_SPLIT_DEDUP's pack (kvsplit.cu dedup_*): a slot's row sent once, its entries read one place. +pub const DedupArgs = extern struct { + p: PackArgs, + /// int32 [map rows]: key slot x (pts << psh) + logical row + map: u64, + /// int32 [R x K] each, and uint32 [blocks of 256 entries, max_world] + first: u64, + place: u64, + bcnt: u64, +}; + +/// The dedup knob's device scratch (Kv's), and what its map covers. +pub const Dedup = struct { + map: u64, + map_rows: u64, + first: u64, + place: u64, + bcnt: u64, + /// entries the scratch takes (R x K) + entries: u64, + /// slots the map holds (row mode's rslot values below it) + slots: u32, + + /// A row-mode window (one stream's exchanges) whose keys fit the map and entries the scratch: alike on every rank. + pub fn covers(d: Dedup, a: PackArgs) bool { + const n = @as(u64, a.rows) * a.k; + const keys = @as(u64, d.slots) * (@as(u64, a.pts) << @intCast(a.psh)); + return a.rslot != 0 and n > 0 and n <= d.entries and keys <= d.map_rows; + } + + pub fn args(d: Dedup, a: PackArgs) DedupArgs { + return .{ .p = a, .map = d.map, .first = d.first, .place = d.place, .bcnt = d.bcnt }; + } +}; + +/// TF_DSV41_KV_SPLIT_DEDUP=1: packed exchanges send each slot's unique rows (default off). +pub fn dedupOn() bool { + const v = std.mem.span(std.c.getenv("TF_DSV41_KV_SPLIT_DEDUP") orelse return false); + return std.mem.eql(u8, std.mem.trim(u8, v, " \t"), "1"); +} + +/// TF_DSV41_KV_SPLIT_EARLY=1: an index layer's exchange issued right after its selection (block.zig; default off). +pub fn earlyOn() bool { + const v = std.mem.span(std.c.getenv("TF_DSV41_KV_SPLIT_EARLY") orelse return false); + return std.mem.eql(u8, std.mem.trim(u8, v, " \t"), "1"); +} + +/// A prefill union by history (`Exchange.history`): owner o's rows of the mapped pages at o x M, in page order. +pub const HistArgs = extern struct { + /// int32 [R, K] logical rows (-1: none) + sel: u64, + rows: u32, + k: u32, + /// int32 [pages] split page table (Slot.localTableAt) + table: u64, + /// the slot's mapped pages (Slot.len): every row a selection names is below pages << psh + pages: u32, + psh: u32, + rank: u32, + world: u32, + /// rows an owner (historyRows): owner o's row list fills recv rows [o M, o M + M) + m: u32, + row_bytes: u32, + base: u64, + /// out: [M, row bytes] (this rank's rows in page order, the padding unspecified) and int32 [R, K] tokens + send: u64, + tok: u64, +}; + +/// Rows an owner in a history union of `pages` mapped pages: owner 0's pages (the most), whole. +pub fn historyRows(pages: u32, psh: u32, world: u32) u32 { + return ((pages + world - 1) / world) << @intCast(psh); +} + +/// A row's place in its owner's history list: its page's turn among the owner's pages, then its row in it. +pub fn historyIndex(t: u32, psh: u32, world: u32) u32 { + const mask = (@as(u32, 1) << @intCast(psh)) - 1; + return (((t >> @intCast(psh)) / world) << @intCast(psh)) | (t & mask); +} + +/// TF_DSV41_KV_SPLIT_HISTORY=1: a prefill union under the cap sends the owned rows; no plan, no host sync. +pub fn historyOn() bool { + const v = std.mem.span(std.c.getenv("TF_DSV41_KV_SPLIT_HISTORY") orelse return false); + return std.mem.eql(u8, std.mem.trim(u8, v, " \t"), "1"); +} + +comptime { + std.debug.assert(@sizeOf(DenseArgs) == 80 and @sizeOf(PackArgs) == 88 and @sizeOf(DedupArgs) == 120 and @sizeOf(HistArgs) == 72); + std.debug.assert(@offsetOf(PackArgs, "rank") == 44 and @offsetOf(PackArgs, "base") == @offsetOf(DenseArgs, "base") and @offsetOf(PackArgs, "lens") == 80); +} + +/// The device work around the all-gather: dense gather + remap and a gather by row list (HostKernels below). +pub const Kernels = struct { + ptr: *anyopaque, + vtable: *const VTable, + + pub const VTable = struct { + dense: *const fn (ptr: *anyopaque, a: DenseArgs, stream: Stream) anyerror!void, + /// Packed gather: tokens, owned rows into send[0:c_rank], lens (one launch, W <= 8). Null: densePacked refuses. + pack: ?*const fn (ptr: *anyopaque, a: PackArgs, stream: Stream) anyerror!void = null, + /// send[i] = base row phys[i] (uint32 [n]) + gather: *const fn (ptr: *anyopaque, base: u64, row_bytes: u32, phys: u64, n: u32, send: u64, stream: Stream) anyerror!void, + /// The deduplicated pack (kvsplit.cu dedup_*: four launches); null: a Kernels without it (packs without dedup). + dedup: ?*const fn (ptr: *anyopaque, a: DedupArgs, stream: Stream) anyerror!void = null, + /// The history union's gather and tokens (kvsplit.cu hist_*: two launches); null: unions plan on the host. + history: ?*const fn (ptr: *anyopaque, a: HistArgs, stream: Stream) anyerror!void = null, + }; +}; + +/// The kernels on host memory (host pointers): the reference the device kernels must equal, and the host ranks' path. +pub const HostKernels = struct { + pub fn kernels(h: *HostKernels) Kernels { + return .{ .ptr = h, .vtable = &.{ .dense = dense, .gather = gather, .pack = pack, .dedup = dedup, .history = history } }; + } + + /// hist_gather / hist_tok: row i of this rank's list is its page rank + W (i >> psh); the tokens by historyIndex. + fn history(_: *anyopaque, a: HistArgs, _: Stream) anyerror!void { + const sel: [*]const i32 = @ptrFromInt(a.sel); + const table: [*]const i32 = @ptrFromInt(a.table); + const base: [*]const u8 = @ptrFromInt(a.base); + const send: [*]u8 = @ptrFromInt(a.send); + const tok: [*]i32 = @ptrFromInt(a.tok); + const mask = (@as(u32, 1) << @intCast(a.psh)) - 1; + for (0..a.m) |i| { + const lp = a.rank + a.world * (@as(u32, @intCast(i)) >> @intCast(a.psh)); + if (lp >= a.pages) continue; + const phys = (@as(usize, @intCast(table[lp])) << @intCast(a.psh)) | (i & mask); + @memcpy(send[i * a.row_bytes ..][0..a.row_bytes], base[phys * a.row_bytes ..][0..a.row_bytes]); + } + for (0..@as(usize, a.rows) * a.k) |i| { + if (sel[i] < 0) { + tok[i] = -1; + continue; + } + const t: u32 = @intCast(sel[i]); + if (t >> @intCast(a.psh) >= a.pages) return error.RowOutsideTable; + tok[i] = @intCast(ownerOf(t, a.psh, a.world) * a.m + historyIndex(t, a.psh, a.world)); + } + } + + /// dedup_*'s steps in order on host memory: the first entry of each key, places among each owner's first entries. + fn dedup(_: *anyopaque, d: DedupArgs, _: Stream) anyerror!void { + const a = d.p; + if (a.world < 1 or a.world > max_world or a.rank >= a.world) return error.BadWorld; + const n = @as(usize, a.rows) * a.k; + const sel: [*]const i32 = @ptrFromInt(a.sel); + const tab: [*]const i32 = @ptrFromInt(a.table); + const rslot: ?[*]const i32 = if (a.rslot == 0) null else @ptrFromInt(a.rslot); + const map: [*]i32 = @ptrFromInt(d.map); + const first: [*]i32 = @ptrFromInt(d.first); + const place: [*]i32 = @ptrFromInt(d.place); + const base: [*]const u8 = @ptrFromInt(a.base); + const send: [*]u8 = @ptrFromInt(a.send); + const tok: [*]i32 = @ptrFromInt(a.tok); + const lens: [*]i32 = @ptrFromInt(a.lens); + const mask = (@as(u32, 1) << @intCast(a.psh)) - 1; + const span = @as(usize, a.pts) << @intCast(a.psh); + const key = struct { + fn of(rs: ?[*]const i32, k: u32, sp: usize, i: usize, t: i32) usize { + return (if (rs) |r| @as(usize, @intCast(r[i / k])) * sp else 0) + @as(usize, @intCast(t)); + } + }.of; + for (0..n) |i| if (sel[i] >= 0) { + const at = &map[key(rslot, a.k, span, i, sel[i])]; + at.* = @min(at.*, @as(i32, @intCast(i))); + }; + var count: [max_world]u32 = @splat(0); + for (0..n) |i| { + first[i] = if (sel[i] < 0) -1 else map[key(rslot, a.k, span, i, sel[i])]; + if (first[i] != @as(i32, @intCast(i))) continue; + const t: u32 = @intCast(sel[i]); + const o = ownerOf(t, a.psh, a.world); + place[i] = @intCast(count[o]); + if (o == a.rank) { + const s: usize = if (rslot) |rs| @intCast(rs[i / a.k]) else 0; + const local: u32 = @intCast(tab[s * a.pts + (t >> @intCast(a.psh))]); + const phys = (@as(usize, local) << @intCast(a.psh)) | (t & mask); + @memcpy(send[count[o] * a.row_bytes ..][0..a.row_bytes], base[phys * a.row_bytes ..][0..a.row_bytes]); + } + count[o] += 1; + } + for (0..n) |i| { + if (first[i] < 0) { + tok[i] = -1; + continue; + } + const t: u32 = @intCast(sel[i]); + tok[i] = @intCast(ownerOf(t, a.psh, a.world) * n + @as(usize, @intCast(place[@intCast(first[i])]))); + } + for (0..n) |i| if (first[i] == @as(i32, @intCast(i))) { + map[key(rslot, a.k, span, i, sel[i])] = std.math.maxInt(i32); + }; + for (0..a.world) |o| lens[o] = @intCast(count[o] * a.row_bytes); + } + + /// One pass in list order: an owner's running count is each entry's place (pack_kernel's prefix sums). + fn pack(_: *anyopaque, a: PackArgs, _: Stream) anyerror!void { + if (a.world < 1 or a.world > max_world or a.rank >= a.world) return error.BadWorld; + const n = @as(usize, a.rows) * a.k; + const sel: [*]const i32 = @ptrFromInt(a.sel); + const tab: [*]const i32 = @ptrFromInt(a.table); + const rslot: ?[*]const i32 = if (a.rslot == 0) null else @ptrFromInt(a.rslot); + const base: [*]const u8 = @ptrFromInt(a.base); + const send: [*]u8 = @ptrFromInt(a.send); + const tok: [*]i32 = @ptrFromInt(a.tok); + const lens: [*]i32 = @ptrFromInt(a.lens); + const mask = (@as(u32, 1) << @intCast(a.psh)) - 1; + var count: [max_world]u32 = @splat(0); + for (0..n) |i| { + if (sel[i] < 0) { + tok[i] = -1; + continue; + } + const t: u32 = @intCast(sel[i]); + const o = ownerOf(t, a.psh, a.world); + const at = count[o]; + count[o] += 1; + tok[i] = @intCast(o * n + at); + if (o != a.rank) continue; + const s: usize = if (rslot) |rs| @intCast(rs[i / a.k]) else 0; + const local: u32 = @intCast(tab[s * a.pts + (t >> @intCast(a.psh))]); + const phys = (@as(usize, local) << @intCast(a.psh)) | (t & mask); + @memcpy(send[at * a.row_bytes ..][0..a.row_bytes], base[phys * a.row_bytes ..][0..a.row_bytes]); + } + for (0..a.world) |o| lens[o] = @intCast(count[o] * a.row_bytes); + } + + fn dense(_: *anyopaque, a: DenseArgs, _: Stream) anyerror!void { + const n = @as(usize, a.rows) * a.k; + const sel: [*]const i32 = @ptrFromInt(a.sel); + const tab: [*]const i32 = @ptrFromInt(a.table); + const rslot: ?[*]const i32 = if (a.rslot == 0) null else @ptrFromInt(a.rslot); + const base: [*]const u8 = @ptrFromInt(a.base); + const send: [*]u8 = @ptrFromInt(a.send); + const tok: [*]i32 = @ptrFromInt(a.tok); + const mask = (@as(u32, 1) << @intCast(a.psh)) - 1; + for (0..n) |i| { + const t: u32 = @intCast(@max(sel[i], 0)); + const row = i / a.k; + const s: usize = if (rslot) |rs| @intCast(rs[row]) else 0; + const local: u32 = @intCast(tab[s * a.pts + (t >> @intCast(a.psh))]); + const phys = (@as(usize, local) << @intCast(a.psh)) | (t & mask); + @memcpy(send[i * a.row_bytes ..][0..a.row_bytes], base[phys * a.row_bytes ..][0..a.row_bytes]); + tok[i] = if (sel[i] < 0) -1 else @intCast(ownerOf(t, a.psh, a.world) * n + i); + } + } + + fn gather(_: *anyopaque, base: u64, rb: u32, phys: u64, n: u32, send: u64, _: Stream) anyerror!void { + const ph: [*]const u32 = @ptrFromInt(phys); + const b: [*]const u8 = @ptrFromInt(base); + const s: [*]u8 = @ptrFromInt(send); + for (0..n) |i| @memcpy(s[i * rb ..][0..rb], b[@as(usize, ph[i]) * rb ..][0..rb]); + } +}; + +/// The rank that stores logical row t of a split family: its logical page's residue. +pub fn ownerOf(t: u32, psh: u32, world: u32) u32 { + return (t >> @intCast(psh)) % world; +} + +/// Send and receive bytes of a dense exchange of R x K entries. +pub fn denseBytes(rows: u32, k: u32, row_bytes: u32, world: u32) struct { send: u64, recv: u64 } { + const s = @as(u64, rows) * k * row_bytes; + return .{ .send = s, .recv = s * world }; +} + +/// A prefill union block: rows [a, b), m rows an owner (padded), this rank's local rows to send, the block's tokens. +pub const Block = struct { + a: u32, + b: u32, + m: u32, + phys: []u32, + tokens: []i32, + + pub fn deinit(blk: *Block, gpa: std.mem.Allocator) void { + gpa.free(blk.phys); + gpa.free(blk.tokens); + } +}; + +pub const UnionIn = struct { + /// [rows, k] logical rows (-1: none), host + sel: []const i32, + k: u32, + /// the slot's split table (local page by logical page), host + table: []const u32, + psh: u32, + world: u32, + rank: u32, + row_bytes: u32, + /// send + receive bytes a block may take (TF_DSV41_KV_SPLIT_UNION_MIB) + cap: u64, +}; + +/// Ranks a split exchange plans for (the session pool's bound: sessions.pool.max_world). +pub const max_world = 8; + +/// Plans a segment's union exchange: blocks of rows whose (W + 1) x M rows fit the cap; O(entries + rows), no sort. +pub fn planUnion(gpa: std.mem.Allocator, in: UnionIn, out: *std.ArrayList(Block)) !void { + if (in.world < 1 or in.world > max_world or in.rank >= in.world) return error.BadWorld; + const rows: u32 = @intCast(in.sel.len / in.k); + // a row's mark, then its index + 1 in its owner's list (every row a selection names is in the table's pages) + const seen = try gpa.alloc(u32, in.table.len << @intCast(in.psh)); + defer gpa.free(seen); + try planRange(gpa, in, seen, 0, rows, out); +} + +fn planRange(gpa: std.mem.Allocator, in: UnionIn, seen: []u32, a: u32, b: u32, out: *std.ArrayList(Block)) !void { + const part = in.sel[@as(usize, a) * in.k .. @as(usize, b) * in.k]; + @memset(seen, 0); + for (part) |t| if (t >= 0) { + if (t >= seen.len) return error.RowOutsideTable; + seen[@intCast(t)] = 1; + }; + // each owner's rows in ascending order (the sorted unique union, partitioned by owner): a row's index is a count + var sizes: [max_world]u32 = @splat(0); + for (seen, 0..) |*x, t| if (x.* != 0) { + const r = ownerOf(@intCast(t), in.psh, in.world); + sizes[r] += 1; + x.* = sizes[r]; + }; + var m: u32 = 1; + for (sizes[0..in.world]) |sz| m = @max(m, sz); + if (@as(u64, in.world + 1) * m * in.row_bytes > in.cap and b - a > 1) { + const h = a + (b - a) / 2; + try planRange(gpa, in, seen, a, h, out); + return planRange(gpa, in, seen, h, b, out); + } + var blk: Block = .{ .a = a, .b = b, .m = m, .phys = try gpa.alloc(u32, m), .tokens = try gpa.alloc(i32, part.len) }; + errdefer blk.deinit(gpa); + @memset(blk.phys, 0); // padding: any stored row + const mask = (@as(u32, 1) << @intCast(in.psh)) - 1; + for (seen, 0..) |x, t| if (x != 0 and ownerOf(@intCast(t), in.psh, in.world) == in.rank) { + const u: u32 = @intCast(t); + blk.phys[x - 1] = (in.table[u >> @intCast(in.psh)] << @intCast(in.psh)) | (u & mask); + }; + for (part, blk.tokens) |t, *o| { + if (t < 0) { + o.* = -1; + continue; + } + const u: u32 = @intCast(t); + o.* = @intCast(ownerOf(u, in.psh, in.world) * m + seen[u] - 1); + } + try out.append(gpa, blk); +} + +/// TF_DSV41_KV_SPLIT_COMPACT: off (0, dense), on (1: packed where all transports take lengths and it fits), force. +pub const Compact = enum { off, on, force }; + +pub const compact_env = "TF_DSV41_KV_SPLIT_COMPACT"; + +pub fn parseCompact(text: []const u8) error{BadCompactMode}!Compact { + const v = std.mem.trim(u8, text, " \t"); + const eq = std.ascii.eqlIgnoreCase; + if (v.len == 0 or eq(v, "0") or eq(v, "off") or eq(v, "no") or eq(v, "false")) return .off; + if (eq(v, "1") or eq(v, "on") or eq(v, "yes") or eq(v, "true")) return .on; + if (eq(v, "force")) return .force; + return error.BadCompactMode; +} + +/// The knob from the environment (unset: off). +pub fn compactMode() error{BadCompactMode}!Compact { + const raw = std.mem.span(std.c.getenv(compact_env) orelse return .off); + return parseCompact(raw) catch |e| { + std.log.err("[tensorfold] {s}={s}: 0 / 1 / force", .{ compact_env, raw }); + return e; + }; +} + +/// The exchange itself: kernels to fill the send buffer, the communicator's all-gather to fill the receive buffer. +pub const Exchange = struct { + comm: tp.Collective, + kernels: Kernels, + compact: Compact = .off, + /// Every rank's transport takes device lengths (`Collective.varAgreed`, asked once at `init` when compact is on). + var_ok: bool = false, + /// TF_DSV41_KV_SPLIT_DEDUP's scratch (null: off) + dedup: ?Dedup = null, + + /// An exchange under `mode`; every rank calls it alike outside graphs (`on` asks the communicator once). + pub fn init(comm: tp.Collective, kernels: Kernels, mode: Compact) !Exchange { + return .{ .comm = comm, .kernels = kernels, .compact = mode, .var_ok = mode == .on and try comm.varAgreed() }; + } + + /// Whether a dense exchange of `nbytes`-byte shards (R x K x row bytes) goes packed: the same answer on every rank. + pub fn packs(x: Exchange, nbytes: u64) bool { + return x.compact == .force or (x.compact == .on and x.var_ok and x.comm.varFits(@intCast(nbytes))); + } + + /// The window's exchange as the knob says: densePacked where `packs`, else dense (a.lens unused then). + pub fn window(x: Exchange, a: PackArgs, recv: DevicePtr, stream: Stream) !void { + if (x.packs(@as(u64, a.rows) * a.k * a.row_bytes)) { + if (x.dedup) |d| if (x.kernels.vtable.dedup) |f| if (d.covers(a)) { + try f(x.kernels.ptr, d.args(a), stream); + return x.comm.allGatherV(a.send, recv, @as(usize, a.rows) * a.k * a.row_bytes, a.lens, .u8, stream); + }; + return x.densePacked(a, recv, stream); + } + return x.dense(a.dense(), recv, stream); + } + + /// Packed: send (own c_rank rows first), tokens, lens by one kernel, then recv [W, R x K, rb]; graph-capturable. + pub fn densePacked(x: Exchange, a: PackArgs, recv: DevicePtr, stream: Stream) !void { + const f = x.kernels.vtable.pack orelse return error.Unsupported; + try f(x.kernels.ptr, a, stream); + try x.comm.allGatherV(a.send, recv, @as(usize, a.rows) * a.k * a.row_bytes, a.lens, .u8, stream); + } + + /// Dense: send [R x K, rb] gathered, tokens remapped, then recv [W, R x K, rb] (graph-capturable). + pub fn dense(x: Exchange, a: DenseArgs, recv: DevicePtr, stream: Stream) !void { + try x.kernels.vtable.dense(x.kernels.ptr, a, stream); + try x.comm.allGather(a.send, recv, @as(usize, a.rows) * a.k * a.row_bytes, .u8, stream); + } + + /// A history union: this rank's M rows and the tokens (two kernels), then recv [W, M, rb]; no host sync. + pub fn history(x: Exchange, a: HistArgs, recv: DevicePtr, stream: Stream) !void { + const f = x.kernels.vtable.history orelse return error.Unsupported; + try f(x.kernels.ptr, a, stream); + try x.comm.allGather(a.send, recv, @as(usize, a.m) * a.row_bytes, .u8, stream); + } + + /// One union block: this rank's M rows (phys: the block's list on the device) to send, recv [W, M, rb]. + pub fn unionBlock(x: Exchange, base: u64, row_bytes: u32, phys: DevicePtr, m: u32, send: DevicePtr, recv: DevicePtr, stream: Stream) !void { + try x.kernels.vtable.gather(x.kernels.ptr, base, row_bytes, phys, m, send, stream); + try x.comm.allGather(send, recv, @as(usize, m) * row_bytes, .u8, stream); + } +}; diff --git a/zig/src/families/deepseek_v41/kv/split_gpu.zig b/zig/src/families/deepseek_v41/kv/split_gpu.zig new file mode 100644 index 0000000000..447205c702 --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/split_gpu.zig @@ -0,0 +1,548 @@ +//! tf-kv-split-test KVSPLIT_FATBIN: split KV exchange on real GPUs, a process a rank; exit 0: rows and tokens equal. + +const std = @import("std"); +const cuda = @import("cuda"); +const tp = @import("tp"); +const sessions = @import("sessions"); +const split = @import("split.zig"); +const DevicePool = @import("device.zig").DevicePool; + +const DeviceKernels = @import("split_gpu_kernels.zig").DeviceKernels; + +/// The replicated pool's row t of slot s in family f, byte i (8-byte words, so a whole row is filled fast). +fn word(f: u64, s: u64, t: u64, i: u64) u64 { + return (f << 56) ^ (s << 48) ^ (t << 20) ^ (i *% 0x9E3779B97F4A7C15); +} + +const page = 256; +const fams = [_]sessions.Family{ + .{ .name = "comp.2", .ratio = 2, .row_bytes = 584, .split = true }, + .{ .name = "comp.20", .ratio = 1, .row_bytes = 584, .split = true }, +}; + +const Rank = struct { + gpa: std.mem.Allocator, + d: *const cuda.Driver, + comm: tp.Collective, + stream: cuda.Stream, + pool: *sessions.Pool, + dp: *DevicePool, + x: split.Exchange, + /// TF_DSV41_KV_SPLIT_COMPACT=force over the same kernels + xp: split.Exchange, + lens: [3]u64, + bad: u64 = 0, + checks: u64 = 0, + shown: u32 = 0, + blocks: u32 = 0, + /// each section's mismatches and checks a family (`run`) + tally: [std.enums.values(Section).len][2][2]u64 = std.mem.zeroes([std.enums.values(Section).len][2][2]u64), + + const Section = enum { dense_eager, dense_graph, packed_vs_dense, dedup, unions, history }; + + /// One section's call; its mismatches and checks land in the tally under (section, family). + fn run(r: *Rank, sec: Section, fi: u32, comptime f: anytype, args: anytype) !void { + const b0, const c0 = .{ r.bad, r.checks }; + _ = try @call(.auto, f, .{r} ++ args); + const t = &r.tally[@backingInt(sec)][fi]; + t[0] += r.bad - b0; + t[1] += r.checks - c0; + } + + fn rowsOf(r: *const Rank, fi: usize, s: usize) u32 { + return @intCast(r.lens[s] / fams[fi].ratio); + } + + /// Selections for R rows (row r on slot r % 3), K entries, some -1; the same on every rank (same seed). + fn select(r: *const Rank, rnd: std.Random, fi: usize, R: u32, K: u32, sel: []i32, rslot: []i32) void { + for (rslot[0..R], 0..) |*s, i| s.* = @intCast(i % 3); + for (sel[0 .. R * K], 0..) |*t, i| { + const n = r.rowsOf(fi, @intCast(rslot[i / K])); + t.* = if (rnd.uintLessThan(u32, 16) == 0) -1 else @intCast(rnd.uintLessThan(u32, n)); + } + } + + /// Received rows and tokens against the reference: tok = owner*n + i, row = the replicated pool's. + fn check(r: *Rank, fi: usize, sel: []const i32, rslot: []const i32, K: u32, tok: []const i32, recv: []const u8, psh: u32) void { + const n: i64 = @intCast(sel.len); + for (sel, tok, 0..) |t, o, i| { + r.checks += 1; + if (t < 0) { + r.bad += @intFromBool(o != -1); + continue; + } + const owner: i64 = split.ownerOf(@intCast(t), psh, 2); + if (o != owner * n + @as(i64, @intCast(i))) { + r.bad += 1; + continue; + } + const row = std.mem.bytesAsSlice(u64, recv[@as(usize, @intCast(o)) * 584 ..][0..584]); + const before = r.bad; + for (row, 0..) |w, j| r.bad += @intFromBool(w != word(fi, @intCast(rslot[i / K]), @intCast(t), j)); + if (r.bad != before and r.shown < 6) { + r.shown += 1; + std.debug.print("MISMATCH fam {d} slot {d} entry {d} t {d} owner {d}: got {x} {x}, want {x} {x}\n", .{ fi, rslot[i / K], i, t, owner, row[0], row[1], word(fi, @intCast(rslot[i / K]), @intCast(t), 0), word(fi, @intCast(rslot[i / K]), @intCast(t), 1) }); + } + } + } + + fn dense(r: *Rank, rnd: std.Random, fi: u32, R: u32, graphs: bool) !void { + const K = 512; + const n = R * K; + const g = r.gpa; + const sel = try g.alloc(i32, n); + defer g.free(sel); + const rslot = try g.alloc(i32, R); + defer g.free(rslot); + const tok = try g.alloc(i32, n); + defer g.free(tok); + const recv_h = try g.alloc(u8, 2 * n * 584); + defer g.free(recv_h); + var dsel = try cuda.DeviceBuffer.alloc(r.d, n * 4); + defer dsel.free(); + var dslot = try cuda.DeviceBuffer.alloc(r.d, R * 4); + defer dslot.free(); + var dtok = try cuda.DeviceBuffer.alloc(r.d, n * 4); + defer dtok.free(); + var send = try cuda.DeviceBuffer.alloc(r.d, n * 584); + defer send.free(); + var recv = try cuda.DeviceBuffer.alloc(r.d, 2 * n * 584); + defer recv.free(); + const v = r.dp.view(fi, 0); + const a: split.DenseArgs = .{ .sel = dsel.ptr, .rows = R, .k = K, .table = v.pt, .pts = @intCast(v.pts), .rslot = dslot.ptr, .psh = @intCast(v.psh), .base = v.cv, .row_bytes = 584, .world = 2, .send = send.ptr, .tok = dtok.ptr }; + var exec: ?cuda.graph.Exec = null; + defer if (exec) |*e| e.deinit(); + if (graphs) { + try cuda.graph.beginCapture(r.stream, .global); + try r.x.dense(a, recv.ptr, r.stream.handle); + var gr = try cuda.graph.endCapture(r.stream); + defer gr.deinit(); + exec = try gr.instantiate(); + } + for (0..if (graphs) 3 else 1) |_| { + r.select(rnd, fi, R, K, sel, rslot); + try dsel.upload(0, std.mem.sliceAsBytes(sel)); + try dslot.upload(0, std.mem.sliceAsBytes(rslot)); + if (exec) |e| try e.launchOn(r.stream) else try r.x.dense(a, recv.ptr, r.stream.handle); + try r.stream.synchronize(); + try dtok.download(0, std.mem.sliceAsBytes(tok)); + try recv.download(0, recv_h); + r.check(fi, sel, rslot, K, tok, recv_h, @intCast(v.psh)); + } + } + + /// Packed vs dense in one captured graph, eager then replayed; every 3rd round all entries on one residue. + fn packedVsDense(r: *Rank, rnd: std.Random, fi: u32, R: u32) !void { + const K = 512; + const n = R * K; + const g = r.gpa; + const sel = try g.alloc(i32, n); + defer g.free(sel); + const rslot = try g.alloc(i32, R); + defer g.free(rslot); + const tok_d = try g.alloc(i32, n); + defer g.free(tok_d); + const tok_p = try g.alloc(i32, n); + defer g.free(tok_p); + const rd = try g.alloc(u8, 2 * n * 584); + defer g.free(rd); + const rp = try g.alloc(u8, 2 * n * 584); + defer g.free(rp); + var dsel = try cuda.DeviceBuffer.alloc(r.d, n * 4); + defer dsel.free(); + var dslot = try cuda.DeviceBuffer.alloc(r.d, R * 4); + defer dslot.free(); + var dtok_d = try cuda.DeviceBuffer.alloc(r.d, n * 4); + defer dtok_d.free(); + var dtok_p = try cuda.DeviceBuffer.alloc(r.d, n * 4); + defer dtok_p.free(); + var dlens = try cuda.DeviceBuffer.alloc(r.d, 2 * 4); + defer dlens.free(); + var send_d = try cuda.DeviceBuffer.alloc(r.d, n * 584); + defer send_d.free(); + var send_p = try cuda.DeviceBuffer.alloc(r.d, n * 584); + defer send_p.free(); + var recv_d = try cuda.DeviceBuffer.alloc(r.d, 2 * n * 584); + defer recv_d.free(); + var recv_p = try cuda.DeviceBuffer.alloc(r.d, 2 * n * 584); + defer recv_p.free(); + const v = r.dp.view(fi, 0); + const psh: u32 = @intCast(v.psh); + const a: split.DenseArgs = .{ .sel = dsel.ptr, .rows = R, .k = K, .table = v.pt, .pts = @intCast(v.pts), .rslot = dslot.ptr, .psh = psh, .base = v.cv, .row_bytes = 584, .world = 2, .send = send_d.ptr, .tok = dtok_d.ptr }; + var pa = split.PackArgs.of(a, r.comm.rank(), dlens.ptr); + pa.send = send_p.ptr; + pa.tok = dtok_p.ptr; + if (!r.xp.packs(@as(u64, n) * 584)) return error.ForceDoesNotPack; + try cuda.graph.beginCapture(r.stream, .global); + try r.x.dense(a, recv_d.ptr, r.stream.handle); + try r.xp.window(pa, recv_p.ptr, r.stream.handle); + var gr = try cuda.graph.endCapture(r.stream); + defer gr.deinit(); + var exec = try gr.instantiate(); + defer exec.deinit(); + for (0..4) |round| { + r.select(rnd, fi, R, K, sel, rslot); + if (round % 3 == 2) for (sel) |*t| if (t.* >= 0 and split.ownerOf(@intCast(t.*), psh, 2) != 0) { + t.* = if (t.* >= @as(i32, 1) << @intCast(psh)) t.* - (@as(i32, 1) << @intCast(psh)) else -1; // the page before: residue 0 + }; + try dsel.upload(0, std.mem.sliceAsBytes(sel)); + try dslot.upload(0, std.mem.sliceAsBytes(rslot)); + try recv_p.fill8(0xEE, r.stream.handle); // past each owner's rows: never read + if (round == 0) { + try r.x.dense(a, recv_d.ptr, r.stream.handle); + try r.xp.densePacked(pa, recv_p.ptr, r.stream.handle); + } else try exec.launchOn(r.stream); + try r.stream.synchronize(); + try dtok_d.download(0, std.mem.sliceAsBytes(tok_d)); + try dtok_p.download(0, std.mem.sliceAsBytes(tok_p)); + try recv_d.download(0, rd); + try recv_p.download(0, rp); + var lens: [2]i32 = undefined; + try dlens.download(0, std.mem.asBytes(&lens)); + r.check(fi, sel, rslot, K, tok_d, rd, psh); + var count: [2]u32 = .{ 0, 0 }; + for (sel, tok_d, tok_p, 0..) |t, od, op, i| { + r.checks += 1; + if (t < 0) { + r.bad += @intFromBool(op != -1); + continue; + } + const o = split.ownerOf(@intCast(t), psh, 2); + const want: i32 = @intCast(o * n + count[o]); + count[o] += 1; + if (op != want or od < 0) { + r.bad += 1; + continue; + } + const before = r.bad; + const x = std.mem.bytesAsSlice(u64, rd[@as(usize, @intCast(od)) * 584 ..][0..584]); + const y = std.mem.bytesAsSlice(u64, rp[@as(usize, @intCast(op)) * 584 ..][0..584]); + for (x, y, 0..) |p, q, j| r.bad += @intFromBool(p != q or q != word(fi, @intCast(rslot[i / K]), @intCast(t), j)); + if (r.bad != before and r.shown < 6) { + r.shown += 1; + std.debug.print("PACKED MISMATCH fam {d} R {d} round {d} entry {d} t {d} owner {d} place {d}\n", .{ fi, R, round, i, t, o, op }); + } + } + for (0..2) |o| r.bad += @intFromBool(lens[o] != @as(i32, @intCast(count[o] * 584))); + if (round % 3 == 2) r.bad += @intFromBool(lens[1] != 0); + } + } + + /// Verify-like selections: a slot's rows share one base list, `fresh` in 16 entries replaced. + fn verifyLike(r: *const Rank, rnd: std.Random, fi: usize, R: u32, K: u32, fresh: u32, sel: []i32, rslot: []i32) void { + for (rslot[0..R], 0..) |*s, i| s.* = @intCast(i * 3 / R); + var base: [3][512]i32 = undefined; + for (&base, 0..) |*b, s| for (b[0..K]) |*t| { + t.* = @intCast(rnd.uintLessThan(u32, r.rowsOf(fi, s))); + }; + for (sel[0 .. R * K], 0..) |*t, i| { + const s: usize = @intCast(rslot[i / K]); + t.* = if (rnd.uintLessThan(u32, 16) < fresh) @intCast(rnd.uintLessThan(u32, r.rowsOf(fi, s))) else base[s][i % K]; + } + } + + /// Dedup (graph replays, verify-like selections) against the pool rows, then dedup / dense / packed times a call. + fn dedupVsDense(r: *Rank, rnd: std.Random, io: std.Io, fi: u32, R: u32) !void { + const K = 512; + const n = R * K; + const g = r.gpa; + const v = r.dp.view(fi, 0); + const psh: u32 = @intCast(v.psh); + const map_rows = 3 * (@as(u64, @intCast(v.pts)) << @intCast(psh)); + var scratch = try cuda.DeviceBuffer.alloc(r.d, 4 * (map_rows + 2 * n + (n + 255) / 256 * split.max_world)); + defer scratch.free(); + try scratch.fill32(std.math.maxInt(i32), null); + // by lengths where every rank takes them (RoCE), else forced (NCCL): never silently dense + const on = try split.Exchange.init(r.comm, r.x.kernels, .on); + var xd = if (on.packs(@as(u64, n) * 584)) on else try split.Exchange.init(r.comm, r.x.kernels, .force); + xd.dedup = .{ .map = scratch.ptr, .map_rows = map_rows, .first = scratch.ptr + 4 * map_rows, .place = scratch.ptr + 4 * (map_rows + n), .bcnt = scratch.ptr + 4 * (map_rows + 2 * n), .entries = n, .slots = 3 }; + const xo = try split.Exchange.init(r.comm, r.x.kernels, .on); + const sel = try g.alloc(i32, n); + defer g.free(sel); + const rslot = try g.alloc(i32, R); + defer g.free(rslot); + const tok = try g.alloc(i32, n); + defer g.free(tok); + const rh = try g.alloc(u8, 2 * n * 584); + defer g.free(rh); + var dsel = try cuda.DeviceBuffer.alloc(r.d, n * 4); + defer dsel.free(); + var dslot = try cuda.DeviceBuffer.alloc(r.d, R * 4); + defer dslot.free(); + var dtok = try cuda.DeviceBuffer.alloc(r.d, n * 4); + defer dtok.free(); + var dlens = try cuda.DeviceBuffer.alloc(r.d, 2 * 4); + defer dlens.free(); + var send = try cuda.DeviceBuffer.alloc(r.d, n * 584); + defer send.free(); + var recv = try cuda.DeviceBuffer.alloc(r.d, 2 * n * 584); + defer recv.free(); + const a: split.DenseArgs = .{ .sel = dsel.ptr, .rows = R, .k = K, .table = v.pt, .pts = @intCast(v.pts), .rslot = dslot.ptr, .psh = psh, .base = v.cv, .row_bytes = 584, .world = 2, .send = send.ptr, .tok = dtok.ptr }; + const pa = split.PackArgs.of(a, r.comm.rank(), dlens.ptr); + var execs: [3]cuda.graph.Exec = undefined; + for (&execs, 0..) |*e, m| { + try cuda.graph.beginCapture(r.stream, .global); + switch (m) { + 0 => try xd.window(pa, recv.ptr, r.stream.handle), + 1 => try r.x.dense(a, recv.ptr, r.stream.handle), + else => try xo.window(pa, recv.ptr, r.stream.handle), + } + var gr = try cuda.graph.endCapture(r.stream); + defer gr.deinit(); + e.* = try gr.instantiate(); + } + defer for (&execs) |*e| e.deinit(); + for (0..4) |round| { + r.verifyLike(rnd, fi, R, K, @intCast(round * 5), sel, rslot); + try dsel.upload(0, std.mem.sliceAsBytes(sel)); + try dslot.upload(0, std.mem.sliceAsBytes(rslot)); + try recv.fill8(0xEE, r.stream.handle); + if (round == 0) try xd.window(pa, recv.ptr, r.stream.handle) else try execs[0].launchOn(r.stream); + try r.stream.synchronize(); + try dtok.download(0, std.mem.sliceAsBytes(tok)); + try recv.download(0, rh); + var lens: [2]i32 = undefined; + try dlens.download(0, std.mem.asBytes(&lens)); + var seen = std.AutoHashMap(u64, i32).init(g); + defer seen.deinit(); + var unique: [2]u32 = .{ 0, 0 }; + for (sel, tok, 0..) |t, o, i| { + r.checks += 1; + if (t < 0 or o < 0) { + r.bad += @intFromBool((t < 0) != (o < 0)); + continue; + } + const gp = try seen.getOrPut((@as(u64, @intCast(rslot[i / K])) << 32) | @as(u64, @intCast(t))); + if (gp.found_existing) r.bad += @intFromBool(gp.value_ptr.* != o) else { + gp.value_ptr.* = o; + unique[split.ownerOf(@intCast(t), psh, 2)] += 1; + } + const before = r.bad; + const y = std.mem.bytesAsSlice(u64, rh[@as(usize, @intCast(o)) * 584 ..][0..584]); + for (y, 0..) |q, j| r.bad += @intFromBool(q != word(fi, @intCast(rslot[i / K]), @intCast(t), j)); + if (r.bad != before and r.shown < 6) { + r.shown += 1; + std.debug.print("DEDUP MISMATCH fam {d} R {d} round {d} entry {d} t {d} token {d}\n", .{ fi, R, round, i, t, o }); + } + } + for (0..2) |o| r.bad += @intFromBool(lens[o] != @as(i32, @intCast(unique[o] * 584))); + const map = try g.alloc(i32, map_rows); + defer g.free(map); + try scratch.download(0, std.mem.sliceAsBytes(map)); + for (map) |m| r.bad += @intFromBool(m != std.math.maxInt(i32)); + if (round == 1) std.debug.print("dedup fam {d} R {d}: {d} + {d} unique rows of {d} entries ({s})\n", .{ fi, R, unique[0], unique[1], n, @tagName(xd.compact) }); + } + r.verifyLike(rnd, fi, R, K, 3, sel, rslot); + try dsel.upload(0, std.mem.sliceAsBytes(sel)); + try dslot.upload(0, std.mem.sliceAsBytes(rslot)); + var us: [3]f64 = undefined; + for (execs, &us) |e, *u| { + try e.launchOn(r.stream); + try r.stream.synchronize(); + const t0 = std.Io.Clock.awake.now(io).toNanoseconds(); + for (0..50) |_| try e.launchOn(r.stream); + try r.stream.synchronize(); + u.* = @as(f64, @floatFromInt(std.Io.Clock.awake.now(io).toNanoseconds() - t0)) / 50e3; + } + std.debug.print("exchange fam {d} R {d} (3 in 16 fresh): dedup {d:.1} us, dense {d:.1} us, packed {d:.1} us a call (packed moves {s})\n", .{ fi, R, us[0], us[1], us[2], if (xo.packs(@as(u64, n) * 584)) "lengths" else "whole strides" }); + } + + /// A 4096-row segment's history union (rows by token == the pool's), timed vs unionOf's planned path. + fn historyVsPlanned(r: *Rank, rnd: std.Random, io: std.Io, fi: u32, si: u32, rank: u32) !void { + const K = 512; + const S = 4096; + const g, const n = .{ r.gpa, S * K }; + const sel = try g.alloc(i32, n); + defer g.free(sel); + for (sel) |*t| t.* = if (rnd.uintLessThan(u32, 16) == 0) -1 else @intCast(rnd.uintLessThan(u32, r.rowsOf(fi, si))); + const v, const slot = .{ r.dp.view(fi, si), r.pool.slots.items[si] }; + const psh: u32 = @intCast(v.psh); + const m = split.historyRows(slot.len, psh, 2); + var dsel = try cuda.DeviceBuffer.fromHost(r.d, std.mem.sliceAsBytes(sel)); + defer dsel.free(); + var tok = try cuda.DeviceBuffer.alloc(r.d, 4 * n); + defer tok.free(); + const cap: u64 = 128 << 20; + var send = try cuda.DeviceBuffer.alloc(r.d, cap / 3); + defer send.free(); + var recv = try cuda.DeviceBuffer.alloc(r.d, 2 * cap / 3); + defer recv.free(); + const h: split.HistArgs = .{ .sel = dsel.ptr, .rows = S, .k = K, .table = v.pt, .pages = slot.len, .psh = psh, .rank = rank, .world = 2, .m = m, .row_bytes = 584, .base = v.cv, .send = send.ptr, .tok = tok.ptr }; + try r.x.history(h, recv.ptr, r.stream.handle); + try r.stream.synchronize(); + const ht = try g.alloc(i32, n); + defer g.free(ht); + try tok.download(0, std.mem.sliceAsBytes(ht)); // the tokens, then the rows behind them + const hr = try g.alloc(u8, 2 * m * 584); + defer g.free(hr); + try recv.download(0, hr); + for (sel, ht) |t, o| { + r.checks += 1; + if (t < 0) { + r.bad += @intFromBool(o != -1); + continue; + } + const row = std.mem.bytesAsSlice(u64, hr[@as(usize, @intCast(o)) * 584 ..][0..584]); + for (row, 0..) |w, j| r.bad += @intFromBool(w != word(fi, si, @intCast(t), j)); + } + const table = try g.alloc(u32, slot.max_pages); + defer g.free(table); + var um: u32, var us: [2]f64 = .{ 0, undefined }; + for (&us, 0..) |*u, path| { + const t0 = std.Io.Clock.awake.now(io).toNanoseconds(); + for (0..10) |_| { + if (path == 1) { + try r.x.history(h, recv.ptr, r.stream.handle); + continue; + } + try r.stream.synchronize(); + try dsel.download(0, std.mem.sliceAsBytes(sel)); + for (table, 0..) |*t, k| t.* = slot.localTableAt(@intCast(k)); + var blocks: std.ArrayList(split.Block) = .empty; + defer { + for (blocks.items) |*b| b.deinit(g); + blocks.deinit(g); + } + try split.planUnion(g, .{ .sel = sel, .k = K, .table = table, .psh = psh, .world = 2, .rank = rank, .row_bytes = 584, .cap = cap }, &blocks); + const b = blocks.items[0]; + um = b.m; + var phys = try cuda.DeviceBuffer.fromHost(r.d, std.mem.sliceAsBytes(b.phys)); + defer phys.free(); + try tok.upload(0, std.mem.sliceAsBytes(b.tokens)); + try r.x.unionBlock(v.cv, 584, phys.ptr, b.m, send.ptr, recv.ptr, r.stream.handle); + } + try r.stream.synchronize(); + u.* = @as(f64, @floatFromInt(std.Io.Clock.awake.now(io).toNanoseconds() - t0)) / 10e6; + } + std.debug.print("prefill union fam {d} slot {d} ({d} pages, 4096 x 512): history {d:.2} ms ({d} rows an owner), planned {d:.2} ms ({d} rows an owner) a call\n", .{ fi, si, slot.len, us[1], m, us[0], um }); + } + + fn unions(r: *Rank, rnd: std.Random, fi: u32, rank: u32) !void { + const K = 512; + const S = 256; // a prefill segment's rows + const g = r.gpa; + const sel = try g.alloc(i32, S * K); + defer g.free(sel); + for (sel) |*t| t.* = if (rnd.uintLessThan(u32, 16) == 0) -1 else @intCast(rnd.uintLessThan(u32, r.rowsOf(fi, 2))); + const v = r.dp.view(fi, 2); + const table = try g.alloc(u32, r.dp.pts); + defer g.free(table); + for (table, 0..) |*t, k| t.* = r.pool.slots.items[2].localTableAt(@intCast(k)); + var blocks: std.ArrayList(split.Block) = .empty; + defer { + for (blocks.items) |*b| b.deinit(g); + blocks.deinit(g); + } + try split.planUnion(g, .{ .sel = sel, .k = K, .table = table, .psh = @intCast(v.psh), .world = 2, .rank = rank, .row_bytes = 584, .cap = 24 << 20 }, &blocks); + for (blocks.items) |blk| { + var phys = try cuda.DeviceBuffer.fromHost(r.d, std.mem.sliceAsBytes(blk.phys)); + defer phys.free(); + var send = try cuda.DeviceBuffer.alloc(r.d, blk.m * 584); + defer send.free(); + var recv = try cuda.DeviceBuffer.alloc(r.d, 2 * blk.m * 584); + defer recv.free(); + try r.x.unionBlock(v.cv, 584, phys.ptr, blk.m, send.ptr, recv.ptr, r.stream.handle); + try r.stream.synchronize(); + const h = try g.alloc(u8, 2 * blk.m * 584); + defer g.free(h); + try recv.download(0, h); + for (sel[blk.a * K .. blk.b * K], blk.tokens) |t, o| { + r.checks += 1; + if (t < 0) continue; + const row = std.mem.bytesAsSlice(u64, h[@as(usize, @intCast(o)) * 584 ..][0..584]); + for (row, 0..) |w, j| r.bad += @intFromBool(w != word(fi, 2, @intCast(t), j)); + } + } + r.blocks += @intCast(blocks.items.len); + } +}; + +/// TP_FATBIN (else TF_TP_KERNEL_IMAGE): tp opens RoCE / mailbox as the server does; without it NCCL only. +pub fn main(init: std.process.Init) !u8 { + const gpa = init.gpa; + const args = try init.minimal.args.toSlice(init.arena.allocator()); + if (args.len < 2) { + std.debug.print("usage: tf-kv-split-test KVSPLIT_FATBIN [TP_FATBIN] (tp's TF_TP_* settings; TP_FATBIN: RoCE / mailbox)\n", .{}); + return 2; + } + const image = try std.Io.Dir.cwd().readFileAlloc(init.io, args[1], gpa, .limited(64 << 20)); + defer gpa.free(image); + // TP_FATBIN, else TF_TP_KERNEL_IMAGE (the server's variable), else none (NCCL only) + const tp_path: ?[]const u8 = if (args.len > 2) args[2] else if (std.c.getenv("TF_TP_KERNEL_IMAGE")) |v| std.mem.span(v) else null; + const tp_image: []align(16) u8 = if (tp_path) |p| try std.Io.Dir.cwd().readFileAllocOptions(init.io, p, gpa, .limited(16 << 20), comptime .fromByteUnits(16), null) else &.{}; + defer gpa.free(tp_image); + const cfg = try tp.Config.fromEnv(); + var driver = try cuda.Driver.open(); + defer driver.close(); + var ctx = try cuda.Context.init(&driver, @intCast(cfg.device)); + defer ctx.deinit(); + var s: tp.Session = undefined; + try s.open(&driver, cfg, tp_image); + std.debug.print("rank {d}: tp backend {t}\n", .{ cfg.rank, s.comm().kind() }); + var open = true; + defer if (open) s.close(); + var module = try cuda.Module.load(&driver, image); + defer module.unload(); + var dk: DeviceKernels = .{ .d = &driver, .dense_fn = try module.function("_ZN13dsv41_kvsplit12dense_kernelENS_9DenseArgsE"), .gather_fn = try module.function("_ZN13dsv41_kvsplit13gather_kernelEPKhjPKjjPh"), .pack_fn = try module.function("_ZN13dsv41_kvsplit11pack_kernelENS_8PackArgsE") }; + dk.dedup_fns = .{ + try module.function("_ZN13dsv41_kvsplit17dedup_mark_kernelENS_9DedupArgsE"), + try module.function("_ZN13dsv41_kvsplit18dedup_flags_kernelENS_9DedupArgsE"), + try module.function("_ZN13dsv41_kvsplit17dedup_pack_kernelENS_9DedupArgsE"), + try module.function("_ZN13dsv41_kvsplit16dedup_tok_kernelENS_9DedupArgsE"), + }; + dk.hist_fns = .{ try module.function("_ZN13dsv41_kvsplit18hist_gather_kernelENS_8HistArgsE"), try module.function("_ZN13dsv41_kvsplit15hist_tok_kernelENS_8HistArgsE") }; + var stream = try cuda.Stream.init(&driver, true); + defer stream.deinit(); + var pool = try sessions.Pool.init(gpa, .{ .families = &fams, .page = page }, 8192 * page, 2, cfg.rank); + defer pool.deinit(); + const lens = [3]u64{ 300_000 + 77, 41_000, 1_000_000 + 5 }; + for (lens) |n| try (try pool.newSlot(1 << 20)).ensure(n); + var dp = try DevicePool.init(gpa, &driver, &ctx, &pool, stream, 3, (1 << 20) / page); + defer dp.deinit(); + try dp.syncTables(); + // each rank writes the rows of the pages it owns (kv_store's writes; the other rank's land on its discard page) + for (fams, 0..) |f, fi| { + const pb = f.pageBytes(page); + const buf = try gpa.alloc(u8, pb); + defer gpa.free(buf); + const per = page / f.ratio; + for (pool.slots.items, 0..) |sl, si| for (sl.mapped(), 0..) |pg, k| { + if (!pool.owns(pg)) continue; + const w = std.mem.bytesAsSlice(u64, buf); + for (0..per) |row| for (0..73) |j| { + w[row * 73 + j] = word(fi, si, k * per + row, j); + }; + try dp.store().write(@intCast(fi), &.{pool.localPage(pg)}, buf); + }; + } + try stream.synchronize(); + var r: Rank = .{ .gpa = gpa, .d = &driver, .comm = s.comm(), .stream = stream, .pool = &pool, .dp = &dp, .x = .{ .comm = s.comm(), .kernels = dk.kernels() }, .xp = try split.Exchange.init(s.comm(), dk.kernels(), .force), .lens = lens }; + var prng = std.Random.DefaultPrng.init(20261006); + const rnd = prng.random(); + for ([_]u32{ 0, 1 }) |fi| for ([_]u32{ 1, 6, 16 }) |R| { + try r.run(.dense_eager, fi, Rank.dense, .{ rnd, fi, R, false }); + try r.run(.dense_graph, fi, Rank.dense, .{ rnd, fi, R, true }); + }; + for ([_]u32{ 0, 1 }) |fi| for ([_]u32{ 1, 6, 16, 64 }) |R| try r.run(.packed_vs_dense, fi, Rank.packedVsDense, .{ rnd, fi, R }); + for ([_]u32{ 0, 1 }) |fi| for ([_]u32{ 1, 4, 8, 24, 32 }) |R| try r.run(.dedup, fi, Rank.dedupVsDense, .{ rnd, init.io, fi, R }); + var blocks: u32 = 0; + for ([_]u32{ 0, 1 }) |fi| { + const b0 = r.blocks; + try r.run(.unions, fi, Rank.unions, .{ rnd, fi, cfg.rank }); + blocks += r.blocks - b0; + } + for ([_][2]u32{ .{ 0, 1 }, .{ 0, 0 }, .{ 1, 1 } }) |c| try r.run(.history, c[0], Rank.historyVsPlanned, .{ rnd, init.io, c[0], c[1], cfg.rank }); // fits the cap + // the time of one dense exchange (pack + all-gather), graph-replayed, R = 16, K = 512 + const t0 = std.Io.Clock.awake.now(init.io).toNanoseconds(); + for (0..20) |_| try r.dense(rnd, 1, 16, false); + const dt = @as(f64, @floatFromInt(std.Io.Clock.awake.now(init.io).toNanoseconds() - t0)) / 20e6; + std.debug.print("rank {d}: {d} entries checked over dense (R 1/6/16, eager + 3 graph replays, ratio 2 and 1), packed vs dense (R 1/6/16/64, eager + 3 graph replays) and {d} union blocks; mismatches {d}; {d:.2} ms a dense round trip incl. host checks\n{s}\n", .{ cfg.rank, r.checks, blocks, r.bad, dt, if (r.bad == 0) "PASS" else "FAIL" }); + for (r.tally, 0..) |t, si| for (t, 0..) |fam, fi| if (fam[1] > 0) { + std.debug.print("rank {d}: section {s} fam {d} (ratio {d}): mismatches {d} of {d} checks\n", .{ cfg.rank, @tagName(@as(Rank.Section, @fromBackingInt(@intCast(si)))), fi, fams[fi].ratio, fam[0], fam[1] }); + }; + // both ranks finish, then close the session (its fate channel) at once, before the slower teardown below + try r.comm.barrier(); + s.close(); + open = false; + return if (r.bad == 0) 0 else 1; +} diff --git a/zig/src/families/deepseek_v41/kv/split_gpu_kernels.zig b/zig/src/families/deepseek_v41/kv/split_gpu_kernels.zig new file mode 100644 index 0000000000..263c7e8105 --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/split_gpu_kernels.zig @@ -0,0 +1,80 @@ +//! tf-kv-split-test's kvsplit kernels (zig/kernels/cuda/deepseek_v41/kvsplit.cu) behind split.Kernels. + +const cuda = @import("cuda"); +const tp = @import("tp"); +const split = @import("split.zig"); + +pub const DeviceKernels = struct { + d: *const cuda.Driver, + dense_fn: cuda.Function, + gather_fn: cuda.Function, + pack_fn: cuda.Function, + /// dedup_mark, _flags, _pack, _tok (null: a fatbin without them) + dedup_fns: ?[4]cuda.Function = null, + /// hist_gather, hist_tok + hist_fns: ?[2]cuda.Function = null, + + fn grid(words: u64) u32 { + return @intCast(@max(1, @min((words + 255) / 256, 4096))); + } + + pub fn kernels(k: *DeviceKernels) split.Kernels { + return .{ .ptr = k, .vtable = &.{ .dense = dense, .gather = gather, .pack = pack, .dedup = dedup, .history = history } }; + } + + fn history(ptr: *anyopaque, a: split.HistArgs, stream: tp.collective.Stream) anyerror!void { + const k: *DeviceKernels = @ptrCast(@alignCast(ptr)); + const fs = k.hist_fns orelse return error.Unsupported; + for (fs, [2]u64{ @as(u64, a.m) * (a.row_bytes / 8), @as(u64, a.rows) * a.k }) |f, w| { + if (w == 0) continue; + var args: cuda.Args = .{}; + args.add(a); + try cuda.launch.launch(f, .{ .grid = .{ .x = grid(w) }, .block = .{ .x = 256 } }, .{ .d = k.d, .handle = stream }, &args); + } + } + + fn dedup(ptr: *anyopaque, a: split.DedupArgs, stream: tp.collective.Stream) anyerror!void { + const k: *DeviceKernels = @ptrCast(@alignCast(ptr)); + const fs = k.dedup_fns orelse return error.Unsupported; + const n = @as(u64, a.p.rows) * a.p.k; + if (n == 0) return; + const b: u32 = @intCast((n + 255) / 256); + for (fs, [4]u32{ grid(n), b, b, grid(n) }) |f, g| { + var args: cuda.Args = .{}; + args.add(a); + try cuda.launch.launch(f, .{ .grid = .{ .x = g }, .block = .{ .x = 256 } }, .{ .d = k.d, .handle = stream }, &args); + } + } + + fn dense(ptr: *anyopaque, a: split.DenseArgs, stream: tp.collective.Stream) anyerror!void { + const k: *DeviceKernels = @ptrCast(@alignCast(ptr)); + const words = @as(u64, a.rows) * a.k * (a.row_bytes / 8); + if (words == 0) return; + var args: cuda.Args = .{}; + args.add(a); + try cuda.launch.launch(k.dense_fn, .{ .grid = .{ .x = grid(words) }, .block = .{ .x = 256 } }, .{ .d = k.d, .handle = stream }, &args); + } + + /// pack_kernel: one block of 256 threads a 256 entries (the kernel's kPackThreads), any world <= 8. + fn pack(ptr: *anyopaque, a: split.PackArgs, stream: tp.collective.Stream) anyerror!void { + const k: *DeviceKernels = @ptrCast(@alignCast(ptr)); + const n = @as(u64, a.rows) * a.k; + if (n == 0) return; + var args: cuda.Args = .{}; + args.add(a); + try cuda.launch.launch(k.pack_fn, .{ .grid = .{ .x = @intCast((n + 255) / 256) }, .block = .{ .x = 256 } }, .{ .d = k.d, .handle = stream }, &args); + } + + fn gather(ptr: *anyopaque, base: u64, rb: u32, phys: u64, n: u32, send: u64, stream: tp.collective.Stream) anyerror!void { + const k: *DeviceKernels = @ptrCast(@alignCast(ptr)); + const words = @as(u64, n) * (rb / 8); + if (words == 0) return; + var args: cuda.Args = .{}; + args.add(base); + args.add(rb); + args.add(phys); + args.add(n); + args.add(send); + try cuda.launch.launch(k.gather_fn, .{ .grid = .{ .x = grid(words) }, .block = .{ .x = 256 } }, .{ .d = k.d, .handle = stream }, &args); + } +}; diff --git a/zig/src/families/deepseek_v41/kv/split_roce_test.zig b/zig/src/families/deepseek_v41/kv/split_roce_test.zig new file mode 100644 index 0000000000..2e7765e6bc --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/split_roce_test.zig @@ -0,0 +1,381 @@ +//! Split KV's Exchange on RoCE's protocol at served settings (hybrid routes, 2 stripes): each post inside slot and MR. + +const std = @import("std"); +const testing = std.testing; +const sessions = @import("sessions"); +const tp = @import("tp"); +const split = @import("split.zig"); +const roce = tp.roce; +const hybrid = tp.hybrid; + +const page = 256; +const pts = 16; +const rb = 584; // the comp rows' bytes on the model +const K = 512; +const fams = [_]sessions.Family{ + .{ .name = "comp.2", .ratio = 2, .row_bytes = rb, .split = true }, + .{ .name = "comp.1", .ratio = 1, .row_bytes = rb, .split = true }, +}; +const slot_lens = [_]u64{ 15 * page + 7, 9 * page, 13 * page + 100 }; + +/// tp's types for the shm wire's post tap (roce.Options.tap): the tap, the region layout and one HCA's stripe. +const PostTap = @typeInfo(@typeInfo(@FieldType(roce.Options, "tap")).optional.child).pointer.child; +const tap_params = @typeInfo(@typeInfo(@FieldType(PostTap, "seen")).pointer.child).@"fn".param_types; +const Layout = tap_params[1].?; +const Stripe = @typeInfo(tap_params[4].?).pointer.child; + +/// The served RoCE settings: two ranks, a 1 MiB slot, the fast kernel path. +fn servedEnv(name: []const u8) ?[]const u8 { + const table = .{ .{ "TF_TP_WORLD", "2" }, .{ "TF_COMM_BACKEND", "roce" }, .{ "TF_TP_MAILBOX_MAX_KB", "1024" }, .{ "TF_TP_ROCE_FAST", "1" } }; + inline for (table) |kv| if (std.mem.eql(u8, name, kv[0])) return kv[1]; + return null; +} + +/// The served settings plus chunked gathers: strides past 1 MiB, up to 20 MiB, go RoCE as <= 1 MiB ops. +fn chunkEnv(name: []const u8) ?[]const u8 { + if (std.mem.eql(u8, name, "TF_TP_ROCE_CHUNKED_KB")) return "20480"; + return servedEnv(name); +} + +/// The served settings with a 10 MiB RoCE slot (boot must refuse it). +fn bigEnv(name: []const u8) ?[]const u8 { + if (std.mem.eql(u8, name, "TF_TP_MAILBOX_MAX_KB")) return "10240"; + return servedEnv(name); +} + +/// What one rank's posts did: count, the largest, and ranges a post would have taken outside its slot or the MR. +const Seen = struct { + posts: u64 = 0, + max_padded: u32 = 0, + outside: u64 = 0, + mr_len: u64, + slot: u64, + + fn seen(ctx: *anyopaque, l: Layout, slot: u32, padded: u32, s: []const Stripe) void { + const v: *Seen = @ptrCast(@alignCast(ctx)); + v.posts += 1; + v.max_padded = @max(v.max_padded, padded); + // the expected layout from roce.zig's region size, not from the wire's own numbers + if (l.len != v.mr_len or l.slot != v.slot or l.recv != l.send + 2 * v.slot or l.send + 4 * v.slot != v.mr_len) v.outside += 1; + var total: u64 = 0; + for (s) |x| { + const lo_send = l.send + slot * v.slot; + const lo_recv = l.recv + slot * v.slot; + if (x.local < lo_send or x.local + x.len > lo_send + v.slot or x.local + x.len > v.mr_len) v.outside += 1; + if (x.remote < lo_recv or x.remote + x.len > lo_recv + v.slot or x.remote + x.len > v.mr_len) v.outside += 1; + if (x.flag < l.flags or x.flag + 4 > l.send) v.outside += 1; + total += x.len; + } + if (total != padded) v.outside += 1; + } +}; + +/// A Session's `roce` collective on host ranks: hybrid.zig's routes pick RoCE (host side) or NCCL (host collective). +const Routed = struct { + nccl: tp.Collective, + r: *roce.Roce, + cfg: tp.Config, + roce_ops: u64 = 0, + nccl_ops: u64 = 0, + big_roce: u64 = 0, + chunked: u64 = 0, + + fn self_(p: *anyopaque) *Routed { + return @ptrCast(@alignCast(p)); + } + fn rankOf(p: *anyopaque) u32 { + return self_(p).nccl.rank(); + } + fn worldOf(p: *anyopaque) u32 { + return self_(p).nccl.world(); + } + fn allReduce(p: *anyopaque, s: u64, r: u64, n: usize, t: tp.DType, op: tp.Op, st: tp.collective.Stream) tp.collective.Error!void { + return self_(p).nccl.allReduce(s, r, n, t, op, st); + } + fn took(h: *Routed, n: usize) void { + h.roce_ops += 1; + if (n > 1 << 20) h.big_roce += 1; + } + fn allGather(p: *anyopaque, s: u64, r: u64, count: usize, t: tp.DType, st: tp.collective.Stream) tp.collective.Error!void { + const h = self_(p); + const n = count * t.size(); + if (!h.small(n)) { + h.nccl_ops += 1; + return h.nccl.allGather(s, r, count, t, st); + } + h.took(n); + const in: [*]const u8 = @ptrFromInt(s); + const out: [*]u8 = @ptrFromInt(r); + h.r.hostGather(in[0..n], out[0 .. 2 * n]) catch return error.BackendFailed; + } + /// hybrid.zig's routes: the small path within the slot; a nonempty variable-length stride there in one op. + fn small(h: *Routed, n: usize) bool { + return n <= h.cfg.mailbox_max_bytes and h.r.fits(n); + } + fn single(h: *Routed, n: usize) bool { + return n > 0 and h.small(n); + } + fn varFits(p: *anyopaque, n: usize) bool { + const h = self_(p); + const one = h.single(n); + return one or hybrid.chunkRoute(h.cfg.roce_chunked_bytes, n, one, h.r.chunk_abi); + } + fn varAgreed(p: *anyopaque) tp.collective.Error!bool { + return varFits(p, 16); + } + fn allGatherV(p: *anyopaque, s: u64, r: u64, stride: usize, lens: u64, t: tp.DType, st: tp.collective.Stream) tp.collective.Error!void { + const h = self_(p); + const n = stride * t.size(); + if (!varFits(p, n)) return allGather(p, s, r, stride, t, st); + const l: [*]const i32 = @ptrFromInt(lens); + const in: [*]const u8 = @ptrFromInt(s); + const out: [*]u8 = @ptrFromInt(r); + if (!h.single(n)) { + h.roce_ops += 1; + h.chunked += 1; + return h.r.hostGatherVChunked(in[0..n], out[0 .. 2 * n], .{ @intCast(l[0]), @intCast(l[1]) }) catch error.BackendFailed; + } + h.took(n); + h.r.hostGatherV(in[0..n], out[0 .. 2 * n], .{ @intCast(l[0]), @intCast(l[1]) }) catch return error.BackendFailed; + } + fn exchange(p: *anyopaque, s: u64, r: u64, n: usize, t: tp.DType, peer: u32, st: tp.collective.Stream) tp.collective.Error!void { + return self_(p).nccl.exchange(s, r, n, t, peer, st); + } + fn send(p: *anyopaque, b: u64, n: usize, t: tp.DType, peer: u32, st: tp.collective.Stream) tp.collective.Error!void { + return self_(p).nccl.send(b, n, t, peer, st); + } + fn recv(p: *anyopaque, b: u64, n: usize, t: tp.DType, peer: u32, st: tp.collective.Stream) tp.collective.Error!void { + return self_(p).nccl.recv(b, n, t, peer, st); + } + fn barrier(p: *anyopaque) tp.collective.Error!void { + return self_(p).nccl.barrier(); + } + fn check(p: *anyopaque) tp.collective.Error!void { + self_(p).r.check() catch |e| return e; + return self_(p).nccl.check(); + } + fn abort(p: *anyopaque) void { + self_(p).r.abort(); + self_(p).nccl.abort(); + } + + const vtable: tp.collective.Collective.VTable = .{ .kind = .hybrid, .rank = rankOf, .world = worldOf, .all_reduce = allReduce, .all_gather = allGather, .exchange = exchange, .send = send, .recv = recv, .barrier = barrier, .check = check, .abort = abort, .all_gather_v = allGatherV, .var_fits = varFits, .var_agreed = varAgreed }; + + fn iface(h: *Routed) tp.Collective { + return .{ .ptr = h, .vtable = &vtable }; + } +}; + +fn value(f: usize, s: usize, t: usize, i: usize) u8 { + return @truncate(f *% 101 +% s *% 61 +% t *% 7 +% i *% 3 +% 1); +} + +const Ctx = struct { + get: *const fn ([]const u8) ?[]const u8, + port: u16, + lfd: tp.sock.Fd, + seen: [2]Seen = undefined, + big_roce: [2]u64 = .{ 0, 0 }, + roce_ops: [2]u64 = .{ 0, 0 }, + chunked: [2]u64 = .{ 0, 0 }, +}; + +fn rank(comm: tp.Collective, me: u32, ctx: *Ctx) anyerror!void { + const gpa = testing.allocator; + const cfg = try tp.Config.parse(ctx.get); + try testing.expectEqual(tp.config.Backend.roce, cfg.backend); + var b = if (me == 0) try tp.Bootstrap.acceptOn(ctx.lfd, 2, 10 * std.time.ns_per_s) else try tp.Bootstrap.open(.{ .world = 2, .rank = 1, .link_port = ctx.port, .connect_timeout_ns = 10 * std.time.ns_per_s }); + defer b.close(); + const slot = std.mem.alignForward(usize, cfg.mailbox_max_bytes, 4096); + ctx.seen[me] = .{ .mr_len = roce.Roce.regionBytes(slot), .slot = slot }; + var tap: PostTap = .{ .ctx = &ctx.seen[me], .seen = Seen.seen }; + const r = try roce.Roce.init(null, &b, "", .{ .wire = .shm, .shm_stripes = 2, .tap = &tap, .max_bytes = cfg.mailbox_max_bytes, .timeout_ns = 20 * std.time.ns_per_s }); + defer r.deinit(); + // no chunked op without the ABI marker; the host side then moves `RoceArgs.chunk` ops as the kernel does + try testing.expectError(error.Unsupported, r.allGatherVChunked(1 << 12, 1 << 24, 2 << 20, 1 << 12, null)); + r.chunk_abi = cfg.roce_chunked_bytes > 0; + var routed: Routed = .{ .nccl = comm, .r = r, .cfg = cfg }; + const c = routed.iface(); + + var pool = try sessions.Pool.init(gpa, .{ .families = &fams, .page = page }, 64 * page, 2, me); + defer pool.deinit(); + var host = try sessions.HostPool.init(gpa, &pool); + defer host.deinit(); + var slots: [3]*sessions.Slot = undefined; + for (&slots, slot_lens) |*s, n| { + s.* = try pool.newSlot(pts * page); + try s.*.ensure(n); + } + for (fams, 0..) |f, fi| for (slots, 0..) |sl, si| for (sl.mapped(), 0..) |pg, k| { + if (!pool.owns(pg)) continue; + const v = host.pageOf(@intCast(fi), pool.localPage(pg)); + const per = page / f.ratio; + for (0..per) |row| for (0..rb) |i| { + v[row * rb + i] = value(fi, si, k * per + row, i); + }; + }; + var tables: [3 * pts]i32 = undefined; + for (slots, 0..) |s, si| for (0..pts) |k| { + tables[si * pts + k] = @intCast(s.localTableAt(@intCast(k))); + }; + var hk: split.HostKernels = .{}; + const x = try split.Exchange.init(c, hk.kernels(), .on); + try testing.expect(x.var_ok); + var prng = std.Random.DefaultPrng.init(42); // the same selections on both ranks + const rnd = prng.random(); + + // windows of R rows (ROWS_CAP 64), both families: R 3 is 897,024 B a stride (RoCE), R 4 1,196,032 (past 1 MiB) + for (fams, 0..) |f, fi| for ([_]u32{ 1, 2, 3, 4, 6, 16, 32, 64 }) |R| { + const n = R * K; + const sel = try gpa.alloc(i32, n); + defer gpa.free(sel); + const rslot = try gpa.alloc(i32, R); + defer gpa.free(rslot); + for (rslot, 0..) |*s, i| s.* = @intCast(i % 3); + for (sel, 0..) |*t, i| { + const rows: u32 = @intCast(slot_lens[@intCast(rslot[i / K])] / f.ratio); + t.* = if (rnd.uintLessThan(u32, 9) == 0) -1 else @intCast(rnd.uintLessThan(u32, rows)); + } + const send = try gpa.alloc(u8, n * rb); + defer gpa.free(send); + const recv = try gpa.alloc(u8, 2 * n * rb); + defer gpa.free(recv); + const tok = try gpa.alloc(i32, n); + defer gpa.free(tok); + const got = try gpa.alloc(u8, n * rb); + defer gpa.free(got); + var lens: [split.max_world]i32 = @splat(-7); + const psh: u32 = std.math.log2_int(u32, page / f.ratio); + const da: split.DenseArgs = .{ .sel = @intFromPtr(sel.ptr), .rows = R, .k = K, .table = @intFromPtr(&tables), .pts = pts, .rslot = @intFromPtr(rslot.ptr), .psh = psh, .base = @intFromPtr(host.tensors[fi].ptr), .row_bytes = rb, .world = 2, .send = @intFromPtr(send.ptr), .tok = @intFromPtr(tok.ptr) }; + for (0..2) |pass| { + @memset(recv, 0xEE); + if (pass == 0) try x.window(split.PackArgs.of(da, me, @intFromPtr(&lens)), @intFromPtr(recv.ptr), null) else try x.dense(da, @intFromPtr(recv.ptr), null); + for (sel, tok, 0..) |t, o, i| { + if (t < 0) continue; + const s: usize = @intCast(rslot[i / K]); + const row = recv[@as(usize, @intCast(o)) * rb ..][0..rb]; + for (row, 0..) |byte, j| if (byte != value(fi, s, @intCast(t), j)) { + std.debug.print("fam {d} R {d} pass {d}: entry {d} wrong\n", .{ fi, R, pass, i }); + return error.TestUnexpectedResult; + }; + // the window's (packed, chunked past 1 MiB) row byte-identical to the dense (NCCL) gather's + if (pass == 0) @memcpy(got[i * rb ..][0..rb], row) else try testing.expectEqualSlices(u8, got[i * rb ..][0..rb], row); + } + } + }; + + // union blocks (allGather of M rows): the 1 MiB edge (1,795 rows = 1,048,280 B) and the 128 MiB cap's block + const max_m: u32 = (128 << 20) / (3 * rb); + const rows_buf = try gpa.alloc(u8, @as(usize, max_m) * rb); + defer gpa.free(rows_buf); + for (rows_buf, 0..) |*v, i| v.* = @truncate(i *% 13 +% me); + const phys = try gpa.alloc(u32, max_m); + defer gpa.free(phys); + for (phys, 0..) |*p, i| p.* = @intCast(max_m - 1 - i); + for ([_]u32{ 1, 1795, 1796, max_m }) |m| { + const send = try gpa.alloc(u8, @as(usize, m) * rb); + defer gpa.free(send); + const recv = try gpa.alloc(u8, 2 * @as(usize, m) * rb); + defer gpa.free(recv); + try x.unionBlock(@intFromPtr(rows_buf.ptr), rb, @intFromPtr(phys.ptr), m, @intFromPtr(send.ptr), @intFromPtr(recv.ptr), null); + for (0..2) |o| for (0..m) |i| { + const want: u8 = @truncate(@as(usize, phys[i]) * rb *% 13 +% o); + if (recv[(o * m + i) * rb] != want) return error.TestUnexpectedResult; + }; + } + try c.check(); + try b.agree("done", "split roce test end"); + ctx.big_roce[me] = routed.big_roce; + ctx.roce_ops[me] = routed.roce_ops; + ctx.chunked[me] = routed.chunked; +} + +fn runAt(get: *const fn ([]const u8) ?[]const u8) !Ctx { + const l = try tp.bootstrap.testListener(); + defer tp.sock.close(l.fd); + var ctx: Ctx = .{ .get = get, .port = l.port, .lfd = l.fd }; + try tp.host.run(2, &ctx, rank); + return ctx; +} + +test "served settings: every split exchange's RoCE post fits the 1 MiB slot and its MR; past 1 MiB goes to NCCL" { + const ctx = try runAt(servedEnv); + for (ctx.seen, ctx.big_roce, ctx.roce_ops) |s, big, ops| { + try testing.expectEqual(@as(u64, 1 << 20), s.slot); + try testing.expect(ops > 0 and s.posts >= ops); + try testing.expectEqual(@as(u64, 0), s.outside); + try testing.expect(s.max_padded <= 1 << 20); + try testing.expectEqual(@as(u64, 0), big); + } +} + +test "TF_TP_ROCE_CHUNKED_KB: strides past 1 MiB go RoCE as <= 1 MiB writes, byte-identical to the dense gather" { + // one op's strides stay single; off (0), past the bound or without the kernel's ABI marker: NCCL + try testing.expect(!hybrid.chunkRoute(20 << 20, 2 << 20, true, true)); + try testing.expect(!hybrid.chunkRoute(0, 2 << 20, false, true)); + try testing.expect(!hybrid.chunkRoute(20 << 20, (20 << 20) + 1, false, true)); + try testing.expect(!hybrid.chunkRoute(20 << 20, 2 << 20, false, false)); + try testing.expect(hybrid.chunkRoute(20 << 20, 20 << 20, false, true)); + const ctx = try runAt(chunkEnv); + for (ctx.seen, ctx.big_roce, ctx.chunked) |s, big, chunked| { + try testing.expectEqual(@as(u64, 1 << 20), s.slot); + // R 4..64 windows of both families (fam 1 R 32: a 9,568,256 B stride; R 64: 19,136,512 B) + try testing.expectEqual(@as(u64, 10), chunked); + try testing.expectEqual(@as(u64, 0), s.outside); + try testing.expect(s.max_padded <= 1 << 20); + try testing.expectEqual(@as(u64, 0), big); + } +} + +test "a 10 MiB RoCE slot (TF_TP_MAILBOX_MAX_KB=10240) fails boot; a RoCE slot past the write cap is refused too" { + try testing.expectError(error.BadSetting, tp.Config.parse(bigEnv)); + const l = try tp.bootstrap.testListener(); + defer tp.sock.close(l.fd); + var ctx: Big = .{ .port = l.port, .lfd = l.fd }; + try tp.host.run(2, &ctx, bigSlot); + for (ctx.errs) |e| try testing.expectEqual(@as(?anyerror, error.WriteTooLarge), e); +} + +const Big = struct { port: u16, lfd: tp.sock.Fd, errs: [2]?anyerror = .{ null, null } }; + +fn bigSlot(_: tp.Collective, me: u32, ctx: *Big) anyerror!void { + var b = if (me == 0) try tp.Bootstrap.acceptOn(ctx.lfd, 2, 10 * std.time.ns_per_s) else try tp.Bootstrap.open(.{ .world = 2, .rank = 1, .link_port = ctx.port, .connect_timeout_ns = 10 * std.time.ns_per_s }); + defer b.close(); + if (roce.Roce.init(null, &b, "", .{ .wire = .shm, .max_bytes = (1 << 20) + 4096 })) |r| r.deinit() else |e| { + ctx.errs[me] = e; + } +} + +const Keep = struct { port: u16, lfd: tp.sock.Fd, fail: bool, region: [2]usize = .{ 0, 0 }, len: usize = 0 }; + +fn keepRank(_: tp.Collective, me: u32, ctx: *Keep) anyerror!void { + var b = if (me == 0) try tp.Bootstrap.acceptOn(ctx.lfd, 2, 10 * std.time.ns_per_s) else try tp.Bootstrap.open(.{ .world = 2, .rank = 1, .link_port = ctx.port, .connect_timeout_ns = 10 * std.time.ns_per_s }); + defer b.close(); + const r = try roce.Roce.init(null, &b, "", .{ .wire = .shm, .max_bytes = 1 << 20 }); + ctx.region[me] = @intFromPtr(r.mapping.ptr); + ctx.len = r.mapping.len; + try b.agree("open", "keep test open"); + if (ctx.fail and me == 1) r.abort(); + r.deinit(); +} + +/// Whether [at, at + len) is still mapped in this process. +fn mapped(at: usize, len: usize) bool { + var vec: [4096]u8 = undefined; + const rc = std.os.linux.mincore(@ptrFromInt(at), @min(len, 4096 * 4096), &vec); + return std.os.linux.errno(rc) == .SUCCESS; +} + +test "deinit frees a healthy transport's region and keeps a failed one's registered and mapped until exit" { + inline for (.{ false, true }) |fail| { + const l = try tp.bootstrap.testListener(); + defer tp.sock.close(l.fd); + var ctx: Keep = .{ .port = l.port, .lfd = l.fd, .fail = fail }; + const kept0 = roce.Roce.kept.load(.monotonic); + try tp.host.run(2, &ctx, keepRank); + const kept = roce.Roce.kept.load(.monotonic) - kept0; + try testing.expectEqual(@as(u32, if (fail) 1 else 0), kept); + try testing.expectEqual(fail, mapped(ctx.region[1], ctx.len)); + try testing.expect(!mapped(ctx.region[0], ctx.len)); + } +} diff --git a/zig/src/families/deepseek_v41/kv/split_test.zig b/zig/src/families/deepseek_v41/kv/split_test.zig new file mode 100644 index 0000000000..53232f162d --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/split_test.zig @@ -0,0 +1,326 @@ +//! Split KV's exchange on two in-process ranks: every row read after the exchange equals the replicated pool's. + +const std = @import("std"); +const testing = std.testing; +const sessions = @import("sessions"); +const tp = @import("tp"); +const split = @import("split.zig"); + +const page = 64; // tokens; ratio 2: 32 rows a page +const rb = 40; // a short row keeps the test fast; the code takes any width +const fams = [_]sessions.Family{.{ .name = "comp.2", .ratio = 2, .row_bytes = rb, .split = true }}; +const psh = 5; + +/// The replicated pool's row t of slot s: what every exchanged row must equal. +fn value(s: usize, t: usize, i: usize) u8 { + return @truncate(s *% 61 +% t *% 7 +% i *% 3 +% 1); +} + +const Ctx = struct { seed: u64 }; + +fn rank(comm: tp.Collective, me: u32, ctx: *const Ctx) anyerror!void { + const gpa = testing.allocator; + var pool = try sessions.Pool.init(gpa, .{ .families = &fams, .page = page }, 64 * page, 2, me); + defer pool.deinit(); + var host = try sessions.HostPool.init(gpa, &pool); + defer host.deinit(); + const lens = [_]u64{ 11 * page + 5, 3 * page, 17 * page + 40 }; + var slots: [3]*sessions.Slot = undefined; + for (&slots, lens) |*s, n| { + s.* = try pool.newSlot(24 * page); + try s.*.ensure(n); + } + // each rank writes the rows it owns (the other rank's go to the discard page, as kv_store does) + for (slots, 0..) |s, si| for (s.mapped(), 0..) |pg, k| { + const v = host.pageOf(0, pool.localPage(pg)); + if (!pool.owns(pg)) continue; + for (0..page / 2) |r| for (0..rb) |i| { + v[r * rb + i] = value(si, k * (page / 2) + r, i); + }; + }; + const pts = 24; + var tables: [3 * pts]i32 = undefined; + for (slots, 0..) |s, si| for (0..pts) |k| { + tables[si * pts + k] = @intCast(s.localTableAt(@intCast(k))); + }; + var prng = std.Random.DefaultPrng.init(ctx.seed); // the same selections on both ranks + const rnd = prng.random(); + var hk: split.HostKernels = .{}; + const x: split.Exchange = .{ .comm = comm, .kernels = hk.kernels() }; + const base = @intFromPtr(host.tensors[0].ptr); + + // dense, row mode: 6 rows over 3 slots, 16 entries each, some -1 + const R = 6; + const K = 16; + var sel: [R * K]i32 = undefined; + var rslot: [R]i32 = undefined; + for (&rslot, 0..) |*s, r| s.* = @intCast(r % 3); + for (&sel, 0..) |*t, i| { + const rows: u32 = @intCast(lens[@intCast(rslot[i / K])] / 2); + t.* = if (rnd.uintLessThan(u32, 8) == 0) -1 else @intCast(rnd.uintLessThan(u32, rows)); + } + var send: [R * K * rb]u8 = undefined; + var recv: [2 * R * K * rb]u8 = undefined; + var tok: [R * K]i32 = undefined; + try x.dense(.{ .sel = @intFromPtr(&sel), .rows = R, .k = K, .table = @intFromPtr(&tables), .pts = pts, .rslot = @intFromPtr(&rslot), .psh = psh, .base = base, .row_bytes = rb, .world = 2, .send = @intFromPtr(&send), .tok = @intFromPtr(&tok) }, @intFromPtr(&recv), null); + for (sel, tok, 0..) |t, o, i| { + if (t < 0) { + try testing.expectEqual(@as(i32, -1), o); + continue; + } + const row = recv[@as(usize, @intCast(o)) * rb ..][0..rb]; + for (row, 0..) |b, j| try testing.expectEqual(value(@intCast(rslot[i / K]), @intCast(t), j), b); + } + + // union over a 40-row prefill segment of slot 2, with a cap that splits it into blocks + const S = 40; + var useg: [S * K]i32 = undefined; + for (&useg) |*t| t.* = if (rnd.uintLessThan(u32, 10) == 0) -1 else @intCast(rnd.uintLessThan(u32, @intCast(lens[2] / 2))); + var table2: [pts]u32 = undefined; + for (&table2, 0..) |*t, k| t.* = slots[2].localTableAt(@intCast(k)); + var blocks: std.ArrayList(split.Block) = .empty; + defer { + for (blocks.items) |*b| b.deinit(gpa); + blocks.deinit(gpa); + } + try split.planUnion(gpa, .{ .sel = &useg, .k = K, .table = &table2, .psh = psh, .world = 2, .rank = me, .row_bytes = rb, .cap = 3 * 120 * rb }, &blocks); + try testing.expect(blocks.items.len > 1); + for (blocks.items) |blk| { + const s2 = try gpa.alloc(u8, blk.m * rb); + defer gpa.free(s2); + const r2 = try gpa.alloc(u8, 2 * blk.m * rb); + defer gpa.free(r2); + try x.unionBlock(base, rb, @intFromPtr(blk.phys.ptr), blk.m, @intFromPtr(s2.ptr), @intFromPtr(r2.ptr), null); + for (useg[blk.a * K .. blk.b * K], blk.tokens) |t, o| { + if (t < 0) continue; + const row = r2[@as(usize, @intCast(o)) * rb ..][0..rb]; + for (row, 0..) |b, j| try testing.expectEqual(value(2, @intCast(t), j), b); + } + } +} + +test "split == replicated: dense row-mode and capped union exchanges on two ranks" { + for ([_]u64{ 1, 2, 3 }) |seed| try tp.host.run(2, &Ctx{ .seed = seed }, rank); +} + +// -- packed (TF_DSV41_KV_SPLIT_COMPACT): packed == dense == replicated behind every token, on 2 and 3 ranks --------- + +const pfams = [_]sessions.Family{ + .{ .name = "comp.2", .ratio = 2, .row_bytes = rb, .split = true }, + .{ .name = "comp.1", .ratio = 1, .row_bytes = rb, .split = true }, +}; + +fn pvalue(f: usize, s: usize, t: usize, i: usize) u8 { + return @truncate(f *% 101 +% s *% 61 +% t *% 7 +% i *% 3 +% 1); +} + +const Kind = enum { random, one_owner, padded }; + +const PCtx = struct { seed: u64, refuse: u32 = 0 }; + +fn packedRank(comm: tp.Collective, me: u32, ctx: *const PCtx) anyerror!void { + const gpa = testing.allocator; + const W = comm.world(); + var pool = try sessions.Pool.init(gpa, .{ .families = &pfams, .page = page }, 96 * page, W, me); + defer pool.deinit(); + var host = try sessions.HostPool.init(gpa, &pool); + defer host.deinit(); + const lens = [_]u64{ 11 * page + 5, 3 * page, 17 * page + 40 }; + var slots: [3]*sessions.Slot = undefined; + for (&slots, lens) |*s, n| { + s.* = try pool.newSlot(24 * page); + try s.*.ensure(n); + } + for (pfams, 0..) |f, fi| for (slots, 0..) |s, si| for (s.mapped(), 0..) |pg, k| { + if (!pool.owns(pg)) continue; + const v = host.pageOf(@intCast(fi), pool.localPage(pg)); + const per = page / f.ratio; + for (0..per) |r| for (0..rb) |i| { + v[r * rb + i] = pvalue(fi, si, k * per + r, i); + }; + }; + const pts = 24; + var tables: [3 * pts]i32 = undefined; + for (slots, 0..) |s, si| for (0..pts) |k| { + tables[si * pts + k] = @intCast(s.localTableAt(@intCast(k))); + }; + var hk: split.HostKernels = .{}; + // the knob's three modes: on agrees once (device lengths on every rank, unless one refuses), force packs anywhere + const off = try split.Exchange.init(comm, hk.kernels(), .off); + const on = try split.Exchange.init(comm, hk.kernels(), .on); + const x = try split.Exchange.init(comm, hk.kernels(), .force); + try testing.expect(!off.packs(4096)); + try testing.expectEqual(ctx.refuse == 0, on.var_ok); + try testing.expectEqual(ctx.refuse == 0, on.packs(4096)); + try testing.expect(x.packs(4096)); + var prng = std.Random.DefaultPrng.init(ctx.seed); // the same selections on every rank + const rnd = prng.random(); + const K = 512; + for (pfams, 0..) |f, fi| for ([_]u32{ 1, 6, 16 }) |R| for (std.enums.values(Kind)) |kind| { + const n = R * K; + const per: u32 = page / f.ratio; + const fpsh: u32 = std.math.log2_int(u32, per); + const sel = try gpa.alloc(i32, n); + defer gpa.free(sel); + var rslot: [16]i32 = undefined; + for (rslot[0..R], 0..) |*s, r| s.* = @intCast((r + fi) % 3); + const one: u32 = rnd.uintLessThan(u32, W); + for (sel, 0..) |*t, i| { + const rows: u32 = @intCast(lens[@intCast(rslot[i / K])] / f.ratio); + t.* = switch (kind) { + .random => if (rnd.uintLessThan(u32, 8) == 0) -1 else @intCast(rnd.uintLessThan(u32, rows)), + // every entry on one residue: its pages k = one + W j that hold rows + .one_owner => blk: { + const pages = (rows + per - 1) / per; + if (one >= pages) break :blk -1; + const k = one + W * rnd.uintLessThan(u32, (pages - one + W - 1) / W); + const row = k * per + rnd.uintLessThan(u32, per); + break :blk if (row < rows) @intCast(row) else -1; + }, + // a row's count, then -1 (the attention's padding) + .padded => if (i % K >= (i / K) * 29 % K) -1 else @intCast(rnd.uintLessThan(u32, rows)), + }; + } + const send = try gpa.alloc(u8, n * rb); + defer gpa.free(send); + const recv_d = try gpa.alloc(u8, W * n * rb); + defer gpa.free(recv_d); + const recv_p = try gpa.alloc(u8, W * n * rb); + defer gpa.free(recv_p); + @memset(recv_p, 0xEE); // past each owner's rows: never read + const tok_d = try gpa.alloc(i32, n); + defer gpa.free(tok_d); + const tok_p = try gpa.alloc(i32, n); + defer gpa.free(tok_p); + var plens: [split.max_world]i32 = @splat(-7); + const base = @intFromPtr(host.tensors[fi].ptr); + const da: split.DenseArgs = .{ .sel = @intFromPtr(sel.ptr), .rows = R, .k = K, .table = @intFromPtr(&tables), .pts = pts, .rslot = @intFromPtr(&rslot), .psh = fpsh, .base = base, .row_bytes = rb, .world = W, .send = @intFromPtr(send.ptr), .tok = @intFromPtr(tok_d.ptr) }; + try x.dense(da, @intFromPtr(recv_d.ptr), null); + var pa = split.PackArgs.of(da, me, @intFromPtr(&plens)); + pa.tok = @intFromPtr(tok_p.ptr); + if (kind == .padded) try x.window(pa, @intFromPtr(recv_p.ptr), null) else try x.densePacked(pa, @intFromPtr(recv_p.ptr), null); + // the reference: each owner's running count in list order + var count: [split.max_world]u32 = @splat(0); + for (sel, tok_d, tok_p, 0..) |t, od, op, i| { + if (t < 0) { + try testing.expectEqual(@as(i32, -1), od); + try testing.expectEqual(@as(i32, -1), op); + continue; + } + const o = split.ownerOf(@intCast(t), fpsh, W); + try testing.expectEqual(@as(i32, @intCast(o * n + count[o])), op); + count[o] += 1; + const rd = recv_d[@as(usize, @intCast(od)) * rb ..][0..rb]; + const rp = recv_p[@as(usize, @intCast(op)) * rb ..][0..rb]; + for (rd, rp, 0..) |a, b, j| { + const want = pvalue(fi, @intCast(rslot[i / K]), @intCast(t), j); + try testing.expectEqual(want, a); + try testing.expectEqual(want, b); + } + } + for (0..W) |o| try testing.expectEqual(@as(i32, @intCast(count[o] * rb)), plens[o]); + if (kind == .one_owner) for (0..W) |o| if (o != one) try testing.expectEqual(@as(i32, 0), plens[o]); + try dedupCheck(x, pa, sel, &rslot, .{ .fi = fi, .K = K, .psh = fpsh, .rows = pts * per, .W = W }); + }; + // TF_DSV41_KV_SPLIT_HISTORY: a one-slot union by history, every family and slot + for (pfams, 0..) |f, fi| for (slots, 0..) |s, si| { + const per: u32 = page / f.ratio; + try historyCheck(x, rnd, .{ .fi = fi, .si = si, .psh = std.math.log2_int(u32, per), .rows = @intCast(lens[si] / f.ratio), .pages = s.len, .W = W, .me = me }, @intFromPtr(host.tensors[fi].ptr), tables[si * pts ..][0..pts]); + }; +} + +const HCase = struct { fi: usize, si: usize, psh: u32, rows: u32, pages: u32, W: u32, me: u32 }; + +/// The history union: rows by token == replicated, each owner's rows within M, M = the largest owner's mapped rows. +fn historyCheck(x: split.Exchange, rnd: std.Random, c: HCase, base: u64, table: []const i32) !void { + const gpa = testing.allocator; + const R = 40; + const K = 16; + var sel: [R * K]i32 = undefined; + for (&sel) |*t| t.* = if (rnd.uintLessThan(u32, 9) == 0) -1 else @intCast(rnd.uintLessThan(u32, c.rows)); + const m = split.historyRows(c.pages, c.psh, c.W); + var most: u32 = 0; + for (0..c.W) |o| most = @max(most, @as(u32, @intCast((c.pages + c.W - 1 - o) / c.W)) << @intCast(c.psh)); + try testing.expectEqual(most, m); + const send = try gpa.alloc(u8, m * rb); + defer gpa.free(send); + const recv = try gpa.alloc(u8, c.W * m * rb); + defer gpa.free(recv); + var tok: [R * K]i32 = undefined; + try x.history(.{ .sel = @intFromPtr(&sel), .rows = R, .k = K, .table = @intFromPtr(table.ptr), .pages = c.pages, .psh = c.psh, .rank = c.me, .world = c.W, .m = m, .row_bytes = rb, .base = base, .send = @intFromPtr(send.ptr), .tok = @intFromPtr(&tok) }, @intFromPtr(recv.ptr), null); + for (sel, tok) |t, o| { + if (t < 0) { + try testing.expectEqual(@as(i32, -1), o); + continue; + } + const u: u32 = @intCast(t); + const own = split.ownerOf(u, c.psh, c.W); + try testing.expect(o >= own * m and o < (own + 1) * m); + for (recv[@as(usize, @intCast(o)) * rb ..][0..rb], 0..) |b, j| try testing.expectEqual(pvalue(c.fi, c.si, u, j), b); + } +} + +const DCase = struct { fi: usize, K: u32, psh: u32, rows: u64, W: u32 }; + +/// TF_DSV41_KV_SPLIT_DEDUP: rows by token == replicated, unique keys sent once, one token a key, the map reset. +fn dedupCheck(x0: split.Exchange, pa0: split.PackArgs, sel: []const i32, rslot: []const i32, c: DCase) !void { + const gpa = testing.allocator; + const n = sel.len; + const map_rows = 3 * c.rows; + const mem = try gpa.alloc(i32, map_rows + 2 * n + (n + 255) / 256 * split.max_world); + defer gpa.free(mem); + @memset(mem[0..map_rows], std.math.maxInt(i32)); + const at = @intFromPtr(mem.ptr); + var x = x0; + x.dedup = .{ .map = at, .map_rows = map_rows, .first = at + 4 * map_rows, .place = at + 4 * (map_rows + n), .bcnt = at + 4 * (map_rows + 2 * n), .entries = n, .slots = 3 }; + const send = try gpa.alloc(u8, n * rb); + defer gpa.free(send); + const recv = try gpa.alloc(u8, c.W * n * rb); + defer gpa.free(recv); + @memset(recv, 0xEE); + const tok = try gpa.alloc(i32, n); + defer gpa.free(tok); + var lens: [split.max_world]i32 = @splat(-7); + var pa = pa0; + pa.send = @intFromPtr(send.ptr); + pa.tok = @intFromPtr(tok.ptr); + pa.lens = @intFromPtr(&lens); + try x.window(pa, @intFromPtr(recv.ptr), null); + var seen = std.AutoHashMap(u64, i32).init(gpa); + defer seen.deinit(); + var unique: [split.max_world]u32 = @splat(0); + for (sel, tok, 0..) |t, o, i| { + if (t < 0) { + try testing.expectEqual(@as(i32, -1), o); + continue; + } + const s: usize = @intCast(rslot[i / c.K]); + const g = try seen.getOrPut(@as(u64, s) * c.rows + @as(u64, @intCast(t))); + if (g.found_existing) try testing.expectEqual(g.value_ptr.*, o) else { + g.value_ptr.* = o; + unique[split.ownerOf(@intCast(t), c.psh, c.W)] += 1; + } + const row = recv[@as(usize, @intCast(o)) * rb ..][0..rb]; + for (row, 0..) |b, j| try testing.expectEqual(pvalue(c.fi, s, @intCast(t), j), b); + } + for (0..c.W) |o| try testing.expectEqual(@as(i32, @intCast(unique[o] * rb)), lens[o]); + for (mem[0..map_rows]) |m| try testing.expectEqual(@as(i32, std.math.maxInt(i32)), m); +} + +test "packed == dedup == dense == replicated, history union: 1 / 6 / 16 rows x K 512, ratio 2 and 1, W 2 and 3" { + for ([_]u64{ 1, 2 }) |seed| { + try tp.host.run(2, &PCtx{ .seed = seed }, packedRank); + try tp.host.run(3, &PCtx{ .seed = seed }, packedRank); + } + // a rank without device lengths: `on` stays dense on every rank, force still packs (whole strides move) + try tp.host.runWith(2, .{ .var_refuse = 1 }, &PCtx{ .seed = 3, .refuse = 1 }, packedRank); +} + +test "TF_DSV41_KV_SPLIT_COMPACT values" { + try testing.expectEqual(split.Compact.off, try split.parseCompact("")); + try testing.expectEqual(split.Compact.off, try split.parseCompact("0")); + try testing.expectEqual(split.Compact.on, try split.parseCompact(" ON ")); + try testing.expectEqual(split.Compact.on, try split.parseCompact("1")); + try testing.expectEqual(split.Compact.force, try split.parseCompact("force")); + try testing.expectError(error.BadCompactMode, split.parseCompact("2")); +} diff --git a/zig/src/families/deepseek_v41/kv/union_plan_test.zig b/zig/src/families/deepseek_v41/kv/union_plan_test.zig new file mode 100644 index 0000000000..23b43942c7 --- /dev/null +++ b/zig/src/families/deepseek_v41/kv/union_plan_test.zig @@ -0,0 +1,134 @@ +//! The union plan against the sort-and-search plan it replaced (kept here as the oracle): byte-identical blocks. + +const std = @import("std"); +const split = @import("split.zig"); +const testing = std.testing; + +fn refRange(gpa: std.mem.Allocator, in: split.UnionIn, a: u32, b: u32, out: *std.ArrayList(split.Block)) !void { + const part = in.sel[@as(usize, a) * in.k .. @as(usize, b) * in.k]; + var uniq: std.ArrayList(u32) = .empty; + defer uniq.deinit(gpa); + try uniq.ensureTotalCapacity(gpa, part.len); + for (part) |t| if (t >= 0) uniq.appendAssumeCapacity(@intCast(t)); + std.mem.sort(u32, uniq.items, {}, std.sort.asc(u32)); + var n: usize = 0; + for (uniq.items, 0..) |t, i| if (i == 0 or t != uniq.items[i - 1]) { + uniq.items[n] = t; + n += 1; + }; + uniq.shrinkRetainingCapacity(n); + // stable partition by owner: each owner's rows stay sorted, so a row's index in its list is a binary search + var sizes: [split.max_world]u32 = @splat(0); + for (uniq.items) |t| sizes[split.ownerOf(t, in.psh, in.world)] += 1; + var m: u32 = 1; + for (sizes[0..in.world]) |sz| m = @max(m, sz); + if (@as(u64, in.world + 1) * m * in.row_bytes > in.cap and b - a > 1) { + const h = a + (b - a) / 2; + try refRange(gpa, in, a, h, out); + return refRange(gpa, in, h, b, out); + } + const lists = try gpa.alloc(u32, uniq.items.len); + defer gpa.free(lists); + var at: [split.max_world]u32 = undefined; + var acc: u32 = 0; + for (0..in.world) |r| { + at[r] = acc; + acc += sizes[r]; + } + const starts = at; + for (uniq.items) |t| { + const r = split.ownerOf(t, in.psh, in.world); + lists[at[r]] = t; + at[r] += 1; + } + var blk: split.Block = .{ .a = a, .b = b, .m = m, .phys = try gpa.alloc(u32, m), .tokens = try gpa.alloc(i32, part.len) }; + errdefer blk.deinit(gpa); + const mask = (@as(u32, 1) << @intCast(in.psh)) - 1; + const mine = lists[starts[in.rank]..][0..sizes[in.rank]]; + for (blk.phys, 0..) |*p, i| p.* = if (i < mine.len) (in.table[mine[i] >> @intCast(in.psh)] << @intCast(in.psh)) | (mine[i] & mask) else 0; // padding: any stored row + for (part, blk.tokens) |t, *o| { + if (t < 0) { + o.* = -1; + continue; + } + const u: u32 = @intCast(t); + const r = split.ownerOf(u, in.psh, in.world); + const list = lists[starts[r]..][0..sizes[r]]; + const idx = std.sort.binarySearch(u32, list, u, orderU32).?; + o.* = @intCast(r * m + idx); + } + try out.append(gpa, blk); +} + +fn orderU32(a: u32, b: u32) std.math.Order { + return std.math.order(a, b); +} + +/// A segment's selections over `kv_rows` stored rows: k random rows a row (repeats allowed), -1 past them. +fn selections(a: std.mem.Allocator, rnd: std.Random, rows: u32, k: u32, kv_rows: u32) ![]i32 { + const sel = try a.alloc(i32, @as(usize, rows) * k); + for (0..rows) |i| { + const row = sel[i * k ..][0..k]; + const have = @min(k, kv_rows); + for (row[0..have]) |*x| x.* = @intCast(rnd.uintLessThan(u32, kv_rows)); + if (kv_rows <= k) for (row[0..have], 0..) |*x, j| { + x.* = @intCast(j); + }; + @memset(row[have..], -1); + if (i % 7 == 3) row[k - 1] = -1; // a short row + } + return sel; +} + +fn expectSame(in: split.UnionIn, want_blocks: ?usize) !void { + var got: std.ArrayList(split.Block) = .empty; + defer { + for (got.items) |*b| b.deinit(testing.allocator); + got.deinit(testing.allocator); + } + var ref: std.ArrayList(split.Block) = .empty; + defer { + for (ref.items) |*b| b.deinit(testing.allocator); + ref.deinit(testing.allocator); + } + try split.planUnion(testing.allocator, in, &got); + try refRange(testing.allocator, in, 0, @intCast(in.sel.len / in.k), &ref); + if (want_blocks) |n| try testing.expectEqual(n, ref.items.len); + try testing.expectEqual(ref.items.len, got.items.len); + for (ref.items, got.items) |r, g| { + try testing.expectEqual(r.a, g.a); + try testing.expectEqual(r.b, g.b); + try testing.expectEqual(r.m, g.m); + try testing.expectEqualSlices(u32, r.phys, g.phys); + try testing.expectEqualSlices(i32, r.tokens, g.tokens); + } +} + +test "union plan == the sort-and-search plan: 7K, 184K and 1M contexts (1M capped into 8 blocks), both ranks, W 2 and 3" { + var arena = std.heap.ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const a = arena.allocator(); + var prng = std.Random.DefaultPrng.init(0x5eed); + const rnd = prng.random(); + const psh = 6; // 256-token pages at ratio 4: 64 rows a page + const cases = [_]struct { ctx: u32, world: u32, blocks: ?usize }{ + .{ .ctx = 7000, .world = 2, .blocks = 1 }, + .{ .ctx = 184000, .world = 2, .blocks = 1 }, + .{ .ctx = 1000000, .world = 2, .blocks = 8 }, + .{ .ctx = 184000, .world = 3, .blocks = null }, + }; + for (cases) |c| { + const kv_rows = c.ctx / 4; + const sel = try selections(a, rnd, 2048, 512, kv_rows); + // the split table: each logical page's local page (a shuffled, sparse slot) + const table = try a.alloc(u32, c.ctx / 256 + 1); + for (table, 0..) |*t, i| t.* = @intCast(i / c.world); + rnd.shuffle(u32, table); + for (0..c.world) |rank| try expectSame(.{ .sel = sel, .k = 512, .table = table, .psh = psh, .world = c.world, .rank = @intCast(rank), .row_bytes = 584, .cap = 128 << 20 }, c.blocks); + } + // a tight cap: many blocks, down to one row + const sel = try selections(a, rnd, 64, 512, 30000); + const table = try a.alloc(u32, 30000 / 64 + 1); + for (table, 0..) |*t, i| t.* = @intCast(i); + try expectSame(.{ .sel = sel, .k = 512, .table = table, .psh = psh, .world = 2, .rank = 1, .row_bytes = 584, .cap = 3 * 300 * 584 }, null); +} diff --git a/zig/src/families/deepseek_v41/kv_glue_emit_test.zig b/zig/src/families/deepseek_v41/kv_glue_emit_test.zig new file mode 100644 index 0000000000..e31f6cb612 --- /dev/null +++ b/zig/src/families/deepseek_v41/kv_glue_emit_test.zig @@ -0,0 +1,51 @@ +//! Compare fusion emission to the complete unfused program, including every unaffected call. +const std = @import("std"); +const testing = std.testing; +const calls = @import("block_calls.zig"); + +fn named(c: calls.Call, name: []const u8) calls.Arg { + for (c.args) |a| if (std.mem.eql(u8, a.name, name)) return a.arg; + @panic("missing call argument"); +} +fn kvNorm(c: calls.Call) bool { + if (!std.mem.eql(u8, c.name, "_rms")) return false; + const w = named(c, "W"); + return w == .t and w.t.role == .weight and std.mem.endsWith(u8, w.t.role.weight, ".attn.kv_norm"); +} + +pub fn compare(plain: []const calls.Call, fused: []const calls.Call, count: usize) !void { + try testing.expectEqual(plain.len - count, fused.len); + var pi: usize = 0; + var got: usize = 0; + for (fused) |c| { + while (pi < plain.len and kvNorm(plain[pi])) pi += 1; + try testing.expect(pi < plain.len); + if (std.mem.eql(u8, c.name, "tf_dsv41_kv_glue_v1.norm_store")) { + const store = plain[pi]; + try testing.expectEqualStrings("_kv_store", store.name); + try testing.expectEqual(@as(i64, 0), named(store, "RATIO").i); + var ni = pi; + while (ni > 0) { + ni -= 1; + if (kvNorm(plain[ni])) break; + } + const norm = plain[ni]; + try testing.expect(kvNorm(norm)); + try testing.expectEqualDeep(named(norm, "OUT"), named(store, "LAT")); + const want = [_]calls.Arg{ + named(norm, "X"), named(norm, "W"), named(store, "CS"), named(store, "V"), named(store, "S"), + named(store, "POS"), named(store, "SL"), named(norm, "x_rs"), named(store, "cs_stride"), named(store, "v_stride"), + named(store, "s_stride"), named(norm, "inv_k"), named(norm, "eps"), named(store, "RING"), named(store, "ROWS"), + }; + try testing.expectEqual(want.len, c.args.len); + for (want, c.args) |arg, actual| try testing.expectEqualDeep(arg, actual.arg); + try testing.expectEqual(store.side, c.side); + try testing.expectEqual(norm.fork or store.fork, c.fork); + try testing.expectEqual(norm.join or store.join, c.join); + got += 1; + } else try testing.expectEqualDeep(plain[pi], c); + pi += 1; + } + try testing.expectEqual(plain.len, pi); + try testing.expectEqual(count, got); +} diff --git a/zig/src/families/deepseek_v41/kv_state.zig b/zig/src/families/deepseek_v41/kv_state.zig new file mode 100644 index 0000000000..239620ebc9 --- /dev/null +++ b/zig/src/families/deepseek_v41/kv_state.zig @@ -0,0 +1,449 @@ +//! The forward's KV in the paged pool: comp and index-key families in kv.DevicePool, split KV and its exchanges. + +const std = @import("std"); +const quiet = @import("builtin").is_test; // a test's stderr is the build runner's channel +const cuda = @import("cuda"); +const tp = @import("tp"); +const kv = @import("kv"); +const calls = @import("block_calls.zig"); +const block = @import("block.zig"); +const buffers = @import("buffers.zig"); +const run = @import("run.zig"); +const Config = @import("config.zig").Config; + +const sessions = kv.sessions; + +pub const Error = error{ PoolTooSmall, BadGlue, NoPool }; + +pub const Options = struct { + /// TF_DSV41_POOL_TOKENS (whole pages of every residue) + tokens: u64, + /// TF_DSV41_KV_SPLIT (needs TP > 1) + split: bool = false, + /// the live slot's capacity in positions (Forward's limit) + capacity: u64, + /// TF_DSV41_KV_SPLIT_UNION_MIB, bytes + union_cap: u64 = 128 << 20, + /// live slots (slots.zig, TF_DSV41_SLOTS): the pool's slots and the device tables' rows + slots: u32 = 1, + /// bytes the plan holds resident beyond the measured terms (the long-prompt stream scratch); added to boot growth + resident: u64 = 0, + /// TF_DSV41_PF_4K / TF_DSV41_PF_TBO: a long prompt's prefill workspace; worst MemAvailable minus it clears floor + workspace: u64 = 0, +}; + +/// TF_DSV41_POOL_TOKENS / TF_DSV41_KV_SPLIT / TF_DSV41_KV_SPLIT_UNION_MIB (null: no pool, the contiguous slot). +pub fn optionsFromEnv(capacity: u64, world: u32) !?Options { + const t = std.c.getenv("TF_DSV41_POOL_TOKENS") orelse return null; + const tokens = try std.fmt.parseInt(u64, std.mem.span(t), 10); + const split = if (std.c.getenv("TF_DSV41_KV_SPLIT")) |v| std.mem.eql(u8, std.mem.span(v), "1") else false; + if (split and world < 2) return error.SplitNeedsTp; + const mib: u64 = if (std.c.getenv("TF_DSV41_KV_SPLIT_UNION_MIB")) |v| try std.fmt.parseInt(u64, std.mem.span(v), 10) else 128; + // pool settings: rounded up to whole pages of every residue + const unit: u64 = 256 * @as(u64, world); + return .{ .tokens = (tokens + unit - 1) / unit * unit, .split = split, .capacity = capacity, .union_cap = mib << 20 }; +} + +/// The boot check's figures for the measured check at the end of boot (`afterBoot`). +pub const Boot = struct { rank: u32, at_check: f64, pool_gib: f64, pool_tokens: u64, bytes_a_token: f64, reserve: f64, dip: f64, hard: f64, target: f64, workspace: u64 }; + +/// Before the pool (GB10): refuses a pool that cannot fit at all; growth is measured at boot's end (null: not GB10). +pub fn bootCheck(ctx: *const cuda.Context, cfg: *const Config, o: Options, rank: u32, world: u32) !?Boot { + const budget = kv.budget; + const mi = budget.meminfo() catch |e| { + if (!quiet) std.log.warn("kv: no /proc/meminfo ({t}): the measured boot check is skipped", .{e}); + return null; + }; + const dev = try ctx.memInfo(); + const integrated = (ctx.attribute(.integrated) catch 0) != 0; + if (!budget.measuredApplies(budget.modeFromEnv(), integrated, dev.total, mi.total)) return null; + var knobs: budget.Knobs = .{}; + if (std.c.getenv("TF_DSV41_FLOOR_HARD_GIB")) |v| knobs.hard_gib = try std.fmt.parseFloat(f64, std.mem.span(v)); + // TF_DSV41_FLOOR_GIB (5): the floor target the load plans for (0 < hard <= target) + const target: f64 = if (std.c.getenv("TF_DSV41_FLOOR_GIB")) |v| try std.fmt.parseFloat(f64, std.mem.span(v)) else @max(5.0, knobs.hard_gib); + if (!(knobs.hard_gib > 0 and knobs.hard_gib <= target)) { + std.log.err("kv: TF_DSV41_FLOOR_HARD_GIB={d} / TF_DSV41_FLOOR_GIB={d}: expected 0 < hard <= target", .{ knobs.hard_gib, target }); + return error.BadFloor; + } + var layout = try kv.Layout.fromConfig(cfg, .{ .split = o.split and world > 1 }); + // Zig's terms, not the reference's: its plan holds from boot what a caching allocator takes while serving + var terms = try budget.zigFromEnv(); + terms.growth += @as(f64, @floatFromInt(o.resident)) / budget.GiB; + const r = budget.serveCheck(knobs, .{ .rank = rank, .avail_gib = @as(f64, @floatFromInt(mi.available)) / budget.GiB, .terms = terms }, layout.pool(), world, o.tokens); + var buf: [1024]u8 = undefined; + if (!r.ok) { + std.log.err("kv: {s}", .{r.message(&buf)}); + return error.PoolDoesNotFit; + } + std.log.info("kv: before the pool: {s}; the boot's growth is measured at its end", .{r.message(&buf)}); + return .{ .rank = rank, .at_check = r.avail_gib, .pool_gib = r.pool_gib, .pool_tokens = o.tokens, .bytes_a_token = r.bytes_a_token, .reserve = try budget.reserveFromEnv(), .dip = terms.dip, .hard = r.hard, .target = target, .workspace = o.workspace }; +} + +/// At boot's end: idle - dip >= the hard floor or the pool is refused; a 4K workspace past it warns (wsAgreed). +pub fn afterBoot(b: Boot) !void { + const budget = kv.budget; + const idle = @as(f64, @floatFromInt((try budget.meminfo()).available)) / budget.GiB; + const x = budget.afterBoot(b.at_check, b.pool_gib, idle, b.reserve, b.dip, b.hard, b.pool_tokens, b.bytes_a_token); + const msg = "kv: rank {d}: boot growth {d:.2} GiB measured (MemAvailable {d:.2} at the check - pool {d:.2} - {d:.2} now); worst serving point {d:.2} GiB (post-boot reserve {d:.2}, TF_DSV41_POST_BOOT_GIB; dip {d:.2}, TF_DSV41_SERVE_DIP_GIB; hard floor {d:.1})"; + const args = .{ b.rank, x.growth, b.at_check, b.pool_gib, idle, x.worst, b.reserve, b.dip, b.hard }; + if (!x.ok) { + std.log.err(msg ++ ": the pool does not fit; the largest that does: TF_DSV41_POOL_TOKENS={d}", args ++ .{x.largest}); + return error.PoolDoesNotFit; + } + std.log.info(msg, args); + if (b.workspace > 0) { + const ws = @as(f64, @floatFromInt(b.workspace)) / budget.GiB; + const left = x.worst - ws; + if (left < b.hard) if (!quiet) std.log.warn("kv: rank {d}: the prefill workspace ({d:.2} GiB, TF_DSV41_PF_4K / TF_DSV41_PF_TBO) at the worst point leaves {d:.2} GiB, under the {d:.1} GiB hard floor: long prompts there run 2,048-row segments", .{ b.rank, ws, left, b.hard }); + } + if (x.worst < b.target) if (!quiet) std.log.warn("kv: memory: floor {d:.2} GiB is under the {d} GiB target (TF_DSV41_FLOOR_GIB)", .{ x.worst, b.target }); +} + +pub const Kv = struct { + gpa: std.mem.Allocator, + layout: kv.Layout, + pool: sessions.Pool, + dev: kv.DevicePool, + /// the current slot (one-slot operations), one of `slots` + slot: *sessions.Slot, + slots: []*sessions.Slot = &.{}, + opts: Options, + world: u32, + rank: u32, + comm: tp.collective.Collective, + /// the forward's runner (kernels, stream) for the exchange's copies + runner: *run.Runner, + /// a union's device list of local rows to send (grows) + phys: ?cuda.DeviceBuffer = null, + /// the exchange (kv/split.zig, dense or TF_DSV41_KV_SPLIT_COMPACT) and its persistent int32 [W] owner lengths + x: kv.split.Exchange = undefined, + ops: Ops = undefined, + lens: ?cuda.DeviceBuffer = null, + /// TF_DSV41_KV_SPLIT_DEDUP's map and scratch (kv/split.zig Dedup) + dedup: ?cuda.DeviceBuffer = null, + /// TF_DSV41_KV_SPLIT_HISTORY: a union that fits the cap sends the slot's owned rows (kv/split.zig HistArgs) + hist: bool = false, + stats: Stats = .{}, + /// the last union's row blocks when it took several (empty: one block); longpf.zig exchanges them block by block + blocks: std.ArrayList(kv.split.Block) = .empty, + block_phys: std.ArrayList(u32) = .empty, + union_at: struct { comp: u64 = 0, send: u64 = 0, recv: u64 = 0 } = .{}, + /// a union's pinned staging: the selection read back, the tokens and rows sent up (reused after the next drain) + pin_sel: ?cuda.HostBuffer = null, + pin_up: ?cuda.HostBuffer = null, + + pub const Stats = struct { dense: u64 = 0, unions: u64 = 0, union_rows_max: u64 = 0, bytes: u64 = 0 }; + + pub fn init(gpa: std.mem.Allocator, d: *const cuda.Driver, ctx: *const cuda.Context, cfg: *const Config, o: Options, runner: *run.Runner, comm: tp.collective.Collective) !*Kv { + const world: u32 = @intCast(comm.world()); + const k = try gpa.create(Kv); + errdefer gpa.destroy(k); + k.* = .{ .gpa = gpa, .layout = try kv.Layout.fromConfig(cfg, .{ .split = o.split and world > 1 }), .pool = undefined, .dev = undefined, .slot = undefined, .opts = o, .world = world, .rank = @intCast(comm.rank()), .comm = comm, .runner = runner }; + k.pool = try sessions.Pool.init(gpa, k.layout.pool(), o.tokens, if (k.layout.opts.split) world else 1, if (k.layout.opts.split) k.rank else 0); + errdefer k.pool.deinit(); + k.slots = try gpa.alloc(*sessions.Slot, @max(o.slots, 1)); + errdefer gpa.free(k.slots); + for (k.slots) |*s| s.* = try k.pool.newSlot(o.capacity); + k.slot = k.slots[0]; + k.dev = try kv.DevicePool.init(gpa, d, ctx, &k.pool, runner.stream, @intCast(k.slots.len), k.slot.max_pages); + errdefer k.dev.deinit(); + k.ops = .{ .r = runner }; + if (k.layout.opts.split) { + k.lens = try cuda.DeviceBuffer.alloc(d, 4 * @as(usize, world)); + // the compact mode's transport agreement happens here, once, outside any graph + k.x = try kv.split.Exchange.init(comm, k.ops.kernels(), try kv.split.compactMode()); + if (kv.split.dedupOn()) try k.initDedup(d, cfg.index_topk); + if (kv.split.historyOn()) { + if (runner.kernels.ops.kv_hist == null) { + std.log.err("[tensorfold] TF_DSV41_KV_SPLIT_HISTORY=1: the kvsplit fatbin has no history kernels (rebuild it)", .{}); + return error.MissingKernel; + } + k.hist = true; + } + } + return k; + } + + /// The dedup scratch: a key map over every slot's rows (ratio 1), INT_MAX; entries for 64 rows (wider: no dedup). + fn initDedup(k: *Kv, d: *const cuda.Driver, topk: u32) !void { + if (k.runner.kernels.ops.kv_dedup == null) { + std.log.err("[tensorfold] TF_DSV41_KV_SPLIT_DEDUP=1: the kvsplit fatbin has no dedup kernels (rebuild it)", .{}); + return error.MissingKernel; + } + const slot_rows = @as(u64, k.slot.max_pages) * block.Pool.page; + const map_rows = slot_rows * k.slots.len; + const entries = 64 * @as(u64, topk); + const bcnt = (entries + 255) / 256 * kv.split.max_world; + const buf = try cuda.DeviceBuffer.alloc(d, @intCast(4 * (map_rows + 2 * entries + bcnt))); + k.dedup = buf; + try buf.fill32(std.math.maxInt(i32), null); + k.x.dedup = .{ .map = buf.ptr, .map_rows = map_rows, .first = buf.ptr + 4 * map_rows, .place = buf.ptr + 4 * (map_rows + entries), .bcnt = buf.ptr + 4 * (map_rows + 2 * entries), .entries = entries, .slots = @intCast(k.slots.len) }; + if (k.rank == 0) std.log.info("[tensorfold] split KV dedup: {d} MiB (map {d} rows, {d} entries)", .{ buf.len >> 20, map_rows, entries }); + } + + pub fn deinit(k: *Kv) void { + k.dropBlocks(); + k.blocks.deinit(k.gpa); + k.block_phys.deinit(k.gpa); + if (k.phys) |*b| b.free(); + if (k.lens) |*b| b.free(); + if (k.dedup) |*b| b.free(); + if (k.pin_sel) |*b| b.free(); + if (k.pin_up) |*b| b.free(); + k.dev.deinit(); + k.pool.deinit(); + k.gpa.free(k.slots); + k.gpa.destroy(k); + } + + /// Slot i becomes the current one (slots.zig: one-slot operations on slot i). + pub fn select(k: *Kv, i: u32) void { + k.slot = k.slots[i]; + } + + /// The emitter's view of the pool (block.Options.pool). + pub fn options(k: *Kv) block.Pool { + const fams = k.layout.families(); + return .{ + .comp_pages = k.pool.familyTensorPages(fams[0]), + .ik_pages = k.pool.familyTensorPages(fams[1]), + .pts = k.slot.max_pages, + .split = k.layout.opts.split, + .early = k.layout.opts.split and kv.split.earlyOn(), + .union_cap = @intCast(k.opts.union_cap), + }; + } + + /// Points the window's pool roles at the device pool's buffers (before Runner.bind), checking their coverage. + pub fn bind(k: *Kv, r: *run.Runner, plan: *const buffers.Plan) !void { + var nb: [64]u8 = undefined; + for (k.layout.sources[0..k.layout.nsources]) |L| { + const c = k.layout.compOf(L).?; + try k.external(r, plan, try std.fmt.bufPrint(&nb, "s.kv.comp.L{d}", .{L}), k.dev.tensors[c]); + try k.external(r, plan, try std.fmt.bufPrint(&nb, "s.kv.ik.L{d}", .{L}), k.dev.tensors[c + 1]); + } + try k.external(r, plan, "s.kv.pt", k.dev.table); + try k.external(r, plan, "s.kv.ct", k.dev.local); + } + + fn external(_: *Kv, r: *run.Runner, plan: *const buffers.Plan, role: []const u8, b: cuda.DeviceBuffer) !void { + if (plan.sizes.get(role)) |need| if (need > b.len) return error.PoolTooSmall; + try r.external(role, b.ptr); + } + + /// Maps every page holding positions < end and uploads the changed table entries (before a window's launches). + pub fn reserve(k: *Kv, end: u64) !void { + try k.slot.ensure(end); + try k.dev.syncTables(); + } + + /// Keeps the pages holding positions < keep (a rollback, or a release at 0). + pub fn truncate(k: *Kv, keep: u64) !void { + _ = try k.slot.truncate(keep); + try k.dev.syncTables(); + } + + /// The slot's pages back to the pool (a session entry may still hold them). + pub fn release(k: *Kv) !void { + _ = try k.slot.releaseAll(); + try k.dev.syncTables(); + } + + // -- split KV's exchange (block.zig Emitter.exchange: sel, comp family, split table, send, recv, tokens, psh) -- + + pub fn glue(k: *Kv, r: *run.Runner, c: *const calls.Call, step: []const u8) !void { + k.dropBlocks(); + if (std.mem.eql(u8, step, "kx_dense")) return k.dense(r, c); + if (std.mem.eql(u8, step, "kx_union")) return k.unionOf(r, c); + return error.BadGlue; + } + + fn args(r: *run.Runner, c: *const calls.Call) !struct { sel: calls.Tensor, comp: u64, table: u64, send: u64, recv: u64, tok: u64, psh: u32 } { + const x = c.args; + if (x.len != 7) return error.BadGlue; + return .{ + .sel = x[0].arg.t, + .comp = try r.tensorAddr(x[1].arg.t), + .table = try r.tensorAddr(x[2].arg.t), + .send = try r.tensorAddr(x[3].arg.t), + .recv = try r.tensorAddr(x[4].arg.t), + .tok = try r.tensorAddr(x[5].arg.t), + .psh = @intCast(x[6].arg.i), + }; + } + + /// Every rank sends its owned selected rows (a discard row elsewhere) in one all-gather; token owner x R x K + j. + fn dense(k: *Kv, r: *run.Runner, c: *const calls.Call) !void { + const a = try args(r, c); + const rows: u32 = @intCast(a.sel.shape[0]); + const kk: u32 = @intCast(a.sel.shape[1]); + const da: kv.split.DenseArgs = .{ + .sel = try r.tensorAddr(a.sel), + .rows = rows, + .k = kk, + .table = a.table, + .pts = 0, + .rslot = 0, + .psh = a.psh, + .base = a.comp, + .row_bytes = block.Pool.row_bytes, + .world = k.world, + .send = a.send, + .tok = a.tok, + }; + const bytes = @as(usize, rows) * kk * block.Pool.row_bytes; + // packed where the compact mode and the transport take it (the same answer on every rank), else dense + try k.x.window(kv.split.PackArgs.of(da, k.rank, k.lens.?.ptr), a.recv, r.stream.handle); + k.stats.dense += 1; + k.stats.bytes += bytes * k.world; + } + + /// A prefill segment's union: sorted unique selected rows, padded per owner (one host sync), token owner x M + i. + fn unionOf(k: *Kv, r: *run.Runner, c: *const calls.Call) !void { + const a = try args(r, c); + const n: usize = @intCast(a.sel.shape[0]); + const kk: u32 = @intCast(a.sel.shape[1]); + if (k.hist) { + const m = kv.split.historyRows(k.slot.len, a.psh, k.world); + if (m > 0 and m <= k.opts.union_cap / ((@as(u64, k.world) + 1) * block.Pool.row_bytes)) return k.historyOf(r, a, n, kk, m); + } + // the drain also ends the last union's async uploads, so its pinned staging is free again + try r.stream.synchronize(); + const sel = (try pinned(r.d, &k.pin_sel, n * kk * 4)).slice(i32)[0 .. n * kk]; + try cuda.DeviceBuffer.download(.{ .d = r.d, .ptr = try r.tensorAddr(a.sel), .len = sel.len * 4 }, 0, std.mem.sliceAsBytes(sel)); + const table = try k.gpa.alloc(u32, k.slot.max_pages); + defer k.gpa.free(table); + for (table, 0..) |*t, i| t.* = k.slot.localTableAt(@intCast(i)); + var blocks: std.ArrayList(kv.split.Block) = .empty; + defer { + for (blocks.items) |*b| b.deinit(k.gpa); + blocks.deinit(k.gpa); + } + try kv.split.planUnion(k.gpa, .{ .sel = sel, .k = kk, .table = table, .psh = a.psh, .world = k.world, .rank = k.rank, .row_bytes = block.Pool.row_bytes, .cap = k.opts.union_cap }, &blocks); + if (blocks.items.len != 1) return k.keepBlocks(r, a.comp, a.send, a.recv, a.tok, kk, &blocks); + const b = blocks.items[0]; + if (k.phys == null or k.phys.?.len < 4 * b.phys.len) { + if (k.phys) |*p| p.free(); + k.phys = try cuda.DeviceBuffer.alloc(r.d, @max(4 * b.phys.len, 256)); + } + const up = (try pinned(r.d, &k.pin_up, 4 * (b.phys.len + b.tokens.len))).slice(u32); + @memcpy(up[0..b.phys.len], b.phys); + @memcpy(up[b.phys.len..][0..b.tokens.len], std.mem.bytesAsSlice(u32, std.mem.sliceAsBytes(b.tokens))); + try cuda.DeviceBuffer.uploadAsync(k.phys.?, 0, std.mem.sliceAsBytes(up[0..b.phys.len]), r.stream.handle); + try cuda.DeviceBuffer.uploadAsync(.{ .d = r.d, .ptr = a.tok, .len = 4 * b.tokens.len }, 0, std.mem.sliceAsBytes(up[b.phys.len..][0..b.tokens.len]), r.stream.handle); + try k.x.unionBlock(a.comp, block.Pool.row_bytes, k.phys.?.ptr, b.m, a.send, a.recv, r.stream.handle); + k.stats.unions += 1; + k.stats.union_rows_max = @max(k.stats.union_rows_max, @as(u64, b.m) * k.world); + k.stats.bytes += @as(u64, b.m) * block.Pool.row_bytes * k.world; + } + + /// The union by history (TF_DSV41_KV_SPLIT_HISTORY): no selection read back, no plan, no drain; M rows an owner. + fn historyOf(k: *Kv, r: *run.Runner, a: anytype, n: usize, kk: u32, m: u32) !void { + const h: kv.split.HistArgs = .{ + .sel = try r.tensorAddr(a.sel), + .rows = @intCast(n), + .k = kk, + .table = a.table, + .pages = k.slot.len, + .psh = a.psh, + .rank = k.rank, + .world = k.world, + .m = m, + .row_bytes = block.Pool.row_bytes, + .base = a.comp, + .send = a.send, + .tok = a.tok, + }; + try k.x.history(h, a.recv, r.stream.handle); + k.stats.unions += 1; + k.stats.union_rows_max = @max(k.stats.union_rows_max, @as(u64, m) * k.world); + k.stats.bytes += @as(u64, m) * block.Pool.row_bytes * k.world; + } + + /// A pinned staging buffer of at least `len` bytes (grown, never shrunk). + fn pinned(d: *const cuda.Driver, b: *?cuda.HostBuffer, len: usize) !cuda.HostBuffer { + if (b.*) |x| if (x.bytes.len >= len) return x; + if (b.*) |*x| x.free(); + b.* = null; + b.* = try cuda.HostBuffer.alloc(d, @max(len, 256)); + return b.*.?; + } + + fn dropBlocks(k: *Kv) void { + for (k.blocks.items) |*b| b.deinit(k.gpa); + k.blocks.clearRetainingCapacity(); + } + + /// A union of several row blocks: every block's rows and tokens uploaded once; exchanges wait for `fetchBlock`. + fn keepBlocks(k: *Kv, r: *run.Runner, comp: u64, send: u64, recv: u64, tok: u64, kk: u32, blocks: *std.ArrayList(kv.split.Block)) !void { + k.block_phys.clearRetainingCapacity(); + for (blocks.items) |b| { + try k.block_phys.appendSlice(k.gpa, b.phys); + try cuda.DeviceBuffer.upload(.{ .d = r.d, .ptr = tok + 4 * @as(u64, b.a) * kk, .len = 4 * b.tokens.len }, 0, std.mem.sliceAsBytes(b.tokens)); + } + const bytes = 4 * k.block_phys.items.len; + if (k.phys == null or k.phys.?.len < bytes) { + if (k.phys) |*p| p.free(); + k.phys = try cuda.DeviceBuffer.alloc(r.d, @max(bytes, 256)); + } + try cuda.DeviceBuffer.upload(k.phys.?, 0, std.mem.sliceAsBytes(k.block_phys.items)); + k.union_at = .{ .comp = comp, .send = send, .recv = recv }; + std.mem.swap(std.ArrayList(kv.split.Block), &k.blocks, blocks); + } + + /// Block j of the kept union: its rows from their owners into the receive buffer (on the compute stream). + pub fn fetchBlock(k: *Kv, r: *run.Runner, j: usize) !void { + var off: u64 = 0; + for (k.blocks.items[0..j]) |b| off += b.phys.len; + const b = k.blocks.items[j]; + try k.x.unionBlock(k.union_at.comp, block.Pool.row_bytes, k.phys.?.ptr + 4 * off, b.m, k.union_at.send, k.union_at.recv, r.stream.handle); + k.stats.unions += 1; + k.stats.union_rows_max = @max(k.stats.union_rows_max, @as(u64, b.m) * k.world); + k.stats.bytes += @as(u64, b.m) * block.Pool.row_bytes * k.world; + } +}; + +/// kv/split.zig's device kernels over the engine's kvsplit ops (kernels_ops: the same argument layouts). +const Ops = struct { + r: *run.Runner, + + fn kernels(o: *Ops) kv.split.Kernels { + return .{ .ptr = o, .vtable = &.{ .dense = dense, .gather = gather, .pack = pack, .dedup = dedup, .history = history } }; + } + + fn self(ptr: *anyopaque) *Ops { + return @ptrCast(@alignCast(ptr)); + } + + fn dense(ptr: *anyopaque, a: kv.split.DenseArgs, _: tp.collective.Stream) anyerror!void { + const r = self(ptr).r; + try r.kernels.others(r.stream).kvDense(.{ .sel = a.sel, .rows = a.rows, .k = a.k, .table = a.table, .pts = a.pts, .rslot = a.rslot, .psh = a.psh, .base = a.base, .row_bytes = a.row_bytes, .world = a.world, .send = a.send, .tok = a.tok }); + } + + fn gather(ptr: *anyopaque, base: u64, row_bytes: u32, phys: u64, n: u32, send: u64, _: tp.collective.Stream) anyerror!void { + const r = self(ptr).r; + try r.kernels.others(r.stream).kvGather(base, row_bytes, phys, n, send); + } + + fn pack(ptr: *anyopaque, a: kv.split.PackArgs, _: tp.collective.Stream) anyerror!void { + const r = self(ptr).r; + try r.kernels.others(r.stream).kvPack(.{ .sel = a.sel, .rows = a.rows, .k = a.k, .table = a.table, .pts = a.pts, .rslot = a.rslot, .psh = a.psh, .rank = a.rank, .base = a.base, .row_bytes = a.row_bytes, .world = a.world, .send = a.send, .tok = a.tok, .lens = a.lens }); + } + + fn history(ptr: *anyopaque, a: kv.split.HistArgs, _: tp.collective.Stream) anyerror!void { + const r = self(ptr).r; + try r.kernels.others(r.stream).kvHistory(.{ .sel = a.sel, .rows = a.rows, .k = a.k, .table = a.table, .pages = a.pages, .psh = a.psh, .rank = a.rank, .world = a.world, .m = a.m, .row_bytes = a.row_bytes, .base = a.base, .send = a.send, .tok = a.tok }); + } + + fn dedup(ptr: *anyopaque, a: kv.split.DedupArgs, _: tp.collective.Stream) anyerror!void { + const r = self(ptr).r; + const p = a.p; + try r.kernels.others(r.stream).kvDedup(.{ + .p = .{ .sel = p.sel, .rows = p.rows, .k = p.k, .table = p.table, .pts = p.pts, .rslot = p.rslot, .psh = p.psh, .rank = p.rank, .base = p.base, .row_bytes = p.row_bytes, .world = p.world, .send = p.send, .tok = p.tok, .lens = p.lens }, + .map = a.map, + .first = a.first, + .place = a.place, + .bcnt = a.bcnt, + }); + } +}; diff --git a/zig/src/families/deepseek_v41/load.zig b/zig/src/families/deepseek_v41/load.zig new file mode 100644 index 0000000000..881e3529cc --- /dev/null +++ b/zig/src/families/deepseek_v41/load.zig @@ -0,0 +1,94 @@ +//! A rank's named weights on the GPU: one device buffer a tensor (bound by name), digested on the way up. + +const std = @import("std"); +const cuda = @import("cuda"); +const named = @import("named.zig"); +const ex_stacks = @import("ex_stacks.zig"); + +pub const Tensor = struct { + ptr: u64, + len: usize, + kind: named.Kind, + sha256: [32]u8, + shape: [4]usize = @splat(1), + rank: u8 = 0, + + pub fn dims(t: *const Tensor) []const usize { + return t.shape[0..t.rank]; + } +}; + +pub const Weights = struct { + gpa: std.mem.Allocator, + bufs: std.ArrayList(cuda.DeviceBuffer) = .empty, + map: std.StringHashMapUnmanaged(Tensor) = .empty, + bytes: usize = 0, + /// each tensor's SHA-256 taken on the way up (the weights check reads it); off, `sha256` is zeros (served boot) + digest: bool = true, + + pub fn deinit(w: *Weights) void { + for (w.bufs.items) |*b| b.free(); + w.bufs.deinit(w.gpa); + var it = w.map.keyIterator(); + while (it.next()) |k| w.gpa.free(k.*); + w.map.deinit(w.gpa); + w.* = undefined; + } + + /// Uploads every tensor `b` holds (its host images can be freed afterwards); a name already on the GPU is refused. + pub fn upload(w: *Weights, d: *const cuda.Driver, b: *const named.Builder) !void { + // TF_DSV41_EX_STACKS: a routed suh / svh and its shared expert's in one buffer (ex_stacks.zig) + var paired: std.AutoHashMapUnmanaged(usize, void) = .empty; + defer paired.deinit(w.gpa); + if (ex_stacks.enabled()) { + const names = try w.gpa.alloc([]const u8, b.out.items.len); + defer w.gpa.free(names); + const lens = try w.gpa.alloc(usize, b.out.items.len); + defer w.gpa.free(lens); + for (b.out.items, names, lens) |n, *x, *l| { + x.* = n.name; + l.* = n.bytes.len; + } + const ps = try ex_stacks.pairs(w.gpa, names, lens); + defer w.gpa.free(ps); + for (ps) |p| { + const r = b.out.items[p[0]]; + const s = b.out.items[p[1]]; + if (w.map.contains(r.name) or w.map.contains(s.name)) return error.DuplicateWeight; + var buf = try cuda.DeviceBuffer.alloc(d, r.bytes.len + s.bytes.len); + errdefer buf.free(); + try cuda.DeviceBuffer.upload(buf, 0, r.bytes); + try cuda.DeviceBuffer.upload(buf, r.bytes.len, s.bytes); + try w.bufs.append(w.gpa, buf); + try w.put(r, buf.ptr); + try w.put(s, buf.ptr + r.bytes.len); + try paired.put(w.gpa, p[0], {}); + try paired.put(w.gpa, p[1], {}); + } + } + for (b.out.items, 0..) |n, i| { + if (paired.contains(i)) continue; + if (w.map.contains(n.name)) return error.DuplicateWeight; + var sha: [32]u8 = @splat(0); + if (w.digest) std.crypto.hash.sha2.Sha256.hash(n.bytes, &sha, .{}); + var buf = try cuda.DeviceBuffer.fromHost(d, n.bytes); + errdefer buf.free(); + try w.bufs.append(w.gpa, buf); + try w.map.put(w.gpa, try w.gpa.dupe(u8, n.name), .{ .ptr = buf.ptr, .len = n.bytes.len, .kind = n.kind, .sha256 = sha, .shape = n.shape, .rank = n.rank }); + w.bytes += n.bytes.len; + } + // pageable uploads can still be in flight when cuMemcpyHtoD returns; the weights are whole before any launch + try d.check(d.api.cuCtxSynchronize(), "cuCtxSynchronize"); + } + + fn put(w: *Weights, n: named.Named, ptr: u64) !void { + var sha: [32]u8 = @splat(0); + if (w.digest) std.crypto.hash.sha2.Sha256.hash(n.bytes, &sha, .{}); + try w.map.put(w.gpa, try w.gpa.dupe(u8, n.name), .{ .ptr = ptr, .len = n.bytes.len, .kind = n.kind, .sha256 = sha, .shape = n.shape, .rank = n.rank }); + w.bytes += n.bytes.len; + } + + pub fn get(w: *const Weights, name: []const u8) ?Tensor { + return w.map.get(name); + } +}; diff --git a/zig/src/families/deepseek_v41/longpf.zig b/zig/src/families/deepseek_v41/longpf.zig new file mode 100644 index 0000000000..83068c6607 --- /dev/null +++ b/zig/src/families/deepseek_v41/longpf.zig @@ -0,0 +1,227 @@ +//! Long prompts on the prefill, up to the 1M limit: stream top-k selection and split-KV attention in row blocks. + +const std = @import("std"); +const cuda = @import("cuda"); +const calls = @import("block_calls.zig"); +const run = @import("run.zig"); + +/// stream_topk.SPLIT: keys a program scans (positions mode) +pub const split_keys: i64 = 16384; +/// stream_topk.BUDGET: bytes of selection scratch a launch (ENGINE-PLAN 5), not the index budget +pub const budget: i64 = 256 << 20; +/// the runtime's own role for a row block's first position (Runner.external) +pub const pos_role = "s.longpf.pos"; + +/// stream_topk.splits: programs a row over `keys` positions. +pub fn splits(keys: i64) i64 { + return @max(1, @divFloor(keys + split_keys - 1, split_keys)); +} + +/// stream_topk.plan: rows a launch so its scratch (rows x splits x 2 K int64) fits the budget. +pub fn plan(rows: i64, keys: i64, k: i64) i64 { + const per = splits(keys) * 2 * k * 8; + return @max(1, @min(rows, @divFloor(budget, per))); +} + +/// TF_DSV41_STREAM_LAZY=1: the stream top-k's scratch held only while prompts that name it prefill (256 MiB at 1M). +pub const lazy_role = "L.ix.stream"; + +pub fn lazyEnv() bool { + const v = std.c.getenv("TF_DSV41_STREAM_LAZY") orelse return false; + return std.mem.eql(u8, std.mem.trim(u8, std.mem.span(v), " "), "1"); +} + +/// The on-demand scratch: the plan's size for it, and its buffer while a prompt holds it. +pub const Lazy = struct { bytes: u64, buf: ?cuda.DeviceBuffer = null, allocs: u64 = 0 }; + +/// Whether a program names `role` (the stream select's scratch, or any other). +pub fn names(cs: []const calls.Call, role: []const u8) bool { + for (cs) |c| for (c.args) |x| if (namesArg(x.arg, role)) return true; + return false; +} + +fn namesArg(x: calls.Arg, role: []const u8) bool { + return switch (x) { + .list => |items| for (items) |y| { + if (namesArg(y, role)) break true; + } else false, + .t, .opaque_table => |t| t.role == .buf and std.mem.eql(u8, t.role.buf, role), + else => false, + }; +} + +/// The runtime's buffers (forward_prefill.State.long). +pub const State = struct { + /// TF_DSV41_STREAM_LAZY: the stream scratch on demand (null: the plan holds it) + lazy: ?Lazy = null, + /// uint32 0, 2, 4, ...: the merge's gather rows (a split's first K slots are every other K-slot row) + even: ?cuda.DeviceBuffer = null, + even_host: std.ArrayList(u32) = .empty, + /// int32 [1]: a row block's first position (the attention's POS) + pos: ?cuda.DeviceBuffer = null, + + pub fn deinit(s: *State, gpa: std.mem.Allocator) void { + if (s.lazy) |*z| if (z.buf) |*b| b.free(); + if (s.even) |*b| b.free(); + if (s.pos) |*b| b.free(); + s.even_host.deinit(gpa); + } + + /// The gather rows for `n` (row, split) pairs, grown on the compute stream (the host copy stays alive). + fn evenRows(s: *State, gpa: std.mem.Allocator, r: *run.Runner, n: usize) !u64 { + if (s.even_host.items.len < n) { + const want = std.math.ceilPowerOfTwo(usize, n) catch n; + const old = s.even_host.items.len; + try s.even_host.resize(gpa, want); + for (s.even_host.items[old..], old..) |*v, i| v.* = @intCast(2 * i); + if (s.even) |*b| b.free(); + s.even = null; + } + if (s.even == null) { + s.even = try cuda.DeviceBuffer.alloc(r.d, 4 * s.even_host.items.len); + try r.d.check(r.d.api.cuMemcpyHtoDAsync_v2(s.even.?.ptr, s.even_host.items.ptr, 4 * s.even_host.items.len, r.stream.handle), "cuMemcpyHtoDAsync"); + } + return s.even.?.ptr; + } +}; + +fn addr(r: *run.Runner, x: calls.Arg) !u64 { + return r.tensorAddr(x.t); +} + +/// glue.stream_merge(buf int64 [R, ns, 2 K], out int32 [R, K], K, keys int64 [R, ns K]): stream_topk.merge. +pub fn merge(s: *State, gpa: std.mem.Allocator, r: *run.Runner, c: *const calls.Call) !void { + const x = c.args; + if (x.len != 4) return error.BadGlue; + const buf = x[0].arg.t; + const R: usize = @intCast(buf.shape[0]); + const ns: usize = @intCast(buf.shape[1]); + const cap: usize = @intCast(buf.shape[2]); + const k: usize = @intCast(x[2].arg.i); + if (cap != 2 * k or x[1].arg.t.shape[1] != k) return error.BadGlue; + const ops = r.kernels.others(r.stream); + if (ns == 1) return ops.topPositions(try addr(r, x[0].arg), cap, R, k, k, try addr(r, x[1].arg)); + const keys = try addr(r, x[3].arg); + try ops.kvGather(try addr(r, x[0].arg), @intCast(8 * k), try s.evenRows(gpa, r, R * ns), @intCast(R * ns), keys); + return ops.topPositions(keys, ns * k, R, ns * k, k, try addr(r, x[1].arg)); +} + +fn named(c: *const calls.Call, name: []const u8) ?calls.Tensor { + for (c.args) |x| if (std.mem.eql(u8, x.name, name)) return switch (x.arg) { + .t => |t| t, + else => null, + }; + return null; +} + +/// A `_fused` attention reading split KV's exchanged rows. +pub fn readsExchange(c: *const calls.Call) bool { + if (!c.triton or !std.mem.eql(u8, c.name, "_fused")) return false; + const cv = named(c, "CV") orelse return false; + return cv.role == .buf and std.mem.eql(u8, cv.role.buf, "w.kx.recv"); +} + +/// `c` (a `_fused` launch) over its rows [a, b): row-indexed tensors moved by a, POS its own, grid rows b - a. +pub fn blockCall(al: std.mem.Allocator, c: *const calls.Call, a: i64, b: i64) !calls.Call { + const args = try al.dupe(calls.Named, c.args); + for (args) |*x| { + const t = switch (x.arg) { + .t => |t| t, + else => continue, + }; + const nm = x.name; + const rowed = std.mem.eql(u8, nm, "Q") or std.mem.eql(u8, nm, "TOK") or std.mem.eql(u8, nm, "CNT") or std.mem.eql(u8, nm, "LO"); + if (rowed) { + const shape = try al.dupe(i64, t.shape); + shape[0] = b - a; + x.arg = .{ .t = .{ .role = t.role, .dt = t.dt, .shape = shape, .stride = t.stride, .offset = t.offset + a * t.stride[0] * dtSize(t.dt) } }; + } else if (std.mem.eql(u8, nm, "OUT")) { + // group-major [groups, OR, HG x 512]: row r of every group at (g OR + r) rows; the base moves by a rows + x.arg = .{ .t = .{ .role = t.role, .dt = t.dt, .shape = t.shape, .stride = t.stride, .offset = t.offset + a * t.stride[1] * dtSize(t.dt) } }; + } else if (std.mem.eql(u8, nm, "POS")) { + x.arg = .{ .t = .{ .role = .{ .buf = pos_role }, .dt = .i32, .shape = &.{1}, .stride = &.{1} } }; + } + } + var out = c.*; + out.args = args; + out.grid[0] = b - a; + return out; +} + +fn dtSize(dt: calls.Dt) i64 { + return switch (dt) { + .bool, .i8, .u8 => 1, + .bf16, .f16, .i16 => 2, + .f32, .i32 => 4, + .f64, .i64 => 8, + }; +} + +/// A prefill segment's calls, as Runner.window, but a union kept in row blocks attends block by block (`k`: ?*Kv). +pub fn window(s: *State, gpa: std.mem.Allocator, r: *run.Runner, k: anytype, start: u64, cs: []const calls.Call) !void { + try r.guard(cs); + if (k) |kx| if (kx.blocks.items.len > 0) for (cs) |*c| if (readsExchange(c)) try guardBlocks(gpa, r, kx, c); + if (s.lazy) |*z| if (z.buf == null and names(cs, lazy_role)) try acquire(z, r); + const p = r.prof; + if (p) |x| x.beginPrefill(); + for (cs) |*c| { + if (p) |x| try x.before(r.stream, c); + if (k) |kx| if (kx.blocks.items.len > 0 and readsExchange(c)) { + try attendBlocks(s, gpa, r, kx, start, c); + if (p) |x| try x.after(r.stream); + continue; + }; + try r.issue(c); + if (p) |x| try x.after(r.stream); + } + if (p) |x| try x.end(r.stream); +} + +/// The scratch for a prompt that streams, zeroed as the plan's arena is (every rank in the same order). +fn acquire(z: *Lazy, r: *run.Runner) !void { + const b = try cuda.DeviceBuffer.alloc(r.d, @max(z.bytes, 256)); + errdefer { + var x = b; + x.free(); + } + try b.fill8(0, r.stream.handle); + try r.external(lazy_role, b.ptr); + z.buf = b; + z.allocs += 1; + if (z.allocs <= 4 or z.allocs % 64 == 0) std.log.scoped(.dsv41).info("stream top-k scratch: {d} MiB allocated (prompt {d}), held while its prompts prefill (TF_DSV41_STREAM_LAZY)", .{ z.bytes >> 20, z.allocs }); +} + +/// Its prompts prefilled: the scratch freed once the stream is past its last use. +pub fn release(s: *State, r: *run.Runner) !void { + const z = &(s.lazy orelse return); + var b = z.buf orelse return; + try r.stream.synchronize(); + r.forget(lazy_role); + b.free(); + z.buf = null; +} + +/// The calls attendBlocks issues for `c`, one a block, probed before the window's first launch (Runner.guard). +fn guardBlocks(gpa: std.mem.Allocator, r: *run.Runner, k: anytype, c: *const calls.Call) !void { + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + const subs = try arena.allocator().alloc(calls.Call, k.blocks.items.len); + for (k.blocks.items, subs) |b, *sub| sub.* = try blockCall(arena.allocator(), c, b.a, b.b); + try r.guard(subs); +} + +fn attendBlocks(s: *State, gpa: std.mem.Allocator, r: *run.Runner, k: anytype, start: u64, c: *const calls.Call) !void { + if (s.pos == null) { + s.pos = try cuda.DeviceBuffer.alloc(r.d, 256); + try r.external(pos_role, s.pos.?.ptr); + } + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + for (k.blocks.items, 0..) |b, j| { + try k.fetchBlock(r, j); + const p: i32 = @intCast(start + b.a); + try r.d.check(r.d.api.cuMemsetD32Async(s.pos.?.ptr, @bitCast(p), 1, r.stream.handle), "cuMemsetD32Async"); + const sub = try blockCall(arena.allocator(), c, b.a, b.b); + try r.issue(&sub); + } +} diff --git a/zig/src/families/deepseek_v41/longpf_test.zig b/zig/src/families/deepseek_v41/longpf_test.zig new file mode 100644 index 0000000000..8b82a0b4af --- /dev/null +++ b/zig/src/families/deepseek_v41/longpf_test.zig @@ -0,0 +1,212 @@ +//! Host tests of long prompts on the prefill emitter (`zig build test-dsv41-longpf`): stream top-k and split blocks. + +const std = @import("std"); +const testing = std.testing; +const calls = @import("block_calls.zig"); +const block = @import("block.zig"); +const block_prefill = @import("block_prefill.zig"); +const buffers = @import("buffers.zig"); +const check = @import("calls_check.zig"); +const longpf = @import("longpf.zig"); +const Config = @import("config.zig").Config; + +const limit: i64 = 1 << 20; +const n: i64 = 2048; + +fn widths(a: std.mem.Allocator) !block.Widths { + var w: block.Widths = .{}; + try check.parseDense(a, &w, @embedFile("fixtures/dense-k2.txt")); + try check.parseExperts(a, &w, @embedFile("fixtures/expert-k2.txt")); + // x3gm's K2s present (the forward gate's widths read them from the pack): two widths a layer + for (0..40) |L| try w.gm.put(a, @intCast(L), .{ (1 << 3) | (1 << 4), (1 << 3) | (1 << 4) }); + return w; +} + +fn poolOf(split: bool) block.Pool { + const pages = @divExact(limit, block.Pool.page); + return .{ .comp_pages = if (split) @divExact(pages, 2) + 2 else pages + 1, .ik_pages = pages + 1, .pts = pages, .split = split }; +} + +fn named(c: calls.Call, name: []const u8) ?calls.Arg { + for (c.args) |x| if (std.mem.eql(u8, x.name, name)) return x.arg; + return null; +} + +fn roleOf(x: calls.Arg) []const u8 { + return switch (x) { + .t => |t| switch (t.role) { + .buf => |b| b, + else => "", + }, + else => "", + }; +} + +fn emit(a: std.mem.Allocator, cfg: *const Config, w: *const block.Widths, pool: ?block.Pool, start: i64) ![]calls.Call { + var backbone: [40]u32 = undefined; + for (&backbone, 0..) |*l, i| l.* = @intCast(i); + var o: block.Options = .{ .limit = limit, .rope_rows = limit + 64 }; + o.pool = pool; + return block_prefill.emitPrefill(a, cfg, w, o, &backbone, n, start, true); +} + +test "stream_topk's splits and plan (its docstring's 300K example)" { + try testing.expectEqual(@as(i64, 1), longpf.splits(1)); + try testing.expectEqual(@as(i64, 1), longpf.splits(16384)); + try testing.expectEqual(@as(i64, 2), longpf.splits(16385)); + try testing.expectEqual(@as(i64, 19), longpf.splits(300_000)); + // 19 splits x 8 KB a row under 0.25 GiB: >= 1,600 rows a launch + try testing.expectEqual(@as(i64, 1724), longpf.plan(4096, 300_000, 512)); + try testing.expectEqual(@as(i64, 2048), longpf.plan(2048, 8192, 512)); + try testing.expectEqual(@as(i64, 1024), longpf.plan(2048, 1 << 19, 512)); +} + +test "a 2,048-row segment at the 1M limit's end: the stream top-k on full layers, in stream_topk.plan's row blocks" { + var arena = std.heap.ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const a = arena.allocator(); + const w = try widths(a); + const cfg: Config = .{}; + const start = limit - n; + for ([_]?block.Pool{ null, poolOf(false), poolOf(true) }) |pool| { + const cs = try emit(a, &cfg, &w, pool, start); + var streams: usize = 0; + var merges: usize = 0; + var layer_streams: [40]usize = @splat(0); + var layer: usize = 0; + for (cs) |c| { + if (std.mem.eql(u8, c.name, "_stream")) { + const ratio = named(c, "RATIO").?.i; + const rows = c.grid[0]; + const nk = named(c, "NK").?.i; + try testing.expectEqual(longpf.splits(nk), c.grid[1]); + try testing.expectEqual(c.grid[1], named(c, "nsplit").?.i); + try testing.expectEqual(longpf.plan(n, @divFloor(start + n, ratio), 512), rows); + // the scratch under the budget, KEYS = BUF (positions mode), paged keys with the pool + try testing.expect(rows * c.grid[1] * 2 * 512 * 8 <= longpf.budget); + try testing.expectEqualStrings(roleOf(named(c, "BUF").?), roleOf(named(c, "KEYS").?)); + try testing.expectEqual(@as(i64, 0), named(c, "MODE").?.i); + try testing.expectEqual(pool != null, named(c, "PT").? != .none); + // two blocks: each its own position + try testing.expectEqualStrings(if (rows >= n) "w.pos" else "L.ix.pos", roleOf(named(c, "POS").?)[0..if (rows >= n) 5 else 8]); + streams += 1; + layer_streams[layer] += 1; + } + if (std.mem.eql(u8, c.name, "glue.stream_merge")) { + const buf = c.args[0].arg.t; + try testing.expectEqualSlices(i64, &.{ buf.shape[0], 512 }, c.args[1].arg.t.shape); + try testing.expectEqualSlices(i64, &.{ buf.shape[0], buf.shape[1] * 512 }, c.args[3].arg.t.shape); + merges += 1; + } + // the materialised selection never runs on a streamed layer (no `_keys` there) + if (std.mem.eql(u8, c.name, "_keys")) try testing.expectEqual(@as(usize, 0), layer_streams[layer]); + if (c.begin == .layer) layer += 1; + } + // every full-mode layer without the candidates' scores streams, in two row blocks at 512K visible keys + var want: usize = 0; + for (0..40) |L| { + const l: u32 = @intCast(L); + if (cfg.mode(l) == .full and !cfg.isCandidateSource(l)) want += 1; + } + try testing.expect(want > 0); + try testing.expectEqual(2 * want, streams); + try testing.expectEqual(streams, merges); + // the plan sizes the stream scratch within the budget + var p: buffers.Plan = .{ .a = a }; + try p.add(cs); + try testing.expect(p.sizes.get("L.ix.stream").? <= longpf.budget); + } +} + +test "below the stream threshold the segment is unchanged: no `_stream`" { + var arena = std.heap.ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const a = arena.allocator(); + const w = try widths(a); + const cfg: Config = .{}; + for ([_]i64{ 0, 2048, 4096 }) |start| { + const cs = try emit(a, &cfg, &w, null, start); + for (cs) |c| try testing.expect(!std.mem.eql(u8, c.name, "_stream") and !std.mem.eql(u8, c.name, "glue.stream_merge")); + } +} + +test "split KV: an exchanged attention in row blocks moves the row-indexed tensors only" { + var arena = std.heap.ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const a = arena.allocator(); + const w = try widths(a); + const cfg: Config = .{}; + const cs = try emit(a, &cfg, &w, poolOf(true), limit - n); + var exchanged: usize = 0; + var local: usize = 0; + for (cs) |*c| { + if (!std.mem.eql(u8, c.name, "_fused")) continue; + if (!longpf.readsExchange(c)) { + local += 1; + continue; + } + exchanged += 1; + const lo: i64 = 640; + const hi: i64 = 1500; + const b = try longpf.blockCall(a, c, lo, hi); + try testing.expectEqual(hi - lo, b.grid[0]); + try testing.expectEqual(c.grid[1], b.grid[1]); + for (c.args, b.args) |x, y| { + try testing.expectEqualStrings(x.name, y.name); + switch (x.arg) { + .t => |t| { + const u = y.arg.t; + const moved = u.offset - t.offset; + if (std.mem.eql(u8, x.name, "POS")) { + try testing.expectEqualStrings(longpf.pos_role, u.role.buf); + } else if (std.mem.eql(u8, x.name, "Q") or std.mem.eql(u8, x.name, "TOK") or std.mem.eql(u8, x.name, "CNT") or std.mem.eql(u8, x.name, "LO")) { + try testing.expectEqual(lo * t.stride[0] * @as(i64, @intCast(buffers.dtSize(t.dt))), moved); + try testing.expectEqual(hi - lo, u.shape[0]); + } else if (std.mem.eql(u8, x.name, "OUT")) { + // group-major rows of HG x 512 + try testing.expectEqual(lo * t.shape[2] * 2, moved); + } else try testing.expectEqual(@as(i64, 0), moved); + }, + // every scalar (OR, HG, the constexprs) as the whole launch's: the same AOT variant + .i => |v| try testing.expectEqual(v, y.arg.i), + .b => |v| try testing.expectEqual(v, y.arg.b), + .f => |v| try testing.expectEqual(v, y.arg.f), + else => {}, + } + } + try testing.expectEqual(n, named(b, "OR").?.i); + } + // the compressed layers attend the exchange, the SWA-only layers their ring + try testing.expect(exchanged > 0 and local > 0); +} + +test "TF_DSV41_STREAM_LAZY: no graphed program names the stream scratch; a long segment does" { + var arena = std.heap.ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const a = arena.allocator(); + const w = try widths(a); + const cfg: Config = .{}; + var layers: [64]u32 = undefined; + for (layers[0..cfg.layers], 0..) |*l, i| l.* = @intCast(i); + for ([_]bool{ false, true }) |split| { + const o: block.Options = .{ .limit = limit, .rope_rows = limit + 2048, .pool = poolOf(split) }; + // decode windows (one slot, every row bucket) and row windows at the slot's last position: graph-captured + var r: i64 = 1; + while (r <= 64) : (r += 1) { + // (a one-slot window the emitter refuses at this position never runs: block.zig's stream guard) + if (r <= 17) if (block.emit(a, &cfg, &w, o, layers[0..cfg.layers], r, limit - r, true)) |cs| + try testing.expect(!longpf.names(cs, longpf.lazy_role)) + else |e| + try testing.expectEqual(error.Unsupported, e); + var ro = o; + ro.rows = true; + try testing.expect(!longpf.names(try block.emit(a, &cfg, &w, ro, layers[0..cfg.layers], r, limit - r, true), longpf.lazy_role)); + } + // a prompt segment past the threshold streams + const seg = try block_prefill.emitPrefill(a, &cfg, &w, o, layers[0..cfg.layers], n, limit - n, true); + try testing.expect(longpf.names(seg, longpf.lazy_role)); + // and one below it does not (the scratch is never allocated for a short prompt) + const short = try block_prefill.emitPrefill(a, &cfg, &w, o, layers[0..cfg.layers], n, 0, true); + try testing.expect(!longpf.names(short, longpf.lazy_role)); + } +} diff --git a/zig/src/families/deepseek_v41/multi_test.zig b/zig/src/families/deepseek_v41/multi_test.zig new file mode 100644 index 0000000000..cb5ce9df35 --- /dev/null +++ b/zig/src/families/deepseek_v41/multi_test.zig @@ -0,0 +1,306 @@ +//! Host tests of the multi-segment prefill run (block_prefill.emitMulti), run by `zig build test-dsv41-knobs`. +const std = @import("std"); +const testing = std.testing; +const calls = @import("block_calls.zig"); +const block = @import("block.zig"); +const bp = @import("block_prefill.zig"); +const check = @import("calls_check.zig"); +const ced = @import("ced.zig"); +const buffers = @import("buffers.zig"); +const Config = @import("config.zig").Config; + +const limit: i64 = 8192; + +fn widths(a: std.mem.Allocator) !block.Widths { + var w: block.Widths = .{}; + try check.parseDense(a, &w, @embedFile("fixtures/dense-k2.txt")); + try check.parseExperts(a, &w, @embedFile("fixtures/expert-k2.txt")); + for (0..40) |L| try w.gm.put(a, @intCast(L), .{ (1 << 3) | (1 << 4), (1 << 3) | (1 << 4) }); + return w; +} + +fn options() block.Options { + const pages = @divExact(limit, block.Pool.page); + var o: block.Options = .{ .limit = limit, .rope_rows = limit + 2048 }; + o.pool = .{ .comp_pages = pages + 1, .ik_pages = pages + 1, .pts = pages, .split = false }; + return o; +} + +fn isGlue(c: calls.Call, name: []const u8) bool { + return c.glue and std.mem.eql(u8, c.name["glue.".len..], name); +} + +/// The run's positions (rope's "w.mpos64") are a one-segment program's "w.pos64". +fn roleName(t: calls.Tensor) []const u8 { + const r = switch (t.role) { + .buf => |b| b, + .weight => |w| w, + .empty => "", + }; + return if (std.mem.eql(u8, r, "w.mpos64")) "w.pos64" else r; +} + +fn sameArg(x: calls.Arg, y: calls.Arg, offsets: bool) bool { + if (std.meta.activeTag(x) != std.meta.activeTag(y)) return false; + return switch (x) { + .t, .opaque_table => |t| blk: { + const u = if (y == .t) y.t else y.opaque_table; + if (!std.mem.eql(u8, roleName(t), roleName(u)) or t.dt != u.dt) break :blk false; + if (!offsets) break :blk true; + break :blk t.offset == u.offset and std.mem.eql(i64, t.shape, u.shape) and std.mem.eql(i64, t.stride, u.stride); + }, + .i => |v| v == y.i, + .f => |v| v == y.f, + .b => |v| v == y.b, + .none => true, + .list => |l| l.len == y.list.len, + }; +} + +/// The same launch: its name, grid and every argument (`offsets`: tensors' shapes, strides and offsets too). +fn same(x: calls.Call, y: calls.Call, offsets: bool) bool { + if (!std.mem.eql(u8, x.name, y.name) or x.args.len != y.args.len) return false; + if (offsets and !std.mem.eql(i64, &x.grid, &y.grid)) return false; + for (x.args, y.args) |p, q| { + // a run's fused core writes its rows of each group's plane: the plane's rows are the run's (checked apart) + if (!offsets and std.mem.eql(u8, p.name, "OR")) continue; + if (!sameArg(p.arg, q.arg, offsets)) return false; + } + return true; +} + +/// The calls without the positions' glue (a run writes its own: mpositions, pf_seg). +fn body(a: std.mem.Allocator, cs: []const calls.Call) ![]const calls.Call { + var out: std.ArrayList(calls.Call) = .empty; + for (cs) |c| { + if (isGlue(c, "positions") or isGlue(c, "mpositions") or isGlue(c, "pf_seg")) continue; + try out.append(a, c); + } + return out.items; +} + +fn named(c: calls.Call, name: []const u8) ?calls.Arg { + for (c.args) |x| if (std.mem.eql(u8, x.name, name)) return x.arg; + return null; +} + +const Layers = struct { all: [25]u32, enc: []const u32, dec: []const u32 }; + +fn layersOf(cfg: *const Config, l: *Layers) !void { + for (&l.all, 0..) |*x, i| x.* = @intCast(i); + const d = try ced.decoderStart(cfg); + l.enc = l.all[0 .. d + 1]; + l.dec = l.all[d..]; +} + +test "a one-segment run is the one-segment program (full mode without the head, and CED's encoder pass)" { + var arena = std.heap.ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const a = arena.allocator(); + const cfg: Config = .{}; + var w = try widths(a); + var l: Layers = undefined; + try layersOf(&cfg, &l); + const o = options(); + for ([_]i64{ 33, 100, 127, 300, 2048 }) |n| for ([_]i64{ 0, 1000 }) |start| { + const sp = [_]bp.Span{.{ .slot = 2, .start = start, .n = n }}; + const one = try body(a, try bp.emitPrefill(a, &cfg, &w, o, &l.all, n, start, false)); + const run = try body(a, try bp.emitMulti(a, &cfg, &w, o, &l.all, &sp, .whole)); + try testing.expectEqual(one.len, run.len); + for (one, run) |x, y| try testing.expect(same(x, y, true)); + if (n <= 127) { + // the decoder replay of a tail of n rows from R0 = start + const dec1 = try body(a, try bp.emitReplay(a, &cfg, &w, o, l.dec, n, start, .decoder)); + const decr = try body(a, try bp.emitMulti(a, &cfg, &w, o, l.dec, &sp, .decoder)); + try testing.expectEqual(dec1.len, decr.len); + for (dec1, decr) |x, y| try testing.expect(same(x, y, true)); + } + const enc1 = try body(a, try bp.emitReplay(a, &cfg, &w, o, l.enc, n, start, .encoder)); + const encr = try body(a, try bp.emitMulti(a, &cfg, &w, o, l.enc, &sp, .encoder)); + try testing.expectEqual(enc1.len, encr.len); + for (enc1, encr) |x, y| try testing.expect(same(x, y, true)); + }; +} + +test "a run of several segments: the rows' launches over all of them, each segment's CSA2 steps as its own program's, at its rows and on its slot" { + var arena = std.heap.ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const a = arena.allocator(); + const cfg: Config = .{}; + var w = try widths(a); + var l: Layers = undefined; + try layersOf(&cfg, &l); + const o = options(); + const H: i64 = cfg.heads / o.world; + const hd: i64 = cfg.head_dim; + // slots at different positions and lengths: a staged segment past the ring, a ring-held one, one from 0 + const spans = [_]bp.Span{ .{ .slot = 3, .start = 4000, .n = 700 }, .{ .slot = 0, .start = 0, .n = 100 }, .{ .slot = 1, .start = 129, .n = 33 } }; + var total: i64 = 0; + for (spans) |sp| total += sp.n; + // the decoder replays: slots' tails from their R0 (a full 127-row tail, shorter prompts' tails) + const tails = [_]bp.Span{ .{ .slot = 2, .start = 3873, .n = 127 }, .{ .slot = 0, .start = 0, .n = 64 }, .{ .slot = 3, .start = 0, .n = 33 } }; + for ([_]bp.Part{ .whole, .encoder, .decoder }) |part| { + const layers = switch (part) { + .encoder => l.enc, + .decoder => l.dec, + else => &l.all, + }; + const segs: []const bp.Span = if (part == .decoder) &tails else &spans; + total = 0; + for (segs) |sp| total += sp.n; + const run = try bp.emitMulti(a, &cfg, &w, o, layers, segs, part); + // the run's per-row launches (outside its segments' CSA2 steps): an all-rows one-segment program's, in order + const whole = try body(a, if (part != .whole) try bp.emitReplay(a, &cfg, &w, o, layers, total, segs[0].start, part) else try bp.emitPrefill(a, &cfg, &w, o, layers, total, segs[0].start, false)); + var at: usize = 0; + var seg: ?usize = null; + var cores: [3]std.ArrayList(calls.Call) = @splat(.empty); + var entered: [3]usize = @splat(0); + for (run) |c| { + if (isGlue(c, "pf_seg")) { + const j = c.args[0].arg.i; + seg = if (j < 0) null else @intCast(j); + if (seg) |s| entered[s] += 1; + continue; + } + if (isGlue(c, "mpositions")) continue; + if (seg) |s| { + try cores[s].append(a, c); + continue; + } + while (at < whole.len and !same(whole[at], c, false)) at += 1; + if (at == whole.len) { + std.debug.print("per-row launch {s} not in the all-rows program's order\n", .{c.name}); + return error.TestUnexpected; + } + at += 1; + } + // each segment's CSA2 steps: its own one-segment program's launches of those kinds, in order, at its rows + var r0: i64 = 0; + for (segs, 0..) |sp, j| { + defer r0 += sp.n; + try testing.expect(entered[j] > 0); + const own = try body(a, if (part != .whole) try bp.emitReplay(a, &cfg, &w, o, layers, sp.n, sp.start, part) else try bp.emitPrefill(a, &cfg, &w, o, layers, sp.n, sp.start, false)); + var k: usize = 0; + for (cores[j].items) |c| { + while (k < own.len and !same(own[k], c, false)) k += 1; + if (k == own.len) { + std.debug.print("segment {d}: {s} not in its own program's order\n", .{ j, c.name }); + return error.TestUnexpected; + } + k += 1; + // the fused core reads its rows of the run's q and writes its rows of each group's plane + if (std.mem.eql(u8, c.name, "_fused")) { + try testing.expectEqual(r0 * H * hd * 2, named(c, "Q").?.t.offset); + try testing.expectEqual(total, named(c, "OR").?.i); + try testing.expectEqual(sp.n, c.grid[0]); + } + if (std.mem.eql(u8, c.name, "_kv_store") and std.mem.eql(u8, roleName(named(c, "LAT").?.t), "L.kv")) + try testing.expectEqual(r0 * hd * 2, named(c, "LAT").?.t.offset); + } + } + } +} + +test "a run refuses what one segment's program must run alone" { + var arena = std.heap.ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const a = arena.allocator(); + const cfg: Config = .{}; + var w = try widths(a); + var l: Layers = undefined; + try layersOf(&cfg, &l); + const o = options(); + // a short segment (the decode kernels' mix), more rows than a prefill segment, the decoder pass + try testing.expectError(error.Unsupported, bp.emitMulti(a, &cfg, &w, o, &l.all, &.{ .{ .slot = 0, .start = 0, .n = 100 }, .{ .slot = 1, .start = 0, .n = 16 } }, .whole)); + try testing.expectError(error.Unsupported, bp.emitMulti(a, &cfg, &w, o, &l.all, &.{ .{ .slot = 0, .start = 0, .n = 2000 }, .{ .slot = 1, .start = 0, .n = 100 } }, .whole)); + // a decoder tail of 32 rows or fewer (the split mHC programs: its replay alone is another kernel path) + try testing.expectError(error.Unsupported, bp.emitMulti(a, &cfg, &w, o, l.dec, &.{ .{ .slot = 0, .start = 0, .n = 100 }, .{ .slot = 1, .start = 0, .n = 32 } }, .decoder)); +} + +test "every role a run reads is in the buffer plan, at its run's rows (forward_prefill.plan: the one-segment programs + planRuns)" { + // regression: the runs' positions "w.mpos64" were in no planned program, so every run failed with Unbound + var arena = std.heap.ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const a = arena.allocator(); + const cfg: Config = .{}; + var w = try widths(a); + var l: Layers = undefined; + try layersOf(&cfg, &l); + const o = options(); + const rows: i64 = 2048; + var plan: buffers.Plan = .{ .a = a }; + try plan.add(try bp.emitPrefill(a, &cfg, &w, o, &l.all, rows, 0, true)); + try plan.add(try bp.emitReplay(a, &cfg, &w, o, l.enc, rows, 0, .encoder)); + try plan.add(try bp.emitReplay(a, &cfg, &w, o, l.dec, ced.keep, 0, .decoder)); + for ((try bp.planRuns(a, &cfg, &w, o, l.enc, l.dec, rows, .encoder)).items) |cs| try plan.add(cs); + const runs = [_]struct { part: bp.Part, spans: []const bp.Span }{ + .{ .part = .encoder, .spans = &.{ .{ .slot = 0, .start = 0, .n = 1000 }, .{ .slot = 1, .start = 0, .n = 1048 } } }, + .{ .part = .encoder, .spans = &.{ .{ .slot = 0, .start = 0, .n = 17 }, .{ .slot = 1, .start = 0, .n = 300 }, .{ .slot = 2, .start = 0, .n = 33 } } }, + .{ .part = .decoder, .spans = &.{ .{ .slot = 0, .start = 0, .n = 127 }, .{ .slot = 1, .start = 0, .n = 127 }, .{ .slot = 2, .start = 0, .n = 127 }, .{ .slot = 3, .start = 0, .n = 64 } } }, + // the counts' widest layout (each 17-row segment's counts padded to the next multiple of 4) + .{ .part = .encoder, .spans = &.{ .{ .slot = 0, .start = 0, .n = 17 }, .{ .slot = 1, .start = 0, .n = 17 }, .{ .slot = 2, .start = 0, .n = 17 }, .{ .slot = 3, .start = 0, .n = 17 }, .{ .slot = 4, .start = 0, .n = 17 }, .{ .slot = 5, .start = 0, .n = 17 }, .{ .slot = 6, .start = 0, .n = 17 }, .{ .slot = 7, .start = 0, .n = 17 }, .{ .slot = 8, .start = 0, .n = 17 }, .{ .slot = 9, .start = 0, .n = 17 }, .{ .slot = 10, .start = 0, .n = 17 }, .{ .slot = 11, .start = 0, .n = 17 }, .{ .slot = 12, .start = 0, .n = 17 }, .{ .slot = 13, .start = 0, .n = 17 }, .{ .slot = 14, .start = 0, .n = 17 }, .{ .slot = 15, .start = 0, .n = 1793 } } }, + }; + for (runs) |r| { + const cs = try bp.emitMulti(a, &cfg, &w, o, if (r.part == .decoder) l.dec else l.enc, r.spans, r.part); + var need: buffers.Plan = .{ .a = a }; + try need.add(cs); + for (need.sizes.keys(), need.sizes.values()) |role, bytes| { + const have = plan.sizes.get(role) orelse { + std.debug.print("role {s} of a run is in no planned program\n", .{role}); + return error.TestUnplanned; + }; + if (have < bytes) { + std.debug.print("role {s}: a run needs {d} bytes, the plan holds {d}\n", .{ role, bytes, have }); + return error.TestUnplanned; + } + } + } +} + +/// The first Triton tensor argument in `cs` not 16-byte aligned from its role's base, or null (Triton specializes). +pub fn misaligned(cs: []const calls.Call) ?struct { call: []const u8, arg: []const u8, offset: i64 } { + for (cs) |c| { + if (!c.triton) continue; + for (c.args) |x| switch (x.arg) { + .t, .opaque_table => |t| if (@mod(t.offset, 16) != 0) return .{ .call = c.name, .arg = x.name, .offset = t.offset }, + else => {}, + }; + } + return null; +} + +test "every tensor a piece run or replay run hands Triton is 16-byte aligned (Triton's specialization, aot-needs' key)" { + // regression: a run's second segment read selection counts at row0 * 4, leaving _fused's CNT not 16-aligned + var arena = std.heap.ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const a = arena.allocator(); + const cfg: Config = .{}; + var w = try widths(a); + var l: Layers = undefined; + try layersOf(&cfg, &l); + const o = options(); + // first rows at every residue mod 4, staged segments (past 129 rows), the counts' widest layout + var widest: [16]bp.Span = undefined; + for (widest[0..15], 0..) |*x, i| x.* = .{ .slot = @intCast(i), .start = 0, .n = 17 }; + widest[15] = .{ .slot = 15, .start = 0, .n = 2048 - 15 * 17 }; + const runs = [_]struct { part: bp.Part, spans: []const bp.Span }{ + .{ .part = .encoder, .spans = &.{ .{ .slot = 0, .start = 0, .n = 17 }, .{ .slot = 1, .start = 0, .n = 18 }, .{ .slot = 2, .start = 700, .n = 19 }, .{ .slot = 3, .start = 0, .n = 20 } } }, + .{ .part = .encoder, .spans = &.{ .{ .slot = 0, .start = 0, .n = 159 }, .{ .slot = 1, .start = 4000, .n = 1497 } } }, + .{ .part = .encoder, .spans = &.{ .{ .slot = 0, .start = 2048, .n = 1501 }, .{ .slot = 1, .start = 0, .n = 131 }, .{ .slot = 2, .start = 0, .n = 33 } } }, + .{ .part = .whole, .spans = &.{ .{ .slot = 0, .start = 0, .n = 299 }, .{ .slot = 1, .start = 0, .n = 499 }, .{ .slot = 2, .start = 0, .n = 699 } } }, + .{ .part = .encoder, .spans = &widest }, + .{ .part = .decoder, .spans = &.{ .{ .slot = 0, .start = 33, .n = 127 }, .{ .slot = 1, .start = 0, .n = 33 }, .{ .slot = 2, .start = 0, .n = 66 }, .{ .slot = 3, .start = 900, .n = 127 } } }, + }; + for (runs) |r| { + const layers = switch (r.part) { + .encoder => l.enc, + .decoder => l.dec, + else => &l.all, + }; + const cs = try bp.emitMulti(a, &cfg, &w, o, layers, r.spans, r.part); + if (misaligned(cs)) |m| { + std.debug.print("{s} {s}: offset {d} is not 16-byte aligned\n", .{ m.call, m.arg, m.offset }); + return error.TestMisaligned; + } + } +} diff --git a/zig/src/families/deepseek_v41/named.zig b/zig/src/families/deepseek_v41/named.zig new file mode 100644 index 0000000000..d439d03508 --- /dev/null +++ b/zig/src/families/deepseek_v41/named.zig @@ -0,0 +1,261 @@ +//! A rank's weights as named host tensors in the reference loader's forms, named as the reference capture names them. + +const std = @import("std"); +const plan = @import("plan.zig"); +const names = @import("names.zig"); +const stage = @import("stage.zig"); +const Pack = @import("pack.zig").Pack; +const DType = @import("pack.zig").DType; +const Io = std.Io; + +pub const Kind = enum { f32, bf16, f16, i16 }; + +/// One tensor the forward holds: its name (`L2.attn.wq_a.trellis`, `embed`), element kind, shape and bytes. +pub const Named = struct { + name: []const u8, + kind: Kind, + shape: [4]usize, + rank: u8, + bytes: []align(16) u8, +}; + +/// What a native tensor becomes (Loader.native and its callers). +const Out = enum { + /// fp32 values (`native(name)`: bf16 or fp32 storage widened) + f32, + /// bf16 values (`native(name, bf16, bf16_param=True)`: the router gate) + bf16, + /// fp32 holding bf16-rounded values (`native(name, bf16_param=True)`: the indexer's weights_proj) + f32_via_bf16, + /// Engram's q / k: combined into `engram.qk` = bf16(q) * bf16(k) in fp32 + qk, +}; + +const Rule = struct { name: []const u8, out: Out }; + +/// Native suffixes (names.zig blockNatives) -> the capture's field names and forms. +const rules = std.StaticStringMap(Rule).initComptime(.{ + .{ "attn_norm.weight", Rule{ .name = "attn_norm", .out = .f32 } }, + .{ "ffn_norm.weight", Rule{ .name = "ffn_norm", .out = .f32 } }, + .{ "hc_attn_fn", Rule{ .name = "hc_attn.fn", .out = .f32 } }, + .{ "hc_attn_base", Rule{ .name = "hc_attn.base", .out = .f32 } }, + .{ "hc_attn_scale", Rule{ .name = "hc_attn.scale", .out = .f32 } }, + .{ "hc_ffn_fn", Rule{ .name = "hc_ffn.fn", .out = .f32 } }, + .{ "hc_ffn_base", Rule{ .name = "hc_ffn.base", .out = .f32 } }, + .{ "hc_ffn_scale", Rule{ .name = "hc_ffn.scale", .out = .f32 } }, + .{ "attn.q_norm.weight", Rule{ .name = "attn.q_norm", .out = .f32 } }, + .{ "attn.kv_norm.weight", Rule{ .name = "attn.kv_norm", .out = .f32 } }, + .{ "attn.attn_sink", Rule{ .name = "attn.sink", .out = .f32 } }, + .{ "attn.compressor.norm.weight", Rule{ .name = "attn.comp_norm", .out = .f32 } }, + .{ "attn.indexer.k_norm.weight", Rule{ .name = "attn.ix_knorm", .out = .f32 } }, + .{ "attn.indexer.weights_proj.weight", Rule{ .name = "attn.ix_wp", .out = .f32_via_bf16 } }, + .{ "ffn.gate.weight", Rule{ .name = "moe.gate", .out = .bf16 } }, + .{ "ffn.gate.bias", Rule{ .name = "moe.bias", .out = .f32 } }, + .{ "engram.q_weight", Rule{ .name = "engram.qk", .out = .qk } }, + .{ "engram.k_weight", Rule{ .name = "engram.qk", .out = .qk } }, +}); + +/// The capture's name of a block group (AttnW / MoEW / EngramW fields). +fn groupField(g: plan.GroupPlan, gl: u32, buf: []u8) ![]const u8 { + return switch (g.proj) { + .wq_a => "attn.wq_a", + .wkv => "attn.wkv", + .wq_b => "attn.wq_b", + .wo_a => std.fmt.bufPrint(buf, "attn.wo_a.{d}", .{g.slice % gl}), + .wo_b => "attn.wo_b", + .comp_wkv => "attn.comp_wkv", + .comp_wgate => "attn.comp_wgate", + .ix_wq_b => "attn.ix_wq_b", + .ix_wk => "attn.ix_wk", + .shared_w1 => "moe.shared.0.w1", + .shared_w2 => "moe.shared.0.w2", + .shared_w3 => "moe.shared.0.w3", + .engram_wkv => "engram.wkv", + }; +} + +/// fp32 -> bf16, round to nearest even (torch's .to(bfloat16)); NaN stays a quiet NaN. +pub fn bf16Round(x: f32) u16 { + const b: u32 = @bitCast(x); + if (std.math.isNan(x)) return @intCast((b >> 16) | 0x40); + return @intCast((b + 0x7fff + ((b >> 16) & 1)) >> 16); +} + +pub fn bf16Widen(h: u16) f32 { + return @bitCast(@as(u32, h) << 16); +} + +/// One stored element as fp32 (bf16, fp16 and fp32 storage). +fn load(dtype: DType, src: []const u8, i: usize) !f32 { + return switch (dtype) { + .f32 => @bitCast(std.mem.readInt(u32, src[4 * i ..][0..4], .little)), + .bf16 => bf16Widen(std.mem.readInt(u16, src[2 * i ..][0..2], .little)), + .f16 => @floatCast(@as(f16, @bitCast(std.mem.readInt(u16, src[2 * i ..][0..2], .little)))), + else => error.UnexpectedTensor, + }; +} + +pub const Builder = struct { + gpa: std.mem.Allocator, + io: Io, + pack: *const Pack, + out: std.ArrayList(Named) = .empty, + + pub fn deinit(b: *Builder) void { + for (b.out.items) |n| { + b.gpa.free(n.name); + b.gpa.free(n.bytes); + } + b.out.deinit(b.gpa); + } + + fn put(b: *Builder, name: []const u8, kind: Kind, shape: []const usize, bytes: []align(16) u8) !void { + var n: Named = .{ .name = try b.gpa.dupe(u8, name), .kind = kind, .shape = @splat(1), .rank = @intCast(shape.len), .bytes = bytes }; + @memcpy(n.shape[0..shape.len], shape); + try b.out.append(b.gpa, n); + } + + fn copy(b: *Builder, src: []const u8) ![]align(16) u8 { + const d = try b.gpa.alignedAlloc(u8, .@"16", src.len); + @memcpy(d, src); + return d; + } + + /// A group's part as `.trellis`, `.suh`, `.svh`. + fn group(b: *Builder, base: []const u8, g: plan.GroupPlan) !void { + var img = try stage.group(b.gpa, b.io, b.pack, g); + defer img.deinit(b.gpa); + const p = g.part.group; + var nb: [160]u8 = undefined; + try b.put(try std.fmt.bufPrint(&nb, "{s}.trellis", .{base}), .i16, &.{ p.k_tiles, p.n_tiles, p.words }, try b.copy(img.trellis)); + try b.put(try std.fmt.bufPrint(&nb, "{s}.suh", .{base}), .f16, &.{p.k()}, try b.copy(img.suh)); + try b.put(try std.fmt.bufPrint(&nb, "{s}.svh", .{base}), .f16, &.{p.n()}, try b.copy(img.svh)); + } + + /// A routed projection: `.trellis` [E, kt, nt, words] (stack) or `.data` (ragged), `.suh` [E, K], `.svh` [E, N]. + fn experts(b: *Builder, base: []const u8, x: *const plan.ExpertsPlan) !void { + var img = try stage.experts(b.gpa, b.io, b.pack, x); + defer img.deinit(b.gpa); + const l = &x.layout; + const e = l.count(); + var nb: [160]u8 = undefined; + if (l.kind == .stack) { + try b.put(try std.fmt.bufPrint(&nb, "{s}.trellis", .{base}), .i16, &.{ e, l.k_tiles, l.n_tiles, l.words[0] }, try b.copy(img.trellis)); + } else try b.put(try std.fmt.bufPrint(&nb, "{s}.data", .{base}), .i16, &.{@intCast(l.elements)}, try b.copy(img.trellis)); + try b.put(try std.fmt.bufPrint(&nb, "{s}.suh", .{base}), .f16, &.{ e, 16 * @as(usize, l.k_tiles) }, try b.copy(img.suh)); + try b.put(try std.fmt.bufPrint(&nb, "{s}.svh", .{base}), .f16, &.{ e, 16 * @as(usize, l.n_tiles) }, try b.copy(img.svh)); + } + + /// The rank's slice of a native tensor, as stored. + fn raw(b: *Builder, v: plan.NativePlan, rank: u32, world: u32) ![]u8 { + const whole = v.slice == .whole; + const len = if (whole) v.info.nbytes else v.info.nbytes / world; + const bytes = try b.gpa.alloc(u8, @intCast(len)); + errdefer b.gpa.free(bytes); + try b.pack.read(b.io, v.info, .{ .offset = if (whole) 0 else rank * len, .len = len }, bytes); + return bytes; + } + + /// A native tensor in its forward form; Engram's q waits for its k (`pending`). + fn native(b: *Builder, name: []const u8, rule: Rule, v: plan.NativePlan, rank: u32, world: u32, pending: *?[]f32) !void { + const bytes = try b.raw(v, rank, world); + defer b.gpa.free(bytes); + const count = bytes.len / v.info.dtype.size(); + var shape: [4]usize = @splat(1); + const dims = v.info.dims(); + @memcpy(shape[0..dims.len], dims); + if (v.slice != .whole) shape[0] /= world; + switch (rule.out) { + .bf16 => { + const out = try b.gpa.alignedAlloc(u8, .@"16", 2 * count); + for (0..count) |i| std.mem.writeInt(u16, out[2 * i ..][0..2], bf16Round(try load(v.info.dtype, bytes, i)), .little); + try b.put(name, .bf16, shape[0..dims.len], out); + }, + .f32, .f32_via_bf16 => { + const out = try b.gpa.alignedAlloc(u8, .@"16", 4 * count); + for (0..count) |i| { + const x = try load(v.info.dtype, bytes, i); + const y = if (rule.out == .f32) x else bf16Widen(bf16Round(x)); + std.mem.writeInt(u32, out[4 * i ..][0..4], @bitCast(y), .little); + } + try b.put(name, .f32, shape[0..dims.len], out); + }, + .qk => { + const vals = try b.gpa.alloc(f32, count); + for (vals, 0..) |*x, i| x.* = bf16Widen(bf16Round(try load(v.info.dtype, bytes, i))); + if (pending.*) |q| { + defer b.gpa.free(q); + defer b.gpa.free(vals); + if (q.len != vals.len) return error.UnexpectedTensor; + const out = try b.gpa.alignedAlloc(u8, .@"16", 4 * count); + for (q, vals, 0..) |x, y, i| std.mem.writeInt(u32, out[4 * i ..][0..4], @bitCast(x * y), .little); + pending.* = null; + try b.put(name, .f32, shape[0..dims.len], out); + } else pending.* = vals; + }, + } + } + + pub fn layer(b: *Builder, l: *const plan.LayerPlan, o_groups: u32, rank: u32, world: u32) !void { + var nb: [160]u8 = undefined; + var fb: [32]u8 = undefined; + const gl = o_groups / world; + for (l.groups) |g| try b.group(try std.fmt.bufPrint(&nb, "L{d}.{s}", .{ l.index, try groupField(g, gl, &fb) }), g); + var pending: ?[]f32 = null; + defer if (pending) |q| b.gpa.free(q); + for (l.natives) |v| { + const suffix = v.name[l.prefix.len + 1 ..]; + const rule = rules.get(suffix) orelse return error.UnnamedNative; + try b.native(try std.fmt.bufPrint(&nb, "L{d}.{s}", .{ l.index, rule.name }), rule, v, rank, world, &pending); + } + if (pending != null) return error.EngramHalf; + for (&l.experts, [_][]const u8{ "w1", "w2", "w3" }) |*x, w| try b.experts(try std.fmt.bufPrint(&nb, "L{d}.moe.{s}", .{ l.index, w }), x); + } + + /// The vocabulary rows (bf16), the final norm (fp32) and the head's part. + pub fn top(b: *Builder, p: *const plan.Plan) !void { + var none: ?[]f32 = null; + for (p.top) |v| { + const is_embed = std.mem.eql(u8, v.name, "embed.weight"); + const rule: Rule = if (is_embed) .{ .name = "embed", .out = .bf16 } else .{ .name = "norm", .out = .f32 }; + try b.native(rule.name, rule, v, p.rank, p.world, &none); + } + if (p.head) |h| try b.group("head", h); + } + + /// DSpark's heads: main_proj (whole), main_norm and norm fp32, Markov embed / head bf16, confidence head fp32. + pub fn dspark(b: *Builder, p: *const plan.Plan) !void { + const d = p.dspark orelse return; + var none: ?[]f32 = null; + try b.group("dspark.main_proj", d.main_proj); + const out_names = [_][]const u8{ "dspark.main_norm", "dspark.norm", "dspark.markov.w1", "dspark.markov.w2" }; + const outs = [_]Out{ .f32, .f32, .bf16, .bf16 }; + for (d.natives, out_names, outs) |v, nm, o| try b.native(nm, .{ .name = nm, .out = o }, v, p.rank, p.world, &none); + if (d.confidence) |c| try b.native("dspark.conf", .{ .name = "dspark.conf", .out = .f32 }, c, p.rank, p.world, &none); + } + + /// Every planned block, then the model-level tensors (a whole-model plan only). + pub fn all(b: *Builder, p: *const plan.Plan, o_groups: u32) !void { + for (p.layers) |*l| try b.layer(l, o_groups, p.rank, p.world); + try b.top(p); + try b.dspark(p); + } + + pub fn find(b: *const Builder, name: []const u8) ?*const Named { + for (b.out.items) |*n| if (std.mem.eql(u8, n.name, name)) return n; + return null; + } +}; + +const testing = std.testing; + +test "the loader's conversions: bf16 round to nearest even, widening, fp16 storage" { + try testing.expectEqual(@as(u16, 0x3f80), bf16Round(1.0)); + // 1 + 2^-8 is a tie between 1.0 and 1 + 2^-7: even keeps 1.0; 1 + 3 * 2^-8 rounds up + try testing.expectEqual(@as(u16, 0x3f80), bf16Round(1.00390625)); + try testing.expectEqual(@as(u16, 0x3f82), bf16Round(1.01171875)); + try testing.expectEqual(@as(f32, -2.5), bf16Widen(bf16Round(-2.5))); + var h: [2]u8 = undefined; + std.mem.writeInt(u16, &h, @bitCast(@as(f16, 0.333251953125)), .little); + try testing.expectEqual(@as(f32, 0.333251953125), try load(.f16, &h, 0)); +} diff --git a/zig/src/families/deepseek_v41/names.zig b/zig/src/families/deepseek_v41/names.zig new file mode 100644 index 0000000000..16da179483 --- /dev/null +++ b/zig/src/families/deepseek_v41/names.zig @@ -0,0 +1,236 @@ +//! The pack's tensor names, each one's TP split and expected shape, plus the names the text engine leaves unread. + +const std = @import("std"); +const Config = @import("config.zig").Config; +const List = @import("config.zig").List; +const Split = @import("exl3.zig").Split; +const DType = @import("core").safetensors.DType; + +/// "layers.L" for the backbone, "mtp.i" for DSpark block i (layer = layers + i). +pub fn blockPrefix(c: *const Config, layer: u32, buf: []u8) ![]const u8 { + return if (c.isBackbone(layer)) std.fmt.bufPrint(buf, "layers.{d}", .{layer}) else std.fmt.bufPrint(buf, "mtp.{d}", .{layer - c.layers}); +} + +/// An EXL3 projection of a block, by the forward's name for it. +pub const Proj = enum { wq_a, wkv, wq_b, wo_a, wo_b, comp_wkv, comp_wgate, ix_wq_b, ix_wk, shared_w1, shared_w2, shared_w3, engram_wkv }; + +/// A block's EXL3 group: its name under the block prefix, how ranks split it, and its K x N (0: not checked). +pub const GroupSpec = struct { + proj: Proj, + suffix: []const u8, + /// wo_a: the global group index this slice is + slice: u32 = 0, + split: Split, + k: u32 = 0, + n: u32 = 0, +}; + +pub const Groups = List(GroupSpec, 32); + +/// The block's EXL3 groups this rank loads, in the loader's order (wo_a: only this rank's groups). +pub fn blockGroups(c: *const Config, layer: u32, rank: u32, world: u32, has_shared: bool) !Groups { + var g: Groups = .{}; + const per_group_k = c.heads / c.o_groups * c.head_dim; + try g.push(.{ .proj = .wq_a, .suffix = "attn.wq_a", .split = .whole, .k = c.hidden, .n = c.q_lora }); + try g.push(.{ .proj = .wkv, .suffix = "attn.wkv", .split = .whole, .k = c.hidden, .n = c.head_dim }); + try g.push(.{ .proj = .wq_b, .suffix = "attn.wq_b", .split = .col, .k = c.q_lora, .n = c.heads * c.head_dim }); + const gl = c.o_groups / world; + var s = rank * gl; + while (s < (rank + 1) * gl) : (s += 1) try g.push(.{ .proj = .wo_a, .suffix = "attn.wo_a.slice", .slice = s, .split = .whole, .k = per_group_k, .n = c.o_lora }); + try g.push(.{ .proj = .wo_b, .suffix = "attn.wo_b", .split = .row, .k = c.o_groups * c.o_lora, .n = c.hidden }); + if (c.isBackbone(layer) and c.isKvSource(layer)) { + try g.push(.{ .proj = .comp_wkv, .suffix = "attn.compressor.wkv", .split = .whole, .k = c.hidden, .n = c.head_dim }); + if (c.compressRatio(layer) > 1) try g.push(.{ .proj = .comp_wgate, .suffix = "attn.compressor.wgate", .split = .whole, .k = c.hidden, .n = c.head_dim }); + try g.push(.{ .proj = .ix_wk, .suffix = "attn.indexer.wk", .split = .whole, .k = c.head_dim, .n = c.index_dim }); + } + if (c.isBackbone(layer) and c.isIndexSource(layer)) + try g.push(.{ .proj = .ix_wq_b, .suffix = "attn.indexer.wq_b", .split = .whole, .k = c.q_lora, .n = c.index_heads * c.index_dim }); + if (has_shared) { + try g.push(.{ .proj = .shared_w1, .suffix = "ffn.shared_experts.w1", .split = .col, .k = c.hidden, .n = c.expert_width }); + try g.push(.{ .proj = .shared_w2, .suffix = "ffn.shared_experts.w2", .split = .row, .k = c.expert_width, .n = c.hidden }); + try g.push(.{ .proj = .shared_w3, .suffix = "ffn.shared_experts.w3", .split = .col, .k = c.hidden, .n = c.expert_width }); + } + if (c.isEngram(layer)) try g.push(.{ .proj = .engram_wkv, .suffix = "engram.wkv", .split = .whole, .k = c.hashCols() * c.engram_head_dim, .n = (c.engram_max_ngram + 1) * c.hidden }); + return g; +} + +/// The group's full name under `block`. +pub fn groupName(spec: GroupSpec, block: []const u8, buf: []u8) ![]const u8 { + return if (spec.proj == .wo_a) std.fmt.bufPrint(buf, "{s}.{s}.{d}", .{ block, spec.suffix, spec.slice }) else std.fmt.bufPrint(buf, "{s}.{s}", .{ block, spec.suffix }); +} + +/// Routed experts' projections: gate (w1) and up (w3) split by columns, down (w2) by rows. +pub const ExpertProj = enum { + w1, + w2, + w3, + + pub fn split(p: ExpertProj) Split { + return if (p == .w2) .row else .col; + } + + /// (K, N) of one expert's full matrix. + pub fn kn(p: ExpertProj, c: *const Config) [2]u32 { + return if (p == .w2) .{ c.expert_width, c.hidden } else .{ c.hidden, c.expert_width }; + } +}; + +pub fn expertName(block: []const u8, e: u32, p: ExpertProj, buf: []u8) ![]const u8 { + return std.fmt.bufPrint(buf, "{s}.ffn.experts.{d}.{t}", .{ block, e, p }); +} + +/// Which dtypes a native tensor may be stored in; the loader converts to what the forward holds. +pub const Store = enum { + /// norms: bf16 in the pack (fp32 in the forward), fp32 accepted + norm, + /// fp32 only (sinks, hc_*, router bias) + f32, + /// fp16 or bf16, rounded to bf16 once (router gate, indexer weights_proj, Markov heads) + half, + + pub fn accepts(s: Store, d: DType) bool { + return switch (s) { + .norm => d == .bf16 or d == .f32, + .f32 => d == .f32, + .half => d == .bf16 or d == .f16, + }; + } +}; + +/// How a rank holds a native tensor. +pub const Slice = enum { whole, heads, vocab_rows }; + +pub const NativeSpec = struct { + suffix: []const u8, + store: Store, + shape: [2]u32, + rank: u8, + slice: Slice = .whole, + + fn v(suffix: []const u8, store: Store, n: u32) NativeSpec { + return .{ .suffix = suffix, .store = store, .shape = .{ n, 0 }, .rank = 1 }; + } + + fn m(suffix: []const u8, store: Store, r: u32, cols: u32) NativeSpec { + return .{ .suffix = suffix, .store = store, .shape = .{ r, cols }, .rank = 2 }; + } + + pub fn dims(s: *const NativeSpec) []const u32 { + return s.shape[0..s.rank]; + } +}; + +pub const Natives = List(NativeSpec, 24); + +/// A block's native tensors: norms, mHC mixers, the sink, compressor / indexer pieces, the router, Engram's q.k. +pub fn blockNatives(c: *const Config, layer: u32) !Natives { + var n: Natives = .{}; + const hc = c.hc_mult; + const mix = (2 + hc) * hc; // pre (hc), post (hc) and the hc x hc residual mix + try n.push(.v("attn_norm.weight", .norm, c.hidden)); + try n.push(.v("ffn_norm.weight", .norm, c.hidden)); + inline for (.{ "attn", "ffn" }) |w| { + try n.push(.m("hc_" ++ w ++ "_fn", .f32, mix, hc * c.hidden)); + try n.push(.v("hc_" ++ w ++ "_base", .f32, mix)); + try n.push(.v("hc_" ++ w ++ "_scale", .f32, 3)); + } + try n.push(.v("attn.q_norm.weight", .norm, c.q_lora)); + try n.push(.v("attn.kv_norm.weight", .norm, c.head_dim)); + var sink = NativeSpec.v("attn.attn_sink", .f32, c.heads); + sink.slice = .heads; + try n.push(sink); + if (c.isBackbone(layer) and c.isKvSource(layer)) { + try n.push(.v("attn.compressor.norm.weight", .norm, c.head_dim)); + try n.push(.v("attn.indexer.k_norm.weight", .norm, c.index_dim)); + } + if (c.isBackbone(layer) and c.isIndexSource(layer)) try n.push(.m("attn.indexer.weights_proj.weight", .half, c.index_heads, c.hidden)); + const ex = c.expertsOf(layer); + try n.push(.m("ffn.gate.weight", .half, ex.count, c.hidden)); + try n.push(.v("ffn.gate.bias", .f32, ex.count)); + if (c.isEngram(layer)) { + try n.push(.m("engram.q_weight", .norm, hc, c.hidden)); + try n.push(.m("engram.k_weight", .norm, hc, c.hidden)); + } + return n; +} + +/// Model-level tensors outside the blocks: this rank's vocabulary rows, the final norm (the head is an EXL3 group). +pub fn topNatives(c: *const Config) !Natives { + var n: Natives = .{}; + var embed = NativeSpec.m("embed.weight", .half, c.vocab, c.hidden); + embed.slice = .vocab_rows; + try n.push(embed); + try n.push(.v("norm.weight", .norm, c.hidden)); + return n; +} + +pub const head = GroupSpec{ .proj = .wq_a, .suffix = "head", .split = .col }; + +/// DSpark's heads beside its blocks (the latest mtp.i that has each). +pub fn dsparkNatives(c: *const Config) !Natives { + var n: Natives = .{}; + try n.push(.v("main_norm.weight", .norm, c.hidden)); + try n.push(.v("norm.weight", .norm, c.hidden)); + try n.push(.m("markov_head.embed.weight", .half, c.vocab, c.dspark_markov_rank)); + try n.push(.m("markov_head.head.weight", .half, c.vocab, c.dspark_markov_rank)); + return n; +} + +/// The optional confidence head: fp32 values [D + r], any shape with that many. +pub const dspark_confidence = "confidence_head.proj.weight"; +pub const dspark_main_proj = "main_proj"; + +/// Names the text engine never reads: vision tower, image markers, image routing bias, bf16 copies of EXL3 groups. +pub fn ignored(name: []const u8, has: anytype) bool { + for ([_][]const u8{ "vision.", "aligner.", "image_" }) |p| if (std.mem.startsWith(u8, name, p)) return true; + if (std.mem.endsWith(u8, name, ".ffn.gate.bias_vl")) return true; + if (std.mem.endsWith(u8, name, ".weight")) { + var buf: [256]u8 = undefined; + const base = name[0 .. name.len - ".weight".len]; + const sib = std.fmt.bufPrint(&buf, "{s}.trellis", .{base}) catch return false; + return has.has(sib); + } + return false; +} + +const testing = std.testing; + +test "the release's block 2: a ratio-2 KV and index source with its compressor gate" { + const c: Config = .{}; + var buf: [64]u8 = undefined; + try testing.expectEqualStrings("mtp.1", try blockPrefix(&c, 41, &buf)); + const g = try blockGroups(&c, 2, 1, 2, true); + var names: [32][]const u8 = undefined; + var nbuf: [32][96]u8 = undefined; + for (g.items(), 0..) |s, i| names[i] = try groupName(s, "layers.2", &nbuf[i]); + // wo_a: rank 1 of 2 holds groups 4..7 + try testing.expectEqualStrings("layers.2.attn.wo_a.slice.4", names[3]); + try testing.expectEqual(@as(usize, 3 + 4 + 1 + 3 + 1 + 3), g.len); + try testing.expectEqual(@as(u32, 4096), g.items()[3].k); + try testing.expectEqual(@as(u32, 8192), g.items()[7].k); + // layer 20: ratio 1, no compressor gate; layer 24: reindex (own wq_b only); layer 30: reuse; DSpark blocks: neither + const g20 = try blockGroups(&c, 20, 0, 2, true); + for (g20.items()) |s| try testing.expect(s.proj != .comp_wgate); + try testing.expectEqual(@as(usize, 3 + 4 + 1 + 1), (try blockGroups(&c, 24, 0, 2, false)).len); + try testing.expectEqual(@as(usize, 3 + 4 + 1), (try blockGroups(&c, 30, 0, 2, false)).len); + try testing.expectEqual(@as(usize, 3 + 4 + 1 + 3), (try blockGroups(&c, 41, 0, 2, true)).len); + try testing.expect((try blockGroups(&c, 14, 0, 2, true)).items()[15].proj == .engram_wkv); + try testing.expectEqualStrings("layers.3.ffn.experts.17.w2", try expertName("layers.3", 17, .w2, &buf)); +} + +test "natives and the ignore list" { + const c: Config = .{}; + const n = try blockNatives(&c, 41); + for (n.items()) |s| if (std.mem.eql(u8, s.suffix, "ffn.gate.weight")) try testing.expectEqual(@as(u32, 128), s.shape[0]); + try testing.expectEqual(@as(u32, 24), (try blockNatives(&c, 0)).items()[2].shape[0]); + const Has = struct { + pub fn has(_: @This(), name: []const u8) bool { + return std.mem.eql(u8, name, "layers.2.attn.indexer.wk.trellis"); + } + }; + try testing.expect(ignored("layers.2.attn.indexer.wk.weight", Has{})); + try testing.expect(!ignored("layers.2.attn.kv_norm.weight", Has{})); + try testing.expect(ignored("vision.blocks.3.mlp.w1.weight", Has{})); + try testing.expect(ignored("layers.9.ffn.gate.bias_vl", Has{})); + try testing.expect(Store.half.accepts(.f16) and !Store.f32.accepts(.bf16)); +} diff --git a/zig/src/families/deepseek_v41/pack.zig b/zig/src/families/deepseek_v41/pack.zig new file mode 100644 index 0000000000..131978bcd3 --- /dev/null +++ b/zig/src/families/deepseek_v41/pack.zig @@ -0,0 +1,306 @@ +//! A DeepSeek-V4.1 pack folder by its safetensors headers alone, read with pread and no mmap. + +const std = @import("std"); +const quiet = @import("builtin").is_test; // a test's stderr is the build runner's channel +const log = std.log.scoped(.dsv41); +const core = @import("core"); +const exl3 = @import("exl3.zig"); +const st = core.safetensors; +const Io = std.Io; + +pub const DType = st.DType; + +/// One stored tensor: its shard, dtype, shape and where its bytes start in that file. +pub const Info = struct { + file: u32, + dtype: DType, + rank: u8, + shape: [st.max_rank]usize, + /// absolute byte offset of the first byte in the shard file + start: u64, + nbytes: u64, + + pub fn dims(i: *const Info) []const usize { + return i.shape[0..i.rank]; + } + + pub fn is(i: *const Info, dtype: DType, shape: []const usize) bool { + return i.dtype == dtype and std.mem.eql(usize, i.dims(), shape); + } +}; + +pub const Pack = struct { + arena: std.heap.ArenaAllocator, + /// the folder, else "" for header-only packs (tests, header dumps) + dir: []const u8 = "", + /// shards the index lists that the folder does not hold + missing: u32 = 0, + files: std.ArrayList([]const u8) = .empty, + names: std.StringArrayHashMapUnmanaged(Info) = .empty, + /// the index's weight map (name -> shard); when set, names another shard owns are skipped + owner: std.StringHashMapUnmanaged([]const u8) = .empty, + + pub fn init(gpa: std.mem.Allocator) Pack { + return .{ .arena = std.heap.ArenaAllocator.init(gpa) }; + } + + pub fn deinit(p: *Pack) void { + p.arena.deinit(); + p.* = undefined; + } + + /// Every shard's header in `dir`: the index's weight map if present, else every *.safetensors file. + pub fn open(gpa: std.mem.Allocator, io: Io, dir: []const u8) !Pack { + var p = init(gpa); + errdefer p.deinit(); + const a = p.arena.allocator(); + p.dir = try a.dupe(u8, dir); + const cwd = Io.Dir.cwd(); + var shards: std.ArrayList([]const u8) = .empty; + const index = try std.fs.path.join(a, &.{ dir, "model.safetensors.index.json" }); + if (cwd.readFileAlloc(io, index, a, .limited(1 << 28))) |text| { + try p.useIndex(text); + var it = p.owner.valueIterator(); + while (it.next()) |f| { + for (shards.items) |s| { + if (std.mem.eql(u8, s, f.*)) break; + } else try shards.append(a, f.*); + } + } else |err| switch (err) { + error.FileNotFound => { + var d = try cwd.openDir(io, dir, .{ .iterate = true }); + defer d.close(io); + var it = d.iterate(); + while (try it.next(io)) |e| if (std.mem.endsWith(u8, e.name, ".safetensors")) try shards.append(a, try a.dupe(u8, e.name)); + }, + else => return err, + } + if (shards.items.len == 0) return error.NoSafetensors; + std.mem.sort([]const u8, shards.items, {}, lessStr); + for (shards.items) |name| { + const path = try std.fs.path.join(a, &.{ dir, name }); + // a shard the index lists but the folder lacks: its tensors stay unknown (a plan needing one fails) + const file = cwd.openFile(io, path, .{}) catch |e| switch (e) { + error.FileNotFound => { + p.missing += 1; + continue; + }, + else => return e, + }; + defer file.close(io); + var head: [8]u8 = undefined; + if (try file.readPositionalAll(io, &head, 0) != 8) return error.BadSafetensors; + const n = std.mem.readInt(u64, &head, .little); + const len = try file.length(io); + if (n > len - 8 or n > 1 << 30) return error.BadSafetensors; + const json = try a.alloc(u8, @intCast(n)); + if (try file.readPositionalAll(io, json, 8) != json.len) return error.BadSafetensors; + try p.addHeader(name, json, len - 8 - n); + } + return p; + } + + /// model.safetensors.index.json's weight map, before any header is added. + pub fn useIndex(p: *Pack, json: []const u8) !void { + const a = p.arena.allocator(); + const parsed = try std.json.parseFromSliceLeaky(std.json.Value, a, json, .{}); + if (parsed != .object) return error.BadIndex; + const map = (parsed.object.get("weight_map") orelse return error.BadIndex); + if (map != .object) return error.BadIndex; + try p.owner.ensureTotalCapacity(a, @intCast(map.object.count())); + var it = map.object.iterator(); + while (it.next()) |e| { + if (e.value_ptr.* != .string) return error.BadIndex; + p.owner.putAssumeCapacity(e.key_ptr.*, e.value_ptr.string); + } + } + + /// A shard's header JSON (`data_len` bytes follow; maxInt(u64) for dumps); a name already indexed is refused. + pub fn addHeader(p: *Pack, file_name: []const u8, json: []const u8, data_len: u64) !void { + const a = p.arena.allocator(); + const id: u32 = @intCast(p.files.items.len); + try p.files.append(a, try a.dupe(u8, file_name)); + const header = try st.parseHeader(a, json, @intCast(data_len)); + try p.names.ensureUnusedCapacity(a, header.count()); + var it = header.iterator(); + while (it.next()) |e| { + const v = e.value_ptr.*; + if (p.owner.count() > 0) { + const f = p.owner.get(e.key_ptr.*) orelse continue; + if (!std.mem.eql(u8, f, file_name)) continue; + } + const gop = p.names.getOrPutAssumeCapacity(e.key_ptr.*); + if (gop.found_existing) return error.DuplicateTensor; + gop.value_ptr.* = .{ .file = id, .dtype = v.dtype, .rank = v.rank, .shape = v.shape, .start = 8 + json.len + v.begin, .nbytes = v.end - v.begin }; + } + } + + /// A header dump (the 8-byte length, then the JSON; nothing after) as shard `file_name`. + pub fn addHeaderDump(p: *Pack, file_name: []const u8, bytes: []const u8) !void { + if (bytes.len < 8) return error.BadSafetensors; + const n = std.mem.readInt(u64, bytes[0..8], .little); + if (n > bytes.len - 8) return error.BadSafetensors; + try p.addHeader(file_name, bytes[8..][0..@intCast(n)], std.math.maxInt(u64)); + } + + pub fn get(p: *const Pack, name: []const u8) ?Info { + return p.names.get(name); + } + + pub fn has(p: *const Pack, name: []const u8) bool { + return p.names.contains(name); + } + + /// A required tensor; a missing name is logged and refused. + pub fn need(p: *const Pack, name: []const u8) !Info { + return p.get(name) orelse { + if (!quiet) log.warn("pack has no tensor {s}", .{name}); + return error.MissingTensor; + }; + } + + /// The EXL3 group at `prefix` (its trellis entry), checked int16 [K/16, N/16, words] with fp16 suh [K] and svh [N]. + pub fn group(p: *const Pack, prefix: []const u8, buf: []u8) !exl3.Group { + const t = try p.need(try std.fmt.bufPrint(buf, "{s}.trellis", .{prefix})); + if (t.dtype != .i16) return error.BadTrellis; + const g = try exl3.Group.fromShape(t.dims()); + const suh = try p.need(try std.fmt.bufPrint(buf, "{s}.suh", .{prefix})); + const svh = try p.need(try std.fmt.bufPrint(buf, "{s}.svh", .{prefix})); + if (!suh.is(.f16, &.{g.k()}) or !svh.is(.f16, &.{g.n()})) { + if (!quiet) log.warn("{s}: suh/svh do not match trellis K={d} N={d}", .{ prefix, g.k(), g.n() }); + return error.BadTrellis; + } + return g; + } + + /// The group's marker tensor name (mul1 or mcg) if it has one; its value picks the codebook at load time. + pub fn marker(p: *const Pack, prefix: []const u8, buf: []u8) !?[]const u8 { + for ([_][]const u8{ "mul1", "mcg" }) |m| { + const name = try std.fmt.bufPrint(buf, "{s}.{s}", .{ prefix, m }); + if (p.names.getKey(name)) |k| return k; + } + return null; + } + + /// `span` of tensor `info`'s bytes into `out` (span.bytes() long), run by run. + pub fn read(p: *const Pack, io: Io, info: Info, span: exl3.Span, out: []u8) !void { + if (out.len != span.bytes()) return error.BadSpan; + if (span.count > 0 and span.at(span.count - 1) + span.len > info.nbytes) return error.BadSpan; + if (p.dir.len == 0) return error.HeaderOnlyPack; + var path_buf: [Io.Dir.max_path_bytes]u8 = undefined; + const path = try std.fmt.bufPrint(&path_buf, "{s}/{s}", .{ p.dir, p.files.items[info.file] }); + const file = try Io.Dir.cwd().openFile(io, path, .{}); + defer file.close(io); + if (span.count <= 1) { + if (try file.readPositionalAll(io, out, info.start + span.offset) != out.len) return error.ShortRead; + dropCache(file, info.start + span.offset, out.len); + return; + } + // a strided part (a column split): one read covering every run, then the runs gathered + const first = span.at(0); + const cover: usize = @intCast(span.at(span.count - 1) + span.len - first); + const tmp = try std.heap.page_allocator.alloc(u8, cover); + defer std.heap.page_allocator.free(tmp); + if (try file.readPositionalAll(io, tmp, info.start + first) != cover) return error.ShortRead; + dropCache(file, info.start + first, cover); + const len: usize = @intCast(span.len); + const stride: usize = @intCast(span.stride); + for (0..@intCast(span.count)) |i| @memcpy(out[i * len ..][0..len], tmp[i * stride ..][0..len]); + } + + /// Drops the page cache of bytes just read (POSIX_FADV_DONTNEED): on unified memory it competes with the device. + fn dropCache(file: Io.File, offset: u64, len: usize) void { + if (@import("builtin").os.tag != .linux) return; + const linux = std.os.linux; + _ = linux.fadvise(file.handle, @intCast(offset), @intCast(len), linux.POSIX_FADV.DONTNEED); + } + + /// The codebook of the group at `prefix`: its marker's value, else the 3-instruction default. + pub fn codebook(p: *const Pack, io: Io, prefix: []const u8) !exl3.Codebook { + var buf: [256]u8 = undefined; + const name = (try p.marker(prefix, &buf)) orelse return .inst3; + const info = p.get(name).?; + if (info.nbytes < 4) return error.BadMarker; + var v: [4]u8 = undefined; + try p.read(io, info, .{ .offset = 0, .len = 4 }, &v); + return exl3.Codebook.fromMarker(std.mem.readInt(u32, &v, .little)); + } +}; + +fn lessStr(_: void, x: []const u8, y: []const u8) bool { + return std.mem.order(u8, x, y) == .lt; +} + +const testing = std.testing; + +/// One tensor of an in-memory image. +pub const Spec = struct { name: []const u8, dtype: []const u8, shape: []const usize, fill: u8 = 0 }; + +/// A safetensors image in memory: tensors in order, each filled with `fill`. +pub fn image(a: std.mem.Allocator, specs: []const Spec) ![]u8 { + var json: std.Io.Writer.Allocating = .init(a); + defer json.deinit(); + try json.writer.writeAll("{\"__metadata__\":{\"format\":\"pt\"}"); + var at: usize = 0; + for (specs) |s| { + var n = (DType.parse(s.dtype) orelse return error.UnsupportedDType).size(); + for (s.shape) |d| n *= d; + try json.writer.print(",\"{s}\":{{\"dtype\":\"{s}\",\"shape\":[", .{ s.name, s.dtype }); + for (s.shape, 0..) |d, i| try json.writer.print("{s}{d}", .{ if (i == 0) "" else ",", d }); + try json.writer.print("],\"data_offsets\":[{d},{d}]}}", .{ at, at + n }); + at += n; + } + try json.writer.writeAll("}"); + const head = json.written(); + const out = try a.alloc(u8, 8 + head.len + at); + std.mem.writeInt(u64, out[0..8], head.len, .little); + @memcpy(out[8..][0..head.len], head); + var pos = 8 + head.len; + for (specs) |s| { + var n = DType.parse(s.dtype).?.size(); + for (s.shape) |d| n *= d; + @memset(out[pos..][0..n], s.fill); + pos += n; + } + return out; +} + +test "a folder by its headers: index order, groups checked, strided reads, codebook markers" { + const io = testing.io; + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + const a = testing.allocator; + const one = try image(a, &.{ + .{ .name = "x.trellis", .dtype = "I16", .shape = &.{ 2, 16, 16 }, .fill = 0 }, + .{ .name = "x.suh", .dtype = "F16", .shape = &.{32} }, + .{ .name = "x.svh", .dtype = "F16", .shape = &.{256} }, + .{ .name = "x.mcg", .dtype = "I32", .shape = &.{}, .fill = 0 }, + }); + defer a.free(one); + // tile (kt, nt) holds bytes of value 16 kt + nt, so a column part's runs are checkable + const hl = std.mem.readInt(u64, one[0..8], .little); + const data = one[8 + hl ..]; + for (0..2) |kt| for (0..16) |nt| @memset(data[(kt * 16 + nt) * 32 ..][0..32], @intCast(16 * kt + nt)); + std.mem.writeInt(u32, data[2 * 16 * 32 + 64 + 512 ..][0..4], exl3.Codebook.marker_mcg, .little); + const two = try image(a, &.{.{ .name = "y.weight", .dtype = "BF16", .shape = &.{ 3, 2 }, .fill = 7 }}); + defer a.free(two); + try tmp.dir.writeFile(io, .{ .sub_path = "b.safetensors", .data = one }); + try tmp.dir.writeFile(io, .{ .sub_path = "a.safetensors", .data = two }); + const dir = try tmp.dir.realPathFileAlloc(io, ".", a); + defer a.free(dir); + var p = try Pack.open(a, io, dir); + defer p.deinit(); + try testing.expectEqualStrings("a.safetensors", p.files.items[0]); + var buf: [128]u8 = undefined; + const g = try p.group("x", &buf); + try testing.expectEqual(@as(u32, 256), g.n()); + const part = try g.part(.col, 1, 2); + const out = try a.alloc(u8, @intCast(part.trellis.bytes())); + defer a.free(out); + try p.read(io, p.get("x.trellis").?, part.trellis, out); + for (0..2) |kt| for (0..8) |j| try testing.expectEqual(@as(u8, @intCast(16 * kt + 8 + j)), out[(kt * 8 + j) * 32]); + try testing.expectEqual(exl3.Codebook.mcg, try p.codebook(io, "x")); + try testing.expectEqual(exl3.Codebook.inst3, try p.codebook(io, "y")); + try testing.expectError(error.MissingTensor, p.group("y", &buf)); + try testing.expectError(error.BadSpan, p.read(io, p.get("y.weight").?, .{ .offset = 8, .len = 8 }, out[0..8])); +} diff --git a/zig/src/families/deepseek_v41/phases.zig b/zig/src/families/deepseek_v41/phases.zig new file mode 100644 index 0000000000..bfd86f1caf --- /dev/null +++ b/zig/src/families/deepseek_v41/phases.zig @@ -0,0 +1,206 @@ +//! TF_DSV41_PHASES (0 | 1 | sync, _OUT=): where every millisecond of a serving round goes, by named phase. + +const std = @import("std"); + +pub const Name = enum { + prefill, + draft, + @"draft.pass", + window, + forward, + @"graph.stage", + @"graph.replay", + @"graph.capture", + @"engram.wait", + prefetch, + candidates, + commit, + /// the speculative pass's launch after a window (TF_DSV41_SPEC_DRAFT; its collect is `draft.pass`) + spec, + /// host time between top-level phases (the lanes' own work, token events, plan link) + host, + /// inside `draft.pass` (TF_DSV41_DRAFT_GRAPHS): the host waiting for the device pass and its copy + @"draft.wait", + /// inside `draft.pass`: the host's chain (Markov bias, choice, confidences) over the gathered candidates + @"draft.chain", + /// inside `draft.pass`, the host step (DRAFT_GRAPHS off): the logits' download, the host candidates, their gather + @"draft.host", +}; + +/// A gap between top-level phases this long or longer is idle (no live stream), not a round's host time. +pub const idle_ns: u64 = 50 * std.time.ns_per_ms; + +const count = std.enums.values(Name).len; + +pub const Mode = enum { off, wall, sync }; + +/// The stream the `sync` mode waits for. +pub const Sync = struct { ctx: *anyopaque, run: *const fn (ctx: *anyopaque) anyerror!void }; + +/// Counters beside the phases (an object without "ms", so phase filters skip it): written into the report. +pub const Extra = struct { ctx: *anyopaque, write: *const fn (ctx: *anyopaque, w: *std.Io.Writer) anyerror!void }; + +pub const Phases = struct { + mode: Mode, + sync: ?Sync = null, + extra: ?Extra = null, + ns: [count]u64 = @splat(0), + n: [count]u64 = @splat(0), + rounds: u64 = 0, + tokens: u64 = 0, + t0: u64, + path: ?[]const u8 = null, + reported: u64 = 0, + /// phases open now (a top-level phase starts at depth 0), and when the last top-level phase ended (0: none yet) + depth: u32 = 0, + last_end: u64 = 0, + + pub fn parseMode(v: []const u8) !Mode { + if (std.mem.eql(u8, v, "0") or v.len == 0) return .off; + if (std.mem.eql(u8, v, "1")) return .wall; + if (std.mem.eql(u8, v, "sync")) return .sync; + return error.BadPhasesMode; + } + + /// From TF_DSV41_PHASES / TF_DSV41_PHASES_OUT; null when off. + pub fn fromEnv(gpa: std.mem.Allocator) !?*Phases { + const mode = try parseMode(if (std.c.getenv("TF_DSV41_PHASES")) |v| std.mem.span(v) else "0"); + if (mode == .off) return null; + const p = try gpa.create(Phases); + p.* = .{ .mode = mode, .t0 = nowNs(), .path = if (std.c.getenv("TF_DSV41_PHASES_OUT")) |v| std.mem.span(v) else null }; + return p; + } + + pub fn reset(p: *Phases) void { + p.ns = @splat(0); + p.n = @splat(0); + p.rounds = 0; + p.tokens = 0; + p.t0 = nowNs(); + p.depth = 0; + p.last_end = 0; + } + + fn settle(p: *Phases) void { + if (p.mode != .sync) return; + const s = p.sync orelse return; + s.run(s.ctx) catch {}; + } + + pub fn add(p: *Phases, name: Name, ns: u64) void { + p.ns[@backingInt(name)] += ns; + p.n[@backingInt(name)] += 1; + } + + /// The report: {name: {ms, n, ms_avg}} plus wall_ms, mode, rounds, tokens. + pub fn write(p: *const Phases, w: *std.Io.Writer) !void { + const wall = @as(f64, @floatFromInt(nowNs() - p.t0)) / 1e6; + try w.print("{{\"mode\": \"{s}\", \"wall_ms\": {d:.2}, \"rounds\": {d}, \"tokens\": {d}", .{ if (p.mode == .sync) "sync" else "1", wall, p.rounds, p.tokens }); + for (0..count) |i| { + if (p.n[i] == 0) continue; + const ms = @as(f64, @floatFromInt(p.ns[i])) / 1e6; + try w.print(", \"{s}\": {{\"ms\": {d:.2}, \"n\": {d}, \"ms_avg\": {d:.3}}}", .{ @tagName(@as(Name, @fromBackingInt(@intCast(i)))), ms, p.n[i], ms / @as(f64, @floatFromInt(p.n[i])) }); + } + if (p.extra) |x| try x.write(x.ctx, w); + try w.writeAll("}\n"); + } + + /// The report into TF_DSV41_PHASES_OUT (when set). + pub fn flush(p: *Phases, gpa: std.mem.Allocator, io: std.Io) void { + const path = p.path orelse return; + var w: std.Io.Writer.Allocating = .init(gpa); + defer w.deinit(); + p.write(&w.writer) catch return; + std.Io.Dir.cwd().writeFile(io, .{ .sub_path = path, .data = w.written() }) catch |e| std.log.err("phases: {s} not written ({t})", .{ path, e }); + p.reported = p.rounds; + } + + /// A verify window's round finished with `kept` tokens committed; the report is rewritten every 64 rounds. + pub fn round(p: *Phases, gpa: std.mem.Allocator, io: std.Io, kept: usize) void { + p.rounds += 1; + p.tokens += kept; + if (p.rounds - p.reported >= 64) p.flush(gpa, io); + } +}; + +/// One phase's timer: `const t = phases.start(p, .window); defer t.stop();` (p null: nothing). +pub const Timer = struct { + p: ?*Phases, + name: Name, + t0: u64, + + pub fn stop(t: Timer) void { + const p = t.p orelse return; + p.settle(); + const now = nowNs(); + p.add(t.name, now -| t.t0); + p.depth -|= 1; + if (p.depth == 0) p.last_end = now; + } +}; + +pub fn start(p: ?*Phases, name: Name) Timer { + const x = p orelse return .{ .p = null, .name = name, .t0 = 0 }; + if (x.depth == 0 and x.last_end != 0) { + const gap = nowNs() -| x.last_end; + if (gap < idle_ns) x.add(.host, gap); + } + x.depth += 1; + x.settle(); + return .{ .p = x, .name = name, .t0 = nowNs() }; +} + +pub fn nowNs() u64 { + var ts: std.c.timespec = undefined; + _ = std.c.clock_gettime(.MONOTONIC, &ts); + return @as(u64, @intCast(ts.sec)) * std.time.ns_per_s + @as(u64, @intCast(ts.nsec)); +} + +test "phases: the reference engine's names and report shape; sync mode syncs at both ends" { + const Count = struct { + n: u32 = 0, + fn run(ctx: *anyopaque) anyerror!void { + const c: *@This() = @ptrCast(@alignCast(ctx)); + c.n += 1; + } + }; + var c: Count = .{}; + var p: Phases = .{ .mode = .sync, .t0 = nowNs(), .sync = .{ .ctx = &c, .run = Count.run } }; + { + const t = start(&p, .@"draft.pass"); + defer t.stop(); + } + const off = start(null, .window); + off.stop(); + try std.testing.expectEqual(@as(u32, 2), c.n); + { + // a second top-level phase: one `host` gap before it; the nested phase opens none + const t = start(&p, .window); + defer t.stop(); + const n = start(&p, .forward); + n.stop(); + } + try std.testing.expectEqual(@as(u64, 1), p.n[@backingInt(Name.host)]); + p.last_end = nowNs() - idle_ns; // an idle gap: not counted + start(&p, .commit).stop(); + try std.testing.expectEqual(@as(u64, 1), p.n[@backingInt(Name.host)]); + p.add(.@"engram.wait", 1_500_000); + p.add(.@"engram.wait", 500_000); + p.rounds = 2; + p.tokens = 7; + var w: std.Io.Writer.Allocating = .init(std.testing.allocator); + defer w.deinit(); + try p.write(&w.writer); + const parsed = try std.json.parseFromSlice(std.json.Value, std.testing.allocator, w.written(), .{}); + defer parsed.deinit(); + const o = parsed.value.object; + try std.testing.expectEqualStrings("sync", o.get("mode").?.string); + try std.testing.expectEqual(@as(i64, 7), o.get("tokens").?.integer); + const ew = o.get("engram.wait").?.object; + try std.testing.expectEqual(@as(i64, 2), ew.get("n").?.integer); + try std.testing.expectEqual(@as(f64, 2.0), ew.get("ms").?.float); + try std.testing.expect(o.get("draft.pass") != null and o.get("window") != null); + try std.testing.expect(o.get("host") != null); // the gap between the two top-level phases above + try std.testing.expectEqual(Mode.off, try Phases.parseMode("0")); + try std.testing.expectError(error.BadPhasesMode, Phases.parseMode("nvtx")); +} diff --git a/zig/src/families/deepseek_v41/plan.zig b/zig/src/families/deepseek_v41/plan.zig new file mode 100644 index 0000000000..e730a4ee2d --- /dev/null +++ b/zig/src/families/deepseek_v41/plan.zig @@ -0,0 +1,295 @@ +//! A rank's weights planned from the pack's headers alone: group TP parts, routed layouts, byte totals, coverage. + +const std = @import("std"); +const quiet = @import("builtin").is_test; // a test's stderr is the build runner's channel +const log = std.log.scoped(.dsv41); +const Config = @import("config.zig").Config; +const exl3 = @import("exl3.zig"); +const names = @import("names.zig"); +const Pack = @import("pack.zig").Pack; +const Info = @import("pack.zig").Info; + +/// One EXL3 group of the rank: its stored name and the rank's part. +pub const GroupPlan = struct { + proj: names.Proj, + prefix: []const u8, + split: exl3.Split, + full: exl3.Group, + part: exl3.Part, + /// marker tensor (mul1 / mcg) whose value picks the codebook at load; null: the 3-instruction default + marker: ?[]const u8, + /// wo_a: the global group index this slice is + slice: u32 = 0, +}; + +pub const NativePlan = struct { + name: []const u8, + info: Info, + slice: names.Slice, + /// bytes of the rank's slice as stored + bytes: u64, +}; + +/// One routed projection: each expert's group part, their layout on the device, and the stored prefixes. +pub const ExpertsPlan = struct { + proj: names.ExpertProj, + parts: []exl3.Part, + prefixes: [][]const u8, + layout: exl3.Layout, +}; + +pub const LayerPlan = struct { + index: u32, + prefix: []const u8, + mode: @import("config.zig").Mode, + groups: []GroupPlan, + natives: []NativePlan, + experts: [3]ExpertsPlan, + /// experts whose gate (w1) and up (w3) widths differ: x3gm cannot fuse them, the block goes to x3pf / grouped + gate_up_mismatch: u32, + + /// Routed trellis bytes plus the shared expert's: the quant repo's `rank_bytes`. + pub fn expertBytes(l: *const LayerPlan) u64 { + var n: u64 = 0; + for (l.experts) |e| n += e.layout.trellisBytes(); + for (l.groups) |g| switch (g.proj) { + .shared_w1, .shared_w2, .shared_w3 => n += g.part.trellis.bytes(), + else => {}, + }; + return n; + } + + /// Everything the rank holds for the block as stored: trellises, suh/svh, natives. + pub fn bytes(l: *const LayerPlan) u64 { + var n: u64 = 0; + for (l.groups) |g| n += g.part.bytes(); + for (l.natives) |v| n += v.bytes; + for (l.experts) |e| { + n += e.layout.trellisBytes(); + for (e.parts) |p| n += p.suh.bytes() + p.svh.bytes(); + } + return n; + } +}; + +pub const Dspark = struct { + main_proj: GroupPlan, + natives: []NativePlan, + confidence: ?NativePlan, +}; + +pub const Plan = struct { + arena: std.heap.ArenaAllocator, + rank: u32, + world: u32, + layers: []LayerPlan, + top: []NativePlan, + /// null when only some blocks were planned + head: ?GroupPlan, + dspark: ?Dspark, + /// stored names neither planned nor on the ignore list (the loader refuses a pack with any) + leftovers: [][]const u8, + + pub fn deinit(p: *Plan) void { + p.arena.deinit(); + p.* = undefined; + } + + pub fn bytes(p: *const Plan) u64 { + var n: u64 = if (p.head) |h| h.part.bytes() else 0; + for (p.top) |v| n += v.bytes; + for (p.layers) |*l| n += l.bytes(); + if (p.dspark) |d| { + n += d.main_proj.part.bytes(); + for (d.natives) |v| n += v.bytes; + if (d.confidence) |c| n += c.bytes; + } + return n; + } +}; + +pub const Options = struct { + rank: u32, + world: u32, + /// plan only these blocks (single-layer runs, header dumps): no top tensors, head, DSpark or coverage; null: all + blocks: ?[]const u32 = null, + /// with `blocks`: also the vocabulary rows, the final norm and the head (a window's finish) + top: bool = false, + /// with `blocks` and `top`: also DSpark's heads (main_proj, its norms, the Markov and confidence heads) + dspark: bool = false, +}; + +const Builder = struct { + a: std.mem.Allocator, + c: *const Config, + pack: *const Pack, + o: Options, + used: std.StringHashMapUnmanaged(void) = .empty, + buf: [256]u8 = undefined, + + fn mark(b: *Builder, name: []const u8) !void { + try b.used.put(b.a, b.pack.names.getKey(name) orelse return error.MissingTensor, {}); + } + + fn dupe(b: *Builder, s: []const u8) ![]const u8 { + return b.a.dupe(u8, s); + } + + fn group(b: *Builder, proj: names.Proj, prefix: []const u8, split: exl3.Split, k: u32, n: u32) !GroupPlan { + const g = try b.pack.group(prefix, &b.buf); + if ((k != 0 and g.k() != k) or (n != 0 and g.n() != n)) { + if (!quiet) log.warn("{s}: K={d} N={d}, expected K={d} N={d}", .{ prefix, g.k(), g.n(), k, n }); + return error.UnexpectedShape; + } + inline for (.{ "trellis", "suh", "svh" }) |part| try b.mark(try std.fmt.bufPrint(&b.buf, "{s}." ++ part, .{prefix})); + const marker = try b.pack.marker(prefix, &b.buf); + if (marker) |m| try b.mark(m); + return .{ .proj = proj, .prefix = try b.dupe(prefix), .split = split, .full = g, .part = try g.part(split, b.o.rank, b.o.world), .marker = if (marker) |m| try b.dupe(m) else null }; + } + + fn native(b: *Builder, name: []const u8, spec: names.NativeSpec) !NativePlan { + const info = try b.pack.need(name); + const want = spec.dims(); + var ok = spec.store.accepts(info.dtype) and info.rank == want.len; + if (ok) for (want, info.dims()) |w, d| { + ok = ok and w == d; + }; + if (!ok) { + if (!quiet) log.warn("{s}: {t} {any}, expected {t} storage of {any}", .{ name, info.dtype, info.dims(), spec.store, want }); + return error.UnexpectedTensor; + } + try b.mark(name); + const bytes = switch (spec.slice) { + .whole => info.nbytes, + .heads, .vocab_rows => blk: { + if (info.shape[0] % b.o.world != 0) return error.SplitNotEven; + break :blk info.nbytes / b.o.world; + }, + }; + return .{ .name = try b.dupe(name), .info = info, .slice = spec.slice, .bytes = bytes }; + } + + fn experts(b: *Builder, block: []const u8, index: u32, proj: names.ExpertProj) !ExpertsPlan { + const count = b.c.expertsOf(index).count; + const parts = try b.a.alloc(exl3.Part, count); + const prefixes = try b.a.alloc([]const u8, count); + const kn = proj.kn(b.c); + var codebook_marker: ?bool = null; + for (parts, prefixes, 0..) |*p, *pre, e| { + var nb: [128]u8 = undefined; + const name = try names.expertName(block, @intCast(e), proj, &nb); + const g = try b.group(.wq_a, name, proj.split(), kn[0], kn[1]); + // the codebook is one a projection; here the marker's presence, its value at load + const has = g.marker != null; + if (codebook_marker) |m| if (m != has) return error.CodebooksDiffer; + codebook_marker = has; + p.* = g.part; + pre.* = g.prefix; + } + return .{ .proj = proj, .parts = parts, .prefixes = prefixes, .layout = try exl3.Layout.of(b.a, parts) }; + } + + fn layer(b: *Builder, index: u32) !LayerPlan { + var pb: [32]u8 = undefined; + const block = try b.dupe(try names.blockPrefix(b.c, index, &pb)); + const has_shared = b.pack.has(try std.fmt.bufPrint(&b.buf, "{s}.ffn.shared_experts.w1.trellis", .{block})); + const specs = try names.blockGroups(b.c, index, b.o.rank, b.o.world, has_shared); + const groups = try b.a.alloc(GroupPlan, specs.len); + for (specs.items(), groups) |s, *g| { + var nb: [128]u8 = undefined; + g.* = try b.group(s.proj, try names.groupName(s, block, &nb), s.split, s.k, s.n); + g.slice = s.slice; + } + const nspecs = try names.blockNatives(b.c, index); + const natives = try b.a.alloc(NativePlan, nspecs.len); + for (nspecs.items(), natives) |s, *v| { + var nb: [128]u8 = undefined; + v.* = try b.native(try std.fmt.bufPrint(&nb, "{s}.{s}", .{ block, s.suffix }), s); + } + // the other ranks' wo_a groups are theirs to read: mark them so coverage holds on every rank + var g: u32 = 0; + while (g < b.c.o_groups) : (g += 1) { + var nb: [128]u8 = undefined; + const pre = try std.fmt.bufPrint(&nb, "{s}.attn.wo_a.slice.{d}", .{ block, g }); + inline for (.{ "trellis", "suh", "svh" }) |part| try b.mark(try std.fmt.bufPrint(&b.buf, "{s}." ++ part, .{pre})); + if (try b.pack.marker(pre, &b.buf)) |m| try b.mark(m); + } + var out: LayerPlan = .{ .index = index, .prefix = block, .mode = b.c.mode(index), .groups = groups, .natives = natives, .experts = undefined, .gate_up_mismatch = 0 }; + inline for (.{ .w1, .w2, .w3 }, 0..) |p, i| out.experts[i] = try b.experts(block, index, p); + for (out.experts[0].layout.words, out.experts[2].layout.words) |w1, w3| out.gate_up_mismatch += @intFromBool(w1 != w3); + return out; + } + + /// The latest mtp.i holding `suffix` (as a tensor, a group or a .weight). + fn findDspark(b: *Builder, suffix: []const u8, buf: []u8) !?[]const u8 { + var i = b.c.mtp_layers; + while (i > 0) { + i -= 1; + const base = try std.fmt.bufPrint(buf, "mtp.{d}.{s}", .{ i, suffix }); + for ([_][]const u8{ "", ".trellis", ".weight" }) |ext| { + var nb: [160]u8 = undefined; + if (b.pack.has(try std.fmt.bufPrint(&nb, "{s}{s}", .{ base, ext }))) return base; + } + } + return null; + } + + fn dspark(b: *Builder) !?Dspark { + if (b.c.mtp_layers == 0) return null; + var fb: [160]u8 = undefined; + const mp = (try b.findDspark(names.dspark_main_proj, &fb)) orelse return null; + const main_proj = try b.group(.wq_a, mp, .whole, 3 * b.c.hidden, b.c.hidden); + const specs = try names.dsparkNatives(b.c); + const natives = try b.a.alloc(NativePlan, specs.len); + for (specs.items(), natives) |s, *v| { + const stem = s.suffix[0 .. s.suffix.len - ".weight".len]; + const base = (try b.findDspark(stem, &fb)) orelse return error.MissingTensor; + var nb: [160]u8 = undefined; + v.* = try b.native(try std.fmt.bufPrint(&nb, "{s}.weight", .{base}), s); + } + var confidence: ?NativePlan = null; + if (try b.findDspark("confidence_head.proj", &fb)) |base| { + var nb: [160]u8 = undefined; + const name = try std.fmt.bufPrint(&nb, "{s}.weight", .{base}); + const info = try b.pack.need(name); + var n: u64 = 1; + for (info.dims()) |d| n *= d; + if (info.dtype != .f32 and info.dtype != .bf16 or n != b.c.hidden + b.c.dspark_markov_rank) return error.UnexpectedTensor; + try b.mark(name); + confidence = .{ .name = try b.dupe(name), .info = info, .slice = .whole, .bytes = info.nbytes }; + } + return .{ .main_proj = main_proj, .natives = natives, .confidence = confidence }; + } +}; + +/// The rank's plan; heads, groups and the vocabulary must split over `world` (blocks of 128). +pub fn build(gpa: std.mem.Allocator, c: *const Config, pack: *const Pack, o: Options) !Plan { + if (o.world == 0 or o.rank >= o.world or c.heads % o.world != 0 or c.o_groups % o.world != 0 or c.vocab % (128 * o.world) != 0) return error.BadWorld; + var arena = std.heap.ArenaAllocator.init(gpa); + errdefer arena.deinit(); + var b: Builder = .{ .a = arena.allocator(), .c = c, .pack = pack, .o = o }; + const full = o.blocks == null; + const layers = try b.a.alloc(LayerPlan, if (o.blocks) |bl| bl.len else c.allLayers()); + for (layers, 0..) |*l, i| { + const index: u32 = if (o.blocks) |bl| bl[i] else @intCast(i); + if (index >= c.allLayers()) return error.LayerOutOfBounds; + l.* = try b.layer(index); + } + var top: []NativePlan = &.{}; + var head: ?GroupPlan = null; + var ds: ?Dspark = null; + if (full or o.top) { + const tspecs = try names.topNatives(c); + top = try b.a.alloc(NativePlan, tspecs.len); + for (tspecs.items(), top) |s, *v| v.* = try b.native(s.suffix, s); + head = try b.group(.wq_a, names.head.suffix, .col, c.hidden, c.vocab); + if (full or o.dspark) ds = try b.dspark(); + } + // coverage: every stored name was planned or is knowingly left (vision, image routing, bf16 copies of groups) + var left: std.ArrayList([]const u8) = .empty; + if (full) for (pack.names.keys()) |k| { + if (b.used.contains(k) or names.ignored(k, pack)) continue; + try left.append(b.a, try b.dupe(k)); + }; + return .{ .arena = arena, .rank = o.rank, .world = o.world, .layers = layers, .top = top, .head = head, .dspark = ds, .leftovers = left.items }; +} diff --git a/zig/src/families/deepseek_v41/prepare.zig b/zig/src/families/deepseek_v41/prepare.zig new file mode 100644 index 0000000000..0869e34320 --- /dev/null +++ b/zig/src/families/deepseek_v41/prepare.zig @@ -0,0 +1,144 @@ +//! The run-time weight forms built at load from named host tensors: linear strips, routed + shared experts' tables. + +const std = @import("std"); + +/// Trellis int16 [kt, nt, words] -> int32 [nt/8, kt, 8, words/2]: each 128-column block's tiles in K order. +pub fn strips(gpa: std.mem.Allocator, src: []const u8, kt: usize, nt: usize, words: usize) ![]align(16) u8 { + if (nt % 8 != 0 or src.len != kt * nt * words * 2) return error.BadTrellis; + const run = 8 * words * 2; // 8 tiles of one K row inside one 128-column block + const out = try gpa.alignedAlloc(u8, .@"16", src.len); + const blocks = nt / 8; + for (0..blocks * kt) |i| { + const nb = i / kt; + const k = i % kt; + @memcpy(out[i * run ..][0..run], src[(k * nt + nb * 8) * words * 2 ..][0..run]); + } + return out; +} + +/// Strips words -> dense3's lanes layout (dense3.cu to_lanes_kernel): per strip, lane L's bits of each G-step group. +pub fn lanes(gpa: std.mem.Allocator, strip_words: []const u8, kt: usize, k2: usize) ![]align(16) u8 { + const G: usize = if (k2 == 16) 1 else 2; + const nb = k2 / 2; // bytes of a lane's run of one tile (4 K2 bits) + const strip = 4 * kt * 32 * k2; // bytes a strip + if (k2 < 4 or k2 % 2 != 0 or kt % G != 0 or strip_words.len % strip != 0) return error.BadStrips; + const out = try gpa.alignedAlloc(u8, .@"16", strip_words.len); + const tile = 16 * k2; // bytes a tile (4 K2 words) + for (0..strip_words.len / strip) |s| { + const src = strip_words[s * strip ..][0..strip]; + const dst = out[s * strip ..][0..strip]; + for (0..kt / G) |gi| { + const g0 = gi * G * 8 * tile; // the group's first byte (src and dst) + for (0..32) |L| for (0..8 * G) |tt| for (0..nb) |j| { + const sb = g0 + tt * tile + L * nb + j; // stream byte in the source + const lb = tt * nb + j; // stream byte in the lane's run + const w = lb / 4; // the lane's word + const db = g0 + 4 * (((w / 4) * 32 + L) * 4 + w % 4) + (lb % 4); // stream byte in the destination + dst[4 * (db / 4) + 3 - db % 4] = src[4 * (sb / 4) + 3 - sb % 4]; + }; + } + } + return out; +} + +/// One expert of a projection: its trellis's device address and K2, its scales as stored (fp16). +pub const Expert = struct { trellis: u64, k2: i32, suh: []const u8, svh: []const u8 }; + +/// A projection's tables: int64 addresses, int32 K2s, suh [E, K] and svh [E, N] fp16. +pub const Tables = struct { + ptrs: []i64, + k2s: []i32, + suh: []align(16) u8, + svh: []align(16) u8, + k2_lo: i32, + k2_hi: i32, + + pub fn deinit(t: *Tables, gpa: std.mem.Allocator) void { + gpa.free(t.ptrs); + gpa.free(t.k2s); + gpa.free(t.suh); + gpa.free(t.svh); + t.* = undefined; + } +}; + +/// The tables of `experts` in order, the shared expert last; every suh / svh must have the projection's K and N. +pub fn tables(gpa: std.mem.Allocator, experts: []const Expert) !Tables { + if (experts.len == 0) return error.NoExperts; + const kb = experts[0].suh.len; + const nb = experts[0].svh.len; + var t: Tables = .{ + .ptrs = try gpa.alloc(i64, experts.len), + .k2s = try gpa.alloc(i32, experts.len), + .suh = try gpa.alignedAlloc(u8, .@"16", experts.len * kb), + .svh = try gpa.alignedAlloc(u8, .@"16", experts.len * nb), + .k2_lo = std.math.maxInt(i32), + .k2_hi = 0, + }; + errdefer t.deinit(gpa); + for (experts, 0..) |e, i| { + if (e.suh.len != kb or e.svh.len != nb) return error.ExpertShapesDiffer; + t.ptrs[i] = @bitCast(e.trellis); + t.k2s[i] = e.k2; + @memcpy(t.suh[i * kb ..][0..kb], e.suh); + @memcpy(t.svh[i * nb ..][0..nb], e.svh); + t.k2_lo = @min(t.k2_lo, e.k2); + t.k2_hi = @max(t.k2_hi, e.k2); + } + return t; +} + +const testing = std.testing; + +test "strips: each 128-column block's tiles in K order" { + const a = testing.allocator; + const kt = 3; + const nt = 16; + const words = 2; // one int32 a tile, to keep the check readable + var src: [kt * nt * words * 2]u8 = undefined; + // tile (k, n) holds the byte 16 k + n + for (0..kt) |k| for (0..nt) |n| @memset(src[(k * nt + n) * words * 2 ..][0 .. words * 2], @intCast(16 * k + n)); + const out = try strips(a, &src, kt, nt, words); + defer a.free(out); + // out[nb][k][j] = tile (k, 8 nb + j) + for (0..2) |nb| for (0..kt) |k| for (0..8) |j| { + try testing.expectEqual(@as(u8, @intCast(16 * k + 8 * nb + j)), out[((nb * kt + k) * 8 + j) * words * 2]); + }; + try testing.expectError(error.BadTrellis, strips(a, src[0..8], kt, nt, words)); +} + +test "lanes: the numpy reference's layout (to_lanes) at every dense3 width" { + // fixtures/lanes-ref.bin: for K2 4, 6, 8, 10, 12, 16: u32 K2, u32 NB, u32 KT, strips words, lanes words + const a = testing.allocator; + var rest: []const u8 = @embedFile("fixtures/lanes-ref.bin"); + var cases: usize = 0; + while (rest.len > 0) : (cases += 1) { + const k2 = std.mem.readInt(u32, rest[0..4], .little); + const nb = std.mem.readInt(u32, rest[4..8], .little); + const kt = std.mem.readInt(u32, rest[8..12], .little); + const bytes = 4 * nb * kt * 32 * k2; + const src = rest[12..][0..bytes]; + const want = rest[12 + bytes ..][0..bytes]; + const got = try lanes(a, src, kt, k2); + defer a.free(got); + try testing.expectEqualSlices(u8, want, got); + rest = rest[12 + 2 * bytes ..]; + } + try testing.expectEqual(@as(usize, 6), cases); +} + +test "expert tables: addresses, K2s and stacked scales in order, the width range" { + const a = testing.allocator; + const s1 = [_]u8{ 1, 2, 3, 4 }; + const s2 = [_]u8{ 5, 6, 7, 8 }; + const v1 = [_]u8{ 9, 9 }; + const v2 = [_]u8{ 7, 7 }; + var t = try tables(a, &.{ .{ .trellis = 0x7f00_0000_1000, .k2 = 4, .suh = &s1, .svh = &v1 }, .{ .trellis = 0x7f00_0002_0000, .k2 = 10, .suh = &s2, .svh = &v2 } }); + defer t.deinit(a); + try testing.expectEqualSlices(i64, &.{ 0x7f00_0000_1000, 0x7f00_0002_0000 }, t.ptrs); + try testing.expectEqualSlices(i32, &.{ 4, 10 }, t.k2s); + try testing.expectEqualSlices(u8, &.{ 1, 2, 3, 4, 5, 6, 7, 8 }, t.suh); + try testing.expectEqualSlices(u8, &.{ 9, 9, 7, 7 }, t.svh); + try testing.expect(t.k2_lo == 4 and t.k2_hi == 10); + try testing.expectError(error.ExpertShapesDiffer, tables(a, &.{ .{ .trellis = 1, .k2 = 4, .suh = &s1, .svh = &v1 }, .{ .trellis = 2, .k2 = 4, .suh = s2[0..2], .svh = &v2 } })); +} diff --git a/zig/src/families/deepseek_v41/prepared.zig b/zig/src/families/deepseek_v41/prepared.zig new file mode 100644 index 0000000000..4f0e0f911f --- /dev/null +++ b/zig/src/families/deepseek_v41/prepared.zig @@ -0,0 +1,281 @@ +//! Prepared weight folders (TF_DSV41_PREPARED, _WRITE, _VERIFY): built layer images cached on disk for a fast boot. +const std = @import("std"); +const quiet = @import("builtin").is_test; // a test's stderr is the build runner's channel +const named = @import("named.zig"); +const pack_mod = @import("pack.zig"); +const Io = std.Io; +const Allocator = std.mem.Allocator; + +const magic = "TFDSV41P"; +const version: u32 = 1; +const page: u64 = 4096; + +/// What the images depend on besides the pack's bytes. +pub const Key = struct { + rank: u32, + world: u32, + o_groups: u32, + blocks: []const u32, + dspark: bool, +}; + +/// The builders' source, so a change to how images are made never reads an old folder. +const sources = [_][]const u8{ @embedFile("named.zig"), @embedFile("plan.zig"), @embedFile("stage.zig"), @embedFile("pack.zig"), @embedFile("exl3.zig") }; + +pub const Stats = struct { hits: u32 = 0, misses: u32 = 0, written: u32 = 0, read_bytes: u64 = 0, read_ns: u64 = 0, write_ns: u64 = 0 }; + +pub const Cache = struct { + gpa: Allocator, + io: Io, + /// ``

//rank`` + root: []u8, + write: bool, + verify: bool, + stats: Stats = .{}, + + /// The cache TF_DSV41_PREPARED names, or null when unset. + pub fn fromEnv(gpa: Allocator, io: Io, p: *const pack_mod.Pack, key: Key) !?Cache { + const dir = std.mem.span(std.c.getenv("TF_DSV41_PREPARED") orelse return null); + if (std.mem.trim(u8, dir, " ").len == 0) return null; + const flag = struct { + fn on(name: [*:0]const u8) bool { + const v = std.c.getenv(name) orelse return false; + return std.mem.eql(u8, std.mem.span(v), "1"); + } + }.on; + return try open(gpa, io, dir, try packDigest(io, p), key, flag("TF_DSV41_PREPARED_WRITE"), flag("TF_DSV41_PREPARED_VERIFY")); + } + + pub fn open(gpa: Allocator, io: Io, dir: []const u8, pack_digest: [32]u8, key: Key, write: bool, verify: bool) !Cache { + var h = std.crypto.hash.sha2.Sha256.init(.{}); + h.update(magic); + h.update(std.mem.asBytes(&version)); + for (sources) |s| h.update(s); + h.update(&pack_digest); + h.update(std.mem.asBytes(&[_]u32{ key.rank, key.world, key.o_groups, @intFromBool(key.dspark), @intCast(key.blocks.len) })); + h.update(std.mem.sliceAsBytes(key.blocks)); + var d: [32]u8 = undefined; + h.final(&d); + const hex = std.fmt.bytesToHex(d[0..8].*, .lower); + const root = try std.fmt.allocPrint(gpa, "{s}/{s}/rank{d}", .{ dir, &hex, key.rank }); + errdefer gpa.free(root); + if (write) try Io.Dir.cwd().createDirPath(io, root); + return .{ .gpa = gpa, .io = io, .root = root, .write = write, .verify = verify }; + } + + pub fn deinit(c: *Cache) void { + c.gpa.free(c.root); + } + + fn path(c: *const Cache, buf: []u8, tag: []const u8, suffix: []const u8) ![]const u8 { + return std.fmt.bufPrint(buf, "{s}/{s}.bin{s}", .{ c.root, tag, suffix }); + } + + /// Asks the kernel to read `.bin` ahead (POSIX_FADV_WILLNEED); a missing file is ignored, nothing waits. + pub fn willNeed(c: *const Cache, tag: []const u8) void { + var pbuf: [std.fs.max_path_bytes]u8 = undefined; + const p = c.path(pbuf[0 .. pbuf.len - 1], tag, "") catch return; + pbuf[p.len] = 0; + const linux = std.os.linux; + const rc = linux.openat(linux.AT.FDCWD, pbuf[0..p.len :0], .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); + if (linux.errno(rc) != .SUCCESS) return; + const fd: linux.fd_t = @intCast(rc); + defer _ = linux.close(fd); + _ = linux.fadvise(fd, 0, 0, linux.POSIX_FADV.WILLNEED); + } + + /// Fills `b` from ``.bin``: true when read, false when there is no usable file (`b` is then untouched). + pub fn get(c: *Cache, b: *named.Builder, tag: []const u8) !bool { + var pbuf: [std.fs.max_path_bytes]u8 = undefined; + const t0 = Io.Clock.awake.now(c.io).toNanoseconds(); + const file = Io.Dir.cwd().openFile(c.io, try c.path(&pbuf, tag, ""), .{}) catch { + c.stats.misses += 1; + return false; + }; + defer file.close(c.io); + const got = readInto(c.gpa, c.io, file, b, c.verify) catch |e| { + if (!quiet) std.log.warn("dsv41 prepared: {s} unusable ({t}); building it from the pack", .{ pbuf[0 .. c.root.len + tag.len + 5], e }); + c.stats.misses += 1; + return false; + }; + c.stats.hits += 1; + c.stats.read_bytes += got; + c.stats.read_ns += @intCast(Io.Clock.awake.now(c.io).toNanoseconds() - t0); + return true; + } + + /// Writes ``.bin`` from `b` when writing is on and the file is missing. + pub fn put(c: *Cache, b: *const named.Builder, tag: []const u8) !void { + if (!c.write) return; + var pbuf: [std.fs.max_path_bytes]u8 = undefined; + var tbuf: [std.fs.max_path_bytes]u8 = undefined; + const final_path = try c.path(&pbuf, tag, ""); + const tmp_path = try c.path(&tbuf, tag, ".tmp"); + const t0 = Io.Clock.awake.now(c.io).toNanoseconds(); + const dir = Io.Dir.cwd(); + { + const file = try dir.createFile(c.io, tmp_path, .{ .truncate = true }); + defer file.close(c.io); + try writeOut(c.gpa, c.io, file, b); + } + try dir.rename(tmp_path, dir, final_path, c.io); + c.stats.written += 1; + c.stats.write_ns += @intCast(Io.Clock.awake.now(c.io).toNanoseconds() - t0); + } +}; + +/// The pack's shards as they are on disk: each file's name, size and modification time. +pub fn packDigest(io: Io, p: *const pack_mod.Pack) ![32]u8 { + var h = std.crypto.hash.sha2.Sha256.init(.{}); + var buf: [std.fs.max_path_bytes]u8 = undefined; + for (p.files.items) |f| { + h.update(f); + const st = Io.Dir.cwd().statFile(io, try std.fmt.bufPrint(&buf, "{s}/{s}", .{ p.dir, f }), .{}) catch |e| switch (e) { + error.FileNotFound => { + h.update("missing"); + continue; + }, + else => return e, + }; + h.update(std.mem.asBytes(&[_]i128{ @intCast(st.size), st.mtime.toNanoseconds() })); + } + var d: [32]u8 = undefined; + h.final(&d); + return d; +} + +const Entry = extern struct { + kind: u8, + rank: u8, + name_len: u16, + pad: u32 = 0, + shape: [4]u64, + offset: u64, + len: u64, + sha256: [32]u8, +}; + +fn writeOut(gpa: Allocator, io: Io, file: Io.File, b: *const named.Builder) !void { + var head: std.ArrayList(u8) = .empty; + defer head.deinit(gpa); + try head.appendSlice(gpa, magic); + try head.appendSlice(gpa, std.mem.asBytes(&version)); + try head.appendSlice(gpa, std.mem.asBytes(&@as(u32, @intCast(b.out.items.len)))); + var size: u64 = 16; + for (b.out.items) |n| size += @sizeOf(Entry) + n.name.len; + var at = std.mem.alignForward(u64, size, page); + for (b.out.items) |n| { + var e: Entry = .{ .kind = @backingInt(n.kind), .rank = n.rank, .name_len = @intCast(n.name.len), .shape = undefined, .offset = at, .len = n.bytes.len, .sha256 = undefined }; + for (&e.shape, n.shape) |*d, s| d.* = s; + std.crypto.hash.sha2.Sha256.hash(n.bytes, &e.sha256, .{}); + try head.appendSlice(gpa, std.mem.asBytes(&e)); + try head.appendSlice(gpa, n.name); + at = std.mem.alignForward(u64, at + n.bytes.len, page); + } + try file.writePositionalAll(io, head.items, 0); + var cursor = std.mem.alignForward(u64, size, page); + for (b.out.items) |n| { + try file.writePositionalAll(io, n.bytes, cursor); + cursor = std.mem.alignForward(u64, cursor + n.bytes.len, page); + } + try file.setLength(io, cursor); +} + +/// Reads every tensor of a file into `b` (each into its own buffer); returns payload bytes. On error `b` gets nothing. +fn readInto(gpa: Allocator, io: Io, file: Io.File, b: *named.Builder, verify: bool) !u64 { + var fixed: [16]u8 = undefined; + if (try file.readPositionalAll(io, &fixed, 0) != 16) return error.Truncated; + if (!std.mem.eql(u8, fixed[0..8], magic)) return error.BadMagic; + if (std.mem.readInt(u32, fixed[8..12], .little) != version) return error.BadVersion; + const count = std.mem.readInt(u32, fixed[12..16], .little); + const total = try file.length(io); + var got: std.ArrayList(named.Named) = .empty; + errdefer { + for (got.items) |n| { + gpa.free(n.name); + gpa.free(n.bytes); + } + got.deinit(gpa); + } + var off: u64 = 16; + var bytes: u64 = 0; + for (0..count) |_| { + var e: Entry = undefined; + if (try file.readPositionalAll(io, std.mem.asBytes(&e), off) != @sizeOf(Entry)) return error.Truncated; + off += @sizeOf(Entry); + if (e.offset + e.len > total or e.rank > 4 or e.kind > @backingInt(named.Kind.i16)) return error.BadEntry; + const name = try gpa.alloc(u8, e.name_len); + errdefer gpa.free(name); + if (try file.readPositionalAll(io, name, off) != name.len) return error.Truncated; + off += e.name_len; + const data = try gpa.alignedAlloc(u8, .@"16", e.len); + errdefer gpa.free(data); + if (try file.readPositionalAll(io, data, e.offset) != data.len) return error.Truncated; + if (verify) { + var sha: [32]u8 = undefined; + std.crypto.hash.sha2.Sha256.hash(data, &sha, .{}); + if (!std.mem.eql(u8, &sha, &e.sha256)) return error.DigestMismatch; + } + var n: named.Named = .{ .name = name, .kind = @fromBackingInt(@intCast(e.kind)), .shape = undefined, .rank = e.rank, .bytes = data }; + for (&n.shape, e.shape) |*d, s| d.* = @intCast(s); + try got.append(gpa, n); + bytes += e.len; + } + try b.out.appendSlice(b.gpa, got.items); + got.deinit(gpa); + return bytes; +} + +test "a layer's images round-trip through its prepared file, and another key misses" { + const gpa = std.testing.allocator; + const io = std.testing.io; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + const root = try std.fmt.allocPrint(gpa, ".zig-cache/tmp/{s}", .{&tmp.sub_path}); + defer gpa.free(root); + var p = pack_mod.Pack.init(gpa); + defer p.deinit(); + const key: Key = .{ .rank = 1, .world = 2, .o_groups = 8, .blocks = &.{ 0, 1, 2 }, .dspark = true }; + var c = try Cache.open(gpa, io, root, try packDigest(io, &p), key, true, true); + defer c.deinit(); + var b: named.Builder = .{ .gpa = gpa, .io = io, .pack = &p }; + defer b.deinit(); + const sizes = [_]usize{ 4096, 5000, 16, 1 }; + for (sizes, 0..) |n, i| { + const bytes = try gpa.alignedAlloc(u8, .@"16", n); + for (bytes, 0..) |*x, j| x.* = @truncate(j *% 31 +% i); + try b.out.append(gpa, .{ .name = try std.fmt.allocPrint(gpa, "L1.t{d}.trellis", .{i}), .kind = @fromBackingInt(@intCast(i % 4)), .shape = .{ n, 1, 1, 1 }, .rank = @intCast(1 + i % 4), .bytes = bytes }); + } + var miss: named.Builder = .{ .gpa = gpa, .io = io, .pack = &p }; + defer miss.deinit(); + try std.testing.expect(!try c.get(&miss, "L1")); + try c.put(&b, "L1"); + var back: named.Builder = .{ .gpa = gpa, .io = io, .pack = &p }; + defer back.deinit(); + try std.testing.expect(try c.get(&back, "L1")); + try std.testing.expectEqual(b.out.items.len, back.out.items.len); + for (b.out.items, back.out.items) |x, y| { + try std.testing.expectEqualStrings(x.name, y.name); + try std.testing.expectEqual(x.kind, y.kind); + try std.testing.expectEqual(x.rank, y.rank); + try std.testing.expectEqualSlices(usize, &x.shape, &y.shape); + try std.testing.expectEqualSlices(u8, x.bytes, y.bytes); + try std.testing.expect(std.mem.isAligned(@intFromPtr(y.bytes.ptr), 16)); + } + try std.testing.expectEqual(@as(u32, 1), c.stats.hits); + // another rank (or blocks, pack, builder source) is another folder + var other = try Cache.open(gpa, io, root, try packDigest(io, &p), .{ .rank = 0, .world = 2, .o_groups = 8, .blocks = &.{ 0, 1, 2 }, .dspark = true }, false, false); + defer other.deinit(); + var none: named.Builder = .{ .gpa = gpa, .io = io, .pack = &p }; + defer none.deinit(); + try std.testing.expect(!try other.get(&none, "L1")); + // a damaged payload is refused with VERIFY and the builder stays empty + var pbuf: [std.fs.max_path_bytes]u8 = undefined; + const f = try Io.Dir.cwd().openFile(io, try c.path(&pbuf, "L1", ""), .{ .mode = .read_write }); + try f.writePositionalAll(io, "x", page); + f.close(io); + var bad: named.Builder = .{ .gpa = gpa, .io = io, .pack = &p }; + defer bad.deinit(); + try std.testing.expect(!try c.get(&bad, "L1")); + try std.testing.expectEqual(@as(usize, 0), bad.out.items.len); +} diff --git a/zig/src/families/deepseek_v41/rowmode.zig b/zig/src/families/deepseek_v41/rowmode.zig new file mode 100644 index 0000000000..e951c69f67 --- /dev/null +++ b/zig/src/families/deepseek_v41/rowmode.zig @@ -0,0 +1,229 @@ +//! Row mode's program: a decode window's CSA2 steps rewritten to read positions and slots from the row table. + +const std = @import("std"); +const calls = @import("block_calls.zig"); +const rowtab = @import("rowtab.zig"); +const Arg = calls.Arg; +const Tensor = calls.Tensor; + +pub const Error = error{ UnknownRowsCall, NoPool, UnionInRows, BadCall }; + +pub const table_role = "s.rows.tab"; + +pub const Spec = struct { + /// stacked slots + slots: u32, + /// the row table's columns hold this many rows + rmax: u32, + /// pages a slot's table holds (the stacked tables' row stride) + pts: i64, +}; + +/// A row block of the program: rows [a, a + n). +const Block = struct { a: i64, n: i64 }; + +pub const Rewriter = struct { + a: std.mem.Allocator, + s: Spec, + /// rows of the program (the bucket) + R: i64, + /// backend._blocked's row blocks by their position role ("L.ix.pos", from the glue "block_pos") + blocks: std.StringHashMapUnmanaged(Block) = .empty, + + fn col(w: *const Rewriter, c: rowtab.Col) Arg { + return w.colOf(c, .{ .a = 0, .n = w.R }); + } + + /// Rows [b.a, b.a + b.n) of column `c`. + fn colOf(w: *const Rewriter, c: rowtab.Col, b: Block) Arg { + return .{ .t = .{ .role = .{ .buf = table_role }, .dt = .i64, .shape = w.dup(&.{b.n}), .stride = w.dup(&.{1}), .offset = @intCast(rowtab.colOffset(c, w.s.rmax) + 8 * @as(usize, @intCast(b.a))) } }; + } + + /// The rows a call's POS names: a row block's, else the program's. + fn rowsOf(w: *const Rewriter, args: []const calls.Named) Block { + const p = find(args, "POS") orelse return .{ .a = 0, .n = w.R }; + const role = switch (args[p].arg) { + .t => |t| switch (t.role) { + .buf => |b| b, + else => null, + }, + else => null, + }; + return if (role) |r| (w.blocks.get(r) orelse .{ .a = 0, .n = w.R }) else .{ .a = 0, .n = w.R }; + } + + fn dup(w: *const Rewriter, xs: []const i64) []const i64 { + return w.a.dupe(i64, xs) catch @panic("rowmode: out of memory"); + } + + /// A stacked role's tensor as the row program sees it: S x the rows (rings), [S, cols] (carries, page tables). + fn stacked(w: *const Rewriter, t: Tensor) !Tensor { + const b = switch (t.role) { + .buf => |b| b, + else => return t, + }; + const S: i64 = w.s.slots; + var x = t; + if (isRing(b) or isCarry(b)) { + if (t.offset != 0 or t.shape.len != 2) return error.BadCall; + if (isRing(b)) { + x.shape = w.dup(&.{ S * t.shape[0], t.shape[1] }); + x.stride = t.stride; + } else { + x.shape = w.dup(&.{ S, t.shape[1] }); + x.stride = w.dup(&.{ t.shape[1], 1 }); + } + return x; + } + if (isTable(b)) { + if (t.offset != 0 or t.shape.len != 1 or t.shape[0] != w.s.pts) return error.BadCall; + x.shape = w.dup(&.{ S, w.s.pts }); + x.stride = w.dup(&.{ w.s.pts, 1 }); + return x; + } + return t; + } + + fn arg(w: *const Rewriter, x: Arg) !Arg { + return switch (x) { + .t => |t| .{ .t = try w.stacked(t) }, + else => x, + }; + } + + /// One call in row mode (a fresh Call; the input is not changed). + pub fn call(w: *Rewriter, c: calls.Call) !calls.Call { + var out = c; + const args = try w.a.alloc(calls.Named, c.args.len); + for (c.args, args) |x, *y| y.* = .{ .name = x.name, .arg = try w.arg(x.arg) }; + out.args = args; + if (c.glue) { + if (eq(c.name, "glue.kx_union")) return error.UnionInRows; + if (eq(c.name, "glue.block_pos")) { + // [position role, start + a, a, rows]: the block's rows of the table from here on + if (args.len != 4 or args[0].arg != .t or args[0].arg.t.role != .buf) return error.BadCall; + const b: Block = .{ .a = args[2].arg.i, .n = args[3].arg.i }; + if (b.a < 0 or b.n < 1 or b.a + b.n > w.R) return error.BadCall; + try w.blocks.put(w.a, args[0].arg.t.role.buf, b); + } + if (eq(c.name, "glue.counts")) { + // [sel, counts, positions int64 [R], ratio]: each row's own position + if (args.len != 4) return error.BadCall; + args[2].arg = w.col(.pos); + } + return out; + } + if (c.triton) return w.triton(out); + if (eq(c.name, "tf_dsv41_kv_glue_v1.norm_store")) { + if (args.len != 15) return error.BadCall; + args[5].arg = w.col(.pos); + args[6].arg = w.col(.wslot); + args[14].arg = .{ .b = true }; + return out; + } + if (eq(c.name, "tf_dsv41_attn_cuda_v1.attn")) { + if (args.len < 23) return error.BadCall; + args[9].arg = w.col(.pos); + args[10].arg = w.col(.rslot); + const pt_on = isTensor(args[11].arg); + args[21].arg = .{ .i = if (pt_on) w.s.pts else 0 }; + return out; + } + if (eq(c.name, "tf_dsv41_attn_cuda_v1.topk") or eq(c.name, "tf_dsv41_topk_b_v1.topk")) { + if (args.len < 22) return error.BadCall; + args[17].arg = w.col(.pos); + return out; + } + return out; + } + + fn triton(w: *const Rewriter, c: calls.Call) !calls.Call { + const args = @constCast(c.args); + const writes = eq(c.name, "_kv_store") or eq(c.name, "_pool_norm") or eq(c.name, "_index_k"); + const reads = eq(c.name, "_scores") or eq(c.name, "_scores_b"); + if (eq(c.name, "_rope")) { + const p = find(args, "POS") orelse return error.BadCall; + args[p].arg = w.col(.pos); + return c; + } + const b = w.rowsOf(args); + if (eq(c.name, "_dtopk") or eq(c.name, "_dtopk_b") or eq(c.name, "_block_keys")) { + // dtopk select / blocks and candidate blocks with rows on: q = POS[r] (the scores are the row's already) + const p = find(args, "POS") orelse return error.BadCall; + const r = find(args, "ROWS") orelse return error.BadCall; + if (find(args, "SL") != null) return error.UnknownRowsCall; + args[p].arg = w.colOf(.pos, b); + args[r].arg = .{ .b = true }; + return c; + } + if (!writes and !reads) { + // a call with row knobs that is not one of the above: refused rather than run with one slot's view + for (args) |x| if (eq(x.name, "ROWS") or eq(x.name, "SL")) return error.UnknownRowsCall; + return c; + } + for (args) |*x| { + if (eq(x.name, "POS")) x.arg = w.colOf(.pos, b); + if (eq(x.name, "SL")) x.arg = w.colOf(if (writes) .wslot else .rslot, b); + if (eq(x.name, "ROWS")) x.arg = .{ .b = true }; + } + if (find(args, "PTS")) |i| { + const pt = find(args, "PT") orelse return error.BadCall; + args[i].arg = .{ .i = if (isTensor(args[pt].arg)) w.s.pts else 0 }; + } + if (eq(c.name, "_pool_norm")) { + const split = if (find(args, "SPLIT")) |i| (args[i].arg == .b and args[i].arg.b) else false; + const prev = find(args, "PREV") orelse return error.BadCall; + args[prev].arg = w.col(.prev); + const off = find(args, "OFF") orelse return error.BadCall; + args[off].arg = .{ .i = if (split) @as(i64, w.s.slots) else 0 }; + } + return c; + } +}; + +/// The row-mode program of `cs` (block.emit's window of R rows); `cs` is not changed. +pub fn transform(a: std.mem.Allocator, cs: []const calls.Call, R: u32, s: Spec) ![]const calls.Call { + if (s.pts <= 0) return error.NoPool; + var w: Rewriter = .{ .a = a, .s = s, .R = R }; + defer w.blocks.deinit(a); + const out = try a.alloc(calls.Call, cs.len); + for (cs, out) |c, *o| o.* = try w.call(c); + return out; +} + +fn eq(x: []const u8, y: []const u8) bool { + return std.mem.eql(u8, x, y); +} + +fn find(args: []const calls.Named, name: []const u8) ?usize { + for (args, 0..) |x, i| if (eq(x.name, name)) return i; + return null; +} + +fn isTensor(x: Arg) bool { + return switch (x) { + .t => |t| t.role != .empty, + else => false, + }; +} + +/// "s.L.swa.v" / ".s": a layer's SWA ring +pub fn isRing(b: []const u8) bool { + return std.mem.startsWith(u8, b, "s.L") and (std.mem.endsWith(u8, b, ".swa.v") or std.mem.endsWith(u8, b, ".swa.s")); +} + +/// "s.L.carry": a ratio-2 kv source's carry +pub fn isCarry(b: []const u8) bool { + return std.mem.startsWith(u8, b, "s.L") and std.mem.endsWith(u8, b, ".carry"); +} + +/// "s.kv.pt" / "s.kv.ct": a slot's page table / split table +pub fn isTable(b: []const u8) bool { + return eq(b, "s.kv.pt") or eq(b, "s.kv.ct"); +} + +test "rowmode: stacked roles by name" { + try std.testing.expect(isRing("s.L7.swa.v") and isRing("s.L7.swa.s") and !isRing("s.L7.comp.v")); + try std.testing.expect(isCarry("s.L2.carry") and !isCarry("w.L2.comp")); + try std.testing.expect(isTable("s.kv.pt") and isTable("s.kv.ct") and !isTable("s.kv.comp.L2")); +} diff --git a/zig/src/families/deepseek_v41/rowtab.zig b/zig/src/families/deepseek_v41/rowtab.zig new file mode 100644 index 0000000000..57d77ec0b8 --- /dev/null +++ b/zig/src/families/deepseek_v41/rowtab.zig @@ -0,0 +1,206 @@ +//! Row mode's per-row metadata: a mixed-slot window padded to a bucket, its per-row values as one int64 device table. + +const std = @import("std"); + +pub const buckets = [_]u32{ 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 24, 32, 48, 64 }; +/// rows a slot's window holds at most (graphs.MAX_ROWS) +pub const seg_max = 16; +pub const Col = enum(u32) { ids, pos, rslot, wslot, prev, src }; +pub const ncol = 6; + +comptime { + std.debug.assert(@backingInt(Col.src) + 1 == ncol); +} + +/// One slot's window of a mix: its slot, first position and rows, in plan order. +pub const Seg = struct { slot: u32, start: u64, rows: u32 }; + +/// The smallest bucket of `cap` or fewer rows holding `n` (null: more than the largest). +pub fn bucketRows(n: u32, cap: u32) ?u32 { + for (buckets) |b| if (b >= n and b <= cap) return b; + return null; +} + +/// Each window's rows [a, b) in the padded run, into `out` (len = mix.len). +pub fn bounds(mix: []const Seg, out: [][2]u32) void { + var a: u32 = 0; + for (mix, out) |m, *o| { + o.* = .{ a, a + m.rows }; + a += m.rows; + } +} + +pub fn totalRows(mix: []const Seg) u32 { + var n: u32 = 0; + for (mix) |m| n += m.rows; + return n; +} + +pub const Error = error{ NoWindow, SlotTwice, SegmentRows, TooManyRows, PastLimit, SlotRange, IdCount }; + +/// Checks a mix: windows of 1..seg_max rows, no slot twice, within `limit` and the slots. +pub fn check(mix: []const Seg, slots: u32, limit: u64) Error!void { + if (mix.len == 0) return error.NoWindow; + var seen: u64 = 0; + for (mix) |m| { + if (m.slot >= slots or m.slot >= 64) return error.SlotRange; + const bit = @as(u64, 1) << @intCast(m.slot); + if (seen & bit != 0) return error.SlotTwice; + seen |= bit; + if (m.rows < 1 or m.rows > seg_max) return error.SegmentRows; + if (m.start + m.rows > limit) return error.PastLimit; + } +} + +/// The last position any row of the mix holds (the context bucket's input). +pub fn lastPos(mix: []const Seg) u64 { + var last: u64 = 0; + for (mix) |m| last = @max(last, m.start + m.rows - 1); + return last; +} + +/// The table int64 [NCOL, rmax] of `mix` padded to `R` rows, then rslot as int32 [rmax]; `slots` carry rows first. +pub fn plan(mix: []const Seg, ids: []const u32, R: u32, rmax: u32, slots: u32, out: []i64, rslot32: []i32) Error!void { + const n = totalRows(mix); + if (ids.len != n) return error.IdCount; + if (n < 1 or n > R or R > rmax) return error.TooManyRows; + std.debug.assert(out.len >= ncol * rmax and rslot32.len >= rmax); + const col = struct { + fn of(o: []i64, rm: u32, c: Col) []i64 { + return o[@backingInt(c) * rm ..][0..rm]; + } + }.of; + const id_c = col(out, rmax, .ids); + const pos_c = col(out, rmax, .pos); + const rs_c = col(out, rmax, .rslot); + const ws_c = col(out, rmax, .wslot); + const prev_c = col(out, rmax, .prev); + const src_c = col(out, rmax, .src); + for (ids, 0..) |t, i| id_c[i] = t; + var a: u32 = 0; + for (mix) |m| { + if (m.slot >= slots) return error.SlotRange; + const s: i64 = m.slot; + for (a..a + m.rows, 0..) |r, k| { + pos_c[r] = @intCast(m.start + k); + rs_c[r] = s; + ws_c[r] = s; + // the slot's carry for the window's first row (position start - 1), else the row before + prev_c[r] = if (k == 0) s else @as(i64, slots) + @as(i64, @intCast(r)) - 1; + src_c[r] = @intCast(r); + } + a += m.rows; + } + // padding: the last real row, writing nothing + for (n..R) |r| { + id_c[r] = id_c[n - 1]; + pos_c[r] = pos_c[n - 1]; + rs_c[r] = rs_c[n - 1]; + ws_c[r] = -1; + prev_c[r] = prev_c[n - 1]; + src_c[r] = src_c[n - 1]; + } + for (rslot32[0..R], rs_c[0..R]) |*d, v| d.* = @intCast(v); +} + +/// Bytes of the device table: the int64 columns, then the int32 rslot copy. +pub fn tableBytes(rmax: u32) usize { + return 8 * ncol * @as(usize, rmax) + 4 * @as(usize, rmax); +} + +/// Byte offset of column `c` (int64 [rmax]) in the table. +pub fn colOffset(c: Col, rmax: u32) usize { + return 8 * @as(usize, @backingInt(c)) * rmax; +} + +/// Byte offset of the int32 rslot copy. +pub fn rslot32Offset(rmax: u32) usize { + return 8 * ncol * @as(usize, rmax); +} + +/// Splits a mix into forwards of at most `cap` rows, segments whole and in order; `out` gets [first, end); the count. +pub fn pack(mix: []const Seg, cap: u32, out: [][2]usize) Error!usize { + var k: usize = 0; + var i: usize = 0; + while (i < mix.len) { + var rows: u32 = 0; + var j = i; + while (j < mix.len and rows + mix[j].rows <= cap) : (j += 1) rows += mix[j].rows; + if (j == i) return error.SegmentRows; // one segment past the cap + if (k == out.len) return error.TooManyRows; + out[k] = .{ i, j }; + k += 1; + i = j; + } + return k; +} + +// -- host tests --------------------------------------------------------------------------------------------------- + +const testing = std.testing; + +test "rowtab: buckets and the largest bucket a cap allows" { + try testing.expectEqual(@as(?u32, 1), bucketRows(1, 64)); + try testing.expectEqual(@as(?u32, 8), bucketRows(8, 64)); + try testing.expectEqual(@as(?u32, 12), bucketRows(9, 64)); + try testing.expectEqual(@as(?u32, 16), bucketRows(13, 64)); + try testing.expectEqual(@as(?u32, 64), bucketRows(49, 64)); + try testing.expectEqual(@as(?u32, null), bucketRows(65, 64)); + try testing.expectEqual(@as(?u32, null), bucketRows(17, 16)); +} + +test "rowtab: the plan of a mix as Python's plan_rows (padding mirrors the last real row and writes nothing)" { + // reference: plan_rows([(2, 10, 3), (0, 40, 2)], ids, 8, carries=4) + const mix = [_]Seg{ .{ .slot = 2, .start = 10, .rows = 3 }, .{ .slot = 0, .start = 40, .rows = 2 } }; + const ids = [_]u32{ 7, 8, 9, 100, 101 }; + const rmax = 16; + var t: [ncol * rmax]i64 = undefined; + var r32: [rmax]i32 = undefined; + try plan(&mix, &ids, 8, rmax, 4, &t, &r32); + const C = struct { + fn c(x: []const i64, k: Col) []const i64 { + return x[@backingInt(k) * rmax ..][0..8]; + } + }.c; + try testing.expectEqualSlices(i64, &.{ 7, 8, 9, 100, 101, 101, 101, 101 }, C(&t, .ids)); + try testing.expectEqualSlices(i64, &.{ 10, 11, 12, 40, 41, 41, 41, 41 }, C(&t, .pos)); + try testing.expectEqualSlices(i64, &.{ 2, 2, 2, 0, 0, 0, 0, 0 }, C(&t, .rslot)); + try testing.expectEqualSlices(i64, &.{ 2, 2, 2, 0, 0, -1, -1, -1 }, C(&t, .wslot)); + // prev: the slot's carry for a window's first row, else carries + the row before (carries = 4) + try testing.expectEqualSlices(i64, &.{ 2, 4, 5, 0, 7, 7, 7, 7 }, C(&t, .prev)); + try testing.expectEqualSlices(i64, &.{ 0, 1, 2, 3, 4, 4, 4, 4 }, C(&t, .src)); + try testing.expectEqualSlices(i32, &.{ 2, 2, 2, 0, 0, 0, 0, 0 }, r32[0..8]); + var b: [2][2]u32 = undefined; + bounds(&mix, &b); + try testing.expectEqual([2]u32{ 0, 3 }, b[0]); + try testing.expectEqual([2]u32{ 3, 5 }, b[1]); + try testing.expectEqual(@as(u64, 41), lastPos(&mix)); + try testing.expectError(error.TooManyRows, plan(&mix, &ids, 4, rmax, 4, &t, &r32)); + try testing.expectError(error.IdCount, plan(&mix, ids[0..4], 8, rmax, 4, &t, &r32)); +} + +test "rowtab: checks (a slot twice, rows a window, the limit, the slots)" { + try check(&.{ .{ .slot = 0, .start = 0, .rows = 1 }, .{ .slot = 3, .start = 5, .rows = 16 } }, 4, 4096); + try testing.expectError(error.SlotTwice, check(&.{ .{ .slot = 1, .start = 0, .rows = 1 }, .{ .slot = 1, .start = 5, .rows = 1 } }, 4, 4096)); + try testing.expectError(error.SegmentRows, check(&.{.{ .slot = 0, .start = 0, .rows = 17 }}, 4, 4096)); + try testing.expectError(error.PastLimit, check(&.{.{ .slot = 0, .start = 4095, .rows = 2 }}, 4, 4096)); + try testing.expectError(error.SlotRange, check(&.{.{ .slot = 4, .start = 0, .rows = 1 }}, 4, 4096)); + try testing.expectError(error.NoWindow, check(&.{}, 4, 4096)); +} + +test "rowtab: a mix packed into forwards under the row cap, each segment whole" { + const mix = [_]Seg{ .{ .slot = 0, .start = 0, .rows = 9 }, .{ .slot = 1, .start = 0, .rows = 7 }, .{ .slot = 2, .start = 0, .rows = 4 }, .{ .slot = 3, .start = 0, .rows = 16 } }; + var out: [4][2]usize = undefined; + try testing.expectEqual(@as(usize, 3), try pack(&mix, 16, &out)); + try testing.expectEqual([2]usize{ 0, 2 }, out[0]); + try testing.expectEqual([2]usize{ 2, 3 }, out[1]); + try testing.expectEqual([2]usize{ 3, 4 }, out[2]); + try testing.expectEqual(@as(usize, 1), try pack(&mix, 64, &out)); + try testing.expectError(error.SegmentRows, pack(&mix, 8, &out)); +} + +test "rowtab: the device table's layout" { + try testing.expectEqual(@as(usize, 8 * 6 * 64 + 4 * 64), tableBytes(64)); + try testing.expectEqual(@as(usize, 512), colOffset(.pos, 64)); + try testing.expectEqual(@as(usize, 3072), rslot32Offset(64)); +} diff --git a/zig/src/families/deepseek_v41/run.zig b/zig/src/families/deepseek_v41/run.zig new file mode 100644 index 0000000000..6a5bf101f6 --- /dev/null +++ b/zig/src/families/deepseek_v41/run.zig @@ -0,0 +1,519 @@ +//! The call executor: block.zig's calls issued on our own buffers, the plan bound once (persistent roles + one arena). + +const std = @import("std"); +const cuda = @import("cuda"); +const dk = @import("dsv41_kernels"); +const calls = @import("block_calls.zig"); +const buffers = @import("buffers.zig"); +const load = @import("load.zig"); +const tc = @import("triton_call.zig"); +const rp = dk.replay; + +/// A call timer behind the runner (profile.zig's Profile.timer); its methods forward to the timer's own. +pub const Timer = struct { + ptr: *anyopaque, + vtable: *const VTable, + + pub const VTable = struct { + begin: *const fn (ptr: *anyopaque) void, + beginPrefill: *const fn (ptr: *anyopaque) void, + prefillRows: *const fn (ptr: *anyopaque, rows: u64, tail: bool) void, + before: *const fn (ptr: *anyopaque, s: cuda.Stream, c: *const calls.Call) anyerror!void, + after: *const fn (ptr: *anyopaque, s: cuda.Stream) anyerror!void, + end: *const fn (ptr: *anyopaque, s: cuda.Stream) anyerror!void, + close: *const fn (ptr: *anyopaque, io: std.Io) void, + }; + + pub fn begin(t: Timer) void { + t.vtable.begin(t.ptr); + } + pub fn beginPrefill(t: Timer) void { + t.vtable.beginPrefill(t.ptr); + } + pub fn prefillRows(t: Timer, rows: u64, tail: bool) void { + t.vtable.prefillRows(t.ptr, rows, tail); + } + pub fn before(t: Timer, s: cuda.Stream, c: *const calls.Call) !void { + return t.vtable.before(t.ptr, s, c); + } + pub fn after(t: Timer, s: cuda.Stream) !void { + return t.vtable.after(t.ptr, s); + } + pub fn end(t: Timer, s: cuda.Stream) !void { + return t.vtable.end(t.ptr, s); + } + pub fn close(t: Timer, io: std.Io) void { + t.vtable.close(t.ptr, io); + } +}; + +pub const Runner = struct { + gpa: std.mem.Allocator, + d: *const cuda.Driver, + stream: cuda.Stream, + kernels: *const dk.Kernels, + triton: *const cuda.aot.Set, + weights: *const load.Weights, + /// role -> device address + addrs: std.StringHashMapUnmanaged(u64) = .empty, + owned: std.ArrayList(cuda.DeviceBuffer) = .empty, + scratch: ?cuda.DeviceBuffer = null, + /// the forward's glue steps (forward.zig: positions, exchanges over the Collective, Engram rows, kit roundings) + glue: ?Glue = null, + /// TF_DSV41_PROFILE: the eager decode windows' calls timed (profile.zig's Profile, set at engine open) + prof: ?Timer = null, + /// TF_DSV41_L2PF (l2pf.zig): L2 prefetch launches on a side stream forked at their site; null: skipped + l2pf: ?*Side = null, + /// rows of the window being issued (0: unknown): a window wider than `Side.rows` skips every prefetch site + rows: u32 = 0, + /// TF_DSV41_BRANCHES (branches.zig): the side stream of calls tagged `side`; null: every call on main + br: ?*Side = null, + /// TF_DSV41_MHC_DEFER (branches.zig): the stream of mHC coefficient launches, joined by the next mHC call + dfr: ?*Side = null, + /// TF_DSV41_DRAFT_OVERLAP: the event a row window records after its last "w.taps" writer, for the next window only + taps_mark: ?TapsMark = null, + /// Runner.guard: the programs (programKey) whose every Triton call the AOT set resolved + verified: std.AutoHashMapUnmanaged(u64, void) = .empty, + /// Runner.guard: each Triton function's runtime parameters (built at the first window) + params: ?Params = null, + + pub const TapsMark = struct { ev: cuda.Event, armed: bool = false, recorded: u64 = 0 }; + + /// The L2 prefetcher on the device: the side stream, the fork / join events, whether a fork is open. + pub const Side = struct { + stream: cuda.Stream, + fork: cuda.Event, + join: cuda.Event, + /// TF_DSV41_L2PF_ROWS (16): wider windows skip every site + rows: u32 = 16, + open: bool = false, + launches: u64 = 0, + skipped_rows: u64 = 0, + /// TF_DSV41_PF_OVERLAP_SITE (branches only): `mark` m records marks[m - 1]; `wait` m makes main wait for it + marks: [max_marks]?cuda.Event = @splat(null), + }; + + pub const max_marks = 8; + + pub const Glue = struct { + ctx: *anyopaque, + run: *const fn (ctx: *anyopaque, r: *Runner, c: *const calls.Call) anyerror!void, + }; + + pub fn deinit(r: *Runner) void { + for (r.owned.items) |*b| b.free(); + r.owned.deinit(r.gpa); + if (r.scratch) |*s| s.free(); + var it = r.addrs.keyIterator(); + while (it.next()) |k| r.gpa.free(k.*); + r.addrs.deinit(r.gpa); + r.verified.deinit(r.gpa); + if (r.params) |*p| freeParams(r.gpa, p); + } + + /// A persistent role whose memory another owner holds (the KV pool's tensors and tables): bind leaves it. + pub fn external(r: *Runner, role: []const u8, addr: u64) !void { + // Triton specializes pointers on 16-byte alignment: a misaligned base fails here by name, not later + if (addr % 16 != 0) { + std.log.scoped(.dsv41).err("role {s} bound at 0x{x}: not 16-byte aligned", .{ role, addr }); + return error.MisalignedRole; + } + const g = try r.addrs.getOrPut(r.gpa, role); + if (!g.found_existing) g.key_ptr.* = try r.gpa.dupe(u8, role); + g.value_ptr.* = addr; + } + + /// An external role's address gone (an on-demand role released): a later use fails as error.Unbound. + pub fn forget(r: *Runner, role: []const u8) void { + if (r.addrs.fetchRemove(role)) |kv| r.gpa.free(kv.key); + } + + /// Allocates every role of `plan`: persistent roles one buffer each (but external ones), the rest one zeroed arena. + pub fn bind(r: *Runner, plan: *const buffers.Plan) !void { + var arena_bytes: u64 = 0; + for (plan.sizes.keys(), plan.sizes.values()) |k, v| { + if (buffers.scopeOf(k) == .persistent and r.addrs.contains(k)) continue; + if (buffers.scopeOf(k) == .persistent) { + const b = try cuda.DeviceBuffer.alloc(r.d, v); + try b.fill8(0, null); + try r.owned.append(r.gpa, b); + try r.addrs.put(r.gpa, try r.gpa.dupe(u8, k), b.ptr); + } else arena_bytes += std.mem.alignForward(u64, v, 256); + } + const arena = try cuda.DeviceBuffer.alloc(r.d, arena_bytes); + try arena.fill8(0, null); + try r.owned.append(r.gpa, arena); + var at: u64 = 0; + for (plan.sizes.keys(), plan.sizes.values()) |k, v| { + if (buffers.scopeOf(k) == .persistent) continue; + try r.addrs.put(r.gpa, try r.gpa.dupe(u8, k), arena.ptr + at); + at += std.mem.alignForward(u64, v, 256); + } + r.scratch = try cuda.DeviceBuffer.alloc(r.d, 64 << 20); + } + + /// A role's device address (state.zig and the glue write through it). + pub fn addressOf(r: *const Runner, role: []const u8) ?u64 { + return r.addrs.get(role); + } + + pub fn tensorAddr(r: *const Runner, t: calls.Tensor) !u64 { + var numel: i64 = 1; + for (t.shape) |n| numel *= n; + if (numel == 0) return 0; + const off: u64 = @intCast(t.offset); + return switch (t.role) { + .weight => |w| (r.weights.get(w) orelse return error.MissingWeight).ptr + off, + .buf => |b| (r.addrs.get(b) orelse return error.Unbound) + off, + .empty => error.EmptyRole, + }; + } + + /// The first Triton call of `cs` the AOT set has no variant for (16-aligned tensors), null when every one has. + pub fn missingTriton(r: *const Runner, cs: []const calls.Call) !?[]const u8 { + return missingIn(r.gpa, r.triton, cs); + } + + /// Before a window's first launch: each Triton call of `cs` has a variant, else MissingTritonVariant (AOT guard). + pub fn guard(r: *Runner, cs: []const calls.Call) !void { + if (!guardOn()) return; + if (r.params == null) r.params = try paramsOf(r.gpa, r.triton); + const k = programKey(cs, &r.params.?); + if (r.verified.contains(k)) return; + if (try missingIn(r.gpa, r.triton, cs)) |name| { + std.log.err("aot guard: no Triton variant of {s} in this window of {d} calls: nothing issued, its request fails", .{ name, cs.len }); + return error.MissingTritonVariant; + } + try r.verified.put(r.gpa, k, {}); + } + + fn addrOf(ctx: *anyopaque, t: calls.Tensor) anyerror!u64 { + const r: *const Runner = @ptrCast(@alignCast(ctx)); + return r.tensorAddr(t); + } + + fn extArg(r: *const Runner, a: std.mem.Allocator, x: calls.Arg) !rp.Arg { + return switch (x) { + .i => |v| .{ .int = v }, + .f => |v| .{ .float = v }, + .b => |v| .{ .boolean = v }, + .none => .none, + .list => |items| blk: { + const out = try a.alloc(rp.Arg, items.len); + for (items, out) |y, *z| z.* = try r.extArg(a, y); + break :blk .{ .list = out }; + }, + .t, .opaque_table => |t| .{ .tensor = .{ .ptr = try r.tensorAddr(t), .dtype = std.meta.stringToEnum(rp.DType, @tagName(t.dt)).?, .shape = t.shape, .stride = t.stride } }, + }; + } + + /// Issues one call on the runner's stream; L2 prefetchers go to the side stream (skipped without TF_DSV41_L2PF). + pub fn issue(r: *Runner, c: *const calls.Call) !void { + if (r.br) |b| { + if (c.join) try r.joinBranch(); + if (c.wait > 0) { + // the main stream after the side's work up to mark `wait` only (the fork stays open) + if (!b.open) return error.BranchNotForked; + try r.stream.wait(b.marks[c.wait - 1] orelse return error.NoBranchMark); + } + if (c.fork) { + try b.fork.record(r.stream); + try b.stream.wait(b.fork); + b.open = true; + b.launches += 1; + } + if (c.side) { + if (!b.open) return error.BranchNotForked; + // the call and anything it runs (a glue step's launches) on the side stream + const main = r.stream; + r.stream = b.stream; + defer r.stream = main; + try r.issueOn(b.stream, c, r.scratch); + if (c.mark > 0) try (b.marks[c.mark - 1] orelse return error.NoBranchMark).record(b.stream); + return; + } + } + if (r.dfr) |d| { + if (c.defer_join) try r.joinDeferred(); + if (c.defer_side) { + try d.fork.record(r.stream); + try d.stream.wait(d.fork); + d.open = true; + d.launches += 1; + const main = r.stream; + r.stream = d.stream; + defer r.stream = main; + return r.issueOn(d.stream, c, r.scratch); + } + } + if (std.mem.startsWith(u8, c.name, "tf_dsv41_l2p")) { + const p = r.l2pf orelse return; + if (r.rows > p.rows) { + p.skipped_rows += 1; + return; + } + try p.fork.record(r.stream); + try p.stream.wait(p.fork); + p.open = true; + p.launches += 1; + return r.issueOn(p.stream, c, r.scratch); + } + return r.issueOn(r.stream, c, r.scratch); + } + + /// The main stream waits for the deferred coefficients' stream (mhc.await_coefs). + fn joinDeferred(r: *Runner) !void { + const d = r.dfr orelse return; + if (!d.open) return; + d.open = false; + try d.join.record(d.stream); + try r.stream.wait(d.join); + } + + /// The main stream waits for the branches' side stream (its open fork, if any). + fn joinBranch(r: *Runner) !void { + const b = r.br orelse return; + if (!b.open) return; + b.open = false; + try b.join.record(b.stream); + try r.stream.wait(b.join); + } + + /// The main stream waits for the side stream's prefetches, so a captured graph holds no open fork. + pub fn join(r: *Runner) !void { + try r.joinBranch(); + try r.joinDeferred(); + const p = r.l2pf orelse return; + if (!p.open) return; + p.open = false; + try p.join.record(p.stream); + try r.stream.wait(p.join); + } + + fn dropForks(r: *Runner) void { + if (r.l2pf) |p| p.open = false; + if (r.br) |b| b.open = false; + if (r.dfr) |d| d.open = false; + } + + fn issueOn(r: *Runner, stream: cuda.Stream, c: *const calls.Call, scratch_buf: ?cuda.DeviceBuffer) !void { + if (c.glue) { + const g = r.glue orelse return error.NoGlue; + return g.run(g.ctx, r, c); + } + var arena = std.heap.ArenaAllocator.init(r.gpa); + defer arena.deinit(); + const a = arena.allocator(); + if (c.triton) { + var mine: std.ArrayList(cuda.aot.Spec) = .empty; + for (r.triton.variants) |v| if (std.mem.eql(u8, v.spec.@"fn", c.name)) try mine.append(a, v.spec); + const sp = try tc.split(a, c, mine.items, .{ .ctx = @constCast(r), .of = addrOf }); + const g = c.grid; + return r.triton.run(stream, c.name, .{ @intCast(g[0]), @intCast(g[1]), @intCast(g[2]) }, sp.args, sp.consts); + } + const dot = std.mem.lastIndexOfScalar(u8, c.name, '.') orelse return error.BadName; + const args = try a.alloc(rp.Arg, c.args.len); + for (c.args, args) |x, *y| y.* = try r.extArg(a, x.arg); + var scratch: rp.Scratch = .{ .ptr = scratch_buf.?.ptr, .len = scratch_buf.?.len }; + if (!try rp.call(r.kernels, stream, c.name[0..dot], c.name[dot + 1 ..], args, &.{}, &scratch)) return error.NoBinding; + } + + /// Issues a window's calls in order (TF_DSV41_SYNC_EACH=1: synced after every call, a failing call named). + pub fn window(r: *Runner, cs: []const calls.Call) !void { + try r.guard(cs); + if (syncEach()) return r.windowChecked(cs); + errdefer r.dropForks(); // a failed window (or capture) leaves no fork for the next one to join + const mark = r.markAt(cs); + for (cs, 0..) |*c, i| { + try r.issue(c); + if (i == mark) try r.recordMark(c); + } + try r.join(); + } + + /// Issues `cs` on `stream` instead of the runner's (a drafter program beside a row window); nothing joins. + pub fn windowOn(r: *Runner, stream: cuda.Stream, cs: []const calls.Call) !void { + try r.guard(cs); + const main = r.stream; + r.stream = stream; + defer r.stream = main; + for (cs) |*c| { + if (c.side or c.fork or c.join or c.defer_side or c.defer_join or c.glue or std.mem.startsWith(u8, c.name, "tf_dsv41_l2p")) return error.NotASideProgram; + try r.issueOn(stream, c, r.scratch); + } + } + + /// The index of the call after which an armed window records the taps mark; null: not armed, or no taps writer. + fn markAt(r: *Runner, cs: []const calls.Call) ?usize { + const m = r.taps_mark orelse return null; + if (!m.armed) return null; + return lastTaps(cs); + } + + /// The mark after call `c` on its stream: an external record node in a capture, a plain record when eager. + fn recordMark(r: *Runner, c: *const calls.Call) !void { + const m = &r.taps_mark.?; + const s = if (c.side and r.br != null) r.br.?.stream else if (c.defer_side and r.dfr != null) r.dfr.?.stream else r.stream; + if (try cuda.graph.captureStatus(s) != .none) try m.ev.recordExternal(s) else try m.ev.record(s); + m.recorded += 1; + } + + fn windowChecked(r: *Runner, cs: []const calls.Call) !void { + const mark = r.markAt(cs); + for (cs, 0..) |*c, i| { + r.issue(c) catch |e| { + std.log.err("sync-each: call {d}/{d} {s} failed to issue ({t})", .{ i, cs.len, c.name, e }); + return e; + }; + if (i == mark) try r.recordMark(c); + r.stream.synchronize() catch |e| { + std.log.err("sync-each: call {d}/{d} {s} faulted ({t})", .{ i, cs.len, c.name, e }); + return e; + }; + } + try r.join(); + } + + /// An eager decode window, each call timed when TF_DSV41_PROFILE is set (never inside a graph capture). + pub fn timedWindow(r: *Runner, cs: []const calls.Call) !void { + const p = r.prof orelse return r.window(cs); + try r.guard(cs); + if (syncEach()) return r.windowChecked(cs); + p.begin(); + errdefer r.dropForks(); + const mark = r.markAt(cs); + for (cs, 0..) |*c, i| { + try p.before(r.stream, c); + try r.issue(c); + if (i == mark) try r.recordMark(c); + try p.after(r.stream); + } + try r.join(); + try p.end(r.stream); + } +}; + +/// The last call of `cs` with a "w.taps" tensor argument; null when none does. +pub fn lastTaps(cs: []const calls.Call) ?usize { + var at: ?usize = null; + for (cs, 0..) |c, i| for (c.args) |x| switch (x.arg) { + .t => |t| if (t.role == .buf and std.mem.eql(u8, t.role.buf, "w.taps")) { + at = i; + }, + else => {}, + }; + return at; +} + +var sync_each: ?bool = null; +/// The first Triton call of `cs` that `set` has no variant for, every role's base 16-aligned (Runner.missingTriton). +pub fn missingIn(gpa: std.mem.Allocator, set: *const cuda.aot.Set, cs: []const calls.Call) !?[]const u8 { + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + const a = arena.allocator(); + for (cs) |*c| if (c.triton) { + var mine: std.ArrayList(cuda.aot.Spec) = .empty; + for (set.variants) |v| if (std.mem.eql(u8, v.spec.@"fn", c.name)) try mine.append(a, v.spec); + const sp = tc.split(a, c, mine.items, .{ .ctx = undefined, .of = alignedOf }) catch return c.name; + _ = set.find(c.name, sp.args, sp.consts) catch return c.name; // error.MissingTritonVariant + }; + return null; +} + +/// A tensor's address with its role's base 16-aligned (aot-needs' rule); an empty tensor or role is 0 (tensorAddr's). +fn alignedOf(_: *anyopaque, t: calls.Tensor) anyerror!u64 { + return if (emptyTensor(t)) 0 else (1 << 20) + @as(u64, @intCast(t.offset)); +} + +fn emptyTensor(t: calls.Tensor) bool { + var numel: i64 = 1; + for (t.shape) |n| numel *= n; + return numel == 0 or t.role == .empty; +} + +/// Each Triton function's runtime parameter names over its variants (tc.split: other ints are constexprs). +pub const Params = std.StringHashMapUnmanaged([]const []const u8); + +pub fn paramsOf(gpa: std.mem.Allocator, set: *const cuda.aot.Set) !Params { + var specs: std.ArrayList(cuda.aot.Spec) = .empty; + defer specs.deinit(gpa); + for (set.variants) |v| try specs.append(gpa, v.spec); + return paramsOfSpecs(gpa, specs.items); +} + +pub fn paramsOfSpecs(gpa: std.mem.Allocator, specs: []const cuda.aot.Spec) !Params { + var lists: std.StringArrayHashMapUnmanaged(std.ArrayList([]const u8)) = .empty; + defer { + for (lists.values()) |*l| l.deinit(gpa); + lists.deinit(gpa); + } + for (specs) |sp| { + const got = try lists.getOrPut(gpa, sp.@"fn"); + if (!got.found_existing) got.value_ptr.* = .empty; + for (sp.params) |q| if (!named(got.value_ptr.items, q.name)) try got.value_ptr.append(gpa, q.name); + } + var p: Params = .empty; + errdefer freeParams(gpa, &p); + for (lists.keys(), lists.values()) |k, *l| try p.put(gpa, k, try l.toOwnedSlice(gpa)); + return p; +} + +pub fn freeParams(gpa: std.mem.Allocator, p: *Params) void { + var it = p.valueIterator(); + while (it.next()) |v| gpa.free(v.*); + p.deinit(gpa); +} + +/// A program's Triton lookups hashed as `find` tells them apart (launch ints by class, tensors by dtype and alignment). +pub fn programKey(cs: []const calls.Call, params: *const Params) u64 { + var h = std.hash.Wyhash.init(0x5d41); + for (cs) |*c| if (c.triton) { + h.update(c.name); + const ps = params.get(c.name) orelse &.{}; + for (c.args) |x| { + h.update(x.name); + if (x.arg == .i and named(ps, x.name)) { + const v = x.arg.i; + h.update(&.{ @intFromBool(v == 1), @intFromBool(@mod(v, 16) == 0), @intFromBool(std.math.cast(i32, v) != null) }); + } else hashArg(&h, x.arg); + } + h.update("|"); + }; + return h.final(); +} + +fn named(ps: []const []const u8, name: []const u8) bool { + for (ps) |p| if (std.mem.eql(u8, p, name)) return true; + return false; +} + +fn hashArg(h: *std.hash.Wyhash, x: calls.Arg) void { + h.update(&.{@backingInt(std.meta.activeTag(x))}); + switch (x) { + .t, .opaque_table => |t| { + h.update(@tagName(t.dt)); + h.update(&.{ @intFromBool(emptyTensor(t)), @intFromBool(@mod(t.offset, 16) == 0) }); + }, + .i => |v| h.update(std.mem.asBytes(&v)), + .f => |v| h.update(std.mem.asBytes(&v)), + .b => |v| h.update(&.{@intFromBool(v)}), + .none => {}, + .list => |items| for (items) |y| hashArg(h, y), + } +} + +var guard_on: ?bool = null; + +fn guardOn() bool { + if (guard_on) |v| return v; + const v = if (std.c.getenv("TF_DSV41_AOT_GUARD")) |x| !std.mem.eql(u8, std.mem.span(x), "0") else true; + guard_on = v; + return v; +} + +fn syncEach() bool { + if (sync_each) |v| return v; + const v = if (std.c.getenv("TF_DSV41_SYNC_EACH")) |x| std.mem.eql(u8, std.mem.span(x), "1") else false; + sync_each = v; + return v; +} diff --git a/zig/src/families/deepseek_v41/sampling_check.zig b/zig/src/families/deepseek_v41/sampling_check.zig new file mode 100644 index 0000000000..e69289e33a --- /dev/null +++ b/zig/src/families/deepseek_v41/sampling_check.zig @@ -0,0 +1,164 @@ +//! tf-dsv41-samp: the keyed sampler's device steps on one GPU against the host reference; PASS / FAIL, exit 0 on PASS. + +const std = @import("std"); +const cuda = @import("cuda"); +const dk = @import("dsv41_kernels"); +const smp = @import("dsv41_serve").sampling; +const pipe = @import("sampling_pipe.zig"); +const Gpu = @import("sampling_dev.zig").Gpu; + +const C: usize = 64640; +const n: usize = 16; + +fn rows(rnd: std.Random, out: []f32) void { + for (0..n) |r| { + const row = out[r * C ..][0..C]; + const kind = r % 4; + for (row, 0..) |*v, c| { + const x: f64 = switch (kind) { + 0 => rnd.floatNorm(f64) * 2.0, + 1 => rnd.floatNorm(f64) * 3.0 + (if (c == r * 97) @as(f64, 14) else 0), + 2 => @floor(rnd.floatNorm(f64) * 4.0) / 2.0, // coarse: ties everywhere + else => if (c % 3 == 0) 1.5 else -2.0, // flat bands + }; + const b: u32 = @bitCast(@as(f32, @floatCast(x))); + v.* = @bitCast(b & 0xFFFF_0000); // bf16-valued, as kit_logits leaves them + } + } +} + +pub fn main(init: std.process.Init) !u8 { + const gpa = init.gpa; + var driver = try cuda.Driver.open(); + defer driver.close(); + var ctx = try cuda.Context.init(&driver, 0); + defer ctx.deinit(); + var stream = try cuda.Stream.init(&driver, true); + defer stream.deinit(); + var kernels = try dk.Kernels.load(&ctx); + defer kernels.deinit(); + var gpu = try Gpu.init(&driver, stream, &kernels); + defer gpu.deinit(); + var host: pipe.HostDevice = .{ .gpa = gpa }; + defer host.deinit(); + const dev = gpu.device(); + const ref = host.device(); + + var prng = std.Random.DefaultPrng.init(4101); + const lg = try gpa.alloc(f32, n * C); + defer gpa.free(lg); + rows(prng.random(), lg); + var d_lg = try cuda.DeviceBuffer.alloc(&driver, 4 * lg.len); + defer d_lg.free(); + try d_lg.upload(0, std.mem.sliceAsBytes(lg)); + const h_lg: u64 = @intFromPtr(lg.ptr); + var bad: usize = 0; + + // candidates: device packed rows == host packed rows, bit for bit + for ([_]usize{ 9, 28, 520, 1024 }) |k| { + const len = n * 2 * k; + const d_out = try dev.vtable.scratch(dev.ptr, 0, 4 * len); + const h_out = try ref.vtable.scratch(ref.ptr, 0, 4 * len); + try dev.vtable.top(dev.ptr, d_lg.ptr, n, C, k, 64640, d_out); + try ref.vtable.top(ref.ptr, h_lg, n, C, k, 64640, h_out); + const got = try gpa.alloc(u32, len); + defer gpa.free(got); + try dev.vtable.download(dev.ptr, d_out, std.mem.sliceAsBytes(got)); + const want = @as([*]const u32, @ptrFromInt(h_out))[0..len]; + const diff: ?usize = for (got, want, 0..) |g, w, i| { + if (g != w) break i; + } else null; + bad += @intFromBool(diff != null); + std.debug.print("candidates k {d}: {s}\n", .{ k, if (diff != null) "differ" else "bit-equal" }); + if (diff) |i| std.debug.print(" first at {d}: {x} vs {x}\n", .{ i, got[i], want[i] }); + } + + // statistics: max bit-equal, sum within 1e-12 relative + var picker = try smp.Picker.init(gpa, C); + defer picker.deinit(); + const split = gpu.fns.split != null; + if (!split) { + bad += 1; + std.debug.print("stats: the fatbin has no split kernels (dsv41_sampling_stats_*)\n", .{}); + } + for ([_]f64{ 0.6, 1.0, 1.3 }) |t| { + const d_st = try dev.vtable.scratch(dev.ptr, 2, 16 * n); + const h_st = try ref.vtable.scratch(ref.ptr, 2, 16 * n); + try ref.vtable.stats(ref.ptr, h_lg, n, C, t, h_st); + const want = @as([*]const f64, @ptrFromInt(h_st))[0 .. 2 * n]; + var got: [2 * n]f64 = undefined; + for ([_]bool{ false, true }) |mode| { + if (mode and !split) continue; + gpu.split = mode; // the split run last: the picks below read its statistics + try dev.vtable.stats(dev.ptr, d_lg.ptr, n, C, t, d_st); + try dev.vtable.download(dev.ptr, d_st, std.mem.sliceAsBytes(&got)); + var worst: f64 = 0; + var max_ok = true; + for (0..n) |r| { + max_ok = max_ok and got[2 * r] == want[2 * r]; + worst = @max(worst, @abs(got[2 * r + 1] - want[2 * r + 1]) / want[2 * r + 1]); + } + const ok = max_ok and worst <= 1e-12; + bad += @intFromBool(!ok); + std.debug.print("stats {s} T {d}: max {s}, sum worst relative {e} ({s})\n", .{ if (mode) "split" else "row", t, if (max_ok) "bit-equal" else "DIFFERS", worst, if (ok) "ok" else "FAIL" }); + } + + // the picks from the device's candidates + statistics == the host's (512 candidates, one rank) + const k = 512; + const d_c = try dev.vtable.scratch(dev.ptr, 0, 4 * n * 2 * k); + const h_c = try ref.vtable.scratch(ref.ptr, 0, 4 * n * 2 * k); + try dev.vtable.top(dev.ptr, d_lg.ptr, n, C, k, 0, d_c); + try ref.vtable.top(ref.ptr, h_lg, n, C, k, 0, h_c); + const dc = try gpa.alloc(f32, n * 2 * k); + defer gpa.free(dc); + try dev.vtable.download(dev.ptr, d_c, std.mem.sliceAsBytes(dc)); + const hc = @as([*]const f32, @ptrFromInt(h_c))[0 .. n * 2 * k]; + var vals: [2][k]f32 = undefined; + var ids: [2][k]u32 = undefined; + var picks: usize = 0; + var same: usize = 0; + for ([_]smp.Sampling{ + .{ .seed = 11, .temperature = t, .top_k = 0, .top_p = 0.9 }, + .{ .seed = 12, .temperature = t, .top_k = 0, .top_p = 0.5, .min_p = 0.05 }, + .{ .seed = 13, .temperature = t, .top_k = 40, .top_p = 0.95 }, + }) |s| for (0..n) |r| for (0..8) |p| { + const pos: u64 = 1000 + 37 * p; + var out: [2]smp.Pick = undefined; + for ([_][]const f32{ dc, hc }, [_][]const f64{ &got, want }, 0..) |cand_data, st, side| { + const cand: smp.Candidates = .{ .data = cand_data, .world = 1, .rows = n, .k = k }; + const m = cand.row(@intCast(r), &vals[side], &ids[side]); + const stats = [1][2]f64{.{ st[2 * r], st[2 * r + 1] }}; + const nuc = smp.isNucleus(s); + out[side] = picker.pick(.{ .values = vals[side][0..m], .ids = ids[side][0..m], .stats = if (nuc) &stats else &.{}, .count = smp.candidateCount(s, @intCast(C), smp.nucleus_count) }, pos, s); + } + picks += 1; + same += @intFromBool(std.meta.eql(out[0], out[1])); + }; + bad += picks - same; + std.debug.print("picks T {d}: {d}/{d} equal\n", .{ t, same, picks }); + } + try timeStats(&driver, stream, &gpu, dev, d_lg.ptr); + std.debug.print("{s} tf-dsv41-samp: {d} checks failed\n", .{ if (bad == 0) "PASS" else "FAIL", bad }); + return if (bad == 0) 0 else 1; +} + +/// Each statistics kernel's device time a call (timing events around `reps` calls) at a segment's row counts. +fn timeStats(driver: *const cuda.Driver, stream: cuda.Stream, gpu: *Gpu, dev: pipe.Device, lg: u64) !void { + const reps = 50; + var e0 = try cuda.Event.init(driver, true); + defer e0.deinit(); + var e1 = try cuda.Event.init(driver, true); + defer e1.deinit(); + const out = try dev.vtable.scratch(dev.ptr, 2, 16 * n); + for ([_]usize{ 1, 5, 16 }) |rows_| for ([_]bool{ false, true }) |mode| { + if (mode and gpu.fns.split == null) continue; + gpu.split = mode; + try dev.vtable.stats(dev.ptr, lg, rows_, C, 0.7, out); // warm (scratch, module) + try e0.record(stream); + for (0..reps) |_| try dev.vtable.stats(dev.ptr, lg, rows_, C, 0.7, out); + try e1.record(stream); + try e1.synchronize(); + const ms = try cuda.Event.elapsedMs(e0, e1); + std.debug.print("stats time {s} rows {d}: {d:.1} us a call\n", .{ if (mode) "split" else "row", rows_, 1000.0 * ms / reps }); + }; +} diff --git a/zig/src/families/deepseek_v41/sampling_dev.zig b/zig/src/families/deepseek_v41/sampling_dev.zig new file mode 100644 index 0000000000..a1b0fa498b --- /dev/null +++ b/zig/src/families/deepseek_v41/sampling_dev.zig @@ -0,0 +1,87 @@ +//! The keyed sampler's device steps on one stream: topk_keys.cu candidates, sampling.cu packed rows and statistics. + +const std = @import("std"); +const quiet = @import("builtin").is_test; // a test's stderr is the build runner's channel +const cuda = @import("cuda"); +const dk = @import("dsv41_kernels"); +const pipe = @import("sampling_pipe.zig"); + +/// The device steps on one stream: topk_keys.cu (`dsv41_kernels`) and sampling.cu, scratch kept between calls. +pub const Gpu = struct { + d: *const cuda.Driver, + stream: cuda.Stream, + kernels: *const dk.Kernels, + fns: dk.sampling.Functions, + bufs: [8]?cuda.DeviceBuffer = @splat(null), + /// nucleus statistics over every SM when the fatbin has them, unless TF_DSV41_SAMP_STATS=row (one CTA a row) + split: bool, + + pub fn init(d: *const cuda.Driver, stream: cuda.Stream, kernels: *const dk.Kernels) !Gpu { + const fns = try dk.sampling.Functions.load(d); + const row = try rowStats(); + const log = std.log.scoped(.dsv41); + if (fns.split_missing) |m| { + if (!quiet) log.warn("sampling: nucleus statistics one CTA a row: the sampling fatbin's {s} did not resolve ({t}); rebuild dsv41_sampling.fatbin from this tree's sampling.cu", .{ m.symbol, m.err }); + } else if (row) { + log.info("sampling: nucleus statistics one CTA a row (TF_DSV41_SAMP_STATS=row)", .{}); + } else log.info("sampling: nucleus statistics split over every SM ({d} columns a CTA)", .{dk.sampling.Functions.chunk}); + return .{ .d = d, .stream = stream, .kernels = kernels, .fns = fns, .split = fns.split != null and !row }; + } + + /// TF_DSV41_SAMP_STATS: split (default) | row. + pub fn rowStats() !bool { + const v = std.mem.span(std.c.getenv("TF_DSV41_SAMP_STATS") orelse return false); + if (v.len == 0 or std.mem.eql(u8, v, "split")) return false; + if (std.mem.eql(u8, v, "row")) return true; + return error.BadSampStats; + } + + pub fn deinit(g: *Gpu) void { + for (&g.bufs) |*b| if (b.*) |*x| x.free(); + g.fns.unload(); + } + + pub fn device(g: *Gpu) pipe.Device { + return .{ .ptr = g, .vtable = &.{ .top = top, .stats = stats, .scratch = scratch, .download = download, .stream = streamOf } }; + } + + fn self_(p: *anyopaque) *Gpu { + return @ptrCast(@alignCast(p)); + } + + fn top(p: *anyopaque, lg: u64, n: usize, C: usize, k: usize, id0: u32, out: u64) anyerror!void { + const g = self_(p); + const vals = try scratch(p, 5, 4 * n * k); + const cols = try scratch(p, 6, 8 * n * k); + try g.kernels.others(g.stream).topKeys(lg, C, n, C, k, vals, cols); + try g.fns.pack(g.stream, vals, cols, n, k, id0, out); + } + + fn stats(p: *anyopaque, lg: u64, n: usize, C: usize, t: f64, out: u64) anyerror!void { + const g = self_(p); + if (!g.split) return g.fns.stats(g.stream, lg, C, n, C, t, out); + const work = try scratch(p, 7, dk.sampling.Functions.splitScratch(n, C)); + try g.fns.statsSplit(g.stream, lg, C, n, C, t, work, out); + } + + fn scratch(p: *anyopaque, slot: u32, bytes: usize) anyerror!u64 { + const g = self_(p); + const b = &g.bufs[slot]; + if (b.* == null or b.*.?.len < bytes) { + if (b.*) |*x| x.free(); + b.* = null; + b.* = try cuda.DeviceBuffer.alloc(g.d, bytes); + } + return b.*.?.ptr; + } + + fn download(p: *anyopaque, src: u64, dst: []u8) anyerror!void { + const g = self_(p); + try g.stream.synchronize(); + try cuda.DeviceBuffer.download(.{ .d = g.d, .ptr = src, .len = dst.len }, 0, dst); + } + + fn streamOf(p: *anyopaque) cuda.abi.Stream { + return self_(p).stream.handle; + } +}; diff --git a/zig/src/families/deepseek_v41/sampling_gpu.zig b/zig/src/families/deepseek_v41/sampling_gpu.zig new file mode 100644 index 0000000000..494ca003ef --- /dev/null +++ b/zig/src/families/deepseek_v41/sampling_gpu.zig @@ -0,0 +1,268 @@ +//! The keyed sampler on the GPU forward: rank 0 sends each call's sampling on the plan link; followers run the steps. + +const std = @import("std"); +const cuda = @import("cuda"); +const fwd = @import("forward.zig"); +const kops = @import("dsv41_kernels").ops; +const pipe = @import("sampling_pipe.zig"); +const Gpu = @import("sampling_dev.zig").Gpu; + +pub const Sampling = pipe.Sampling; +pub const Seg = pipe.Pipe.Seg; +pub const sampled = pipe.sampled; + +/// TF_DSV41_SAMPLING=1 (every rank alike): model.zig makes the sampler; unset, sampled requests are refused as before. +pub fn enabled() bool { + const v = std.c.getenv("TF_DSV41_SAMPLING") orelse return false; + return std.mem.eql(u8, std.mem.span(v), "1"); +} + +/// One rank's sampler over its forward (model.zig makes one on every rank; it hooks the follower side itself). +pub const GpuSampler = struct { + gpa: std.mem.Allocator, + f: *fwd.Forward, + gpu: Gpu, + p: pipe.Pipe, + /// the call in flight (rows, sampling, first draw position) and the device choice's pinned copy, event and tokens + cur: ?Cur = null, + pinned: ?cuda.HostBuffer = null, + done: ?cuda.Event = null, + chosen: ?cuda.DeviceBuffer = null, + /// TF_DSV41_SAMP_BATCH=1 (rank 0 decides): a forward's keyed segments in one call, one gather a forward + batch: bool = false, + /// the batched call's per-segment device-choice start (null: none), for its hook + seg_pos0: []const ?u64 = &.{}, + /// keyed paths already logged (`note`) + seen: u8 = 0, + + const Cur = struct { row0: u32, s: Sampling, pos0: ?u64 }; + + pub fn init(gpa: std.mem.Allocator, f: *fwd.Forward) !*GpuSampler { + const x = try gpa.create(GpuSampler); + errdefer gpa.destroy(x); + const W = f.comm.world(); + x.* = .{ .gpa = gpa, .f = f, .gpu = try Gpu.init(f.runner.d, f.runner.stream, f.runner.kernels), .p = undefined }; + errdefer x.gpu.deinit(); + x.p = try pipe.Pipe.init(gpa, f.comm, f.cfg.vocab / W, try pipe.nucleusEnv()); + f.sampler = .{ .ptr = x, .follow = follow }; + x.p.after_gather = .{ .ctx = x, .run = afterGather }; + x.batch = try batchEnv(); + return x; + } + + /// TF_DSV41_SAMP_BATCH: 0 (default) | 1. + pub fn batchEnv() !bool { + const v = std.mem.span(std.c.getenv("TF_DSV41_SAMP_BATCH") orelse return false); + if (v.len == 0 or std.mem.eql(u8, v, "0")) return false; + if (std.mem.eql(u8, v, "1")) return true; + return error.BadSampBatch; + } + + /// Whether `chooseSegments` takes a segment under `s` (sampling_pipe.Pipe.batchable, with the knob on). + pub fn batchable(x: *const GpuSampler, s: Sampling) bool { + return x.batch and x.p.batchable(s); + } + + /// The most segments one op_sample_many carries. + pub const max_segments = 16; + const seg_words = 8; + + /// Rank 0: several keyed segments of "w.logits" in one call (op 13 first), each choosing from the one gather. + pub fn chooseSegments(x: *GpuSampler, segs: []const pipe.Pipe.Seg) !void { + if (segs.len == 0 or segs.len > max_segments) return error.BadSegments; + var msg: [2 + seg_words * max_segments]i64 = undefined; + var pos0: [max_segments]?u64 = @splat(null); + msg[0] = fwd.Forward.op_sample_many; + msg[1] = @intCast(segs.len); + for (segs, 0..) |g, i| { + if (x.f.after_sample != null) if (g.positions) |ps| if (consecutive(ps[0..g.n])) { + pos0[i] = ps[0]; + }; + const o = opOf(g.row0, g.n, g.s); + @memcpy(msg[2 + seg_words * i ..][0..6], o[1..7]); + msg[2 + seg_words * i + 6] = @bitCast(g.s.seed); + msg[2 + seg_words * i + 7] = if (pos0[i]) |p0| @intCast(p0) else -1; + } + try x.f.sendSample(msg[0 .. 2 + seg_words * segs.len]); + try x.runSegments(segs, pos0[0..segs.len]); + } + + fn runSegments(x: *GpuSampler, segs: []const pipe.Pipe.Seg, pos0: []const ?u64) !void { + const f = x.f; + const lg = f.runner.addressOf("w.logits") orelse return error.Unbound; + if (f.grammar) |g| for (segs) |sg| try g.apply(g.ptr, sg.row0, sg.n); + x.cur = null; // the single call's hook stays off: the segments' own below + x.seg_pos0 = pos0; + defer x.seg_pos0 = &.{}; + for (segs) |sg| x.note(sg.s, true); + try x.p.chooseSegments(x.gpu.device(), lg, segs, .{ .ctx = x, .run = segGather }); + if (x.p.whole_rows > 0) std.log.scoped(.dsv41).debug("sampling: {d} rows taken whole ({d} segments)", .{ x.p.whole_rows, segs.len }); + } + + /// A follower: op_sample_many's segments (tokens dropped; device choices as the leader's). + fn followSegments(x: *GpuSampler, msg: []const i64) !void { + if (msg.len < 2) return error.BadPlan; + const ns: usize = @intCast(msg[1]); + if (ns == 0 or ns > max_segments or msg.len != 2 + seg_words * ns) return error.BadPlan; + var segs: [max_segments]pipe.Pipe.Seg = undefined; + var pos0: [max_segments]?u64 = undefined; + var outs: [64]u32 = undefined; + var used: usize = 0; + for (0..ns) |i| { + const w = msg[2 + seg_words * i ..][0..seg_words]; + const n: u32 = @intCast(w[1]); + if (n == 0 or used + n > outs.len) return error.BadPlan; + segs[i] = .{ .row0 = @intCast(w[0]), .n = n, .s = .{ .seed = @bitCast(w[6]), .temperature = @bitCast(w[2]), .top_k = @intCast(w[3]), .top_p = @bitCast(w[4]), .min_p = @bitCast(w[5]) }, .out = outs[used..][0..n] }; + pos0[i] = if (w[7] >= 0) @intCast(w[7]) else null; + used += n; + } + try x.runSegments(segs[0..ns], pos0[0..ns]); + } + + /// chooseSegments' hook (every rank): device choices from the shared gather, then wait for the copy; false: none. + fn segGather(ctx: *anyopaque, gathered: u64, n: usize, k: usize, segs: []const pipe.Pipe.Seg, host: []u8) anyerror!bool { + const x: *GpuSampler = @ptrCast(@alignCast(ctx)); + const h = x.f.after_sample orelse return false; + const r = x.f.runner; + const ops = r.kernels.others(r.stream); + const W: usize = x.f.comm.world(); + if (ops.f.vs_choose == null or W * k > kops.glue.vs_max) return false; + const r0 = segs[0].row0; + var any = false; + for (segs, x.seg_pos0) |g, p0| any = any or (p0 != null and !isNucleusRow(g.s)); + if (!any) return false; + try x.ensureDevice(host.len, n); + try r.d.check(r.d.api.cuMemcpyDtoHAsync_v2(x.pinned.?.bytes.ptr, gathered, host.len, r.stream.handle), "cuMemcpyDtoHAsync"); + try x.done.?.record(r.stream); + const vocab: u32 = x.f.cfg.vocab; + for (segs, x.seg_pos0) |g, p0| { + const p = p0 orelse continue; + const a = g.row0 - r0; + if (isNucleusRow(g.s)) continue; + const count = pipe.countOf(g.s, vocab, x.p.nucleus); + try ops.vsChoose(gathered, W, n, k, count, g.s.seed, p -% a, g.s.top_k, g.s.temperature, g.s.top_p, g.s.min_p, x.chosen.?.ptr); + try h.run(h.ctx, g.row0, g.n, x.chosen.?.ptr + 8 * @as(u64, a)); + } + try x.done.?.synchronize(); + @memcpy(host, x.pinned.?.bytes[0..host.len]); + return true; + } + + /// The pinned copy, its event and the device choices, grown to `bytes` / `n` rows. + fn ensureDevice(x: *GpuSampler, bytes: usize, n: usize) !void { + const r = x.f.runner; + if (x.pinned == null or x.pinned.?.bytes.len < bytes) { + if (x.pinned) |*b| b.free(); + x.pinned = null; + x.pinned = try cuda.HostBuffer.alloc(r.d, @max(bytes, 1 << 16)); + } + if (x.chosen == null or x.chosen.?.len < 8 * n) { + if (x.chosen) |*b| b.free(); + x.chosen = null; + x.chosen = try cuda.DeviceBuffer.alloc(r.d, @max(8 * n, 1024)); + } + if (x.done == null) x.done = try cuda.Event.init(r.d, false); + } + + /// Rank 0, once a path: which keyed path serving takes (the server's default sampling decides it). + fn note(x: *GpuSampler, s: Sampling, batched: bool) void { + if (x.f.comm.rank() != 0) return; + const V: usize = x.p.V; + const count = pipe.countOf(s, @intCast(V * x.f.comm.world()), x.p.nucleus); + const kind: u3 = if (@min(count, V) > pipe.max_k or V > pipe.max_cols) 0 else if (isNucleusRow(s)) 1 else 2; + const bit = @as(u8, 1) << (kind + @as(u3, if (batched) 3 else 0)); + if (x.seen & bit != 0) return; + x.seen |= bit; + const what = switch (kind) { + 0 => "whole rows (top_k 0 without the nucleus path, or past the device top-k)", + 1 => "nucleus candidates + row statistics", + else => "top_k candidates", + }; + const start = x.f.after_sample != null and kind == 2; + std.log.scoped(.dsv41).info("sampling: first keyed call ({s}): {s}, {d} candidates a rank (top_k {d}, top_p {d}, T {d}), device choice {s}", .{ if (batched) "one call a forward" else "one call a segment", what, @min(count, V), s.top_k, s.top_p, s.temperature, if (start) "on" else "off" }); + } + + pub fn deinit(x: *GpuSampler) void { + x.f.sampler = null; + if (x.pinned) |*b| b.free(); + if (x.done) |*e| e.deinit(); + if (x.chosen) |*b| b.free(); + x.gpu.deinit(); + x.p.deinit(); + x.gpa.destroy(x); + } + + /// Rank 0: the keyed choices of "w.logits" rows [row0, row0 + n) under `s` (T > 0), row i drawn at `draws[i]`. + pub fn choose(x: *GpuSampler, row0: u32, n: u32, s: Sampling, draws: []const u64, out: []u32) !void { + // a device start (Forward.after_sample) needs the seed and consecutive draws on every rank: op 12 carries them + const pos0: ?u64 = if (x.f.after_sample != null and consecutive(draws)) draws[0] else null; + if (pos0) |p0| try x.f.sendSample(&opDevice(row0, n, s, p0)) else try x.f.sendSample(&opOf(row0, n, s)); + try x.run(row0, n, s, draws, out, pos0); + } + + fn consecutive(draws: []const u64) bool { + if (draws.len == 0) return false; + for (draws, 0..) |d, i| if (d != draws[0] + i) return false; + return true; + } + + fn run(x: *GpuSampler, row0: u32, n: u32, s: Sampling, draws: ?[]const u64, out: []u32, pos0: ?u64) !void { + const f = x.f; + const V: u64 = f.cfg.vocab / f.comm.world(); + const lg = (f.runner.addressOf("w.logits") orelse return error.Unbound) + 4 * V * row0; + if (f.grammar) |g| try g.apply(g.ptr, row0, n); + x.cur = .{ .row0 = row0, .s = s, .pos0 = pos0 }; + defer x.cur = null; + x.note(s, false); + try x.p.choose(x.gpu.device(), lg, n, s, draws, out); + if (x.p.whole_rows > 0) std.log.scoped(.dsv41).debug("sampling: {d} of {d} rows taken whole", .{ x.p.whole_rows, n }); + } + + /// The plan-link message: [op, row0, n, T, top_k, top_p, min_p] (floats as their bits; the seed stays on rank 0). + pub fn opOf(row0: u32, n: u32, s: Sampling) [7]i64 { + return .{ fwd.Forward.op_sample, row0, n, @bitCast(s.temperature), s.top_k, @bitCast(s.top_p), @bitCast(s.min_p) }; + } + + /// The message with the device start's seed and first draw position: [op 12 ..., seed, pos0] (9 words). + pub fn opDevice(row0: u32, n: u32, s: Sampling, pos0: u64) [9]i64 { + const o = opOf(row0, n, s); + return o ++ [2]i64{ @bitCast(s.seed), @intCast(pos0) }; + } + + /// A follower: the leader's call from its message (tokens dropped; with 9 words its device choice too). + fn follow(ptr: *anyopaque, msg: []const i64) anyerror!void { + const x: *GpuSampler = @ptrCast(@alignCast(ptr)); + if (msg[0] == fwd.Forward.op_sample_many) return x.followSegments(msg); + if (msg.len != 7 and msg.len != 9) return error.BadPlan; + const n: u32 = @intCast(msg[2]); + const dev = msg.len == 9; + const s: Sampling = .{ .seed = if (dev) @bitCast(msg[7]) else 0, .temperature = @bitCast(msg[3]), .top_k = @intCast(msg[4]), .top_p = @bitCast(msg[5]), .min_p = @bitCast(msg[6]) }; + var out: [64]u32 = undefined; + if (n == 0 or n > out.len) return error.BadPlan; + try x.run(@intCast(msg[1]), n, s, null, out[0..n], if (dev) @intCast(msg[8]) else null); + } + + /// The download hook (every rank): device tokens before the pass when a start is wanted, then wait; false: none. + fn afterGather(ctx: *anyopaque, gathered: u64, n: usize, k: usize, count: usize, host: []u8) anyerror!bool { + const x: *GpuSampler = @ptrCast(@alignCast(ctx)); + const c = x.cur orelse return false; + const h = x.f.after_sample orelse return false; + const p0 = c.pos0 orelse return false; + const r = x.f.runner; + const ops = r.kernels.others(r.stream); + const W: usize = x.f.comm.world(); + if (ops.f.vs_choose == null or pipe.sampled(c.s) == false or isNucleusRow(c.s) or W * k > kops.glue.vs_max) return false; + try x.ensureDevice(host.len, n); + try r.d.check(r.d.api.cuMemcpyDtoHAsync_v2(x.pinned.?.bytes.ptr, gathered, host.len, r.stream.handle), "cuMemcpyDtoHAsync"); + try x.done.?.record(r.stream); + try ops.vsChoose(gathered, W, n, k, count, c.s.seed, p0, c.s.top_k, c.s.temperature, c.s.top_p, c.s.min_p, x.chosen.?.ptr); + try h.run(h.ctx, c.row0, @intCast(n), x.chosen.?.ptr); + try x.done.?.synchronize(); + @memcpy(host, x.pinned.?.bytes[0..host.len]); + return true; + } + + fn isNucleusRow(s: Sampling) bool { + return s.temperature > 0 and s.top_k == 0 and s.top_p > 0 and s.top_p < 1; + } +}; diff --git a/zig/src/families/deepseek_v41/sampling_kernels.zig b/zig/src/families/deepseek_v41/sampling_kernels.zig new file mode 100644 index 0000000000..2bdbc76406 --- /dev/null +++ b/zig/src/families/deepseek_v41/sampling_kernels.zig @@ -0,0 +1,118 @@ +//! sampling.cu's launches in their own optional fatbin (`dsv41_sampling`): nucleus statistics and packed rows. + +const std = @import("std"); +const cuda = @import("cuda"); + +const Blob = struct { + pub const bytes align(16) = @embedFile("dsv41_fatbin_dsv41_sampling").*; +}; +const image: []const u8 = &Blob.bytes; + +/// The build embedded the kernel (-Dnvcc, or -Dfatbins with dsv41_sampling.fatbin). +pub const available = image.len > 0; + +pub const Functions = struct { + module: cuda.Module, + stats_fn: cuda.Function, + pack_fn: cuda.Function, + /// the split statistics (every SM; null in a fatbin built before them: `stats` then runs one CTA a row) + split: ?Split = null, + /// why `split` is null: the symbol that did not resolve, and the driver's error + split_missing: ?struct { symbol: [:0]const u8, err: anyerror } = null, + + pub const Split = struct { max: cuda.Function, sum: cuda.Function, fin: cuda.Function }; + + /// Columns a CTA of the split statistics covers (64,640 a rank: 32 chunks a row). + pub const chunk: usize = 2048; + + pub fn load(d: *const cuda.Driver) !Functions { + if (!available) return error.SamplingKernelNotBuilt; + var m = try cuda.Module.load(d, image); + errdefer m.unload(); + var f: Functions = .{ + .module = m, + .stats_fn = try m.function("_ZN14dsv41_sampling12stats_kernelEPKfxidPd"), + .pack_fn = try m.function("_ZN14dsv41_sampling11pack_kernelEPKfPKxiiPf"), + }; + var fs: [3]cuda.Function = undefined; + for (split_syms, &fs) |sym, *x| x.* = m.function(sym) catch |e| { + f.split_missing = .{ .symbol = sym, .err = e }; + return f; + }; + f.split = .{ .max = fs[0], .sum = fs[1], .fin = fs[2] }; + return f; + } + + const split_syms = [3][:0]const u8{ "dsv41_sampling_stats_max", "dsv41_sampling_stats_sum", "dsv41_sampling_stats_fin" }; + + /// Bytes of device scratch `statsSplit` needs for `rows` rows of C columns. + pub fn splitScratch(rows: usize, C: usize) usize { + const chunks = std.math.divCeil(usize, C, chunk) catch unreachable; + return std.mem.alignForward(usize, 4 * rows * chunks, 8) + 8 * rows * chunks; + } + + /// `stats` over every SM (sum in chunk order); `work` holds `splitScratch(rows, C)` bytes; error.NoSplit without. + pub fn statsSplit(f: *const Functions, s: cuda.Stream, lg: u64, ld: usize, rows: usize, C: usize, t: f64, work: u64, out: u64) !void { + const sp = f.split orelse return error.NoSplit; + if (rows < 1 or C < 1 or !(t > 0)) return error.Shape; + const chunks = std.math.divCeil(usize, C, chunk) catch unreachable; + const pmax = work; + const psum = work + std.mem.alignForward(usize, 4 * rows * chunks, 8); + const grid: cuda.launch.Dim3 = .{ .x = @intCast(chunks), .y = @intCast(rows), .z = 1 }; + const block: cuda.launch.Dim3 = .{ .x = 256, .y = 1, .z = 1 }; + { + var args: cuda.Args = .{}; + args.add(lg); + args.add(@as(i64, @intCast(ld))); + args.add(@as(c_int, @intCast(C))); + args.add(@as(c_int, @intCast(chunk))); + args.add(pmax); + try cuda.launch.launch(sp.max, .{ .grid = grid, .block = block }, s, &args); + } + { + var args: cuda.Args = .{}; + args.add(lg); + args.add(@as(i64, @intCast(ld))); + args.add(@as(c_int, @intCast(C))); + args.add(@as(c_int, @intCast(chunk))); + args.add(t); + args.add(pmax); + args.add(psum); + try cuda.launch.launch(sp.sum, .{ .grid = grid, .block = block }, s, &args); + } + var args: cuda.Args = .{}; + args.add(pmax); + args.add(psum); + args.add(@as(c_int, @intCast(chunks))); + args.add(out); + try cuda.launch.launch(sp.fin, .{ .grid = .{ .x = @intCast(rows), .y = 1, .z = 1 }, .block = .{ .x = 32, .y = 1, .z = 1 } }, s, &args); + } + + pub fn unload(f: *Functions) void { + f.module.unload(); + } + + /// nucleus.row_stats: float64 [rows, 2] at `out`, each row's (max, sum exp(v / t - max / t)) over its C columns. + pub fn stats(f: *const Functions, s: cuda.Stream, lg: u64, ld: usize, rows: usize, C: usize, t: f64, out: u64) !void { + if (rows < 1 or C < 1 or !(t > 0)) return error.Shape; + var args: cuda.Args = .{}; + args.add(lg); + args.add(@as(i64, @intCast(ld))); + args.add(@as(c_int, @intCast(C))); + args.add(t); + args.add(out); + try cuda.launch.launch(f.stats_fn, .{ .grid = .{ .x = @intCast(rows), .y = 1, .z = 1 }, .block = .{ .x = 1024, .y = 1, .z = 1 } }, s, &args); + } + + /// cand_gather's packed rows: fp32 [rows, 2k] at `out` from topk_keys' vals fp32 / cols int64 [rows, k]. + pub fn pack(f: *const Functions, s: cuda.Stream, vals: u64, cols: u64, rows: usize, k: usize, id0: u32, out: u64) !void { + if (rows < 1 or k < 1) return error.Shape; + var args: cuda.Args = .{}; + args.add(vals); + args.add(cols); + args.add(@as(c_int, @intCast(k))); + args.add(@as(c_int, @intCast(id0))); + args.add(out); + try cuda.launch.launch(f.pack_fn, .{ .grid = .{ .x = @intCast(rows), .y = 1, .z = 1 }, .block = .{ .x = 256, .y = 1, .z = 1 } }, s, &args); + } +}; diff --git a/zig/src/families/deepseek_v41/sampling_pipe.zig b/zig/src/families/deepseek_v41/sampling_pipe.zig new file mode 100644 index 0000000000..b2c889cb40 --- /dev/null +++ b/zig/src/families/deepseek_v41/sampling_pipe.zig @@ -0,0 +1,404 @@ +//! A window's keyed choices from the ranks' logits: every rank runs the same steps alike; rank 0's tokens are used. + +const std = @import("std"); +const tp = @import("tp"); +const smp = @import("dsv41_serve").sampling; + +pub const Sampling = smp.Sampling; + +/// The device top-k's bounds: a larger k, or a wider slice, gathers whole rows instead (the same tokens). +pub const max_k: usize = 1024; +pub const max_cols: usize = 1 << 16; + +/// The device side of the steps: `lg` is this rank's fp32 logits [n, C] (row stride C), addresses device addresses. +pub const Device = struct { + ptr: *anyopaque, + vtable: *const VTable, + + pub const VTable = struct { + /// Each row's best k (value desc, then the lower column), packed fp32 [n, 2k] at `out` with ids column + id0. + top: *const fn (ptr: *anyopaque, lg: u64, n: usize, C: usize, k: usize, id0: u32, out: u64) anyerror!void, + /// float64 [n, 2] at `out`: each row's (max, sum exp(v / t - max / t)). + stats: *const fn (ptr: *anyopaque, lg: u64, n: usize, C: usize, t: f64, out: u64) anyerror!void, + /// Scratch slot `slot` of at least `bytes` (grown, kept). + scratch: *const fn (ptr: *anyopaque, slot: u32, bytes: usize) anyerror!u64, + /// The device bytes at `src` into `dst`, after the stream's work. + download: *const fn (ptr: *anyopaque, src: u64, dst: []u8) anyerror!void, + /// The stream the collectives run on. + stream: *const fn (ptr: *anyopaque) tp.collective.Stream, + }; +}; + +/// The candidates' count of a sampling (``Batcher._decode``) at `nucleus` (TF_DSV41_NUCLEUS, 0: off). +extern "c" fn exp(x: f64) f64; +extern "c" fn log(x: f64) f64; + +/// glue.cu vs_choose mirrored on the host: row tokens from gathered candidates `g`, row r at pos0 + r; -1: nucleus. +pub fn chooseMirror(scratch: []u64, work: []f64, g: []const f32, W: usize, n: usize, k: usize, count: usize, s: Sampling, pos0: u64, out: []i64) void { + const C = W * k; + for (0..n) |r| { + const keys = scratch[0..C]; + for (0..C) |i| { + const w = i / k; + const j = i - w * k; + const row = g[(w * n + r) * 2 * k ..][0 .. 2 * k]; + const bits: u32 = @bitCast(row[j] + 0.0); + const mono: u32 = if (bits & 0x8000_0000 != 0) ~bits else bits | 0x8000_0000; + const id: u32 = @bitCast(row[k + j]); + keys[i] = @as(u64, mono) << 32 | (0xFFFF_FFFF - id); + } + std.sort.pdq(u64, keys, {}, std.sort.desc(u64)); + const idOf = struct { + fn f(x: u64) i64 { + return @intCast(0xFFFF_FFFF - (x & 0xFFFF_FFFF)); + } + }.f; + const valueOf = struct { + fn f(x: u64) f64 { + const mono: u32 = @truncate(x >> 32); + const v: f32 = @bitCast(if (mono & 0x8000_0000 != 0) mono & 0x7FFF_FFFF else ~mono); + return v; + } + }.f; + if (!(s.temperature > 0)) { + out[r] = idOf(keys[0]); + continue; + } + if (s.top_k == 0 and s.top_p > 0 and s.top_p < 1) { + out[r] = -1; + continue; + } + const width = if (count > 0 and count < C) count else C; + var kk: usize = if (s.top_k != 0) @min(s.top_k, width) else width; + kk = @min(@max(kk, 1), 1024); + const tt = @max(s.temperature, 1e-6); + const s0 = valueOf(keys[0]) / tt; + var keep = kk; + if (s.top_p > 0 and s.top_p < 1) { + for (0..kk) |j| work[j] = exp(valueOf(keys[j]) / tt - s0); + const total = @import("lanes").sampling.pairwiseSum(work[0..kk]); + var cum: f64 = 0; + var below: usize = 0; + for (0..kk) |j| { + cum += work[j] / total; + if (cum < s.top_p) below += 1; + } + keep = below + 1; + } + const floor_ = s0 + (if (s.min_p > 0) log(s.min_p) else -std.math.inf(f64)); + const lim = @min(keep, kk); + var best: usize = 0; + var best_score = -std.math.inf(f64); + for (0..lim) |j| { + const x = valueOf(keys[j]) / tt; + if (s.min_p > 0 and x < floor_) continue; + const id: u64 = @intCast(idOf(keys[j])); + const score = x - log(-log(@import("lanes").sampling.uniform(s.seed, pos0 +% r, id))); + if (j == 0 or score > best_score) { + best = j; + best_score = score; + } + } + out[r] = idOf(keys[best]); + } +} + +/// sampling_gpu.zig's hook over a call's gathered candidates [W][n][2 k] (`count` merged), before their download. +pub const AfterGather = struct { ctx: *anyopaque, run: *const fn (ctx: *anyopaque, gathered: u64, n: usize, k: usize, count: usize, host: []u8) anyerror!bool }; + +pub fn countOf(s: Sampling, vocab: u32, nucleus: u32) u32 { + return smp.candidateCount(s, vocab, nucleus); +} + +/// T > 0: a keyed choice (T <= 0 is greedy: forward.greedy). +pub fn sampled(s: ?Sampling) bool { + return if (s) |x| x.temperature > 0 else false; +} + +/// TF_DSV41_NUCLEUS (``nucleus.candidates``): candidates a nucleus row, default 512, 0 = off. +pub fn nucleusEnv() !u32 { + const v = std.c.getenv("TF_DSV41_NUCLEUS") orelse return smp.nucleus_count; + const s = std.mem.span(v); + if (s.len == 0) return smp.nucleus_count; + return std.fmt.parseInt(u32, s, 10) catch error.BadNucleus; +} + +/// One rank's sampler: the host scratch, sized once for the whole vocabulary (picking allocates nothing). +pub const Pipe = struct { + gpa: std.mem.Allocator, + comm: tp.collective.Collective, + /// this rank's vocabulary slice (the head's columns): global ids rank x V + column + V: u32, + nucleus: u32, + picker: smp.Picker, + values: []f32, + ids: []u32, + host: std.array_list.Aligned(u8, .@"16") = .empty, + full: std.ArrayList(u32) = .empty, + picks: std.ArrayList(smp.Pick) = .empty, + /// the candidates' download taken over: true when it downloaded `host`; null or false: the device's own download + after_gather: ?AfterGather = null, + /// rows taken whole in the last call (the nucleus fallback or k > max_k): the tests' and logs' count + whole_rows: usize = 0, + + pub fn init(gpa: std.mem.Allocator, comm: tp.collective.Collective, V: u32, nucleus: u32) !Pipe { + const vocab: usize = @as(usize, V) * comm.world(); + var picker = try smp.Picker.init(gpa, vocab); + errdefer picker.deinit(); + const values = try gpa.alloc(f32, vocab); + errdefer gpa.free(values); + return .{ .gpa = gpa, .comm = comm, .V = V, .nucleus = nucleus, .picker = picker, .values = values, .ids = try gpa.alloc(u32, vocab) }; + } + + pub fn deinit(p: *Pipe) void { + p.picker.deinit(); + p.gpa.free(p.values); + p.gpa.free(p.ids); + p.host.deinit(p.gpa); + p.full.deinit(p.gpa); + p.picks.deinit(p.gpa); + } + + fn hostBytes(p: *Pipe, bytes: usize) ![]align(16) u8 { + try p.host.resize(p.gpa, bytes); + return p.host.items; + } + + /// The keyed choices of rows [0, n) of `lg` under `s` (T > 0), row i keyed at `positions[i]` (followers: null). + pub fn choose(p: *Pipe, dev: Device, lg: u64, n: usize, s: Sampling, positions: ?[]const u64, out: []u32) !void { + std.debug.assert(out.len >= n and n > 0); + const W: usize = p.comm.world(); + const V: usize = p.V; + const count: usize = countOf(s, @intCast(V * W), p.nucleus); + const k = @min(count, V); + p.whole_rows = 0; + try p.picks.resize(p.gpa, n); + if (k > max_k or V > max_cols) { + // every row whole (top_k 0 without the nucleus path, a top_k past the device bound, or one rank's 129,280) + p.full.clearRetainingCapacity(); + for (0..n) |r| try p.full.append(p.gpa, @intCast(r)); + return p.wholeRows(dev, lg, s, count, positions, out); + } + const nuc = smp.isNucleus(s) and p.nucleus > 0; + const vt = dev.vtable; + const stream = vt.stream(dev.ptr); + const row_f32 = 2 * k; + const packed_ = try vt.scratch(dev.ptr, 0, 4 * n * row_f32); + const gathered = try vt.scratch(dev.ptr, 1, 4 * W * n * row_f32); + try vt.top(dev.ptr, lg, n, V, k, @intCast(p.comm.rank() * V), packed_); + try p.comm.allGather(packed_, gathered, n * row_f32, .f32, stream); + var stats_dev: u64 = 0; + if (nuc) { + const st = try vt.scratch(dev.ptr, 2, 16 * n); + stats_dev = try vt.scratch(dev.ptr, 3, 16 * W * n); + try vt.stats(dev.ptr, lg, n, V, s.temperature, st); + try p.comm.allGather(st, stats_dev, 2 * n, .f64, stream); + } + const cand_bytes = 4 * W * n * row_f32; + const stat_bytes: usize = if (nuc) 16 * W * n else 0; + const host = try p.hostBytes(cand_bytes + stat_bytes); + const taken = if (p.after_gather) |h| try h.run(h.ctx, gathered, n, k, count, host[0..cand_bytes]) else false; + if (!taken) try vt.download(dev.ptr, gathered, host[0..cand_bytes]); + if (nuc) try vt.download(dev.ptr, stats_dev, host[cand_bytes..][0..stat_bytes]); + const cand: smp.Candidates = .{ .data = @alignCast(std.mem.bytesAsSlice(f32, host[0..cand_bytes])), .world = @intCast(W), .rows = @intCast(n), .k = @intCast(k) }; + const all_stats: []const f64 = if (nuc) @alignCast(std.mem.bytesAsSlice(f64, host[cand_bytes..][0..stat_bytes])) else &.{}; + var stats: [tp.host.max_ranks][2]f64 = undefined; + p.full.clearRetainingCapacity(); + for (0..n) |r| { + const m = cand.row(@intCast(r), p.values, p.ids); + // rank w's (m, s) of row r: [W][n][2] + if (nuc) for (stats[0..W], 0..) |*st, w| { + st.* = .{ all_stats[2 * (w * n + r)], all_stats[2 * (w * n + r) + 1] }; + }; + const row: smp.Row = .{ .values = p.values[0..m], .ids = p.ids[0..m], .stats = if (nuc) stats[0..W] else &.{}, .count = count }; + switch (p.picker.pick(row, if (positions) |ps| ps[r] else 0, s)) { + .token => |t| out[r] = t, + .full_row => try p.full.append(p.gpa, @intCast(r)), + } + } + if (p.full.items.len > 0) try p.wholeRows(dev, lg, s, 0, positions, out); + } + + /// The rows in `full`, each gathered whole in rank order and picked from all its tokens (`count` = their cut). + fn wholeRows(p: *Pipe, dev: Device, lg: u64, s: Sampling, count: usize, positions: ?[]const u64, out: []u32) !void { + for (p.full.items) |r| out[r] = try p.wholeRow(dev, lg + 4 * @as(u64, r) * p.V, s, count, if (positions) |ps| ps[r] else 0); + } + + /// One row at `row` (this rank's slice), gathered whole and picked at `position` (`wholeRows`' step). + fn wholeRow(p: *Pipe, dev: Device, row: u64, s: Sampling, count: usize, position: u64) !u32 { + const W: usize = p.comm.world(); + const V: usize = p.V; + const vt = dev.vtable; + const recv = try vt.scratch(dev.ptr, 4, 4 * W * V); + const host = try p.hostBytes(4 * W * V); + const vals: []const f32 = @alignCast(std.mem.bytesAsSlice(f32, host)); + for (p.ids[0 .. W * V], 0..) |*id, i| id.* = @intCast(i); // rank order: global id = the index + try p.comm.allGather(row, recv, V, .f32, vt.stream(dev.ptr)); + try vt.download(dev.ptr, recv, host); + @memcpy(p.values[0 .. W * V], vals); + const r: smp.Row = .{ .values = p.values[0 .. W * V], .ids = p.ids[0 .. W * V], .count = count }; + p.whole_rows += 1; + return p.picker.pick(r, position, s).token; // no statistics: never .full_row + } + + /// A sampled segment: logits rows [row0, row0 + n) under `s`, row i keyed at `positions[i]`, tokens into `out`. + pub const Seg = struct { row0: u32, n: u32, s: Sampling, positions: ?[]const u64 = null, out: []u32 }; + + /// Whether `chooseSegments` takes a segment under `s`: a keyed candidates-path row within the device bounds. + pub fn batchable(p: *const Pipe, s: Sampling) bool { + if (!sampled(s) or p.V > max_cols) return false; + return @min(countOf(s, @intCast(@as(usize, p.V) * p.comm.world()), p.nucleus), p.V) <= max_k; + } + + /// `chooseSegments`' hook over the gathered candidates before their download; true when it downloaded `host`. + pub const SegGather = struct { ctx: *anyopaque, run: *const fn (ctx: *anyopaque, gathered: u64, n: usize, k: usize, segs: []const Seg, host: []u8) anyerror!bool }; + + /// Several keyed segments of one forward in one gather; each row's kept candidates and order equal `choose`'s. + pub fn chooseSegments(p: *Pipe, dev: Device, lg: u64, segs: []const Seg, hook: ?SegGather) !void { + std.debug.assert(segs.len > 0); + const W: usize = p.comm.world(); + const V: usize = p.V; + const vocab: u32 = @intCast(V * W); + var count: usize = 0; + var nuc = false; + const r0: usize = segs[0].row0; + var end: usize = r0; + for (segs) |g| { + if (!p.batchable(g.s)) return error.NotBatchable; + if (g.row0 < end or g.n == 0 or g.out.len < g.n) return error.BadSegments; + end = g.row0 + g.n; + count = @max(count, countOf(g.s, vocab, p.nucleus)); + nuc = nuc or smp.isNucleus(g.s); + } + const n = end - r0; + const k = @min(count, V); + p.whole_rows = 0; + const vt = dev.vtable; + const stream = vt.stream(dev.ptr); + const row_f32 = 2 * k; + const span = lg + 4 * @as(u64, r0) * V; + const packed_ = try vt.scratch(dev.ptr, 0, 4 * n * row_f32); + const gathered = try vt.scratch(dev.ptr, 1, 4 * W * n * row_f32); + try vt.top(dev.ptr, span, n, V, k, @intCast(p.comm.rank() * V), packed_); + try p.comm.allGather(packed_, gathered, n * row_f32, .f32, stream); + var stats_dev: u64 = 0; + if (nuc) { + const st = try vt.scratch(dev.ptr, 2, 16 * n); + stats_dev = try vt.scratch(dev.ptr, 3, 16 * W * n); + for (segs) |g| if (smp.isNucleus(g.s)) try vt.stats(dev.ptr, lg + 4 * @as(u64, g.row0) * V, g.n, V, g.s.temperature, st + 16 * (g.row0 - r0)); + try p.comm.allGather(st, stats_dev, 2 * n, .f64, stream); + } + const cand_bytes = 4 * W * n * row_f32; + const stat_bytes: usize = if (nuc) 16 * W * n else 0; + const host = try p.hostBytes(cand_bytes + stat_bytes); + const taken = if (hook) |h| try h.run(h.ctx, gathered, n, k, segs, host[0..cand_bytes]) else false; + if (!taken) try vt.download(dev.ptr, gathered, host[0..cand_bytes]); + if (nuc) try vt.download(dev.ptr, stats_dev, host[cand_bytes..][0..stat_bytes]); + const cand: smp.Candidates = .{ .data = @alignCast(std.mem.bytesAsSlice(f32, host[0..cand_bytes])), .world = @intCast(W), .rows = @intCast(n), .k = @intCast(k) }; + const all_stats: []const f64 = if (nuc) @alignCast(std.mem.bytesAsSlice(f64, host[cand_bytes..][0..stat_bytes])) else &.{}; + var stats: [tp.host.max_ranks][2]f64 = undefined; + // every pick from the downloaded rows first (a whole row reuses the host buffer), the whole rows after + p.full.clearRetainingCapacity(); + for (segs) |g| { + const own = countOf(g.s, vocab, p.nucleus); + const st_on = smp.isNucleus(g.s); + for (0..g.n) |i| { + const r = g.row0 - r0 + i; + const m = cand.row(@intCast(r), p.values, p.ids); + if (st_on) for (stats[0..W], 0..) |*x, w| { + x.* = .{ all_stats[2 * (w * n + r)], all_stats[2 * (w * n + r) + 1] }; + }; + const row: smp.Row = .{ .values = p.values[0..m], .ids = p.ids[0..m], .stats = if (st_on) stats[0..W] else &.{}, .count = own }; + switch (p.picker.pick(row, if (g.positions) |ps| ps[i] else 0, g.s)) { + .token => |t| g.out[i] = t, + .full_row => try p.full.append(p.gpa, @intCast(r)), + } + } + } + var j: usize = 0; + for (segs) |g| for (0..g.n) |i| { + const r = g.row0 - r0 + i; + if (j == p.full.items.len or p.full.items[j] != r) continue; + j += 1; + g.out[i] = try p.wholeRow(dev, lg + 4 * @as(u64, g.row0 + i) * V, g.s, 0, if (g.positions) |ps| ps[i] else 0); + }; + } +}; + +/// The reference device: `lg` and every address are host pointers (``tp.host``'s ranks share one address space). +pub const HostDevice = struct { + gpa: std.mem.Allocator, + bufs: [8][]align(16) u8 = @splat(&.{}), + keys: std.ArrayList(u64) = .empty, + + pub fn deinit(h: *HostDevice) void { + for (h.bufs) |b| if (b.len > 0) h.gpa.free(b); + h.keys.deinit(h.gpa); + } + + pub fn device(h: *HostDevice) Device { + return .{ .ptr = h, .vtable = &.{ .top = top, .stats = stats, .scratch = scratch, .download = download, .stream = stream } }; + } + + fn self_(p: *anyopaque) *HostDevice { + return @ptrCast(@alignCast(p)); + } + + fn f32s(a: u64, n: usize) []f32 { + return @as([*]f32, @ptrFromInt(a))[0..n]; + } + + /// ``pick.keys``' order on any fp32 values: larger value (-0.0 as +0.0), then the lower column. + fn key(v: f32, col: usize) u64 { + const bits: u32 = @bitCast(v + 0.0); + const mono: u32 = if (bits & 0x8000_0000 == 0) bits | 0x8000_0000 else ~bits; + return @as(u64, mono) << 32 | (0xFFFF_FFFF - @as(u32, @intCast(col))); + } + + fn top(p: *anyopaque, lg: u64, n: usize, C: usize, k: usize, id0: u32, out: u64) anyerror!void { + const h = self_(p); + try h.keys.resize(h.gpa, C); + const o = f32s(out, n * 2 * k); + for (0..n) |r| { + const row = f32s(lg + 4 * r * C, C); + for (h.keys.items, row, 0..) |*kk, v, c| kk.* = key(v, c); + std.sort.pdq(u64, h.keys.items, {}, std.sort.desc(u64)); + for (h.keys.items[0..k], 0..) |kk, j| { + const c = 0xFFFF_FFFF - @as(u32, @truncate(kk)); + o[r * 2 * k + j] = row[c]; + o[r * 2 * k + k + j] = @bitCast(c + id0); + } + } + } + + fn stats(_: *anyopaque, lg: u64, n: usize, C: usize, t: f64, out: u64) anyerror!void { + const o = @as([*]f64, @ptrFromInt(out))[0 .. 2 * n]; + for (0..n) |r| { + const row = f32s(lg + 4 * r * C, C); + var m = -std.math.inf(f64); + for (row) |v| m = @max(m, @as(f64, v)); + const mt = m / t; + var s: f64 = 0; + for (row) |v| s += @exp(@as(f64, v) / t - mt); + o[2 * r] = m; + o[2 * r + 1] = s; + } + } + + fn scratch(p: *anyopaque, slot: u32, bytes: usize) anyerror!u64 { + const h = self_(p); + if (h.bufs[slot].len < bytes) { + if (h.bufs[slot].len > 0) h.gpa.free(h.bufs[slot]); + h.bufs[slot] = try h.gpa.alignedAlloc(u8, .@"16", bytes); + } + return @intFromPtr(h.bufs[slot].ptr); + } + + fn download(_: *anyopaque, src: u64, dst: []u8) anyerror!void { + @memcpy(dst, @as([*]const u8, @ptrFromInt(src))[0..dst.len]); + } + + fn stream(_: *anyopaque) tp.collective.Stream { + return null; + } +}; diff --git a/zig/src/families/deepseek_v41/sampling_test.zig b/zig/src/families/deepseek_v41/sampling_test.zig new file mode 100644 index 0000000000..0bfd39dbdc --- /dev/null +++ b/zig/src/families/deepseek_v41/sampling_test.zig @@ -0,0 +1,369 @@ +//! The sampling pipeline on in-process ranks against the serving goldens: each row split over ranks and picked. + +const std = @import("std"); +const testing = std.testing; +const tp = @import("tp"); +const json = @import("json"); +const serve = @import("dsv41_serve"); +const pipe = @import("sampling_pipe.zig"); + +const Case = struct { + logits: []const f32, + world: u32, + rows: usize, + sampling: pipe.Sampling, + positions: []const u64, + nucleus: u32 = serve.sampling.nucleus_count, + got: []u32, + whole: []usize, + /// rank 0: vs_choose's mirror over the gathered candidates (sampling_pipe.chooseMirror), per row; null: not run + mirror: ?[]i64 = null, +}; + +/// The download hook on rank 0: the mirror over the gathered candidates, then the pipe's own download (false). +const Mirror = struct { + c: *Case, + fn run(ctx: *anyopaque, gathered: u64, n: usize, k: usize, count: usize, host: []u8) anyerror!bool { + _ = host; + const m: *Mirror = @ptrCast(@alignCast(ctx)); + const W: usize = m.c.world; + const g: [*]const f32 = @ptrFromInt(gathered); + const a = testing.allocator; + const keys = try a.alloc(u64, W * k); + defer a.free(keys); + const work = try a.alloc(f64, 1024); + defer a.free(work); + pipe.chooseMirror(keys, work, g[0 .. W * n * 2 * k], W, n, k, count, m.c.sampling, m.c.positions[0], m.c.mirror.?); + return false; + } +}; + +/// Rank `me` of a window: its vocabulary slice of each row (contiguous [rows, V]), the pipeline's tokens on rank 0. +fn rankBody(comm: tp.collective.Collective, me: u32, c: *Case) anyerror!void { + const a = testing.allocator; + const vocab = c.logits.len / c.rows; + const V = vocab / c.world; + const mine = try a.alloc(f32, c.rows * V); + defer a.free(mine); + for (0..c.rows) |r| @memcpy(mine[r * V ..][0..V], c.logits[r * vocab + me * V ..][0..V]); + var dev: pipe.HostDevice = .{ .gpa = a }; + defer dev.deinit(); + var p = try pipe.Pipe.init(a, comm, @intCast(V), c.nucleus); + defer p.deinit(); + var mh: Mirror = .{ .c = c }; + if (me == 0 and c.mirror != null) p.after_gather = .{ .ctx = &mh, .run = Mirror.run }; + const out = try a.alloc(u32, c.rows); + defer a.free(out); + try p.choose(dev.device(), @intFromPtr(mine.ptr), c.rows, c.sampling, if (me == 0) c.positions else null, out); + if (me == 0) { + @memcpy(c.got, out); + c.whole[0] = p.whole_rows; + } +} + +fn run(c: *Case) !void { + try tp.host.run(c.world, c, rankBody); +} + +fn parse(x: std.mem.Allocator, line: []const u8) !struct { logits: []f32, world: u32, s: ?pipe.Sampling, position: u64, full: bool, token: u32 } { + const c = (try json.parseText(x, line)).ok; + const b64 = c.get("logits").?.string; + const raw = try x.alloc(u8, try std.base64.standard.Decoder.calcSizeForSlice(b64)); + try std.base64.standard.Decoder.decode(raw, b64); + const logits = try x.alloc(f32, raw.len / 4); + for (logits, 0..) |*v, i| v.* = @bitCast(std.mem.readInt(u32, raw[4 * i ..][0..4], .little)); + const sv = c.get("sampling").?; + const s: ?pipe.Sampling = if (sv == .array) .{ + .seed = @truncate(@as(u128, @bitCast(@as(i128, try std.fmt.parseInt(i128, sv.array[0].int, 10))))), + .temperature = sv.array[1].float64().?, + .top_k = @intCast(sv.array[2].int64().?), + .top_p = sv.array[3].float64().?, + .min_p = sv.array[4].float64().?, + } else null; + return .{ .logits = logits, .world = @intCast(c.get("world").?.int64().?), .s = s, .position = @intCast(c.get("position").?.int64().?), .full = c.get("full").?.bool, .token = @intCast(c.get("token").?.int64().?) }; +} + +test "sampled rows on two ranks pick the Python engine's tokens (the committed serving goldens)" { + const a = testing.allocator; + var arena = std.heap.ArenaAllocator.init(a); + defer arena.deinit(); + var it = std.mem.splitScalar(u8, serve.fixtures.sampling, '\n'); + var rows: usize = 0; + var bad: usize = 0; + var whole: usize = 0; + var fallbacks: usize = 0; + while (it.next()) |line| { + if (line.len == 0) continue; + _ = arena.reset(.retain_capacity); + const g = try parse(arena.allocator(), line); + const s = g.s orelse continue; // greedy rows: forward.greedy's path + if (!pipe.sampled(s) or g.logits.len % g.world != 0) continue; + var got: [1]u32 = .{0}; + var w: [1]usize = .{0}; + var c: Case = .{ .logits = g.logits, .world = g.world, .rows = 1, .sampling = s, .positions = &.{g.position}, .got = &got, .whole = &w }; + try run(&c); + rows += 1; + whole += w[0]; + fallbacks += @intFromBool(g.full); + if (got[0] != g.token) { + bad += 1; + if (bad <= 5) std.debug.print("row {d}: want {d} got {d} (vocab {d}, world {d}, sampling {any})\n", .{ rows, g.token, got[0], g.logits.len, g.world, s }); + } + } + std.debug.print("sampling pipeline: {d}/{d} sampled rows as Python picks them ({d} taken whole, {d} nucleus fallbacks in the goldens)\n", .{ rows - bad, rows, whole, fallbacks }); + try testing.expect(rows > 100 and fallbacks > 0); + try testing.expectEqual(@as(usize, 0), bad); +} + +test "a window's rows pick as each row does alone (one call, several positions, every path)" { + const a = testing.allocator; + var prng = std.Random.DefaultPrng.init(41); + const rnd = prng.random(); + const vocab = 4096; + const n = 6; + const logits = try a.alloc(f32, n * vocab); + defer a.free(logits); + for (logits, 0..) |*v, i| { + const x: f32 = @floatCast(rnd.floatNorm(f64) * 3.0 + @as(f64, if (i % vocab == 77) 6 else 0)); + v.* = @bitCast(@as(u32, @bitCast(x)) & 0xFFFF_0000); // bf16-valued, as kit_logits leaves them + } + const positions = [_]u64{ 100, 101, 102, 103, 104, 105 }; + const cases = [_]pipe.Sampling{ + .{ .seed = 7, .temperature = 0.7, .top_k = 0, .top_p = 0.9 }, // nucleus + .{ .seed = 9, .temperature = 1.0, .top_k = 40, .top_p = 0.95, .min_p = 0.05 }, // top-k + .{ .seed = 3, .temperature = 1.3, .top_k = 0, .top_p = 1.0 }, // whole rows (k > max_k) + .{ .seed = 1 << 63 | 5, .temperature = 0.6, .top_k = 0, .top_p = 0.8 }, + }; + for (cases) |s| for ([_]u32{ 1, 2 }) |world| { + var all: [n]u32 = undefined; + var w: [1]usize = .{0}; + var c: Case = .{ .logits = logits, .world = world, .rows = n, .sampling = s, .positions = &positions, .got = &all, .whole = &w }; + try run(&c); + for (0..n) |r| { + var one: [1]u32 = undefined; + var w1: [1]usize = .{0}; + var c1: Case = .{ .logits = logits[r * vocab ..][0..vocab], .world = world, .rows = 1, .sampling = s, .positions = positions[r..][0..1], .got = &one, .whole = &w1 }; + try run(&c1); + try testing.expectEqual(one[0], all[r]); + } + // the keyed choice is the host rule's over the whole row (lanes.sampling.choose = exact_sampling.choose) + for (0..n) |r| { + const row = logits[r * vocab ..][0..vocab]; + const vals = try a.alloc(f64, vocab); + defer a.free(vals); + const ids = try a.alloc(u64, vocab); + defer a.free(ids); + for (vals, ids, row, 0..) |*v, *id, x, i| { + v.* = x; + id.* = i; + } + const want = try @import("lanes").sampling.choose(a, vals, ids, positions[r], s); + try testing.expectEqual(@as(u32, @intCast(want)), all[r]); + } + }; +} + +test "vs_choose's arithmetic (its host mirror) picks the pipeline's tokens on the serving goldens' keyed rows" { + // the device choice only decides where the speculative pass starts; its host mirror must equal the host pick + const a = testing.allocator; + var arena = std.heap.ArenaAllocator.init(a); + defer arena.deinit(); + var it = std.mem.splitScalar(u8, serve.fixtures.sampling, '\n'); + var rows: usize = 0; + var bad: usize = 0; + while (it.next()) |line| { + if (line.len == 0) continue; + _ = arena.reset(.retain_capacity); + const g = try parse(arena.allocator(), line); + const s = g.s orelse continue; + if (!pipe.sampled(s) or g.logits.len % g.world != 0) continue; + if (s.top_k == 0 and s.top_p > 0 and s.top_p < 1) continue; // nucleus rows: the host's statistics + var got: [1]u32 = .{0}; + var w: [1]usize = .{0}; + var mirror: [1]i64 = .{-2}; + var c: Case = .{ .logits = g.logits, .world = g.world, .rows = 1, .sampling = s, .positions = &.{g.position}, .got = &got, .whole = &w, .mirror = &mirror }; + try run(&c); + if (mirror[0] == -2) continue; // taken whole (k past the device bound): no gathered candidates + rows += 1; + if (mirror[0] != got[0]) { + bad += 1; + if (bad <= 5) std.debug.print("row {d}: mirror {d}, pipeline {d} (sampling {any})\n", .{ rows, mirror[0], got[0], s }); + } + } + std.debug.print("vs_choose mirror: {d}/{d} keyed rows as the pipeline picks them\n", .{ rows - bad, rows }); + try testing.expect(rows > 20); + try testing.expectEqual(@as(usize, 0), bad); +} + +test "vs_choose's mirror == the pipeline's picks over top_k x top_p x min_p x temperature, one and two ranks" { + const a = testing.allocator; + var prng = std.Random.DefaultPrng.init(4101); + const rnd = prng.random(); + const vocab = 4096; + const n = 6; + const logits = try a.alloc(f32, n * vocab); + defer a.free(logits); + const positions = [_]u64{ 300, 301, 302, 303, 304, 305 }; + var rows: usize = 0; + for (0..3) |round| { + // flat-ish rows (the top-p cut lands inside the top k), peaked rows, and ties (bf16-valued) + for (logits, 0..) |*v, i| { + const spread: f64 = if (round == 0) 0.6 else 2.5; + const x: f32 = @floatCast(rnd.floatNorm(f64) * spread + @as(f64, if (i % vocab < 3) 4 else 0)); + v.* = if (round == 2) @bitCast(@as(u32, @bitCast(x)) & 0xFFFF_0000) else x; + } + for ([_]u32{ 20, 40, 100 }) |top_k| for ([_]f64{ 0.5, 0.8, 0.95, 1.0 }) |top_p| for ([_]f64{ 0, 0.05 }) |min_p| for ([_]f64{ 0.6, 1.3 }) |t| for ([_]u32{ 1, 2 }) |world| { + const s: pipe.Sampling = .{ .seed = rnd.int(u64) >> 1, .temperature = t, .top_k = top_k, .top_p = top_p, .min_p = min_p }; + var got: [n]u32 = undefined; + var w: [1]usize = .{0}; + var mirror: [n]i64 = @splat(-2); + var c: Case = .{ .logits = logits, .world = world, .rows = n, .sampling = s, .positions = &positions, .got = &got, .whole = &w, .mirror = &mirror }; + try run(&c); + for (got, mirror) |g, m| { + try testing.expectEqual(@as(i64, g), m); + rows += 1; + } + }; + } + try testing.expectEqual(@as(usize, 3 * 3 * 4 * 2 * 2 * 2 * n), rows); +} + +const SegSpec = struct { row0: u32, n: u32, s: pipe.Sampling, positions: []const u64 }; + +/// Several segments of one forward's rows (rows not in a segment are another window's, never picked). +const Multi = struct { + logits: []const f32, + world: u32, + rows: usize, + segs: []const SegSpec, + batched: bool, + got: []u32, + whole: usize = 0, + /// rank 0, batched: vs_choose's mirror of each top_k segment over the one gather (row0-shifted draws), its rows + mirror: []i64 = &.{}, +}; + +/// The batched call's hook on rank 0: each top_k segment's device choice from the shared gather, then download. +const SegMirror = struct { + c: *Multi, + fn run(ctx: *anyopaque, gathered: u64, n: usize, k: usize, segs: []const pipe.Pipe.Seg, host: []u8) anyerror!bool { + _ = host; + const m: *SegMirror = @ptrCast(@alignCast(ctx)); + const W: usize = m.c.world; + const g: [*]const f32 = @ptrFromInt(gathered); + const a = testing.allocator; + const keys = try a.alloc(u64, W * k); + defer a.free(keys); + const work = try a.alloc(f64, 1024); + defer a.free(work); + const out = try a.alloc(i64, n); + defer a.free(out); + const r0 = segs[0].row0; + for (segs) |sg| { + if (serve.sampling.isNucleus(sg.s)) continue; + const off = sg.row0 - r0; + const count = pipe.countOf(sg.s, @intCast(m.c.logits.len / m.c.rows), serve.sampling.nucleus_count); + pipe.chooseMirror(keys, work, g[0 .. W * n * 2 * k], W, n, k, count, sg.s, sg.positions.?[0] -% off, out); + @memcpy(m.c.mirror[sg.row0..][0..sg.n], out[off..][0..sg.n]); + } + return false; + } +}; + +fn multiBody(comm: tp.collective.Collective, me: u32, c: *Multi) anyerror!void { + const a = testing.allocator; + const vocab = c.logits.len / c.rows; + const V = vocab / c.world; + const mine = try a.alloc(f32, c.rows * V); + defer a.free(mine); + for (0..c.rows) |r| @memcpy(mine[r * V ..][0..V], c.logits[r * vocab + me * V ..][0..V]); + var dev: pipe.HostDevice = .{ .gpa = a }; + defer dev.deinit(); + var p = try pipe.Pipe.init(a, comm, @intCast(V), serve.sampling.nucleus_count); + defer p.deinit(); + const lg: u64 = @intFromPtr(mine.ptr); + const out = try a.alloc(u32, c.rows); + defer a.free(out); + @memset(out, 0); + var whole: usize = 0; + if (c.batched) { + var segs: [8]pipe.Pipe.Seg = undefined; + for (c.segs, segs[0..c.segs.len]) |g, *x| { + try testing.expect(p.batchable(g.s)); + x.* = .{ .row0 = g.row0, .n = g.n, .s = g.s, .positions = if (me == 0) g.positions else null, .out = out[g.row0..][0..g.n] }; + } + var mh: SegMirror = .{ .c = c }; + try p.chooseSegments(dev.device(), lg, segs[0..c.segs.len], if (me == 0) .{ .ctx = &mh, .run = SegMirror.run } else null); + whole = p.whole_rows; + } else for (c.segs) |g| { + try p.choose(dev.device(), lg + 4 * @as(u64, g.row0) * V, g.n, g.s, if (me == 0) g.positions else null, out[g.row0..][0..g.n]); + whole += p.whole_rows; + } + if (me == 0) { + @memcpy(c.got, out); + c.whole = whole; + } +} + +test "a forward's keyed segments in one call (TF_DSV41_SAMP_BATCH) pick the tokens and keyed draws of one call a segment" { + const a = testing.allocator; + var prng = std.Random.DefaultPrng.init(4113); + const rnd = prng.random(); + const vocab = 4096; + const rows = 12; + const logits = try a.alloc(f32, rows * vocab); + defer a.free(logits); + for (0..rows) |r| for (0..vocab) |c| { + // rows 3 and 9 flat (their top-p mass needs more than the nucleus candidates: the whole-row fallback) + const x: f64 = if (r == 3 or r == 9) (if (c % 2 == 0) 0.25 else 0.0) else rnd.floatNorm(f64) * 3.0 + @as(f64, if (c == 13 * r + 5) 7 else 0); + logits[r * vocab + c] = @bitCast(@as(u32, @bitCast(@as(f32, @floatCast(x)))) & 0xFFFF_0000); + }; + const T = pipe.Sampling; + // row 5 belongs to no segment (another window's row); rows 3 and 9 (flat) take the nucleus fallback + const nucleus_segs = [_]SegSpec{ + .{ .row0 = 0, .n = 5, .s = T{ .seed = 7, .temperature = 0.7, .top_k = 0, .top_p = 0.95 }, .positions = &.{ 40, 41, 42, 43, 44 } }, + .{ .row0 = 6, .n = 2, .s = T{ .seed = 1 << 63 | 9, .temperature = 1.1, .top_k = 0, .top_p = 0.8, .min_p = 0.02 }, .positions = &.{ 900, 901 } }, + .{ .row0 = 8, .n = 4, .s = T{ .seed = 7, .temperature = 0.5, .top_k = 0, .top_p = 0.9 }, .positions = &.{ 3, 4, 5, 6 } }, + }; + // the served default (top_k 20 under top_p 0.95), other cuts, a wrapping draw start, and a nucleus segment + const topk_segs = [_]SegSpec{ + .{ .row0 = 0, .n = 5, .s = T{ .seed = 1234, .temperature = 0.7, .top_k = 20, .top_p = 0.95 }, .positions = &.{ 40, 41, 42, 43, 44 } }, + .{ .row0 = 6, .n = 2, .s = T{ .seed = 1 << 63 | 9, .temperature = 1.1, .top_k = 5, .top_p = 1.0, .min_p = 0.05 }, .positions = &.{ 2, 3 } }, + .{ .row0 = 8, .n = 3, .s = T{ .seed = 1234, .temperature = 0.7, .top_k = 0, .top_p = 0.9 }, .positions = &.{ 70, 71, 72 } }, + .{ .row0 = 11, .n = 1, .s = T{ .seed = 3, .temperature = 0.4, .top_k = 300, .top_p = 0.99 }, .positions = &.{500} }, + }; + for ([_][]const SegSpec{ &nucleus_segs, &topk_segs }) |segs| for ([_]u32{ 1, 2 }) |world| { + var got: [2][rows]u32 = undefined; + var whole: [2]usize = undefined; + var mirror: [rows]i64 = @splat(-2); + for ([_]bool{ false, true }, 0..) |batched, i| { + var c: Multi = .{ .logits = logits, .world = world, .rows = rows, .segs = segs, .batched = batched, .got = &got[i], .mirror = &mirror }; + try tp.host.run(world, &c, multiBody); + whole[i] = c.whole; + } + try testing.expectEqualSlices(u32, &got[0], &got[1]); + try testing.expectEqual(whole[0], whole[1]); + if (segs.ptr == &nucleus_segs) try testing.expect(whole[1] >= 2); // the flat rows took the fallback + // each top_k segment's device choice from the shared gather == its host pick + var mirrored: usize = 0; + for (segs) |g| if (!serve.sampling.isNucleus(g.s)) for (0..g.n) |j| { + try testing.expectEqual(@as(i64, got[1][g.row0 + j]), mirror[g.row0 + j]); + mirrored += 1; + }; + if (segs.ptr == &topk_segs) try testing.expect(mirrored == 8); + // each row's token is the keyed rule over its whole row (exact_sampling.choose at its own position) + for (segs) |g| for (0..g.n) |j| { + const r = g.row0 + j; + const vals = try a.alloc(f64, vocab); + defer a.free(vals); + const ids = try a.alloc(u64, vocab); + defer a.free(ids); + for (vals, ids, logits[r * vocab ..][0..vocab], 0..) |*v, *id, x, c| { + v.* = x; + id.* = c; + } + const want = try @import("lanes").sampling.choose(a, vals, ids, g.positions[j], g.s); + try testing.expectEqual(@as(u32, @intCast(want)), got[1][r]); + }; + }; +} diff --git a/zig/src/families/deepseek_v41/serve/root.zig b/zig/src/families/deepseek_v41/serve/root.zig new file mode 100644 index 0000000000..82ba338341 --- /dev/null +++ b/zig/src/families/deepseek_v41/serve/root.zig @@ -0,0 +1,8 @@ +//! DeepSeek-V4.1's host-only serving side: the exact sampler (zig/src/server). +pub const sampling = @import("sampling.zig"); +/// The committed goldens the serving tests read. +pub const fixtures = @import("dsv41_serve_fixtures"); + +test { + @import("std").testing.refAllDecls(@This()); +} diff --git a/zig/src/families/deepseek_v41/serve/sampling.zig b/zig/src/families/deepseek_v41/serve/sampling.zig new file mode 100644 index 0000000000..3903c558f2 --- /dev/null +++ b/zig/src/families/deepseek_v41/serve/sampling.zig @@ -0,0 +1,240 @@ +//! DeepSeek-V4.1's host token choice: rank 0 picks each row keyed by (seed, position, id), so drafted == serial. +const std = @import("std"); +const lanes = @import("lanes"); +const Allocator = std.mem.Allocator; + +extern "c" fn exp(x: f64) f64; +extern "c" fn log(x: f64) f64; + +pub const Sampling = lanes.Sampling; +pub const seedFor = lanes.sampling.seedFor; +const uniform = lanes.sampling.uniform; +const pairwiseSum = lanes.sampling.pairwiseSum; + +/// Candidates read beyond top_k, so tied values resolve by id on the host. +pub const margin: u32 = 8; +/// Candidates a nucleus row asks for (``TF_DSV41_NUCLEUS``'s default; 0 turns the nucleus path off). +pub const nucleus_count: u32 = 512; +/// A cumulative sum this close to top_p is decided on the whole row. +const guard = 1e-9; + +fn greedy(s: ?Sampling) bool { + return s == null or s.?.temperature <= 0; +} + +/// Top-p sampling without top-k, decided from candidates and the ranks' statistics. +pub fn isNucleus(s: ?Sampling) bool { + const x = s orelse return false; + return x.temperature > 0 and x.top_k == 0 and x.top_p > 0 and x.top_p < 1; +} + +/// Candidates a row of this sampling needs gathered; ``nucleus`` 0: no nucleus path. +pub fn candidateCount(s: ?Sampling, vocab: u32, nucleus: u32) u32 { + const k: u32 = if (greedy(s)) 1 + margin else if (s.?.top_k > 0) s.?.top_k +| margin else if (isNucleus(s) and nucleus > 0) nucleus else vocab; + return @min(k, vocab); +} + +/// One row's candidates, any order: fp32 logits and their vocabulary ids. +pub const Row = struct { + values: []const f32, + ids: []const u32, + /// Nucleus rows: each rank's (max logit, sum exp(v/T - max/T)) as f64, rank order; empty: whole row or a top_k cut. + stats: []const [2]f64 = &.{}, + /// The merged candidates kept (``candidateCount``); 0: all of them. + count: usize = 0, +}; + +/// A gathered candidate buffer: fp32 [world, rows, 2k], each rank's top k values then its ids as int32 bits. +pub const Candidates = struct { + data: []const f32, + world: u32, + rows: u32, + k: u32, + + /// Row ``r``'s candidates from every rank, rank order (unsorted). + pub fn row(c: Candidates, r: u32, values: []f32, ids: []u32) usize { + var n: usize = 0; + for (0..c.world) |w| { + const base = (w * c.rows + r) * 2 * c.k; + for (0..c.k) |j| { + values[n] = c.data[base + j]; + ids[n] = @bitCast(c.data[base + c.k + j]); + n += 1; + } + } + return n; + } +}; + +pub const Pick = union(enum) { + token: u32, + /// The candidates cannot decide this nucleus row: gather its whole vocabulary and pick again without stats. + full_row, +}; + +/// The sort key: larger value first (-0.0 as +0.0), then the lower id. +inline fn key(v: f32, id: u32) u64 { + const bits: u32 = @bitCast(v + 0.0); + const mono: u32 = if (bits & 0x8000_0000 == 0) bits | 0x8000_0000 else ~bits; + return @as(u64, mono) << 32 | (0xFFFF_FFFF - id); +} + +/// A thread's sampling scratch for rows up to ``capacity`` candidates (the vocabulary, for full rows). +pub const Picker = struct { + gpa: Allocator, + keys: []u64, + scaled: []f64, + work: []f64, + + pub fn init(gpa: Allocator, capacity: usize) Allocator.Error!Picker { + const keys = try gpa.alloc(u64, capacity); + errdefer gpa.free(keys); + const scaled = try gpa.alloc(f64, capacity); + errdefer gpa.free(scaled); + return .{ .gpa = gpa, .keys = keys, .scaled = scaled, .work = try gpa.alloc(f64, capacity) }; + } + + pub fn deinit(p: *Picker) void { + p.gpa.free(p.keys); + p.gpa.free(p.scaled); + p.gpa.free(p.work); + } + + /// Orders a row's candidates: ``keys`` holds them sorted, value and id recoverable. + fn merge(p: *Picker, r: Row) []u64 { + const n = r.values.len; + const keys = p.keys[0..n]; + for (keys, r.values, r.ids) |*k, v, id| k.* = key(v, id); + std.sort.pdq(u64, keys, {}, std.sort.desc(u64)); + return if (r.count > 0 and r.count < n) keys[0..r.count] else keys; + } + + inline fn valueOf(k: u64) f32 { + const mono: u32 = @truncate(k >> 32); + return @bitCast(if (mono & 0x8000_0000 != 0) mono & 0x7FFF_FFFF else ~mono); + } + + inline fn idOf(k: u64) u32 { + return 0xFFFF_FFFF - @as(u32, @truncate(k)); + } + + /// The token row ``r`` samples at absolute ``position``. + pub fn pick(p: *Picker, r: Row, position: u64, s: ?Sampling) Pick { + std.debug.assert(r.values.len == r.ids.len and r.values.len > 0 and r.values.len <= p.keys.len); + if (greedy(s)) { // the first merged candidate, without a sort + var best = key(r.values[0], r.ids[0]); + for (r.values[1..], r.ids[1..]) |v, id| best = @max(best, key(v, id)); + return .{ .token = idOf(best) }; + } + const x = s.?; + const keys = p.merge(r); + if (r.stats.len > 0 and isNucleus(s)) { + const n = p.keepCount(keys, x, r.stats) orelse return .full_row; + return .{ .token = p.keyed(keys[0..n], x, position) }; + } + return .{ .token = p.chooseRow(keys, x, position) }; + } + + /// The keyed choice on one merged row. + fn chooseRow(p: *Picker, keys: []const u64, s: Sampling, position: u64) u32 { + const width = keys.len; + const k = @max(1, @min(if (s.top_k != 0) @as(usize, s.top_k) else width, width)); + const t = @max(s.temperature, 1e-6); + const scaled = p.scaled[0..k]; + for (scaled, keys[0..k]) |*x, kk| x.* = @as(f64, valueOf(kk)) / t; + var keep = k; + if (s.top_p > 0 and s.top_p < 1) { + const probs = p.work[0..k]; + for (probs, scaled) |*q, x| q.* = exp(x - scaled[0]); + const total = pairwiseSum(probs); + var cum: f64 = 0; + var below: usize = 0; + for (probs) |q| { + cum += q / total; + if (cum < s.top_p) below += 1; + } + keep = below + 1; + } + const floor = scaled[0] + s.minLog(); + var best: usize = 0; + var best_score = -std.math.inf(f64); + for (scaled[0..@min(keep, k)], keys[0..@min(keep, k)], 0..) |x, kk, j| { + if (s.min_p > 0 and x < floor) continue; // ``choose``'s min_p cut + const score = x - log(-log(uniform(s.seed, position, idOf(kk)))); + if (j == 0 or score > best_score) { + best = j; + best_score = score; + } + } + return idOf(keys[best]); + } + + /// Tokens the nucleus row may sample, or null when its candidates cannot decide it. + fn keepCount(p: *Picker, keys: []const u64, s: Sampling, stats: []const [2]f64) ?usize { + const n = keys.len; + const t = @max(s.temperature, 1e-6); + var m: f64 = -std.math.inf(f64); + for (stats) |st| m = @max(m, st[0]); + var z: f64 = 0; + for (stats) |st| z += st[1] * exp(st[0] / t - m / t); + const top: f64 = valueOf(keys[0]); + if (n < 2 or !std.math.isFinite(top) or !std.math.isFinite(z) or z <= 0) return null; + if (top != m) return null; // the candidates' best is not the row's max + const scaled = p.scaled[0..n]; + for (scaled, keys) |*x, kk| x.* = @as(f64, valueOf(kk)) / t; + var cum: f64 = 0; + var below: usize = 0; + for (scaled) |x| { + cum += exp(x - scaled[0]) / z; + if (@abs(cum - s.top_p) <= guard) return null; + if (cum < s.top_p) below += 1; + } + var keep = below + 1; + if (keep >= n or !(valueOf(keys[keep - 1]) > valueOf(keys[n - 1]))) return null; + if (s.min_p > 0) { + const floor = scaled[0] + log(s.min_p); + var ok: usize = 0; + for (scaled) |x| ok += @intFromBool(x >= floor); + keep = @min(keep, ok); + } + return @max(1, keep); + } + + /// The nucleus keyed draw over a kept prefix. + fn keyed(p: *Picker, keys: []const u64, s: Sampling, position: u64) u32 { + _ = p; + const t = @max(s.temperature, 1e-6); + var best: usize = 0; + var best_score = -std.math.inf(f64); + for (keys, 0..) |kk, j| { + const score = @as(f64, valueOf(kk)) / t - log(-log(uniform(s.seed, position, idOf(kk)))); + if (j == 0 or score > best_score) { + best = j; + best_score = score; + } + } + return idOf(keys[best]); + } + + /// Every row of a window: ``positions[i]`` is row i's absolute position; ``out[i]`` its pick. + pub fn pickRows(p: *Picker, rows: []const Row, positions: []const u64, s: ?Sampling, out: []Pick) void { + for (rows, positions, out) |r, pos, *o| o.* = p.pick(r, pos, s); + } +}; + +/// Drafts kept from chain window ``[pending, d1, ...]``: d_i stays while it equals chosen[i - 1]; emits accepted + 1. +pub fn accept(window: []const u32, chosen: []const u32) usize { + const drafts = window[@min(1, window.len)..]; + const n = @min(drafts.len, chosen.len); // zip's length: a window has one row more than drafts + for (drafts[0..n], chosen[0..n], 0..) |d, c, i| if (d != c) return i; + return n; +} + +test "greedy ties go to the lower id and keys order -0.0 with +0.0" { + var p = try Picker.init(std.testing.allocator, 8); + defer p.deinit(); + const got = p.pick(.{ .values = &.{ 1.0, 3.0, 3.0, -0.0 }, .ids = &.{ 9, 7, 5, 1 } }, 0, null); + try std.testing.expectEqual(@as(u32, 5), got.token); + try std.testing.expect(key(-0.0, 4) > key(0.0, 6) and key(0.0, 4) == key(-0.0, 4)); + try std.testing.expectEqual(@as(usize, 2), accept(&.{ 10, 11, 12, 13 }, &.{ 11, 12, 99, 0 })); +} diff --git a/zig/src/families/deepseek_v41/serve_knobs.zig b/zig/src/families/deepseek_v41/serve_knobs.zig new file mode 100644 index 0000000000..169a3570a5 --- /dev/null +++ b/zig/src/families/deepseek_v41/serve_knobs.zig @@ -0,0 +1,288 @@ +//! The served engine knobs the Zig engine reads under their shared names; every rank reads the same environment. +const std = @import("std"); +const quiet = @import("builtin").is_test; // a test's stderr is the build runner's channel +const dk = @import("dsv41_kernels"); +const Config = @import("config.zig").Config; +const knob_types = @import("knob_types.zig"); +const PfDense = knob_types.PfDense; + +const pfd = dk.ops.pfd; + +pub const Error = error{ BadKnob, BadTable }; + +/// Reads one setting; null when unset or empty (the environment in `env`, a fixture in tests). +pub const Get = *const fn ([]const u8) ?[]const u8; + +/// TF_DSV41_PF_DENSE (off | fused; 0 / no / false = off): the fused path's settings, null when off. +pub fn pfDense(a: std.mem.Allocator, io: std.Io, get: Get) !?*const PfDense { + const raw = get("TF_DSV41_PF_DENSE") orelse return null; + const v = std.mem.trim(u8, raw, " "); + const off = [_][]const u8{ "off", "0", "no", "false" }; + for (off) |o| if (std.ascii.eqlIgnoreCase(v, o)) return null; + if (!std.ascii.eqlIgnoreCase(v, "fused")) { + if (!quiet) std.log.warn("TF_DSV41_PF_DENSE={s}: expected off or fused", .{raw}); + return error.BadKnob; + } + const p = try a.create(PfDense); + p.* = .{}; + if (get("TF_DSV41_PF_DENSE_CFG")) |c| p.override = PfDense.parseOverride(c) catch { + if (!quiet) std.log.warn("TF_DSV41_PF_DENSE_CFG={s}: expected id[,group], id 0-{d}, group 1-1024", .{ c, pfd.cfgs.len - 1 }); + return error.BadKnob; + }; + if (get("TF_DSV41_PF_DENSE_TABLE")) |path| { + const bytes = std.Io.Dir.cwd().readFileAlloc(io, path, a, .limited(1 << 24)) catch |e| { + if (!quiet) std.log.warn("TF_DSV41_PF_DENSE_TABLE={s}: {t}", .{ path, e }); + return e; + }; + p.entries = PfDense.parseTable(a, bytes) catch |e| { + if (!quiet) std.log.warn("TF_DSV41_PF_DENSE_TABLE={s}: not a pfdense tuning table ({t})", .{ path, e }); + return e; + }; + } + return p; +} + +/// pf4k.TARGET: the only prefill segment above 2,048 rows +pub const pf4k_rows: u32 = 4096; + +/// TF_DSV41_PF_4K ("0" or "1"): 4,096-row prefill segments, refused without TF_DSV41_PF_4K_MEMORY_OK=1. +pub fn pf4k(get: Get) !bool { + const raw = std.mem.trim(u8, get("TF_DSV41_PF_4K") orelse "0", " "); + if (std.mem.eql(u8, raw, "0")) return false; + if (!std.mem.eql(u8, raw, "1")) { + if (!quiet) std.log.warn("TF_DSV41_PF_4K={s}: 0 or 1", .{raw}); + return error.BadKnob; + } + const ok = std.mem.trim(u8, get("TF_DSV41_PF_4K_MEMORY_OK") orelse "", " "); + if (!std.mem.eql(u8, ok, "1")) { + if (!quiet) std.log.warn("TF_DSV41_PF_4K=1: 4,096-row prefill segments hold about 0.66 GiB more worker memory than 2,048-row ones; set TF_DSV41_PF_4K_MEMORY_OK=1 to acknowledge", .{}); + return error.BadKnob; + } + return true; +} + +/// A prompt segment's rows: min(TF_DSV41_PREFILL_CHUNK, _ROWS) on the 16-row grid, 32-2,048; 4,096 under PF_4K. +pub fn prefillRows(get: Get) !?u32 { + const big = try pf4k(get); + var rows: ?u32 = null; + for ([_][]const u8{ "TF_DSV41_PREFILL_CHUNK", "TF_DSV41_PREFILL_ROWS" }) |name| if (get(name)) |raw| { + const v = std.fmt.parseInt(u32, std.mem.trim(u8, raw, " "), 10) catch return badRows(name, raw, big); + if (big and v != pf4k_rows) return badRows(name, raw, big); + if (v < 32 or (v > 2048 and !big) or v % 16 != 0) return badRows(name, raw, big); + rows = if (rows) |r| @min(r, v) else v; + }; + return if (big) pf4k_rows else rows; +} + +fn badRows(name: []const u8, raw: []const u8, big: bool) error{BadKnob} { + if (!quiet) if (big) std.log.warn("{s}={s}: TF_DSV41_PF_4K=1 means 4,096-row segments (unset it or set 4096)", .{ name, raw }) else std.log.warn("{s}={s}: expected a multiple of 16 in 32..2048", .{ name, raw }); + return error.BadKnob; +} + +/// TF_DSV41_PF_OVERLAP (unset / 0 / 1: off): a prefill segment's two exchanges a layer in this many row pieces (2-8). +pub fn pfOverlap(get: Get) !i64 { + const raw = std.mem.trim(u8, get("TF_DSV41_PF_OVERLAP") orelse "0", " "); + const v = std.fmt.parseInt(i64, raw, 10) catch -1; + if (v < 0 or v > 8) { + if (!quiet) std.log.warn("TF_DSV41_PF_OVERLAP={s}: 0 (off) or row pieces 2-8", .{raw}); + return error.BadKnob; + } + return if (v < 2) 0 else v; +} + +/// TF_DSV41_MHC_SITE_ROWS (unset / 0 off, else 256-4,096, a multiple of 16): a prefill boundary site's rows a piece. +pub fn mhcSiteRows(get: Get) !i64 { + const raw = std.mem.trim(u8, get("TF_DSV41_MHC_SITE_ROWS") orelse "0", " "); + const v = std.fmt.parseInt(i64, raw, 10) catch -1; + if (v == 0) return 0; + if (v < 256 or v > 4096 or @rem(v, 16) != 0) { + if (!quiet) std.log.warn("TF_DSV41_MHC_SITE_ROWS={s}: 0 (off) or 256-4096, a multiple of 16", .{raw}); + return error.BadKnob; + } + return v; +} + +/// TF_DSV41_GM_GU2 (0 / 1, with TF_DSV41_GM_V2=1): a 4,096-row segment's routed gate / up at x3gm's 128-member tile. +pub fn gmGu2(get: Get) !bool { + const raw = std.mem.trim(u8, get("TF_DSV41_GM_GU2") orelse "0", " "); + if (std.mem.eql(u8, raw, "0")) return false; + if (std.mem.eql(u8, raw, "1")) return true; + if (!quiet) std.log.warn("TF_DSV41_GM_GU2={s}: 0 or 1", .{raw}); + return error.BadKnob; +} + +/// TF_DSV41_PF_OVERLAP_SITE (0 / 1, with PF_OVERLAP >= 2): every exchange piece forked, the next site in pieces. +pub fn pfOverlapSite(get: Get) !bool { + const raw = std.mem.trim(u8, get("TF_DSV41_PF_OVERLAP_SITE") orelse "0", " "); + if (std.mem.eql(u8, raw, "0")) return false; + if (std.mem.eql(u8, raw, "1")) return true; + if (!quiet) std.log.warn("TF_DSV41_PF_OVERLAP_SITE={s}: 0 or 1", .{raw}); + return error.BadKnob; +} + +/// TF_DSV41_PF_TBO (ours; 0 / 1, default 0): CED encoder segments two at a time on two streams (block_prefill.emitTbo). +pub fn pfTbo(get: Get) !bool { + const raw = std.mem.trim(u8, get("TF_DSV41_PF_TBO") orelse "0", " "); + if (std.mem.eql(u8, raw, "0")) return false; + if (std.mem.eql(u8, raw, "1")) return true; + if (!quiet) std.log.warn("TF_DSV41_PF_TBO={s}: 0 or 1", .{raw}); + return error.BadKnob; +} + +/// TF_DSV41_PF_TBO_GM_SMS (under TF_DSV41_PF_TBO): the SMs x3gm's persistent grid covers at most, 8-48; 0: every SM. +pub const tbo_gm_sms: u32 = 0; +pub fn pfTboGmSms(get: Get) !u32 { + const raw = std.mem.trim(u8, get("TF_DSV41_PF_TBO_GM_SMS") orelse return tbo_gm_sms, " "); + const v = std.fmt.parseInt(u32, raw, 10) catch return badSms(raw); + if (v != 0 and (v < 8 or v > 48)) return badSms(raw); + return v; +} + +fn badSms(raw: []const u8) error{BadKnob} { + if (!quiet) std.log.warn("TF_DSV41_PF_TBO_GM_SMS={s}: 0 (every SM) or 8-48", .{raw}); + return error.BadKnob; +} + +/// TF_DSV41_STREAM_RB (unset / 0 off, 1, 2 or 4): the stream top-k's twin: 1 `_stream_pf`, 2 / 4 `_stream_rb`. +pub fn streamRb(get: Get) !i64 { + const raw = std.mem.trim(u8, get("TF_DSV41_STREAM_RB") orelse "0", " "); + if (std.mem.eql(u8, raw, "0")) return 0; + if (std.mem.eql(u8, raw, "1")) return 1; + if (std.mem.eql(u8, raw, "2")) return 2; + if (std.mem.eql(u8, raw, "4")) return 4; + if (!quiet) std.log.warn("TF_DSV41_STREAM_RB={s}: 0, 1, 2 or 4", .{raw}); + return error.BadKnob; +} + +/// TF_DSV41_INDEX_BOUND (0 by default): decode windows' dense index scores as `_scores_b` (block.Options.index_bound). +pub fn indexBound(get: Get) !bool { + const raw = std.mem.trim(u8, get("TF_DSV41_INDEX_BOUND") orelse "0", " "); + if (std.mem.eql(u8, raw, "0")) return false; + if (std.mem.eql(u8, raw, "1")) return true; + if (!quiet) std.log.warn("TF_DSV41_INDEX_BOUND={s}: 0 or 1", .{raw}); + return error.BadKnob; +} + +/// TF_DSV41_PREFETCH_AHEAD (0 by default): the next prompt segment's Engram reads before this one runs. +pub fn prefetchAhead(get: Get) bool { + const v = get("TF_DSV41_PREFETCH_AHEAD") orelse return false; + return !std.mem.eql(u8, std.mem.trim(u8, v, " "), "0"); +} + +/// TF_DSV41_KV_NORM_STORE=1: KV-only RMS + SWA RoPE/FP8 store, default off. +pub fn kvNormStore(get: Get) bool { + const v = get("TF_DSV41_KV_NORM_STORE") orelse return false; + return std.mem.eql(u8, std.mem.trim(u8, v, " "), "1"); +} + +/// TF_DSV41_ROUTER_GROUP_ROT=1: histogram grouping + input rotation, default off. +pub fn routerGroupRot(get: Get) bool { + const v = get("TF_DSV41_ROUTER_GROUP_ROT") orelse return false; + return std.mem.eql(u8, std.mem.trim(u8, v, " "), "1"); +} + +/// TF_DSV41_X3LD_EPI (unset / 0 off, 1 on, else refused): the decode MoE's expert epilogues in x3ld's tail. +pub fn x3ldEpi(get: Get) !bool { + const raw = std.mem.trim(u8, get("TF_DSV41_X3LD_EPI") orelse "0", " "); + if (std.mem.eql(u8, raw, "0")) return false; + if (std.mem.eql(u8, raw, "1")) return true; + if (!quiet) std.log.warn("TF_DSV41_X3LD_EPI={s}: 0 or 1", .{raw}); + return error.BadKnob; +} + +/// The decode R1 set (ATTN_SPLIT=4 and seven switches at 1), or TF_DSV41_R1 (wins): R1 runs whole or not at all. +pub fn r1(get: Get) bool { + if (get("TF_DSV41_R1")) |v| return std.mem.eql(u8, std.mem.trim(u8, v, " "), "1"); + const names = [_][]const u8{ "TF_DSV41_ATTN_SPLIT", "TF_DSV41_ATTN_ROPE", "TF_DSV41_MHC_TAIL", "TF_DSV41_MHC_DEFER", "TF_DSV41_MOE_BF16", "TF_DSV41_PRUNE_KIT", "TF_DSV41_RG_PRUNE", "TF_DSV41_RMS_FOLD" }; + var set: usize = 0; + var served: usize = 0; + for (names, 0..) |n, i| if (get(n)) |raw| { + set += 1; + if (std.mem.eql(u8, std.mem.trim(u8, raw, " "), if (i == 0) "4" else "1")) served += 1; + }; + if (served == names.len) return true; + if (set > 0) if (!quiet) std.log.warn("decode R1: {d} of its {d} Python knobs at the reference's values; Zig runs R1 whole (TF_DSV41_R1=1, or all of ATTN_SPLIT=4 ATTN_ROPE MHC_TAIL MHC_DEFER MOE_BF16 PRUNE_KIT RG_PRUNE RMS_FOLD = 1) or not at all: off", .{ served, names.len }); + return false; +} + +/// TF_DSV41_MHC_PFDEC: unset / 0 off, else the fewest rows (1-64) a window's mixing mHC sites take mhc_pf at. +pub fn mhcPfRows(get: Get) !i64 { + const raw = std.mem.trim(u8, get("TF_DSV41_MHC_PFDEC") orelse return 0, " \t"); + const v = std.fmt.parseInt(i64, raw, 10) catch -1; + if (v < 0 or v > 64) { + if (!quiet) std.log.warn("TF_DSV41_MHC_PFDEC={s}: expected 0 (off) or the fewest rows, 1..64", .{raw}); + return error.BadKnob; + } + return v; +} + +/// TF_DSV41_INDEX_BUDGET_MIB (1-4,096): the indexer's scores and sort keys a launch; prefill row blocks follow it. +pub fn indexBudget(get: Get) !?i64 { + const raw = get("TF_DSV41_INDEX_BUDGET_MIB") orelse return null; + const v = std.fmt.parseInt(i64, std.mem.trim(u8, raw, " "), 10) catch return badBudget(raw); + if (v < 1 or v > 4096) return badBudget(raw); + return v << 20; +} + +fn badBudget(raw: []const u8) error{BadKnob} { + if (!quiet) std.log.warn("TF_DSV41_INDEX_BUDGET_MIB={s}: expected 1..4096", .{raw}); + return error.BadKnob; +} + +/// block_prefill's stream threshold (TF_DSV41_INDEX_STREAM_MIN, 4,096 visible keys): the emitter's constant. +pub const stream_min: i64 = 4096; + +/// The long-prompt stream top-k's scratch the buffer plan holds for `limit` positions and `rows`-row segments. +pub fn streamScratch(cfg: *const Config, limit: i64, rows: i64) u64 { + const longpf = @import("longpf.zig"); + const k: i64 = cfg.index_topk; + if (rows <= 16 or @popCount(k) != 1) return 0; + var most: i64 = 0; + for (0..cfg.layers) |i| { + const L: u32 = @intCast(i); + if (cfg.mode(L) != .full or !cfg.isIndexSource(L) or cfg.isCandidateSource(L)) continue; + const keys = @divFloor(limit, cfg.compressRatio(L)); + if (keys < stream_min) continue; + most = @max(most, longpf.plan(rows, keys, k) * longpf.splits(keys) * 2 * k * 8); + } + return @intCast(most); +} + +pub const PrefillMode = enum { full, replay }; + +/// TF_DSV41_PREFILL: the prompt's prefill mode (full | replay); unset is `full`. +pub fn prefillMode(get: Get) !PrefillMode { + const raw = get("TF_DSV41_PREFILL") orelse return .full; + return std.meta.stringToEnum(PrefillMode, std.mem.trim(u8, raw, " ")) orelse { + if (!quiet) std.log.warn("TF_DSV41_PREFILL={s}: expected full or replay", .{raw}); + return error.BadKnob; + }; +} + +/// Where a served prompt's last token runs (TF_DSV41_PROMPT_TAIL): `verify` as a decode row (default), or `prefill`. +pub const PromptTail = enum { verify, prefill }; + +pub fn promptTail(get: Get) !PromptTail { + const raw = get("TF_DSV41_PROMPT_TAIL") orelse return .verify; + return std.meta.stringToEnum(PromptTail, std.mem.trim(u8, raw, " ")) orelse { + if (!quiet) std.log.warn("TF_DSV41_PROMPT_TAIL={s}: expected verify or prefill", .{raw}); + return error.BadKnob; + }; +} + +/// A prompt of `n` rows in `tail`'s shape: the rows through the prefill, then the rows of one decode window. +pub fn promptSplit(n: usize, tail: PromptTail) struct { prefill: usize, window: usize } { + return switch (tail) { + .prefill => .{ .prefill = n, .window = 0 }, + .verify => .{ .prefill = n -| 1, .window = @min(n, 1) }, + }; +} + +pub fn env(name: []const u8) ?[]const u8 { + var buf: [64]u8 = undefined; + if (name.len >= buf.len) return null; + @memcpy(buf[0..name.len], name); + buf[name.len] = 0; + const v = std.mem.span(std.c.getenv(buf[0..name.len :0]) orelse return null); + return if (v.len == 0) null else v; +} diff --git a/zig/src/families/deepseek_v41/stage.zig b/zig/src/families/deepseek_v41/stage.zig new file mode 100644 index 0000000000..726618d9b1 --- /dev/null +++ b/zig/src/families/deepseek_v41/stage.zig @@ -0,0 +1,148 @@ +//! A rank's EXL3 bytes read into host images the device buffers copy as is: group parts and ragged routed experts. + +const std = @import("std"); +const exl3 = @import("exl3.zig"); +const plan = @import("plan.zig"); +const Pack = @import("pack.zig").Pack; +const Io = std.Io; + +/// One group's part: trellis int16 [kt, nt, words], suh fp16 [K part], svh fp16 [N part]. +pub const GroupImage = struct { + trellis: []u8, + suh: []u8, + svh: []u8, + + pub fn deinit(g: *GroupImage, gpa: std.mem.Allocator) void { + gpa.free(g.trellis); + gpa.free(g.suh); + gpa.free(g.svh); + g.* = undefined; + } +}; + +pub fn group(gpa: std.mem.Allocator, io: Io, pack: *const Pack, g: plan.GroupPlan) !GroupImage { + var out: GroupImage = .{ .trellis = &.{}, .suh = &.{}, .svh = &.{} }; + errdefer out.deinit(gpa); + var buf: [256]u8 = undefined; + out.trellis = try gpa.alloc(u8, @intCast(g.part.trellis.bytes())); + try pack.read(io, try pack.need(try std.fmt.bufPrint(&buf, "{s}.trellis", .{g.prefix})), g.part.trellis, out.trellis); + out.suh = try gpa.alloc(u8, @intCast(g.part.suh.bytes())); + try pack.read(io, try pack.need(try std.fmt.bufPrint(&buf, "{s}.suh", .{g.prefix})), g.part.suh, out.suh); + out.svh = try gpa.alloc(u8, @intCast(g.part.svh.bytes())); + try pack.read(io, try pack.need(try std.fmt.bufPrint(&buf, "{s}.svh", .{g.prefix})), g.part.svh, out.svh); + return out; +} + +/// A routed projection: experts' trellises back to back, suh [E, K part] and svh [E, N part] stacked, K2 per expert. +pub const ExpertsImage = struct { + trellis: []align(16) u8, + suh: []u8, + svh: []u8, + /// the kernels' int32 K2 table + k2: []i32, + /// byte offset of each expert's trellis in `trellis` (the int64 address table is the device base plus these) + offsets: []u64, + + pub fn deinit(e: *ExpertsImage, gpa: std.mem.Allocator) void { + gpa.free(e.trellis); + gpa.free(e.suh); + gpa.free(e.svh); + gpa.free(e.k2); + gpa.free(e.offsets); + e.* = undefined; + } +}; + +pub fn experts(gpa: std.mem.Allocator, io: Io, pack: *const Pack, x: *const plan.ExpertsPlan) !ExpertsImage { + const n = x.layout.count(); + const kpart = 2 * @as(u64, x.layout.k_tiles) * 16; + const npart = 2 * @as(u64, x.layout.n_tiles) * 16; + var out: ExpertsImage = .{ .trellis = &.{}, .suh = &.{}, .svh = &.{}, .k2 = &.{}, .offsets = &.{} }; + errdefer out.deinit(gpa); + out.trellis = try gpa.alignedAlloc(u8, .@"16", @intCast(x.layout.trellisBytes())); + out.suh = try gpa.alloc(u8, @intCast(n * kpart)); + out.svh = try gpa.alloc(u8, @intCast(n * npart)); + out.k2 = try gpa.alloc(i32, n); + out.offsets = try gpa.alloc(u64, n); + var buf: [256]u8 = undefined; + for (x.parts, x.prefixes, 0..) |p, pre, e| { + const at = x.layout.byteOffset(e); + out.offsets[e] = at; + out.k2[e] = @intCast(p.group.k2()); + const t = out.trellis[@intCast(at)..][0..@intCast(p.trellis.bytes())]; + try pack.read(io, try pack.need(try std.fmt.bufPrint(&buf, "{s}.trellis", .{pre})), p.trellis, t); + try pack.read(io, try pack.need(try std.fmt.bufPrint(&buf, "{s}.suh", .{pre})), p.suh, out.suh[@intCast(e * kpart)..][0..@intCast(kpart)]); + try pack.read(io, try pack.need(try std.fmt.bufPrint(&buf, "{s}.svh", .{pre})), p.svh, out.svh[@intCast(e * npart)..][0..@intCast(npart)]); + } + return out; +} + +const testing = std.testing; +const image = @import("pack.zig").image; + +test "staged images: a column part's runs, ragged experts back to back, stacked scales" { + const a = testing.allocator; + const io = testing.io; + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + // three experts of a gate projection (K 32, N 256): expert 1 at 3 bits, the others at 2 + const words = [_]usize{ 32, 48, 32 }; + var specs: [12]@import("pack.zig").Spec = undefined; + var nb: [12][48]u8 = undefined; + const shapes = [3][3]usize{ .{ 2, 16, words[0] }, .{ 2, 16, words[1] }, .{ 2, 16, words[2] } }; + for (0..3) |e| { + const base = e * 4; + specs[base] = .{ .name = try std.fmt.bufPrint(&nb[base], "layers.0.ffn.experts.{d}.w1.trellis", .{e}), .dtype = "I16", .shape = &shapes[e] }; + specs[base + 1] = .{ .name = try std.fmt.bufPrint(&nb[base + 1], "layers.0.ffn.experts.{d}.w1.suh", .{e}), .dtype = "F16", .shape = &.{32} }; + specs[base + 2] = .{ .name = try std.fmt.bufPrint(&nb[base + 2], "layers.0.ffn.experts.{d}.w1.svh", .{e}), .dtype = "F16", .shape = &.{256} }; + specs[base + 3] = .{ .name = try std.fmt.bufPrint(&nb[base + 3], "layers.0.ffn.experts.{d}.w1.mul1", .{e}), .dtype = "I32", .shape = &.{} }; + } + const file = try image(a, &specs); + defer a.free(file); + // every byte of expert e's tensors: trellis tile (kt, nt) = 64 e + 16 kt + nt; svh element j's low byte = j + const hl = std.mem.readInt(u64, file[0..8], .little); + var pos: usize = 8 + hl; + for (0..3) |e| { + const tile = 2 * words[e]; + for (0..2) |kt| for (0..16) |nt| @memset(file[pos + (kt * 16 + nt) * tile ..][0..tile], @intCast(64 * e + 16 * kt + nt)); + pos += 2 * 16 * tile; + @memset(file[pos..][0..64], @intCast(200 + e)); + pos += 64; + for (0..256) |j| file[pos + 2 * j] = @intCast(j); + pos += 512 + 4; + } + try tmp.dir.writeFile(io, .{ .sub_path = "x.safetensors", .data = file }); + const dir = try tmp.dir.realPathFileAlloc(io, ".", a); + defer a.free(dir); + var pack = try Pack.open(a, io, dir); + defer pack.deinit(); + var arena = std.heap.ArenaAllocator.init(a); + defer arena.deinit(); + var parts: [3]exl3.Part = undefined; + var prefixes: [3][]const u8 = undefined; + for (0..3) |e| { + var buf: [128]u8 = undefined; + prefixes[e] = try std.fmt.bufPrint(&nb[e], "layers.0.ffn.experts.{d}.w1", .{e}); + parts[e] = try (try pack.group(prefixes[e], &buf)).part(.col, 1, 2); + } + const x: plan.ExpertsPlan = .{ .proj = .w1, .parts = &parts, .prefixes = &prefixes, .layout = try exl3.Layout.of(arena.allocator(), &parts) }; + var img = try experts(a, io, &pack, &x); + defer img.deinit(a); + try testing.expectEqualSlices(i32, &.{ 4, 6, 4 }, img.k2); + try testing.expectEqualSlices(u64, &.{ 0, 2 * 8 * 64, 2 * 8 * 64 + 2 * 8 * 96 }, img.offsets); + // rank 1's N tiles are 8..15 of each K-tile row + for (0..3) |e| for (0..2) |kt| for (0..8) |j| { + const tile = 2 * words[e]; + try testing.expectEqual(@as(u8, @intCast(64 * e + 16 * kt + 8 + j)), img.trellis[@intCast(img.offsets[e] + (kt * 8 + j) * tile)]); + }; + // suh whole (a column split keeps K), svh this rank's 128 columns + try testing.expectEqual(@as(u8, 201), img.suh[64]); + try testing.expectEqual(@as(u8, 128), img.svh[0]); + try testing.expectEqual(@as(u8, 128), img.svh[256 + 0]); + try testing.expectEqual(@as(usize, 3 * 256), img.svh.len); + var buf: [128]u8 = undefined; + const gp: plan.GroupPlan = .{ .proj = .wq_a, .prefix = prefixes[1], .split = .row, .full = try pack.group(prefixes[1], &buf), .part = try (try pack.group(prefixes[1], &buf)).part(.whole, 0, 2), .marker = null }; + var gi = try group(a, io, &pack, gp); + defer gi.deinit(a); + try testing.expectEqual(@as(u8, 64 + 16 + 15), gi.trellis[(16 + 15) * 96]); +} diff --git a/zig/src/families/deepseek_v41/streamrb_test.zig b/zig/src/families/deepseek_v41/streamrb_test.zig new file mode 100644 index 0000000000..b22a75cb85 --- /dev/null +++ b/zig/src/families/deepseek_v41/streamrb_test.zig @@ -0,0 +1,93 @@ +//! Host test of TF_DSV41_STREAM_RB: the reader, and the long-prefill program with `_stream` as `_stream_rb`. +const std = @import("std"); +const testing = std.testing; +const calls = @import("block_calls.zig"); +const block = @import("block.zig"); +const bp = @import("block_prefill.zig"); +const check = @import("calls_check.zig"); +const pk = @import("serve_knobs.zig"); +const Config = @import("config.zig").Config; + +const Fake = struct { + var pairs: []const [2][]const u8 = &.{}; + fn get(name: []const u8) ?[]const u8 { + for (pairs) |p| if (std.mem.eql(u8, p[0], name)) return p[1]; + return null; + } +}; + +test "TF_DSV41_STREAM_RB: 0 / 1 / 2 / 4" { + defer Fake.pairs = &.{}; + Fake.pairs = &.{}; + try testing.expectEqual(@as(i64, 0), try pk.streamRb(&Fake.get)); + Fake.pairs = &.{.{ "TF_DSV41_STREAM_RB", "1" }}; + try testing.expectEqual(@as(i64, 1), try pk.streamRb(&Fake.get)); + Fake.pairs = &.{.{ "TF_DSV41_STREAM_RB", "2" }}; + try testing.expectEqual(@as(i64, 2), try pk.streamRb(&Fake.get)); + Fake.pairs = &.{.{ "TF_DSV41_STREAM_RB", "4" }}; + try testing.expectEqual(@as(i64, 4), try pk.streamRb(&Fake.get)); + Fake.pairs = &.{.{ "TF_DSV41_STREAM_RB", "3" }}; + try testing.expectError(error.BadKnob, pk.streamRb(&Fake.get)); +} + +fn arg(c: calls.Call, name: []const u8) ?calls.Arg { + for (c.args) |x| if (std.mem.eql(u8, x.name, name)) return x.arg; + return null; +} + +test "TF_DSV41_STREAM_RB on the long-context prefill: _stream_rb in place of _stream, the same tensors and merge" { + var arena = std.heap.ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const aa = arena.allocator(); + const cfg: Config = .{}; + var w: block.Widths = .{}; + try check.parseDense(aa, &w, @embedFile("fixtures/dense-k2.txt")); + try check.parseExperts(aa, &w, @embedFile("fixtures/expert-k2.txt")); + var enc: [21]u32 = undefined; + for (&enc, 0..) |*l, i| l.* = @intCast(i); + const base: block.Options = .{ .gm_v2 = .one, .limit = 1 << 20, .rope_rows = (1 << 20) + 2048, .index_budget = 64 << 20 }; + for ([_]i64{ 2048, 2047, 300 }) |n| for ([_]i64{ 2 * 4096, 131072 - 2048 }) |start| { + const off = try bp.emitReplay(aa, &cfg, &w, base, &enc, n, start, .encoder); + var streams: usize = 0; + for (off) |c| streams += @intFromBool(std.mem.eql(u8, c.name, "_stream")); + try testing.expect(streams > 0); // layers 2 / 8 / 14 past 4,096 keys + for ([_]i64{ 1, 2, 4 }) |rb| { + var o = base; + o.stream_rb = rb; + const on = try bp.emitReplay(aa, &cfg, &w, o, &enc, n, start, .encoder); + try testing.expectEqual(off.len, on.len); + const want = switch (rb) { + 1 => "_stream_pf", + 2 => "_stream_rb2", + else => "_stream_rb4", + }; + var seen: usize = 0; + for (off, on) |x, y| { + if (!std.mem.eql(u8, x.name, "_stream")) { + try testing.expectEqualStrings(x.name, y.name); + try testing.expectEqual(x.grid, y.grid); + continue; + } + try testing.expectEqual(@as(i64, 0), arg(x, "MODE").?.i); + try testing.expectEqualStrings(want, y.name); + try testing.expectEqual(@divFloor(x.grid[0] + rb - 1, rb), y.grid[0]); + try testing.expectEqual(x.grid[1], y.grid[1]); + try testing.expectEqual(x.grid[0], arg(y, "R").?.i); + for ([_][]const u8{ "QI", "W", "IK", "POS", "BUF", "PT" }) |nm| { + const u = arg(x, nm).?; + const v = arg(y, nm).?; + try testing.expectEqual(std.meta.activeTag(u), std.meta.activeTag(v)); + if (u != .t) continue; // PT without a paged pool + try testing.expectEqual(u.t.offset, v.t.offset); + try testing.expectEqualSlices(i64, u.t.shape, v.t.shape); + try testing.expectEqual(u.t.dt, v.t.dt); + } + for ([_][]const u8{ "w_stride", "nsplit", "RATIO", "H", "D", "BP", "SPLIT", "K", "CAP", "PSH" }) |nm| try testing.expectEqual(arg(x, nm).?.i, arg(y, nm).?.i); + try testing.expectEqual(arg(x, "WS").?.f, arg(y, "WS").?.f); + try testing.expectEqual(arg(x, "SCALE").?.f, arg(y, "SCALE").?.f); + seen += 1; + } + try testing.expectEqual(streams, seen); + } + }; +} diff --git a/zig/src/families/deepseek_v41/triton_call.zig b/zig/src/families/deepseek_v41/triton_call.zig new file mode 100644 index 0000000000..191378d970 --- /dev/null +++ b/zig/src/families/deepseek_v41/triton_call.zig @@ -0,0 +1,61 @@ +//! A block.zig Triton call as the AOT set takes it: runtime args vs constexprs, so `Set.find` picks Triton's variant. + +const std = @import("std"); +const cuda = @import("cuda"); +const calls = @import("block_calls.zig"); +const aot = cuda.aot; + +/// Triton's pointer type of a dtype ("*bf16" ...). +pub fn ptrType(dt: calls.Dt) []const u8 { + return switch (dt) { + .bf16 => "*bf16", + .f16 => "*fp16", + .f32 => "*fp32", + .f64 => "*fp64", + .i8 => "*i8", + .u8 => "*u8", + .i16 => "*i16", + .i32 => "*i32", + .i64 => "*i64", + .bool => "*i1", + }; +} + +pub const Split = struct { args: []aot.Arg, consts: []aot.Const }; + +/// The device address of a tensor argument (the caller's buffers; 0 for an empty one). +pub const Address = struct { + ctx: *anyopaque, + of: *const fn (ctx: *anyopaque, t: calls.Tensor) anyerror!u64, +}; + +/// Splits `c`'s named arguments over `variants` (every compiled variant of `c.name`). +pub fn split(a: std.mem.Allocator, c: *const calls.Call, variants: []const aot.Spec, addr: Address) !Split { + var args: std.ArrayList(aot.Arg) = .empty; + var consts: std.ArrayList(aot.Const) = .empty; + for (c.args) |x| { + const param = for (variants) |v| { + const p = for (v.params) |p| { + if (std.mem.eql(u8, p.name, x.name)) break p; + } else continue; + break p; + } else null; + switch (x.arg) { + .none => {}, + .t, .opaque_table => |t| try args.append(a, aot.ptr(x.name, ptrType(t.dt), try addr.of(addr.ctx, t))), + .b => |v| try consts.append(a, aot.ci(x.name, @intFromBool(v))), + .i => |v| if (param) |p| { + if (std.mem.eql(u8, p.type, "i32")) { + try args.append(a, aot.int(x.name, std.math.cast(i32, v) orelse return error.IntRange)); + } else if (std.mem.eql(u8, p.type, "u64") or std.mem.eql(u8, p.type, "i64")) { + try args.append(a, aot.word(x.name, @bitCast(v))); + } else return error.UnsupportedParam; + } else try consts.append(a, aot.ci(x.name, v)), + .f => |v| if (param != null) { + try args.append(a, aot.float(x.name, @floatCast(v))); + } else try consts.append(a, aot.cf(x.name, @floatCast(v))), + .list => return error.UnsupportedArg, + } + } + return .{ .args = args.items, .consts = consts.items }; +} diff --git a/zig/src/families/deepseek_v41/triton_capture.zig b/zig/src/families/deepseek_v41/triton_capture.zig new file mode 100644 index 0000000000..05ac448ecf --- /dev/null +++ b/zig/src/families/deepseek_v41/triton_capture.zig @@ -0,0 +1,121 @@ +//! The reference capture's Triton cubins (aot/aot.json, by hash) loaded one by one; launches packed as Triton does. + +const std = @import("std"); +const cuda = @import("cuda"); +const Value = std.json.Value; + +pub const Param = struct { name: []const u8, ty: []const u8 }; + +pub const Kernel = struct { + kernel: cuda.triton.Kernel, + params: []Param, + global_scratch: u32, + global_align: u32, +}; + +pub const Set = struct { + arena: std.heap.ArenaAllocator, + kernels: std.StringHashMapUnmanaged(Kernel) = .empty, + /// hashes whose cubin did not load, with the reason + failed: std.ArrayList([2][]const u8) = .empty, + + pub fn deinit(s: *Set) void { + var it = s.kernels.valueIterator(); + while (it.next()) |k| k.kernel.unload(); + s.arena.deinit(); + s.* = undefined; + } + + /// Every kernel `dir`/aot.json lists; one that does not load is recorded in `failed`, the rest stay usable. + pub fn load(gpa: std.mem.Allocator, io: std.Io, d: *const cuda.Driver, device: cuda.abi.Device, dir: []const u8) !Set { + var s: Set = .{ .arena = std.heap.ArenaAllocator.init(gpa) }; + errdefer s.deinit(); + const a = s.arena.allocator(); + const text = try std.Io.Dir.cwd().readFileAlloc(io, try std.fs.path.join(a, &.{ dir, "aot.json" }), a, .limited(1 << 26)); + const root = try std.json.parseFromSliceLeaky(Value, a, text, .{}); + for (root.object.get("kernels").?.array.items) |kv| { + const k = kv.object; + const hash = k.get("hash").?.string; + const name = k.get("name").?.string; + const file = try std.fmt.allocPrint(a, "{s}/cubins/{s}.cubin", .{ dir, hash }); + const cubin = std.Io.Dir.cwd().readFileAllocOptions(io, file, gpa, .limited(1 << 26), .@"16", null) catch |e| { + try s.failed.append(a, .{ hash, @errorName(e) }); + continue; + }; + defer gpa.free(cubin); + const meta: cuda.triton.Meta = .{ + .name = name, + .num_warps = @intCast(k.get("num_warps").?.integer), + .num_ctas = @intCast(k.get("num_ctas").?.integer), + .shared = @intCast(k.get("shared").?.integer), + .global_scratch_size = @intCast(k.get("global_scratch").?.integer), + .global_scratch_align = @intCast(k.get("global_align").?.integer), + .profile_scratch_size = @intCast(k.get("profile_scratch").?.integer), + .launch_pdl = k.get("pdl").?.bool, + }; + const kernel = cuda.triton.Kernel.load(d, device, cubin, meta, try a.dupeSentinel(u8, name, 0)) catch |e| { + try s.failed.append(a, .{ hash, @errorName(e) }); + continue; + }; + const ps = k.get("params").?.array.items; + const params = try a.alloc(Param, ps.len); + for (ps, params) |p, *out| out.* = .{ .name = p.object.get("name").?.string, .ty = p.object.get("type").?.string }; + try s.kernels.put(a, hash, .{ .kernel = kernel, .params = params, .global_scratch = meta.global_scratch_size, .global_align = meta.global_scratch_align }); + } + return s; + } +}; + +/// A captured scalar's bits as the parameter's C type (Triton's ty_to_cpp: i1 is int8, floats by their bit patterns). +fn scalar(args: *cuda.Args, ty: []const u8, v: Value) !void { + const T = enum { i1, i8, i16, i32, i64, u8, u16, u32, u64, fp32, fp64 }; + const t = std.meta.stringToEnum(T, ty) orelse return error.UnsupportedScalar; + const o = v.object; + const kind = o.get("t").?.string; + if (t == .fp32 or t == .fp64) { + if (!std.mem.eql(u8, kind, "float")) return error.WrongScalar; + const bits = o.get(if (t == .fp32) "f32" else "f64").?.string; + const x = try std.fmt.parseInt(u64, bits[2..], 16); + if (t == .fp32) args.add(@as(u32, @intCast(x))) else args.add(x); + return; + } + const n: i64 = if (std.mem.eql(u8, kind, "bool")) @intFromBool(o.get("v").?.bool) else if (std.mem.eql(u8, kind, "int")) o.get("v").?.integer else return error.WrongScalar; + switch (t) { + .i1, .i8 => args.add(@as(i8, @intCast(n))), + .u8 => args.add(@as(u8, @intCast(n))), + .i16 => args.add(@as(i16, @intCast(n))), + .u16 => args.add(@as(u16, @intCast(n))), + .i32 => args.add(@as(i32, @intCast(n))), + .u32 => args.add(@as(u32, @intCast(n))), + .i64 => args.add(n), + .u64 => args.add(@as(u64, @bitCast(n))), + .fp32, .fp64 => unreachable, + } +} + +/// The address of a tensor argument (`addr`), the int it carries, or null for None. +pub const Resolve = *const fn (ctx: *anyopaque, arg: Value) anyerror!?u64; + +/// Packs `op`'s named arguments in runtime order and launches it on the captured grid (global scratch: `scratch`). +pub fn launch(k: *const Kernel, op: std.json.ObjectMap, stream: cuda.Stream, ctx: *anyopaque, resolve: Resolve, scratch: u64) !void { + var args: cuda.Args = .{}; + const given = op.get("args").?.array.items; + for (k.params) |p| { + const arg = for (given) |g| { + if (std.mem.eql(u8, g.object.get("name").?.string, p.name)) break g; + } else return error.MissingArgument; + if (p.ty[0] == '*') { + args.add(@as(u64, (try resolve(ctx, arg)) orelse 0)); + } else try scalar(&args, p.ty, arg); + } + const g = op.get("grid").?.array.items; + const grid: cuda.Dim3 = .{ .x = @intCast(g[0].integer), .y = @intCast(g[1].integer), .z = @intCast(g[2].integer) }; + try k.kernel.launchOn(grid, stream, &args, .{ .global = scratch }, &.{}); +} + +/// Global scratch bytes a launch of `op` needs (Triton: blocks x CTAs x the kernel's size). +pub fn scratchBytes(k: *const Kernel, op: std.json.ObjectMap) usize { + if (k.global_scratch == 0) return 0; + const g = op.get("grid").?.array.items; + return k.kernel.globalScratchBytes(.{ .x = @intCast(g[0].integer), .y = @intCast(g[1].integer), .z = @intCast(g[2].integer) }); +} diff --git a/zig/src/server/anthropic.zig b/zig/src/server/anthropic.zig index 3f0f7f72ee..bf6085416a 100644 --- a/zig/src/server/anthropic.zig +++ b/zig/src/server/anthropic.zig @@ -105,7 +105,7 @@ const Wire = struct { payload: Value = .null, fn out(w: *Wire) openai.Out { - return .{ .ctx = w, .vt = &.{ .open = open, .event = event, .reply = whole } }; + return .{ .ctx = w, .vt = &.{ .open = open, .event = event, .reply = whole, .keepalive = ping } }; } fn open(ctx: *anyopaque, prompt_len: usize) error{Closed}!void { @@ -130,6 +130,12 @@ const Wire = struct { w.status = status; w.payload = payload; } + + /// Anthropic's own keepalive event. + fn ping(ctx: *anyopaque) error{Closed}!void { + const w: *Wire = @ptrCast(@alignCast(ctx)); + return w.reply.send("ping", try w.reply.obj("ping")); + } }; const Call = struct { id: []const u8 = "", name: []const u8 = "", arguments: []const u8 = "", sent: usize = 0, done: bool = false }; diff --git a/zig/src/server/chat.zig b/zig/src/server/chat.zig index 47ec679be7..8edf497c10 100644 --- a/zig/src/server/chat.zig +++ b/zig/src/server/chat.zig @@ -262,6 +262,7 @@ const Generation = struct { streaming_done: bool = false, first_ns: ?i96 = null, last_ns: ?i96 = null, + wrote_ns: ?i96 = null, reason: ?[]const u8 = null, // the server ended the reply (a stop string, the length) before the engine said so engine_done: bool = false, consumed: usize = 0, @@ -350,6 +351,7 @@ const Generation = struct { g.cancel(); return error.Cancelled; }; + g.wrote_ns = nowNs(g.srv.io); } /// End the request and wait for the engine; unfinished requests count as disconnects. @@ -395,6 +397,15 @@ const Generation = struct { g.cancel(); return error.Cancelled; } + if (chunk == null and @hasDecl(@TypeOf(gone), "beat")) { + const now = nowNs(g.srv.io); + const since = g.wrote_ns orelse now; + if (g.wrote_ns == null) g.wrote_ns = now; + if (gone.beat(now - since) catch { + g.cancel(); + return error.Cancelled; + }) g.wrote_ns = now; + } const tokens = chunk orelse continue; if (g.reason == null) try g.commit(tokens); } diff --git a/zig/src/server/drain_test.zig b/zig/src/server/drain_test.zig new file mode 100644 index 0000000000..71b9514baf --- /dev/null +++ b/zig/src/server/drain_test.zig @@ -0,0 +1,379 @@ +//! A stop's drain over HTTP: serve.run with a real SIGTERM, a slow stub engine, bytes as tokens. +const std = @import("std"); +const api = @import("engine_api"); +const json = @import("json"); +const model_text = @import("model_text.zig"); +const serve = @import("serve.zig"); +const cli = @import("cli.zig"); +const routes = @import("routes.zig"); +const Allocator = std.mem.Allocator; +const testing = std.testing; + +/// Stops mid-reply: "@short" ends after 12 tokens, "@long" when halted or cancelled, "@hold" waits for `release`. +pub const Slow = struct { + gpa: Allocator, + io: std.Io, + halted: std.atomic.Value(bool) = .init(false), + reason: []const u8 = "", + active: std.atomic.Value(u32) = .init(0), + halts: u32 = 0, + closes: u32 = 0, + /// halts before the first close (the halt must come first) + halts_at_close: u32 = 0, + cancelled: std.atomic.Value(u64) = .init(0), + /// "@hold" replies stay silent until this is set + release: std.atomic.Value(bool) = .init(false), + + fn engine(s: *Slow) api.Engine { + return .{ .ctx = s, .vtable = &.{ .info = info, .submit = submit, .cancel = cancel, .status = status, .memory = memory } }; + } + + fn self(ctx: *anyopaque) *Slow { + return @ptrCast(@alignCast(ctx)); + } + + fn info(_: *anyopaque) api.Info { + return .{ .context_window = 4096, .name = "slow" }; + } + + fn cancel(ctx: *anyopaque, id: api.Id) void { + self(ctx).cancelled.store(id, .release); + } + + fn status(ctx: *anyopaque, out: *api.Status, _: []u32) void { + out.* = .{ .running = self(ctx).active.load(.acquire) }; + } + + fn memory(_: *anyopaque, _: bool) ?api.Memory { + return null; + } + + fn submit(ctx: *anyopaque, id: api.Id, r: *const api.Request, sink: api.Sink) api.SubmitError!void { + const s = self(ctx); + _ = s.active.fetchAdd(1, .acq_rel); + const t = std.Thread.spawn(.{}, run, .{ s, id, r, sink }) catch { + _ = s.active.fetchSub(1, .acq_rel); + return error.Busy; + }; + t.detach(); + } + + fn run(s: *Slow, id: api.Id, r: *const api.Request, sink: api.Sink) void { + defer _ = s.active.fetchSub(1, .acq_rel); + var arena = std.heap.ArenaAllocator.init(s.gpa); + defer arena.deinit(); + const a = arena.allocator(); + const prompt = a.alloc(u8, r.prompt.len) catch return sink.event(sink.ctx, id, &.{ .finished = .{ .reason = .failed, .message = "out of memory" } }); + for (r.prompt, prompt) |t, *b| b.* = @truncate(t); + const long = std.mem.indexOf(u8, prompt, "@long") != null; + const word: []const u32 = if (long) &.{ ' ', 'l', 'a' } else &.{ ' ', 'o', 'k' }; + const hang = std.mem.indexOf(u8, prompt, "@hang") != null; + const wait = std.mem.indexOf(u8, prompt, "@wait") != null; + const hold = std.mem.indexOf(u8, prompt, "@hold") != null; + // a long prefill: nothing for 800 ms ("@wait"), or nothing until stopped ("@hang") + var quiet: u32 = 0; + while (hang or (wait and quiet < 80) or (hold and !s.release.load(.acquire))) : (quiet += 1) { + if (s.halted.load(.acquire)) return sink.event(sink.ctx, id, &.{ .finished = .{ .reason = .failed, .message = s.reason } }); + if (s.cancelled.load(.acquire) == id) return sink.event(sink.ctx, id, &.{ .finished = .{ .reason = .cancelled } }); + std.Io.sleep(s.io, .fromMilliseconds(10), .awake) catch {}; + } + sink.event(sink.ctx, id, &.{ .prefilled = 0 }); + var n: usize = 0; + while (true) : (n += 1) { + if (s.halted.load(.acquire)) return sink.event(sink.ctx, id, &.{ .finished = .{ .reason = .failed, .message = s.reason } }); + if (s.cancelled.load(.acquire) == id) return sink.event(sink.ctx, id, &.{ .finished = .{ .reason = .cancelled } }); + if (!long and n == 12) return sink.event(sink.ctx, id, &.{ .finished = .{ .reason = .stop } }); + sink.event(sink.ctx, id, &.{ .tokens = word }); + std.Io.sleep(s.io, .fromMilliseconds(15), .awake) catch {}; + } + } + + fn halt(ctx: *anyopaque, reason: []const u8) void { + const s = self(ctx); + s.reason = reason; + s.halts += 1; + s.halted.store(true, .release); + // a round boundary: every reply has ended before halt returns + while (s.active.load(.acquire) > 0) std.Io.sleep(s.io, .fromMilliseconds(1), .awake) catch {}; + } + + fn close(ctx: *anyopaque) void { + const s = self(ctx); + if (s.closes == 0) s.halts_at_close = s.halts; + s.closes += 1; + } +}; + +/// Bytes as tokens; a prompt renders as its messages' JSON, so the engine sees the client's marker. +pub const TestText = struct { + fn text(t: *@This()) model_text.Text { + return .{ .ctx = t, .vtable = &.{ .encode = encode, .decode = decode, .token_id = tokenId, .token_string = tokenString, .vocab_size = vocabSize, .eos_ids = eosIds, .render = render, .template_source = templateSource } }; + } + + fn encode(_: *anyopaque, a: Allocator, input: []const u8, _: bool) model_text.Error![]u32 { + const ids = try a.alloc(u32, input.len); + for (input, ids) |byte, *id| id.* = byte; + return ids; + } + + fn decode(_: *anyopaque, a: Allocator, ids: []const u32) Allocator.Error![]u8 { + const decoded = try a.alloc(u8, ids.len); + for (ids, decoded) |id, *byte| byte.* = @intCast(id); + return decoded; + } + + fn tokenId(_: *anyopaque, _: []const u8) ?u32 { + return null; + } + + fn tokenString(_: *anyopaque, a: Allocator, id: u32) Allocator.Error![]u8 { + return std.fmt.allocPrint(a, "{d}", .{id}); + } + + fn vocabSize(_: *anyopaque) u32 { + return 256; + } + + fn eosIds(_: *anyopaque) []const u32 { + return &.{}; + } + + fn render(_: *anyopaque, a: Allocator, messages: json.Value, _: model_text.RenderOptions, _: *[]const u8) model_text.Error![]u8 { + return json.stringify(a, messages, .{}) catch error.OutOfMemory; + } + + fn templateSource(_: *anyopaque) []const u8 { + return ""; + } +}; + +pub var listen_port: std.atomic.Value(u16) = .init(0); + +fn onListen(port: u16) void { + listen_port.store(port, .release); +} + +pub const Reply = struct { status: u16, head: []const u8, body: []const u8 }; + +/// One request on its own connection (closed by us after it), the whole response read. +pub fn request(a: Allocator, io: std.Io, port: u16, method: []const u8, path: []const u8, body: []const u8) !Reply { + const addr: std.Io.net.IpAddress = .{ .ip4 = .loopback(port) }; + const stream = try addr.connect(io, .{ .mode = .stream }); + defer stream.close(io); + const head = try std.fmt.allocPrint(a, "{s} {s} HTTP/1.1\r\nHost: x\r\nContent-Type: application/json\r\nContent-Length: {d}\r\nConnection: close\r\n\r\n{s}", .{ method, path, body.len, body }); + var wbuf: [4096]u8 = undefined; + var w = std.Io.net.Stream.Writer.init(stream, io, &wbuf); + try w.interface.writeAll(head); + try w.interface.flush(); + var rbuf: [16384]u8 = undefined; + var r = std.Io.net.Stream.Reader.init(stream, io, &rbuf); + const text = try r.interface.allocRemaining(a, .unlimited); + const split = std.mem.indexOf(u8, text, "\r\n\r\n") orelse return error.Head; + var payload: []const u8 = text[split + 4 ..]; + if (std.mem.indexOf(u8, text[0..split], "chunked") != null) payload = try unchunk(a, payload); + return .{ .status = try std.fmt.parseInt(u16, text[9..12], 10), .head = text[0..split], .body = payload }; +} + +fn unchunk(a: Allocator, s: []const u8) ![]const u8 { + var out: std.ArrayList(u8) = .empty; + var i: usize = 0; + while (i < s.len) { + const eol = std.mem.indexOfPos(u8, s, i, "\r\n") orelse break; + const n = try std.fmt.parseInt(usize, std.mem.trim(u8, s[i..eol], " "), 16); + if (n == 0) break; + try out.appendSlice(a, s[eol + 2 .. eol + 2 + n]); + i = eol + 2 + n + 2; + } + return out.items; +} + +pub const Background = struct { + a: Allocator, + io: std.Io, + port: u16, + body: []const u8, + reply: ?Reply = null, + failed: bool = false, + + fn run(b: *Background) void { + b.reply = request(b.a, b.io, b.port, "POST", "/v1/chat/completions", b.body) catch { + b.failed = true; + return; + }; + } +}; + +pub fn chatBody(a: Allocator, marker: []const u8, stream: bool) ![]const u8 { + return std.fmt.allocPrint(a, "{{\"messages\":[{{\"role\":\"user\",\"content\":\"go {s}\"}}],\"stream\":{},\"max_tokens\":4000}}", .{ marker, stream }); +} + +pub const Served = struct { + code: u8 = 255, + fn run(x: *Served, gpa: Allocator, io: std.Io, setup: serve.Setup) void { + x.code = serve.run(gpa, io, .{ .host = "127.0.0.1", .port = 0 }, setup); + } +}; + +/// serve.run on its own thread with `drain_s`, the engine `slow`; the port once it listens. +pub fn start(gpa: Allocator, io: std.Io, env: *std.process.Environ.Map, slow: *Slow, tt: *TestText, x: *Served) !std.Thread { + listen_port.store(0, .release); + const t = try std.Thread.spawn(.{}, Served.run, .{ x, gpa, io, serve.Setup{ + .engine = slow.engine(), + .text = tt.text(), + .served = "slow", + .environ = env, + .on_listen = onListen, + .stop = .{ .ctx = slow, .halt = Slow.halt, .close = Slow.close }, + } }); + while (listen_port.load(.acquire) == 0) std.Io.sleep(io, .fromMilliseconds(2), .awake) catch {}; + return t; +} + +pub fn waitActive(io: std.Io, slow: *Slow, n: u32) void { + while (slow.active.load(.acquire) < n) std.Io.sleep(io, .fromMilliseconds(2), .awake) catch {}; +} + +test "SIGTERM drains: 503 + Retry-After and /health draining while a reply finishes; at the deadline the rest end \"server restarting\" at a round boundary, then the engine closes" { + const gpa = testing.allocator; + const io = testing.io; + var tt: TestText = .{}; + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + const a = arena.allocator(); + var env = std.process.Environ.Map.init(gpa); + defer env.deinit(); + try env.put("TENSORFOLD_NO_LIVE", "1"); + + // 1. the reply in progress finishes within the drain: no halt, a whole reply; new work refused meanwhile + { + try env.put("TF_DRAIN_S", "10"); + var slow: Slow = .{ .gpa = gpa, .io = io }; + var x: Served = .{}; + const t = try start(gpa, io, &env, &slow, &tt, &x); + const port = listen_port.load(.acquire); + // held through the checks below: a reply that ended first would end the drain and close the listener + var bg: Background = .{ .a = a, .io = io, .port = port, .body = try chatBody(a, "@hold", true) }; + const bt = try std.Thread.spawn(.{}, Background.run, .{&bg}); + errdefer slow.release.store(true, .release); + waitActive(io, &slow, 1); + try std.posix.raise(.TERM); + // the drain has begun once a new request is refused + var refused: Reply = undefined; + while (true) { + refused = try request(a, io, port, "POST", "/v1/chat/completions", try chatBody(a, "@short", false)); + if (refused.status == 503) break; + try testing.expectEqual(@as(u16, 200), refused.status); // only before the signal lands + } + try testing.expect(std.mem.indexOf(u8, refused.head, "Retry-After: 30") != null); + try testing.expect(std.mem.indexOf(u8, refused.body, "\"code\": \"server_restarting\"") != null); + const anth = try request(a, io, port, "POST", "/v1/messages", "{\"model\":\"x\",\"max_tokens\":5,\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}]}"); + try testing.expectEqual(@as(u16, 503), anth.status); + try testing.expect(std.mem.indexOf(u8, anth.body, "overloaded_error") != null); + const health = try request(a, io, port, "GET", "/health", ""); + try testing.expectEqual(@as(u16, 503), health.status); + try testing.expect(std.mem.indexOf(u8, health.head, "Retry-After: 30") != null); + try testing.expect(std.mem.indexOf(u8, health.body, "\"status\": \"draining\"") != null); + slow.release.store(true, .release); + bt.join(); + t.join(); + try testing.expect(!bg.failed); + try testing.expectEqual(@as(u16, 200), bg.reply.?.status); + try testing.expect(std.mem.indexOf(u8, bg.reply.?.body, "\"finish_reason\": \"stop\"") != null); + try testing.expect(std.mem.indexOf(u8, bg.reply.?.body, "restarting") == null); + try testing.expect(std.mem.endsWith(u8, std.mem.trimEnd(u8, bg.reply.?.body, "\r\n"), "data: [DONE]")); + try testing.expectEqual(@as(u8, 0), x.code); + try testing.expectEqual(@as(u32, 0), slow.halts); + try testing.expectEqual(@as(u32, 1), slow.closes); + } + // 2. replies that outlast the drain end "server restarting"; the engine is halted, then closed once + { + try env.put("TF_DRAIN_S", "0.3"); + var slow: Slow = .{ .gpa = gpa, .io = io }; + var x: Served = .{}; + const t = try start(gpa, io, &env, &slow, &tt, &x); + const port = listen_port.load(.acquire); + var streamed: Background = .{ .a = a, .io = io, .port = port, .body = try chatBody(a, "@long", true) }; + var whole: Background = .{ .a = a, .io = io, .port = port, .body = try chatBody(a, "@long", false) }; + const st = try std.Thread.spawn(.{}, Background.run, .{&streamed}); + const wt = try std.Thread.spawn(.{}, Background.run, .{&whole}); + waitActive(io, &slow, 2); + try std.posix.raise(.TERM); + st.join(); + wt.join(); + t.join(); + try testing.expectEqual(@as(u8, 0), x.code); + try testing.expectEqual(@as(u32, 1), slow.halts); + try testing.expectEqual(@as(u32, 1), slow.closes); + try testing.expectEqual(@as(u32, 1), slow.halts_at_close); + try testing.expectEqualStrings(routes.restarting, slow.reason); + const sb = streamed.reply.?.body; + try testing.expectEqual(@as(u16, 200), streamed.reply.?.status); + try testing.expect(std.mem.indexOf(u8, sb, "\"type\": \"server_error\"") != null); + try testing.expect(std.mem.indexOf(u8, sb, routes.restarting) != null); + try testing.expect(std.mem.indexOf(u8, sb, "data: [DONE]") != null); + try testing.expect(whole.reply.?.status >= 500); + try testing.expect(std.mem.indexOf(u8, whole.reply.?.body, routes.restarting) != null); + } +} + +/// The race seam's state: the stop delivered between a request's count and its drain check. +const Gap = struct { + var io: std.Io = undefined; + var port: u16 = 0; + var fired: std.atomic.Value(bool) = .init(false); + var saw_draining: bool = false; + var drain_waited: bool = false; + var served: *Served = undefined; + + fn hook() void { + if (fired.swap(true, .acq_rel)) return; + std.posix.raise(.TERM) catch return; + var arena = std.heap.ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + // the drain has begun (it reads the count after it sets the flag): /health says so + var tries: u32 = 0; + while (tries < 500) : (tries += 1) { + const h = request(arena.allocator(), io, port, "GET", "/health", "") catch break; + if (h.status == 503 and std.mem.indexOf(u8, h.body, "draining") != null) { + saw_draining = true; + break; + } + std.Io.sleep(io, .fromMilliseconds(5), .awake) catch {}; + } + // this request is counted: the drain may not finish while it is between its count and its check + std.Io.sleep(io, .fromMilliseconds(300), .awake) catch {}; + drain_waited = served.code == 255; + } +}; + +test "a stop that lands between a request's count and its drain check: the request is refused (503) and the drain waited for it, never lost" { + const gpa = testing.allocator; + const io = testing.io; + var tt: TestText = .{}; + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + const a = arena.allocator(); + var env = std.process.Environ.Map.init(gpa); + defer env.deinit(); + try env.put("TENSORFOLD_NO_LIVE", "1"); + try env.put("TF_DRAIN_S", "10"); + var slow: Slow = .{ .gpa = gpa, .io = io }; + var x: Served = .{}; + const t = try start(gpa, io, &env, &slow, &tt, &x); + Gap.io = io; + Gap.port = listen_port.load(.acquire); + Gap.served = &x; + Gap.fired.store(false, .release); + routes.counted_hook = Gap.hook; + defer routes.counted_hook = null; + const r = try request(a, io, Gap.port, "POST", "/v1/chat/completions", try chatBody(a, "@short", false)); + t.join(); + try testing.expect(Gap.fired.load(.acquire)); + try testing.expect(Gap.saw_draining); + try testing.expect(Gap.drain_waited); + try testing.expectEqual(@as(u16, 503), r.status); + try testing.expect(std.mem.indexOf(u8, r.body, "server_restarting") != null); + try testing.expectEqual(@as(u32, 0), slow.closes -| 1); // closed once, after + try testing.expectEqual(@as(u32, 0), slow.active.load(.acquire)); // nothing reached the engine + try testing.expectEqual(@as(u8, 0), x.code); +} diff --git a/zig/src/server/keepalive_test.zig b/zig/src/server/keepalive_test.zig new file mode 100644 index 0000000000..43435c1d04 --- /dev/null +++ b/zig/src/server/keepalive_test.zig @@ -0,0 +1,75 @@ +//! A silent stream keeps its client: keepalives on both routes before the first token; a failed one ends the request. +const std = @import("std"); +const server = @import("server.zig"); +const d = @import("drain_test.zig"); +const testing = std.testing; + +/// A raw request whose response is read until the connection ends. +fn messagesBody(a: std.mem.Allocator, marker: []const u8) ![]const u8 { + return std.fmt.allocPrint(a, "{{\"model\":\"x\",\"max_tokens\":50,\"stream\":true,\"messages\":[{{\"role\":\"user\",\"content\":\"go {s}\"}}]}}", .{marker}); +} + +/// Sends `body` streamed and hangs up once the response head arrived and the request reached `slow`. +fn leave(a: std.mem.Allocator, io: std.Io, port: u16, body: []const u8, slow: *d.Slow) !void { + const addr: std.Io.net.IpAddress = .{ .ip4 = .loopback(port) }; + const stream = try addr.connect(io, .{ .mode = .stream }); + defer stream.close(io); + const head = try std.fmt.allocPrint(a, "POST /v1/chat/completions HTTP/1.1\r\nHost: x\r\nContent-Type: application/json\r\nContent-Length: {d}\r\n\r\n{s}", .{ body.len, body }); + var wbuf: [4096]u8 = undefined; + var w = std.Io.net.Stream.Writer.init(stream, io, &wbuf); + try w.interface.writeAll(head); + try w.interface.flush(); + var rbuf: [64]u8 = undefined; + var r = std.Io.net.Stream.Reader.init(stream, io, &rbuf); + _ = try r.interface.peek(1); + // the head goes out before the engine has the request: a hang-up before that leaves nothing to cancel + d.waitActive(io, slow, 1); +} + +test "a silent engine's stream gets keepalives before its first token (chat: SSE comments, messages: ping events)" { + const period = comptime std.meta.fieldInfo(server.Config, .sse_keepalive_s).attrs.defaultValue(f64).?; + try testing.expect(period > 0 and period <= 15); + const gpa = testing.allocator; + const io = testing.io; + var tt: d.TestText = .{}; + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + const a = arena.allocator(); + var env = std.process.Environ.Map.init(gpa); + defer env.deinit(); + try env.put("TENSORFOLD_NO_LIVE", "1"); + try env.put("TF_DRAIN_S", "5"); + try env.put("TF_SSE_KEEPALIVE_S", "0.1"); + var slow: d.Slow = .{ .gpa = gpa, .io = io }; + var x: d.Served = .{}; + const t = try d.start(gpa, io, &env, &slow, &tt, &x); + const port = d.listen_port.load(.acquire); + + const chat = try d.request(a, io, port, "POST", "/v1/chat/completions", try d.chatBody(a, "@wait", true)); + try testing.expectEqual(@as(u16, 200), chat.status); + const first_token = std.mem.indexOf(u8, chat.body, "\" ok\"") orelse { + std.debug.print("{s}\n", .{chat.body[0..@min(chat.body.len, 1500)]}); + return error.NoToken; + }; + try testing.expect(std.mem.count(u8, chat.body[0..first_token], ": keepalive\n\n") >= 3); + try testing.expect(std.mem.indexOf(u8, chat.body, "data: [DONE]") != null); + + const msg = try d.request(a, io, port, "POST", "/v1/messages", try messagesBody(a, "@wait")); + try testing.expectEqual(@as(u16, 200), msg.status); + const start = std.mem.indexOf(u8, msg.body, "event: message_start") orelse return error.NoStart; + const ping = std.mem.indexOf(u8, msg.body, "event: ping\ndata: {\"type\": \"ping\"}") orelse return error.NoPing; + const delta = std.mem.indexOf(u8, msg.body, "event: content_block_delta") orelse return error.NoDelta; + try testing.expect(start < ping and ping < delta); + + // a client that leaves a silent stream: the request is cancelled (the disconnect poll or a failed keepalive) + var idle: u32 = 0; + while (slow.active.load(.acquire) > 0 and idle < 300) : (idle += 1) std.Io.sleep(io, .fromMilliseconds(10), .awake) catch {}; + try leave(a, io, port, try d.chatBody(a, "@hang", true), &slow); + var waited: u32 = 0; + while (slow.cancelled.load(.acquire) == 0 and waited < 300) : (waited += 1) std.Io.sleep(io, .fromMilliseconds(10), .awake) catch {}; + try testing.expect(slow.cancelled.load(.acquire) != 0); + + try std.posix.raise(.TERM); + t.join(); + try testing.expectEqual(@as(u8, 0), x.code); +} diff --git a/zig/src/server/main.zig b/zig/src/server/main.zig index f1cceb9ed4..32c40047f5 100644 --- a/zig/src/server/main.zig +++ b/zig/src/server/main.zig @@ -123,7 +123,8 @@ pub fn main(init: std.process.Init) !u8 { defer up.text.deinit(); var record: ?*@import("engine_api").recall_store.Store = null; defer if (record) |r| r.close(); // after the engine below has stopped reading it - defer up.engine.close(up.engine.ctx); + var closer: Closer = .{ .opened = up.engine }; + defer closer.closeOnce(); const served = hub.servedName(args.name, args.model, dir); problem = ""; record = recall_open.open(gpa, io, a, args, up.text, served, &problem) catch |e| { @@ -131,6 +132,7 @@ pub fn main(init: std.process.Init) !u8 { }; if (record) |r| if (!up.engine.engine.attachRecall(r)) return fail(not_lanes); return serve.run(gpa, io, args, .{ + .stop = .{ .ctx = &closer, .close = Closer.close }, .engine = up.engine.engine, .text = up.text.text(), .served = served, @@ -144,6 +146,22 @@ pub fn main(init: std.process.Init) !u8 { }); } +/// Closes once: serve.run closes the engine before freeing the server, main's defer covers paths that end before it. +const Closer = struct { + opened: engines.Opened, + closed: bool = false, + + fn closeOnce(c: *Closer) void { + if (c.closed) return; + c.closed = true; + c.opened.close(c.opened.ctx); + } + + fn close(ctx: *anyopaque) void { + closeOnce(@ptrCast(@alignCast(ctx))); + } +}; + /// TF_SLIDE_FORGET_FAIL in a -Dslide-test-hooks build: the fact whose check a delete fails, so its put-back is seen. fn forgetFail(env: *const std.process.Environ.Map) u32 { const v = env.get("TF_SLIDE_FORGET_FAIL") orelse return 0; diff --git a/zig/src/server/openai.zig b/zig/src/server/openai.zig index 45cf437364..1cdee2eda8 100644 --- a/zig/src/server/openai.zig +++ b/zig/src/server/openai.zig @@ -36,6 +36,8 @@ pub const Out = struct { event: *const fn (ctx: *anyopaque, payload: ?Value) error{Closed}!void, /// A whole JSON reply. reply: *const fn (ctx: *anyopaque, status: u16, payload: Value) void, + /// Bytes that keep a silent stream alive (an SSE comment, or the API's ping); null: none. + keepalive: ?*const fn (ctx: *anyopaque) error{Closed}!void = null, }; }; @@ -56,7 +58,7 @@ const ClientOut = struct { a: Allocator, fn out(c: *ClientOut) Out { - return .{ .ctx = c, .vt = &.{ .open = open, .event = event, .reply = reply } }; + return .{ .ctx = c, .vt = &.{ .open = open, .event = event, .reply = reply, .keepalive = keepalive } }; } fn open(ctx: *anyopaque, _: usize) error{Closed}!void { @@ -73,14 +75,31 @@ const ClientOut = struct { const c: *ClientOut = @ptrCast(@alignCast(ctx)); routes.sendValue(c.conn, c.a, status, payload); } + + fn keepalive(ctx: *anyopaque) error{Closed}!void { + const c: *ClientOut = @ptrCast(@alignCast(ctx)); + return sse.comment(c.conn); + } }; /// Whether the client has gone, read between rounds (Python's socket_cancellation). pub const Gone = struct { conn: *Conn, + /// The stream a keepalive goes to once it has been silent `every_ns` (0: never) + out: ?Out = null, + every_ns: i96 = 0, pub fn check(g: Gone) bool { return g.conn.peerGone(); } + + /// A keepalive if the stream has been silent long enough; a failed write means the client left. + pub fn beat(g: Gone, silent_ns: i96) error{Closed}!bool { + const o = g.out orelse return false; + const send = o.vt.keepalive orelse return false; + if (g.every_ns <= 0 or silent_ns < g.every_ns) return false; + try send(o.ctx); + return true; + } }; /// ``error_body``: OpenAI's error object, with param and code where clients key on them. @@ -514,8 +533,11 @@ const Run = struct { defer if (!handed) chat.release(r.srv, prepared.preparing); var sink_state: StreamSink = .{ .run = r, .tools = tools, .prompt_len = prepared.prompt_len }; handed = true; + var live = gone; // keepalives start once begin has opened the stream, after the engine admits the request + live.out = r.out; + live.every_ns = @intFromFloat(r.srv.config.sse_keepalive_s * std.time.ns_per_s); const sink: chat.Sink = .{ .ctx = &sink_state, .call = StreamSink.call, .begin = StreamSink.begin }; - var reply = (if (r.turn) |t| cortheon.loop.stream(t, r.srv, &cx, &r.plan.input, prepared, sink, gone) else chat.generate(r.srv, &cx, prepared, sink, gone)) catch |e| { + var reply = (if (r.turn) |t| cortheon.loop.stream(t, r.srv, &cx, &r.plan.input, prepared, sink, gone) else chat.generate(r.srv, &cx, prepared, sink, live)) catch |e| { if (!sink_state.opened) return r.unsent(&cx, e, field); switch (e) { error.Cancelled => return, diff --git a/zig/src/server/responses.zig b/zig/src/server/responses.zig index e84a74d0a9..0c72b48678 100644 --- a/zig/src/server/responses.zig +++ b/zig/src/server/responses.zig @@ -203,7 +203,7 @@ const Wire = struct { payload: Value = .null, fn out(w: *Wire) openai.Out { - return .{ .ctx = w, .vt = &.{ .open = open, .event = event, .reply = whole } }; + return .{ .ctx = w, .vt = &.{ .open = open, .event = event, .reply = whole, .keepalive = keepalive } }; } fn open(ctx: *anyopaque, _: usize) error{Closed}!void { @@ -223,6 +223,11 @@ const Wire = struct { w.status = status; w.payload = payload; } + + fn keepalive(ctx: *anyopaque) error{Closed}!void { + const w: *Wire = @ptrCast(@alignCast(ctx)); + return sse.comment(w.reply.conn); + } }; /// A Response built from chat-completion deltas, in order: reasoning, text, calls; streamed as typed events. diff --git a/zig/src/server/root.zig b/zig/src/server/root.zig index c7b31b5004..35484bffbc 100644 --- a/zig/src/server/root.zig +++ b/zig/src/server/root.zig @@ -56,6 +56,8 @@ test { _ = @import("stream_preflight_test.zig"); _ = @import("stream_dsml_test.zig"); _ = @import("logprobs_test.zig"); + _ = @import("drain_test.zig"); + _ = @import("keepalive_test.zig"); _ = @import("late_system_test.zig"); _ = @import("compact_test.zig"); _ = @import("request_log.zig"); diff --git a/zig/src/server/routes.zig b/zig/src/server/routes.zig index 5cf7f19eb2..8eee7e84ce 100644 --- a/zig/src/server/routes.zig +++ b/zig/src/server/routes.zig @@ -1,6 +1,7 @@ //! Method and path dispatch, as ``do_GET``, ``do_POST`` and ``do_DELETE`` route them. const speech = @import("speech.zig"); const std = @import("std"); +const api = @import("engine_api"); const json = @import("json"); const http_conn = @import("http_conn.zig"); const http_body = @import("http_body.zig"); @@ -77,19 +78,61 @@ fn discardBody(conn: *Conn, a: Allocator) void { fn post(srv: *Server, conn: *Conn, a: Allocator) void { const route = auth.routePath(conn.path); - if (std.mem.eql(u8, route, "/v1/audio/speech")) return speech.post(srv, conn, a) catch {}; - if (std.mem.eql(u8, route, "/v1/slide/learn")) return slide.learn(srv, conn, a); - if (std.mem.eql(u8, route, "/v1/imprint/resume")) return imprint.post(srv, conn, a, .where); - if (std.mem.eql(u8, route, "/v1/imprint/load")) return imprint.post(srv, conn, a, .load); - if (std.mem.endsWith(u8, route, "/decisions")) return decisions.post(srv, conn, a); - if (std.mem.endsWith(u8, route, "/systemone")) return systemone.post(srv, conn, a); - if (anthropic.route(conn.path)) return anthropic.post(srv, conn, a); - if (responses.route(route)) |rid| if (rid.len == 0) return responses.post(srv, conn, a); for (tokens.paths) |r| if (std.mem.eql(u8, route, r)) return tokens.post(srv, conn, a, std.mem.endsWith(u8, route, "/detokenize")); + const is_anthropic = anthropic.route(conn.path); + const is_responses = if (responses.route(route)) |rid| rid.len == 0 else false; + const is_decisions = std.mem.endsWith(u8, route, "/decisions"); + const is_systemone = std.mem.endsWith(u8, route, "/systemone"); + const is_slide = std.mem.eql(u8, route, "/v1/slide/learn"); + const is_speech = std.mem.eql(u8, route, "/v1/audio/speech"); + const imprint_op: ?api.ImprintOp = if (std.mem.eql(u8, route, "/v1/imprint/resume")) .where else if (std.mem.eql(u8, route, "/v1/imprint/load")) .load else null; const is_chat = std.mem.endsWith(u8, route, "/chat/completions"); - if (!is_chat and !std.mem.endsWith(u8, route, "/completions")) { + if (!is_slide and !is_speech and imprint_op == null and !is_anthropic and !is_responses and !is_decisions and !is_systemone and !is_chat and !std.mem.endsWith(u8, route, "/completions")) { discardBody(conn, a); return unknown(conn, a); } + // counted before the drain flag is read: a stop that sees no request in progress sees every later one refused + _ = srv.generating.fetchAdd(1, .seq_cst); + defer _ = srv.generating.fetchSub(1, .seq_cst); + if (@import("builtin").is_test) if (counted_hook) |h| h(); + if (srv.draining.load(.seq_cst)) { + discardBody(conn, a); + return draining(conn, is_anthropic); + } + if (is_speech) return speech.post(srv, conn, a) catch {}; + if (is_slide) return slide.learn(srv, conn, a); + if (imprint_op) |op| return imprint.post(srv, conn, a, op); + if (is_decisions) return decisions.post(srv, conn, a); + if (is_systemone) return systemone.post(srv, conn, a); + if (is_anthropic) return anthropic.post(srv, conn, a); + if (is_responses) return responses.post(srv, conn, a); openai.post(srv, conn, a, is_chat); } + +/// Tests only: the gap between a request's count and its drain check, where a stop must still see it counted. +pub var counted_hook: ?*const fn () void = null; + +/// Seconds a refused client is told to wait while the server drains for a restart (Retry-After). +pub const retry_after_s = 30; +pub const restarting = "server restarting: retry shortly"; + +/// A request that arrives while a stop drains: 503, Retry-After, the API's own error shape; the connection closes. +pub fn draining(conn: *Conn, anthropic_shape: bool) void { + const body = if (anthropic_shape) + "{\"type\": \"error\", \"error\": {\"type\": \"overloaded_error\", \"message\": \"" ++ restarting ++ "\"}}" + else + "{\"error\": {\"message\": \"" ++ restarting ++ "\", \"type\": \"server_error\", \"param\": null, \"code\": \"server_restarting\"}}"; + sendDraining(conn, body); +} + +/// 503 with Retry-After and Connection: close, `body` as JSON. +pub fn sendDraining(conn: *Conn, body: []const u8) void { + conn.close = true; + conn.startResponse(503, null) catch return; + conn.addHeader("Content-Type", "application/json") catch return; + conn.addHeader("Retry-After", std.fmt.comptimePrint("{d}", .{retry_after_s})) catch return; + var len: [24]u8 = undefined; + conn.addHeader("Content-Length", std.fmt.bufPrint(&len, "{d}", .{body.len}) catch unreachable) catch return; + conn.addHeader("Connection", "close") catch return; + conn.finish(body) catch {}; +} diff --git a/zig/src/server/serve.zig b/zig/src/server/serve.zig index 1342f2df4a..c97c9b207f 100644 --- a/zig/src/server/serve.zig +++ b/zig/src/server/serve.zig @@ -10,16 +10,29 @@ const live = @import("live.zig"); const listener_mod = @import("listener.zig"); const model_text = @import("model_text.zig"); const server_mod = @import("server.zig"); +const routes = @import("routes.zig"); const recall_routes = @import("recall_routes.zig"); const Allocator = std.mem.Allocator; const posix = platform.posix; var stop_requested: std.atomic.Value(bool) = .init(false); +/// The signal that asked for the stop (0: none yet), for the shutdown line +var stop_signal: std.atomic.Value(i32) = .init(0); +/// Stop signals so far: a second one cuts the drain short +var stop_count: std.atomic.Value(u32) = .init(0); -fn onStop(_: posix.SIG) callconv(.c) void { +fn onStop(sig: posix.SIG) callconv(.c) void { + stop_signal.store(@intCast(@backingInt(sig)), .release); + _ = stop_count.fetchAdd(1, .acq_rel); stop_requested.store(true, .release); } +fn signalName(sig: i32) []const u8 { + if (sig == @backingInt(posix.SIG.TERM)) return "SIGTERM"; + if (sig == @backingInt(posix.SIG.INT)) return "SIGINT"; + return "a stop signal"; +} + /// SIGTERM and SIGINT stop the server cleanly; SIGHUP rereads the key file; SIGPIPE is ignored, as Python ignores it. pub fn installSignals(keys: bool) void { const stop: posix.Sigaction = .{ .handler = .{ .handler = onStop }, .mask = posix.sigemptyset(), .flags = 0 }; @@ -81,6 +94,15 @@ pub const Setup = struct { on_listen: ?*const fn (port: u16) void = null, /// Recall's record, attached to the engine already (--recall); null: off. recall: ?*api.recall_store.Store = null, + /// Run by `run` so the engine stops before the server is freed; null: the caller closes it after `run`. + stop: ?EngineStop = null, +}; + +/// `halt` (optional) ends the rounds at a boundary at the drain's deadline, so `close` never meets a running round. +pub const EngineStop = struct { + ctx: *anyopaque, + halt: ?*const fn (ctx: *anyopaque, reason: []const u8) void = null, + close: *const fn (ctx: *anyopaque) void, }; fn env(s: Setup, name: []const u8) ?[]const u8 { @@ -90,6 +112,9 @@ fn env(s: Setup, name: []const u8) ?[]const u8 { /// Serves until a stop signal; the process exit status. pub fn run(gpa: Allocator, io: std.Io, args: cli.Args, s: Setup) u8 { + stop_requested.store(false, .release); + stop_count.store(0, .release); + stop_signal.store(0, .release); var arena: std.heap.ArenaAllocator = .init(gpa); defer arena.deinit(); const a = arena.allocator(); @@ -112,6 +137,14 @@ pub fn run(gpa: Allocator, io: std.Io, args: cli.Args, s: Setup) u8 { std.debug.print("tensorfold: TENSORFOLD_SEED_SALT={s}: an integer\n", .{t}); return 1; } else 0; + const drain_s = seconds(env(s, "TF_DRAIN_S"), 20) orelse { + std.debug.print("tensorfold: TF_DRAIN_S: seconds, a number >= 0\n", .{}); + return 1; + }; + const keepalive_s = seconds(env(s, "TF_SSE_KEEPALIVE_S"), 15) orelse { + std.debug.print("tensorfold: TF_SSE_KEEPALIVE_S: seconds, a number >= 0\n", .{}); + return 1; + }; const config: server_mod.Config = .{ .served_name = s.served, .model_dir = s.model_dir, @@ -126,6 +159,7 @@ pub fn run(gpa: Allocator, io: std.Io, args: cli.Args, s: Setup) u8 { .use_drafts = !args.no_drafts, .vision_urls = args.vision_urls, .seed_salt = salt, + .sse_keepalive_s = keepalive_s, .request_log = env(s, "TENSORFOLD_REQUEST_LOG"), .dashboard = args.dashboard, .compact_at = if (args.compact_auto) .{ .auto = {} } else if (args.compact_fraction) |f| .{ .fraction = f } else null, @@ -170,6 +204,8 @@ pub fn run(gpa: Allocator, io: std.Io, args: cli.Args, s: Setup) u8 { s.served, args.host, port, shownSampling(a, s.sampling), if (args.no_drafts or srv.info.plain_only) "off" else "on", if (window > 0) std.fmt.bufPrint(&window_text, "{d}", .{window}) catch "?" else "unlimited", loaded, }); + if (greedy(s.sampling)) + log.line("WARNING: default sampling is greedy (temperature <= 0) - a client that omits `temperature` decodes greedily, which on a long agentic turn can loop in reasoning and return empty content", .{}); if (args.thinking and std.mem.indexOf(u8, s.text.templateSource(), "enable_thinking") != null) log.line("thinking on (the chat template's default): replies reason in reasoning_content before the answer in content, and max_tokens counts both. --no-thinking turns it off; a request can send chat_template_kwargs {{\"enable_thinking\": false}}", .{}); if (s.recall) |r| recall_routes.line(r); @@ -178,17 +214,57 @@ pub fn run(gpa: Allocator, io: std.Io, args: cli.Args, s: Setup) u8 { var ticker: live.Ticker = .{ .engine = s.engine }; const drawing = live.wanted(io, if (env(s, "TENSORFOLD_NO_LIVE")) |v| std.mem.eql(u8, v, "1") else false); if (drawing) ticker.start(); - const accept = std.Thread.spawn(.{}, server_mod.Server.serve, .{ srv, lis, &stop_requested }) catch return 1; + // the listener stays open through the drain: new requests get 503 + Retry-After, /health "draining" + var accept_stop: std.atomic.Value(bool) = .init(false); + const accept = std.Thread.spawn(.{}, server_mod.Server.serve, .{ srv, lis, &accept_stop }) catch return 1; + while (!stop_requested.load(.acquire)) std.Io.sleep(io, .fromMilliseconds(50), .awake) catch {}; + drain(srv, io, s.stop, drain_s); + accept_stop.store(true, .release); accept.join(); lis.close(); if (drawing) ticker.finish(); + s.engine.drain(); // kept states waiting for disk are written (under the engine's deadline) before the engine closes + // the engine stops while nothing runs on it: no round meets a CUDA driver torn down by the exit + if (s.stop) |st| st.close(st.ctx); var waited: u32 = 0; while (srv.open_connections.load(.acquire) > 0 and waited < 40) : (waited += 1) std.Io.sleep(io, .fromMilliseconds(50), .awake) catch {}; - s.engine.drain(); // kept states waiting for disk are written (under the engine's deadline) before the process ends - if (srv.open_connections.load(.acquire) > 0) std.process.exit(0); // replies still open: end without freeing what they read + if (srv.open_connections.load(.acquire) > 0) { + log.line("shutting down: exiting with {d} idle connection(s) still open", .{srv.open_connections.load(.acquire)}); + std.process.exit(0); // connections still open: end without freeing what they read + } + log.line("shutting down: stopped", .{}); return 0; } +/// Work in progress gets `drain_s` to finish (new work 503s) before the rest is halted; a second signal cuts it short. +fn drain(srv: *server_mod.Server, io: std.Io, stop: ?EngineStop, drain_s: f64) void { + srv.draining.store(true, .seq_cst); + const signals = stop_count.load(.acquire); + log.line("shutting down ({s}): draining: new requests get 503; {d} request(s) in progress may finish within {d:.1} s", .{ signalName(stop_signal.load(.acquire)), srv.generating.load(.seq_cst), drain_s }); + const t0 = std.Io.Clock.awake.now(io).toNanoseconds(); + const deadline = t0 + @as(i96, @intFromFloat(drain_s * 1e9)); + while (srv.generating.load(.seq_cst) > 0 and std.Io.Clock.awake.now(io).toNanoseconds() < deadline and stop_count.load(.acquire) == signals) + std.Io.sleep(io, .fromMilliseconds(20), .awake) catch {}; + const left = srv.generating.load(.seq_cst); + if (left == 0) { + log.line("shutting down: drained", .{}); + return; + } + const cut = if (stop_count.load(.acquire) != signals) "a second stop signal" else "the drain deadline (TF_DRAIN_S)"; + log.line("shutting down: {s}: {d} request(s) still in progress end \"{s}\" at the next round boundary", .{ cut, left, routes.restarting }); + if (stop) |st| if (st.halt) |h| h(st.ctx, routes.restarting); + var waited: u32 = 0; + while (srv.generating.load(.seq_cst) > 0 and waited < 100) : (waited += 1) std.Io.sleep(io, .fromMilliseconds(20), .awake) catch {}; +} + +/// A seconds setting (TF_DRAIN_S, TF_SSE_KEEPALIVE_S): unset or blank keeps the default; null: not a number >= 0. +fn seconds(raw: ?[]const u8, default: f64) ?f64 { + const t = std.mem.trim(u8, raw orelse "", " \t"); + if (t.len == 0) return default; + const x = std.fmt.parseFloat(f64, t) catch return null; + return if (x >= 0) x else null; +} + /// ``--host`` as an address: an IP literal, else a name the resolver knows (localhost first). fn resolve(io: std.Io, host: []const u8, port: u16) ?std.Io.net.IpAddress { if (std.Io.net.IpAddress.parse(host, port)) |ip| return ip else |_| {} @@ -196,6 +272,17 @@ fn resolve(io: std.Io, host: []const u8, port: u16) ?std.Io.net.IpAddress { return std.Io.net.IpAddress.resolve(io, host, port) catch null; } +/// Whether requests that name no temperature decode greedily (the startup warning). +fn greedy(sampling: ?json.Value) bool { + const v = sampling orelse return true; + const t = v.get("temperature") orelse return true; + return switch (t) { + .float => |f| f <= 0, + .int => |i| (std.fmt.parseFloat(f64, i) catch 0) <= 0, + else => true, + }; +} + /// The serving line's sampling: greedy, or each default as ``name value``. fn shownSampling(a: Allocator, sampling: ?json.Value) []const u8 { const v = sampling orelse return "greedy"; diff --git a/zig/src/server/server.zig b/zig/src/server/server.zig index cd95098680..2c2ecfa185 100644 --- a/zig/src/server/server.zig +++ b/zig/src/server/server.zig @@ -69,6 +69,8 @@ pub const Config = struct { /// --learn-conversations: /v1/imprint answers; without it each of its routes refuses and names the flag. saved_conversations: bool = false, timeouts: http_conn.Timeouts = .{}, + /// TF_SSE_KEEPALIVE_S: a silent stream's keepalive period before its first token (serve.zig; 0: none) + sse_keepalive_s: f64 = 15, /// The open /health names these after its other keys; empty fields are left out. identity: Identity = .{}, /// The process environment GET /v1/benchmark/manifest reads (rank count, revision, the hardware probe); null: none. @@ -97,6 +99,10 @@ pub const Server = struct { next_id: std.atomic.Value(u64) = .init(1), /// Connections open now; a stop waits for them before freeing what they read. open_connections: std.atomic.Value(u32) = .init(0), + /// A stop is draining (serve.zig): new generation requests get 503 with Retry-After, /health answers "draining" + draining: std.atomic.Value(bool) = .init(false), + /// Generation requests in progress (routes.post), what a drain waits for; counted before `draining` is read + generating: std.atomic.Value(u32) = .init(0), /// The idle keepalive on the engine's queue, armed by --keep-warm; null when off or not Metal. keepalive: ?*api.keepalive.Keepalive = null, preparing: std.atomic.Value(i64) = .init(0), @@ -179,7 +185,8 @@ pub const Server = struct { const options = try json.newObject(cx.a); const defaults = if (srv.config.default_sampling) |d| try fields.parseNumbers(cx, d) else null; if (defaults) |d| try merge(options, cx, d); - try merge(options, cx, try fields.parseNumbers(cx, f)); + const asked = try fields.parseNumbers(cx, f); + try merge(options, cx, asked); const temp = number(options.get("temperature")) orelse 0; if (temp <= 0) return null; if (srv.info.greedy_only) return cx.refuse(if ((if (defaults) |d| number(d.get("temperature")) else null) orelse 0 > 0) diff --git a/zig/src/server/sse.zig b/zig/src/server/sse.zig index 0942a7e145..2438c41eee 100644 --- a/zig/src/server/sse.zig +++ b/zig/src/server/sse.zig @@ -19,6 +19,11 @@ pub fn data(conn: *Conn, a: Allocator, payload: json.Value) error{Closed}!void { return conn.writeAll(std.mem.concat(a, u8, &.{ "data: ", text, "\n\n" }) catch return error.Closed); } +/// An SSE comment line, which clients skip: it keeps a stream alive while nothing else is sent. +pub fn comment(conn: *Conn) error{Closed}!void { + return conn.writeAll(": keepalive\n\n"); +} + pub fn done(conn: *Conn) error{Closed}!void { return conn.writeAll("data: [DONE]\n\n"); } diff --git a/zig/src/server/status_routes.zig b/zig/src/server/status_routes.zig index 8235bbb8f9..3db0c6d335 100644 --- a/zig/src/server/status_routes.zig +++ b/zig/src/server/status_routes.zig @@ -12,6 +12,8 @@ const Allocator = std.mem.Allocator; /// With keys on, health names nothing about the model. pub fn health(srv: *Server, conn: *Conn, a: Allocator) !void { + // draining: the engine is not asked, it may be stopping + if (srv.draining.load(.seq_cst)) return routes.sendDraining(conn, try std.fmt.allocPrint(a, "{{\"status\": \"draining\", \"requests_in_progress\": {d}}}", .{srv.generating.load(.seq_cst)})); var status: api.Status = .{}; srv.engine.status(&status, &.{}); if (!status.healthy) return conn.sendJson(503, "{\"status\": \"unavailable\"}"); diff --git a/zig/src/sessions/disk.zig b/zig/src/sessions/disk.zig new file mode 100644 index 0000000000..f5ab58f06f --- /dev/null +++ b/zig/src/sessions/disk.zig @@ -0,0 +1,587 @@ +//! The NVMe tier: a file an entry under //rank/, written via .tmp + rename, streamed via the lanes. + +const std = @import("std"); +const linux = std.os.linux; +const Io = std.Io; +const lanes_mod = @import("lanes.zig"); +const Lanes = lanes_mod.Lanes; +const Staging = lanes_mod.Staging; +const Op = lanes_mod.Op; +const up = lanes_mod.up; +const format = @import("format.zig"); +const Layout = format.Layout; +const Head = format.Head; +const Seg = format.Seg; +const PageStore = @import("pagestore.zig").PageStore; +const pool_mod = @import("pool.zig"); +const Family = pool_mod.Family; +const Digest = @import("prefix.zig").Digest; + +pub const none = std.math.maxInt(u32); + +pub const Options = struct { + root: []const u8, + compat: Digest, + rank: u32 = 0, + world: u32 = 1, + /// bytes of files the tier keeps (least recently used files without dependents go first) + budget: u64 = 64 << 30, + /// entries shorter than this are dropped, not parked + min_tokens: u64 = 1024, + /// O_DIRECT where the filesystem takes it (buffered with the page cache dropped otherwise) + direct: bool = true, + /// staging buffers and their size: the host transient of any park or restore + staging: u32 = 16, + chunk: u32 = 8 << 20, + lanes: u32 = 4, + /// delta files a chain may stack before a park writes the entry whole + max_chain: u32 = 8, +}; + +/// A piece of bounded state (rings, carries, taps): small, host bytes. +pub const Blob = struct { + name: []const u8, + bytes: []const u8, +}; + +/// What a park writes: the entry, its tokens from first_page on, and this rank's pages per family, in logical order. +pub const WriteSrc = struct { + key: Digest, + /// the file holding pages [0, first_page) (a delta), or null (a whole entry: first_page 0) + base: ?Digest = null, + tag: u32, + kind: u32 = 0, + page: u32, + pos: u64, + first_page: u32 = 0, + npages: u32, + /// tokens [first_page x page, pos) + ids: []const i32, + store: PageStore, + fence: u64 = 0, + families: []const Family, + /// per family: family-tensor pages of this rank's logical pages [first_page, npages), in logical order + pages: []const []const u32, + blobs: []const Blob = &.{}, +}; + +/// One file of a restore: read logical pages [lo, hi) of it into the slot. +pub const Extent = struct { + key: Digest, + first_page: u32, + lo: u32, + hi: u32, +}; + +/// Where a restore puts rows: per family, the family-tensor page of each of the slot's logical pages. +pub const ReadSink = struct { + store: PageStore, + families: []const Family, + page: u32, + world: u32, + rank: u32, + /// per family: family-tensor pages for logical pages [0, npages) + pages: []const []const u32, +}; + +/// The bounded state a restore returns (owned). +pub const Restored = struct { + blobs: []Blob, + + pub fn deinit(r: *Restored, gpa: std.mem.Allocator) void { + for (r.blobs) |b| { + gpa.free(b.name); + gpa.free(b.bytes); + } + gpa.free(r.blobs); + } +}; + +/// The index record of one file. +pub const Rec = struct { + key: Digest, + base: u32 = none, + tag: u32, + kind: u32, + pos: u64, + first_page: u32, + npages: u32, + size: u64, + /// files stacked on this one + refs: u32 = 0, + /// files under it, this one included + depth: u32 = 1, + older: u32 = none, + newer: u32 = none, + live: bool = true, +}; + +pub const Stats = struct { + writes: u64 = 0, + reads: u64 = 0, + bytes_written: u64 = 0, + bytes_read: u64 = 0, + bad: u64 = 0, + evicted: u64 = 0, +}; + +pub const Disk = struct { + gpa: std.mem.Allocator, + io: Io, + opts: Options, + dir: Io.Dir, + staging: Staging, + lanes: *Lanes, + recs: std.ArrayList(Rec) = .empty, + free_recs: std.ArrayList(u32) = .empty, + by_key: std.AutoHashMapUnmanaged(Digest, u32) = .empty, + /// least recently used first + oldest: u32 = none, + newest: u32 = none, + used: u64 = 0, + stats: Stats = .{}, + + pub fn open(gpa: std.mem.Allocator, io: Io, opts: Options) !*Disk { + const d = try gpa.create(Disk); + errdefer gpa.destroy(d); + var name_buf: [64]u8 = undefined; + const sub = try std.fmt.bufPrint(&name_buf, "{x}/rank{d}", .{ opts.compat[0..8], opts.rank }); + var root = try Io.Dir.cwd().createDirPathOpen(io, opts.root, .{}); + defer root.close(io); + const dir = try root.createDirPathOpen(io, sub, .{ .open_options = .{ .iterate = true } }); + errdefer dir.close(io); + d.* = .{ .gpa = gpa, .io = io, .opts = opts, .dir = dir, .staging = try Staging.init(gpa, io, @max(2, opts.staging), up(opts.chunk)), .lanes = undefined }; + errdefer d.staging.deinit(); + d.lanes = try Lanes.start(gpa, io, &d.staging, @max(1, opts.lanes)); + return d; + } + + pub fn close(d: *Disk) void { + d.lanes.shutdown(); + d.staging.deinit(); + d.dir.close(d.io); + d.recs.deinit(d.gpa); + d.free_recs.deinit(d.gpa); + d.by_key.deinit(d.gpa); + const gpa = d.gpa; + gpa.destroy(d); + } + + pub fn accepts(d: *const Disk, pos: u64) bool { + return pos >= d.opts.min_tokens; + } + + pub fn get(d: *Disk, key: Digest) ?*Rec { + const i = d.by_key.get(key) orelse return null; + return &d.recs.items[i]; + } + + fn fileName(key: Digest, ext: []const u8, buf: *[48]u8) [:0]const u8 { + const n = (std.fmt.bufPrint(buf[0..47], "{x}.{s}", .{ &key, ext }) catch unreachable).len; + buf[n] = 0; + return buf[0..n :0]; + } + + fn openFile(d: *Disk, name: [:0]const u8, write: bool) !struct { fd: linux.fd_t, direct: bool } { + var flags: linux.O = .{ .ACCMODE = if (write) .WRONLY else .RDONLY, .CLOEXEC = true }; + if (write) { + flags.CREAT = true; + flags.TRUNC = true; + } + if (d.opts.direct) { + var f2 = flags; + f2.DIRECT = true; + const rc = linux.openat(d.dir.handle, name, f2, 0o644); + if (linux.errno(rc) == .SUCCESS) return .{ .fd = @intCast(rc), .direct = true }; + } + const rc = linux.openat(d.dir.handle, name, flags, 0o644); + if (linux.errno(rc) != .SUCCESS) return if (linux.errno(rc) == .NOENT) error.FileNotFound else error.OpenFailed; + return .{ .fd = @intCast(rc), .direct = false }; + } + + /// The layout a WriteSrc gets: segments, chunk sizes (whole pages a chunk for pool segments). + pub fn plan(d: *Disk, src: *const WriteSrc) !Layout { + var segs: std.ArrayList(Seg) = .empty; + defer segs.deinit(d.gpa); + const cap = d.opts.chunk; + try segs.append(d.gpa, .{ .name = format.segName("ids"), .kind = .ids, .family = 0, .nbytes = src.ids.len * 4, .chunk = cap }); + for (src.families, 0..) |f, i| { + const pb: u32 = @intCast(f.pageBytes(src.page)); + const k = @max(1, cap / pb); + if (@as(u64, k) * pb > d.staging.size) return error.PageTooLarge; + try segs.append(d.gpa, .{ .name = format.segName(f.name), .kind = .pool, .family = @intCast(i), .nbytes = @as(u64, src.pages[i].len) * pb, .chunk = k * pb }); + } + for (src.blobs) |b| try segs.append(d.gpa, .{ .name = format.segName(b.name), .kind = .blob, .family = 0, .nbytes = b.bytes.len, .chunk = cap }); + const head: Head = .{ .key = src.key, .base = src.base orelse @splat(0), .compat = d.opts.compat, .tag = src.tag, .kind = src.kind, .page = src.page, .world = d.opts.world, .rank = d.opts.rank, .first_page = src.first_page, .npages = src.npages, .nseg = 0, .ndigest = 0, .pos = src.pos }; + return Layout.build(d.gpa, head, segs.items); + } + + /// Writes an entry's file streamed (the pump copies pages to staging, lanes hash and write); returns its bytes. + pub fn writeFile(d: *Disk, src: *const WriteSrc) !u64 { + var lay = try d.plan(src); + defer lay.deinit(d.gpa); + var tb: [48]u8 = undefined; + var fb: [48]u8 = undefined; + const tmp = fileName(src.key, "tmp", &tb); + const f = try d.openFile(tmp, true); + var ok = false; + defer { + _ = linux.close(f.fd); + if (!ok) _ = linux.unlinkat(d.dir.handle, tmp, 0); + } + try src.store.waitFence(src.fence); + var op = try Op.init(d.gpa, d.io, &d.staging); + defer op.deinit(); + errdefer op.wait() catch {}; + for (lay.segs) |*s| { + for (0..s.nchunks) |ci| { + if (op.failed()) |e| return e; + const c = s.chunkAt(@intCast(ci)); + const sb = try d.staging.get(); + errdefer d.staging.put(sb); + const buf = d.staging.buf(sb)[0..c.data]; + const at = @as(u64, ci) * s.chunk; + switch (s.kind) { + .ids => @memcpy(buf, std.mem.sliceAsBytes(src.ids)[at..][0..c.data]), + .blob => @memcpy(buf, blobOf(src, s).bytes[at..][0..c.data]), + .pool => { + const pb: u32 = @intCast(src.families[s.family].pageBytes(src.page)); + const p0: usize = @intCast(at / pb); + try src.store.read(s.family, src.pages[s.family][p0..][0 .. c.data / pb], buf); + }, + _ => unreachable, + } + d.lanes.submit(.{ .kind = .write, .fd = f.fd, .staging = sb, .data = c.data, .span = c.span, .off = c.off, .digest = &lay.digests[s.first_digest + ci], .op = &op }); + } + } + try op.wait(); + const hb = try d.staging.get(); + defer d.staging.put(hb); + if (lay.head_span > d.staging.size) return error.HeaderTooLarge; + lay.encode(d.staging.buf(hb)); + try lanes_mod.pwriteAll(f.fd, d.staging.buf(hb)[0..lay.head_span], 0); + if (linux.errno(linux.fdatasync(f.fd)) != .SUCCESS) return error.SyncFailed; + if (!f.direct) _ = linux.fadvise(f.fd, 0, 0, linux.POSIX_FADV.DONTNEED); + if (linux.errno(linux.renameat(d.dir.handle, tmp, d.dir.handle, fileName(src.key, "tfs", &fb))) != .SUCCESS) return error.RenameFailed; + ok = true; + return lay.head.total; + } + + fn blobOf(src: *const WriteSrc, s: *const Seg) Blob { + for (src.blobs) |b| if (std.mem.eql(u8, b.name, s.nameOf())) return b; + unreachable; + } + + /// A file's header, checked. + pub fn readHead(d: *Disk, fd: linux.fd_t) !Layout { + const hb = try d.staging.get(); + defer d.staging.put(hb); + const buf = d.staging.buf(hb); + try lanes_mod.preadAll(fd, buf[0..4096], 0); + const hn = std.mem.readInt(u32, buf[12..16], .little); + if (up(hn) > buf.len) return error.BadHeader; + if (hn > 4096) try lanes_mod.preadAll(fd, buf[4096..up(hn)], 4096); + return Layout.decode(d.gpa, buf[0..up(hn)]); + } + + /// Reads pages [lo, hi) of one restore file into the sink, each chunk checked first; blobs too when want_blobs. + pub fn readFile(d: *Disk, ext: Extent, sink: *const ReadSink, want_blobs: bool) !?Restored { + var nb: [48]u8 = undefined; + const f = try d.openFile(fileName(ext.key, "tfs", &nb), false); + defer _ = linux.close(f.fd); + var lay = try d.readHead(f.fd); + defer lay.deinit(d.gpa); + if (!std.mem.eql(u8, &lay.head.key, &ext.key) or lay.head.first_page != ext.first_page or lay.head.page != sink.page) return error.BadHeader; + var st: linux.Statx = undefined; + if (linux.errno(linux.statx(f.fd, "", linux.AT.EMPTY_PATH, .{ .SIZE = true }, &st)) != .SUCCESS or st.size < lay.head.total) return error.Truncated; + var out: std.ArrayList(Blob) = .empty; + errdefer { + for (out.items) |b| { + d.gpa.free(b.name); + d.gpa.free(b.bytes); + } + out.deinit(d.gpa); + } + var op = try Op.init(d.gpa, d.io, &d.staging); + defer op.deinit(); + errdefer op.wait() catch {}; + // (segment, chunk) jobs; the pump keeps at most staging - 1 in flight (one buffer for the header path) + var jobs: std.ArrayList(struct { seg: u32, chunk: u32 }) = .empty; + defer jobs.deinit(d.gpa); + for (lay.segs, 0..) |*s, si| switch (s.kind) { + .pool => { + if (s.family >= sink.families.len) return error.BadHeader; + const fam = sink.families[s.family]; + const pb: u32 = @intCast(fam.pageBytes(sink.page)); + if (s.chunk % pb != 0 or !std.mem.eql(u8, s.nameOf(), fam.name)) return error.BadHeader; + const need = ownedIn(fam, sink, ext.first_page, ext.hi) - ownedIn(fam, sink, ext.first_page, ext.lo); + const skip = ownedIn(fam, sink, ext.first_page, ext.lo); + if (s.nbytes < (@as(u64, skip) + need) * pb) return error.BadHeader; + const kpp = s.chunk / pb; + const c0 = skip / kpp; + const c1 = if (need == 0) c0 else (skip + need + kpp - 1) / kpp; + for (c0..c1) |c| try jobs.append(d.gpa, .{ .seg = @intCast(si), .chunk = @intCast(c) }); + }, + .blob => if (want_blobs) { + const buf = try d.gpa.alloc(u8, s.nbytes); + errdefer d.gpa.free(buf); + const name = try d.gpa.dupe(u8, s.nameOf()); + errdefer d.gpa.free(name); + try out.append(d.gpa, .{ .name = name, .bytes = buf }); + for (0..s.nchunks) |c| try jobs.append(d.gpa, .{ .seg = @intCast(si), .chunk = @intCast(c) }); + }, + else => {}, + }; + const window = d.staging.count - 1; + var sent: usize = 0; + var seen: usize = 0; + var bytes: u64 = 0; + while (seen < jobs.items.len) { + while (sent < jobs.items.len and sent - seen < window and op.failed() == null) : (sent += 1) { + const jb = jobs.items[sent]; + const s = &lay.segs[jb.seg]; + const c = s.chunkAt(jb.chunk); + d.lanes.submit(.{ .kind = .read, .fd = f.fd, .staging = try d.staging.get(), .data = c.data, .span = c.span, .off = c.off, .digest = &lay.digests[s.first_digest + jb.chunk], .op = &op, .tag = @intCast(sent) }); + bytes += c.span; + } + const j = op.next() orelse break; + seen += 1; + defer d.staging.put(j.staging); + if (op.failed() != null) continue; + const jb = jobs.items[j.tag]; + const s = &lay.segs[jb.seg]; + const data = d.staging.buf(j.staging)[0..j.data]; + if (s.kind == .blob) { + for (out.items) |b| if (std.mem.eql(u8, b.name, s.nameOf())) @memcpy(@constCast(b.bytes)[@as(u64, jb.chunk) * s.chunk ..][0..data.len], data); + continue; + } + try d.drain(s, jb.chunk, data, ext, sink); + } + try op.wait(); + if (!f.direct) _ = linux.fadvise(f.fd, 0, 0, linux.POSIX_FADV.DONTNEED); + d.stats.bytes_read += bytes; + if (!want_blobs) { + out.deinit(d.gpa); + return null; + } + return .{ .blobs = try out.toOwnedSlice(d.gpa) }; + } + + /// A checked pool chunk's pages that fall in [lo, hi) into the sink's pages. + fn drain(d: *Disk, s: *const Seg, chunk: u32, data: []const u8, ext: Extent, sink: *const ReadSink) !void { + _ = d; + const fam = sink.families[s.family]; + const pb: u32 = @intCast(fam.pageBytes(sink.page)); + const kpp = s.chunk / pb; + const skip = ownedIn(fam, sink, ext.first_page, ext.lo); + const end = ownedIn(fam, sink, ext.first_page, ext.hi); + const a = @max(chunk * kpp, skip); + const b = @min(chunk * kpp + @as(u32, @intCast(data.len / pb)), end); + if (a >= b) return; + var list: [512]u32 = undefined; + var n: u32 = 0; + var j = a; + while (j < b) : (j += 1) { + list[n] = sink.pages[s.family][logicalOf(fam, sink, ext.first_page, j)]; + n += 1; + if (n == list.len or j + 1 == b) { + const first = j + 1 - n; + try sink.store.write(s.family, list[0..n], data[(first - chunk * kpp) * pb ..][0 .. n * pb]); + n = 0; + } + } + } + + /// Records a written file in the index (round thread, at the plan's point); its base gains a dependent. + pub fn register(d: *Disk, src: *const WriteSrc, size: u64) !u32 { + const id = try d.add(src.key, src.base, src.tag, src.kind, src.pos, src.first_page, src.npages, size); + d.stats.writes += 1; + d.stats.bytes_written += size; + return id; + } + + fn add(d: *Disk, key: Digest, base: ?Digest, tag: u32, kind: u32, pos: u64, first_page: u32, npages: u32, size: u64) !u32 { + var r: Rec = .{ .key = key, .tag = tag, .kind = kind, .pos = pos, .first_page = first_page, .npages = npages, .size = size }; + if (base) |b| { + const bi = d.by_key.get(b) orelse return error.NoBase; + r.base = bi; + r.depth = d.recs.items[bi].depth + 1; + } + const id: u32 = if (d.free_recs.pop()) |i| i else blk: { + try d.recs.append(d.gpa, undefined); + break :blk @intCast(d.recs.items.len - 1); + }; + try d.by_key.put(d.gpa, key, id); + if (r.base != none) d.recs.items[r.base].refs += 1; + d.recs.items[id] = r; + d.link(id); + d.used += size; + return id; + } + + /// A valid file found on restart: its header, size and verified ids (tokens [first_page x page, pos), owned). + pub const Found = struct { + head: Head, + size: u64, + mtime: i64, + ids: []i32, + + fn older(_: void, a: Found, b: Found) bool { + return a.mtime < b.mtime or (a.mtime == b.mtime and a.head.first_page < b.head.first_page); + } + }; + + /// Indexes the directory on restart: drops .tmp and bad files, checks ids, registers bases before their deltas. + pub fn scan(d: *Disk, out: *std.ArrayList(Found)) !void { + var it = d.dir.iterate(); + var nb: [48]u8 = undefined; + while (try it.next(d.io)) |ent| { + if (ent.name.len >= nb.len) continue; + @memcpy(nb[0..ent.name.len], ent.name); + nb[ent.name.len] = 0; + const name = nb[0..ent.name.len :0]; + if (std.mem.endsWith(u8, name, ".tmp")) { + _ = linux.unlinkat(d.dir.handle, name, 0); + continue; + } + if (!std.mem.endsWith(u8, name, ".tfs")) continue; + const f = d.found(name) catch { + d.stats.bad += 1; + _ = linux.unlinkat(d.dir.handle, name, 0); + continue; + }; + try out.append(d.gpa, f); + } + std.mem.sort(Found, out.items, {}, Found.older); + var i: usize = 0; + while (i < out.items.len) { + const f = out.items[i]; + const base: ?Digest = if (f.head.hasBase()) f.head.base else null; + if (base != null and d.by_key.get(base.?) == null) { + d.stats.bad += 1; + var kb: [48]u8 = undefined; + _ = linux.unlinkat(d.dir.handle, fileName(f.head.key, "tfs", &kb), 0); + d.gpa.free(f.ids); + _ = out.orderedRemove(i); + continue; + } + _ = try d.add(f.head.key, base, f.head.tag, f.head.kind, f.head.pos, f.head.first_page, f.head.npages, f.size); + i += 1; + } + } + + fn found(d: *Disk, name: [:0]const u8) !Found { + const f = try d.openFile(name, false); + defer _ = linux.close(f.fd); + var lay = try d.readHead(f.fd); + defer lay.deinit(d.gpa); + const h = lay.head; + var kb: [48]u8 = undefined; + if (!std.mem.eql(u8, fileName(h.key, "tfs", &kb), name)) return error.BadHeader; + if (!std.mem.eql(u8, &h.compat, &d.opts.compat) or h.rank != d.opts.rank or h.world != d.opts.world) return error.Foreign; + var st: linux.Statx = undefined; + if (linux.errno(linux.statx(f.fd, "", linux.AT.EMPTY_PATH, .{ .SIZE = true, .MTIME = true }, &st)) != .SUCCESS or st.size < h.total) return error.Truncated; + const seg = lay.find(.ids, "ids") orelse return error.BadHeader; + if (seg.nbytes % 4 != 0 or seg.nbytes / 4 != h.pos - @as(u64, h.first_page) * h.page) return error.BadHeader; + const ids = try d.gpa.alloc(i32, seg.nbytes / 4); + errdefer d.gpa.free(ids); + const sb = try d.staging.get(); + defer d.staging.put(sb); + for (0..seg.nchunks) |ci| { + const c = seg.chunkAt(@intCast(ci)); + const buf = d.staging.buf(sb); + try lanes_mod.preadAll(f.fd, buf[0..c.span], c.off); + var dg: [32]u8 = undefined; + std.crypto.hash.sha2.Sha256.hash(buf[0..c.data], &dg, .{}); + if (!std.mem.eql(u8, &dg, &lay.digests[seg.first_digest + ci])) return error.ChecksumMismatch; + @memcpy(std.mem.sliceAsBytes(ids)[@as(usize, ci) * seg.chunk ..][0..c.data], buf[0..c.data]); + } + return .{ .head = h, .size = st.size, .mtime = st.mtime.sec * std.time.ns_per_s + st.mtime.nsec, .ids = ids }; + } + + fn link(d: *Disk, id: u32) void { + const r = &d.recs.items[id]; + r.older = d.newest; + r.newer = none; + if (d.newest != none) d.recs.items[d.newest].newer = id else d.oldest = id; + d.newest = id; + } + + fn unlink(d: *Disk, id: u32) void { + const r = &d.recs.items[id]; + if (r.older != none) d.recs.items[r.older].newer = r.newer else d.oldest = r.newer; + if (r.newer != none) d.recs.items[r.newer].older = r.older else d.newest = r.older; + } + + /// Marks a file (and its bases) used now: O(chain). + pub fn touch(d: *Disk, key: Digest) void { + var id = d.by_key.get(key) orelse return; + while (id != none) : (id = d.recs.items[id].base) { + d.unlink(id); + d.link(id); + } + // bases end up newest: walk order keeps the entry itself older than its bases, so trims reach dependents first + } + + /// The files a restore of key reads, base first, with the logical pages each gives. + pub fn extents(d: *Disk, key: Digest, out: *std.ArrayList(Extent)) !void { + out.clearRetainingCapacity(); + var id = d.by_key.get(key) orelse return error.FileNotFound; + var hi = d.recs.items[id].npages; + while (id != none) { + const r = &d.recs.items[id]; + try out.append(d.gpa, .{ .key = r.key, .first_page = r.first_page, .lo = r.first_page, .hi = hi }); + hi = r.first_page; + id = r.base; + } + std.mem.reverse(Extent, out.items); + } + + /// Deletes a file without dependents (error when one stacks on it). + pub fn remove(d: *Disk, key: Digest) !void { + const id = d.by_key.get(key) orelse return; + const r = &d.recs.items[id]; + if (r.refs > 0) return error.HasDependents; + if (r.base != none) d.recs.items[r.base].refs -= 1; + d.unlink(id); + _ = d.by_key.remove(key); + d.used -= r.size; + r.live = false; + var nb: [48]u8 = undefined; + _ = linux.unlinkat(d.dir.handle, fileName(key, "tfs", &nb), 0); + try d.free_recs.append(d.gpa, id); + } + + /// Deletes least recently used files without dependents while over budget; their keys go to out. + pub fn trim(d: *Disk, out: *std.ArrayList(Digest)) !void { + var id = d.oldest; + while (d.used > d.opts.budget and id != none) { + const next = d.recs.items[id].newer; + if (d.recs.items[id].refs == 0) { + const key = d.recs.items[id].key; + try d.remove(key); + try out.append(d.gpa, key); + d.stats.evicted += 1; + id = d.oldest; // a removed file may free its base + continue; + } + id = next; + } + } +}; + +/// Owned (this rank's) logical pages of family f in [first, k): the file's index of logical page k. +pub fn ownedIn(f: Family, sink: *const ReadSink, first: u32, k: u32) u32 { + if (!(f.split and sink.world > 1)) return k - first; + var by: [pool_mod.max_world]u32 = undefined; + pool_mod.span(first, k, sink.world, &by); + return by[sink.rank]; +} + +/// The logical page of a family's j-th stored page of a file that starts at first. +pub fn logicalOf(f: Family, sink: *const ReadSink, first: u32, j: u32) u32 { + if (!(f.split and sink.world > 1)) return first + j; + const w = sink.world; + const r0 = first + (sink.rank + w - first % w) % w; // the first owned logical page at or after first + return r0 + j * w; +} diff --git a/zig/src/sessions/disk_test.zig b/zig/src/sessions/disk_test.zig new file mode 100644 index 0000000000..6ac0525e45 --- /dev/null +++ b/zig/src/sessions/disk_test.zig @@ -0,0 +1,125 @@ +//! The NVMe tier on real files: whole and delta files round-trip bit for bit, split ranks, damage. + +const std = @import("std"); +const testing = std.testing; +const pool_mod = @import("pool.zig"); +const Pool = pool_mod.Pool; +const Family = pool_mod.Family; +const HostPool = @import("pagestore.zig").HostPool; +const disk = @import("disk.zig"); + +const fams = [_]Family{ + .{ .name = "comp.2", .ratio = 2, .row_bytes = 584, .split = true }, + .{ .name = "index_k.2", .ratio = 2, .row_bytes = 132 }, + .{ .name = "comp.20", .ratio = 1, .row_bytes = 584, .split = true }, +}; + +/// Fills a page of every family with bytes that name (family, logical page, byte). +fn fill(h: *HostPool, p: *const Pool, logical: u32, pg: u32, salt: u8) void { + for (fams, 0..) |f, fi| { + if (f.split and !p.owns(pg)) continue; + const v = h.pageOf(@intCast(fi), p.familyPage(f, pg)); + for (v, 0..) |*b, i| b.* = @truncate(i *% 131 +% logical *% 7 +% fi *% 29 +% salt); + } +} + +fn familyPages(gpa: std.mem.Allocator, p: *const Pool, pages: []const u32, first: u32, owned_only: bool) ![][]u32 { + const out = try gpa.alloc([]u32, fams.len); + for (fams, 0..) |f, fi| { + var l: std.ArrayList(u32) = .empty; + for (pages[first..], first..) |pg, k| { + if (f.split and p.world > 1 and owned_only and k % p.world != p.rank) continue; + try l.append(gpa, p.familyPage(f, pg)); + } + out[fi] = try l.toOwnedSlice(gpa); + } + return out; +} + +fn freePages(gpa: std.mem.Allocator, x: [][]u32) void { + for (x) |l| gpa.free(l); + gpa.free(x); +} + +fn roundTrip(world: u32, rank: u32) !void { + const gpa = testing.allocator; + const io = testing.io; + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + const root = try tmp.dir.realPathFileAlloc(io, ".", gpa); + defer gpa.free(root); + var p = try Pool.init(gpa, .{ .families = &fams, .page = 256 }, 64 * 256, world, rank); + defer p.deinit(); + var h = try HostPool.init(gpa, &p); + defer h.deinit(); + // small chunks so pool segments span many chunks; few staging buffers so the pump waits on the lanes + const d = try disk.Disk.open(gpa, io, .{ .root = root, .compat = @splat(5), .rank = rank, .world = world, .chunk = 3 * 74_752, .staging = 3, .lanes = 3, .min_tokens = 1 }); + defer d.close(); + const a = try p.newSlot(32 * 256); + const pos: u64 = 9 * 256 + 100; + try a.ensure(pos); + for (a.mapped(), 0..) |pg, k| fill(&h, &p, @intCast(k), pg, 1); + const ids = try gpa.alloc(i32, pos); + defer gpa.free(ids); + for (ids, 0..) |*t, i| t.* = @intCast(i * 3 + 1); + var blob: [5000]u8 = undefined; + for (&blob, 0..) |*x, i| x.* = @truncate(9 - i % 5); + const fp = try familyPages(gpa, &p, a.mapped(), 0, true); + defer freePages(gpa, fp); + const src: disk.WriteSrc = .{ .key = @splat(1), .tag = 4, .page = 256, .pos = pos, .npages = a.len, .ids = ids, .store = h.store(), .families = &fams, .pages = fp, .blobs = &.{.{ .name = "rings", .bytes = &blob }} }; + const size = try d.writeFile(&src); + _ = try d.register(&src, size); + // a newer turn stacks a delta on it: pages [9, 14) only (page 9 was partial in the base) + const pos2: u64 = 13 * 256 + 7; + try a.ensure(pos2); + for (a.mapped()[9..], 9..) |pg, k| fill(&h, &p, @intCast(k), pg, 2); + const ids2 = try gpa.alloc(i32, pos2); + defer gpa.free(ids2); + for (ids2, 0..) |*t, i| t.* = @intCast(i * 3 + 1); + const fp2 = try familyPages(gpa, &p, a.mapped(), 9, true); + defer freePages(gpa, fp2); + const src2: disk.WriteSrc = .{ .key = @splat(2), .base = @splat(1), .tag = 4, .page = 256, .pos = pos2, .first_page = 9, .npages = a.len, .ids = ids2[9 * 256 ..], .store = h.store(), .families = &fams, .pages = fp2, .blobs = &.{.{ .name = "rings", .bytes = blob[0..77] }} }; + const size2 = try d.writeFile(&src2); + _ = try d.register(&src2, size2); + try testing.expect(size2 < size); + try testing.expectError(error.HasDependents, d.remove(@splat(1))); + // restore the delta onto other pages through its chain + const b = try p.newSlot(32 * 256); + try b.ensure(pos2); + var ext: std.ArrayList(disk.Extent) = .empty; + defer ext.deinit(gpa); + try d.extents(@splat(2), &ext); + try testing.expectEqual(@as(usize, 2), ext.items.len); + try testing.expectEqual(@as(u32, 9), ext.items[0].hi); + const tp = try familyPages(gpa, &p, b.mapped(), 0, false); + defer freePages(gpa, tp); + const sink: disk.ReadSink = .{ .store = h.store(), .families = &fams, .page = 256, .world = world, .rank = rank, .pages = tp }; + try testing.expect(try d.readFile(ext.items[0], &sink, false) == null); + var got = (try d.readFile(ext.items[1], &sink, true)).?; + defer got.deinit(gpa); + try testing.expectEqualSlices(u8, blob[0..77], got.blobs[0].bytes); + for (a.mapped(), b.mapped()) |pa, pb| for (fams, 0..) |f, fi| { + if (f.split and !p.owns(pa)) continue; + try testing.expectEqualSlices(u8, h.pageOf(@intCast(fi), p.familyPage(f, pa)), h.pageOf(@intCast(fi), p.familyPage(f, pb))); + }; + // damage one chunk: the read refuses it before or as its rows land + var nb: [48]u8 = undefined; + const key2: [16]u8 = @splat(2); + const name = try std.fmt.bufPrint(&nb, "{x}.tfs", .{&key2}); + const f = try d.dir.openFile(io, name, .{ .mode = .read_write }); + var one = [_]u8{0}; + _ = try f.readPositional(io, &.{&one}, 3 * 4096 + 100); + one[0] ^= 0xff; + try f.writePositionalAll(io, &one, 3 * 4096 + 100); + f.close(io); + try testing.expectError(error.ChecksumMismatch, d.readFile(ext.items[1], &sink, true)); +} + +test "whole and delta files round-trip bit for bit (replicated)" { + try roundTrip(1, 0); +} + +test "split ranks write and restore their own pages (rank 0 and 1 of 2)" { + try roundTrip(2, 0); + try roundTrip(2, 1); +} diff --git a/zig/src/sessions/format.zig b/zig/src/sessions/format.zig new file mode 100644 index 0000000000..2ac391d156 --- /dev/null +++ b/zig/src/sessions/format.zig @@ -0,0 +1,197 @@ +//! A session file (format 2): a header, then segments of 4 KiB-aligned chunks, each with its own SHA-256. + +const std = @import("std"); +const Sha256 = std.crypto.hash.sha2.Sha256; +const up = @import("lanes.zig").up; + +pub const magic = "TFSESS02".*; +pub const version: u32 = 2; + +pub const SegKind = enum(u32) { ids = 1, pool = 2, blob = 3, _ }; + +pub const Head = extern struct { + magic: [8]u8 = magic, + version: u32 = version, + /// bytes of Head + segments + digests (before the pad) + head_bytes: u32 = 0, + /// SHA-256 of those bytes with this field zero + head_sha: [32]u8 = @splat(0), + key: [16]u8, + /// the entry whose files hold logical pages [0, first_page); zero: none + base: [16]u8 = @splat(0), + /// what makes these bytes mean the same thing to a build (family, knobs, layout, split) + compat: [16]u8, + tag: u32, + kind: u32, + page: u32, + world: u32, + rank: u32, + first_page: u32, + npages: u32, + nseg: u32, + ndigest: u32, + _pad: u32 = 0, + pos: u64, + total: u64 = 0, + + pub fn hasBase(h: *const Head) bool { + return !std.mem.allEqual(u8, &h.base, 0); + } +}; + +pub const Seg = extern struct { + name: [40]u8, + kind: SegKind, + /// pool: the family's index in the layout + family: u32, + nbytes: u64, + offset: u64 = 0, + /// bytes a chunk (the last may be shorter) and file bytes a chunk (4 KiB multiple) + chunk: u32, + stride: u32 = 0, + first_digest: u32 = 0, + nchunks: u32 = 0, + + pub fn nameOf(s: *const Seg) []const u8 { + return std.mem.sliceTo(&s.name, 0); + } + + /// Chunk i's data bytes and file span. + pub fn chunkAt(s: *const Seg, i: u32) struct { off: u64, data: u32, span: u32 } { + const data: u32 = @intCast(@min(@as(u64, s.chunk), s.nbytes - @as(u64, i) * s.chunk)); + return .{ .off = s.offset + @as(u64, i) * s.stride, .data = data, .span = @intCast(up(data)) }; + } +}; + +pub const Digest32 = [32]u8; + +pub fn segName(name: []const u8) [40]u8 { + var n: [40]u8 = @splat(0); + @memcpy(n[0..@min(name.len, 39)], name[0..@min(name.len, 39)]); + return n; +} + +/// A file's layout: offsets, chunks and the digest slots, before or after its chunks are written. +pub const Layout = struct { + head: Head, + segs: []Seg, + digests: []Digest32, + /// file bytes of the padded header + head_span: u64, + + /// Places segments (each with name, kind, family, nbytes, chunk) after the header. + pub fn build(gpa: std.mem.Allocator, head: Head, segs_in: []const Seg) !Layout { + const segs = try gpa.dupe(Seg, segs_in); + errdefer gpa.free(segs); + var nd: u32 = 0; + for (segs) |*s| { + if (s.chunk == 0) return error.BadSegment; + s.nchunks = @intCast((s.nbytes + s.chunk - 1) / s.chunk); + s.stride = @intCast(up(s.chunk)); + s.first_digest = nd; + nd += s.nchunks; + } + var h = head; + h.nseg = @intCast(segs.len); + h.ndigest = nd; + h.head_bytes = @intCast(@sizeOf(Head) + segs.len * @sizeOf(Seg) + nd * @sizeOf(Digest32)); + const span = up(h.head_bytes); + var off = span; + for (segs) |*s| { + s.offset = off; + if (s.nchunks > 0) off += @as(u64, s.nchunks - 1) * s.stride + up(s.nbytes - @as(u64, s.nchunks - 1) * s.chunk); + } + h.total = off; + const d = try gpa.alloc(Digest32, nd); + @memset(d, @splat(0)); + return .{ .head = h, .segs = segs, .digests = d, .head_span = span }; + } + + pub fn deinit(l: *Layout, gpa: std.mem.Allocator) void { + gpa.free(l.segs); + gpa.free(l.digests); + l.* = undefined; + } + + /// The header's bytes with its checksum into out (len >= head_span; the pad is zeroed). + pub fn encode(l: *Layout, out: []u8) void { + @memset(out[0..l.head_span], 0); + var h = l.head; + h.head_sha = @splat(0); + const hb = std.mem.asBytes(&h); + const sb = std.mem.sliceAsBytes(l.segs); + const db = std.mem.sliceAsBytes(l.digests); + @memcpy(out[0..hb.len], hb); + @memcpy(out[hb.len..][0..sb.len], sb); + @memcpy(out[hb.len + sb.len ..][0..db.len], db); + var sha: [32]u8 = undefined; + Sha256.hash(out[0..h.head_bytes], &sha, .{}); + @memcpy(out[@offsetOf(Head, "head_sha")..][0..32], &sha); + l.head.head_sha = sha; + } + + /// The header read back from bytes (at least head_bytes of them), every field and offset checked. + pub fn decode(gpa: std.mem.Allocator, bytes: []const u8) !Layout { + if (bytes.len < @sizeOf(Head)) return error.BadHeader; + var h: Head = undefined; + @memcpy(std.mem.asBytes(&h), bytes[0..@sizeOf(Head)]); + if (!std.mem.eql(u8, &h.magic, &magic) or h.version != version) return error.BadHeader; + const want = @sizeOf(Head) + @as(u64, h.nseg) * @sizeOf(Seg) + @as(u64, h.ndigest) * @sizeOf(Digest32); + if (h.head_bytes != want or bytes.len < want) return error.BadHeader; + var check: [32]u8 = undefined; + var sha = Sha256.init(.{}); + sha.update(bytes[0..@offsetOf(Head, "head_sha")]); + sha.update(&@as([32]u8, @splat(0))); + sha.update(bytes[@offsetOf(Head, "head_sha") + 32 .. want]); + sha.final(&check); + if (!std.mem.eql(u8, &check, &h.head_sha)) return error.ChecksumMismatch; + const segs = try gpa.alloc(Seg, h.nseg); + errdefer gpa.free(segs); + @memcpy(std.mem.sliceAsBytes(segs), bytes[@sizeOf(Head)..][0 .. segs.len * @sizeOf(Seg)]); + const d = try gpa.alloc(Digest32, h.ndigest); + errdefer gpa.free(d); + @memcpy(std.mem.sliceAsBytes(d), bytes[@sizeOf(Head) + segs.len * @sizeOf(Seg) ..][0 .. d.len * 32]); + var nd: u64 = 0; + for (segs) |s| { + if (s.chunk == 0 or s.stride != up(s.chunk) or s.first_digest != nd or s.offset % 4096 != 0) return error.BadHeader; + if (s.nchunks != (s.nbytes + s.chunk - 1) / s.chunk) return error.BadHeader; + if (s.nchunks > 0 and s.offset + @as(u64, s.nchunks - 1) * s.stride + up(s.nbytes - @as(u64, s.nchunks - 1) * s.chunk) > h.total) return error.BadHeader; + nd += s.nchunks; + } + if (nd != h.ndigest) return error.BadHeader; + return .{ .head = h, .segs = segs, .digests = d, .head_span = up(h.head_bytes) }; + } + + pub fn find(l: *const Layout, kind: SegKind, name: []const u8) ?*const Seg { + for (l.segs) |*s| if (s.kind == kind and std.mem.eql(u8, s.nameOf(), name)) return s; + return null; + } +}; + +test "a layout encodes, decodes, and refuses a damaged header" { + const gpa = std.testing.allocator; + const head: Head = .{ .key = @splat(1), .compat = @splat(2), .tag = 3, .kind = 0, .page = 256, .world = 2, .rank = 1, .first_page = 4, .npages = 9, .nseg = 0, .ndigest = 0, .pos = 2200 }; + var l = try Layout.build(gpa, head, &.{ + .{ .name = segName("ids"), .kind = .ids, .family = 0, .nbytes = 4 * (2200 - 1024), .chunk = 1 << 20 }, + .{ .name = segName("comp.20"), .kind = .pool, .family = 0, .nbytes = 3 * 149_504, .chunk = 2 * 149_504 }, + .{ .name = segName("taps"), .kind = .blob, .family = 0, .nbytes = 0, .chunk = 4096 }, + }); + defer l.deinit(gpa); + try std.testing.expectEqual(@as(u32, 1 + 2 + 0), l.head.ndigest); + try std.testing.expectEqual(@as(u64, 4096), l.segs[0].offset); + try std.testing.expectEqual(@as(u64, 4096 + up(4 * 1176)), l.segs[1].offset); + const c1 = l.segs[1].chunkAt(1); + try std.testing.expectEqual(@as(u32, 149_504), c1.data); + try std.testing.expectEqual(l.segs[1].offset + up(2 * 149_504), c1.off); + try std.testing.expectEqual(l.segs[2].offset, l.head.total); + l.digests[1][5] = 9; + var buf: [8192]u8 = undefined; + l.encode(&buf); + var back = try Layout.decode(gpa, &buf); + defer back.deinit(gpa); + try std.testing.expectEqual(l.head.total, back.head.total); + try std.testing.expectEqual(@as(u8, 9), back.digests[1][5]); + try std.testing.expectEqualStrings("comp.20", back.find(.pool, "comp.20").?.nameOf()); + buf[200] ^= 0x40; + try std.testing.expectError(error.ChecksumMismatch, Layout.decode(gpa, &buf)); +} diff --git a/zig/src/sessions/freeset.zig b/zig/src/sessions/freeset.zig new file mode 100644 index 0000000000..b8cce323af --- /dev/null +++ b/zig/src/sessions/freeset.zig @@ -0,0 +1,114 @@ +//! A set of free page indices, lowest first in O(log64 n): a bit a page, then a bit a non-empty word a level up. + +const std = @import("std"); + +const max_levels = 6; // 64^6 pages: more than any pool + +pub const FreeSet = struct { + /// levels[0] holds a bit a page, levels[k] a bit a non-empty word of levels[k - 1] + levels: [max_levels][]u64 = @splat(&.{}), + depth: u8 = 0, + n: u32 = 0, + free: u32 = 0, + + /// All of 0 .. n - 1 free. + pub fn init(gpa: std.mem.Allocator, n: u32) !FreeSet { + var s: FreeSet = .{ .n = n }; + errdefer s.deinit(gpa); + var bits: usize = @max(n, 1); + while (true) { + const words = (bits + 63) / 64; + s.levels[s.depth] = try gpa.alloc(u64, words); + @memset(s.levels[s.depth], 0); + s.depth += 1; + if (words == 1) break; + bits = words; + } + for (0..n) |i| s.setBit(@intCast(i)); + s.free = n; + return s; + } + + pub fn deinit(s: *FreeSet, gpa: std.mem.Allocator) void { + for (s.levels[0..s.depth]) |l| gpa.free(l); + s.* = .{}; + } + + pub fn count(s: *const FreeSet) u32 { + return s.free; + } + + pub fn contains(s: *const FreeSet, i: u32) bool { + return s.levels[0][i / 64] & (@as(u64, 1) << @intCast(i % 64)) != 0; + } + + /// The lowest free index, taken; null when none is free. + pub fn takeLowest(s: *FreeSet) ?u32 { + if (s.free == 0) return null; + var idx: usize = 0; + var lv: usize = s.depth; + while (lv > 0) { + lv -= 1; + const w = s.levels[lv][idx]; + idx = idx * 64 + @ctz(w); + } + const i: u32 = @intCast(idx); + s.clearBit(i); + s.free -= 1; + return i; + } + + /// Takes a given free index (error when it is not free). + pub fn take(s: *FreeSet, i: u32) error{NotFree}!void { + if (i >= s.n or !s.contains(i)) return error.NotFree; + s.clearBit(i); + s.free -= 1; + } + + /// Gives an index back (error when it is already free: a double free). + pub fn give(s: *FreeSet, i: u32) error{DoubleFree}!void { + if (i >= s.n or s.contains(i)) return error.DoubleFree; + s.setBit(i); + s.free += 1; + } + + fn setBit(s: *FreeSet, i: u32) void { + var idx: usize = i; + for (s.levels[0..s.depth]) |l| { + const was = l[idx / 64]; + l[idx / 64] = was | (@as(u64, 1) << @intCast(idx % 64)); + if (was != 0) return; // the word was non-empty: the levels above already say so + idx /= 64; + } + } + + fn clearBit(s: *FreeSet, i: u32) void { + var idx: usize = i; + for (s.levels[0..s.depth]) |l| { + l[idx / 64] &= ~(@as(u64, 1) << @intCast(idx % 64)); + if (l[idx / 64] != 0) return; // the word still has a free bit: the levels above stay set + idx /= 64; + } + } +}; + +test "lowest first, give back, double free refused, across levels" { + const gpa = std.testing.allocator; + var s = try FreeSet.init(gpa, 70_000); // three levels + defer s.deinit(gpa); + try std.testing.expectEqual(@as(u8, 3), s.depth); + for (0..5000) |i| try std.testing.expectEqual(@as(?u32, @intCast(i)), s.takeLowest()); + try s.give(4097); + try s.give(17); + try std.testing.expectError(error.DoubleFree, s.give(17)); + try std.testing.expectEqual(@as(?u32, 17), s.takeLowest()); + try std.testing.expectEqual(@as(?u32, 4097), s.takeLowest()); + try std.testing.expectEqual(@as(?u32, 5000), s.takeLowest()); + try s.take(69_999); + try std.testing.expectError(error.NotFree, s.take(69_999)); + try std.testing.expectEqual(@as(u32, 70_000 - 5001 - 1), s.count()); + while (s.takeLowest()) |_| {} + try std.testing.expectEqual(@as(u32, 0), s.count()); + try s.give(69_999); + try std.testing.expectEqual(@as(?u32, 69_999), s.takeLowest()); +} diff --git a/zig/src/sessions/jobs.zig b/zig/src/sessions/jobs.zig new file mode 100644 index 0000000000..5b445f20e7 --- /dev/null +++ b/zig/src/sessions/jobs.zig @@ -0,0 +1,150 @@ +//! Parks and restores off the round: a tier thread runs each job's file I/O until the plan settles it (Store.settle). + +const std = @import("std"); +const Io = std.Io; +const disk_mod = @import("disk.zig"); +const Disk = disk_mod.Disk; + +pub const Kind = enum { park, restore }; +pub const State = enum(u8) { queued, running, done, failed }; + +pub const Job = struct { + kind: Kind, + /// the store's entry + entry: u32, + state: std.atomic.Value(State) = .init(.queued), + err: ?anyerror = null, + started_ns: i96 = 0, + elapsed_ns: u64 = 0, + /// park: the file to write (owned buffers below) and its size once written + src: disk_mod.WriteSrc = undefined, + size: u64 = 0, + /// restore: files base first, the sink, the bounded state read + extents: []disk_mod.Extent = &.{}, + sink: disk_mod.ReadSink = undefined, + restored: ?disk_mod.Restored = null, + /// restore: the slot it fills + slot: u32 = 0, + /// pool pages the job holds until settled + held: []u32 = &.{}, + /// owned buffers + ids: []i32 = &.{}, + lists: [][]u32 = &.{}, + blobs: []disk_mod.Blob = &.{}, + + pub fn done(j: *const Job) bool { + const s = j.state.load(.acquire); + return s == .done or s == .failed; + } + + pub fn deinit(j: *Job, gpa: std.mem.Allocator) void { + gpa.free(j.held); + gpa.free(j.ids); + for (j.lists) |l| gpa.free(l); + gpa.free(j.lists); + for (j.blobs) |b| { + gpa.free(b.name); + gpa.free(b.bytes); + } + gpa.free(j.blobs); + gpa.free(j.extents); + if (j.restored) |*r| r.deinit(gpa); + gpa.destroy(j); + } + + fn run(j: *Job, d: *Disk, io: Io) void { + j.state.store(.running, .release); + const t0 = Io.Clock.awake.now(io).toNanoseconds(); + j.err = switch (j.kind) { + .park => if (d.writeFile(&j.src)) |n| blk: { + j.size = n; + break :blk null; + } else |e| e, + .restore => j.restore(d), + }; + j.elapsed_ns = @intCast(Io.Clock.awake.now(io).toNanoseconds() - t0); + j.state.store(if (j.err == null) .done else .failed, .release); + } + + fn restore(j: *Job, d: *Disk) ?anyerror { + for (j.extents, 0..) |e, i| { + const last = i + 1 == j.extents.len; + const r = d.readFile(e, &j.sink, last) catch |err| return err; + if (last) j.restored = r; + } + j.sink.store.publish() catch |err| return err; + return null; + } +}; + +/// The tier thread and its queue (null thread: jobs run inline). +pub const Runner = struct { + gpa: std.mem.Allocator, + io: Io, + disk: *Disk, + thread: ?std.Thread = null, + queue: std.ArrayList(*Job) = .empty, + head: usize = 0, + mutex: Io.Mutex = .init, + cond: Io.Condition = .init, + stop: bool = false, + + pub fn init(gpa: std.mem.Allocator, io: Io, d: *Disk) Runner { + return .{ .gpa = gpa, .io = io, .disk = d }; + } + + /// Starts the tier thread: jobs then run off the caller's thread. + pub fn startThread(r: *Runner) !void { + if (r.thread != null) return; + r.thread = try std.Thread.spawn(.{ .stack_size = 1 << 20 }, loop, .{r}); + } + + pub fn deinit(r: *Runner) void { + if (r.thread) |t| { + r.mutex.lockUncancelable(r.io); + r.stop = true; + r.cond.broadcast(r.io); + r.mutex.unlock(r.io); + t.join(); + } + r.queue.deinit(r.gpa); + } + + pub fn submit(r: *Runner, j: *Job) !void { + if (r.thread == null) return j.run(r.disk, r.io); + r.mutex.lockUncancelable(r.io); + defer r.mutex.unlock(r.io); + if (r.head == r.queue.items.len) { + r.queue.clearRetainingCapacity(); + r.head = 0; + } + try r.queue.append(r.gpa, j); + r.cond.signal(r.io); + } + + /// Waits for a job (the plan settles it next). + pub fn wait(r: *Runner, j: *Job) void { + while (!j.done()) { + r.mutex.lockUncancelable(r.io); + defer r.mutex.unlock(r.io); + if (!j.done()) r.cond.waitTimeout(r.io, &r.mutex, .{ .duration = .{ .raw = .fromMilliseconds(5), .clock = .awake } }) catch {}; + } + } + + fn loop(r: *Runner) void { + while (true) { + const j = blk: { + r.mutex.lockUncancelable(r.io); + defer r.mutex.unlock(r.io); + while (r.head == r.queue.items.len and !r.stop) r.cond.waitUncancelable(r.io, &r.mutex); + if (r.head == r.queue.items.len) return; + r.head += 1; + break :blk r.queue.items[r.head - 1]; + }; + j.run(r.disk, r.io); + r.mutex.lockUncancelable(r.io); + r.cond.broadcast(r.io); + r.mutex.unlock(r.io); + } + } +}; diff --git a/zig/src/sessions/lanes.zig b/zig/src/sessions/lanes.zig new file mode 100644 index 0000000000..2b005aca9a --- /dev/null +++ b/zig/src/sessions/lanes.zig @@ -0,0 +1,337 @@ +//! The NVMe tier's lane threads, which hash and write (or read and check) 4 KiB chunks, and their staging buffers. + +const std = @import("std"); +const linux = std.os.linux; +const Io = std.Io; +const Sha256 = std.crypto.hash.sha2.Sha256; + +pub const block = 4096; + +pub fn up(n: u64) u64 { + return (n + block - 1) / block * block; +} + +/// Bounded, aligned staging buffers (allocated once; `release` gives the memory back while idle). +pub const Staging = struct { + io: Io, + size: usize, + count: u32, + bufs: [][]align(block) u8, + free_list: []u32, + free_n: u32, + mutex: Io.Mutex = .init, + cond: Io.Condition = .init, + gpa: std.mem.Allocator, + + pub fn init(gpa: std.mem.Allocator, io: Io, count: u32, size: usize) !Staging { + if (count == 0 or size % block != 0) return error.BadStaging; + const bufs = try gpa.alloc([]align(block) u8, count); + errdefer gpa.free(bufs); + const fl = try gpa.alloc(u32, count); + errdefer gpa.free(fl); + for (bufs, 0..) |*b, i| { + b.* = &.{}; + fl[i] = @intCast(i); + } + return .{ .io = io, .size = size, .count = count, .bufs = bufs, .free_list = fl, .free_n = count, .gpa = gpa }; + } + + pub fn deinit(s: *Staging) void { + s.release(); + s.gpa.free(s.bufs); + s.gpa.free(s.free_list); + s.* = undefined; + } + + /// A free buffer's index, waiting for one; its memory is mapped on first use. + pub fn get(s: *Staging) !u32 { + s.mutex.lockUncancelable(s.io); + defer s.mutex.unlock(s.io); + while (s.free_n == 0) s.cond.waitUncancelable(s.io, &s.mutex); + s.free_n -= 1; + const i = s.free_list[s.free_n]; + if (s.bufs[i].len == 0) s.bufs[i] = try s.gpa.alignedAlloc(u8, .fromByteUnits(block), s.size); + return i; + } + + pub fn put(s: *Staging, i: u32) void { + s.mutex.lockUncancelable(s.io); + defer s.mutex.unlock(s.io); + s.free_list[s.free_n] = i; + s.free_n += 1; + s.cond.signal(s.io); + } + + pub fn buf(s: *Staging, i: u32) []align(block) u8 { + return s.bufs[i]; + } + + /// Unmaps every buffer (all must be free): the memory goes back between operations. + pub fn release(s: *Staging) void { + std.debug.assert(s.free_n == s.count); + for (s.bufs) |*b| if (b.len != 0) { + s.gpa.free(b.*); + b.* = &.{}; + }; + } + + pub fn bytes(s: *const Staging) u64 { + return @as(u64, s.count) * s.size; + } +}; + +pub const Kind = enum { write, read }; + +/// One chunk's I/O: buf[0 .. span] at file offset off (span: the chunk padded to 4 KiB; data: the chunk's own bytes). +pub const Job = struct { + kind: Kind, + fd: linux.fd_t, + staging: u32, + data: u32, + span: u32, + off: u64, + /// write: the chunk's digest goes here; read: the digest it must have + digest: *[32]u8, + op: *Op, + /// the pump's tag for a finished read + tag: u32 = 0, +}; + +/// A park's or restore's chunks in flight: how many are pending, the first error, and finished reads for the pump. +pub const Op = struct { + io: Io, + mutex: Io.Mutex = .init, + cond: Io.Condition = .init, + pending: u32 = 0, + err: ?anyerror = null, + /// finished reads (their jobs), drained by the pump + done: std.ArrayList(Job) = .empty, + gpa: std.mem.Allocator, + /// write jobs hand their staging back themselves + staging: *Staging, + + pub fn init(gpa: std.mem.Allocator, io: Io, staging: *Staging) !Op { + var o: Op = .{ .io = io, .gpa = gpa, .staging = staging }; + try o.done.ensureTotalCapacity(gpa, staging.count); + return o; + } + + pub fn deinit(o: *Op) void { + o.done.deinit(o.gpa); + } + + fn finish(o: *Op, j: Job, err: ?anyerror) void { + o.mutex.lockUncancelable(o.io); + defer o.mutex.unlock(o.io); + o.pending -= 1; + if (err) |e| { + if (o.err == null) o.err = e; + } + if (j.kind == .read) o.done.appendAssumeCapacity(j) else o.staging.put(j.staging); + o.cond.broadcast(o.io); + } + + /// The next finished read, waiting for one; null when none is pending. + pub fn next(o: *Op) ?Job { + o.mutex.lockUncancelable(o.io); + defer o.mutex.unlock(o.io); + while (o.done.items.len == 0 and o.pending > 0) o.cond.waitUncancelable(o.io, &o.mutex); + return o.done.pop(); + } + + /// Waits for every pending job; returns the first error. + pub fn wait(o: *Op) !void { + o.mutex.lockUncancelable(o.io); + defer o.mutex.unlock(o.io); + while (o.pending > 0) o.cond.waitUncancelable(o.io, &o.mutex); + if (o.err) |e| return e; + } + + pub fn failed(o: *Op) ?anyerror { + o.mutex.lockUncancelable(o.io); + defer o.mutex.unlock(o.io); + return o.err; + } +}; + +/// The lane threads and their queue. +pub const Lanes = struct { + io: Io, + gpa: std.mem.Allocator, + staging: *Staging, + threads: []std.Thread, + /// queued jobs: a ring as long as the staging set (every queued job holds a buffer) + ring: []Job, + head: usize = 0, + len: usize = 0, + mutex: Io.Mutex = .init, + cond: Io.Condition = .init, + stop: bool = false, + /// seconds the lanes spent hashing and in I/O (summed over lanes) + hash_ns: std.atomic.Value(u64) = .init(0), + io_ns: std.atomic.Value(u64) = .init(0), + + pub fn start(gpa: std.mem.Allocator, io: Io, staging: *Staging, n: u32) !*Lanes { + const l = try gpa.create(Lanes); + errdefer gpa.destroy(l); + const ring = try gpa.alloc(Job, staging.count); + errdefer gpa.free(ring); + l.* = .{ .io = io, .gpa = gpa, .staging = staging, .threads = try gpa.alloc(std.Thread, n), .ring = ring }; + errdefer gpa.free(l.threads); + var made: usize = 0; + errdefer { + l.halt(); + for (l.threads[0..made]) |t| t.join(); + } + for (l.threads) |*t| { + t.* = try std.Thread.spawn(.{ .stack_size = 1 << 20 }, run, .{l}); + made += 1; + } + return l; + } + + fn halt(l: *Lanes) void { + l.mutex.lockUncancelable(l.io); + l.stop = true; + l.cond.broadcast(l.io); + l.mutex.unlock(l.io); + } + + pub fn shutdown(l: *Lanes) void { + l.halt(); + for (l.threads) |t| t.join(); + l.gpa.free(l.ring); + l.gpa.free(l.threads); + l.gpa.destroy(l); + } + + /// Queues a job (each holds a staging buffer, so the ring never overflows). + pub fn submit(l: *Lanes, j: Job) void { + { + j.op.mutex.lockUncancelable(l.io); + defer j.op.mutex.unlock(l.io); + j.op.pending += 1; + } + l.mutex.lockUncancelable(l.io); + defer l.mutex.unlock(l.io); + std.debug.assert(l.len < l.ring.len); + l.ring[(l.head + l.len) % l.ring.len] = j; + l.len += 1; + l.cond.signal(l.io); + } + + fn run(l: *Lanes) void { + while (true) { + const j = blk: { + l.mutex.lockUncancelable(l.io); + defer l.mutex.unlock(l.io); + while (l.len == 0 and !l.stop) l.cond.waitUncancelable(l.io, &l.mutex); + if (l.len == 0) return; + const j = l.ring[l.head]; + l.head = (l.head + 1) % l.ring.len; + l.len -= 1; + break :blk j; + }; + j.op.finish(j, l.work(j)); + } + } + + fn work(l: *Lanes, j: Job) ?anyerror { + const b = l.staging.buf(j.staging); + var t0 = std.Io.Clock.awake.now(l.io).toNanoseconds(); + switch (j.kind) { + .write => { + Sha256.hash(b[0..j.data], j.digest, .{}); + const t1 = std.Io.Clock.awake.now(l.io).toNanoseconds(); + _ = l.hash_ns.fetchAdd(@intCast(t1 - t0), .monotonic); + t0 = t1; + @memset(b[j.data..j.span], 0); + pwriteAll(j.fd, b[0..j.span], j.off) catch |e| return e; + _ = l.io_ns.fetchAdd(@intCast(std.Io.Clock.awake.now(l.io).toNanoseconds() - t0), .monotonic); + }, + .read => { + preadAll(j.fd, b[0..j.span], j.off) catch |e| return e; + const t1 = std.Io.Clock.awake.now(l.io).toNanoseconds(); + _ = l.io_ns.fetchAdd(@intCast(t1 - t0), .monotonic); + var d: [32]u8 = undefined; + Sha256.hash(b[0..j.data], &d, .{}); + _ = l.hash_ns.fetchAdd(@intCast(std.Io.Clock.awake.now(l.io).toNanoseconds() - t1), .monotonic); + if (!std.mem.eql(u8, &d, j.digest)) return error.ChecksumMismatch; + }, + } + return null; + } +}; + +pub fn pwriteAll(fd: linux.fd_t, data: []const u8, off: u64) !void { + var done: usize = 0; + while (done < data.len) { + const rc = linux.pwrite(fd, data[done..].ptr, data.len - done, @intCast(off + done)); + switch (linux.errno(rc)) { + .SUCCESS => {}, + .INTR => continue, + .NOSPC => return error.NoSpaceLeft, + else => return error.WriteFailed, + } + if (rc == 0) return error.WriteFailed; + done += rc; + } +} + +pub fn preadAll(fd: linux.fd_t, out: []u8, off: u64) !void { + var done: usize = 0; + while (done < out.len) { + const rc = linux.pread(fd, out[done..].ptr, out.len - done, @intCast(off + done)); + switch (linux.errno(rc)) { + .SUCCESS => {}, + .INTR => continue, + else => return error.ReadFailed, + } + if (rc == 0) return error.ShortRead; + done += rc; + } +} + +test "lanes write, read back and check chunks through bounded staging" { + if (@import("builtin").os.tag != .linux) return error.SkipZigTest; + const gpa = std.testing.allocator; + const io = std.testing.io; + var st = try Staging.init(gpa, io, 3, 2 * block); + defer st.deinit(); + const l = try Lanes.start(gpa, io, &st, 2); + defer l.shutdown(); + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + const f = try tmp.dir.createFile(io, "lanes.bin", .{ .read = true }); + defer f.close(io); + const fd = f.handle; + var digests: [7][32]u8 = undefined; + var op = try Op.init(gpa, io, &st); + defer op.deinit(); + for (0..7) |i| { + const s = try st.get(); + const n: u32 = if (i == 6) 1000 else 2 * block - 7; + for (st.buf(s)[0..n], 0..) |*b, k| b.* = @truncate(k * 31 + i); + l.submit(.{ .kind = .write, .fd = fd, .staging = s, .data = n, .span = @intCast(up(n)), .off = i * 2 * block, .digest = &digests[i], .op = &op }); + } + try op.wait(); + var rop = try Op.init(gpa, io, &st); + defer rop.deinit(); + var seen: u32 = 0; + var sent: u32 = 0; + while (seen < 7) { + while (sent < 7 and sent - seen < 3) : (sent += 1) { + const n: u32 = if (sent == 6) 1000 else 2 * block - 7; + l.submit(.{ .kind = .read, .fd = fd, .staging = try st.get(), .data = n, .span = @intCast(up(n)), .off = sent * 2 * block, .digest = &digests[sent], .op = &rop, .tag = sent }); + } + const j = rop.next().?; + for (st.buf(j.staging)[0..j.data], 0..) |b, k| try std.testing.expectEqual(@as(u8, @truncate(k * 31 + j.tag)), b); + st.put(j.staging); + seen += 1; + } + try rop.wait(); + digests[2][0] ^= 1; // a damaged chunk is refused + l.submit(.{ .kind = .read, .fd = fd, .staging = try st.get(), .data = 2 * block - 7, .span = 2 * block, .off = 2 * 2 * block, .digest = &digests[2], .op = &rop }); + st.put(rop.next().?.staging); + try std.testing.expectError(error.ChecksumMismatch, rop.wait()); +} diff --git a/zig/src/sessions/pagestore.zig b/zig/src/sessions/pagestore.zig new file mode 100644 index 0000000000..0e4792699b --- /dev/null +++ b/zig/src/sessions/pagestore.zig @@ -0,0 +1,129 @@ +//! The bytes behind the pool's pages: a family's tensor of pages, read and written a page run at a time. + +const std = @import("std"); +const pool_mod = @import("pool.zig"); +const Pool = pool_mod.Pool; +const Family = pool_mod.Family; + +pub const PageStore = struct { + ptr: *anyopaque, + vtable: *const VTable, + + pub const VTable = struct { + /// family fam's pages, back to back into dst (pages.len x a page's bytes) + read: *const fn (ptr: *anyopaque, fam: u32, pages: []const u32, dst: []u8) anyerror!void, + /// src (pages.len x a page's bytes) into family fam's pages + write: *const fn (ptr: *anyopaque, fam: u32, pages: []const u32, src: []const u8) anyerror!void, + /// one page's rows of family fam into another page (copy on write of a RAM resume's partial page) + copyPage: *const fn (ptr: *anyopaque, fam: u32, src: u32, dst: u32) anyerror!void, + /// round thread: a point after every row the compute stream has written so far + fence: *const fn (ptr: *anyopaque) anyerror!u64, + /// before reads: wait until the fence's rows are visible + waitFence: *const fn (ptr: *anyopaque, token: u64) anyerror!void, + /// after writes: make them visible to the compute stream + publish: *const fn (ptr: *anyopaque) anyerror!void, + }; + + pub fn read(s: PageStore, fam: u32, pages: []const u32, dst: []u8) !void { + return s.vtable.read(s.ptr, fam, pages, dst); + } + pub fn write(s: PageStore, fam: u32, pages: []const u32, src: []const u8) !void { + return s.vtable.write(s.ptr, fam, pages, src); + } + pub fn copyPage(s: PageStore, fam: u32, src: u32, dst: u32) !void { + return s.vtable.copyPage(s.ptr, fam, src, dst); + } + pub fn fence(s: PageStore) !u64 { + return s.vtable.fence(s.ptr); + } + pub fn waitFence(s: PageStore, token: u64) !void { + return s.vtable.waitFence(s.ptr, token); + } + pub fn publish(s: PageStore) !void { + return s.vtable.publish(s.ptr); + } +}; + +/// Every family's pages in host memory: [familyTensorPages x page bytes] a family, zeroed (the null page stays zero). +pub const HostPool = struct { + gpa: std.mem.Allocator, + page: u32, + families: []const Family, + tensors: [][]u8, + + pub fn init(gpa: std.mem.Allocator, p: *const Pool) !HostPool { + const t = try gpa.alloc([]u8, p.layout.families.len); + errdefer gpa.free(t); + var made: usize = 0; + errdefer for (t[0..made]) |x| gpa.free(x); + for (p.layout.families, 0..) |f, i| { + t[i] = try gpa.alloc(u8, @as(usize, p.familyTensorPages(f)) * f.pageBytes(p.page)); + @memset(t[i], 0); + made += 1; + } + return .{ .gpa = gpa, .page = p.page, .families = p.layout.families, .tensors = t }; + } + + pub fn deinit(h: *HostPool) void { + for (h.tensors) |x| h.gpa.free(x); + h.gpa.free(h.tensors); + h.* = undefined; + } + + pub fn store(h: *HostPool) PageStore { + return .{ .ptr = h, .vtable = &vtable }; + } + + /// One page of family fam (a view). + pub fn pageOf(h: *HostPool, fam: u32, pg: u32) []u8 { + const n: usize = @intCast(h.families[fam].pageBytes(h.page)); + return h.tensors[fam][pg * n ..][0..n]; + } + + const vtable: PageStore.VTable = .{ .read = read, .write = write, .copyPage = copyPage, .fence = fence, .waitFence = waitFence, .publish = publish }; + + fn read(ptr: *anyopaque, fam: u32, pages: []const u32, dst: []u8) anyerror!void { + const h: *HostPool = @ptrCast(@alignCast(ptr)); + const n: usize = @intCast(h.families[fam].pageBytes(h.page)); + if (dst.len != pages.len * n) return error.BadLength; + for (pages, 0..) |pg, i| @memcpy(dst[i * n ..][0..n], h.pageOf(fam, pg)); + } + + fn write(ptr: *anyopaque, fam: u32, pages: []const u32, src: []const u8) anyerror!void { + const h: *HostPool = @ptrCast(@alignCast(ptr)); + const n: usize = @intCast(h.families[fam].pageBytes(h.page)); + if (src.len != pages.len * n) return error.BadLength; + for (pages, 0..) |pg, i| @memcpy(h.pageOf(fam, pg), src[i * n ..][0..n]); + } + + fn copyPage(ptr: *anyopaque, fam: u32, src: u32, dst: u32) anyerror!void { + const h: *HostPool = @ptrCast(@alignCast(ptr)); + @memcpy(h.pageOf(fam, dst), h.pageOf(fam, src)); + } + + fn fence(_: *anyopaque) anyerror!u64 { + return 0; + } + fn waitFence(_: *anyopaque, _: u64) anyerror!void {} + fn publish(_: *anyopaque) anyerror!void {} +}; + +test "host pool pages round-trip through the interface" { + const gpa = std.testing.allocator; + const fams = [_]Family{ .{ .name = "comp", .ratio = 1, .row_bytes = 8 }, .{ .name = "ik", .ratio = 2, .row_bytes = 4 } }; + var p = try Pool.init(gpa, .{ .families = &fams, .page = 16 }, 64, 1, 0); + defer p.deinit(); + var h = try HostPool.init(gpa, &p); + defer h.deinit(); + const s = h.store(); + var src: [2 * 128]u8 = undefined; + for (&src, 0..) |*b, i| b.* = @truncate(i * 7 + 3); + try s.write(0, &.{ 3, 1 }, &src); + var dst: [2 * 128]u8 = undefined; + try s.read(0, &.{ 3, 1 }, &dst); + try std.testing.expectEqualSlices(u8, &src, &dst); + try s.copyPage(0, 3, 0); + try std.testing.expectEqualSlices(u8, src[0..128], h.pageOf(0, 0)); + try std.testing.expect(std.mem.allEqual(u8, h.pageOf(0, p.null_page), 0)); + try std.testing.expectError(error.BadLength, s.read(1, &.{0}, &dst)); +} diff --git a/zig/src/sessions/pool.zig b/zig/src/sessions/pool.zig new file mode 100644 index 0000000000..85ee2c48c8 --- /dev/null +++ b/zig/src/sessions/pool.zig @@ -0,0 +1,484 @@ +//! The paged KV pool's host book: lowest-free pages per residue mod W, holder counts, slot page tables, quotas. + +const std = @import("std"); +const FreeSet = @import("freeset.zig").FreeSet; + +pub const Error = error{ PoolExhausted, PastCapacity, SlotNotEmpty, DoubleFree, BadPool, ResidueMismatch } || std.mem.Allocator.Error; + +/// A cache family: rows of `row_bytes` every `ratio` tokens; `split` families shard by logical page over the ranks. +pub const Family = struct { + name: []const u8, + ratio: u32, + row_bytes: u32, + split: bool = false, + + pub fn pageRows(f: Family, page: u32) u32 { + return page / f.ratio; + } + + /// Bytes of one page of this family (a whole page's rows). + pub fn pageBytes(f: Family, page: u32) u64 { + return @as(u64, f.pageRows(page)) * f.row_bytes; + } +}; + +/// The pool's families and page size: what a token costs and how pages are laid out. +pub const Layout = struct { + families: []const Family, + /// tokens a page (a power of two; 256 for every family we serve) + page: u32 = 256, + + /// A rank's bytes a token: the split families count 1 / W. + pub fn bytesPerToken(l: Layout, world: u32) f64 { + var b: f64 = 0; + for (l.families) |f| b += @as(f64, @floatFromInt(f.row_bytes)) / @as(f64, @floatFromInt(f.ratio)) / @as(f64, @floatFromInt(if (f.split and world > 1) world else 1)); + return b; + } + + /// A rank's average bytes a page (split families 1 / W): what the residency budget counts. + pub fn pageBytes(l: Layout, world: u32) u64 { + var b: u64 = 0; + for (l.families) |f| b += f.pageBytes(l.page) / (if (f.split and world > 1) world else 1); + return b; + } +}; + +pub fn pagesFor(tokens: u64, page: u32) u32 { + return @intCast((tokens + page - 1) / page); +} + +/// Logical pages [k0, k1) counted by residue mod `w` (O(w)). +pub fn span(k0: u32, k1: u32, w: u32, out: []u32) void { + for (out[0..w], 0..) |*o, r| { + const rr: u32 = @intCast(r); + const first = k0 + (rr + w - k0 % w) % w; + o.* = if (first >= k1) 0 else (k1 - 1 - first) / w + 1; + } +} + +pub const max_world = 8; + +/// A file whose logical pages [0, pages) match a slot's or entry's rows: a later park writes only past it. +pub const Origin = struct { + key: [16]u8, + pages: u32, +}; + +/// One slot's page table: logical page i -> physical page; the device tables follow through the dirty range. +pub const Slot = struct { + pool: *Pool, + id: u32, + capacity: u64, + max_pages: u32, + /// physical page of each mapped logical page; len is the mapped count + pages: []u32, + len: u32 = 0, + /// pages the admission reserved for this slot's request (null: no admission control) + quota: ?u32 = null, + /// pages taken past the quota, from pages nobody had reserved + over: u32 = 0, + /// table entries changed since the device side last synced them: [lo, hi) + dirty_lo: u32 = std.math.maxInt(u32), + dirty_hi: u32 = 0, + /// the file the leading pages came from (truncation shortens it, release clears it) + origin: ?Origin = null, + + pub fn mapped(s: *const Slot) []const u32 { + return s.pages[0..s.len]; + } + + pub fn tokens(s: *const Slot) u64 { + return @as(u64, s.len) * s.pool.page; + } + + fn mark(s: *Slot, lo: u32, hi: u32) void { + s.dirty_lo = @min(s.dirty_lo, lo); + s.dirty_hi = @max(s.dirty_hi, hi); + } + + /// The table range to upload since the last call (empty when nothing changed): entries past len are the null page. + pub fn takeDirty(s: *Slot) ?struct { lo: u32, hi: u32 } { + if (s.dirty_lo >= s.dirty_hi) return null; + defer { + s.dirty_lo = std.math.maxInt(u32); + s.dirty_hi = 0; + } + return .{ .lo = s.dirty_lo, .hi = s.dirty_hi }; + } + + /// The device table's value at logical page i (the page, or the null page past len). + pub fn tableAt(s: *const Slot, i: u32) u32 { + return if (i < s.len) s.pages[i] else s.pool.null_page; + } + + /// The split families' device table at logical page i: the local page this rank stores, else the discard page. + pub fn localTableAt(s: *const Slot, i: u32) u32 { + return s.pool.localPage(s.tableAt(i)); + } + + fn reservedSpan(s: *const Slot, out: []u32) void { + const q = s.quota orelse 0; + span(s.len, @max(s.len, q), s.pool.world, out); + } + + /// Maps every page holding positions < end (a no-op when they are: the common case). + pub fn ensure(s: *Slot, end: u64) Error!void { + const p = s.pool; + const need = pagesFor(end, p.page); + if (need <= s.len) return; + if (need > s.max_pages) return error.PastCapacity; + const w = p.world; + var before: [max_world]u32 = undefined; + s.reservedSpan(&before); + if (s.quota) |q| if (need > q) { + // past the reservation: only pages nobody else reserved, residue by residue + var extra: [max_world]u32 = undefined; + span(@max(q, s.len), need, w, &extra); + for (0..w) |r| if (extra[r] > p.free[r].count() -| (p.reserved[r] - before[r])) return error.PoolExhausted; + s.over += need - @max(q, s.len); + }; + var want: [max_world]u32 = undefined; + span(s.len, need, w, &want); + for (0..w) |r| if (want[r] > p.free[r].count()) return error.PoolExhausted; + const lo = s.len; + for (lo..need) |k| { + const pg = p.takeAt(@intCast(k)) catch unreachable; // counted above + p.hold(pg, 0, 1); // the taker's hold becomes the slot's + s.pages[k] = pg; + } + s.len = need; + p.unreserve(&before); + var after: [max_world]u32 = undefined; + s.reservedSpan(&after); + p.addReserved(&after); + s.mark(lo, need); + } + + /// Maps shared pages as the first pages of an empty slot (a RAM resume: read only, the slot writes past them). + pub fn adopt(s: *Slot, shared: []const u32) Error!void { + if (s.len != 0) return error.SlotNotEmpty; + if (shared.len > s.max_pages) return error.PastCapacity; + const p = s.pool; + var before: [max_world]u32 = undefined; + s.reservedSpan(&before); + for (shared, 0..) |pg, k| if (pg >= p.npages or pg % p.world != k % p.world) return error.ResidueMismatch; + for (shared, 0..) |pg, k| { + p.hold(pg, 1, 1); + s.pages[k] = pg; + } + s.len = @intCast(shared.len); + p.unreserve(&before); + var after: [max_world]u32 = undefined; + s.reservedSpan(&after); + p.addReserved(&after); + s.mark(0, s.len); + } + + /// Appends one page taken for logical page len (the copy-on-write page of a RAM resume). + pub fn push(s: *Slot, pg: u32) Error!void { + if (s.len >= s.max_pages) return error.PastCapacity; + const p = s.pool; + if (p.world > 1 and pg % p.world != s.len % p.world) return error.ResidueMismatch; + var before: [max_world]u32 = undefined; + s.reservedSpan(&before); + p.hold(pg, 0, 1); // the taker's hold becomes the slot's + s.pages[s.len] = pg; + s.len += 1; + p.unreserve(&before); + var after: [max_world]u32 = undefined; + s.reservedSpan(&after); + p.addReserved(&after); + s.mark(s.len - 1, s.len); + } + + /// Lets go of the pages past position keep (rows nothing reads again); returns how many. + pub fn truncate(s: *Slot, keep: u64) Error!u32 { + const p = s.pool; + // positions >= keep get written again: a page holding one no longer equals the origin file's + if (s.origin) |*o| o.pages = @min(o.pages, @as(u32, @intCast(keep / p.page))); + const k = pagesFor(keep, p.page); + if (k >= s.len) return 0; + var before: [max_world]u32 = undefined; + s.reservedSpan(&before); + const n = s.len - k; + for (s.pages[k..s.len]) |pg| try p.release(pg, 1); + const old = s.len; + s.len = k; + p.unreserve(&before); + var after: [max_world]u32 = undefined; + s.reservedSpan(&after); + p.addReserved(&after); + s.mark(k, old); + return n; + } + + /// Sets the admission's reservation (null: none). + pub fn reserve(s: *Slot, quota: ?u32) void { + var before: [max_world]u32 = undefined; + s.reservedSpan(&before); + s.quota = quota; + s.pool.unreserve(&before); + var after: [max_world]u32 = undefined; + s.reservedSpan(&after); + s.pool.addReserved(&after); + } + + /// Frees the slot's pages and its reservation. + pub fn releaseAll(s: *Slot) Error!u32 { + s.reserve(null); + s.over = 0; + s.origin = null; + return s.truncate(0); + } +}; + +/// The pool's book: free sets by residue, holders a page, the slots and their reservations. +pub const Pool = struct { + gpa: std.mem.Allocator, + layout: Layout, + npages: u32, + page: u32, + /// log2 of page tokens + shift: u5, + /// ranks the split families are sharded over (1: every rank stores every row) + world: u32, + rank: u32, + /// the page unmapped table entries point at (all zeros: a stray read reads zeros) + null_page: u32, + /// a split family's local page count (npages / W), its local null page and its discard page (other ranks' rows) + local_pages: u32, + local_null: u32, + discard: u32, + free: [max_world]FreeSet = @splat(.{}), + /// pages each residue's slots reserved and have not mapped yet + reserved: [max_world]u32 = @splat(0), + /// holders a page (slots + entries + in-flight jobs) and how many are slots + refs: []u16, + slot_refs: []u16, + /// pages held by something and by no slot (session entries alone): what parking can free + entry_only: u32 = 0, + slots: std.ArrayList(*Slot) = .empty, + + pub fn init(gpa: std.mem.Allocator, layout: Layout, tokens: u64, world: u32, rank: u32) Error!Pool { + if (layout.page == 0 or layout.page & (layout.page - 1) != 0) return error.BadPool; + if (tokens % layout.page != 0 or tokens == 0) return error.BadPool; + if (world == 0 or world > max_world or rank >= world) return error.BadPool; + const npages: u32 = @intCast(tokens / layout.page); + if (npages % world != 0) return error.BadPool; + for (layout.families) |f| if (f.ratio == 0 or layout.page % f.ratio != 0) return error.BadPool; + var p: Pool = .{ + .gpa = gpa, + .layout = layout, + .npages = npages, + .page = layout.page, + .shift = @intCast(std.math.log2_int(u32, layout.page)), + .world = world, + .rank = rank, + .null_page = npages, + .local_pages = npages / world, + .local_null = npages / world, + .discard = npages / world + 1, + .refs = &.{}, + .slot_refs = &.{}, + }; + errdefer p.deinit(); + for (0..world) |r| p.free[r] = try FreeSet.init(gpa, npages / world); + p.refs = try gpa.alloc(u16, npages); + @memset(p.refs, 0); + p.slot_refs = try gpa.alloc(u16, npages); + @memset(p.slot_refs, 0); + return p; + } + + pub fn deinit(p: *Pool) void { + for (p.slots.items) |s| { + p.gpa.free(s.pages); + p.gpa.destroy(s); + } + p.slots.deinit(p.gpa); + for (0..p.world) |r| p.free[r].deinit(p.gpa); + p.gpa.free(p.refs); + p.gpa.free(p.slot_refs); + p.* = undefined; + } + + pub fn newSlot(p: *Pool, capacity: u64) Error!*Slot { + const s = try p.gpa.create(Slot); + errdefer p.gpa.destroy(s); + const mp = pagesFor(capacity, p.page); + s.* = .{ .pool = p, .id = @intCast(p.slots.items.len), .capacity = capacity, .max_pages = mp, .pages = try p.gpa.alloc(u32, mp) }; + errdefer p.gpa.free(s.pages); + try p.slots.append(p.gpa, s); + return s; + } + + /// A free physical page for logical page k (its residue when split), held once by the caller. + pub fn takeAt(p: *Pool, k: u32) Error!u32 { + const r = k % p.world; + const local = p.free[r].takeLowest() orelse return error.PoolExhausted; + const pg = local * p.world + r; + p.hold(pg, 1, 0); // the caller's hold (an entry-like holder until a slot maps it) + return pg; + } + + /// Holders change on page pg: dref total, dslot of them slots; keeps entry_only and gives the page back at zero. + fn hold(p: *Pool, pg: u32, dref: i32, dslot: i32) void { + const r0 = p.refs[pg]; + const s0 = p.slot_refs[pg]; + const r1: u16 = @intCast(@as(i32, r0) + dref); + const s1: u16 = @intCast(@as(i32, s0) + dslot); + p.refs[pg] = r1; + p.slot_refs[pg] = s1; + const was = r0 > 0 and s0 == 0; + const now = r1 > 0 and s1 == 0; + if (was and !now) p.entry_only -= 1; + if (!was and now) p.entry_only += 1; + } + + /// One holder lets go (slot: from a slot's table); the page returns to its free set when nobody holds it. + fn release(p: *Pool, pg: u32, slot: u1) Error!void { + if (p.refs[pg] == 0) return error.DoubleFree; + p.hold(pg, -1, -@as(i32, slot)); + if (p.refs[pg] == 0) p.free[pg % p.world].give(pg / p.world) catch return error.DoubleFree; + } + + /// One more holder (a session entry or an in-flight job) of each page. + pub fn share(p: *Pool, pages: []const u32) void { + for (pages) |pg| p.hold(pg, 1, 0); + } + + /// Those holders let go. + pub fn drop(p: *Pool, pages: []const u32) Error!void { + for (pages) |pg| try p.release(pg, 0); + } + + pub fn holders(p: *const Pool, pg: u32) u16 { + return p.refs[pg]; + } + + fn unreserve(p: *Pool, by: []const u32) void { + for (0..p.world) |r| p.reserved[r] -= by[r]; + } + + fn addReserved(p: *Pool, by: []const u32) void { + for (0..p.world) |r| p.reserved[r] += by[r]; + } + + /// Free pages no admission reserved (split: W x the scarcer residue, so any logical run fits). + pub fn available(p: *const Pool) u32 { + var low: u32 = std.math.maxInt(u32); + for (0..p.world) |r| low = @min(low, p.free[r].count() -| p.reserved[r]); + return low * p.world; + } + + pub fn freePages(p: *const Pool) u32 { + var n: u32 = 0; + for (0..p.world) |r| n += p.free[r].count(); + return n; + } + + /// Pages an admission needs for a request (prompt + reply + slack, at most the slot's capacity). + pub fn needPages(p: *const Pool, prompt: u64, max_tokens: u64, capacity: u64, slack: u64) u32 { + const total = prompt + max_tokens + slack; + return pagesFor(if (capacity == 0) total else @min(total, capacity), p.page); + } + + /// This rank stores the split families' rows of page pg. + pub fn owns(p: *const Pool, pg: u32) bool { + return pg != p.null_page and pg % p.world == p.rank; + } + + /// A split family's local page of pg: its own when owned, the local null for the null page, else the discard page. + pub fn localPage(p: *const Pool, pg: u32) u32 { + if (pg == p.null_page) return p.local_null; + return if (pg % p.world == p.rank) pg / p.world else p.discard; + } + + /// The page a family's tensor indexes for physical page pg (split families: localPage). + pub fn familyPage(p: *const Pool, f: Family, pg: u32) u32 { + return if (f.split and p.world > 1) p.localPage(pg) else pg; + } + + /// Pages a family's device tensor holds: npages + null, or (split) local pages + local null + discard page. + pub fn familyTensorPages(p: *const Pool, f: Family) u32 { + return if (f.split and p.world > 1) p.local_pages + 2 else p.npages + 1; + } + + /// Bytes this rank stores of family f for logical pages [first, first + n) of a slot or entry. + pub fn familyBytes(p: *const Pool, f: Family, first: u32, n: u32) u64 { + if (!(f.split and p.world > 1)) return @as(u64, n) * f.pageBytes(p.page); + var by: [max_world]u32 = undefined; + span(first, first + n, p.world, &by); + return @as(u64, by[p.rank]) * f.pageBytes(p.page); + } + + /// Bytes of every family's device tensor on this rank. + pub fn tensorBytes(p: *const Pool) u64 { + var b: u64 = 0; + for (p.layout.families) |f| b += @as(u64, p.familyTensorPages(f)) * f.pageBytes(p.page); + return b; + } + + /// Pages held by session entries alone and their bytes on this rank (the residency budget's measure): O(1). + pub fn entryOnlyBytes(p: *const Pool) u64 { + return @as(u64, p.entry_only) * p.layout.pageBytes(p.world); + } +}; + +test "residues, holders, quotas and the device tables" { + const gpa = std.testing.allocator; + const fams = [_]Family{ .{ .name = "comp", .ratio = 2, .row_bytes = 584, .split = true }, .{ .name = "index_k", .ratio = 2, .row_bytes = 132 } }; + var p = try Pool.init(gpa, .{ .families = &fams }, 16 * 256, 2, 1); + defer p.deinit(); + try std.testing.expectEqual(@as(u32, 8 + 2), p.familyTensorPages(fams[0])); + try std.testing.expectEqual(@as(u32, 16 + 1), p.familyTensorPages(fams[1])); + try std.testing.expectApproxEqAbs(@as(f64, 146 + 66), p.layout.bytesPerToken(2), 1e-9); + const a = try p.newSlot(8 * 256); + const b = try p.newSlot(8 * 256); + try a.ensure(3 * 256 + 1); // 4 pages: residues 0,1,0,1 + try std.testing.expectEqualSlices(u32, &.{ 0, 1, 2, 3 }, a.mapped()); + try b.ensure(256); + try std.testing.expectEqualSlices(u32, &.{4}, b.mapped()); + try std.testing.expectEqual(p.discard, a.localTableAt(0)); // page 0 is rank 0's + try std.testing.expectEqual(@as(u32, 0), a.localTableAt(1)); // page 1 -> rank 1's local page 0 + try std.testing.expectEqual(p.local_null, a.localTableAt(7)); + const d = a.takeDirty().?; + try std.testing.expectEqual(@as(u32, 0), d.lo); + try std.testing.expectEqual(@as(u32, 4), d.hi); + try std.testing.expect(a.takeDirty() == null); + // an entry shares a's pages; a lets go; the pages stay held by the entry alone + p.share(a.mapped()[0..3]); + try std.testing.expectEqual(@as(u32, 0), p.entry_only); + _ = try a.releaseAll(); + try std.testing.expectEqual(@as(u32, 3), p.entry_only); + try std.testing.expectEqual(@as(u32, 16 - 1 - 3), p.freePages()); + try p.drop(&.{ 0, 1, 2 }); + try std.testing.expectEqual(@as(u32, 0), p.entry_only); + try std.testing.expectError(error.DoubleFree, p.drop(&.{0})); + // quotas: b reserves 6 pages; available counts W x the scarcer residue + b.reserve(6); + var by: [2]u32 = undefined; + span(1, 6, 2, &by); + try std.testing.expectEqual(@as(u32, 2), by[0]); + try std.testing.expectEqual(@as(u32, 3), by[1]); + try std.testing.expectEqual(@as(u32, 2 * @min(7 - 2, 8 - 3)), p.available()); + try b.ensure(6 * 256); + try std.testing.expectEqual(@as(u32, 0), p.reserved[0] + p.reserved[1]); + for (b.mapped(), 0..) |pg, k| try std.testing.expectEqual(k % 2, pg % 2); + // RAM resume: adopt full pages of the right residues, refuse the wrong ones + const c = try p.newSlot(8 * 256); + try std.testing.expectError(error.ResidueMismatch, c.adopt(&.{b.pages[1]})); + p.share(b.mapped()[0..2]); + try c.adopt(b.mapped()[0..2]); + try c.push(try p.takeAt(2)); + try std.testing.expectEqual(@as(u16, 3), p.holders(b.pages[0])); + try std.testing.expectEqual(@as(u32, 0), c.pages[2] % 2); +} + +test "admission without a capacity still reserves prompt, reply and slack pages" { + var p = try Pool.init(std.testing.allocator, .{ .families = &.{}, .page = 64 }, 1024, 1, 0); + defer p.deinit(); + try std.testing.expectEqual(@as(u32, 3), p.needPages(65, 64, 0, 1)); + try std.testing.expectEqual(@as(u32, 1), p.needPages(65, 64, 64, 1)); + try std.testing.expectEqual(@as(u32, 0), p.needPages(0, 0, 0, 0)); +} diff --git a/zig/src/sessions/portable_test.zig b/zig/src/sessions/portable_test.zig new file mode 100644 index 0000000000..2e70055c68 --- /dev/null +++ b/zig/src/sessions/portable_test.zig @@ -0,0 +1,9 @@ +//! Portable session bookkeeping tests; the Linux NVMe tier is tested separately. +test { + _ = @import("freeset.zig"); + _ = @import("pool.zig"); + _ = @import("pagestore.zig"); + _ = @import("prefix.zig"); + _ = @import("prefix_fail_test.zig"); + _ = @import("format.zig"); +} diff --git a/zig/src/sessions/prefix.zig b/zig/src/sessions/prefix.zig new file mode 100644 index 0000000000..80c5838c60 --- /dev/null +++ b/zig/src/sessions/prefix.zig @@ -0,0 +1,512 @@ +//! Session entries by prefix: a trie over chained page digests, one node per distinct run of full pages a tag. + +const std = @import("std"); + +pub const Digest = [16]u8; +pub const none = std.math.maxInt(u32); +const Blake = std.crypto.hash.blake2.Blake2b128; + +/// Chained digests of the full pages of ids under a tag: out[j] determines tag and ids[0 .. page (j + 1)]. +pub fn chain(tag: u32, ids: []const i32, page: u32, out: []Digest) void { + var prev = root(tag); + for (out, 0..) |*o, j| { + o.* = step(prev, ids[j * page ..][0..page]); + prev = o.*; + } +} + +/// The digest of a tag's empty prefix. +pub fn root(tag: u32) Digest { + var h = Blake.init(.{}); + h.update("tf-sess-root"); + h.update(std.mem.asBytes(&tag)); + return h.finalResult(); +} + +/// The next page's digest. +pub fn step(prev: Digest, page_ids: []const i32) Digest { + var h = Blake.init(.{}); + h.update(&prev); + h.update(std.mem.sliceAsBytes(page_ids)); + return h.finalResult(); +} + +/// An entry's identity: its node's digest (tag and full pages), its length and tail. +pub fn entryKey(node: Digest, pos: u64, tail: []const i32) Digest { + var h = Blake.init(.{}); + h.update("tf-sess-entry"); + h.update(&node); + h.update(std.mem.asBytes(&pos)); + h.update(std.mem.sliceAsBytes(tail)); + return h.finalResult(); +} + +pub const Tier = enum { ram, any }; + +pub fn Index(comptime Extra: type) type { + return struct { + const Self = @This(); + + pub const Node = struct { + digest: Digest, + parent: u32, + depth: u32, + first_child: u32 = none, + next_sib: u32 = none, + prev_sib: u32 = none, + first_entry: u32 = none, + /// entries in the subtree (this node's included): all, and those in RAM + sub_all: u32 = 0, + sub_ram: u32 = 0, + /// RAM entries at this node itself + own_ram: u32 = 0, + /// the tokens of the page leading into this node, while sub_ram > 0 + tokens: ?[]i32 = null, + }; + + pub const Entry = struct { + key: Digest, + tag: u32, + pos: u64, + node: u32, + tail: []i32, + ram: bool, + next_at: u32 = none, + prev_at: u32 = none, + live: bool = true, + extra: Extra, + }; + + /// An entry inserted, or the one already there with the same key. + pub const Placed = struct { id: u32, new: bool }; + + gpa: std.mem.Allocator, + page: u32, + nodes: std.ArrayList(Node) = .empty, + free_nodes: std.ArrayList(u32) = .empty, + by_digest: std.AutoHashMapUnmanaged(Digest, u32) = .empty, + dead: u32 = 0, // nodes dropped from `by_digest` since it was last rehashed + entries: std.ArrayList(Entry) = .empty, + free_entries: std.ArrayList(u32) = .empty, + by_key: std.AutoHashMapUnmanaged(Digest, u32) = .empty, + /// page-sized token blocks, reused + blocks: std.ArrayList([]i32) = .empty, + + pub fn init(gpa: std.mem.Allocator, page: u32) Self { + return .{ .gpa = gpa, .page = page }; + } + + pub fn deinit(x: *Self) void { + for (x.nodes.items) |n| if (n.tokens) |t| x.gpa.free(t); + for (x.blocks.items) |b| x.gpa.free(b); + for (x.entries.items) |e| if (e.live) x.gpa.free(e.tail); + x.nodes.deinit(x.gpa); + x.free_nodes.deinit(x.gpa); + x.by_digest.deinit(x.gpa); + x.entries.deinit(x.gpa); + x.free_entries.deinit(x.gpa); + x.by_key.deinit(x.gpa); + x.blocks.deinit(x.gpa); + x.* = undefined; + } + + pub fn get(x: *Self, e: u32) *Entry { + return &x.entries.items[e]; + } + + pub fn node(x: *Self, n: u32) *Node { + return &x.nodes.items[n]; + } + + pub fn lookup(x: *const Self, key: Digest) ?u32 { + return x.by_key.get(key); + } + + /// Reserves first, so a failure changes nothing; free_nodes keeps room for every slot (prune never allocates). + fn newNode(x: *Self, digest: Digest, parent: u32, depth: u32) !u32 { + try x.by_digest.ensureUnusedCapacity(x.gpa, 1); + if (x.free_nodes.items.len == 0) { + try x.nodes.ensureUnusedCapacity(x.gpa, 1); + try x.free_nodes.ensureTotalCapacity(x.gpa, x.nodes.items.len + 1); + } + const id: u32 = x.free_nodes.pop() orelse blk: { + x.nodes.appendAssumeCapacity(undefined); + break :blk @intCast(x.nodes.items.len - 1); + }; + x.nodes.items[id] = .{ .digest = digest, .parent = parent, .depth = depth }; + x.by_digest.putAssumeCapacityNoClobber(digest, id); + if (parent != none) { + const p = &x.nodes.items[parent]; + x.nodes.items[id].next_sib = p.first_child; + if (p.first_child != none) x.nodes.items[p.first_child].prev_sib = id; + p.first_child = id; + } + return id; + } + + fn takeBlock(x: *Self) ![]i32 { + if (x.blocks.pop()) |b| return b; + return x.gpa.alloc(i32, x.page); + } + + /// Adds an entry for ids (chain: its full pages' digests) or returns the one with its key; an error adds none. + pub fn insert(x: *Self, tag: u32, ids: []const i32, digests: []const Digest, ram: bool, extra: Extra) !Placed { + const full: u32 = @intCast(ids.len / x.page); + std.debug.assert(digests.len >= full); + const r = root(tag); + var n = x.by_digest.get(r) orelse try x.newNode(r, none, 0); + errdefer x.prune(n); // the path's new nodes, which no entry holds yet + for (0..full) |j| { + const d = digests[j]; + n = x.by_digest.get(d) orelse try x.newNode(d, n, @intCast(j + 1)); + } + const r2 = try x.place(tag, n, ids.len, ids[full * x.page ..], extra); + if (r2.new and ram) x.setRam(r2.id, true, ids) catch |err| { + _ = x.drop(r2.id); // the errdefer above prunes its path + return err; + }; + return r2; + } + + /// Adds a disk entry continuing from node anchor (none: the tag's root); suffix: tokens [depth x page, pos). + pub fn insertFrom(x: *Self, tag: u32, anchor: u32, suffix: []const i32, extra: Extra) !Placed { + var n = if (anchor != none) anchor else blk: { + const r = root(tag); + break :blk x.by_digest.get(r) orelse try x.newNode(r, none, 0); + }; + errdefer x.prune(n); // the path's new nodes, which no entry holds yet + const d0 = x.nodes.items[n].depth; + const full: u32 = @intCast(suffix.len / x.page); + for (0..full) |j| { + const d = step(x.nodes.items[n].digest, suffix[j * x.page ..][0..x.page]); + n = x.by_digest.get(d) orelse try x.newNode(d, n, d0 + @as(u32, @intCast(j)) + 1); + } + return x.place(tag, n, @as(u64, d0) * x.page + suffix.len, suffix[full * x.page ..], extra); + } + + /// The node at depth on an entry's path (its first `depth` full pages). + pub fn nodeAt(x: *Self, id: u32, depth: u32) ?u32 { + var n = x.entries.items[id].node; + while (n != none and x.nodes.items[n].depth > depth) n = x.nodes.items[n].parent; + return if (n != none and x.nodes.items[n].depth == depth) n else null; + } + + fn place(x: *Self, tag: u32, n: u32, pos: u64, tail_ids: []const i32, extra: Extra) !Placed { + const key = entryKey(x.nodes.items[n].digest, pos, tail_ids); + if (x.by_key.get(key)) |e| return .{ .id = e, .new = false }; + try x.by_key.ensureUnusedCapacity(x.gpa, 1); + if (x.free_entries.items.len == 0) { + try x.entries.ensureUnusedCapacity(x.gpa, 1); + try x.free_entries.ensureTotalCapacity(x.gpa, x.entries.items.len + 1); + } + const tail = try x.gpa.dupe(i32, tail_ids); // the last step that can fail + const id: u32 = x.free_entries.pop() orelse blk: { + x.entries.appendAssumeCapacity(undefined); + break :blk @intCast(x.entries.items.len - 1); + }; + x.by_key.putAssumeCapacityNoClobber(key, id); + const nd = &x.nodes.items[n]; + x.entries.items[id] = .{ .key = key, .tag = tag, .pos = pos, .node = n, .tail = tail, .ram = false, .next_at = nd.first_entry, .extra = extra }; + if (nd.first_entry != none) x.entries.items[nd.first_entry].prev_at = id; + nd.first_entry = id; + x.addPath(n, 1, 0); + return .{ .id = id, .new = true }; + } + + fn addPath(x: *Self, from: u32, d_all: i32, d_ram: i32) void { + var n = from; + while (n != none) { + const nd = &x.nodes.items[n]; + nd.sub_all = @intCast(@as(i64, nd.sub_all) + d_all); + nd.sub_ram = @intCast(@as(i64, nd.sub_ram) + d_ram); + n = nd.parent; + } + } + + /// Moves an entry in or out of RAM; going in needs its ids (to keep its path's tokens). + pub fn setRam(x: *Self, id: u32, ram: bool, ids: ?[]const i32) !void { + const e = &x.entries.items[id]; + if (e.ram == ram) return; + if (ram) { + const all = ids orelse return error.IdsNeeded; + errdefer x.dropTokens(e.node); // the blocks this call took: tokens stay only where sub_ram > 0 + var n = e.node; + while (n != none and x.nodes.items[n].depth > 0) : (n = x.nodes.items[n].parent) { + const nd = &x.nodes.items[n]; + if (nd.tokens != null) continue; + const b = try x.takeBlock(); + @memcpy(b, all[(nd.depth - 1) * x.page ..][0..x.page]); + nd.tokens = b; + } + } + e.ram = ram; + x.nodes.items[e.node].own_ram = if (ram) x.nodes.items[e.node].own_ram + 1 else x.nodes.items[e.node].own_ram - 1; + x.addPath(e.node, 0, if (ram) 1 else -1); + if (!ram) x.dropTokens(e.node); + } + + fn dropTokens(x: *Self, from: u32) void { + var n = from; + while (n != none) : (n = x.nodes.items[n].parent) { + const nd = &x.nodes.items[n]; + if (nd.sub_ram > 0) return; + if (nd.tokens) |t| { + x.blocks.append(x.gpa, t) catch x.gpa.free(t); + nd.tokens = null; + } + } + } + + /// Removes an entry and the nodes no entry needs any more. + pub fn remove(x: *Self, id: u32) void { + x.prune(x.drop(id)); + } + + /// Unlinks an entry and frees its slot; returns its node, whose path may now hold no entry. + fn drop(x: *Self, id: u32) u32 { + x.setRam(id, false, null) catch unreachable; + const e = &x.entries.items[id]; + const n = e.node; + if (e.prev_at != none) x.entries.items[e.prev_at].next_at = e.next_at else x.nodes.items[n].first_entry = e.next_at; + if (e.next_at != none) x.entries.items[e.next_at].prev_at = e.prev_at; + _ = x.by_key.remove(e.key); + x.gpa.free(e.tail); + e.live = false; + x.free_entries.appendAssumeCapacity(id); // place keeps room for every slot + x.addPath(n, -1, 0); + return n; + } + + /// Frees the nodes from n up that no entry holds (sub_all 0). + fn prune(x: *Self, n: u32) void { + var m = n; + while (m != none and x.nodes.items[m].sub_all == 0) { + const nd = x.nodes.items[m]; + if (nd.parent != none) { + if (nd.prev_sib != none) x.nodes.items[nd.prev_sib].next_sib = nd.next_sib else x.nodes.items[nd.parent].first_child = nd.next_sib; + if (nd.next_sib != none) x.nodes.items[nd.next_sib].prev_sib = nd.prev_sib; + } + if (x.by_digest.remove(nd.digest)) x.dropped(); + x.free_nodes.appendAssumeCapacity(m); // newNode keeps room for every slot + m = nd.parent; + } + } + + /// std's tombstones never clear and keys fill 80 %: rehash at a tenth, so a tenth of the map always stays free. + fn dropped(x: *Self) void { + x.dead += 1; + if (@as(u64, x.dead) * 10 < x.by_digest.capacity()) return; + x.by_digest.rehash(std.hash_map.AutoContext(Digest){}); + x.dead = 0; + } + + /// The entry's tail matches the prompt there and the entry is shorter (its full pages matched by digest). + fn tailOf(e: *const Entry, prompt: []const i32) bool { + const at: usize = @intCast(e.pos - e.tail.len); + return e.pos < prompt.len and std.mem.eql(i32, e.tail, prompt[at..][0..e.tail.len]); + } + + /// The longest entry of tag whose ids strictly prefix prompt (RAM first on a tie); digests: the prompt's chain. + pub fn find(x: *Self, tag: u32, prompt: []const i32, digests: []const Digest, tier: Tier) ?u32 { + const full: u32 = @intCast(prompt.len / x.page); + const r = x.by_digest.get(root(tag)) orelse return null; + // the deepest node on the prompt's path: each prefix of an entry's path has a node: binary search + var lo: u32 = 0; + var hi: u32 = full; + while (lo < hi) { + const mid = lo + (hi - lo + 1) / 2; + if (x.by_digest.contains(digests[mid - 1])) lo = mid else hi = mid - 1; + } + var n: u32 = if (lo == 0) r else x.by_digest.get(digests[lo - 1]).?; + while (n != none) : (n = x.nodes.items[n].parent) { + const nd = &x.nodes.items[n]; + if (nd.sub_all == 0 or (tier == .ram and nd.own_ram == 0)) continue; + var best: u32 = none; + var e = nd.first_entry; + while (e != none) : (e = x.entries.items[e].next_at) { + const en = &x.entries.items[e]; + if (tier == .ram and !en.ram) continue; + if (!tailOf(en, prompt)) continue; + if (best == none or en.pos > x.entries.items[best].pos or (en.pos == x.entries.items[best].pos and en.ram)) best = e; + } + if (best != none) return best; + } + return null; + } + + /// A newer entry of id's chain holds every position it does (tier .ram: in RAM), so id need not be written. + pub fn covered(x: *Self, id: u32, tier: Tier) bool { + const e = &x.entries.items[id]; + const nd = &x.nodes.items[e.node]; + var f = nd.first_entry; + while (f != none) : (f = x.entries.items[f].next_at) { + const o = &x.entries.items[f]; + if (f == id or (tier == .ram and !o.ram)) continue; + if (o.pos > e.pos and std.mem.startsWith(i32, o.tail, e.tail)) return true; + } + var c = nd.first_child; + while (c != none) : (c = x.nodes.items[c].next_sib) { + const ch = &x.nodes.items[c]; + const n = if (tier == .ram) ch.sub_ram else ch.sub_all; + if (n == 0) continue; + if (e.tail.len == 0) return true; + if (ch.tokens) |t| if (std.mem.startsWith(i32, t, e.tail)) return true; + } + return false; + } + + /// How many RAM entries extend a RAM entry (longer, same tag, its ids their prefix): branch points have more. + pub fn extensions(x: *Self, id: u32) u32 { + const e = &x.entries.items[id]; + const nd = &x.nodes.items[e.node]; + var n: u32 = 0; + var f = nd.first_entry; + while (f != none) : (f = x.entries.items[f].next_at) { + const o = &x.entries.items[f]; + if (f != id and o.ram and o.pos > e.pos and std.mem.startsWith(i32, o.tail, e.tail)) n += 1; + } + var c = nd.first_child; + while (c != none) : (c = x.nodes.items[c].next_sib) { + const ch = &x.nodes.items[c]; + if (ch.sub_ram == 0) continue; + if (std.mem.startsWith(i32, ch.tokens.?, e.tail)) n += ch.sub_ram; + } + return n; + } + + /// A RAM entry no other RAM entry extends: a chat's newest state. + pub fn isLeaf(x: *Self, id: u32) bool { + return x.entries.items[id].ram and !x.covered(id, .ram); + } + + /// A RAM leaf's older turns (the RAM entries it extends) into out; returns the chat's recency (newest `used`). + pub fn members(x: *Self, leaf: u32, out: *std.ArrayList(u32), used: *const fn (*const Extra) u64) !u64 { + const l = &x.entries.items[leaf]; + var recency = used(&l.extra); + var below: u32 = none; // the path's node under n: its tokens are the page an entry at n must prefix + var n = l.node; + while (n != none) : ({ + below = n; + n = x.nodes.items[n].parent; + }) { + const nd = &x.nodes.items[n]; + if (nd.own_ram == 0) continue; + const page_ids: []const i32 = if (below == none) l.tail else x.nodes.items[below].tokens.?; + var f = nd.first_entry; + while (f != none) : (f = x.entries.items[f].next_at) { + const o = &x.entries.items[f]; + if (f == leaf or !o.ram or o.pos >= l.pos) continue; + if (!std.mem.startsWith(i32, page_ids, o.tail)) continue; + try out.append(x.gpa, f); + recency = @max(recency, used(&o.extra)); + } + } + return recency; + } + + /// A RAM entry's ids, rebuilt from its path's pages and tail, into out (len = pos). + pub fn idsOf(x: *Self, id: u32, out: []i32) !void { + const e = &x.entries.items[id]; + if (out.len != e.pos) return error.BadLength; + const at = e.pos - e.tail.len; + @memcpy(out[at..], e.tail); + var n = e.node; + while (n != none and x.nodes.items[n].depth > 0) : (n = x.nodes.items[n].parent) { + const nd = &x.nodes.items[n]; + const t = nd.tokens orelse return error.NotInRam; + @memcpy(out[(nd.depth - 1) * x.page ..][0..x.page], t); + } + } + }; +} + +const T = Index(u64); + +fn usedOf(e: *const u64) u64 { + return e.*; +} + +fn seq(gpa: std.mem.Allocator, n: usize, salt: i32) ![]i32 { + const v = try gpa.alloc(i32, n); + for (v, 0..) |*t, i| t.* = @as(i32, @intCast(i % 997)) * 3 + salt; + return v; +} + +fn add(x: *T, tag: u32, ids: []const i32, ram: bool, used: u64) !u32 { + var d: [64]Digest = undefined; + chain(tag, ids, x.page, d[0 .. ids.len / x.page]); + return (try x.insert(tag, ids, d[0 .. ids.len / x.page], ram, used)).id; +} + +test "longest strict prefix, ties, tags and coverage" { + const gpa = std.testing.allocator; + var x = T.init(gpa, 16); + defer x.deinit(); + const chat = try seq(gpa, 200, 0); + defer gpa.free(chat); + const t1 = try add(&x, 7, chat[0..40], true, 1); // a prompt's replay point + const t2 = try add(&x, 7, chat[0..72], true, 2); // a turn's end + const t3 = try add(&x, 7, chat[0..128], false, 3); // on disk only, page aligned + _ = try add(&x, 8, chat[0..100], true, 4); // another tag + var d: [16]Digest = undefined; + chain(7, chat[0..40], 16, d[0..2]); + const again = try x.insert(7, chat[0..40], d[0..2], true, 9); + try std.testing.expect(again.id == t1 and !again.new); + chain(7, chat[0..150], 16, d[0..9]); + try std.testing.expectEqual(@as(?u32, t3), x.find(7, chat[0..150], d[0..9], .any)); + try std.testing.expectEqual(@as(?u32, t2), x.find(7, chat[0..150], d[0..9], .ram)); + try std.testing.expectEqual(@as(?u32, t2), x.find(7, chat[0..128], d[0..8], .any)); // strict: 128 is not a prefix of 128 + chain(7, chat[0..72], 16, d[0..4]); + try std.testing.expectEqual(@as(?u32, t1), x.find(7, chat[0..72], d[0..4], .any)); + var other = try gpa.dupe(i32, chat[0..150]); + defer gpa.free(other); + other[50] = -1; // diverges inside t2's tail page: t1 still prefixes it + chain(7, other, 16, d[0..9]); + try std.testing.expectEqual(@as(?u32, t1), x.find(7, other, d[0..9], .any)); + // t3 extends t2 on disk only: its page's tokens are gone, so a tail there is undecided (the store marks covered_by) + try std.testing.expect(x.covered(t1, .ram) and !x.covered(t2, .any) and !x.covered(t2, .ram) and !x.covered(t3, .any)); + const t4 = try add(&x, 7, chat[0..64], false, 5); // page aligned: any deeper entry covers it + try std.testing.expect(x.covered(t4, .any) and x.covered(t4, .ram)); + x.remove(t4); + try std.testing.expect(x.isLeaf(t2) and !x.isLeaf(t1)); + try std.testing.expectEqual(@as(u32, 1), x.extensions(t1)); + try std.testing.expectEqual(@as(u32, 0), x.extensions(t2)); + var m: std.ArrayList(u32) = .empty; + defer m.deinit(gpa); + try std.testing.expectEqual(@as(u64, 2), try x.members(t2, &m, usedOf)); + try std.testing.expectEqualSlices(u32, &.{t1}, m.items); + const ids = try gpa.alloc(i32, 72); + defer gpa.free(ids); + try x.idsOf(t2, ids); + try std.testing.expectEqualSlices(i32, chat[0..72], ids); + // going to disk drops the path's tokens once no RAM entry needs them; removal prunes the nodes + try x.setRam(t2, false, null); + try x.setRam(t1, false, null); + try std.testing.expectError(error.NotInRam, x.idsOf(t2, ids)); + x.remove(t3); + x.remove(t2); + x.remove(t1); + chain(7, chat[0..150], 16, d[0..9]); + try std.testing.expectEqual(@as(?u32, null), x.find(7, chat[0..150], d[0..9], .any)); + try std.testing.expectEqual(@as(u32, 1 + 6), x.by_digest.count()); // tag 8's root + its 6 full pages +} + +test "a long run of turns saved and dropped leaves the page map free slots" { + const gpa = std.testing.allocator; + var x = T.init(gpa, 16); + defer x.deinit(); + var live: std.ArrayList(u32) = .empty; + defer live.deinit(gpa); + var rng = std.Random.DefaultPrng.init(5); + for (0..3000) |i| { + // a chat's turn (its own pages), the oldest dropped once eight are kept: nodes come and go + const ids = try seq(gpa, 16 * 60, @intCast(i)); + defer gpa.free(ids); + try live.append(gpa, try add(&x, 7, ids[0 .. 16 * (1 + rng.random().uintLessThan(usize, 60))], true, i)); + if (live.items.len > 8) x.remove(live.orderedRemove(0)); + const meta = x.by_digest.metadata orelse continue; + var free: usize = 0; + for (meta[0..x.by_digest.capacity()]) |m| free += @intFromBool(m.isFree()); + try std.testing.expect(free * 10 >= x.by_digest.capacity()); + } +} diff --git a/zig/src/sessions/prefix_fail_test.zig b/zig/src/sessions/prefix_fail_test.zig new file mode 100644 index 0000000000..dd0d8ec489 --- /dev/null +++ b/zig/src/sessions/prefix_fail_test.zig @@ -0,0 +1,127 @@ +//! prefix.Index under allocation failure: an insert that fails changes nothing, and the same insert then succeeds. + +const std = @import("std"); +const prefix = @import("prefix.zig"); +const testing = std.testing; +const T = prefix.Index(u64); +const Digest = prefix.Digest; +const page = 16; + +/// What a failed insert must leave as it was; spare token blocks may stay in the reuse pool. +const Shape = struct { keys: u32, digests: u32, ram: u32, held: u32, sub_all: u32, sub_ram: u32 }; + +fn shape(x: *T, tag: u32) Shape { + var s: Shape = .{ .keys = x.by_key.count(), .digests = x.by_digest.count(), .ram = 0, .held = 0, .sub_all = 0, .sub_ram = 0 }; + var e = x.by_key.valueIterator(); + while (e.next()) |id| s.ram += @intFromBool(x.get(id.*).ram); + var n = x.by_digest.valueIterator(); + while (n.next()) |id| s.held += @intFromBool(x.node(id.*).tokens != null); + if (x.by_digest.get(prefix.root(tag))) |r| { + s.sub_all = x.node(r).sub_all; + s.sub_ram = x.node(r).sub_ram; + } + return s; +} + +/// Every slot is live in its map or on its free list, and a node keeps its page's tokens exactly while sub_ram > 0. +fn consistent(x: *T) !void { + try testing.expectEqual(x.entries.items.len - x.free_entries.items.len, x.by_key.count()); + try testing.expectEqual(x.nodes.items.len - x.free_nodes.items.len, x.by_digest.count()); + var n = x.by_digest.valueIterator(); + while (n.next()) |id| { + const nd = x.node(id.*); + if (nd.depth > 0) try testing.expectEqual(nd.sub_ram > 0, nd.tokens != null); + } +} + +fn seq(gpa: std.mem.Allocator, n: usize, salt: i32) ![]i32 { + const v = try gpa.alloc(i32, n); + for (v, 0..) |*t, i| t.* = @as(i32, @intCast(i % 997)) * 3 + salt; + return v; +} + +fn add(x: *T, tag: u32, ids: []const i32, ram: bool, used: u64) !u32 { + var d: [64]Digest = undefined; + prefix.chain(tag, ids, page, d[0 .. ids.len / page]); + return (try x.insert(tag, ids, d[0 .. ids.len / page], ram, used)).id; +} + +/// The (k + 1)-th allocation from now fails, and every one after it until `heal`. +fn failAfter(fa: *testing.FailingAllocator, k: usize) void { + fa.fail_index = fa.alloc_index + k; +} + +fn heal(fa: *testing.FailingAllocator) void { + fa.fail_index = std.math.maxInt(usize); +} + +test "a RAM insert failing at any allocation leaves the index as it was, and the same insert then succeeds" { + const chat = try seq(testing.allocator, 160, 0); + defer testing.allocator.free(chat); + var d: [16]Digest = undefined; + prefix.chain(7, chat[0..150], page, d[0..9]); + var k: usize = 0; + while (k < 256) : (k += 1) { + var fa = testing.FailingAllocator.init(testing.allocator, .{}); + var x = T.init(fa.allocator(), page); + defer x.deinit(); + // what the insert meets: entries sharing its first pages, a disk entry, another tag, and free slots to reuse + _ = try add(&x, 7, chat[0..40], true, 1); + _ = try add(&x, 7, chat[0..72], false, 2); + _ = try add(&x, 8, chat[0..33], true, 3); + x.remove(try add(&x, 9, chat[0..48], true, 4)); + try consistent(&x); + const before = shape(&x, 7); + failAfter(&fa, k); + const got = x.insert(7, chat[0..150], d[0..9], true, 9) catch |err| { + try testing.expectEqual(error.OutOfMemory, err); + try consistent(&x); + try testing.expectEqual(before, shape(&x, 7)); + heal(&fa); + const again = try x.insert(7, chat[0..150], d[0..9], true, 9); + try testing.expect(again.new); + try consistent(&x); + var out: [150]i32 = undefined; + try x.idsOf(again.id, &out); + try testing.expectEqualSlices(i32, chat[0..150], &out); + var d10: [10]Digest = undefined; + prefix.chain(7, chat[0..160], page, &d10); + try testing.expectEqual(@as(?u32, again.id), x.find(7, chat[0..160], &d10, .ram)); + x.remove(again.id); + try consistent(&x); + try testing.expectEqual(before, shape(&x, 7)); + continue; + }; + // no allocation left to fail: every failure point before this one was injected and rolled back + try testing.expect(got.new and k > 0); + return; + } + return error.TestUnexpectedResult; +} + +test "a disk insert into an empty index failing at any allocation leaves it empty, and the same insert then succeeds" { + const chat = try seq(testing.allocator, 90, 0); + defer testing.allocator.free(chat); + var k: usize = 0; + while (k < 256) : (k += 1) { + var fa = testing.FailingAllocator.init(testing.allocator, .{}); + var x = T.init(fa.allocator(), page); + defer x.deinit(); + failAfter(&fa, k); + const got = x.insertFrom(7, prefix.none, chat, 5) catch |err| { + try testing.expectEqual(error.OutOfMemory, err); + try consistent(&x); + try testing.expectEqual(@as(u32, 0), x.by_digest.count()); // the tag's root and pages made before the failure + try testing.expectEqual(@as(u32, 0), x.by_key.count()); + heal(&fa); + const again = try x.insertFrom(7, prefix.none, chat, 5); + try testing.expect(again.new); + try testing.expectEqual(@as(u32, 1 + 5), x.by_digest.count()); + try consistent(&x); + continue; + }; + try testing.expect(got.new and k > 0); + return; + } + return error.TestUnexpectedResult; +} diff --git a/zig/src/sessions/sessions.zig b/zig/src/sessions/sessions.zig new file mode 100644 index 0000000000..d13bef6029 --- /dev/null +++ b/zig/src/sessions/sessions.zig @@ -0,0 +1,34 @@ +//! Session memory, family-neutral: the KV pool book, page bytes, prefix entries, the RAM tier and the NVMe tier. + +pub const freeset = @import("freeset.zig"); +pub const pool = @import("pool.zig"); +pub const Pool = pool.Pool; +pub const Slot = pool.Slot; +pub const Family = pool.Family; +pub const Layout = pool.Layout; +pub const pagestore = @import("pagestore.zig"); +pub const PageStore = pagestore.PageStore; +pub const HostPool = pagestore.HostPool; +pub const prefix = @import("prefix.zig"); +pub const lanes = @import("lanes.zig"); +pub const format = @import("format.zig"); +pub const disk = @import("disk.zig"); +pub const Disk = disk.Disk; +pub const jobs = @import("jobs.zig"); +pub const store = @import("store.zig"); +pub const Store = store.Store; + +test { + _ = freeset; + _ = pool; + _ = pagestore; + _ = prefix; + _ = lanes; + _ = format; + _ = disk; + _ = jobs; + _ = store; + _ = @import("prefix_fail_test.zig"); + _ = @import("disk_test.zig"); + _ = @import("store_test.zig"); +} diff --git a/zig/src/sessions/store.zig b/zig/src/sessions/store.zig new file mode 100644 index 0000000000..7f0548de0a --- /dev/null +++ b/zig/src/sessions/store.zig @@ -0,0 +1,586 @@ +//! The session store: RAM entries holding pool pages and bounded state, a residency budget, and the NVMe tier. + +const std = @import("std"); +const Io = std.Io; +const pool_mod = @import("pool.zig"); +const Pool = pool_mod.Pool; +const Slot = pool_mod.Slot; +const PageStore = @import("pagestore.zig").PageStore; +const prefix = @import("prefix.zig"); +const Digest = prefix.Digest; +const disk_mod = @import("disk.zig"); +const Disk = disk_mod.Disk; +const jobs = @import("jobs.zig"); +const Job = jobs.Job; + +pub const none = prefix.none; + +/// A slot's bounded state (rings, carries, lookback, taps) as the family snapshots it: opaque to the store. +pub const Bounded = struct { + ptr: *anyopaque, + vtable: *const VTable, + + pub const VTable = struct { + nbytes: *const fn (ptr: *anyopaque) u64, + /// host copies of its pieces for a park (owned by the caller: name and bytes allocated with gpa) + blobs: *const fn (ptr: *anyopaque, gpa: std.mem.Allocator) anyerror![]disk_mod.Blob, + release: *const fn (ptr: *anyopaque) void, + }; +}; + +pub const Policy = struct { + /// bounded state the RAM tier keeps (least recently used entries go past it) + ram_bytes: u64 = 512 << 20, + /// Idle chats and entry-only pool bytes: null is unlimited; zero retains no idle residency. + chats: ?u32 = null, + resident_bytes: ?u64 = null, + /// park an evicted chat's older turns too (default: drop them, the leaf's file covers them) + park_prefixes: bool = false, + /// drop, never write, an entry a newer entry of its chain covers + skip_covered: bool = true, + /// write a newer turn as a delta on the file its leading pages came from + delta: bool = true, + /// parks and restores on the tier thread (settled by the plan) + async_io: bool = false, +}; + +pub const Extra = struct { + /// RAM: the pool pages it holds, logical [0, npages) + pages: []u32 = &.{}, + bounded: ?Bounded = null, + /// a file with this key is registered on the NVMe tier + file: bool = false, + used: u64 = 0, + origin: ?pool_mod.Origin = null, + /// a newer entry of its chain was parked from RAM: writing this one again is redundant + covered_by: bool = false, + kind: u32 = 0, + /// an unsettled park of it + job: ?*Job = null, + older: u32 = none, + newer: u32 = none, +}; + +const Index = prefix.Index(Extra); + +pub const Hit = struct { id: u32, ram: bool, pos: u64 }; + +pub const Stats = struct { + saves: u64 = 0, + dups: u64 = 0, + hits_ram: u64 = 0, + hits_disk: u64 = 0, + parks: u64 = 0, + delta_parks: u64 = 0, + drops: u64 = 0, + covered_drops: u64 = 0, + chat_evictions: u64 = 0, + park_ns: u64 = 0, + restore_ns: u64 = 0, +}; + +pub const Store = struct { + gpa: std.mem.Allocator, + io: Io, + pool: *Pool, + pages: PageStore, + index: Index, + disk: ?*Disk, + /// heap-held: the tier thread keeps its address + runner: ?*jobs.Runner = null, + policy: Policy, + clock: u64 = 0, + bounded_total: u64 = 0, + /// RAM entries, least recently used first + oldest: u32 = none, + newest: u32 = none, + ram_count: u32 = 0, + pending: std.ArrayList(*Job) = .empty, + scratch: std.ArrayList(Digest) = .empty, + stats: Stats = .{}, + + pub fn init(gpa: std.mem.Allocator, io: Io, pool: *Pool, pages: PageStore, d: ?*Disk, policy: Policy) !Store { + var s: Store = .{ .gpa = gpa, .io = io, .pool = pool, .pages = pages, .index = Index.init(gpa, pool.page), .disk = d, .policy = policy }; + if (d) |dd| { + const r = try gpa.create(jobs.Runner); + r.* = jobs.Runner.init(gpa, io, dd); + s.runner = r; + if (policy.async_io) r.startThread() catch |e| { + gpa.destroy(r); + return e; + }; + } + return s; + } + + pub fn deinit(s: *Store) void { + if (s.runner) |r| { + for (s.pending.items) |j| r.wait(j); + r.deinit(); + s.gpa.destroy(r); + } + for (s.pending.items) |j| j.deinit(s.gpa); + s.pending.deinit(s.gpa); + for (s.index.entries.items) |*e| if (e.live) { + if (e.extra.bounded) |b| b.vtable.release(b.ptr); + s.pool.drop(e.extra.pages) catch {}; + s.gpa.free(e.extra.pages); + }; + s.index.deinit(); + s.scratch.deinit(s.gpa); + s.* = undefined; + } + + fn digests(s: *Store, tag: u32, ids: []const i32) ![]const Digest { + const n = ids.len / s.pool.page; + try s.scratch.resize(s.gpa, n); + prefix.chain(tag, ids, s.pool.page, s.scratch.items); + return s.scratch.items; + } + + pub fn entry(s: *Store, id: u32) *Index.Entry { + return s.index.get(id); + } + + /// The longest stored entry (RAM or NVMe) whose ids strictly prefix prompt, with the same tag. + pub fn find(s: *Store, tag: u32, prompt: []const i32) !?Hit { + const d = try s.digests(tag, prompt); + const id = s.index.find(tag, prompt, d, .any) orelse return null; + const e = s.index.get(id); + return .{ .id = id, .ram = e.ram, .pos = e.pos }; + } + + fn link(s: *Store, id: u32) void { + const x = &s.index.get(id).extra; + x.older = s.newest; + x.newer = none; + if (s.newest != none) s.index.get(s.newest).extra.newer = id else s.oldest = id; + s.newest = id; + } + + fn unlink(s: *Store, id: u32) void { + const x = &s.index.get(id).extra; + if (x.older != none) s.index.get(x.older).extra.newer = x.newer else s.oldest = x.newer; + if (x.newer != none) s.index.get(x.newer).extra.older = x.older else s.newest = x.older; + } + + fn touch(s: *Store, id: u32) void { + s.clock += 1; + const e = s.index.get(id); + e.extra.used = s.clock; + if (e.ram) { + s.unlink(id); + s.link(id); + } + } + + /// An entry for a slot's state at len(ids), sharing its pages; null for a duplicate (bounded is released then). + pub fn save(s: *Store, slot: *Slot, bounded: Bounded, ids: []const i32, tag: u32, kind: u32) !?u32 { + if (ids.len == 0) return error.EmptyEntry; + const need = pool_mod.pagesFor(ids.len, s.pool.page); + if (slot.len < need) return error.SlotTooShort; + const d = try s.digests(tag, ids); + const ins = try s.index.insert(tag, ids, d, false, .{ .kind = kind }); + errdefer if (ins.new) s.index.remove(ins.id); + const e = s.index.get(ins.id); + if (e.ram) { + bounded.vtable.release(bounded.ptr); + s.touch(ins.id); + s.stats.dups += 1; + return null; + } + const pages = try s.gpa.dupe(u32, slot.mapped()[0..need]); + errdefer s.gpa.free(pages); + try s.index.setRam(ins.id, true, ids); + s.pool.share(pages); + const x = &s.index.get(ins.id).extra; + x.pages = pages; + x.bounded = bounded; + x.kind = kind; + if (slot.origin) |o| x.origin = .{ .key = o.key, .pages = @min(o.pages, @as(u32, @intCast(ids.len / s.pool.page))) }; + s.bounded_total += bounded.vtable.nbytes(bounded.ptr); + s.ram_count += 1; + s.link(ins.id); + s.touch(ins.id); + s.stats.saves += 1; + try s.trimRam(); + _ = try s.enforce(); + return ins.id; + } + + /// A RAM entry into an empty slot: full pages shared, the partial last page copied; returns its bounded state. + pub fn restoreRam(s: *Store, id: u32, slot: *Slot) !Bounded { + const e = s.index.get(id); + if (!e.ram) return error.NotInRam; + const full: u32 = @intCast(e.pos / s.pool.page); + try slot.adopt(e.extra.pages[0..full]); + errdefer _ = slot.releaseAll() catch {}; + if (e.pos % s.pool.page != 0) { + const src = e.extra.pages[full]; + const dst = try s.pool.takeAt(full); + for (s.pool.layout.families, 0..) |f, fi| { + if (f.split and !s.pool.owns(src)) continue; + try s.pages.copyPage(@intCast(fi), s.pool.familyPage(f, src), s.pool.familyPage(f, dst)); + } + try slot.push(dst); + } + if (e.extra.origin) |o| slot.origin = .{ .key = o.key, .pages = @min(o.pages, full) }; + s.touch(id); + s.stats.hits_ram += 1; + return e.extra.bounded.?; + } + + /// A restored entry's ids (pos long): RAM from the index, else `given`, the prompt's prefix, checked. + pub fn historyOf(s: *Store, id: u32, given: ?[]const i32, out: []i32) !void { + const e = s.index.get(id); + if (out.len != e.pos) return error.BadLength; + if (e.ram) return s.index.idsOf(id, out); + // an NVMe entry's tokens left the index with its RAM (prefix.dropTokens): only the prompt still holds them + const ids = given orelse return error.NotInRam; + if (ids.len < e.pos) return error.BadLength; + const pos: usize = @intCast(e.pos); + const full = pos / s.pool.page; + const d = try s.digests(e.tag, ids[0..pos]); + const node = if (full == 0) prefix.root(e.tag) else d[full - 1]; + if (!std.mem.eql(u8, &prefix.entryKey(node, e.pos, ids[full * s.pool.page .. pos]), &e.key)) return error.NotTheEntry; + @memcpy(out, ids[0..pos]); + } + + /// Starts an NVMe resume into an empty slot (pages mapped now, filled by the job); settle it before the slot runs. + pub fn beginRestore(s: *Store, id: u32, slot: *Slot) !*Job { + const d = s.disk orelse return error.NoDisk; + const e = s.index.get(id); + if (!e.extra.file) return error.NotOnDisk; + if (slot.len != 0) return error.SlotNotEmpty; + try slot.ensure(e.pos); + const j = try s.gpa.create(Job); + j.* = .{ .kind = .restore, .entry = id, .slot = slot.id }; + errdefer j.deinit(s.gpa); + var ext: std.ArrayList(disk_mod.Extent) = .empty; + errdefer ext.deinit(s.gpa); + try d.extents(e.key, &ext); + j.extents = try ext.toOwnedSlice(s.gpa); + const np = pool_mod.pagesFor(e.pos, s.pool.page); + j.lists = try s.familyLists(slot.mapped()[0..np], 0, false); + j.sink = .{ .store = s.pages, .families = s.pool.layout.families, .page = s.pool.page, .world = s.pool.world, .rank = s.pool.rank, .pages = j.lists }; + try s.pending.append(s.gpa, j); + try s.runner.?.submit(j); + return j; + } + + /// Per family: family-tensor pages of logical [first, len(pages)) (owned_only: this rank's of split families). + fn familyLists(s: *Store, pages: []const u32, first: u32, owned_only: bool) ![][]u32 { + const fams = s.pool.layout.families; + const out = try s.gpa.alloc([]u32, fams.len); + var made: usize = 0; + errdefer { + for (out[0..made]) |l| s.gpa.free(l); + s.gpa.free(out); + } + for (fams, 0..) |f, fi| { + const w = s.pool.world; + const sharded = f.split and w > 1 and owned_only; + var n: usize = pages.len - first; + if (sharded) { + var by: [pool_mod.max_world]u32 = undefined; + pool_mod.span(first, @intCast(pages.len), w, &by); + n = by[s.pool.rank]; + } + const l = try s.gpa.alloc(u32, n); + var i: usize = 0; + var k: usize = first; + if (sharded) k = first + (s.pool.rank + w - first % w) % w; + while (k < pages.len) : (k += if (sharded) w else 1) { + l[i] = s.pool.familyPage(f, pages[k]); + i += 1; + } + out[fi] = l; + made += 1; + } + return out; + } + + /// Starts a park of a RAM entry (null: nothing to write, the entry is already settled out of RAM). + pub fn beginPark(s: *Store, id: u32) !?*Job { + const d = s.disk orelse return error.NoDisk; + const e = s.index.get(id); + if (!e.ram or e.extra.job != null) return error.NotParkable; + if (e.extra.file and d.get(e.key) != null) { + try s.leaveRam(id); + return null; + } + const page = s.pool.page; + const npages = pool_mod.pagesFor(e.pos, page); + var base: ?Digest = null; + var first: u32 = 0; + if (s.policy.delta) if (e.extra.origin) |o| if (o.pages > 0) { + var r = d.get(o.key); + while (r) |rec| : (r = if (rec.base == disk_mod.none) null else &d.recs.items[rec.base]) { + if (rec.first_page < o.pages) break; + } + if (r) |rec| if (rec.depth < d.opts.max_chain) { + base = rec.key; + first = o.pages; + }; + }; + const j = try s.gpa.create(Job); + j.* = .{ .kind = .park, .entry = id }; + errdefer j.deinit(s.gpa); + j.ids = try s.gpa.alloc(i32, e.pos); + try s.index.idsOf(id, j.ids); + j.lists = try s.familyLists(e.extra.pages, first, true); + j.blobs = try e.extra.bounded.?.vtable.blobs(e.extra.bounded.?.ptr, s.gpa); + j.src = .{ .key = e.key, .base = base, .tag = e.tag, .kind = e.extra.kind, .page = page, .pos = e.pos, .first_page = first, .npages = npages, .ids = j.ids[first * page ..], .store = s.pages, .fence = try s.pages.fence(), .families = s.pool.layout.families, .pages = j.lists, .blobs = j.blobs }; + if (base) |b| d.get(b).?.refs += 1; // held while the job runs: a trim cannot take the base + e.extra.job = j; + try s.pending.append(s.gpa, j); + try s.runner.?.submit(j); + return j; + } + + /// Settles a finished job at the plan's point: a park registers its file, a restore returns the bounded state. + pub fn settle(s: *Store, j: *Job) !?disk_mod.Restored { + s.runner.?.wait(j); + defer { + for (s.pending.items, 0..) |p, i| if (p == j) { + _ = s.pending.orderedRemove(i); + break; + }; + j.deinit(s.gpa); + } + const d = s.disk.?; + switch (j.kind) { + .park => { + if (j.src.base) |b| d.get(b).?.refs -= 1; + const e = s.index.get(j.entry); + e.extra.job = null; + if (j.err) |err| return err; // the entry stays in RAM; the .tmp is gone + _ = try d.register(&j.src, j.size); + e.extra.file = true; + s.stats.parks += 1; + if (j.src.base != null) s.stats.delta_parks += 1; + s.stats.park_ns += j.elapsed_ns; + if (s.policy.skip_covered) { + var m: std.ArrayList(u32) = .empty; + defer m.deinit(s.gpa); + _ = try s.index.members(j.entry, &m, usedOf); + for (m.items) |o| s.index.get(o).extra.covered_by = true; + } + try s.leaveRam(j.entry); + try s.trimDisk(); + return null; + }, + .restore => { + if (j.err) |err| { + // the slot as before the restore: empty, its reservation kept + _ = try s.pool.slots.items[j.slot].truncate(0); + return err; + } + const e = s.index.get(j.entry); + d.touch(e.key); + s.touch(j.entry); + s.pool.slots.items[j.slot].origin = .{ .key = e.key, .pages = @intCast(e.pos / s.pool.page) }; + s.stats.hits_disk += 1; + s.stats.restore_ns += j.elapsed_ns; + const r = j.restored; + j.restored = null; + return r; + }, + } + } + + /// Settles every pending job in submission order (one rank, or the plan's "settle all"). + pub fn settleAll(s: *Store) !void { + while (s.pending.items.len > 0) { + if (try s.settle(s.pending.items[0])) |r| { + var rr = r; + rr.deinit(s.gpa); + } + } + } + + fn usedOf(x: *const Extra) u64 { + return x.used; + } + + /// A RAM entry lets go of its pages and bounded state; it stays indexed if a file holds it. + fn leaveRam(s: *Store, id: u32) !void { + const e = s.index.get(id); + if (!e.ram) return; + try s.pool.drop(e.extra.pages); + s.gpa.free(e.extra.pages); + e.extra.pages = &.{}; + if (e.extra.bounded) |b| { + s.bounded_total -= b.vtable.nbytes(b.ptr); + b.vtable.release(b.ptr); + } + e.extra.bounded = null; + s.unlink(id); + s.ram_count -= 1; + try s.index.setRam(id, false, null); + if (!e.extra.file) s.index.remove(id); + } + + /// An entry whose file could not be read back: the file deleted, the entry gone unless RAM holds it (every rank). + pub fn forget(s: *Store, id: u32) !void { + const e = s.index.get(id); + if (e.extra.job != null) return error.Busy; + if (e.extra.file) if (s.disk) |d| { + d.stats.bad += 1; + // a delta's base stays while a newer file stacks on it (an opt-in delta mode) + d.remove(e.key) catch |err| return if (err == error.HasDependents) {} else err; + e.extra.file = false; + }; + if (!e.ram) s.index.remove(id); + } + + /// Drops an entry from RAM without writing it. + pub fn drop(s: *Store, id: u32) !void { + try s.leaveRam(id); + s.stats.drops += 1; + } + + /// Lets go of a RAM entry: dropped when covered, parked if the tier takes it, else dropped; returns a started park. + pub fn evict(s: *Store, id: u32) !?*Job { + const e = s.index.get(id); + if (e.extra.job != null) return null; + if (s.policy.skip_covered and (e.extra.covered_by or s.index.covered(id, .any))) { + try s.drop(id); + s.stats.covered_drops += 1; + return null; + } + if (s.disk) |d| if (d.accepts(e.pos)) { + const j = try s.beginPark(id) orelse return null; + if (!s.policy.async_io) _ = try s.settle(j); + return if (s.policy.async_io) j else null; + }; + try s.drop(id); + return null; + } + + /// Evicts least recently used RAM entries past the bounded-state budget. + fn trimRam(s: *Store) !void { + var id = s.oldest; + while (s.bounded_total > s.policy.ram_bytes and s.ram_count > 1 and id != none) { + const next = s.index.get(id).extra.newer; + if (s.index.get(id).extra.job == null) _ = try s.evict(id); + id = next; + } + } + + /// Files the NVMe budget deleted leave the index (a RAM entry with that key stays, unwritten again). + fn trimDisk(s: *Store) !void { + const d = s.disk orelse return; + var gone: std.ArrayList(Digest) = .empty; + defer gone.deinit(s.gpa); + try d.trim(&gone); + for (gone.items) |k| if (s.index.lookup(k)) |id| { + s.index.get(id).extra.file = false; + if (!s.index.get(id).ram) s.index.remove(id); + }; + } + + const Chat = struct { leaf: u32, recency: u64 }; + + fn chatLess(_: void, a: Chat, b: Chat) bool { + return a.recency < b.recency or (a.recency == b.recency and a.leaf < b.leaf); + } + + /// A chat is live while a slot maps its leaf's first page. + fn idleLeaf(s: *Store, id: u32) bool { + const e = s.index.get(id); + return e.extra.job == null and s.index.isLeaf(id) and (e.extra.pages.len == 0 or s.pool.slot_refs[e.extra.pages[0]] == 0); + } + + /// Evicts LRU idle chats past the residency budget (leaf parked, unshared older turns dropped); returns how many. + pub fn enforce(s: *Store) !u32 { + const pol = s.policy; + if (pol.chats == null and pol.resident_bytes == null) return 0; + var chats: std.ArrayList(Chat) = .empty; + defer chats.deinit(s.gpa); + var members: std.ArrayList(u32) = .empty; + defer members.deinit(s.gpa); + var id = s.oldest; + while (id != none) : (id = s.index.get(id).extra.newer) { + if (!s.idleLeaf(id)) continue; + members.clearRetainingCapacity(); + try chats.append(s.gpa, .{ .leaf = id, .recency = try s.index.members(id, &members, usedOf) }); + } + std.mem.sort(Chat, chats.items, {}, chatLess); + var idle: u32 = @intCast(chats.items.len); + var gone: u32 = 0; + for (chats.items) |c| { + const over_n = if (pol.chats) |limit| idle > limit else false; + const over_b = if (pol.resident_bytes) |limit| s.pool.entryOnlyBytes() > limit else false; + if (!over_n and !over_b) break; + if (!s.index.get(c.leaf).ram or !s.idleLeaf(c.leaf)) continue; + members.clearRetainingCapacity(); + _ = try s.index.members(c.leaf, &members, usedOf); + _ = try s.evict(c.leaf); + // deepest first: a member more RAM entries extend than this chat's leaf is a branch point, as is all above + const own: u32 = if (s.index.get(c.leaf).ram) 1 else 0; + for (members.items) |m| { + if (s.index.get(m).extra.job != null) continue; + if (s.index.extensions(m) > own) break; + if (pol.park_prefixes) _ = try s.evict(m) else try s.drop(m); + } + idle -= 1; + gone += 1; + s.stats.chat_evictions += 1; + } + return gone; + } + + /// RAM entries to let go of (coldest first, never keep) so need pages are free: [] if they are, null if impossible. + pub fn planSpill(s: *Store, need: u32, keep: []const u32, out: *std.ArrayList(u32)) !bool { + out.clearRetainingCapacity(); + var avail = s.pool.available(); + if (avail >= need) return true; + var left: std.AutoHashMapUnmanaged(u32, u16) = .empty; + defer left.deinit(s.gpa); + var id = s.oldest; + while (id != none and avail < need) : (id = s.index.get(id).extra.newer) { + if (std.mem.indexOfScalar(u32, keep, id) != null or s.index.get(id).extra.job != null) continue; + try out.append(s.gpa, id); + for (s.index.get(id).extra.pages) |pg| { + const g = try left.getOrPut(s.gpa, pg); + if (!g.found_existing) g.value_ptr.* = s.pool.refs[pg]; + g.value_ptr.* -= 1; + if (g.value_ptr.* == 0) avail += 1; // split: a page of the scarcer residue may count for less; the check below decides + } + } + return avail >= need; + } + + /// Rebuilds NVMe entries on restart (ranks then intersect them), deltas on their bases' paths; returns how many. + pub fn reconcile(s: *Store) !u32 { + const d = s.disk orelse return 0; + var found: std.ArrayList(Disk.Found) = .empty; + defer { + for (found.items) |f| s.gpa.free(f.ids); + found.deinit(s.gpa); + } + try d.scan(&found); + for (found.items) |f| { + var anchor: u32 = none; + if (f.head.hasBase()) { + const b = s.index.lookup(f.head.base) orelse return error.NoBase; + anchor = s.index.nodeAt(b, f.head.first_page) orelse return error.BadChain; + } + const r = try s.index.insertFrom(f.head.tag, anchor, f.ids, .{ .file = true, .kind = f.head.kind }); + s.clock += 1; + s.index.get(r.id).extra.used = s.clock; + } + return @intCast(found.items.len); + } + + /// Idle resident chats and the pool bytes held by entries alone. + pub fn resident(s: *Store) struct { chats: u32, bytes: u64 } { + var n: u32 = 0; + var id = s.oldest; + while (id != none) : (id = s.index.get(id).extra.newer) if (s.idleLeaf(id)) { + n += 1; + }; + return .{ .chats = n, .bytes = s.pool.entryOnlyBytes() }; + } +}; diff --git a/zig/src/sessions/store_test.zig b/zig/src/sessions/store_test.zig new file mode 100644 index 0000000000..06491799aa --- /dev/null +++ b/zig/src/sessions/store_test.zig @@ -0,0 +1,280 @@ +//! The session store on a host pool and a real NVMe tier: chats, evictions, resumes bit for bit, deltas, two ranks. + +const std = @import("std"); +const testing = std.testing; +const pool_mod = @import("pool.zig"); +const Pool = pool_mod.Pool; +const Slot = pool_mod.Slot; +const Family = pool_mod.Family; +const HostPool = @import("pagestore.zig").HostPool; +const disk_mod = @import("disk.zig"); +const store_mod = @import("store.zig"); +const Store = store_mod.Store; +const Bounded = store_mod.Bounded; + +const page = 64; +const fams = [_]Family{ + .{ .name = "comp.2", .ratio = 2, .row_bytes = 584, .split = true }, + .{ .name = "index_k.2", .ratio = 2, .row_bytes = 132 }, + .{ .name = "comp.20", .ratio = 1, .row_bytes = 584, .split = true }, +}; + +/// Bounded state in host memory: one blob naming its chat and length. +const HostBounded = struct { + gpa: std.mem.Allocator, + bytes: []u8, + + fn make(gpa: std.mem.Allocator, chat: u32, pos: u64) !Bounded { + const h = try gpa.create(HostBounded); + h.* = .{ .gpa = gpa, .bytes = try gpa.alloc(u8, 3000) }; + for (h.bytes, 0..) |*b, i| b.* = @truncate(i +% chat *% 17 +% pos); + return .{ .ptr = h, .vtable = &vt }; + } + const vt: Bounded.VTable = .{ .nbytes = nbytes, .blobs = blobs, .release = release }; + fn nbytes(p: *anyopaque) u64 { + const h: *HostBounded = @ptrCast(@alignCast(p)); + return h.bytes.len; + } + fn blobs(p: *anyopaque, gpa: std.mem.Allocator) anyerror![]disk_mod.Blob { + const h: *HostBounded = @ptrCast(@alignCast(p)); + const out = try gpa.alloc(disk_mod.Blob, 1); + out[0] = .{ .name = try gpa.dupe(u8, "rings"), .bytes = try gpa.dupe(u8, h.bytes) }; + return out; + } + fn release(p: *anyopaque) void { + const h: *HostBounded = @ptrCast(@alignCast(p)); + h.gpa.free(h.bytes); + h.gpa.destroy(h); + } +}; + +/// One rank: its pool, host pages and store, plus what each chat's slot wrote (the reference rows by logical page). +const Rank = struct { + pool: Pool, + host: HostPool, + disk: *disk_mod.Disk, + store: Store, + + fn init(r: *Rank, gpa: std.mem.Allocator, root: []const u8, world: u32, rank: u32, policy: store_mod.Policy) !void { + r.pool = try Pool.init(gpa, .{ .families = &fams, .page = page }, 96 * page, world, rank); + r.host = try HostPool.init(gpa, &r.pool); + r.disk = try disk_mod.Disk.open(gpa, testing.io, .{ .root = root, .compat = @splat(7), .rank = rank, .world = world, .chunk = 64 << 10, .staging = 4, .lanes = 2, .min_tokens = 1 }); + r.store = try Store.init(gpa, testing.io, &r.pool, r.host.store(), r.disk, policy); + } + + fn deinit(r: *Rank) void { + r.store.deinit(); + r.disk.close(); + r.host.deinit(); + r.pool.deinit(); + } + + /// The rows a chat's slot writes at a logical page: a function of (chat, logical page, family) alone. + fn write(r: *Rank, s: *const Slot, chat: u32, from_page: u32) void { + for (s.mapped()[from_page..], from_page..) |pg, k| for (fams, 0..) |f, fi| { + if (f.split and !r.pool.owns(pg)) continue; + const v = r.host.pageOf(@intCast(fi), r.pool.familyPage(f, pg)); + for (v, 0..) |*b, i| b.* = @truncate(i *% 13 +% k *% 101 +% chat *% 7 +% fi); + }; + } + + /// Every stored row of the slot's pages below pos equals what write() put there. + fn check(r: *Rank, s: *const Slot, chat: u32, pos: u64) !void { + const n = pool_mod.pagesFor(pos, page); + for (s.mapped()[0..n], 0..) |pg, k| for (fams, 0..) |f, fi| { + if (f.split and !r.pool.owns(pg)) continue; + const v = r.host.pageOf(@intCast(fi), r.pool.familyPage(f, pg)); + const rows: usize = if (k + 1 == n and pos % page != 0) (pos % page) / f.ratio * f.row_bytes else v.len; + for (v[0..rows], 0..) |b, i| try testing.expectEqual(@as(u8, @truncate(i *% 13 +% k *% 101 +% chat *% 7 +% fi)), b); + }; + } +}; + +fn chatIds(gpa: std.mem.Allocator, chat: u32, n: usize) ![]i32 { + const v = try gpa.alloc(i32, n); + for (v, 0..) |*t, i| t.* = @intCast(chat * 100_000 + i); + return v; +} + +/// Prefill or resume a chat to len(ids) in slot s, save it, free the slot, enforce residency; returns the resume point. +fn turn(gpa: std.mem.Allocator, r: *Rank, s: *Slot, chat: u32, ids: []const i32) ![]const u8 { + var tier: []const u8 = "fresh"; + var from: u32 = 0; + if (try r.store.find(1, ids)) |hit| { + if (hit.ram) { + const b = try r.store.restoreRam(hit.id, s); + try testing.expectEqual(@as(u64, 3000), b.vtable.nbytes(b.ptr)); + tier = "ram"; + } else { + const j = try r.store.beginRestore(hit.id, s); + var got = (try r.store.settle(j)).?; + defer got.deinit(gpa); + try testing.expectEqual(@as(u8, @truncate(chat *% 17 +% hit.pos)), got.blobs[0].bytes[0]); + tier = "disk"; + } + try r.check(s, chat, hit.pos); + from = @intCast(hit.pos / page); + } + try s.ensure(ids.len); + r.write(s, chat, from); + _ = try r.store.save(s, try HostBounded.make(gpa, chat, ids.len), ids, 1, 0); + _ = try s.releaseAll(); + _ = try r.store.enforce(); + try r.store.settleAll(); // the plan's settle point (async parks finish here) + return tier; +} + +/// The scenario on one rank; returns a trace of its decisions (stats and page ids) to compare ranks. +fn scenario(gpa: std.mem.Allocator, root: []const u8, world: u32, rank: u32, async_io: bool, trace: *std.ArrayList(u64)) !void { + var r: Rank = undefined; + try r.init(gpa, root, world, rank, .{ .chats = 1, .async_io = async_io }); + defer r.deinit(); + const s = try r.pool.newSlot(64 * page); + const a = try chatIds(gpa, 1, 20 * page); + defer gpa.free(a); + const b = try chatIds(gpa, 2, 20 * page); + defer gpa.free(b); + const st = &r.store.stats; + try testing.expectEqualStrings("fresh", try turn(gpa, &r, s, 1, a[0 .. 5 * page + 10])); + try testing.expectEqualStrings("ram", try turn(gpa, &r, s, 1, a[0 .. 8 * page + 3])); + try testing.expectEqual(@as(u64, 0), st.parks); + // a second idle chat: chat 1 (least recent) parked whole, its older turn dropped (not written) + try testing.expectEqualStrings("fresh", try turn(gpa, &r, s, 2, b[0 .. 6 * page])); + try testing.expectEqual(@as(u64, 1), st.parks); + try testing.expectEqual(@as(u64, 1), st.drops); + try testing.expectEqual(@as(u32, 1), r.store.resident().chats); + const whole = r.disk.used; + // chat 1 resumes from NVMe bit for bit; chat 2 goes + try testing.expectEqualStrings("disk", try turn(gpa, &r, s, 1, a[0 .. 12 * page + 40])); + try testing.expectEqual(@as(u64, 2), st.parks); + const before = r.disk.used; + // chat 2 resumes; chat 1 goes again, written as a delta on its first file (pages 8..12 of 13) + try testing.expectEqualStrings("disk", try turn(gpa, &r, s, 2, b[0 .. 9 * page])); + try testing.expectEqual(@as(u64, 3), st.parks); + try testing.expectEqual(@as(u64, 1), st.delta_parks); + // the delta holds 5 of the entry's 13 pages: less than chat 1's first whole file of 9 pages + try testing.expect(r.disk.used - before < whole); + // chat 1's newest turn restores through its two files, bit for bit + try testing.expectEqualStrings("disk", try turn(gpa, &r, s, 1, a[0 .. 15 * page])); + try testing.expectEqual(@as(u64, 3), st.hits_disk); + try trace.appendSlice(gpa, &.{ st.parks, st.delta_parks, st.drops, st.covered_drops, st.hits_ram, st.hits_disk, r.pool.freePages(), r.pool.entry_only }); + var id = r.store.oldest; + while (id != store_mod.none) : (id = r.store.entry(id).extra.newer) for (r.store.entry(id).extra.pages) |pg| try trace.append(gpa, pg); +} + +/// After the scenario: a new process on the same tier finds every file and resumes chat 1 through its delta chain. +fn restart(gpa: std.mem.Allocator, root: []const u8, world: u32, rank: u32) !void { + var r: Rank = undefined; + try r.init(gpa, root, world, rank, .{ .chats = 1 }); + defer r.deinit(); + try testing.expectEqual(@as(u32, 4), try r.store.reconcile()); + const s = try r.pool.newSlot(64 * page); + const a = try chatIds(gpa, 1, 20 * page); + defer gpa.free(a); + try testing.expectEqualStrings("disk", try turn(gpa, &r, s, 1, a[0 .. 16 * page])); + try testing.expectEqual(@as(u64, 1), r.store.stats.hits_disk); +} + +fn tmpRoot(gpa: std.mem.Allocator, tmp: *std.testing.TmpDir, sub: []const u8) ![:0]u8 { + try tmp.dir.createDirPath(testing.io, sub); + return tmp.dir.realPathFileAlloc(testing.io, sub, gpa); +} + +test "chats over turns: residency to NVMe, bit-identical resumes, deltas, older turns dropped (sync and async)" { + const gpa = testing.allocator; + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + var t0: std.ArrayList(u64) = .empty; + defer t0.deinit(gpa); + var t1: std.ArrayList(u64) = .empty; + defer t1.deinit(gpa); + const r0 = try tmpRoot(gpa, &tmp, "sync"); + defer gpa.free(r0); + try scenario(gpa, r0, 1, 0, false, &t0); + try restart(gpa, r0, 1, 0); + const r1 = try tmpRoot(gpa, &tmp, "async"); + defer gpa.free(r1); + try scenario(gpa, r1, 1, 0, true, &t1); + try testing.expectEqualSlices(u64, t0.items, t1.items); // the tier thread changes when bytes move, not what is decided +} + +test "two split ranks make the same decisions on the same calls and each restores its own rows" { + const gpa = testing.allocator; + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + var t0: std.ArrayList(u64) = .empty; + defer t0.deinit(gpa); + var t1: std.ArrayList(u64) = .empty; + defer t1.deinit(gpa); + const root = try tmpRoot(gpa, &tmp, "tier"); + defer gpa.free(root); + try scenario(gpa, root, 2, 0, false, &t0); + try scenario(gpa, root, 2, 1, true, &t1); + try restart(gpa, root, 2, 0); + try restart(gpa, root, 2, 1); + try testing.expectEqualSlices(u64, t0.items, t1.items); +} + +test "historyOf: a RAM entry's ids come from the index, an NVMe entry's only from the prompt that names it" { + const gpa = testing.allocator; + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + const root = try tmpRoot(gpa, &tmp, "history"); + defer gpa.free(root); + var r: Rank = undefined; + try r.init(gpa, root, 1, 0, .{ .chats = 1 }); + defer r.deinit(); + const s = try r.pool.newSlot(64 * page); + const a = try chatIds(gpa, 1, 8 * page); + defer gpa.free(a); + const b = try chatIds(gpa, 2, 8 * page); + defer gpa.free(b); + const n = 5 * page + 10; + try testing.expectEqualStrings("fresh", try turn(gpa, &r, s, 1, a[0..n])); + const hit = (try r.store.find(1, a[0 .. n + 1])).?; + try testing.expect(hit.ram and hit.pos == n); + var out: [n]i32 = undefined; + try r.store.historyOf(hit.id, null, &out); + try testing.expectEqualSlices(i32, a[0..n], &out); + try testing.expectError(error.BadLength, r.store.historyOf(hit.id, null, out[0 .. n - 1])); + // a second idle chat parks chat 1 on NVMe: its ids leave the index with its RAM + try testing.expectEqualStrings("fresh", try turn(gpa, &r, s, 2, b[0 .. 6 * page])); + const parked = (try r.store.find(1, a[0 .. n + 1])).?; + try testing.expect(!parked.ram and parked.pos == n); + try testing.expectError(error.NotInRam, r.store.historyOf(parked.id, null, &out)); + @memset(&out, 0); + try r.store.historyOf(parked.id, a, &out); // a longer prompt: its first pos ids, checked against the entry's key + try testing.expectEqualSlices(i32, a[0..n], &out); + // another chat's ids, or these with the partial page's last id changed, are not this entry + try testing.expectError(error.NotTheEntry, r.store.historyOf(parked.id, b[0..n], &out)); + const edited = try gpa.dupe(i32, a[0..n]); + defer gpa.free(edited); + edited[n - 1] += 1; + try testing.expectError(error.NotTheEntry, r.store.historyOf(parked.id, edited, &out)); + try testing.expectError(error.BadLength, r.store.historyOf(parked.id, a[0 .. n - 1], &out)); +} + +test "zero chat or byte budgets evict idle residency while null remains unlimited" { + const gpa = testing.allocator; + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + const root = try tmpRoot(gpa, &tmp, "zero-budget"); + defer gpa.free(root); + var r: Rank = undefined; + try r.init(gpa, root, 1, 0, .{}); + defer r.deinit(); + const slot = try r.pool.newSlot(64 * page); + const ids = try chatIds(gpa, 1, 5 * page); + defer gpa.free(ids); + _ = try turn(gpa, &r, slot, 1, ids); + try testing.expectEqual(@as(u32, 1), r.store.resident().chats); + r.store.policy.chats = 0; + try testing.expectEqual(@as(u32, 1), try r.store.enforce()); + try testing.expectEqual(@as(u32, 0), r.store.resident().chats); + r.store.policy.chats = null; + _ = try turn(gpa, &r, slot, 1, ids); + try testing.expect(r.store.resident().bytes > 0); + r.store.policy.resident_bytes = 0; + try testing.expectEqual(@as(u32, 1), try r.store.enforce()); + try testing.expectEqual(@as(u64, 0), r.store.resident().bytes); +} diff --git a/zig/tests/cuda/deepseek_v41/experts.zig b/zig/tests/cuda/deepseek_v41/experts.zig new file mode 100644 index 0000000000..7996eb20b8 --- /dev/null +++ b/zig/tests/cuda/deepseek_v41/experts.zig @@ -0,0 +1,472 @@ +//! Bit checks of the routed EXL3 expert kernels (decode chain, x3ld / x3pf settings, x3gm) against the oracle. + +const std = @import("std"); +const cuda = @import("cuda"); +const dsv41 = @import("dsv41_kernels"); +const check = @import("../check.zig"); +const Fixture = @import("../fixture.zig").Fixture; +const experts_cases = @import("experts_cases.zig"); + +const exl3 = dsv41.exl3; +const Gpu = check.Gpu; + +pub const Kind = struct { + buf: [32]u8 = undefined, + len: usize = 0, + pub fn slice(k: *const Kind) []const u8 { + return k.buf[0..k.len]; + } +}; + +/// The case's "kind" param (which check reads it). +pub fn caseKind(gpu: Gpu, dir: []const u8) !Kind { + var fx = try Fixture.open(gpu.gpa, gpu.io, dir); + defer fx.deinit(); + const s = try fx.string("kind"); + var k: Kind = .{}; + if (s.len > k.buf.len) return error.WrongType; + @memcpy(k.buf[0..s.len], s); + k.len = s.len; + return k; +} + +/// Device copies of a fixture's arrays, freed together. +pub const Bufs = struct { + gpu: Gpu, + fx: *const Fixture, + list: std.ArrayList(cuda.DeviceBuffer) = .empty, + + pub fn deinit(b: *Bufs) void { + for (b.list.items) |*x| x.free(); + b.list.deinit(b.gpu.gpa); + } + + pub fn up(b: *Bufs, name: []const u8) !cuda.DeviceBuffer { + const host = try b.fx.bytes(name); + defer b.gpu.gpa.free(host); + const buf = try cuda.DeviceBuffer.fromHost(b.gpu.d, host); + try b.list.append(b.gpu.gpa, buf); + return buf; + } + + pub fn zeros(b: *Bufs, len: usize, byte: u8) !cuda.DeviceBuffer { + const buf = try cuda.DeviceBuffer.alloc(b.gpu.d, @max(len, 4)); + try buf.fill8(byte, null); + try b.list.append(b.gpu.gpa, buf); + return buf; + } + + /// int64 [E] pointer table: `base` + the fixture's byte offsets `name`. + fn table(b: *Bufs, base: cuda.DeviceBuffer, name: []const u8) !cuda.DeviceBuffer { + const host = try b.fx.bytes(name); + defer b.gpu.gpa.free(host); + const offs = std.mem.bytesAsSlice(u64, host); + const ptrs = try b.gpu.gpa.alloc(u64, offs.len); + defer b.gpu.gpa.free(ptrs); + for (offs, ptrs) |o, *p| p.* = base.ptr + o; + const buf = try cuda.DeviceBuffer.fromHost(b.gpu.d, std.mem.sliceAsBytes(ptrs)); + try b.list.append(b.gpu.gpa, buf); + return buf; + } +}; + +pub fn size(fx: Fixture, name: []const u8) !usize { + return @intCast(try fx.int(name)); +} + +/// `got` (the first `want.len` bytes of the device buffer) against the fixture's array `name`. +pub fn same(gpu: Gpu, fx: Fixture, buf: cuda.DeviceBuffer, name: []const u8, what: []const u8) !void { + const want = try fx.bytes(name); + defer gpu.gpa.free(want); + const got = try gpu.gpa.alloc(u8, want.len); + defer gpu.gpa.free(got); + try buf.download(0, got); + try check.sameBytes(what, got, want); +} + +fn has(fx: Fixture, name: []const u8) bool { + const arrays = fx.parsed.value.object.get("arrays") orelse return false; + return arrays.object.get(name) != null; +} + +/// The decode chain of one routed layer at R rows, every stage against the reference output of the same stage. +pub fn decodeChain(gpu: Gpu, k: *const dsv41.Kernels, dir: []const u8) !void { + var fx = try Fixture.open(gpu.gpa, gpu.io, dir); + defer fx.deinit(); + var b: Bufs = .{ .gpu = gpu, .fx = &fx }; + defer b.deinit(); + var stream = try cuda.Stream.init(gpu.d, false); // blocking: ordered after the uploads and memsets on the default stream + defer stream.deinit(); + const o = k.experts(stream); + + const R = try size(fx, "R"); + const slots = try size(fx, "slots"); + const E = try size(fx, "E"); + const D = try size(fx, "D"); + const I = try size(fx, "I"); + const P = R * slots; + const maxu = @min(P, E); + const input: exl3.Input = if (std.mem.eql(u8, try fx.string("input"), "bf16")) .bf16 else .f16; + const limit: f32 = if ((try fx.int("limit_inf")) != 0) std.math.inf(f32) else @floatCast(try fx.float("limit")); + const act_mode: c_int = @intCast(try fx.int("act_mode")); + + const x = try b.up("x"); + const pick = try b.up("pick"); + const wts = try b.up("wts"); + const words = try b.up("words"); + const tg = try b.table(words, "off_g"); + const tu = try b.table(words, "off_u"); + const td = try b.table(words, "off_d"); + const k2g = try b.up("k2_g"); + const k2u = try b.up("k2_u"); + const k2d = try b.up("k2_d"); + const suh_g = try b.up("suh_g"); + const suh_u = try b.up("suh_u"); + const svh_g = try b.up("svh_g"); + const svh_u = try b.up("svh_u"); + const suh_d = try b.up("suh_d"); + const svh_d = try b.up("svh_d"); + + const uids = try b.zeros(maxu * 4, 0); + const ucount = try b.zeros(4, 0); + const members = try b.zeros(maxu * R * 4, 0xff); + try o.group(pick.ptr, uids.ptr, ucount.ptr, members.ptr, R, slots, E, R); + try stream.synchronize(); + try same(gpu, fx, uids, "uids", "group uids"); + try same(gpu, fx, ucount, "ucount", "group ucount"); + try same(gpu, fx, members, "members", "group members"); + + const xg = try b.zeros(P * D * 2, 0); + const xu = try b.zeros(P * D * 2, 0); + try o.rotIn(input, x.ptr, D, pick.ptr, suh_g.ptr, suh_u.ptr, xg.ptr, xu.ptr, R, D, slots, E); + try stream.synchronize(); + try same(gpu, fx, xg, "xg", "rot_in gate"); + try same(gpu, fx, xu, "xu", "rot_in up"); + + // Exercise the actual Zig ABI against the same fixture, including untouched invalid-pick rows. + if (input == .bf16 and R <= 64 and D == 5120 and slots <= 9 and E <= 512) { + const fg = try b.zeros(P * D * 2, 0); + const fu = try b.zeros(P * D * 2, 0); + const fi = try b.zeros(maxu * 4, 0); + const fc = try b.zeros(4, 0); + const fm = try b.zeros(maxu * R * 4, 0xff); + try o.groupRotIn(x.ptr, D, pick.ptr, suh_g.ptr, suh_u.ptr, fg.ptr, fu.ptr, fi.ptr, fc.ptr, fm.ptr, R, D, slots, E, R); + try stream.synchronize(); + try same(gpu, fx, fi, "uids", "group_rot ids"); + try same(gpu, fx, fc, "ucount", "group_rot count"); + try same(gpu, fx, fm, "members", "group_rot members"); + try same(gpu, fx, fg, "xg", "group_rot gate"); + try same(gpu, fx, fu, "xu", "group_rot up"); + } + + const lo_gu: u32 = @intCast(try fx.int("lo_gu")); + const hi_gu: u32 = @intCast(try fx.int("hi_gu")); + const lo_d: u32 = @intCast(try fx.int("lo_d")); + const hi_d: u32 = @intCast(try fx.int("hi_d")); + const sk_gu = try size(fx, "sk_gu"); + const sk_d = try size(fx, "sk_d"); + const gu: exl3.Grouped = .{ .x0 = xg.ptr, .x1 = xu.ptr, .tp0 = tg.ptr, .tp1 = tu.ptr, .k2_0 = k2g.ptr, .k2_1 = k2u.ptr, .uids = uids.ptr, .ucount = ucount.ptr, .members = members.ptr, .z = 0, .mats = 2, .K = D, .N = I, .P = P, .SK = sk_gu, .slots = slots, .maxm = R, .nexp = maxu, .lo = lo_gu, .hi = hi_gu }; + const z = try b.zeros(@max(2 * sk_gu * P * I, sk_d * P * D) * 4, 0); + const zlen_gu = 2 * sk_gu * P * I * 4; + const zlen_d = sk_d * P * D * 4; + + // upstream's grouped kernel at the shape's default setting: the Z the epilogue reads + var g = gu; + g.z = z.ptr; + const ucfg_gu = [3]u32{ @intCast(try fx.int("nt_gu")), @intCast(try fx.int("w_gu")), @intCast(try fx.int("pf_gu")) }; + try zeroThen(z, zlen_gu); + try o.grouped(g, ucfg_gu[0], ucfg_gu[1], ucfg_gu[2]); + try stream.synchronize(); + try same(gpu, fx, z, "z_gu", "upstream grouped gate/up Z"); + var n_ld: usize = 0; + var n_pf: usize = 0; + try settings(gpu, fx, o, stream, z, g, zlen_gu, "gu", &n_ld, &n_pf); + + // the gate/up epilogue on upstream's Z (re-run: the settings above overwrote it with the same bits) + try zeroThen(z, zlen_gu); + try o.grouped(g, ucfg_gu[0], ucfg_gu[1], ucfg_gu[2]); + const xd = try b.zeros(P * I * 2, 0); + try o.gateupEpilogue(z.ptr, pick.ptr, svh_g.ptr, svh_u.ptr, suh_d.ptr, xd.ptr, R, P, I, sk_gu, slots, E, limit, act_mode); + try stream.synchronize(); + try same(gpu, fx, xd, "xd", "gate/up epilogue Xd"); + + var dn: exl3.Grouped = .{ .x0 = xd.ptr, .x1 = xd.ptr, .tp0 = td.ptr, .tp1 = td.ptr, .k2_0 = k2d.ptr, .k2_1 = k2d.ptr, .uids = uids.ptr, .ucount = ucount.ptr, .members = members.ptr, .z = z.ptr, .mats = 1, .K = I, .N = D, .P = P, .SK = sk_d, .slots = slots, .maxm = R, .nexp = maxu, .lo = lo_d, .hi = hi_d }; + const ucfg_d = [3]u32{ @intCast(try fx.int("nt_d")), @intCast(try fx.int("w_d")), @intCast(try fx.int("pf_d")) }; + try zeroThen(z, zlen_d); + try o.grouped(dn, ucfg_d[0], ucfg_d[1], ucfg_d[2]); + try stream.synchronize(); + try same(gpu, fx, z, "z_d", "upstream grouped down Z"); + try settings(gpu, fx, o, stream, z, dn, zlen_d, "d", &n_ld, &n_pf); + + try zeroThen(z, zlen_d); + try o.grouped(dn, ucfg_d[0], ucfg_d[1], ucfg_d[2]); + const y = try b.zeros(P * D * 4, 0); + try o.downEpilogue(z.ptr, pick.ptr, svh_d.ptr, y.ptr, R, P, D, sk_d, slots, E); + try stream.synchronize(); + try same(gpu, fx, y, "y", "down epilogue Y"); + const out = try b.zeros(R * D * 4, 0); + try o.combine(y.ptr, wts.ptr, out.ptr, R, D, slots); + try stream.synchronize(); + try same(gpu, fx, out, "out", "combine"); + try y.fill8(0, null); + try out.fill8(0, null); + try o.downCombine(z.ptr, pick.ptr, svh_d.ptr, y.ptr, wts.ptr, out.ptr, R, P, D, sk_d, slots, E, false); + try stream.synchronize(); + try same(gpu, fx, out, "out", "down_combine"); + dn.z = 0; + check.pass("BITEXACT experts {s}: R {d} x {d} slots, E {d}, D {d}, I {d}, K2 gu [{d},{d}] d [{d},{d}]: group, rot_in, grouped, {d} x3ld and {d} x3pf settings, epilogues, combine", .{ std.fs.path.basename(dir), R, slots, E, D, I, lo_gu, hi_gu, lo_d, hi_d, n_ld, n_pf }); +} + +/// The reference zeroes the whole Z before each launch: rows a launch does not write stay 0 on both sides. +fn zeroThen(z: cuda.DeviceBuffer, len: usize) !void { + _ = len; + try z.fill8(0, null); +} + +/// Every x3ld setting (and PDL) and every x3pf setting that fits this matrix: Z against the reference of that setting. +fn settings(gpu: Gpu, fx: Fixture, o: exl3.Ops, stream: cuda.Stream, z: cuda.DeviceBuffer, g: exl3.Grouped, zlen: usize, tag: []const u8, n_ld: *usize, n_pf: *usize) !void { + var name_buf: [64]u8 = undefined; + for (exl3.ld_cfgs) |c| for ([_]u32{ 0, 3 }) |probe| { + _ = exl3.x3ldCheck(g, c[0], c[1], probe) catch continue; + if (probe == 3) continue; // timing only: wrong results by design + for ([_]bool{ false, true }) |pdl| { + const name = try std.fmt.bufPrint(&name_buf, "z_{s}_ld_{d}_{d}", .{ tag, c[0], c[1] }); + try zeroThen(z, zlen); + try o.x3ld(g, c[0], c[1], 0, pdl); + try stream.synchronize(); + if (!has(fx, name)) return error.FixtureMissingSetting; + try same(gpu, fx, z, name, name); + n_ld.* += 1; + } + }; + for (exl3.pf_cfgs) |c| { + _ = exl3.x3pfCheck(g, c[0], c[1]) catch continue; + const name = try std.fmt.bufPrint(&name_buf, "z_{s}_pf_{d}_{d}", .{ tag, c[0], c[1] }); + if (!has(fx, name)) continue; // the oracle runs x3pf at prefill row counts only + try zeroThen(z, zlen); + try o.x3pf(g, c[0], c[1]); + try stream.synchronize(); + try same(gpu, fx, z, name, name); + n_pf.* += 1; + } +} + +/// x3gm over one row block (rot, gate/up and down launches per width, combine): every oracle configuration checked. +pub fn x3gm(gpu: Gpu, k: *const dsv41.Kernels, dir: []const u8) !void { + var fx = try Fixture.open(gpu.gpa, gpu.io, dir); + defer fx.deinit(); + var b: Bufs = .{ .gpu = gpu, .fx = &fx }; + defer b.deinit(); + var stream = try cuda.Stream.init(gpu.d, false); // blocking: ordered after the uploads and memsets on the default stream + defer stream.deinit(); + const o = k.experts(stream); + + const R = try size(fx, "R"); + const slots = try size(fx, "slots"); + const D = try size(fx, "D"); + const I = try size(fx, "I"); + const P = R * slots; + const shx = (try fx.int("shx")) != 0; + const mats_x: usize = if (shx) 1 else 2; + const ragged = (try fx.int("ragged")) != 0; + const limit: f32 = @floatCast(try fx.float("limit")); + const input: exl3.Input = if (std.mem.eql(u8, try fx.string("input"), "bf16")) .bf16 else .f16; + + const x = try b.up("x"); + const pick = try b.up("pick"); + const wts = try b.up("wts"); + const words = try b.up("words"); + const suh_g = try b.up("suh_g"); + const suh_u = try b.up("suh_u"); + const svh_g = try b.up("svh_g"); + const svh_u = try b.up("svh_u"); + const suh_d = try b.up("suh_d"); + const svh_d = try b.up("svh_d"); + // the stacked form: base + 0 / base + gate bytes / ...; the ragged form: pointer tables + const tg = if (ragged) try b.table(words, "off_g") else words; + const tu = if (ragged) try b.table(words, "off_u") else words; + const td = if (ragged) try b.table(words, "off_d") else words; + const base_u: u64 = if (ragged) 0 else @intCast(try fx.int("base_u")); + const base_d: u64 = if (ragged) 0 else @intCast(try fx.int("base_d")); + + const xs = try b.zeros(mats_x * P * D * 2, 0); + const xg = xs.ptr; + const xu = xs.ptr + (mats_x - 1) * P * D * 2; + try o.gmRot(input, x.ptr, D, pick.ptr, suh_g.ptr, suh_u.ptr, xg, xu, D, slots, P, mats_x); + try stream.synchronize(); + try same(gpu, fx, xs, "xs", "x3gm rot"); + + const xd = try b.zeros(P * I * 2, 0); + const y = try b.zeros(P * D * 4, 0); + const ticket = try b.zeros(4, 0); + const out = try b.zeros(R * D * 4, 0); + var name_buf: [96]u8 = undefined; + const runs = try fx.int("runs"); + var launches: usize = 0; + for (0..@intCast(runs)) |r| { + // run r: a configuration per (kind, width) run__ and its plan run___ + const tk = (try fx.int(try std.fmt.bufPrint(&name_buf, "run{d}_ticket", .{r}))) != 0; + try xd.fill8(0, null); + try y.fill8(0, null); + for (exl3.gm_k2s) |k2| { + const pl = (try plan(&b, fx, r, "gu", k2)) orelse continue; + const cg: usize = @intCast(try fx.int(try std.fmt.bufPrint(&name_buf, "run{d}_gu_{d}", .{ r, k2 }))); + try nativePlan(&b, fx, o, stream, r, "gu", k2, P, cg); + try ticket.fill8(0, null); + try o.gmGateup(xg, xu, tg.ptr, tu.ptr + base_u, pl[0], pl[1], pl[2], pl[3], pl[4], ticket.ptr, svh_g.ptr, svh_u.ptr, suh_d.ptr, xd.ptr, D, I, k2, shx, cg, tk, limit, ragged); + launches += 1; + } + try stream.synchronize(); + try same(gpu, fx, xd, try std.fmt.bufPrint(&name_buf, "run{d}_xd", .{r}), "x3gm gate/up Xd"); + for (exl3.gm_k2s) |k2| { + const pl = (try plan(&b, fx, r, "dn", k2)) orelse continue; + const cd: usize = @intCast(try fx.int(try std.fmt.bufPrint(&name_buf, "run{d}_dn_{d}", .{ r, k2 }))); + try nativePlan(&b, fx, o, stream, r, "dn", k2, P, cd); + try ticket.fill8(0, null); + try o.gmDown(xd.ptr, td.ptr + base_d, pl[0], pl[1], pl[2], pl[3], pl[4], ticket.ptr, svh_d.ptr, y.ptr, I, D, k2, cd, tk, ragged); + launches += 1; + } + try stream.synchronize(); + try same(gpu, fx, y, try std.fmt.bufPrint(&name_buf, "run{d}_y", .{r}), "x3gm down Y"); + try o.combine(y.ptr, wts.ptr, out.ptr, R, D, slots); + try stream.synchronize(); + try same(gpu, fx, out, try std.fmt.bufPrint(&name_buf, "run{d}_out", .{r}), "x3gm combine"); + } + var v2_launches: usize = 0; + if (has(fx, "v2_xd") or has(fx, "v2_t1_xd")) { + // x3gm v2: one plan over every pair, one gate/up and one down launch over every width + const k2g = try b.up("v2_k2g"); + const k2d = try b.up("v2_k2d"); + const pl = [_]u64{ (try b.up("v2_order")).ptr, (try b.up("v2_pe")).ptr, (try b.up("v2_poff")).ptr, (try b.up("v2_pcnt")).ptr, (try b.up("v2_npass")).ptr }; + const cg: usize = @intCast(try fx.int("v2_gu")); + const cd: usize = @intCast(try fx.int("v2_dn")); + for ([_]bool{ true, false }) |tk| { + try xd.fill8(0, null); + try y.fill8(0, null); + try ticket.fill8(0, null); + try o.gm2Gateup(xg, xu, tg.ptr, tu.ptr + base_u, k2g.ptr, pl[0], pl[1], pl[2], pl[3], pl[4], ticket.ptr, svh_g.ptr, svh_u.ptr, suh_d.ptr, xd.ptr, D, I, shx, cg, tk, limit, ragged); + try ticket.fill8(0, null); + try o.gm2Down(xd.ptr, td.ptr + base_d, k2d.ptr, pl[0], pl[1], pl[2], pl[3], pl[4], ticket.ptr, svh_d.ptr, y.ptr, I, D, cd, tk, ragged); + try o.combine(y.ptr, wts.ptr, out.ptr, R, D, slots); + try stream.synchronize(); + const t: u8 = @intFromBool(tk); + try same(gpu, fx, xd, try std.fmt.bufPrint(&name_buf, "v2_t{d}_xd", .{t}), "x3gm v2 gate/up Xd"); + try same(gpu, fx, y, try std.fmt.bufPrint(&name_buf, "v2_t{d}_y", .{t}), "x3gm v2 down Y"); + try same(gpu, fx, out, try std.fmt.bufPrint(&name_buf, "v2_t{d}_out", .{t}), "x3gm v2 combine"); + v2_launches += 2; + } + } + if (v2_launches == 0) { + // No v2 arrays in the fixture: gm2 against gm_kernel outputs (run 0, ticket on), same bits by v2's contract. + const E = try size(fx, "E"); + const T = exl3.planPasses(P, E, 64); + const order = try b.zeros(P * 4, 0); + const pe = try b.zeros(T * 4, 0); + const poff = try b.zeros(T * 4, 0); + const pcnt = try b.zeros(T * 4, 0); + const npass = try b.zeros(4, 0); + try o.gmPlan(pick.ptr, P, E, 64, order.ptr, pe.ptr, poff.ptr, pcnt.ptr, npass.ptr); + const k2g = try b.up("k2_g"); + const k2d = try b.up("k2_d"); + const cfg = exl3.gmTuned2(null, null, shx); + for ([_]bool{ true, false }) |tk| { + try xd.fill8(0, null); + try y.fill8(0, null); + try ticket.fill8(0, null); + try o.gm2Gateup(xg, xu, tg.ptr, tu.ptr + base_u, k2g.ptr, order.ptr, pe.ptr, poff.ptr, pcnt.ptr, npass.ptr, ticket.ptr, svh_g.ptr, svh_u.ptr, suh_d.ptr, xd.ptr, D, I, shx, cfg[0], tk, limit, ragged); + try ticket.fill8(0, null); + try o.gm2Down(xd.ptr, td.ptr + base_d, k2d.ptr, order.ptr, pe.ptr, poff.ptr, pcnt.ptr, npass.ptr, ticket.ptr, svh_d.ptr, y.ptr, I, D, cfg[1], tk, ragged); + try o.combine(y.ptr, wts.ptr, out.ptr, R, D, slots); + try stream.synchronize(); + try same(gpu, fx, xd, "run0_xd", "x3gm v2 Xd vs gm_kernel"); + try same(gpu, fx, y, "run0_y", "x3gm v2 Y vs gm_kernel"); + try same(gpu, fx, out, "run0_out", "x3gm v2 combine vs gm_kernel"); + v2_launches += 2; + } + } + // x3gm v3 (gm3_kernel): every variant against gm_kernel outputs (run 0), both ticket modes, one plan (bm 64). + var v3_launches: usize = 0; + { + const E = try size(fx, "E"); + const T = exl3.planPasses(P, E, 64); + const order = try b.zeros(P * 4, 0); + const pe = try b.zeros(T * 4, 0); + const poff = try b.zeros(T * 4, 0); + const pcnt = try b.zeros(T * 4, 0); + const npass = try b.zeros(4, 0); + try o.gmPlan(pick.ptr, P, E, 64, order.ptr, pe.ptr, poff.ptr, pcnt.ptr, npass.ptr); + const k2g = try b.up("k2_g"); + const k2d = try b.up("k2_d"); + const xd_want = try b.up("run0_xd"); + var vb: [exl3.v3_variants.len]usize = undefined; + for (exl3.v3Of(2, @intCast(mats_x), &vb)) |v| for ([_]bool{ true, false }) |tk| { + try xd.fill8(0, null); + try ticket.fill8(0, null); + try o.gm3Gateup(v, xg, xu, tg.ptr, tu.ptr + base_u, k2g.ptr, order.ptr, pe.ptr, poff.ptr, pcnt.ptr, npass.ptr, ticket.ptr, svh_g.ptr, svh_u.ptr, suh_d.ptr, xd.ptr, D, I, shx, tk, limit, ragged); + try stream.synchronize(); + try same(gpu, fx, xd, "run0_xd", try std.fmt.bufPrint(&name_buf, "x3gm v3 gate/up variant {d} Xd vs gm_kernel", .{v})); + v3_launches += 1; + }; + for (exl3.v3Of(1, 1, &vb)) |v| for ([_]bool{ true, false }) |tk| { + try y.fill8(0, null); + try ticket.fill8(0, null); + try o.gm3Down(v, xd_want.ptr, td.ptr + base_d, k2d.ptr, order.ptr, pe.ptr, poff.ptr, pcnt.ptr, npass.ptr, ticket.ptr, svh_d.ptr, y.ptr, I, D, tk, ragged); + try stream.synchronize(); + try same(gpu, fx, y, "run0_y", try std.fmt.bufPrint(&name_buf, "x3gm v3 down variant {d} Y vs gm_kernel", .{v})); + v3_launches += 1; + }; + } + if (has(fx, "dequant_in")) { + const t = try b.up("dequant_in"); + const dk: usize = @intCast(try fx.int("dequant_k")); + const dn: usize = @intCast(try fx.int("dequant_n")); + const dk2: u32 = @intCast(try fx.int("dequant_k2")); + const dq = try b.zeros(dk * dn * 2, 0); + try o.gmDequant(t.ptr, dq.ptr, dk, dn, dk2); + try stream.synchronize(); + try same(gpu, fx, dq, "dequant_gm", "x3gm dequant"); + try dq.fill8(0, null); + try o.dequant(t.ptr, dq.ptr, dk, dn, dk2); + try stream.synchronize(); + try same(gpu, fx, dq, "dequant_up", "upstream dequant"); + } + check.pass("BITEXACT x3gm {s}: R {d} x {d} slots, D {d}, I {d}, {s}, shx {}: rot, {d} runs ({d} launches), v2 {d} launches, v3 {d} launches, combine", .{ std.fs.path.basename(dir), R, slots, D, I, if (ragged) "ragged" else "stacked", shx, runs, launches, v2_launches, v3_launches }); +} + +pub const pointwiseCases = experts_cases.pointwiseCases; +pub const glueCases = experts_cases.glueCases; +pub const pfglueCases = experts_cases.pfglueCases; +pub const topkCases = experts_cases.topkCases; +pub const planCases = experts_cases.planCases; + +/// x3gm_plan.cu on the plan's input picks against torch's x3gm.plan (the arrays the launches then use). +fn nativePlan(b: *Bufs, fx: Fixture, o: exl3.Ops, stream: cuda.Stream, r: usize, kind: []const u8, k2: u32, P: usize, cfg: usize) !void { + var name_buf: [64]u8 = undefined; + const pk = try b.up(try std.fmt.bufPrint(&name_buf, "run{d}_{s}_{d}_pk", .{ r, kind, k2 })); + const E = try size(fx, "E"); + const bm: usize = exl3.gmMembers(if (kind[0] == 'g') .gu else .dn, cfg); + const T = exl3.planPasses(P, E, bm); + const order = try b.zeros(P * 4, 0xee); + const pe = try b.zeros(T * 4, 0xee); + const poff = try b.zeros(T * 4, 0xee); + const pcnt = try b.zeros(T * 4, 0xee); + const npass = try b.zeros(4, 0xee); + try o.gmPlan(pk.ptr, P, E, bm, order.ptr, pe.ptr, poff.ptr, pcnt.ptr, npass.ptr); + try stream.synchronize(); + for ([_][]const u8{ "order", "pe", "poff", "pcnt", "npass" }, [_]cuda.DeviceBuffer{ order, pe, poff, pcnt, npass }) |part, buf| { + const name = try std.fmt.bufPrint(&name_buf, "run{d}_{s}_{d}_{s}", .{ r, kind, k2, part }); + try same(b.gpu, fx, buf, name, "x3gm_plan.cu vs x3gm.plan"); + } +} + +/// The plan arrays of (run, kind, width), uploaded; null when that width has no launch in the run. +fn plan(b: *Bufs, fx: Fixture, r: usize, kind: []const u8, k2: u32) !?[5]u64 { + var out: [5]u64 = undefined; + var name_buf: [64]u8 = undefined; + for ([_][]const u8{ "order", "pe", "poff", "pcnt", "npass" }, 0..) |part, i| { + const name = try std.fmt.bufPrint(&name_buf, "run{d}_{s}_{d}_{s}", .{ r, kind, k2, part }); + if (!has(fx, name)) return null; + out[i] = (try b.up(name)).ptr; + } + return out; +} diff --git a/zig/tests/cuda/deepseek_v41/experts_cases.zig b/zig/tests/cuda/deepseek_v41/experts_cases.zig new file mode 100644 index 0000000000..812cd29ea0 --- /dev/null +++ b/zig/tests/cuda/deepseek_v41/experts_cases.zig @@ -0,0 +1,303 @@ +//! Bit checks of the pointwise, glue, prefill glue, top-k keys and x3gm plan kernels against torch. + +const std = @import("std"); +const cuda = @import("cuda"); +const dsv41 = @import("dsv41_kernels"); +const check = @import("../check.zig"); +const fixture = @import("../fixture.zig"); +const Fixture = fixture.Fixture; +const experts = @import("experts.zig"); + +const exl3 = dsv41.exl3; +const Gpu = check.Gpu; +const Bufs = experts.Bufs; +const size = experts.size; +const same = experts.same; + +/// pointwise.cu against torch: the shared expert's SwiGLU with the limit, and the partial adds (fp32, into bf16). +pub fn pointwiseCases(gpu: Gpu, k: *const dsv41.Kernels, dir: []const u8) !void { + var fx = try Fixture.open(gpu.gpa, gpu.io, dir); + defer fx.deinit(); + var b: Bufs = .{ .gpu = gpu, .fx = &fx }; + defer b.deinit(); + var stream = try cuda.Stream.init(gpu.d, false); // blocking: ordered after the uploads and memsets on the default stream + defer stream.deinit(); + const o = k.others(stream); + const R = try size(fx, "R"); + const n = try size(fx, "n"); + const D = try size(fx, "D"); + const g = try b.up("g"); + const u = try b.up("u"); + const act = try b.zeros(R * n * 4, 0xee); + try o.swiglu(g.ptr, n, u.ptr, n, act.ptr, n, R, n, @floatCast(try fx.float("limit"))); + try stream.synchronize(); + try same(gpu, fx, act, "act", "swiglu vs torch clamp_ / silu / mul_"); + const a = try b.up("a"); + const bb = try b.up("b"); + const y16 = try b.zeros(R * D * 2, 0xee); + try o.addBf16(a.ptr, bb.ptr, y16.ptr, R * D); + try o.addInto(a.ptr, bb.ptr, R * D); + try stream.synchronize(); + try same(gpu, fx, a, "sum32", "out += shared"); + try same(gpu, fx, y16, "sum16", "torch.add(out=bf16)"); + check.pass("BITEXACT pointwise.cu == torch: swiglu with limit ({d} x {d}, NaN / inf / -0.0), adds into fp32 and bf16", .{ R, n }); +} + +/// glue.cu against torch: the window's embedding send / sum, the casts, positions, gather_cols and the carry. +pub fn glueCases(gpu: Gpu, k: *const dsv41.Kernels, dir: []const u8) !void { + var fx = try Fixture.open(gpu.gpa, gpu.io, dir); + defer fx.deinit(); + var b: Bufs = .{ .gpu = gpu, .fx = &fx }; + defer b.deinit(); + var stream = try cuda.Stream.init(gpu.d, false); // blocking: ordered after the uploads on the default stream + defer stream.deinit(); + const o = k.others(stream); + const V = try size(fx, "V"); + const D = try size(fx, "D"); + const W = try size(fx, "W"); + const n = try size(fx, "n"); + const c = try size(fx, "c"); + + const embed = try b.up("embed"); + const ids = try b.up("ids"); + const send = try b.zeros(n * D * 2, 0xee); + try o.embedSend(ids.ptr, n, embed.ptr, try fx.int("lo"), V, D, send.ptr); + const recv = try b.up("recv"); + const summed = try b.zeros(n * 4 * D * 2, 0xee); + try o.embedSum(recv.ptr, W, n, D, summed.ptr); + const x = try b.up("x"); + const cast = try b.zeros(n * D * 2, 0xee); + try o.castBf16(x.ptr, cast.ptr, n * D); + const kw = try b.zeros(n * D * 4, 0xee); + try o.kitWeights(x.ptr, kw.ptr, n * D); + const kl = try b.up("x"); + try o.kitLogits(kl.ptr, n * D); + const gc = try b.up("gc"); + const cols = try b.zeros(n * W * c * 2, 0xee); + try o.gatherCols(gc.ptr, W, n, c, cols.ptr); + const src = try b.up("src"); + const row: usize = @intCast(try fx.int("row")); + const carry = try b.zeros(1024 * 4, 0xee); + try o.carry(src.ptr, @intCast(try fx.int("ld")), 0, row, 1024, carry.ptr); + // the device-held row (graph-safe form): the same bits + const acc = try cuda.DeviceBuffer.fromHost(gpu.d, std.mem.asBytes(&@as(i32, @intCast(row)))); + try b.list.append(gpu.gpa, acc); + const carry2 = try b.zeros(1024 * 4, 0xee); + try o.carry(src.ptr, @intCast(try fx.int("ld")), acc.ptr, 0, 1024, carry2.ptr); + const start: i32 = @intCast(try fx.int("start")); + const pos = try b.zeros(4, 0xee); + const pos64 = try b.zeros(n * 8, 0xee); + const lo = try b.zeros(n * 4, 0xee); + try o.positions(start, n, pos.ptr, pos64.ptr, lo.ptr); + // the graphed window's form: start read from the device, the same values + const start_dev = try cuda.DeviceBuffer.fromHost(gpu.d, std.mem.asBytes(&start)); + try b.list.append(gpu.gpa, start_dev); + const pos_d = try b.zeros(4, 0xee); + const pos64_d = try b.zeros(n * 8, 0xee); + const lo_d = try b.zeros(n * 4, 0xee); + try o.positionsDev(start_dev.ptr, n, pos_d.ptr, pos64_d.ptr, lo_d.ptr); + // widen_f32: the recv rows (bf16, NaN / inf / -0 among them) to fp32, against the bits shifted up on the host + const wide = try b.zeros(W * n * D * 4, 0xee); + try o.widenF32(recv.ptr, wide.ptr, W * n * D); + try stream.synchronize(); + { + const recv_host = try fx.bytes("recv"); + defer gpu.gpa.free(recv_host); + const want = try gpu.gpa.alloc(u32, recv_host.len / 2); + defer gpu.gpa.free(want); + for (want, 0..) |*w, i| w.* = @as(u32, std.mem.readInt(u16, recv_host[2 * i ..][0..2], .little)) << 16; + const got = try gpu.gpa.alloc(u8, 4 * want.len); + defer gpu.gpa.free(got); + try wide.download(0, got); + try check.sameBytes("glue widen_f32", got, std.mem.sliceAsBytes(want)); + } + + try same(gpu, fx, send, "send", "glue embed_send"); + try same(gpu, fx, summed, "summed", "glue embed_sum"); + try same(gpu, fx, cast, "cast", "glue cast_bf16"); + try same(gpu, fx, kw, "kw", "glue kit_weights"); + try same(gpu, fx, kl, "kl", "glue kit_logits"); + try same(gpu, fx, cols, "cols", "glue gather_cols"); + try same(gpu, fx, carry, "carry", "glue carry (host row)"); + try same(gpu, fx, carry2, "carry", "glue carry (device row)"); + // positions: no torch op to record; Win.make's values + var want_pos: [1]i32 = .{start}; + const want64 = try gpu.gpa.alloc(i64, n); + defer gpu.gpa.free(want64); + const want_lo = try gpu.gpa.alloc(i32, n); + defer gpu.gpa.free(want_lo); + for (want64, want_lo, 0..) |*p, *l, i| { + p.* = start + @as(i64, @intCast(i)); + l.* = 0; + } + for ([_]struct { cuda.DeviceBuffer, []const u8 }{ .{ pos, std.mem.sliceAsBytes(&want_pos) }, .{ pos64, std.mem.sliceAsBytes(want64) }, .{ lo, std.mem.sliceAsBytes(want_lo) }, .{ pos_d, std.mem.sliceAsBytes(&want_pos) }, .{ pos64_d, std.mem.sliceAsBytes(want64) }, .{ lo_d, std.mem.sliceAsBytes(want_lo) } }) |pw| { + const got = try gpu.gpa.alloc(u8, pw[1].len); + defer gpu.gpa.free(got); + try pw[0].download(0, got); + try check.sameBytes("glue positions", got, pw[1]); + } + check.pass("BITEXACT glue.cu == torch: embed send / sum (W {d}), cast_bf16, kit_weights, kit_logits, gather_cols, carry, positions (host and device start), widen_f32", .{W}); +} + +/// prefill_glue.cu against torch: block_prefill.zig's glue steps (picks, masks, casts, projections, ring copies). +pub fn pfglueCases(gpu: Gpu, k: *const dsv41.Kernels, dir: []const u8) !void { + var fx = try Fixture.open(gpu.gpa, gpu.io, dir); + defer fx.deinit(); + var b: Bufs = .{ .gpu = gpu, .fx = &fx }; + defer b.deinit(); + var stream = try cuda.Stream.init(gpu.d, false); // blocking: ordered after the uploads on the default stream + defer stream.deinit(); + const o = k.others(stream); + var nb: [64]u8 = undefined; + var nb2: [64]u8 = undefined; + + const ntp = try size(fx, "tp"); + for (0..ntp) |i| { + const R = try size(fx, try std.fmt.bufPrint(&nb, "tp{d}_R", .{i})); + const n = try size(fx, try std.fmt.bufPrint(&nb, "tp{d}_n", .{i})); + const count = try size(fx, try std.fmt.bufPrint(&nb, "tp{d}_count", .{i})); + const keys = try b.up(try std.fmt.bufPrint(&nb, "tp{d}_keys", .{i})); + const got = try b.zeros(R * count * 4, 0xee); + try o.topPositions(keys.ptr, n, R, n, count, got.ptr); + try stream.synchronize(); + try same(gpu, fx, got, try std.fmt.bufPrint(&nb, "tp{d}_want", .{i}), try std.fmt.bufPrint(&nb2, "pfglue top_positions {d}", .{i})); + } + { + const R = try size(fx, "ck_R"); + const cnb = try size(fx, "ck_nb"); + const bs = try size(fx, "ck_bs"); + const blocks = try b.up("ck_blocks"); + const got = try b.zeros(R * cnb * bs * 4, 0xee); + try o.candKeys(blocks.ptr, cnb, R, cnb, bs, got.ptr); + try stream.synchronize(); + try same(gpu, fx, got, "ck_want", "pfglue candidate_keys"); + } + { + const sel = try b.up("tp0_want"); + const pos = try b.up("vc_pos"); + const R = try size(fx, "tp0_R"); + const K = try size(fx, "tp0_count"); + for ([_]u32{ 4, 128 }) |ratio| { + const got = try b.zeros(R * 4, 0xee); + try o.visibleCounts(sel.ptr, K, pos.ptr, ratio, R, got.ptr); + try stream.synchronize(); + try same(gpu, fx, got, try std.fmt.bufPrint(&nb, "vc{d}_want", .{ratio}), "pfglue visible_counts"); + } + } + { + const n = try size(fx, "gp_n"); + const ld = try size(fx, "gp_slots"); + const topk = try size(fx, "gp_topk"); + const pick = try b.up("gp_pick"); + const wts = try b.up("gp_wts"); + const pk = try b.zeros(n * topk * 4, 0xee); + const w6 = try b.zeros(n * topk * 4, 0xee); + try o.gmPicks(pick.ptr, wts.ptr, ld, topk, n, pk.ptr, w6.ptr); + try stream.synchronize(); + try same(gpu, fx, pk, "gp_pk", "pfglue gm_picks picks"); + try same(gpu, fx, w6, "gp_w6", "pfglue gm_picks kit weights"); + } + { + const E = try size(fx, "wm_E"); + const P = try size(fx, "wm_P"); + const pick = try b.up("wm_pick"); + const tab = try b.up("wm_tab"); + for ([_]u32{ 4, 10 }) |k2| { + const got = try b.zeros(P * 4, 0xee); + try o.widthMask(pick.ptr, P, tab.ptr, E, k2, got.ptr); + try stream.synchronize(); + try same(gpu, fx, got, try std.fmt.bufPrint(&nb, "wm{d}_want", .{k2}), "pfglue width mask"); + } + } + { + const x = try b.up("f64_in"); + const n = x.len / 8; + const got = try b.zeros(n * 4, 0xee); + try o.f64Bf16F32(x.ptr, got.ptr, n); + try stream.synchronize(); + try same(gpu, fx, got, "f64_want", "pfglue fp64 -> bf16 -> fp32"); + } + { + const n = try size(fx, "pj_n"); + const hd = try size(fx, "pj_hd"); + const kv = try b.up("pj_kv"); + const gate = try b.up("pj_gate"); + const two = try b.zeros(n * 2 * hd * 4, 0xee); + try o.widenCat(kv.ptr, gate.ptr, n, hd, two.ptr); + const one = try b.zeros(n * hd * 4, 0xee); + try o.widenCat(kv.ptr, 0, n, hd, one.ptr); + try stream.synchronize(); + try same(gpu, fx, two, "pj_two", "pfglue projection [kv | gate]"); + try same(gpu, fx, one, "pj_one", "pfglue projection kv"); + } + for (0..2) |i| { + const sr = try size(fx, try std.fmt.bufPrint(&nb, "rc{d}_sr", .{i})); + const dr = try size(fx, try std.fmt.bufPrint(&nb, "rc{d}_dr", .{i})); + const rb = try size(fx, try std.fmt.bufPrint(&nb, "rc{d}_rb", .{i})); + const lo: u64 = @intCast(try fx.int(try std.fmt.bufPrint(&nb, "rc{d}_lo", .{i}))); + const hi: u64 = @intCast(try fx.int(try std.fmt.bufPrint(&nb, "rc{d}_hi", .{i}))); + const src = try b.up(try std.fmt.bufPrint(&nb, "rc{d}_src", .{i})); + const dst = try b.up(try std.fmt.bufPrint(&nb, "rc{d}_dst", .{i})); + try o.ringCopy(src.ptr, sr, dst.ptr, dr, rb, lo, hi); + try stream.synchronize(); + try same(gpu, fx, dst, try std.fmt.bufPrint(&nb, "rc{d}_want", .{i}), "pfglue staging ring copy"); + } + check.pass("BITEXACT prefill_glue.cu == torch: top_positions ({d} shapes, counts 512 / 2,048), candidate_keys, visible_counts, gm_picks, width mask, fp64 casts, projection, ring copies", .{ntp}); +} + +/// topk_keys.cu against pick.top (torch.topk on pick.keys) at k 1 / 5 / 64 / 1,024 over 64,640 columns. +pub fn topkCases(gpu: Gpu, k: *const dsv41.Kernels, dir: []const u8) !void { + var fx = try Fixture.open(gpu.gpa, gpu.io, dir); + defer fx.deinit(); + var b: Bufs = .{ .gpu = gpu, .fx = &fx }; + defer b.deinit(); + var stream = try cuda.Stream.init(gpu.d, false); // blocking: ordered after the uploads and memsets on the default stream + defer stream.deinit(); + const o = k.others(stream); + const R = try size(fx, "R"); + const C = try size(fx, "C"); + const lg = try b.up("lg"); + var name_buf: [32]u8 = undefined; + for (0..try size(fx, "ks")) |i| { + const kk = try size(fx, try std.fmt.bufPrint(&name_buf, "k{d}", .{i})); + const vals = try b.zeros(R * kk * 4, 0xee); + const cols = try b.zeros(R * kk * 8, 0xee); + try o.topKeys(lg.ptr, C, R, C, kk, vals.ptr, cols.ptr); + try stream.synchronize(); + try same(gpu, fx, cols, try std.fmt.bufPrint(&name_buf, "cols{d}", .{i}), "topk_keys cols"); + try same(gpu, fx, vals, try std.fmt.bufPrint(&name_buf, "vals{d}", .{i}), "topk_keys vals"); + } + check.pass("BITEXACT topk_keys.cu == pick.top on {d} rows x {d} columns, 4 k", .{ R, C }); +} + +/// x3gm_plan.cu against torch's x3gm.plan on full-sized picks (the "plan" case). +pub fn planCases(gpu: Gpu, k: *const dsv41.Kernels, dir: []const u8) !void { + var fx = try Fixture.open(gpu.gpa, gpu.io, dir); + defer fx.deinit(); + var b: Bufs = .{ .gpu = gpu, .fx = &fx }; + defer b.deinit(); + var stream = try cuda.Stream.init(gpu.d, false); // blocking: ordered after the uploads and memsets on the default stream + defer stream.deinit(); + const o = k.experts(stream); + var name_buf: [64]u8 = undefined; + const n = try size(fx, "cases"); + for (0..n) |i| { + const P = try size(fx, try std.fmt.bufPrint(&name_buf, "c{d}_P", .{i})); + const E = try size(fx, try std.fmt.bufPrint(&name_buf, "c{d}_E", .{i})); + const bm = try size(fx, try std.fmt.bufPrint(&name_buf, "c{d}_bm", .{i})); + const T = exl3.planPasses(P, E, bm); + const pk = try b.up(try std.fmt.bufPrint(&name_buf, "c{d}_pk", .{i})); + const order = try b.zeros(P * 4, 0xee); + const pe = try b.zeros(T * 4, 0xee); + const poff = try b.zeros(T * 4, 0xee); + const pcnt = try b.zeros(T * 4, 0xee); + const npass = try b.zeros(4, 0xee); + try o.gmPlan(pk.ptr, P, E, bm, order.ptr, pe.ptr, poff.ptr, pcnt.ptr, npass.ptr); + try stream.synchronize(); + for ([_][]const u8{ "order", "pe", "poff", "pcnt", "npass" }, [_]cuda.DeviceBuffer{ order, pe, poff, pcnt, npass }) |part, buf| { + const name = try std.fmt.bufPrint(&name_buf, "c{d}_{s}", .{ i, part }); + try same(gpu, fx, buf, name, name); + } + } + check.pass("BITEXACT x3gm_plan.cu == torch x3gm.plan on {d} cases (up to 24,576 pairs, 384 experts)", .{n}); +} diff --git a/zig/tests/cuda/deepseek_v41/kv_glue.zig b/zig/tests/cuda/deepseek_v41/kv_glue.zig new file mode 100644 index 0000000000..61b03e102b --- /dev/null +++ b/zig/tests/cuda/deepseek_v41/kv_glue.zig @@ -0,0 +1,95 @@ +//! Actual Zig binding + replay ABI against Python Triton's unfused RMS/store fixture bytes. +const std = @import("std"); +const cuda = @import("cuda"); +const dsv41 = @import("dsv41_kernels"); +const check = @import("../check.zig"); +const Fixture = @import("../fixture.zig").Fixture; + +fn up(gpu: check.Gpu, fx: Fixture, name: []const u8) !cuda.DeviceBuffer { + const bytes = try fx.bytes(name); + defer gpu.gpa.free(bytes); + return cuda.DeviceBuffer.fromHost(gpu.d, bytes); +} +fn same(gpu: check.Gpu, fx: Fixture, b: cuda.DeviceBuffer, name: []const u8) !void { + const want = try fx.bytes(name); + defer gpu.gpa.free(want); + const got = try gpu.gpa.alloc(u8, want.len); + defer gpu.gpa.free(got); + try b.download(0, got); + try check.sameBytes(name, got, want); +} + +pub fn run(gpu: check.Gpu, k: *const dsv41.Kernels, dir: []const u8) !void { + var fx = try Fixture.open(gpu.gpa, gpu.io, dir); + defer fx.deinit(); + var x = try up(gpu, fx, "x"); + defer x.free(); + var w = try up(gpu, fx, "w"); + defer w.free(); + var cs = try up(gpu, fx, "cs"); + defer cs.free(); + var pos = try up(gpu, fx, "pos"); + defer pos.free(); + var sl = try up(gpu, fx, "sl"); + defer sl.free(); + var v = try up(gpu, fx, "v"); + defer v.free(); + var s = try up(gpu, fx, "s"); + defer s.free(); + var norm = try up(gpu, fx, "norm"); + defer norm.free(); + var stream = try cuda.Stream.init(gpu.d, false); + defer stream.deinit(); + const n = try fx.int("n"); + const rows = try fx.int("rows") == 1; + const ring = try fx.int("ring"); + const eps = try fx.float("eps"); + try v.fill8(0xa5, null); + try s.fill8(0xa5, null); + try norm.fill8(0xa5, null); + try k.kvStore(stream).normStore(.{ + .x = x.ptr, + .w = w.ptr, + .cs = cs.ptr, + .v = v.ptr, + .s = s.ptr, + .pos = pos.ptr, + .sl = if (rows) sl.ptr else 0, + .norm_out = norm.ptr, + .ring = @intCast(ring), + .rows = @intFromBool(rows), + .eps = @floatCast(eps), + }, @intCast(n)); + try stream.synchronize(); + try same(gpu, fx, norm, "norm"); + try same(gpu, fx, v, "v"); + try same(gpu, fx, s, "s"); + + // Production uses norm_out=null. Also exercise argument packing through the real replay dispatcher. + try v.fill8(0xa5, null); + try s.fill8(0xa5, null); + const rp = dsv41.replay; + const args = [_]rp.Arg{ + .{ .tensor = .{ .ptr = x.ptr, .dtype = .bf16, .shape = &.{ n, 512 }, .stride = &.{ 512, 1 } } }, + .{ .tensor = .{ .ptr = w.ptr, .dtype = .f32, .shape = &.{512}, .stride = &.{1} } }, + .{ .tensor = .{ .ptr = cs.ptr, .dtype = .f32, .shape = &.{ 1024, 64 }, .stride = &.{ 64, 1 } } }, + .{ .tensor = .{ .ptr = v.ptr, .dtype = .u8, .shape = &.{ 3 * ring + 1, 576 }, .stride = &.{ 576, 1 } } }, + .{ .tensor = .{ .ptr = s.ptr, .dtype = .u8, .shape = &.{ 3 * ring + 1, 8 }, .stride = &.{ 8, 1 } } }, + .{ .tensor = .{ .ptr = pos.ptr, .dtype = if (rows) .i64 else .i32, .shape = if (rows) &.{n} else &.{1}, .stride = &.{1} } }, + if (rows) .{ .tensor = .{ .ptr = sl.ptr, .dtype = .i64, .shape = &.{n}, .stride = &.{1} } } else .none, + .{ .int = 512 }, + .{ .int = 64 }, + .{ .int = 576 }, + .{ .int = 8 }, + .{ .float = 1.0 / 512.0 }, + .{ .float = eps }, + .{ .int = ring }, + .{ .boolean = rows }, + }; + var scratch: rp.Scratch = .{ .ptr = 0, .len = 0 }; + if (!try rp.call(k, stream, "tf_dsv41_kv_glue_v1", "norm_store", &args, &.{}, &scratch)) return error.NoBinding; + try stream.synchronize(); + try same(gpu, fx, v, "v"); + try same(gpu, fx, s, "s"); + check.pass("KV norm/store: debug and replay bindings, norm/cache/guards byte-exact ({d} rows)", .{n}); +} diff --git a/zig/tests/cuda/deepseek_v41/kvsplit.zig b/zig/tests/cuda/deepseek_v41/kvsplit.zig new file mode 100644 index 0000000000..561b1dcbd2 --- /dev/null +++ b/zig/tests/cuda/deepseek_v41/kvsplit.zig @@ -0,0 +1,95 @@ +//! kvsplit.cu against kv/split.zig's HostKernels formulas on seeded data (dense, row mode, gather), byte for byte. + +const std = @import("std"); +const cuda = @import("cuda"); +const dsv41 = @import("dsv41_kernels"); +const check = @import("../check.zig"); + +const Gpu = check.Gpu; +const DenseArgs = dsv41.ops.kvsplit.DenseArgs; + +const Case = struct { rows: u32, k: u32, slots: u32, pages: u32, psh: u5, row_bytes: u32, world: u32, stacked: bool }; + +pub fn run(gpu: Gpu, k: *const dsv41.Kernels) !void { + const cases = [_]Case{ + .{ .rows = 1, .k = 512, .slots = 1, .pages = 64, .psh = 7, .row_bytes = 584, .world = 2, .stacked = false }, + .{ .rows = 16, .k = 512, .slots = 5, .pages = 40, .psh = 8, .row_bytes = 584, .world = 2, .stacked = true }, + .{ .rows = 3, .k = 1024, .slots = 3, .pages = 33, .psh = 7, .row_bytes = 136, .world = 3, .stacked = true }, + }; + var stream = try cuda.Stream.init(gpu.d, false); // blocking: ordered after the uploads and memsets on the default stream + defer stream.deinit(); + const o = k.others(stream); + var prng = std.Random.DefaultPrng.init(4141); + const rnd = prng.random(); + for (cases) |c| { + const gpa = gpu.gpa; + const n: usize = @as(usize, c.rows) * c.k; + const local_pages = c.pages; // rows a family holds locally: pages << psh + const base_rows = @as(usize, local_pages) << c.psh; + const base = try gpa.alloc(u8, base_rows * c.row_bytes); + defer gpa.free(base); + rnd.bytes(base); + const pts: u32 = c.pages * 2; // logical pages a slot + const table = try gpa.alloc(i32, @as(usize, c.slots) * pts); + defer gpa.free(table); + for (table) |*t| t.* = rnd.intRangeLessThan(i32, 0, @intCast(local_pages)); + const rslot = try gpa.alloc(i32, c.rows); + defer gpa.free(rslot); + for (rslot) |*s| s.* = if (c.stacked) rnd.intRangeLessThan(i32, 0, @intCast(c.slots)) else 0; + const sel = try gpa.alloc(i32, n); + defer gpa.free(sel); + const logical_rows: i32 = @intCast(@as(u32, pts) << c.psh); + for (sel) |*s| s.* = if (rnd.uintLessThan(u32, 8) == 0) -1 else rnd.intRangeLessThan(i32, 0, logical_rows); + + // HostKernels.dense + const want = try gpa.alloc(u8, n * c.row_bytes); + defer gpa.free(want); + const want_tok = try gpa.alloc(i32, n); + defer gpa.free(want_tok); + const mask = (@as(u32, 1) << c.psh) - 1; + for (0..n) |i| { + const t: u32 = @intCast(@max(sel[i], 0)); + const s: usize = if (c.stacked) @intCast(rslot[i / c.k]) else 0; + const local: u32 = @intCast(table[s * pts + (t >> c.psh)]); + const phys = (@as(usize, local) << c.psh) | (t & mask); + @memcpy(want[i * c.row_bytes ..][0..c.row_bytes], base[phys * c.row_bytes ..][0..c.row_bytes]); + want_tok[i] = if (sel[i] < 0) -1 else @intCast(((t >> c.psh) % c.world) * n + i); + } + + var d_base = try cuda.DeviceBuffer.fromHost(gpu.d, base); + defer d_base.free(); + var d_table = try cuda.DeviceBuffer.fromHost(gpu.d, std.mem.sliceAsBytes(table)); + defer d_table.free(); + var d_rslot = try cuda.DeviceBuffer.fromHost(gpu.d, std.mem.sliceAsBytes(rslot)); + defer d_rslot.free(); + var d_sel = try cuda.DeviceBuffer.fromHost(gpu.d, std.mem.sliceAsBytes(sel)); + defer d_sel.free(); + var d_send = try cuda.DeviceBuffer.alloc(gpu.d, n * c.row_bytes); + defer d_send.free(); + var d_tok = try cuda.DeviceBuffer.alloc(gpu.d, n * 4); + defer d_tok.free(); + try o.kvDense(.{ .sel = d_sel.ptr, .rows = c.rows, .k = c.k, .table = d_table.ptr, .pts = pts, .rslot = if (c.stacked) d_rslot.ptr else 0, .psh = c.psh, .base = d_base.ptr, .row_bytes = c.row_bytes, .world = c.world, .send = d_send.ptr, .tok = d_tok.ptr }); + try stream.synchronize(); + const got = try check.download(gpu, d_send); + defer gpa.free(got); + try check.sameBytes("kvsplit dense rows", got, want); + const got_tok = try check.download(gpu, d_tok); + defer gpa.free(got_tok); + try check.sameBytes("kvsplit dense tokens", got_tok, std.mem.sliceAsBytes(want_tok)); + + // HostKernels.gather: a row list of the local rows + const m: u32 = 300; + const phys = try gpa.alloc(u32, m); + defer gpa.free(phys); + for (phys) |*p| p.* = rnd.uintLessThan(u32, @intCast(base_rows)); + for (0..m) |i| @memcpy(want[i * c.row_bytes ..][0..c.row_bytes], base[@as(usize, phys[i]) * c.row_bytes ..][0..c.row_bytes]); + var d_phys = try cuda.DeviceBuffer.fromHost(gpu.d, std.mem.sliceAsBytes(phys)); + defer d_phys.free(); + try o.kvGather(d_base.ptr, c.row_bytes, d_phys.ptr, m, d_send.ptr); + try stream.synchronize(); + const got2 = try check.download(gpu, d_send); + defer gpa.free(got2); + try check.sameBytes("kvsplit gather", got2[0 .. m * c.row_bytes], want[0 .. m * c.row_bytes]); + check.pass("BITEXACT kvsplit dense + gather == kv/split.zig HostKernels: R {d} x K {d}, {d} B rows, psh {d}, world {d}, {s}", .{ c.rows, c.k, c.row_bytes, c.psh, c.world, if (c.stacked) "row mode" else "one table" }); + } +} diff --git a/zig/tests/cuda/deepseek_v41/mhc_probe.zig b/zig/tests/cuda/deepseek_v41/mhc_probe.zig new file mode 100644 index 0000000000..7ddefc72b2 --- /dev/null +++ b/zig/tests/cuda/deepseek_v41/mhc_probe.zig @@ -0,0 +1,238 @@ +//! `tf-dsv41-test mhc-probe [windows]`: the 16-row mHC boundary timed under one variable at a time (cold L2, spin...). + +const std = @import("std"); +const cuda = @import("cuda"); +const dsv41 = @import("dsv41_kernels"); +const check = @import("../check.zig"); + +const Gpu = check.Gpu; +const ops = dsv41.ops; + +const D = 5120; +const R = 16; +const W = 2; +const SITES = 80; + +const Cache = enum { cold_write, cold_read, engine, warm }; +const Layout = enum { @"packed", aligned2m, stagger }; + +/// Byte offsets of the window's buffers in one block. +const Off = struct { x: u64, xout: u64, g: u64, part: u64, c: u64, out: u64, coef: u64, cnt: u64, end: u64 }; + +fn layout(l: Layout) Off { + const sizes = [_]u64{ R * 4 * D * 2, R * 4 * D * 2, W * R * D * 2, R * 4 * 40 * 32 * 4, R * D * 2, R * D * 2, 2 * R * 24 * 4, 256 }; + var at: [8]u64 = undefined; + var cur: u64 = 0; + for (sizes, 0..) |s, i| { + at[i] = switch (l) { + .@"packed" => cur, + .aligned2m => @as(u64, i) * (2 << 20), + .stagger => @as(u64, i) * ((1 << 20) + 384 * @as(u64, i)), + }; + cur = std.mem.alignForward(u64, at[i] + s, 512); + } + return .{ .x = at[0], .xout = at[1], .g = at[2], .part = at[3], .c = at[4], .out = at[5], .coef = at[6], .cnt = at[7], .end = cur }; +} + +const Site = struct { fnw: cuda.DeviceBuffer, base: cuda.DeviceBuffer, scale: cuda.DeviceBuffer, nw: cuda.DeviceBuffer }; + +const Bench = struct { + gpu: Gpu, + k: *const dsv41.Kernels, + sites: [SITES]Site, + win: cuda.DeviceBuffer, + big: cuda.DeviceBuffer, // 512 MiB: the fills and the prefetched "weights" + table: cuda.DeviceBuffer, // the prefetch's (address, bytes) rows + stream: cuda.Stream, + other: cuda.Stream, + e: [3]cuda.Event, + host: cuda.HostBuffer, // pinned, mapped: the exchange's slots + dev2: cuda.DeviceBuffer, // the same bytes in device memory (sysmem's control) + + fn args(b: *const Bench, off: Off, s: *const Site, cur: u64) ops.mhc.Args { + const p = b.win.ptr; + const cs: u64 = R * 24 * 4; + return .{ + .x = p + off.x, + .xs = 4 * D, + .xout = p + off.xout, + .g = p + off.g, + .gr = R * D, + .world = W, + .gbf16 = 1, + .post = p + off.coef + cur * cs + R * 4 * 4, + .comb = p + off.coef + cur * cs + R * 8 * 4, + .pre = p + off.coef + cur * cs, + .@"fn" = s.fnw.ptr, + .base = s.base.ptr, + .scale = s.scale.ptr, + .part = p + off.part, + .c = p + off.c, + .nw = s.nw.ptr, + .out = p + off.out, + .opre = p + off.coef + (1 - cur) * cs, + .opost = p + off.coef + (1 - cur) * cs + R * 4 * 4, + .ocomb = p + off.coef + (1 - cur) * cs + R * 8 * 4, + .cnt = p + off.cnt, + .R = R, + .eps = 1e-6, + .hc_eps = 1e-6, + .post_alpha = 2.0, + .iters = 20, + .spin = 8000, + .@"defer" = 1, + }; + } + + fn sub(b: *const Bench, at: u64, len: usize) cuda.DeviceBuffer { + return .{ .d = b.gpu.d, .ptr = b.win.ptr + at, .len = len }; + } + + /// One variant: `windows` x 80 timed launches after one untimed window. `name` overhead: the memset alone. + fn run(b: *Bench, name: []const u8, l: Layout, cache: Cache, cross: bool, sys: ?bool, windows: usize) !void { + const off = layout(l); + try b.win.fill32(0x3f803f80, null); // bf16 1.0 streams / partials + try b.sub(off.coef, 2 * R * 24 * 4).fill32(0x3e800000, null); + try b.sub(off.cnt, 256).fill32(0, null); + const o = b.k.others(b.stream); + const n = windows * SITES; + const tb = try b.gpu.gpa.alloc(f32, n); + defer b.gpu.gpa.free(tb); + const tc = try b.gpu.gpa.alloc(f32, n); + defer b.gpu.gpa.free(tc); + const overhead = std.mem.eql(u8, name, "overhead"); + const gbuf = b.sub(off.g, W * R * D * 2); + for (0..windows + 1) |wi| for (0..SITES) |si| { + const s = &b.sites[if (cache == .warm) 0 else si]; + switch (cache) { + .cold_write => try b.big.fill32(@intCast(si), b.stream.handle), + .cold_read, .engine => try o.l2Segments(b.table.ptr, 4096, 48, 128, 64 << 10, false), + .warm => {}, + } + if (cache == .engine) { + if (cross) { + try b.e[2].record(b.stream); + try b.other.wait(b.e[2]); + try gbuf.fill32(@intCast(si | 0x3f800000), b.other.handle); + try b.e[2].record(b.other); + try b.stream.wait(b.e[2]); + } else try gbuf.fill32(@intCast(si | 0x3f800000), b.stream.handle); + } + if (sys) |host| { + // the exchange's slot traffic: stage 160 KiB out (bf16 from fp32), read 320 KiB of fp32 back into g + const slot = if (host) try b.host.device() else b.dev2.ptr; + try o.castBf16(b.big.ptr, slot, W * R * D / 2); + try o.castBf16(slot + (512 << 10), gbuf.ptr, W * R * D / 2); + } + const a = b.args(off, s, @intCast(si % 2)); + try b.e[0].record(b.stream); + if (overhead) try b.sub(off.cnt + 128, 4).fill32(0, b.stream.handle) else try o.mhcBoundary(a, 0, false); + try b.e[1].record(b.stream); + if (!overhead) try o.mhcCoef(a); + try b.e[2].record(b.stream); + try b.e[2].synchronize(); + if (wi == 0) continue; + const i = (wi - 1) * SITES + si; + tb[i] = 1e3 * try cuda.Event.elapsedMs(b.e[0], b.e[1]); + tc[i] = 1e3 * try cuda.Event.elapsedMs(b.e[1], b.e[2]); + }; + const x = stats(tb); + const y = stats(tc); + if (overhead) { + std.debug.print("mhc-probe {s:<11} {d:6.1} us (p10 {d:.1}, p90 {d:.1})\n", .{ name, x[0], x[1], x[2] }); + } else std.debug.print("mhc-probe {s:<11} boundary {d:6.1} us (p10 {d:.1}, p90 {d:.1}), coef {d:5.1} us\n", .{ name, x[0], x[1], x[2], y[0] }); + } +}; + +fn stats(xs: []f32) [3]f32 { + std.mem.sort(f32, xs, {}, std.sort.asc(f32)); + return .{ xs[xs.len / 2], xs[xs.len / 10], xs[xs.len * 9 / 10] }; +} + +var spin_stop: std.atomic.Value(bool) = .init(false); + +fn spinner(sink: *u64) void { + var x: u64 = 1; + while (!spin_stop.load(.monotonic)) { + for (0..4096) |_| x = x *% 6364136223846793005 +% 1442695040888963407; + } + sink.* = x; +} + +/// `engine` with `n` host threads busy-spinning. +fn spun(b: *Bench, n: usize, windows: usize) !void { + var name_buf: [16]u8 = undefined; + const name = try std.fmt.bufPrint(&name_buf, "spin-{d}", .{n}); + var sinks: [256]u64 = undefined; + var ts: [256]std.Thread = undefined; + const m = @min(n, ts.len); + spin_stop.store(false, .monotonic); + for (0..m) |i| ts[i] = try std.Thread.spawn(.{}, spinner, .{&sinks[i]}); + defer { + spin_stop.store(true, .monotonic); + for (ts[0..m]) |t| t.join(); + } + try b.run(name, .@"packed", .engine, false, null, windows); +} + +pub fn run(gpu: Gpu, k: *const dsv41.Kernels, windows_arg: ?[]const u8) !void { + const windows: usize = @max(1, if (windows_arg) |w| try std.fmt.parseInt(usize, w, 10) else 5); + const d = gpu.d; + var b: Bench = undefined; + b.gpu = gpu; + b.k = k; + b.win = try cuda.DeviceBuffer.alloc(d, layout(.stagger).end + layout(.aligned2m).end); + defer b.win.free(); + for (&b.sites) |*s| { + s.* = .{ + .fnw = try cuda.DeviceBuffer.alloc(d, 24 * 4 * D * 2), + .base = try cuda.DeviceBuffer.alloc(d, 24 * 4), + .scale = try cuda.DeviceBuffer.alloc(d, 3 * 4), + .nw = try cuda.DeviceBuffer.alloc(d, D * 4), + }; + try s.fnw.fill32(0x3c003c00, null); + try s.base.fill32(0, null); + try s.scale.fill32(0x3f800000, null); + try s.nw.fill32(0x3f800000, null); + } + defer for (&b.sites) |*s| { + s.fnw.free(); + s.base.free(); + s.scale.free(); + s.nw.free(); + }; + b.big = try cuda.DeviceBuffer.alloc(d, 512 << 20); + defer b.big.free(); + try b.big.fill32(0, null); + // 4,096 rows of 64 KiB: 256 MiB of the big buffer, read into L2 + var rows: [4096][2]i64 = undefined; + for (&rows, 0..) |*r, i| r.* = .{ @intCast(b.big.ptr + i * (64 << 10)), 64 << 10 }; + b.table = try cuda.DeviceBuffer.fromHost(d, std.mem.sliceAsBytes(&rows)); + defer b.table.free(); + b.stream = try cuda.Stream.init(d, false); + defer b.stream.deinit(); + b.other = try cuda.Stream.init(d, true); + defer b.other.deinit(); + for (&b.e) |*e| e.* = try cuda.Event.init(d, true); + defer for (&b.e) |*e| e.deinit(); + b.host = try cuda.HostBuffer.allocMapped(d, 1 << 20); + @memset(b.host.bytes, 0); + defer b.host.free(); + b.dev2 = try cuda.DeviceBuffer.alloc(d, 1 << 20); + defer b.dev2.free(); + try b.dev2.fill32(0x3f800000, null); + + try b.run("overhead", .@"packed", .cold_read, false, null, windows); + try b.run("cold-write", .@"packed", .cold_write, false, null, windows); + try b.run("cold-read", .@"packed", .cold_read, false, null, windows); + try b.run("engine", .@"packed", .engine, false, null, windows); + try b.run("warm", .@"packed", .warm, false, null, windows); + try b.run("aligned2m", .aligned2m, .engine, false, null, windows); + try b.run("stagger", .stagger, .engine, false, null, windows); + try b.run("cross", .@"packed", .engine, true, null, windows); + try b.run("sysmem", .@"packed", .engine, false, true, windows); + try b.run("devmem", .@"packed", .engine, false, false, windows); + const cores = std.Thread.getCpuCount() catch 8; + for ([_]usize{ 1, @max(cores / 2, 2), cores }) |n| try spun(&b, n, windows); + check.pass("mhc-probe: {d} windows a variant", .{windows}); +} diff --git a/zig/tests/cuda/deepseek_v41/mhcdec.zig b/zig/tests/cuda/deepseek_v41/mhcdec.zig new file mode 100644 index 0000000000..2e3f2d5c57 --- /dev/null +++ b/zig/tests/cuda/deepseek_v41/mhcdec.zig @@ -0,0 +1,167 @@ +//! TF_DSV41_MHC_PFDEC kernels against the reference decode mHC site (oracle.py `mhcdec_case`), bit for bit, 1-64 rows. + +const std = @import("std"); +const cuda = @import("cuda"); +const dsv41 = @import("dsv41_kernels"); +const check = @import("../check.zig"); +const Fixture = @import("../fixture.zig").Fixture; + +const Gpu = check.Gpu; +const ops = dsv41.ops; + +const Bufs = struct { + gpu: Gpu, + fx: *const Fixture, + list: std.ArrayList(cuda.DeviceBuffer) = .empty, + + fn deinit(b: *Bufs) void { + for (b.list.items) |*x| x.free(); + b.list.deinit(b.gpu.gpa); + } + + fn up(b: *Bufs, name: []const u8) !cuda.DeviceBuffer { + const host = try b.fx.bytes(name); + defer b.gpu.gpa.free(host); + const buf = try cuda.DeviceBuffer.fromHost(b.gpu.d, host); + try b.list.append(b.gpu.gpa, buf); + return buf; + } + + fn zeros(b: *Bufs, len: usize) !cuda.DeviceBuffer { + const buf = try cuda.DeviceBuffer.alloc(b.gpu.d, @max(len, 4)); + try buf.fill8(0, null); + try b.list.append(b.gpu.gpa, buf); + return buf; + } +}; + +fn same(gpu: Gpu, fx: Fixture, buf: cuda.DeviceBuffer, name: []const u8, what: []const u8) !void { + const want = try fx.bytes(name); + defer gpu.gpa.free(want); + const got = try gpu.gpa.alloc(u8, want.len); + defer gpu.gpa.free(got); + try buf.download(0, got); + try check.sameBytes(what, got, want); +} + +/// cvt.rn.bf16.f32 of a finite value. +fn bf16Rne(f: f32) u16 { + const u: u32 = @bitCast(f); + return @intCast((u + 0x7fff + ((u >> 16) & 1)) >> 16); +} + +/// `_finish_k`'s normed input: rn = div_rn(1, sqrt_rn(div_rn(ssc, D) + eps)), then bf16((c * rn) * w) per column. +fn normed(part: []const f32, c: []const u16, nw: []const f32, R: usize, D: usize, eps: f32, out: []u16) void { + for (0..R) |r| { + var ssc: f32 = 0.0; + for (0..40) |b| ssc += part[(r * 4 * 40 + b) * 32 + 25]; + const rn: f32 = 1.0 / @sqrt(ssc / @as(f32, @floatFromInt(D)) + eps); + for (0..D) |d| { + const cv: f32 = @bitCast(@as(u32, c[r * D + d]) << 16); + out[r * D + d] = bf16Rne((cv * rn) * nw[d]); + } + } +} + +pub fn cases(gpu: Gpu, k: *const dsv41.Kernels, dir: []const u8) !void { + var fx = try Fixture.open(gpu.gpa, gpu.io, dir); + defer fx.deinit(); + var b: Bufs = .{ .gpu = gpu, .fx = &fx }; + defer b.deinit(); + var stream = try cuda.Stream.init(gpu.d, false); // blocking: ordered after the uploads on the default stream + defer stream.deinit(); + const o = k.others(stream); + const R: usize = @intCast(try fx.int("R")); + const D: usize = @intCast(try fx.int("D")); + const W: usize = @intCast(try fx.int("W")); + const mode: u32 = @intCast(try fx.int("mode")); + const tap = try fx.int("tap") != 0; + const in_place = try fx.int("in_place") != 0; + + const x = try b.up("x"); + const xout = if (in_place) x else try b.up("xout0"); + const g = try b.up("g"); + const ppre = try b.up("ppre"); + const ppost = try b.up("ppost"); + const pcomb = try b.up("pcomb"); + const f = try b.up("fn"); + const base = try b.up("base"); + const scale = try b.up("scale"); + const taps = try b.up("taps0"); + const part = try b.zeros(R * 4 * 40 * 32 * 4); + const c = try b.zeros(R * D * 2); + const npre = try b.zeros(R * 4 * 4); + const npost = try b.zeros(R * 4 * 4); + const ncomb = try b.zeros(R * 16 * 4); + + // the forward's PFDEC triple: block_wide.zig's mhc_pf call, then the coefficients (norm checked on the host below) + const post = mode == 0; + const a: ops.mhc_pf.Args = .{ + .x = x.ptr, + .xs = @intCast(4 * D), + .xout = if (post) xout.ptr else 0, + .g = if (post) g.ptr else 0, + .gr = if (post) @intCast(R * D) else 0, + .world = if (post) @intCast(W) else 1, + .gbf16 = @intFromBool(post), + .post = if (post) ppost.ptr else 0, + .comb = if (post) pcomb.ptr else 0, + .pre = if (mode != 1) ppre.ptr else 0, + .@"fn" = f.ptr, + .part = part.ptr, + .c = c.ptr, + .tap = if (tap) taps.ptr + D * 2 else 0, // column block 1 of [R, 3 D] + .ts = if (tap) @intCast(3 * D) else 0, + .R = @intCast(R), + }; + try o.mhcSite(a, mode, false, tap); + try o.mhcCoef(.{ + .part = part.ptr, + .base = base.ptr, + .scale = scale.ptr, + .opre = npre.ptr, + .opost = npost.ptr, + .ocomb = ncomb.ptr, + .R = @intCast(R), + .eps = @floatCast(try fx.float("eps")), + .hc_eps = @floatCast(try fx.float("hc_eps")), + .post_alpha = @floatCast(try fx.float("post_alpha")), + .iters = @intCast(try fx.int("iters")), + }); + try stream.synchronize(); + + const what = std.fs.path.basename(dir); + var name_buf: [160]u8 = undefined; + const n = struct { + fn of(buf: []u8, case: []const u8, part_name: []const u8) []const u8 { + return std.fmt.bufPrint(buf, "{s}: {s}", .{ case, part_name }) catch part_name; + } + }; + if (post) try same(gpu, fx, xout, "xout", n.of(&name_buf, what, "new streams (mhc_pf vs _site)")); + try same(gpu, fx, x, "xs", n.of(&name_buf, what, "streams after the site")); + try same(gpu, fx, c, "c", n.of(&name_buf, what, "collapsed row")); + try same(gpu, fx, taps, "taps", n.of(&name_buf, what, "DSpark tap buffer")); + try same(gpu, fx, part, "part", n.of(&name_buf, what, "partials [R, 4, 40, 32]")); + try same(gpu, fx, npre, "npre", n.of(&name_buf, what, "next pre (coef_kernel vs _finish_k)")); + try same(gpu, fx, npost, "npost", n.of(&name_buf, what, "next post")); + try same(gpu, fx, ncomb, "ncomb", n.of(&name_buf, what, "next comb")); + + // the normed input from these partials and this collapsed row, against the reference `_finish_k` + const ph = try check.download(gpu, part); + defer gpu.gpa.free(ph); + const ch = try check.download(gpu, c); + defer gpu.gpa.free(ch); + const nwb = try fx.bytes("nw"); + defer gpu.gpa.free(nwb); + const got = try gpu.gpa.alloc(u16, R * D); + defer gpu.gpa.free(got); + const pf: []const f32 = @alignCast(std.mem.bytesAsSlice(f32, ph)); + const cf: []const u16 = @alignCast(std.mem.bytesAsSlice(u16, ch[0 .. R * D * 2])); + const wf: []const f32 = @alignCast(std.mem.bytesAsSlice(f32, nwb)); + normed(pf, cf, wf, R, D, @floatCast(try fx.float("eps")), got); + const want = try fx.bytes("out"); + defer gpu.gpa.free(want); + try check.sameBytes(n.of(&name_buf, what, "normed input (_finish_k without COEF's arithmetic)"), std.mem.sliceAsBytes(got), want); + + check.pass("BITEXACT mhcdec {s}: R {d}, mode {d}{s}{s}: mhc_pf site + coef_kernel + normed input == Triton _site + _finish_k", .{ what, R, mode, if (tap) ", tap" else "", if (in_place) ", in place" else "" }); +} diff --git a/zig/tests/cuda/deepseek_v41/replay.zig b/zig/tests/cuda/deepseek_v41/replay.zig new file mode 100644 index 0000000000..a91abaf707 --- /dev/null +++ b/zig/tests/cuda/deepseek_v41/replay.zig @@ -0,0 +1,211 @@ +//! Replays one recorded extension call (dsv41_capture.py) on its recorded bytes; every viewed storage bit-compared. + +const std = @import("std"); +const cuda = @import("cuda"); +const dsv41 = @import("dsv41_kernels"); +const check = @import("../check.zig"); +const Fixture = @import("../fixture.zig").Fixture; + +const replay = dsv41.replay; +const Gpu = check.Gpu; +const Value = std.json.Value; + +pub const Outcome = enum { exact, skipped }; + +const Storage = struct { name: []const u8, buf: ?cuda.DeviceBuffer, len: usize }; + +const Ctx = struct { + gpa: std.mem.Allocator, + a: std.mem.Allocator, // the case's arena: argument trees + storages: []Storage, + extra: std.ArrayList(cuda.DeviceBuffer) = .empty, + + fn ptrOf(c: *Ctx, name: []const u8) !u64 { + for (c.storages) |s| if (std.mem.eql(u8, s.name, name)) return if (s.buf) |b| b.ptr else 0; + return error.UnknownStorage; + } + + fn dtype(s: []const u8) !replay.DType { + const map = .{ .{ "float16", .f16 }, .{ "bfloat16", .bf16 }, .{ "float32", .f32 }, .{ "float64", .f64 }, .{ "int8", .i8 }, .{ "uint8", .u8 }, .{ "int16", .i16 }, .{ "int32", .i32 }, .{ "int64", .i64 }, .{ "bool", .bool } }; + inline for (map) |m| if (std.mem.eql(u8, s, m[0])) return m[1]; + return error.UnknownDType; + } + + fn ints(c: *Ctx, v: Value) ![]const i64 { + const items = v.array.items; + const out = try c.a.alloc(i64, items.len); + for (items, out) |x, *o| o.* = x.integer; + return out; + } + + /// dsv41_capture.py's argument node -> a replay Arg. + fn arg(c: *Ctx, v: Value) !replay.Arg { + const o = v.object; + if (o.get("t")) |t| { + const shape = try c.ints(o.get("shape").?); + var numel: i64 = 1; + for (shape) |d| numel *= d; + // an empty tensor's pointer can precede its storage: pass null, as launchers expect for numel 0 + const raw = o.get("off").?.integer; + const base = try c.ptrOf(t.string); + const ptr: u64 = if (base == 0 or raw < 0 or numel == 0) 0 else base + @as(u64, @intCast(raw)); + return .{ .tensor = .{ .ptr = ptr, .dtype = try dtype(o.get("dtype").?.string), .shape = shape, .stride = try c.ints(o.get("stride").?) } }; + } + if (o.get("i")) |x| return .{ .int = x.integer }; + if (o.get("f")) |x| return .{ .float = switch (x) { + .float => |f| f, + .integer => |n| @floatFromInt(n), + else => return error.WrongType, + } }; + if (o.get("b")) |x| return .{ .boolean = x.bool }; + if (o.get("n") != null) return .none; + if (o.get("l")) |l| { + const items = l.array.items; + const out = try c.a.alloc(replay.Arg, items.len); + for (items, out) |x, *y| y.* = try c.arg(x); + return .{ .list = out }; + } + return error.WrongType; + } + + /// A zeroed device buffer of `n` bytes owned by the case. + fn zeros(c: *Ctx, gpu: Gpu, n: usize) !u64 { + const b = try cuda.DeviceBuffer.alloc(gpu.d, @max(n, 16)); + try b.fill8(0, null); + try c.extra.append(c.gpa, b); + return b.ptr; + } +}; + +fn upload(gpu: Gpu, fx: Fixture, storages: []Storage) !void { + var name_buf: [64]u8 = undefined; + for (storages) |s| { + if (s.buf) |b| { + const host = try fx.bytes(try std.fmt.bufPrint(&name_buf, "{s}_before", .{s.name})); + defer gpu.gpa.free(host); + try b.upload(0, host); + } + } +} + +/// Every storage against the recorded after-call bytes, except `skip` (a ticket / counter replaced by a variant). +fn compare(gpu: Gpu, fx: Fixture, storages: []Storage, what: []const u8, skip: ?[]const u8) !void { + var name_buf: [64]u8 = undefined; + for (storages) |s| { + const b = s.buf orelse continue; + if (skip) |k| if (std.mem.eql(u8, k, s.name)) continue; + const want = try fx.bytes(try std.fmt.bufPrint(&name_buf, "{s}_after", .{s.name})); + defer gpu.gpa.free(want); + const got = try gpu.gpa.alloc(u8, want.len); + defer gpu.gpa.free(got); + try b.download(0, got); + const label = try std.fmt.allocPrint(gpu.gpa, "{s} storage {s}", .{ what, s.name }); + defer gpu.gpa.free(label); + try check.sameBytes(label, got, want); + } +} + +fn storageOf(v: Value) ?[]const u8 { + const t = v.object.get("t") orelse return null; + return t.string; +} + +pub fn run(gpu: Gpu, k: *const dsv41.Kernels, dir: []const u8) !Outcome { + var fx = try Fixture.open(gpu.gpa, gpu.io, dir); + defer fx.deinit(); + var arena = std.heap.ArenaAllocator.init(gpu.gpa); + defer arena.deinit(); + const a = arena.allocator(); + const call = fx.parsed.value.object.get("call") orelse return error.MissingField; + const ext = call.object.get("ext").?.string; + const func = call.object.get("func").?.string; + + // the storages, sized by their recorded bytes + const n: usize = @intCast(try fx.int("storages")); + const storages = try a.alloc(Storage, n); + var made: usize = 0; + defer for (storages[0..made]) |*s| if (s.buf) |*b| b.free(); + const arrays = fx.parsed.value.object.get("arrays").?.object; + for (storages, 0..) |*s, i| { + s.name = try std.fmt.allocPrint(a, "s{d}", .{i}); + const ent = arrays.get(try std.fmt.allocPrint(a, "s{d}_before", .{i})).?.object; + s.len = @intCast(ent.get("shape").?.array.items[0].integer); + s.buf = if (s.len == 0) null else try cuda.DeviceBuffer.alloc(gpu.d, s.len); + made += 1; + } + var c: Ctx = .{ .gpa = gpu.gpa, .a = a, .storages = storages }; + defer { + for (c.extra.items) |*b| b.free(); + c.extra.deinit(gpu.gpa); + } + const jargs = call.object.get("args").?.array.items; + const args = try a.alloc(replay.Arg, jargs.len); + for (jargs, args) |v, *o| o.* = try c.arg(v); + var kws: std.ArrayList(replay.Kw) = .empty; + var it = call.object.get("kwargs").?.object.iterator(); + while (it.next()) |e| try kws.append(a, .{ .name = e.key_ptr.*, .value = try c.arg(e.value_ptr.*) }); + + var stream = try cuda.Stream.init(gpu.d, false); // blocking: ordered after the uploads on the default stream + defer stream.deinit(); + const scratch_buf = try cuda.DeviceBuffer.alloc(gpu.d, 64 << 20); + var scratch_owned = scratch_buf; + defer scratch_owned.free(); + var scratch: replay.Scratch = .{ .ptr = scratch_buf.ptr, .len = scratch_buf.len }; + + try upload(gpu, fx, storages); + if (!try replay.call(k, stream, ext, func, args, kws.items, &scratch)) return .skipped; + try stream.synchronize(); + try compare(gpu, fx, storages, func, null); + + var variants: usize = 0; + // R1 variants of a pre-R1 call (the twin's kernel, today's launch): the same bits by contract + if (std.mem.eql(u8, ext, "tf_dsv41_attn_cuda_v1") and std.mem.eql(u8, func, "attn") and args.len == 23) { + const ticket_name = storageOf(jargs[18]).?; + const q = args[0].tensor; + const hb: usize = @intCast(@divTrunc(q.shape[1], 16)); + for ([_]i64{ 1, 4 }) |p| { + const v = try a.alloc(replay.Arg, 24); + @memcpy(v[0..23], args); + var t = args[18].tensor; + t.ptr = try c.zeros(gpu, @as(usize, @intCast(q.shape[0])) * hb * dsv41.ops.attn.MAXP * 4); + v[18] = .{ .tensor = t }; + v[23] = .{ .int = p }; + try upload(gpu, fx, storages); + if (!try replay.call(k, stream, ext, func, v, kws.items, &scratch)) return error.VariantNotBound; + try stream.synchronize(); + try compare(gpu, fx, storages, if (p == 1) "attn split 1" else "attn split 4", ticket_name); + variants += 1; + } + } + if (std.mem.eql(u8, ext, "tf_dsv41_mhc_cuda_v1") and std.mem.eql(u8, func, "run") and args.len == 24) { + const cnt_name = storageOf(jargs[17]).?; + const mode = args[19].int; + const opre = args[14].tensor; + var opre_n: i64 = 1; + for (opre.shape) |d| opre_n *= d; + // the deferred form only where the boundary computes the next site's coefficients (MIX: modes 0-2) + const forms: []const bool = if (mode <= 2 and opre_n > 0) &.{ false, true } else &.{false}; + for (forms) |deferred| { + const v = try a.alloc(replay.Arg, 26); + @memcpy(v[0..24], args); + var t = args[17].tensor; + t.ptr = try c.zeros(gpu, 16); // the tail's 3 self-resetting ints + v[17] = .{ .tensor = t }; + v[24] = .{ .int = 0 }; // spin 0: the parallel tail, no waiting + v[25] = .{ .int = @intFromBool(deferred) }; + try upload(gpu, fx, storages); + if (!try replay.call(k, stream, ext, func, v, kws.items, &scratch)) return error.VariantNotBound; + if (deferred) { + // coef_kernel: (part, base, scale, opre, opost, ocomb, R, eps, hc_eps, post_alpha, iters) + const cv = [_]replay.Arg{ args[9], args[7], args[8], args[14], args[15], args[16], args[18], args[20], args[21], args[22], args[23] }; + if (!try replay.call(k, stream, ext, "coef", &cv, &.{}, &scratch)) return error.VariantNotBound; + } + try stream.synchronize(); + try compare(gpu, fx, storages, if (deferred) "mhc tail + defer" else "mhc tail", cnt_name); + variants += 1; + } + } + const test_name = if (call.object.get("test")) |t| t.string else ""; + check.pass("BITEXACT replay {s}.{s} ({d} storages, {d} R1 variants) from {s}", .{ ext, func, n, variants, test_name }); + return .exact; +} diff --git a/zig/tests/cuda/dsv41_main.zig b/zig/tests/cuda/dsv41_main.zig new file mode 100644 index 0000000000..c6fd5f92cc --- /dev/null +++ b/zig/tests/cuda/dsv41_main.zig @@ -0,0 +1,135 @@ +//! GPU test runner for DeepSeek-V4.1's kernels: `tf-dsv41-test [fixture dir]`, bit-exact against oracle.py. + +const std = @import("std"); +const cuda = @import("cuda"); +const dsv41 = @import("dsv41_kernels"); +const check = @import("check.zig"); +const experts = @import("deepseek_v41/experts.zig"); +const replay_case = @import("deepseek_v41/replay.zig"); +const kvglue = @import("deepseek_v41/kv_glue.zig"); +const kvsplit = @import("deepseek_v41/kvsplit.zig"); +const mhcdec = @import("deepseek_v41/mhcdec.zig"); +const mhc_probe = @import("deepseek_v41/mhc_probe.zig"); + +const usage = + \\usage: tf-dsv41-test [fixture dir] + \\ symbols load every DeepSeek-V4.1 fatbin and resolve every instance + \\ experts decode chain: group, rot_in, x3ld (every setting) / upstream grouped / x3pf, epilogues, combine + \\ x3gm fast prefill: rot, gate/up and down at every configuration (uniform and ragged), combine + \\ replay one recorded Python extension call (dsv41_capture.py) re-issued and compared, storage by storage + \\ kvglue fused KV RMS/SWA store against the reference Triton bytes + \\ kvsplit split KV's exchange copies against kv/split.zig's host reference (no fixture) + \\ mhc-probe [windows] the 16-row mHC boundary under cache states, buffer layouts and a cross-stream dependency + \\ all every case directory under by its manifest's "kind" + \\ +; + +pub fn main(init: std.process.Init) !u8 { + const args = try init.minimal.args.toSlice(init.arena.allocator()); + if (args.len < 2) { + std.debug.print("{s}", .{usage}); + return 2; + } + var driver = try cuda.Driver.open(); + defer driver.close(); + var ctx = try cuda.Context.init(&driver, 0); + defer ctx.deinit(); + const gpu: check.Gpu = .{ .d = &driver, .ctx = &ctx, .gpa = init.gpa, .io = init.io }; + run(gpu, args[1], args[2..]) catch |e| { + std.debug.print("FAIL {s}: {t}\n", .{ args[1], e }); + return 1; + }; + return 0; +} + +fn arg(rest: []const [:0]const u8, i: usize) ![]const u8 { + if (i >= rest.len) { + std.debug.print("{s}", .{usage}); + return error.MissingArgument; + } + return rest[i]; +} + +fn run(gpu: check.Gpu, cmd: []const u8, rest: []const [:0]const u8) !void { + var k = try dsv41.Kernels.load(gpu.ctx); + defer k.deinit(); + if (std.mem.eql(u8, cmd, "symbols")) { + check.pass("dsv41 symbols: every instance resolved ({d} SMs)", .{k.sms}); + return; + } + if (std.mem.eql(u8, cmd, "experts")) return experts.decodeChain(gpu, &k, try arg(rest, 0)); + if (std.mem.eql(u8, cmd, "x3gm")) return experts.x3gm(gpu, &k, try arg(rest, 0)); + if (std.mem.eql(u8, cmd, "replay")) { + if (try replay_case.run(gpu, &k, try arg(rest, 0)) == .skipped) std.debug.print("SKIP no binding\n", .{}); + return; + } + if (std.mem.eql(u8, cmd, "kvsplit")) return kvsplit.run(gpu, &k); + if (std.mem.eql(u8, cmd, "kvglue")) return kvglue.run(gpu, &k, try arg(rest, 0)); + if (std.mem.eql(u8, cmd, "mhc-probe")) return mhc_probe.run(gpu, &k, if (rest.len > 0) rest[0] else null); + if (std.mem.eql(u8, cmd, "all")) return all(gpu, &k, try arg(rest, 0)); + std.debug.print("{s}", .{usage}); + return error.UnknownCommand; +} + +/// Every case under `root` (one directory a case, its manifest's "kind" naming the check); fails if any fails. +fn all(gpu: check.Gpu, k: *const dsv41.Kernels, root: []const u8) !void { + var dir = try std.Io.Dir.cwd().openDir(gpu.io, root, .{ .iterate = true }); + defer dir.close(gpu.io); + var names: std.ArrayList([]u8) = .empty; + defer { + for (names.items) |n| gpu.gpa.free(n); + names.deinit(gpu.gpa); + } + var it = dir.iterate(); + while (try it.next(gpu.io)) |e| { + if (e.kind == .directory) try names.append(gpu.gpa, try gpu.gpa.dupe(u8, e.name)); + } + std.mem.sort([]u8, names.items, {}, struct { + fn lt(_: void, a: []u8, b: []u8) bool { + return std.mem.lessThan(u8, a, b); + } + }.lt); + var failed: usize = 0; + var skipped: usize = 0; + for (names.items) |n| { + const path = try std.fs.path.join(gpu.gpa, &.{ root, n }); + defer gpu.gpa.free(path); + const kind = experts.caseKind(gpu, path) catch |e| { + std.debug.print("FAIL {s}: no manifest ({t})\n", .{ n, e }); + failed += 1; + continue; + }; + const r = if (std.mem.eql(u8, kind.slice(), "experts")) + experts.decodeChain(gpu, k, path) + else if (std.mem.eql(u8, kind.slice(), "x3gm")) + experts.x3gm(gpu, k, path) + else if (std.mem.eql(u8, kind.slice(), "plan")) + experts.planCases(gpu, k, path) + else if (std.mem.eql(u8, kind.slice(), "topk")) + experts.topkCases(gpu, k, path) + else if (std.mem.eql(u8, kind.slice(), "pointwise")) + experts.pointwiseCases(gpu, k, path) + else if (std.mem.eql(u8, kind.slice(), "glue")) + experts.glueCases(gpu, k, path) + else if (std.mem.eql(u8, kind.slice(), "kvglue")) + kvglue.run(gpu, k, path) + else if (std.mem.eql(u8, kind.slice(), "pfglue")) + experts.pfglueCases(gpu, k, path) + else if (std.mem.eql(u8, kind.slice(), "mhcdec")) + mhcdec.cases(gpu, k, path) + else if (std.mem.eql(u8, kind.slice(), "replay")) blk: { + const o = replay_case.run(gpu, k, path) catch |e| break :blk e; + if (o == .skipped) { + std.debug.print("SKIP {s}: no Zig binding\n", .{n}); + skipped += 1; + } + break :blk {}; + } else error.UnknownKind; + r catch |e| { + std.debug.print("FAIL {s}: {t}\n", .{ n, e }); + failed += 1; + }; + } + std.debug.print("RESULT {d} cases, {d} failed, {d} skipped (no binding)\n", .{ names.items.len, failed, skipped }); + if (failed > 0) return error.TestFailed; +}