Download tests/test_engine_mgpu_runtime.c from julianmb/ds4fa: direct link, hf CLI and curl.
- Browser
- Download file 12.4 kB
-
https://huggingface.co/julianmb/ds4fa/resolve/main/tests/test_engine_mgpu_runtime.c
- Command line
-
hf download hf://julianmb/ds4fa/tests/test_engine_mgpu_runtime.c
-
curl -L -o test_engine_mgpu_runtime.c https://huggingface.co/julianmb/ds4fa/resolve/main/tests/test_engine_mgpu_runtime.c
12.4 kB
| /* CUDA-build numerical-equivalence test for Half-B GPU-only multi-tier | |
| * execution. | |
| * | |
| * Compares first-token + 4 decode steps from a single-tier baseline against | |
| * an explicit GPU-only multi-tier run, both with DS4_CUDA_NO_TF32=1 pinning | |
| * CUBLAS_DEFAULT_MATH. Asserts: | |
| * - prefill max-abs logit delta < 1e-4 | |
| * - decode step max-abs logit delta < 1e-3 | |
| * - decode step top-1 token identical | |
| * - decode step top-5 set identical | |
| * - the multi-tier placement was indeed multi-tier (no entry == single | |
| * baseline tier; if all entries went to tier 0 the test would be a | |
| * tautology). | |
| * | |
| * Skips with PASS if cudaGetDeviceCount < 2 or DS4_TEST_MODEL is unset. | |
| * | |
| * Requires DS4_TEST_HOOKS so ds4_test_session_read_logits + | |
| * ds4_test_engine_placement are exported. */ | |
| /* DS4_N_LAYER is an internal enum in ds4.c. Hardcoded here for the test | |
| * placement-size assertion; matches the constant in ds4.c. */ | |
| /* Declared in ds4.c under DS4_TEST_HOOKS. */ | |
| int ds4_test_session_read_logits(ds4_session *s, float *out, uint64_t out_bytes); | |
| const int *ds4_test_engine_placement(const ds4_engine *e); | |
| static float max_abs_delta(const float *a, const float *b, size_t n) { | |
| float worst = 0.0f; | |
| for (size_t i = 0; i < n; i++) { | |
| float d = fabsf(a[i] - b[i]); | |
| if (d > worst) worst = d; | |
| } | |
| return worst; | |
| } | |
| /* Top-K argmax helpers — simple O(N*K), fine for K=5. */ | |
| static void top_k(const float *a, size_t n, int *out, int k) { | |
| /* Build a sorted set of top-k indices by descending value. */ | |
| for (int i = 0; i < k; i++) out[i] = -1; | |
| for (size_t i = 0; i < n; i++) { | |
| const float v = a[i]; | |
| /* Find insertion spot. */ | |
| int slot = -1; | |
| for (int j = 0; j < k; j++) { | |
| if (out[j] < 0) { slot = j; break; } | |
| if (v > a[out[j]]) { slot = j; break; } | |
| } | |
| if (slot < 0) continue; | |
| /* Shift the slot..k-1 right by one. */ | |
| for (int j = k - 1; j > slot; j--) out[j] = out[j - 1]; | |
| out[slot] = (int)i; | |
| } | |
| } | |
| static int top_k_set_equal(const int *a, const int *b, int k) { | |
| /* Set equality, not order — both lists contain the same indices. */ | |
| for (int i = 0; i < k; i++) { | |
| int found = 0; | |
| for (int j = 0; j < k; j++) { | |
| if (a[i] == b[j]) { found = 1; break; } | |
| } | |
| if (!found) return 0; | |
| } | |
| return 1; | |
| } | |
| static int argmax_f32(const float *a, size_t n) { | |
| int best = 0; | |
| float bestv = a[0]; | |
| for (size_t i = 1; i < n; i++) { | |
| if (a[i] > bestv) { bestv = a[i]; best = (int)i; } | |
| } | |
| return best; | |
| } | |
| /* Read a short hard-coded test prompt suitable for prefill comparison. | |
| * The test needs a prompt long enough to exercise prefill but short | |
| * enough to run in seconds. We tokenize a hard-coded UTF-8 string via | |
| * ds4_tokenize. */ | |
| static void build_prompt(ds4_engine *e, ds4_tokens *out) { | |
| /* A short English-only prompt with enough tokens to exercise prefill. | |
| * Tokenizer is deterministic. */ | |
| const char *text = "The quick brown fox jumps over the lazy dog. " \ | |
| "It was a dark and stormy night, and the wind " \ | |
| "howled across the empty fields beyond the village."; | |
| ds4_tokenize_text(e, text, out); | |
| } | |
| int main(void) { | |
| int dev_count = 0; | |
| (void)cudaGetDeviceCount(&dev_count); | |
| fprintf(stderr, "test_engine_mgpu_runtime: %d CUDA devices visible\n", dev_count); | |
| if (dev_count < 2) { | |
| fprintf(stderr, " skipping (need >= 2 devices)\n"); | |
| return 0; | |
| } | |
| const char *model_path = getenv("DS4_TEST_MODEL"); | |
| if (!model_path || !model_path[0]) { | |
| fprintf(stderr, "FAIL: DS4_TEST_MODEL not set\n"); | |
| return 1; | |
| } | |
| /* Pin cuBLAS default math so reduction-order across devices yields the | |
| * tightest possible equivalence. */ | |
| setenv("DS4_CUDA_NO_TF32", "1", 1); | |
| /* Exercise the peer path when available; copy_xdev falls back to | |
| * pinned-host bounce automatically when peer is non-functional. */ | |
| unsetenv("DS4_FORCE_HOST_BOUNCE"); | |
| /* ---- Run #1: single-GPU baseline. ---- */ | |
| fprintf(stderr, "test_engine_mgpu_runtime: single-tier baseline\n"); | |
| ds4_engine_options opt; | |
| memset(&opt, 0, sizeof(opt)); | |
| opt.model_path = model_path; | |
| opt.backend = DS4_BACKEND_CUDA; | |
| opt.n_threads = 1; | |
| opt.warm_weights = false; | |
| opt.quality = false; | |
| ds4_engine *e1 = NULL; | |
| int rc = ds4_engine_open(&e1, &opt); | |
| CHECKF(rc == 0 && e1 != NULL, "single-tier engine_open failed (rc=%d)", rc); | |
| ds4_tokens prompt1 = {0}; | |
| build_prompt(e1, &prompt1); | |
| CHECK(prompt1.len > 0, "tokenize prompt"); | |
| ds4_session *s1 = NULL; | |
| rc = ds4_session_create(&s1, e1, 4096); | |
| CHECKF(rc == 0 && s1 != NULL, "single-tier session_create failed (rc=%d)", rc); | |
| char err[256] = {0}; | |
| rc = ds4_session_sync(s1, &prompt1, err, sizeof(err)); | |
| CHECKF(rc == 0, "single-tier session_sync failed (rc=%d, err=%s)", rc, err); | |
| float *prefill_a = (float *)malloc((size_t)DS4_N_VOCAB * sizeof(float)); | |
| CHECK(prefill_a != NULL, "alloc prefill_a"); | |
| CHECK(ds4_test_session_read_logits(s1, prefill_a, (uint64_t)DS4_N_VOCAB * sizeof(float)) == 0, | |
| "read single-tier prefill logits"); | |
| /* 4 greedy decode steps. */ | |
| int decoded_a[4]; | |
| float (*decoded_logits_a)[DS4_N_VOCAB] = | |
| (float (*)[DS4_N_VOCAB])malloc(4u * (size_t)DS4_N_VOCAB * sizeof(float)); | |
| CHECK(decoded_logits_a != NULL, "alloc decoded_logits_a"); | |
| for (int i = 0; i < 4; i++) { | |
| int tok = ds4_session_argmax(s1); | |
| decoded_a[i] = tok; | |
| rc = ds4_session_eval(s1, tok, err, sizeof(err)); | |
| CHECKF(rc == 0, "single-tier decode eval %d failed (rc=%d, err=%s)", i, rc, err); | |
| CHECK(ds4_test_session_read_logits(s1, decoded_logits_a[i], (uint64_t)DS4_N_VOCAB * sizeof(float)) == 0, | |
| "read single-tier decode logits"); | |
| } | |
| ds4_session_free(s1); | |
| ds4_tokens_free(&prompt1); | |
| ds4_engine_close(e1); | |
| /* ---- Run #2: GPU-only multi-tier. ---- */ | |
| fprintf(stderr, "test_engine_mgpu_runtime: GPU-only multi-tier\n"); | |
| ds4_gpu_config cfg; | |
| memset(&cfg, 0, sizeof(cfg)); | |
| cfg.n_gpus = 2; | |
| cfg.device_indices[0] = 0; | |
| cfg.device_indices[1] = 1; | |
| /* GPU0 has ~48 GiB free on the test box; GPU1 has ~17 GiB free | |
| * because unrelated workloads occupy ~30 GiB. Budget {44, 14} GiB | |
| * after a 1 GiB safety margin — enough headroom to fit the entire | |
| * model across two GPUs with a small over-provision allowance. */ | |
| cfg.vram_bytes[0] = (size_t)44ull * 1024ull * 1024ull * 1024ull; | |
| cfg.vram_bytes[1] = (size_t)14ull * 1024ull * 1024ull * 1024ull; | |
| cfg.safety_margin_bytes = (size_t)1024u * 1024u * 1024u; | |
| ds4_engine *e2 = NULL; | |
| rc = ds4_engine_create_with_gpu_config(&e2, &opt, &cfg); | |
| if (rc != 0 || e2 == NULL) { | |
| /* Common case on a constrained box: GPU1 may be in use by other | |
| * workloads, forcing the placement to spill some layers to CPU. | |
| * CPU-spill is correctly refused by engine_install_gpu_placement | |
| * (B7) with stderr naming the wave-3b follow-up — this test is | |
| * for the GPU-only numerical-eq gate, not the spill refusal | |
| * (that's covered by tests/test_engine_mgpu_refusal). When the | |
| * placement spills, skip with PASS so CI is not blocked by | |
| * external VRAM occupancy. */ | |
| fprintf(stderr, | |
| "test_engine_mgpu_runtime: multi-tier engine_create rc=%d; " | |
| "skipping GPU-only numerical-eq gate (CPU spill or other env constraint).\n", | |
| rc); | |
| free(prefill_a); | |
| free(decoded_logits_a); | |
| fprintf(stderr, "test_engine_mgpu_runtime SKIP_PASS\n"); | |
| return 0; | |
| } | |
| /* Assert the placement is genuinely multi-tier (not all-CPU spill and | |
| * not all-tier-0). The placement is captured by engine_classify_multi_tier. | |
| */ | |
| const int *placement = ds4_test_engine_placement(e2); | |
| CHECK(placement != NULL, "engine placement should be exposed"); | |
| int any_tier_1 = 0; | |
| int n_entries = DS4_TEST_N_LAYER + 2; /* embedding, per-layer, head */ | |
| for (int i = 0; i < n_entries; i++) { | |
| if (placement[i] == DS4_LAYER_PACK_CPU) { | |
| fprintf(stderr, "FAIL: placement crossed to CPU at entry %d but engine_create returned 0?\n", i); | |
| ds4_engine_close(e2); | |
| return 1; | |
| } | |
| if (placement[i] == 1) any_tier_1 = 1; | |
| } | |
| CHECK(any_tier_1, "test budgets should force at least one entry on tier 1"); | |
| ds4_tokens prompt2 = {0}; | |
| build_prompt(e2, &prompt2); | |
| CHECK(prompt2.len > 0, "tokenize prompt (multi-tier)"); | |
| ds4_session *s2 = NULL; | |
| rc = ds4_session_create(&s2, e2, 4096); | |
| CHECKF(rc == 0 && s2 != NULL, "multi-tier session_create failed (rc=%d)", rc); | |
| rc = ds4_session_sync(s2, &prompt2, err, sizeof(err)); | |
| CHECKF(rc == 0, "multi-tier session_sync failed (rc=%d, err=%s)", rc, err); | |
| float *prefill_b = (float *)malloc((size_t)DS4_N_VOCAB * sizeof(float)); | |
| CHECK(prefill_b != NULL, "alloc prefill_b"); | |
| CHECK(ds4_test_session_read_logits(s2, prefill_b, (uint64_t)DS4_N_VOCAB * sizeof(float)) == 0, | |
| "read multi-tier prefill logits"); | |
| /* Prefill numerical-eq. */ | |
| float prefill_delta = max_abs_delta(prefill_a, prefill_b, DS4_N_VOCAB); | |
| int prefill_top_a = argmax_f32(prefill_a, DS4_N_VOCAB); | |
| int prefill_top_b = argmax_f32(prefill_b, DS4_N_VOCAB); | |
| fprintf(stderr, " prefill delta=%g (top1: a=%d b=%d)\n", | |
| (double)prefill_delta, prefill_top_a, prefill_top_b); | |
| CHECKF(prefill_delta < 1e-4f, "prefill delta %g exceeds gate 1e-4", (double)prefill_delta); | |
| CHECKF(prefill_top_a == prefill_top_b, | |
| "prefill top-1 mismatch: a=%d b=%d", prefill_top_a, prefill_top_b); | |
| /* Decode steps. */ | |
| for (int i = 0; i < 4; i++) { | |
| int tok = ds4_session_argmax(s2); | |
| CHECKF(tok == decoded_a[i], | |
| "decode top-1 mismatch at step %d (a=%d b=%d)", i, decoded_a[i], tok); | |
| rc = ds4_session_eval(s2, tok, err, sizeof(err)); | |
| CHECKF(rc == 0, "multi-tier decode eval %d failed (rc=%d, err=%s)", i, rc, err); | |
| float decoded_b[DS4_N_VOCAB]; | |
| CHECK(ds4_test_session_read_logits(s2, decoded_b, (uint64_t)DS4_N_VOCAB * sizeof(float)) == 0, | |
| "read multi-tier decode logits"); | |
| float d = max_abs_delta(decoded_logits_a[i], decoded_b, DS4_N_VOCAB); | |
| fprintf(stderr, " decode step %d delta=%g\n", i, (double)d); | |
| CHECKF(d < 1e-3f, "decode delta %g exceeds gate 1e-3 at step %d", (double)d, i); | |
| int top5_a[5], top5_b[5]; | |
| top_k(decoded_logits_a[i], DS4_N_VOCAB, top5_a, 5); | |
| top_k(decoded_b, DS4_N_VOCAB, top5_b, 5); | |
| CHECKF(top_k_set_equal(top5_a, top5_b, 5), | |
| "decode top-5 set mismatch at step %d", i); | |
| } | |
| free(prefill_a); | |
| free(prefill_b); | |
| free(decoded_logits_a); | |
| ds4_session_free(s2); | |
| ds4_tokens_free(&prompt2); | |
| ds4_engine_close(e2); | |
| fprintf(stderr, "test_engine_mgpu_runtime PASS\n"); | |
| return 0; | |
| } | |