// tests/program/split_walk_test.cpp - the layer split's spill walk (include/strata/program/split_walk.hpp): who is short, who is not, and what is given up in which order. #include "strata/program/split_walk.hpp" #include #include #include using namespace strata::program::split_walk; namespace { int g_fail = 0; void check(bool ok, const std::string& what) { if (ok) return; std::fprintf(stderr, "FAIL: %s\n", what.c_str()); ++g_fail; } // IQ3_S on Qwen3.8-Flash-Next, 128K, int8 KV, window 32768, MTP: the figures the 8 GB start log gave (3485 MiB dense set, 835 MiB draft layer, 410 MiB head) Model iq3s() { Model m; m.dense_mib = 1466 + 2019; m.session_mib = 400; m.head_mib = 410; m.drafter_mib = 1000; m.slot_mib = 3; m.reserve_mib = 796; return m; } } // namespace int main() { const Model m = iq3s(); // 1. the trim: 8 GB cards are short, 12 GB and bigger are not (a big card never reaches the walk) check(trim_needed(7635, m, true), "an 8 GB card holding the head and the drafter is short"); check(trim_needed(7635, m, false), "an 8 GB card without them is short too"); check(!trim_needed(12288, m, true), "a 12 GB card is not short"); check(!trim_needed(16384, m, true), "a 16 GB card is not short"); check(!trim_needed(97000, m, true), "a 96 GB card is never short"); check(!trim_needed(0, m, true), "an unreadable card (0 free) decides nothing"); check(untrimmed_need_mib(m, true) > untrimmed_need_mib(m, false), "the last stage needs more"); // monotone in the card: the threshold is one number int64_t edge = 0; for (int64_t f = 1000; f < 40000; ++f) if (!trim_needed(f, m, true)) { edge = f; break; } check(edge == untrimmed_need_mib(m, true), "the threshold is the need"); for (int64_t f = edge; f < edge + 5000; f += 97) check(!trim_needed(f, m, true), "never short above the threshold"); // a model with a smaller dense set (a smaller quant) is short later Model small = m; small.dense_mib = 1500; check(!trim_needed(7635, small, false), "a small dense set fits an 8 GB card untrimmed"); // 2. fewer cards check(!card_usable(2048), "a card with 2 GB free leaves the split"); check(card_usable(4250), "a 6 GB card (4.15 GiB free) stays: the other steps make it fit"); check(card_usable(7635), "an 8 GB card stays"); check(card_usable(kMinCardMib) && !card_usable(kMinCardMib - 1), "the edge"); // 3. the draft layer: the single card's arithmetic, unchanged const uint64_t layer = 835ull << 20, head = 410ull << 20; check(draft_layer_need_bytes(layer, head) == layer + head + (220ull << 20) + (1550ull << 20), "the single card's need"); check(draft_layer_short(1830ull << 20, layer, head), "1.8 GiB free: the layer goes"); check(!draft_layer_short(3640ull << 20, layer, head), "3.6 GiB free (trimmed 8 GB card): the layer stays"); check(!draft_layer_short(0, layer, head), "0 free is an unreadable figure"); check(dflash2_need_bytes(715ull << 20, 213ull << 20) < draft_layer_need_bytes(layer, head), "DFlash2 needs less than the draft layer"); // 4. the KV window walks 32768 -> 20480 -> 8192 -> 4096 -> stop, and never goes past the context or the engine's minimum check(next_kv_window(32768, 131072, 4096) == 20480, "32768 -> 20480"); check(next_kv_window(20480, 131072, 4096) == 8192, "20480 -> 8192"); check(next_kv_window(8192, 131072, 4096) == 4096, "8192 -> 4096"); check(next_kv_window(4096, 131072, 4096) == 0, "4096 is the last"); check(next_kv_window(0, 131072, 4096) == 20480, "the whole context in VRAM goes to 20480"); check(next_kv_window(0, 16384, 4096) == 8192, "a context of 16K has no 20480 step"); check(next_kv_window(0, 4096, 4096) == 0, "a context at the minimum has none"); check(next_kv_window(8192, 131072, 8192) == 0, "a larger engine minimum stops the walk"); // 5. the shares of the layers int64_t fm[2] = {5000, 5000}, out[2] = {0, 0}; proportional_layers(fm, 2, 48, out); check(out[0] == 24 && out[1] == 24, "two equal rooms share 48 layers 24/24"); int64_t fm1[2] = {5285, 3875}; proportional_layers(fm1, 2, 48, out); check(out[0] + out[1] == 48 && out[0] == 28 && out[1] == 20, "the card that carries the head and the drafter takes fewer layers (28/20)"); int64_t fm2[2] = {9000, 5000}; proportional_layers(fm2, 2, 48, out); check(out[0] + out[1] == 48 && out[0] > out[1], "the bigger room takes more layers"); int64_t fm3[3] = {5000, 5000, 5000}, out3[3]; proportional_layers(fm3, 3, 48, out3); check(out3[0] == 16 && out3[1] == 16 && out3[2] == 16, "three equal rooms 16/16/16"); int64_t fm4[2] = {90000, 100}, out4[2]; proportional_layers(fm4, 2, 48, out4); check(out4[0] + out4[1] == 48 && out4[0] >= 2 && out4[1] >= 0, "a tiny room leaves the sum right"); int64_t fm5[2] = {0, 0}, out5[2]; proportional_layers(fm5, 2, 48, out5); check(out5[0] + out5[1] == 48, "rooms of 0 still sum to the layers"); // 6. fewer cards: feasible when the rooms plus the relief pay for every layer int64_t r8[2] = {5200, 5200}; check(split_feasible(r8, 2, 48, 90, 1410), "two 8 GB cards (5.2 GiB of room each) hold the model"); int64_t r6[2] = {1744, 1552}; check(split_feasible(r6, 2, 48, 87, 1410), "two 6 GB cards hold it only with the drafter and the head moved off"); check(!split_feasible(r6, 2, 48, 87, 0), "...and not without that relief"); int64_t r3[2] = {2000, 100}; check(!split_feasible(r3, 2, 48, 90, 1410), "two cards with 2 GB and 0.1 GB of room do not: fewer cards"); if (g_fail == 0) std::printf("split_walk_test: ok\n"); return g_fail == 0 ? 0 : 1; }