summaryrefslogtreecommitdiffstats
path: root/tools/cuda-ab/wedge_bisect.cu
blob: e062a2b407f60d17fb45881133585f3bc4fb7eaa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// SPDX-License-Identifier: BSD-3-Clause
/*
 * wedge_bisect.cu - bisect the wedge trigger: which inter-launch activity
 * makes the SECOND vadd launch no-execute?
 *
 * Every iteration is: mutate host inputs -> H2D da/db -> launch -> sync ->
 * D2H dc -> verify (the wedge_recover shape). Modes add activity between
 * the verify and the next launch:
 *
 *   mode A: none (pure second-launch class)
 *   mode B: an extra H2D of the same buffers (CE write before the launch)
 *   mode C: an extra D2H readback (CE read before the launch)
 *   mode D: a 500 ms sleep (no CE traffic)
 *
 * A no-execute launch is reported (bad>0) not masked: the inputs mutate every
 * iteration.
 *
 * Build: nvcc -O2 --cudart=static -o wedge_bisect wedge_bisect.cu
 */

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cuda_runtime.h>
#include <unistd.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];
}

int main(int argc, char **argv)
{
	const int n = 1 << 15;
	size_t bytes = (size_t)n * sizeof(float);
	float *ha, *hb, *hc;
	float *da, *db, *dc;
	const char *mode = argc > 1 ? argv[1] : "A";
	int iters = argc > 2 ? atoi(argv[2]) : 4;
	int i, bad = 0;

	setvbuf(stdout, NULL, _IONBF, 0);
	ha = (float *)malloc(bytes);
	hb = (float *)malloc(bytes);
	hc = (float *)malloc(bytes);
	if (!ha || !hb || !hc)
		return 1;
	for (i = 0; i < n; i++) {
		ha[i] = (float)(i % 977) * 0.25f;
		hb[i] = (float)(i % 251) * 0.5f;
	}
	CHECK(cudaMalloc(&da, bytes));
	CHECK(cudaMalloc(&db, bytes));
	CHECK(cudaMalloc(&dc, bytes));
	CHECK(cudaMemcpy(da, ha, bytes, cudaMemcpyHostToDevice));
	CHECK(cudaMemcpy(db, hb, bytes, cudaMemcpyHostToDevice));

	for (i = 0; i < iters; i++) {
		int j, itbad = 0;

		if (i > 0) {
			ha[i % n] += 1.0f;
			hb[(i * 7 + 3) % n] += 2.0f;
		}
		CHECK(cudaMemcpy(da, ha, bytes, cudaMemcpyHostToDevice));
		CHECK(cudaMemcpy(db, hb, bytes, cudaMemcpyHostToDevice));
		vadd<<<(n + 255) / 256, 256>>>(da, db, dc, n);
		CHECK(cudaGetLastError());
		CHECK(cudaDeviceSynchronize());
		CHECK(cudaMemcpy(hc, dc, bytes, cudaMemcpyDeviceToHost));
		for (j = 0; j < n; j++)
			if (hc[j] != ha[j] + hb[j])
				itbad++;
		printf("MODE %s iter=%d bad=%d/%d\n", mode, i, itbad, n);
		bad += itbad;
		if (i + 1 < iters) {
			if (mode[0] == 'B') {
				CHECK(cudaMemcpy(da, ha, bytes,
						 cudaMemcpyHostToDevice));
			} else if (mode[0] == 'C') {
				CHECK(cudaMemcpy(hc, dc, bytes,
						 cudaMemcpyDeviceToHost));
			} else if (mode[0] == 'D') {
				usleep(500000);
			}
		}
	}
	printf("STATUS %s\n", bad ? "FAIL" : "OK");
	return bad ? 1 : 0;
}