| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #define DS4_TEST_HOOKS |
| #include "../ds4.h" |
| #include "../ds4_gpu_mgpu.h" |
| #include "../ds4_layer_pack.h" |
|
|
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdint.h> |
|
|
| |
| typedef struct { |
| const char *name; |
| uint64_t bytes; |
| } ds4_test_fake_tensor; |
|
|
| int ds4_test_classify_multi_tier(const ds4_test_fake_tensor *tensors, |
| int n_tensors, |
| const ds4_gpu_config *cfg, |
| int placement_out[], |
| int *out_multi_tier, |
| int *out_n_entries); |
| int ds4_test_tensor_to_entry(const char *name, int name_len); |
|
|
| |
| |
| int ds4_test_classify_multi_tier_with_ctx(const ds4_test_fake_tensor *tensors, |
| int n_tensors, |
| const ds4_gpu_config *cfg, |
| int placement_ctx_hint, |
| int placement_out[], |
| int *out_multi_tier, |
| int *out_n_entries); |
| int ds4_test_classify_multi_tier_with_ctx_cuda_tp( |
| const ds4_test_fake_tensor *tensors, |
| int n_tensors, |
| const ds4_gpu_config *cfg, |
| int placement_ctx_hint, |
| int placement_out[], |
| int *out_multi_tier, |
| int *out_n_entries); |
| void ds4_test_seed_compress_ratios(void); |
| void ds4_test_clear_compress_ratios(void); |
| size_t ds4_test_per_tier_graph_overhead_bytes(int placement_ctx_hint); |
| size_t ds4_test_per_tier_graph_overhead_bytes_with_prefill( |
| int placement_ctx_hint, |
| uint32_t prefill_chunk); |
| size_t ds4_test_compute_entry_bytes_sum(const ds4_test_fake_tensor *tensors, |
| int n_tensors, |
| int placement_ctx_hint); |
| size_t ds4_test_compute_entry_bytes_sum_with_prefill( |
| const ds4_test_fake_tensor *tensors, |
| int n_tensors, |
| int placement_ctx_hint, |
| uint32_t prefill_chunk); |
| uint32_t ds4_test_effective_prefill_chunk(bool cuda_tensor_parallel, |
| uint32_t requested_chunk); |
| uint32_t ds4_test_planner_prefill_cap(int prompt_len, |
| uint32_t prefill_chunk); |
| uint32_t ds4_test_planner_raw_cap(int ctx_size, uint32_t prefill_cap); |
| size_t ds4_test_glm_per_layer_kv_bytes(uint32_t layer, int ctx_size); |
|
|
| |
| |
| #define DS4_N_LAYER_LOCAL 43 |
| #define DS4_N_VOCAB_LOCAL 129280 |
| #define DS4_N_ENTRIES (DS4_N_LAYER_LOCAL + 2) |
|
|
| static int g_failures = 0; |
| static int g_checks = 0; |
|
|
| #define CHECK(cond, msg) do { \ |
| g_checks++; \ |
| if (!(cond)) { \ |
| fprintf(stderr, " FAIL: %s (line %d)\n", msg, __LINE__); \ |
| g_failures++; \ |
| } \ |
| } while (0) |
|
|
| static void test_tensor_to_entry(void) { |
| fprintf(stderr, "RUN: test_tensor_to_entry\n"); |
| |
| char buf[64]; |
|
|
| |
| memcpy(buf, "blk.0.attn_norm.weight", 22); |
| CHECK(ds4_test_tensor_to_entry(buf, 22) == 1, "blk.0.* -> entry 1"); |
|
|
| |
| memcpy(buf, "blk.42.ffn_norm.weight", 22); |
| CHECK(ds4_test_tensor_to_entry(buf, 22) == 43, "blk.42.* -> entry 43"); |
|
|
| |
| memcpy(buf, "blk.43.x", 8); |
| CHECK(ds4_test_tensor_to_entry(buf, 8) == 0, "blk.43.* out of range"); |
|
|
| |
| memcpy(buf, "output.weight", 13); |
| CHECK(ds4_test_tensor_to_entry(buf, 13) == 44, "output.weight -> entry 44"); |
|
|
| |
| memcpy(buf, "output_norm.weight", 18); |
| CHECK(ds4_test_tensor_to_entry(buf, 18) == 44, "output_norm.weight -> entry 44"); |
|
|
| |
| memcpy(buf, "token_embd.weight", 17); |
| CHECK(ds4_test_tensor_to_entry(buf, 17) == 0, "token_embd.weight -> entry 0"); |
|
|
| |
| memcpy(buf, "mtp.0.foo", 9); |
| CHECK(ds4_test_tensor_to_entry(buf, 9) == 44, "mtp.* -> head"); |
|
|
| |
| |
| |
| memcpy(buf, "output_hc_base.weight", 21); |
| CHECK(ds4_test_tensor_to_entry(buf, 21) == 44, "output_hc_base.weight -> head"); |
| memcpy(buf, "output_hc_fn.weight", 19); |
| CHECK(ds4_test_tensor_to_entry(buf, 19) == 44, "output_hc_fn.weight -> head"); |
| memcpy(buf, "output_hc_scale.weight", 22); |
| CHECK(ds4_test_tensor_to_entry(buf, 22) == 44, "output_hc_scale.weight -> head"); |
| |
| memcpy(buf, "output.weight", 13); |
| CHECK(ds4_test_tensor_to_entry(buf, 13) == 44, "output.weight -> head"); |
| memcpy(buf, "output_norm.weight", 18); |
| CHECK(ds4_test_tensor_to_entry(buf, 18) == 44, "output_norm.weight -> head"); |
| |
| memcpy(buf, "token_embd.weight", 17); |
| CHECK(ds4_test_tensor_to_entry(buf, 17) == 0, "token_embd.weight -> embedding"); |
|
|
| |
| const char with_trailing[] = "blk.5.attn_norm.weightTRAILINGGARBAGE"; |
| CHECK(ds4_test_tensor_to_entry(with_trailing, 22) == 6, |
| "bounded parsing ignores trailing bytes"); |
|
|
| |
| CHECK(ds4_test_tensor_to_entry("", 0) == 0, "empty name -> entry 0"); |
| } |
|
|
| static void test_null_config(void) { |
| fprintf(stderr, "RUN: test_null_config\n"); |
| int placement[DS4_N_ENTRIES]; |
| int multi_tier = 99; |
| int n_entries = 99; |
|
|
| |
| ds4_test_fake_tensor tensors[] = { |
| {"token_embd.weight", 4096}, |
| {"output.weight", 4096}, |
| }; |
| int rc = ds4_test_classify_multi_tier(tensors, |
| (int)(sizeof(tensors)/sizeof(tensors[0])), |
| NULL, |
| placement, &multi_tier, &n_entries); |
| CHECK(rc == 0, "NULL cfg returns success"); |
| CHECK(multi_tier == 0, "NULL cfg -> multi_tier 0"); |
| CHECK(n_entries == 0, "NULL cfg -> n_entries 0"); |
| } |
|
|
| |
| |
| |
| static int build_synthetic_model(ds4_test_fake_tensor *out, int cap) { |
| int n = 0; |
| static char names[1024][32]; |
|
|
| |
| snprintf(names[n], 32, "token_embd.weight"); |
| out[n].name = names[n]; out[n].bytes = (uint64_t)8ull * 1024 * 1024; |
| n++; |
|
|
| |
| for (int il = 0; il < DS4_N_LAYER_LOCAL; il++) { |
| snprintf(names[n], 32, "blk.%d.attn_q.weight", il); |
| out[n].name = names[n]; out[n].bytes = (uint64_t)256ull * 1024 * 1024; |
| n++; |
| snprintf(names[n], 32, "blk.%d.ffn_down.weight", il); |
| out[n].name = names[n]; out[n].bytes = (uint64_t)768ull * 1024 * 1024; |
| n++; |
| if (n + 2 > cap) return -1; |
| } |
|
|
| |
| snprintf(names[n], 32, "output.weight"); |
| out[n].name = names[n]; out[n].bytes = (uint64_t)16ull * 1024 * 1024; |
| n++; |
| snprintf(names[n], 32, "output_norm.weight"); |
| out[n].name = names[n]; out[n].bytes = (uint64_t)1ull * 1024 * 1024; |
| n++; |
| return n; |
| } |
|
|
| static void test_forced_two_tier_no_spill(void) { |
| fprintf(stderr, "RUN: test_forced_two_tier_no_spill\n"); |
| ds4_test_fake_tensor tensors[256]; |
| int n = build_synthetic_model(tensors, 256); |
| CHECK(n > 0, "synthetic model built"); |
| if (n <= 0) return; |
|
|
| |
| |
| |
| |
| |
| |
| ds4_gpu_config cfg; |
| memset(&cfg, 0, sizeof(cfg)); |
| cfg.n_gpus = 2; |
| cfg.device_indices[0] = 0; |
| cfg.device_indices[1] = 1; |
| |
| |
| |
| |
| cfg.vram_bytes[0] = (size_t)28ull * 1024ull * 1024ull * 1024ull; |
| cfg.vram_bytes[1] = (size_t)40ull * 1024ull * 1024ull * 1024ull; |
| cfg.safety_margin_bytes = 0; |
|
|
| int placement[DS4_N_ENTRIES]; |
| int multi_tier = 0; |
| int n_entries = 0; |
| int rc = ds4_test_classify_multi_tier(tensors, n, &cfg, |
| placement, &multi_tier, &n_entries); |
| CHECK(rc == 0, "classify succeeded"); |
| CHECK(n_entries == DS4_N_ENTRIES, "n_entries == DS4_N_LAYER + 2"); |
| CHECK(multi_tier == 1, "multi_tier set"); |
|
|
| |
| |
| |
| int prev = placement[0]; |
| int saw_0 = 0, saw_1 = 0, saw_cpu = 0; |
| for (int i = 0; i < n_entries; i++) { |
| int cur = placement[i]; |
| CHECK(cur == prev || cur > prev || cur == DS4_LAYER_PACK_CPU, |
| "monotonic (cur >= prev or CPU)"); |
| if (cur == 0) saw_0 = 1; |
| else if (cur == 1) saw_1 = 1; |
| else if (cur == DS4_LAYER_PACK_CPU) saw_cpu = 1; |
| prev = cur; |
| } |
| CHECK(saw_0 && saw_1, "both tiers used"); |
| CHECK(!saw_cpu, "no CPU spill for this budget"); |
| } |
|
|
| static void test_cpu_spill(void) { |
| fprintf(stderr, "RUN: test_cpu_spill\n"); |
| ds4_test_fake_tensor tensors[256]; |
| int n = build_synthetic_model(tensors, 256); |
| if (n <= 0) return; |
|
|
| ds4_gpu_config cfg; |
| memset(&cfg, 0, sizeof(cfg)); |
| cfg.n_gpus = 2; |
| cfg.device_indices[0] = 0; |
| cfg.device_indices[1] = 1; |
| |
| |
| cfg.vram_bytes[0] = (size_t)5ull * 1024ull * 1024ull * 1024ull; |
| cfg.vram_bytes[1] = (size_t)5ull * 1024ull * 1024ull * 1024ull; |
|
|
| int placement[DS4_N_ENTRIES]; |
| int multi_tier = 0; |
| int n_entries = 0; |
| int rc = ds4_test_classify_multi_tier(tensors, n, &cfg, |
| placement, &multi_tier, &n_entries); |
| CHECK(rc == 0, "classify succeeded"); |
| CHECK(multi_tier == 1, "multi_tier set with CPU spill"); |
| int any_cpu = 0; |
| for (int i = 0; i < n_entries; i++) { |
| if (placement[i] == DS4_LAYER_PACK_CPU) { any_cpu = 1; break; } |
| } |
| CHECK(any_cpu, "at least one CPU spill entry"); |
| } |
|
|
| static void test_zero_budget_guard(void) { |
| fprintf(stderr, "RUN: test_zero_budget_guard\n"); |
| ds4_test_fake_tensor tensors[256]; |
| int n = build_synthetic_model(tensors, 256); |
| if (n <= 0) return; |
|
|
| |
| |
| |
| ds4_gpu_config cfg; |
| memset(&cfg, 0, sizeof(cfg)); |
| cfg.n_gpus = 2; |
| cfg.device_indices[0] = 0; |
| cfg.device_indices[1] = 1; |
| |
|
|
| int placement[DS4_N_ENTRIES]; |
| int multi_tier = 0; |
| int n_entries = 0; |
| int rc = ds4_test_classify_multi_tier(tensors, n, &cfg, |
| placement, &multi_tier, &n_entries); |
| CHECK(rc != 0, "classify rejects all-zero vram_bytes"); |
| } |
|
|
| |
| |
| |
| static void test_placement_ctx_hint_scales(void) { |
| fprintf(stderr, "RUN: test_placement_ctx_hint_scales\n"); |
| ds4_test_fake_tensor tensors[256]; |
| int n = build_synthetic_model(tensors, 256); |
| if (n <= 0) return; |
|
|
| |
| |
| |
| |
| ds4_test_seed_compress_ratios(); |
|
|
| |
| |
| ds4_gpu_config cfg; |
| memset(&cfg, 0, sizeof(cfg)); |
| cfg.n_gpus = 2; |
| cfg.device_indices[0] = 0; |
| cfg.device_indices[1] = 1; |
| cfg.vram_bytes[0] = (size_t)24ull * 1024ull * 1024ull * 1024ull; |
| cfg.vram_bytes[1] = (size_t)24ull * 1024ull * 1024ull * 1024ull; |
| cfg.safety_margin_bytes = 0; |
|
|
| int placement_small[DS4_N_ENTRIES] = {0}; |
| int placement_big[DS4_N_ENTRIES] = {0}; |
| int mt_small = 0, mt_big = 0; |
| int ne_small = 0, ne_big = 0; |
|
|
| int rc_s = ds4_test_classify_multi_tier_with_ctx( |
| tensors, n, &cfg, 4096, placement_small, &mt_small, &ne_small); |
| CHECK(rc_s == 0, "ctx=4096 classify ok"); |
| int spill_s = 0; |
| for (int i = 0; i < ne_small; i++) |
| if (placement_small[i] == DS4_LAYER_PACK_CPU) spill_s++; |
|
|
| int rc_b = ds4_test_classify_multi_tier_with_ctx( |
| tensors, n, &cfg, 131072, placement_big, &mt_big, &ne_big); |
| |
| int spill_b = 0; |
| for (int i = 0; i < ne_big; i++) |
| if (placement_big[i] == DS4_LAYER_PACK_CPU) spill_b++; |
|
|
| |
| |
| CHECK(rc_b != 0 || spill_b > spill_s, |
| "placement_ctx_hint plumbs through to per-layer KV / per-tier " |
| "overhead — larger ctx forces more spill (or refusal)."); |
|
|
| ds4_test_clear_compress_ratios(); |
| } |
|
|
| |
| |
| |
| |
| static void test_pertier_overhead_pushes_to_spill(void) { |
| fprintf(stderr, "RUN: test_pertier_overhead_pushes_to_spill\n"); |
| ds4_test_fake_tensor tensors[256]; |
| int n = build_synthetic_model(tensors, 256); |
| if (n <= 0) return; |
|
|
| |
| |
| ds4_test_seed_compress_ratios(); |
|
|
| |
| |
| const size_t entry_sum = ds4_test_compute_entry_bytes_sum(tensors, n, 4096); |
| const size_t overhead = ds4_test_per_tier_graph_overhead_bytes(4096); |
| CHECK(entry_sum > 0, "planner entry-bytes sum > 0"); |
| CHECK(overhead > 0, "per-tier overhead > 0 with seeded compress ratios"); |
|
|
| |
| |
| |
| |
| |
| const size_t cublas_workspace = (size_t)64ull * 1024ull * 1024ull; |
| const size_t headroom = overhead * 6 / 10; |
| const size_t budget = entry_sum + cublas_workspace + headroom; |
|
|
| ds4_gpu_config cfg; |
| memset(&cfg, 0, sizeof(cfg)); |
| cfg.n_gpus = 1; |
| cfg.device_indices[0] = 0; |
| cfg.vram_bytes[0] = budget; |
| cfg.safety_margin_bytes = 0; |
|
|
| int placement[DS4_N_ENTRIES] = {0}; |
| int multi_tier = 0; |
| int n_entries = 0; |
| int rc = ds4_test_classify_multi_tier(tensors, n, &cfg, |
| placement, &multi_tier, &n_entries); |
|
|
| if (rc == 0) { |
| int any_cpu = 0; |
| for (int i = 0; i < n_entries; i++) { |
| if (placement[i] == DS4_LAYER_PACK_CPU) { any_cpu = 1; break; } |
| } |
| CHECK(any_cpu, |
| "per-tier overhead pre-subtract pushes layout to CPU spill"); |
| } else { |
| CHECK(rc == -1, |
| "per-tier overhead pre-subtract refuses upfront (budget < overhead)"); |
| } |
|
|
| |
| |
| |
| cfg.vram_bytes[0] = entry_sum + cublas_workspace + overhead * 3 / 2; |
| int placement2[DS4_N_ENTRIES] = {0}; |
| int mt2 = 0, ne2 = 0; |
| int rc2 = ds4_test_classify_multi_tier(tensors, n, &cfg, |
| placement2, &mt2, &ne2); |
| CHECK(rc2 == 0, "1.5x-overhead budget classify ok"); |
| int spill2 = 0; |
| for (int i = 0; i < ne2; i++) |
| if (placement2[i] == DS4_LAYER_PACK_CPU) spill2++; |
| CHECK(spill2 == 0, |
| "1.5x-overhead budget fits without CPU spill (control)"); |
|
|
| ds4_test_clear_compress_ratios(); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| static void test_no_per_layer_scratch_double_count(void) { |
| fprintf(stderr, "RUN: test_no_per_layer_scratch_double_count\n"); |
| ds4_test_fake_tensor tensors[256]; |
| int n = build_synthetic_model(tensors, 256); |
| if (n <= 0) return; |
|
|
| ds4_test_seed_compress_ratios(); |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| const size_t small = ds4_test_compute_entry_bytes_sum(tensors, n, 4096); |
| const size_t large = ds4_test_compute_entry_bytes_sum(tensors, n, 65536); |
| const size_t delta = large > small ? large - small : 0; |
| const size_t bound = (size_t)5ull * 1024ull * 1024ull * 1024ull; |
| CHECK(delta < bound, |
| "per-layer entry-bytes delta 4096->65536 is KV-only (no scratch double-count)"); |
|
|
| ds4_test_clear_compress_ratios(); |
| } |
|
|
| static void test_glm_per_layer_cache_accounting(void) { |
| fprintf(stderr, "RUN: test_glm_per_layer_cache_accounting\n"); |
| const uint64_t ctx = 100000u; |
| #if defined(__APPLE__) |
| const uint64_t elem_bytes = sizeof(uint16_t); |
| #else |
| const uint64_t elem_bytes = sizeof(float); |
| #endif |
| const size_t base = |
| (size_t)(ctx * (512u + 64u) * elem_bytes); |
| const size_t indexed = |
| (size_t)(ctx * (512u + 64u + 128u) * elem_bytes); |
|
|
| CHECK(ds4_test_glm_per_layer_kv_bytes(4, (int)ctx) == base, |
| "GLM normal layer includes compact KV and RoPE cache"); |
| CHECK(ds4_test_glm_per_layer_kv_bytes(6, (int)ctx) == indexed, |
| "GLM indexed layer also includes compact indexer cache"); |
| CHECK(ds4_test_glm_per_layer_kv_bytes(78, (int)ctx) == 0, |
| "GLM NextN layer has no generation cache"); |
| } |
|
|
| static char *save_env_value(const char *name) { |
| const char *v = getenv(name); |
| if (!v) return NULL; |
| size_t n = strlen(v) + 1; |
| char *copy = malloc(n); |
| if (copy) memcpy(copy, v, n); |
| return copy; |
| } |
|
|
| static void restore_env_value(const char *name, char *saved) { |
| if (saved) { |
| setenv(name, saved, 1); |
| free(saved); |
| } else { |
| unsetenv(name); |
| } |
| } |
|
|
| static void test_cuda_tp_prefill_default_accounting(void) { |
| fprintf(stderr, "RUN: test_cuda_tp_prefill_default_accounting\n"); |
|
|
| CHECK(ds4_test_effective_prefill_chunk(true, 0) == 2048, |
| "CUDA TP defaults to a 2048-token prefill chunk"); |
| CHECK(ds4_test_effective_prefill_chunk(true, 4096) == 4096, |
| "CUDA TP preserves an explicit prefill chunk"); |
| CHECK(ds4_test_effective_prefill_chunk(false, 0) == 0, |
| "ordinary inference retains its model-specific default"); |
|
|
| ds4_test_fake_tensor tensors[256]; |
| const int n = build_synthetic_model(tensors, 256); |
| if (n <= 0) return; |
|
|
| char *old_chunk = save_env_value("DS4_METAL_PREFILL_CHUNK"); |
| char *old_raw = save_env_value("DS4_METAL_GRAPH_RAW_CAP"); |
| unsetenv("DS4_METAL_PREFILL_CHUNK"); |
| unsetenv("DS4_METAL_GRAPH_RAW_CAP"); |
| ds4_test_seed_compress_ratios(); |
|
|
| const uint32_t ordinary_prefill = |
| ds4_test_planner_prefill_cap(100000, 0); |
| const uint32_t cuda_tp_prefill = |
| ds4_test_planner_prefill_cap(100000, 2048); |
| CHECK(ordinary_prefill == 4096, |
| "ordinary long-context prefill cap remains 4096"); |
| CHECK(cuda_tp_prefill == 2048, |
| "CUDA TP long-context prefill cap is 2048"); |
| CHECK(ds4_test_planner_raw_cap(100000, cuda_tp_prefill) < |
| ds4_test_planner_raw_cap(100000, ordinary_prefill), |
| "CUDA TP prefill default reduces raw KV allocation"); |
|
|
| const size_t ordinary_entries = |
| ds4_test_compute_entry_bytes_sum_with_prefill(tensors, n, 100000, 0); |
| const size_t cuda_tp_entries = |
| ds4_test_compute_entry_bytes_sum_with_prefill(tensors, n, 100000, 2048); |
| const size_t ordinary_scratch = |
| ds4_test_per_tier_graph_overhead_bytes_with_prefill(100000, 0); |
| const size_t cuda_tp_scratch = |
| ds4_test_per_tier_graph_overhead_bytes_with_prefill(100000, 2048); |
| CHECK(cuda_tp_entries < ordinary_entries, |
| "placement KV accounting uses the effective CUDA TP chunk"); |
| CHECK(cuda_tp_scratch < ordinary_scratch, |
| "placement scratch accounting uses the effective CUDA TP chunk"); |
|
|
| ds4_test_clear_compress_ratios(); |
| restore_env_value("DS4_METAL_PREFILL_CHUNK", old_chunk); |
| restore_env_value("DS4_METAL_GRAPH_RAW_CAP", old_raw); |
| } |
|
|
| static int build_output_tp_head_move_model(ds4_test_fake_tensor *out, int cap) { |
| if (cap < DS4_N_LAYER_LOCAL + 2) return -1; |
| int n = 0; |
| static char names[DS4_N_LAYER_LOCAL + 2][32]; |
| const uint64_t mib = 1024ull * 1024ull; |
|
|
| snprintf(names[n], sizeof(names[n]), "token_embd.weight"); |
| out[n].name = names[n]; |
| out[n].bytes = 1536ull * mib; |
| n++; |
|
|
| for (int il = 0; il < DS4_N_LAYER_LOCAL; il++) { |
| snprintf(names[n], sizeof(names[n]), "blk.%d.ffn_gate_exps.weight", il); |
| out[n].name = names[n]; |
| out[n].bytes = 3550ull * mib; |
| n++; |
| } |
|
|
| snprintf(names[n], sizeof(names[n]), "output.weight"); |
| out[n].name = names[n]; |
| out[n].bytes = ((1536ull * mib) / DS4_N_VOCAB_LOCAL) * DS4_N_VOCAB_LOCAL; |
| n++; |
| return n; |
| } |
|
|
| static void test_cuda_tp_output_head_moves_to_lower_half(void) { |
| fprintf(stderr, "RUN: test_cuda_tp_output_head_moves_to_lower_half\n"); |
| ds4_test_fake_tensor tensors[DS4_N_LAYER_LOCAL + 2]; |
| int n = build_output_tp_head_move_model(tensors, |
| (int)(sizeof(tensors) / sizeof(tensors[0]))); |
| CHECK(n > 0, "output-head synthetic model built"); |
| if (n <= 0) return; |
|
|
| char *old_pipe = save_env_value("DS4_CUDA_PREFILL_PIPELINE"); |
| char *old_chunk = save_env_value("DS4_METAL_PREFILL_CHUNK"); |
| unsetenv("DS4_CUDA_PREFILL_PIPELINE"); |
| unsetenv("DS4_METAL_PREFILL_CHUNK"); |
|
|
| ds4_gpu_config cfg; |
| memset(&cfg, 0, sizeof(cfg)); |
| cfg.n_gpus = 8; |
| for (int i = 0; i < cfg.n_gpus; i++) { |
| cfg.device_indices[i] = i; |
| cfg.vram_bytes[i] = (size_t)42ull * 1024ull * 1024ull * 1024ull; |
| } |
| cfg.safety_margin_bytes = (size_t)512ull * 1024ull * 1024ull; |
|
|
| int placement[DS4_N_ENTRIES] = {0}; |
| int multi_tier = 0; |
| int n_entries = 0; |
| int rc = ds4_test_classify_multi_tier_with_ctx_cuda_tp(tensors, |
| n, |
| &cfg, |
| 4096, |
| placement, |
| &multi_tier, |
| &n_entries); |
| CHECK(rc == 0, "CUDA TP output-head classify succeeds"); |
| CHECK(multi_tier == 1, "CUDA TP output-head model is multi-tier"); |
| CHECK(n_entries == DS4_N_ENTRIES, "CUDA TP output-head n_entries"); |
| const int last_layer_tier = placement[DS4_N_LAYER_LOCAL]; |
| CHECK(last_layer_tier >= 0 && last_layer_tier < cfg.n_gpus, |
| "last layer remains on a GPU tier"); |
| CHECK(placement[DS4_N_LAYER_LOCAL + 1] >= 0 && |
| placement[DS4_N_LAYER_LOCAL + 1] < cfg.n_gpus / 2, |
| "output head moved to a lower-half tier for output TP"); |
|
|
| restore_env_value("DS4_CUDA_PREFILL_PIPELINE", old_pipe); |
| restore_env_value("DS4_METAL_PREFILL_CHUNK", old_chunk); |
| } |
|
|
| int main(void) { |
| test_tensor_to_entry(); |
| test_null_config(); |
| test_forced_two_tier_no_spill(); |
| test_cpu_spill(); |
| test_zero_budget_guard(); |
| test_placement_ctx_hint_scales(); |
| test_pertier_overhead_pushes_to_spill(); |
| test_no_per_layer_scratch_double_count(); |
| test_glm_per_layer_cache_accounting(); |
| test_cuda_tp_prefill_default_accounting(); |
| test_cuda_tp_output_head_moves_to_lower_half(); |
|
|
| fprintf(stderr, "\ntest_engine_mgpu_placement: %d/%d checks passed (%d failed)\n", |
| g_checks - g_failures, g_checks, g_failures); |
| return g_failures == 0 ? 0 : 1; |
| } |
|
|