| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include "ds4.h" |
| #include "ds4_gpu_mgpu.h" |
| #include "ds4_layer_pack.h" |
|
|
| #include <cuda_runtime.h> |
| #include <math.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
|
|
| #ifndef DS4_N_VOCAB |
| #define DS4_N_VOCAB 129280 |
| #endif |
|
|
| |
| |
| #ifndef DS4_TEST_N_LAYER |
| #define DS4_TEST_N_LAYER 43 |
| #endif |
|
|
| |
| 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); |
|
|
| #define CHECK(cond, msg) \ |
| do { \ |
| if (!(cond)) { \ |
| fprintf(stderr, "FAIL: %s (line %d)\n", (msg), __LINE__); \ |
| return 1; \ |
| } \ |
| } while (0) |
|
|
| #define CHECKF(cond, fmt, ...) \ |
| do { \ |
| if (!(cond)) { \ |
| fprintf(stderr, "FAIL: " fmt " (line %d)\n", __VA_ARGS__, __LINE__); \ |
| return 1; \ |
| } \ |
| } while (0) |
|
|
| 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; |
| } |
|
|
| |
| static void top_k(const float *a, size_t n, int *out, int k) { |
| |
| for (int i = 0; i < k; i++) out[i] = -1; |
| for (size_t i = 0; i < n; i++) { |
| const float v = a[i]; |
| |
| 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; |
| |
| 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) { |
| |
| 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; |
| } |
|
|
| |
| |
| |
| |
| static void build_prompt(ds4_engine *e, ds4_tokens *out) { |
| |
| |
| 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; |
| } |
|
|
| |
| |
| setenv("DS4_CUDA_NO_TF32", "1", 1); |
| |
| |
| unsetenv("DS4_FORCE_HOST_BOUNCE"); |
|
|
| |
| 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"); |
|
|
| |
| 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); |
|
|
| |
| 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; |
| |
| |
| |
| |
| 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) { |
| |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| } |
|
|
| |
| |
| |
| 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; |
| 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"); |
|
|
| |
| 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); |
|
|
| |
| 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; |
| } |
|
|