ds4fa / tests /test_gpu_model_cache.c
julianmb's picture
Upload folder using huggingface_hub (part 4)
8d0b310 verified
Raw
History Blame Contribute Delete
5.85 kB
/* Unit test for the per-device selective model cache
* (mgpu-selective-model-cache).
*
* Exercises:
* - ds4_gpu_device_cache_tensors with disjoint ranges on device 0
* - ds4_gpu_lookup_cache at range bases and at interior offsets
* (proves the subrange pointer offset arithmetic is right)
* - device-id resolution
* - on multi-GPU boxes: caching on device 1 and active-device
* preference in lookup */
#include "ds4_gpu.h"
#include <cuda_runtime.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CHECK(cond, msg) \
do { \
if (!(cond)) { \
fprintf(stderr, "FAIL: %s (line %d)\n", (msg), __LINE__); \
return 1; \
} \
} while (0)
int main(void) {
int dev_count = 0;
(void)cudaGetDeviceCount(&dev_count);
fprintf(stderr, "test_gpu_model_cache: %d CUDA devices visible\n",
dev_count);
if (dev_count < 1) {
fprintf(stderr, "no CUDA devices\n");
return 0;
}
CHECK(ds4_gpu_init(), "ds4_gpu_init");
/* Build a synthetic 1-MiB "model" in host memory. */
const size_t total = 1024 * 1024;
void *host = NULL;
CHECK(cudaMallocHost(&host, total) == cudaSuccess, "cudaMallocHost");
unsigned char *bytes = (unsigned char *)host;
for (size_t i = 0; i < total; i++) bytes[i] = (unsigned char)(i & 0xff);
CHECK(ds4_gpu_set_model_map(host, total), "set_model_map");
/* Three disjoint ranges on device 0. */
ds4_tensor_range ranges[3];
ranges[0].source_offset = 0; ranges[0].bytes = 256 * 1024; ranges[0].target_device = 0;
ranges[1].source_offset = 384 * 1024; ranges[1].bytes = 128 * 1024; ranges[1].target_device = 0;
ranges[2].source_offset = 768 * 1024; ranges[2].bytes = 256 * 1024; ranges[2].target_device = 0;
CHECK(ds4_gpu_device_cache_tensors(0, ranges, 3) == 0,
"device_cache_tensors dev 0 (3 ranges)");
/* Base lookups + interior offset arithmetic. */
int dev = -1; void *base0 = NULL, *interior0 = NULL;
CHECK(ds4_gpu_lookup_cache(0, 1024, &dev, &base0) == 1, "lookup range 0 base");
CHECK(dev == 0, "range 0 device");
CHECK(base0 != NULL, "range 0 ptr");
/* An interior offset must return base0 + delta. */
CHECK(ds4_gpu_lookup_cache(100, 1024, &dev, &interior0) == 1, "lookup range 0 interior");
CHECK(dev == 0, "range 0 interior device");
CHECK(interior0 == (char *)base0 + 100, "interior offset arithmetic");
void *base1 = NULL;
CHECK(ds4_gpu_lookup_cache(384 * 1024, 1024, &dev, &base1) == 1, "lookup range 1 base");
CHECK(dev == 0, "range 1 device");
/* Interior of range 1 at +200 bytes should be base1 + 200. */
void *interior1 = NULL;
CHECK(ds4_gpu_lookup_cache(384 * 1024 + 200, 1024, &dev, &interior1) == 1, "lookup range 1 interior");
CHECK(interior1 == (char *)base1 + 200, "range 1 interior offset");
void *base2 = NULL;
CHECK(ds4_gpu_lookup_cache(900 * 1024, 1024, &dev, &base2) == 1, "lookup range 2");
CHECK(dev == 0 && base2 != NULL, "range 2 device+ptr");
/* Convenience wrapper. */
CHECK(ds4_gpu_lookup_cache_device(0, 1024) == 0, "lookup_device range 0");
/* Lookup must be overflow-safe: a query with bytes=UINT64_MAX must
* not wrap around into a false hit. */
int dev_overflow = -1; void *ptr_overflow = NULL;
int hit = ds4_gpu_lookup_cache(100, UINT64_MAX, &dev_overflow, &ptr_overflow);
/* Either miss (preferred), or hit but the path must NOT have wrapped.
* Accept miss only — a wrap-induced hit would be a bug. */
CHECK(hit == 0, "lookup with bytes=UINT64_MAX does not wrap into a false hit");
/* Bounds-check: ranges that overflow the model must be rejected
* before any allocation. */
ds4_tensor_range bad_overflow = { 0, total + 1, 0 };
CHECK(ds4_gpu_device_cache_tensors(0, &bad_overflow, 1) != 0,
"overflow range rejected");
ds4_tensor_range bad_offset = { total + 1, 16, 0 };
CHECK(ds4_gpu_device_cache_tensors(0, &bad_offset, 1) != 0,
"out-of-range offset rejected");
ds4_tensor_range bad_wrap = { total - 4, UINT64_MAX, 0 };
CHECK(ds4_gpu_device_cache_tensors(0, &bad_wrap, 1) != 0,
"wrap-around range rejected");
/* Gap not covered by selective ranges. The legacy chunked path may
* happen to cover it (it caches the whole model span); accept either
* outcome, but if it returns 1 the device must be 0. */
int dev_gap = -1; void *ptr_gap = NULL;
int gap_hit = ds4_gpu_lookup_cache(300 * 1024, 1024, &dev_gap, &ptr_gap);
if (gap_hit) {
CHECK(dev_gap == 0, "gap fallback device");
}
if (dev_count >= 2) {
/* Cache a different range on device 1. */
ds4_tensor_range r2;
r2.source_offset = 256 * 1024;
r2.bytes = 128 * 1024;
r2.target_device = 1;
CHECK(ds4_gpu_device_cache_tensors(1, &r2, 1) == 0, "cache dev 1");
/* With cudaGetDevice() == 1, the lookup should resolve to dev 1
* for this range (the only selective entry covering it). */
(void)cudaSetDevice(1);
int dd = -1; void *pp = NULL;
CHECK(ds4_gpu_lookup_cache(256 * 1024 + 10, 1024, &dd, &pp) == 1,
"lookup dev 1");
CHECK(dd == 1, "lookup resolves to dev 1");
CHECK(pp != NULL, "lookup ptr non-null");
(void)cudaSetDevice(0);
}
ds4_gpu_cleanup();
(void)cudaFreeHost(host);
fprintf(stderr, "test_gpu_model_cache PASS (devs=%d)\n", dev_count);
return 0;
}