summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoshua Bakita <bakitajoshua@gmail.com>2023-06-13 08:24:26 -0400
committerJoshua Bakita <bakitajoshua@gmail.com>2023-06-13 08:24:26 -0400
commitd82c60cbfb35697b1dbd579eccc35e0d7902e453 (patch)
tree845d8c88c5753f7eaa055b8c3e821ef76e7a6ce3
parent5ba80e14efb595688a3dc3d49c212bc393840513 (diff)
Add mon_cross_ctx_copies benchmark and make target
Benchmark supports: => Generating parallel copies from CPU to GPU in seperate process => Granular timestamping of when each portion of the copy completes => Monitoring of copy process from either CPU or GPU => Tested on platforms including the GV100 and GV11B Formerly known as diff_ctx_diff_stream. *** Warning *** * The current implementation requires some hardcoded parameters * (such as the number of monitoring threads on the GPU) to be * updated and recompiled when switching test platform. ***
-rw-r--r--copy_experiments/Makefile8
-rw-r--r--copy_experiments/mon_cross_ctx_copies.cu263
2 files changed, 271 insertions, 0 deletions
diff --git a/copy_experiments/Makefile b/copy_experiments/Makefile
index b6b5a38..6ccec54 100644
--- a/copy_experiments/Makefile
+++ b/copy_experiments/Makefile
@@ -1,2 +1,10 @@
1.PHONY: all
2
3all: copy_contender mon_cross_ctx_copies
4
1copy_contender: copy_contender.cu copy_testbench.h ../testbench.h 5copy_contender: copy_contender.cu copy_testbench.h ../testbench.h
2 /usr/local/cuda/bin/nvcc copy_contender.cu -o $@ -lcuda -g 6 /usr/local/cuda/bin/nvcc copy_contender.cu -o $@ -lcuda -g
7
8mon_cross_ctx_copies: mon_cross_ctx_copies.cu copy_testbench.h ../testbench.h
9 /usr/local/cuda/bin/nvcc mon_cross_ctx_copies.cu -o $@ -lcuda -lpthread -g
10
diff --git a/copy_experiments/mon_cross_ctx_copies.cu b/copy_experiments/mon_cross_ctx_copies.cu
new file mode 100644
index 0000000..b38d037
--- /dev/null
+++ b/copy_experiments/mon_cross_ctx_copies.cu
@@ -0,0 +1,263 @@
1/* Copyright 2022 Joshua Bakita
2 * Starts two copies of configurable size in seperate contexts at the same time
3 * (by default) and logs the times that pages are copied to the GPU. Supports
4 * monitoring copy progress from either the GPU or the CPU. All output times are
5 * converted to ns.
6 * WARNING: By default, assumes that GPU and CPU clocks tick at the same rate.
7 */
8#include "copy_testbench.h"
9#include "../task_host_utilities.cu" // Just for GetHostDeviceTimeOffset()
10#include <stdio.h>
11#include <stdlib.h>
12#include <stdint.h>
13#include <unistd.h>
14#include <cuda.h>
15#include <pthread.h>
16
17// CPU-only monitoring can only go a fraction of the speed of GPU monitoring.
18// This sets how much slower it should go.
19// WARNING: Setting this too low will result in low clock resolution, but
20// setting this too high will result in low unique sample count.
21static const int CPU_MON_DIVISOR = 50;
22// A single GPU thread cannot log page times as fast as they can be copied, so
23// spread the work across multiple threads.
24// WARNING: Setting this too high will result in extra memory contention, but
25// setting this too low will result in inaccurate copy logs.
26// Known working settings:
27// | Chip: | Num threads:
28// | GV100 | 8
29// | GV11b | 32
30static const int GPU_MON_THREADS = 32;
31// If delay start is enabled on any thread, how great is the delay?
32static const struct timespec DELAY_TIME = {0, 50*1000l*1000l}; // 50ms (use 30ms for GV100)
33// Should each thread doing GPU monitoring check for GPU clock skew?
34static const bool SKEW_CHECK = false;
35
36// Size of each copy to the GPU, in bytes
37static unsigned int COPY_SIZE;
38// GPU time offset. CPU time = GPU time - offset
39static uint64_t GPU_TIME_OFFSET;
40// Copy start barrier
41static unsigned int READY = 0;
42
43typedef enum {
44 CPU_MONITORING,
45 GPU_MONITORING,
46 DELAY_START, // For DELAY_TIME as set above
47} my_config_t;
48
49typedef struct {
50 int *cond_var;
51 my_config_t config;
52} copy_thread_args_t;
53
54// Repeatedly update a time counter in the last 8 bytes of every Nth page, where N is CPU_MON_DIVISOR
55void cpu_copy_mon(int loops, char* cpu_mem) {
56 struct timespec curr_time;
57 uint64_t curr_time_64;
58
59 for (int i = 0; i < loops; i++) {
60 clock_gettime(CLOCK_MONOTONIC_RAW, &curr_time);
61 curr_time_64 = s2ns(curr_time.tv_sec);
62 curr_time_64 += curr_time.tv_nsec;
63 // Updating pages is slower than the CE, so sparely timestamp
64 for (int offset = 0; offset < COPY_SIZE; offset += PG_SZ * CPU_MON_DIVISOR - 8)
65 *(uint64_t*)(cpu_mem + offset) = curr_time_64;
66 }
67}
68
69// Run a single copy in it's own context (intended to be run as a thread)
70void* copy_thread(void* args_raw) {
71 CUcontext ctx;
72 int dev, i;
73 cudaError_t err;
74 int *barrier;
75 char *pinned_hostmem, *devmem;
76 uint64_t *devtimes, *times;
77 cudaStream_t stream1, stream2;
78 copy_thread_args_t *args = (copy_thread_args_t*)args_raw;
79
80 bool DELAY = args->config == DELAY_START;
81 bool GPU_COMPUTE = args->config == GPU_MONITORING;
82
83 // Explictly create a context (avoids creating a primary context implictly)
84 // This has been verified on CUDA 11.1 to give each thread a different context
85 // handle
86 cudaGetDevice(&dev);
87 SAFE(cuCtxCreate(&ctx, 0, dev));
88
89 uint64_t dev_ns, dev_ns2;
90 double host_s, host_s2;
91 //GetHostDeviceTimeOffset(dev, &host_s, &dev_ns);
92 if (GPU_COMPUTE && SKEW_CHECK)
93 InternalReadGPUNanoseconds(dev, &host_s, &dev_ns);
94
95 SAFE(cudaStreamCreate(&stream1));
96 SAFE(cudaStreamCreate(&stream2));
97
98 if (GPU_COMPUTE) {
99 SAFE(cudaHostAlloc(&barrier, sizeof(int) * GPU_MON_THREADS, cudaHostAllocMapped));
100 SAFE(cudaMemset(barrier, 0, sizeof(int) * GPU_MON_THREADS));
101 }
102
103 SAFE(cudaMallocHost(&pinned_hostmem, COPY_SIZE));
104 for (i = 0; i < COPY_SIZE; i++)
105 // Don't allow 0 so that the copy detection logic works
106 pinned_hostmem[i] = max((rand() & 0xff), 1);
107 SAFE(cudaMalloc(&devmem, COPY_SIZE));
108 SAFE(cudaMemset(devmem, 0, COPY_SIZE));
109
110 SAFE(cudaMalloc(&devtimes, 8 * COPY_SIZE / PG_SZ));
111 times = (uint64_t*)malloc(8 * COPY_SIZE / PG_SZ);
112 if (!times) {
113 fprintf(stderr, "Out of memory! Exiting...\n");
114 exit(1);
115 }
116
117 if (GPU_COMPUTE) {
118 // 1 thread can't go quick enough, even on the GV100
119 watch_devmem<<<1,GPU_MON_THREADS,0,stream2>>>(devmem, COPY_SIZE, devtimes, barrier);
120 // Wait for monitor(s) to initialize
121 bool ready = false;
122 while (!ready) {
123 ready = true;
124 for (int i = 0; i < GPU_MON_THREADS; i++)
125 ready &= barrier[i];
126 }
127 }
128
129 // Tell our parent we're ready
130 *args->cond_var = 1;
131 // Wait for our parent to tell us to go
132 while (!READY)
133 continue;
134
135 if (DELAY)
136 nanosleep(&DELAY_TIME, NULL);
137
138 if (!GPU_COMPUTE) {
139 // If CPU-only monitoring, populate initial times
140 cpu_copy_mon(1, pinned_hostmem);
141 }
142
143 SAFE(cudaMemcpyAsync(devmem, pinned_hostmem, COPY_SIZE, cudaMemcpyHostToDevice, stream1));
144 // XXX start
145 //if (GPU_COMPUTE) {
146 // const struct timespec DELAY_TIME = {0, 50*1000l*1000l}; // 50ms
147 // nanosleep(&DELAY_TIME, NULL);
148 // watch_devmem<<<1,8,0,stream2>>>(devmem, COPY_SIZE, devtimes, barrier);
149 //}
150 // XXX end
151
152 if (GPU_COMPUTE) {
153 // Wait for copy monitor (and hence copy) to complete
154 SAFE(cudaStreamSynchronize(stream2));
155 } else {
156 // Guess number of needed CPU-only monitoring cycles
157 cpu_copy_mon(COPY_SIZE / PG_SZ, pinned_hostmem);
158 // Make sure that the copy finished in case we guessed small
159 SAFE(cudaStreamSynchronize(stream1));
160 }
161
162 // Pull back times from the GPU
163 if (GPU_COMPUTE) {
164 SAFE(cudaMemcpy(times, devtimes, 8 * COPY_SIZE / PG_SZ, cudaMemcpyDeviceToHost));
165 // Convert GPU times to CPU times
166 for (int i = 0; i < COPY_SIZE / PG_SZ; i++)
167 times[i] -= GPU_TIME_OFFSET;
168 } else {
169 SAFE(cudaMemcpy(pinned_hostmem, devmem, COPY_SIZE, cudaMemcpyDeviceToHost));
170 int times_idx = 0;
171 // CPU-only monitoring has a fraction the # samples, so broadcast
172 for (int offset = PG_SZ * CPU_MON_DIVISOR - 8; offset < COPY_SIZE; offset += PG_SZ * CPU_MON_DIVISOR - 8) {
173 uint64_t time = *(uint64_t*)(pinned_hostmem + offset);
174 for (int i = 0; i < CPU_MON_DIVISOR; i++) {
175 times[times_idx++] = time;
176 }
177 }
178 // With CPU-only monitoring, we also record the very first page seperately
179 times[0] = *(uint64_t*)pinned_hostmem;
180 }
181
182 if (GPU_COMPUTE && SKEW_CHECK) {
183 InternalReadGPUNanoseconds(dev, &host_s2, &dev_ns2);
184 double host_diff = s2ns(host_s2 - host_s);
185 double dev_diff = dev_ns2 - dev_ns;