summaryrefslogtreecommitdiffstats
path: root/tools/cuda-ab
diff options
context:
space:
mode:
Diffstat (limited to 'tools/cuda-ab')
-rwxr-xr-xtools/cuda-ab/ab.sh1
-rw-r--r--tools/cuda-ab/smiley.cu230
2 files changed, 231 insertions, 0 deletions
diff --git a/tools/cuda-ab/ab.sh b/tools/cuda-ab/ab.sh
index 8ae05ee..5a290e4 100755
--- a/tools/cuda-ab/ab.sh
+++ b/tools/cuda-ab/ab.sh
@@ -86,6 +86,7 @@ host) run_host ;;
guest) run_guest ;;
compare) compare ;;
all) run_host; run_guest; compare ;;
+push) push_bin ;;
victory)
WORKLOAD=victory_mail
SRC="$HERE/$WORKLOAD.cu"
diff --git a/tools/cuda-ab/smiley.cu b/tools/cuda-ab/smiley.cu
new file mode 100644
index 0000000..42770cc
--- /dev/null
+++ b/tools/cuda-ab/smiley.cu
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * smiley.cu - vectorAdd draws a smiley face.
+ *
+ * Four vectorAdd passes execute on the GPU; each computes one scalar field
+ *
+ * c[i] = a[i] + b[i]
+ *
+ * over the same n = 32768 elements laid out as a 256x128 sample grid:
+ *
+ * pass 0 a=u^2 b=v^2 -> face disk field
+ * pass 1 a=(u+xe)^2 b=(v+ye)^2 -> left-eye disk field
+ * pass 2 a=(u-xe)^2 b=(v+ye)^2 -> right-eye disk field
+ * pass 3 a=u^2 b=(v-ym)^2 -> mouth annulus field
+ *
+ * with u,v centre-relative, aspect-corrected coordinates. The host copies
+ * the four fields back and thresholds them to render a 64x32 ASCII smiley
+ * (4x4 samples per character cell). The artwork therefore comes out of GPU
+ * addition results only - no GPU-side drawing, no CPU-side math on pixels
+ * beyond the thresholds.
+ *
+ * Output lines are stable and greppable (matches tools/cuda-ab/ab.sh):
+ *
+ * DEVICE <name> cc=<major>.<minor> sm=<count> mem=<bytes>
+ * MALLOC bytes=<n> rc=<0|err>
+ * VADD passes=4 n=<count> bad=<mismatches> checksum=<sum>
+ * RESULT cells=<W>x<H> inked=<cells> checksum=<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 smiley smiley.cu
+ *
+ * Then, from the guest: /tmp/smiley
+ */
+
+#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)
+
+__global__ void vadd(const float *a, const float *b, float *c, int n)
+{
+ int i = blockIdx.x * blockDim.x + threadIdx.x;
+
+ if (i < n)
+ c[i] = a[i] + b[i];
+}
+
+/* sample grid */
+#define GW 256
+#define GH 128
+#define GN (GW * GH)
+
+/* character cell = 4x4 samples */
+#define CW 64
+#define CH 32
+#define CELL 4
+
+/* feature geometry, in aspect-corrected grid units (u ~ +/-127, v ~ +/-127) */
+#define FACE_R2 10000.0f /* face disk: u^2+v^2 <= 100^2 */
+#define EYE_X 36.0f
+#define EYE_Y 30.0f
+#define EYE_R2 144.0f /* eyes: r=12 */
+#define MOUTH_Y 24.0f
+#define MOUTH_IN 2025.0f /* 45^2 */
+#define MOUTH_OUT 3025.0f /* 55^2 */
+#define MOUTH_V 40.0f
+
+int main(void)
+{
+ cudaDeviceProp prop;
+ float *h_a, *h_b, *h_out, *fields;
+ float *d_a, *d_b, *d_c;
+ size_t bytes = (size_t)GN * sizeof(float);
+ double checksum = 0.0;
+ int bad = 0, pass, 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);
+
+ {
+ cudaError_t e_a = cudaMalloc(&d_a, bytes);
+ cudaError_t e_b = e_a == cudaSuccess ?
+ cudaMalloc(&d_b, bytes) : e_a;
+ cudaError_t e_c = e_b == cudaSuccess ?
+ cudaMalloc(&d_c, bytes) : e_b;
+
+ printf("MALLOC bytes=%zu rc=%d\n", bytes, (int)e_a);
+ if (e_a != cudaSuccess) {
+ size_t fm = 0, tm = 0;
+
+ cudaMemGetInfo(&fm, &tm);
+ printf("MEMINFO free=%llu total=%llu\n",
+ (unsigned long long)fm,
+ (unsigned long long)tm);
+ }
+ CHECK(e_a);
+ CHECK(e_b);
+ CHECK(e_c);
+ }
+
+ h_a = (float *)malloc(bytes);
+ h_b = (float *)malloc(bytes);
+ h_out = (float *)malloc(bytes);
+ fields = (float *)malloc((size_t)4 * bytes);
+ if (!h_a || !h_b || !h_out || !fields)
+ return 1;
+
+ for (pass = 0; pass < 4; pass++) {
+ for (i = 0; i < GN; i++) {
+ int x = i % GW, y = i / GW;
+ float u = (float)x - 127.5f;
+ float v = ((float)y - 63.5f) * 2.0f;
+ float pa, pb;
+
+ switch (pass) {
+ case 0: /* face */
+ pa = u * u; pb = v * v;
+ break;
+ case 1: /* left eye */
+ pa = (u + EYE_X) * (u + EYE_X);
+ pb = (v + EYE_Y) * (v + EYE_Y);
+ break;
+ case 2: /* right eye */
+ pa = (u - EYE_X) * (u - EYE_X);
+ pb = (v + EYE_Y) * (v + EYE_Y);
+ break;
+ default: /* mouth */
+ pa = u * u;
+ pb = (v - MOUTH_Y) * (v - MOUTH_Y);
+ break;
+ }
+ h_a[i] = pa;
+ h_b[i] = pb;
+ }
+
+ CHECK(cudaMemcpy(d_a, h_a, bytes, cudaMemcpyHostToDevice));
+ CHECK(cudaMemcpy(d_b, h_b, bytes, cudaMemcpyHostToDevice));
+ vadd<<<(GN + 255) / 256, 256>>>(d_a, d_b, d_c, GN);
+ CHECK(cudaGetLastError());
+ CHECK(cudaDeviceSynchronize());
+ CHECK(cudaMemcpy(h_out, d_c, bytes, cudaMemcpyDeviceToHost));
+
+ for (i = 0; i < GN; i++) {
+ if (h_out[i] != h_a[i] + h_b[i])
+ bad++;
+ checksum += (double)h_out[i];
+ }
+ for (i = 0; i < GN; i++)
+ fields[(size_t)pass * GN + i] = h_out[i];
+ }
+
+ printf("VADD passes=4 n=%d bad=%d checksum=%.0f\n", GN, bad, checksum);
+ if (bad != 0) {
+ printf("STATUS FAIL\n");
+ return 1;
+ }
+
+ {
+ static char art[CH][CW + 1];
+ int inked = 0, cy, cx;
+
+ for (cy = 0; cy < CH; cy++) {
+ for (cx = 0; cx < CW; cx++) {
+ int hit = 0, sy, sx;
+ const float *f0 = fields;
+ const float *f1 = fields + GN;
+ const float *f2 = fields + 2 * GN;
+ const float *f3 = fields + 3 * GN;
+
+ for (sy = 0; sy < CELL; sy++) {
+ int y = cy * CELL + sy;
+ float v = ((float)y - 63.5f) * 2.0f;
+
+ for (sx = 0; sx < CELL; sx++) {
+ int x = cx * CELL + sx;
+ int idx = y * GW + x;
+ int inside;
+
+ inside = f0[idx] <= FACE_R2;
+ if (f1[idx] <= EYE_R2 ||
+ f2[idx] <= EYE_R2)
+ inside = 0;
+ if (f3[idx] >= MOUTH_IN &&
+ f3[idx] <= MOUTH_OUT &&
+ v >= MOUTH_V)
+ inside = 0;
+ hit += inside;
+ }
+ }
+ art[cy][cx] = hit >= CELL * CELL / 2 ? '#' :
+ ' ';
+ inked += art[cy][cx] == '#';
+ }
+ art[cy][CW] = 0;
+ }
+
+ printf("RESULT cells=%dx%d inked=%d checksum=%.0f\n",
+ CW, CH, inked, checksum);
+ for (cy = 0; cy < CH; cy++)
+ printf("%s\n", art[cy]);
+ }
+
+ printf("STATUS OK\n");
+ CHECK(cudaFree(d_a));
+ CHECK(cudaFree(d_b));
+ CHECK(cudaFree(d_c));
+ free(h_a);
+ free(h_b);
+ free(h_out);
+ free(fields);
+ return 0;
+}