summaryrefslogtreecommitdiffstats
path: root/tools/cuda-ab/victory_matmul.cu
diff options
context:
space:
mode:
Diffstat (limited to 'tools/cuda-ab/victory_matmul.cu')
-rw-r--r--tools/cuda-ab/victory_matmul.cu135
1 files changed, 135 insertions, 0 deletions
diff --git a/tools/cuda-ab/victory_matmul.cu b/tools/cuda-ab/victory_matmul.cu
new file mode 100644
index 0000000..78ab59c
--- /dev/null
+++ b/tools/cuda-ab/victory_matmul.cu
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * victory_matmul.cu - 256x256 tiled matrix multiply with shared memory.
+ *
+ * C = A * B for three 256x256 float matrices of small integer values. The
+ * inputs are integers in [0, 10), so every product and every partial sum is
+ * exactly representable in float (< 2^24); whatever order the GPU accumulates
+ * in, the result is exact. The host replays the multiply with 64-bit integers
+ * and verifies every element exactly, plus a checksum over C.
+ *
+ * Output lines are stable and greppable:
+ *
+ * DEVICE <name> cc=<major>.<minor> sm=<count> mem=<bytes>
+ * MATMUL n=256 bad=<mismatches> csum=<sum>
+ * 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_matmul victory_matmul.cu
+ *
+ * Then, from the guest: /tmp/victory_matmul
+ */
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstdint>
+#include <cuda_runtime.h>
+
+#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 TILE 16
+#define N 256
+
+__global__ void matmul_tiled(const float *A, const float *B, float *C, int n)
+{
+ __shared__ float As[TILE][TILE];
+ __shared__ float Bs[TILE][TILE];
+ int row = blockIdx.y * TILE + threadIdx.y;
+ int col = blockIdx.x * TILE + threadIdx.x;
+ float acc = 0.0f;
+
+ for (int t = 0; t < n / TILE; t++) {
+ As[threadIdx.y][threadIdx.x] = A[row * n + t * TILE + threadIdx.x];
+ Bs[threadIdx.y][threadIdx.x] =
+ B[(t * TILE + threadIdx.y) * n + col];
+ __syncthreads();
+
+ for (int k = 0; k < TILE; k++)
+ acc += As[threadIdx.y][k] * Bs[k][threadIdx.x];
+ __syncthreads();
+ }
+
+ C[row * n + col] = acc;
+}
+
+int main(void)
+{
+ cudaDeviceProp prop;
+ const int n = N;
+ const size_t bytes = (size_t)n * n * sizeof(float);
+ float *h_a, *h_b, *h_c;
+ float *d_a, *d_b, *d_c;
+ double csum = 0.0;
+ int bad = 0, i, j, k;
+ dim3 grid(n / TILE, n / TILE);
+ dim3 block(TILE, TILE);
+
+ 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_a = (float *)malloc(bytes);
+ h_b = (float *)malloc(bytes);
+ h_c = (float *)malloc(bytes);
+ if (!h_a || !h_b || !h_c)
+ return 1;
+
+ for (i = 0; i < n * n; i++) {
+ h_a[i] = (float)(i % 10);
+ h_b[i] = (float)((i * 3 + 1) % 10);
+ }
+
+ CHECK(cudaMalloc(&d_a, bytes));
+ CHECK(cudaMalloc(&d_b, bytes));
+ CHECK(cudaMalloc(&d_c, bytes));
+ CHECK(cudaMemcpy(d_a, h_a, bytes, cudaMemcpyHostToDevice));
+ CHECK(cudaMemcpy(d_b, h_b, bytes, cudaMemcpyHostToDevice));
+
+ matmul_tiled<<<grid, block>>>(d_a, d_b, d_c, n);
+ CHECK(cudaGetLastError());
+ CHECK(cudaDeviceSynchronize());
+ CHECK(cudaMemcpy(h_c, d_c, bytes, cudaMemcpyDeviceToHost));
+
+ for (i = 0; i < n; i++) {
+ for (j = 0; j < n; j++) {
+ long long ref = 0;
+
+ for (k = 0; k < n; k++)
+ ref += (long long)h_a[i * n + k] *
+ (long long)h_b[k * n + j];
+ if (h_c[i * n + j] != (float)ref)
+ bad++;
+ csum += (double)h_c[i * n + j];
+ }
+ }
+
+ printf("MATMUL n=%d bad=%d csum=%.6f\n", n, bad, csum);
+
+ if (bad != 0) {
+ printf("STATUS FAIL\n");
+ return 1;
+ }
+
+ printf("STATUS OK\n");
+ CHECK(cudaFree(d_a));
+ CHECK(cudaFree(d_b));
+ CHECK(cudaFree(d_c));
+ free(h_a);
+ free(h_b);
+ free(h_c);
+ return 0;
+}