// SPDX-License-Identifier: BSD-3-Clause /* * victory_strided.cu - strided/offset memory access over 4 MB of floats. * * Two kernels permute an n-element array (n = 1M floats, 4 MB) with a * stride co-prime to n (idx = i*7 mod n), so every element is written exactly * once and the host can verify every single position: * * out1[(i*7) % n] = in[i] + 1.0f (plain permutation) * out2[(i*7) % n] = in[off + i] * 2.0f (offset base pointer) * * All values are multiples of 0.25, so both operations are exact in float * and the host comparison is exact. * * Output lines are stable and greppable: * * DEVICE cc=. sm= mem= * STRIDED n= stride=7 off= bad= csum= * STATUS OK|FAIL * * Build with a static CUDA runtime (--cudart=static) so the guest only needs * the driver's libcuda.so, not a CUDA toolkit installation: * * nvcc -O2 --cudart=static -o victory_strided victory_strided.cu * * Then, from the guest: /tmp/victory_strided */ #include #include #include #include #define CHECK(x) \ do { \ cudaError_t e_ = (x); \ if (e_ != cudaSuccess) { \ printf("CUDA_ERROR %s at line %d\n", \ cudaGetErrorString(e_), __LINE__); \ printf("STATUS FAIL\n"); \ return 1; \ } \ } while (0) #define STRIDE 7 #define OFFSET 4097 __global__ void permute_add(const float *in, float *out, int n, int stride) { long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x; if (i < n) { long long idx = (i * (long long)stride) % (long long)n; out[idx] = in[i] + 1.0f; } } __global__ void permute_offset(const float *in, float *out, int n, int off, int stride) { long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x; if (i < n) { long long idx = (i * (long long)stride) % (long long)n; out[idx] = in[(long long)off + i] * 2.0f; } } int main(void) { cudaDeviceProp prop; const int n = 1 << 20; /* 1M floats = 4 MB */ const long long total = (long long)n + OFFSET; size_t bytes = (size_t)n * sizeof(float); size_t total_bytes = (size_t)total * sizeof(float); float *h_in, *h_out1, *h_out2; float *d_in, *d_out1, *d_out2; double csum = 0.0; int bad = 0, i; setvbuf(stdout, NULL, _IONBF, 0); CHECK(cudaGetDeviceProperties(&prop, 0)); printf("DEVICE %s cc=%d.%d sm=%d mem=%llu\n", prop.name, prop.major, prop.minor, prop.multiProcessorCount, (unsigned long long)prop.totalGlobalMem); h_in = (float *)malloc(total_bytes); h_out1 = (float *)malloc(bytes); h_out2 = (float *)malloc(bytes); if (!h_in || !h_out1 || !h_out2) return 1; for (i = 0; i < (int)total; i++) h_in[i] = (float)(i % 1000) * 0.25f; CHECK(cudaMalloc(&d_in, total_bytes)); CHECK(cudaMalloc(&d_out1, bytes)); CHECK(cudaMalloc(&d_out2, bytes)); CHECK(cudaMemcpy(d_in, h_in, total_bytes, cudaMemcpyHostToDevice)); permute_add<<<(n + 255) / 256, 256>>>(d_in, d_out1, n, STRIDE); permute_offset<<<(n + 255) / 256, 256>>>(d_in, d_out2, n, OFFSET, STRIDE); CHECK(cudaGetLastError()); CHECK(cudaDeviceSynchronize()); CHECK(cudaMemcpy(h_out1, d_out1, bytes, cudaMemcpyDeviceToHost)); CHECK(cudaMemcpy(h_out2, d_out2, bytes, cudaMemcpyDeviceToHost)); for (i = 0; i < n; i++) { long long idx = ((long long)i * STRIDE) % (long long)n; if (h_out1[idx] != h_in[i] + 1.0f) bad++; if (h_out2[idx] != h_in[OFFSET + i] * 2.0f) bad++; csum += (double)h_out1[idx]; } printf("STRIDED n=%d stride=%d off=%d bad=%d csum=%.6f\n", n, STRIDE, OFFSET, bad, csum); if (bad != 0) { printf("STATUS FAIL\n"); return 1; } printf("STATUS OK\n"); CHECK(cudaFree(d_in)); CHECK(cudaFree(d_out1)); CHECK(cudaFree(d_out2)); free(h_in); free(h_out1); free(h_out2); return 0; }