summaryrefslogtreecommitdiffstats
path: root/copy_experiments/copy_contender.cu
diff options
context:
space:
mode:
authorJoshua Bakita <bakitajoshua@gmail.com>2023-06-09 18:55:07 -0400
committerJoshua Bakita <bakitajoshua@gmail.com>2023-06-09 18:55:07 -0400
commite63404cfda97050b9d516232ceb4f918f48edcfc (patch)
tree3eaa93b74c46cc31749c767856eda99e7204f0bb /copy_experiments/copy_contender.cu
Add copy_contender benchmark, Makefile, and copy_testbench header
Benchmark supports: => Generating a chain of copies in a single context - No timing pause between copies => Copies to, from, or between device(s) => Variable size and number of copies => Logging how many milliseconds each copy takes *** Warning *** * Relies on implicit synchronization of unidirectional copies across * streams. Verify that this is still the case with CUDA before * trusting results. One can check by verifying that the runtime for * one iteration roughly matches the time for each iteration in a * multi-iteration run. ***
Diffstat (limited to 'copy_experiments/copy_contender.cu')
-rw-r--r--copy_experiments/copy_contender.cu148
1 files changed, 148 insertions, 0 deletions
diff --git a/copy_experiments/copy_contender.cu b/copy_experiments/copy_contender.cu
new file mode 100644
index 0000000..5514552
--- /dev/null
+++ b/copy_experiments/copy_contender.cu
@@ -0,0 +1,148 @@
1/* Copyright 2023 Joshua Bakita
2 * Serially dispatch copies in one direction and record the transfer times
3 * In order to both record individual copy times, and maintain constant
4 * pending copies, this program relies on the implict synchronization which
5 * occurs in CUDA when unidirectional copies are dispatched into seprat
6 * streams.
7 *
8 * Parameters include the size of the copy to perform and the number of times
9 * to perform said copy.
10 */
11#include <stdio.h>
12#include <cuda.h>
13
14#include "copy_testbench.h"
15
16typedef enum {
17 MODE_TO_GPU,
18 MODE_FROM_GPU,
19 MODE_PEER,
20} copy_mode_t;
21
22void usage(char** argv) {
23 fprintf(stderr, "Usage: %s <# of 4KiB pages to copy> <# of iterations>"
24 "<direction [to/from/peer] GPU>\n", argv[0]);
25}
26
27int main(int argc, char** argv) {
28 cudaError_t err;
29 int dev1, dev2;
30 size_t i;
31 char *pinned_hostmem, *devmem, *devmem_d2;
32 cudaStream_t even_stream, odd_stream;
33 struct timespec even_finish, odd_finish;
34 copy_mode_t mode;
35
36 if (argc != 4) {
37 usage(argv);
38 return 1;
39 }
40
41 // Size of each copy to the GPU, in bytes
42 const size_t COPY_SIZE = strtoul(argv[1], NULL, 10) * PG_SZ;
43 // Total copies = NUM_ITERS * 2 + 1
44 const size_t NUM_ITERS = strtoul(argv[2], NULL, 10);
45 if (strcmp(argv[3], "to") == 0)
46 mode = MODE_TO_GPU;
47 else if (strcmp(argv[3], "from") == 0)
48 mode = MODE_FROM_GPU;
49 else if (strcmp(argv[3], "peer") == 0)
50 mode = MODE_PEER;
51 else {
52 usage(argv);
53 return 1;
54 }
55 fprintf(stderr, "Copying %s %lu times %s.\n", human_readable_bytes(COPY_SIZE),
56 NUM_ITERS * 2 + 1, argv[3]);
57
58 // When doing GPU-to-GPU copy, what is the source device?
59 if (mode == MODE_PEER) {
60 SAFE(cudaGetDevice(&dev1));
61 if (dev1 == 0)
62 dev2 = 1;
63 else
64 dev2 = 0;
65 }
66
67 SAFE(cudaMallocHost(&pinned_hostmem, COPY_SIZE));
68 // Populate pinned_hostmem with random data
69 for (i = 0; i < COPY_SIZE; i++)
70 // Don't allow 0 so that the copy detection logic works
71 pinned_hostmem[i] = max((rand() & 0xff), 1);
72 SAFE(cudaMalloc(&devmem, COPY_SIZE));
73 SAFE(cudaMemset(devmem, 0, COPY_SIZE));
74 if (mode == MODE_PEER) {
75 err = cudaSetDevice(dev2);
76 if (err)
77 fprintf(stderr, "Warning: No other device available. Treating 'peer' as an internal device transfer.\n");
78 SAFE(cudaMalloc(&devmem_d2, COPY_SIZE));
79 SAFE(cudaMemset(devmem_d2, 0, COPY_SIZE));
80 SAFE(cudaSetDevice(dev1));
81 }
82
83 // If copying from device (to CPU or peer), first copy over initialization data
84 if (mode != MODE_TO_GPU) {
85 // Use a synchronizing memory copy
86 SAFE(cudaMemcpy(devmem, pinned_hostmem, COPY_SIZE, cudaMemcpyHostToDevice));
87 }
88
89 // Initialize two streams for alternating dispatch and synchronization
90 SAFE(cudaStreamCreate(&even_stream));
91 SAFE(cudaStreamCreate(&odd_stream));
92
93 // Reconfigure depending on the copy type
94 void *target, *source;
95 cudaMemcpyKind direction;
96 switch (mode) {
97 case MODE_TO_GPU:
98 target = devmem;
99 source = pinned_hostmem;
100 direction = cudaMemcpyHostToDevice;
101 break;
102 case MODE_FROM_GPU:
103 target = pinned_hostmem;
104 source = devmem;
105 direction = cudaMemcpyDeviceToHost;
106 break;
107 case MODE_PEER:
108 target = devmem;
109 source = devmem_d2;
110 direction = cudaMemcpyDeviceToDevice;
111 break;
112 default:
113 fprintf(stderr, "FATAL ERROR: 'mode' is not a valid enum value.\n");
114 return 1;
115 }
116
117 // Kick off the first copy that doesn't include a log
118 clock_gettime(CLOCK_MONOTONIC_RAW, &even_finish);
119 SAFE(cudaMemcpyAsync(target, source, COPY_SIZE, direction, odd_stream));
120 // Primary timing and dispatch loop. NUM_ITERS * 2 + 1 copies will be done
121 for (i = 1; NUM_ITERS == (size_t) -1 || i < NUM_ITERS + 2;) {
122 // Odd copy is underway. Queue up even copy so that there's no break.
123 SAFE(cudaMemcpyAsync(target, source, COPY_SIZE, direction, even_stream));
124 SAFE(cudaStreamSynchronize(odd_stream));
125 // Odd copy has finished. Record and print time.
126 clock_gettime(CLOCK_MONOTONIC_RAW, &odd_finish);
127 // Time for the 'odd' copy: odd_finish (end) - even_finish (start)
128 printf("%s/%lu: %ldms\n", argv[3], i++, ns2ms(timediff(even_finish, odd_finish)));
129 // Even copy is underway. Queue up odd copy so that there's no break.
130 SAFE(cudaMemcpyAsync(target, source, COPY_SIZE, direction, odd_stream));
131 SAFE(cudaStreamSynchronize(even_stream));
132 // Even copy has finished. Record and print time.
133 clock_gettime(CLOCK_MONOTONIC_RAW, &even_finish);
134 // Time for the 'even' copy: even_finish (end) - odd_finish (start)
135 printf("%s/%lu: %ldms\n", argv[3], i++, ns2ms(timediff(odd_finish, even_finish)));
136 }
137 // Record the time for the last copy to end
138 SAFE(cudaStreamSynchronize(odd_stream));
139 clock_gettime(CLOCK_MONOTONIC_RAW, &odd_finish);
140 printf("%s/%lu: %ldms\n", argv[3], i, ns2ms(timediff(even_finish, odd_finish)));
141 fprintf(stderr, "Done. Shutting down...\n");
142
143 cudaFree(pinned_hostmem);
144 cudaFree(devmem_d2);
145 cudaFree(devmem);
146 return 0;
147}
148