| #include "ds4_gpu.h" |
|
|
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <time.h> |
|
|
| static double monotonic_seconds(void) { |
| struct timespec ts; |
| clock_gettime(CLOCK_MONOTONIC, &ts); |
| return (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0; |
| } |
|
|
| static double getenv_seconds(const char *name, double fallback) { |
| const char *s = getenv(name); |
| if (!s || !s[0]) return fallback; |
| char *end = NULL; |
| const double v = strtod(s, &end); |
| return end != s && v > 0.0 ? v : fallback; |
| } |
|
|
| static int check_large_topk(void) { |
| const uint32_t n_comp = 32768; |
| const uint32_t n_tokens = 32; |
| const uint32_t top_k = 512; |
| const uint64_t score_count = (uint64_t)n_comp * n_tokens; |
| float *scores_host = (float *)malloc((size_t)score_count * sizeof(float)); |
| uint32_t *selected_host = (uint32_t *)malloc((size_t)n_tokens * top_k * sizeof(uint32_t)); |
| if (!scores_host || !selected_host) return 1; |
|
|
| for (uint32_t t = 0; t < n_tokens; t++) { |
| for (uint32_t i = 0; i < n_comp; i++) { |
| scores_host[(uint64_t)t * n_comp + i] = (float)i; |
| } |
| } |
|
|
| ds4_gpu_tensor *scores = ds4_gpu_tensor_alloc(score_count * sizeof(float)); |
| ds4_gpu_tensor *selected = ds4_gpu_tensor_alloc((uint64_t)n_tokens * top_k * sizeof(uint32_t)); |
| int rc = 1; |
| double elapsed = 0.0; |
| if (scores && selected && |
| ds4_gpu_tensor_write(scores, 0, scores_host, score_count * sizeof(float))) { |
| |
| if (!ds4_gpu_indexer_topk_tensor(selected, scores, n_comp, n_tokens, top_k) || |
| !ds4_gpu_synchronize()) { |
| rc = 1; |
| goto cleanup; |
| } |
| const double t0 = monotonic_seconds(); |
| if (ds4_gpu_indexer_topk_tensor(selected, scores, n_comp, n_tokens, top_k) && |
| ds4_gpu_synchronize()) { |
| elapsed = monotonic_seconds() - t0; |
| rc = ds4_gpu_tensor_read(selected, 0, selected_host, |
| (uint64_t)n_tokens * top_k * sizeof(uint32_t)) ? 0 : 1; |
| } |
| } |
| if (rc == 0) { |
| for (uint32_t t = 0; t < n_tokens && rc == 0; t++) { |
| for (uint32_t i = 0; i < top_k; i++) { |
| const uint32_t expected = n_comp - 1u - i; |
| const uint32_t got = selected_host[(uint64_t)t * top_k + i]; |
| if (got != expected) { |
| fprintf(stderr, "top-k mismatch token=%u rank=%u got=%u expected=%u\n", |
| t, i, got, expected); |
| rc = 1; |
| break; |
| } |
| } |
| } |
| } |
| if (rc == 0) { |
| const double max_seconds = getenv_seconds("DS4_CUDA_TOPK_REGRESSION_SEC", 2.0); |
| fprintf(stderr, "cuda-regression: top-k n_comp=%u n_tokens=%u elapsed=%.3fs\n", |
| n_comp, n_tokens, elapsed); |
| if (elapsed > max_seconds) { |
| fprintf(stderr, "top-k regression: %.3fs exceeds %.3fs\n", elapsed, max_seconds); |
| rc = 1; |
| } |
| } |
|
|
| cleanup: |
| ds4_gpu_tensor_free(selected); |
| ds4_gpu_tensor_free(scores); |
| free(selected_host); |
| free(scores_host); |
| return rc; |
| } |
|
|
| static int check_decode_attention_overflow_path(void) { |
| const uint32_t n_head = 8; |
| const uint32_t head_dim = 512; |
| const uint32_t n_raw = 128; |
| const uint32_t n_comp = 8100; |
| const uint64_t q_count = (uint64_t)n_head * head_dim; |
| const uint64_t raw_count = (uint64_t)n_raw * head_dim; |
| const uint64_t comp_count = (uint64_t)n_comp * head_dim; |
|
|
| float *sinks = (float *)calloc(n_head, sizeof(float)); |
| float *q_host = (float *)calloc((size_t)q_count, sizeof(float)); |
| float *raw_host = (float *)calloc((size_t)raw_count, sizeof(float)); |
| float *comp_host = (float *)calloc((size_t)comp_count, sizeof(float)); |
| float *heads_host = (float *)calloc((size_t)q_count, sizeof(float)); |
| if (!sinks || !q_host || !raw_host || !comp_host || !heads_host) return 1; |
|
|
| for (uint32_t c = 0; c < n_comp; c++) { |
| comp_host[(uint64_t)c * head_dim] = 1.0f; |
| } |
|
|
| ds4_gpu_tensor *heads = ds4_gpu_tensor_alloc(q_count * sizeof(float)); |
| ds4_gpu_tensor *q = ds4_gpu_tensor_alloc(q_count * sizeof(float)); |
| ds4_gpu_tensor *raw = ds4_gpu_tensor_alloc(raw_count * sizeof(float)); |
| ds4_gpu_tensor *comp = ds4_gpu_tensor_alloc(comp_count * sizeof(float)); |
| int rc = 1; |
| if (heads && q && raw && comp && |
| ds4_gpu_tensor_write(q, 0, q_host, q_count * sizeof(float)) && |
| ds4_gpu_tensor_write(raw, 0, raw_host, raw_count * sizeof(float)) && |
| ds4_gpu_tensor_write(comp, 0, comp_host, comp_count * sizeof(float)) && |
| ds4_gpu_attention_decode_heads_tensor(heads, |
| sinks, |
| n_head * sizeof(float), |
| 0, |
| q, |
| raw, |
| n_raw, |
| n_raw, |
| 0, |
| comp, |
| 0, |
| n_comp, |
| NULL, |
| 0, |
| n_head, |
| head_dim) && |
| ds4_gpu_synchronize() && |
| ds4_gpu_tensor_read(heads, 0, heads_host, q_count * sizeof(float))) { |
| rc = 0; |
| for (uint32_t h = 0; h < n_head; h++) { |
| const float v = heads_host[(uint64_t)h * head_dim]; |
| if (v < 0.90f) { |
| fprintf(stderr, "attention fallback ignored compressed rows for head=%u value=%f\n", |
| h, (double)v); |
| rc = 1; |
| } |
| } |
| } |
|
|
| ds4_gpu_tensor_free(comp); |
| ds4_gpu_tensor_free(raw); |
| ds4_gpu_tensor_free(q); |
| ds4_gpu_tensor_free(heads); |
| free(heads_host); |
| free(comp_host); |
| free(raw_host); |
| free(q_host); |
| free(sinks); |
| return rc; |
| } |
|
|
| int main(void) { |
| if (!ds4_gpu_init()) return 1; |
| int rc = check_large_topk(); |
| if (check_decode_attention_overflow_path() != 0) rc = 1; |
| ds4_gpu_cleanup(); |
| if (rc == 0) puts("cuda long-context regression: OK"); |
| return rc; |
| } |
|
|