source: CIVL/examples/cuda/newCudaMockup.cvl@ 66ea4f9

1.23 2.0 acw/focus-triggers main test-branch
Last change on this file since 66ea4f9 was 66ea4f9, checked in by Alex Wilton <awilton@…>, 3 years ago

cuda mockup no longer has any ample sets

git-svn-id: svn://vsl.cis.udel.edu/civl/trunk@5761 fb995dde-84ed-4084-dfe6-e5aef3e2452c

  • Property mode set to 100644
File size: 18.7 KB
Line 
1/**
2 * TODO:
3 * - implement cudaMemset and cudaMemsetAsync
4 * - flesh out basic structure of cuda kernel:
5 * - spawn gridDim blocks
6 * - spawn blockDim threads
7 * Alternatively, spawn blockDim warps and then spawn warp's threads
8 * - wait for blocks to finish
9 * - Add in block-level barriers and a __syncthreads() nested function that uses the barrier.
10 * - Add in data race checking support and implement atomicAdd for integers
11 * - Handle dependencies at atomic blocks (replace some with local blocks if possible)
12 */
13
14#include <concurrency.cvh>
15#include <comm.cvh>
16#include <stdlib.h>
17#include <stdio.h>
18#include <stdbool.h>
19#include <string.h>
20#pragma CIVL ACSL
21
22///////////
23// Types //
24///////////
25
26enum cudaError {
27 cudaSuccess
28};
29typedef enum cudaError cudaError_t;
30
31typedef enum cudaMemcpyKind {
32 cudaMemcpyHostToHost,
33 cudaMemcpyHostToDevice,
34 cudaMemcpyDeviceToHost,
35 cudaMemcpyDeviceToDevice,
36 cudaMemcpyDefault
37} cudaMemcpyKind;
38
39typedef struct {
40 unsigned int x, y, z;
41} dim3;
42
43/* used to represent a location in a three dimensional grid
44 */
45typedef struct {
46 unsigned int x, y, z;
47} uint3;
48
49typedef struct $cuda_op_state* $cuda_op_state_t;
50struct $cuda_op_state {
51 _Bool start;
52 $proc op;
53};
54
55typedef struct $cuda_op_state_node* $cuda_op_state_node_t;
56struct $cuda_op_state_node {
57 $cuda_op_state_t opState;
58 $cuda_op_state_node_t next;
59};
60
61typedef struct cudaStream* cudaStream_t;
62typedef struct $cuda_stream_node* $cuda_stream_node_t;
63struct cudaStream {
64 $cuda_op_state_node_t head;
65 $cuda_op_state_node_t tail;
66 int numOps;
67 $cuda_stream_node_t containingNode;
68 _Bool alive;
69};
70
71struct $cuda_stream_node {
72 cudaStream_t stream;
73 $cuda_stream_node_t prev;
74 $cuda_stream_node_t next;
75};
76
77typedef struct $cuda_context {
78 $cuda_stream_node_t head; //list of streams
79 int numStreams;
80} $cuda_context;
81
82typedef struct $cuda_memcpy_data {
83 void* dst;
84 const void* src;
85 size_t count;
86 cudaMemcpyKind kind;
87} $cuda_memcpy_data;
88
89typedef struct $cuda_kernel_1_data {
90 dim3 gridDim;
91 dim3 blockDim;
92 size_t $cudaMemSize;
93 cudaStream_t $cudaStream;
94 const float* A;
95 const float* B;
96 float* C;
97 int numElements;
98} $cuda_kernel_1_data;
99
100//////////////////////
101// Global Variables //
102//////////////////////
103
104$gcomm $cuda_gcomm = $gcomm_create($here, 2);
105const int $CUDA_PLACE_HOST = 0;
106const int $CUDA_PLACE_DEVICE = 1;
107$comm $cuda_host_comm = $comm_create($here, $cuda_gcomm, $CUDA_PLACE_HOST);
108
109/**
110 * Tags used for message-passing between host and device
111 */
112
113enum $cuda_tag {
114 // Predefined tags
115 $CUDA_TAG_TEARDOWN,
116 $CUDA_TAG_SCOPE_REQUEST,
117 $CUDA_TAG_cudaFree,
118 $CUDA_TAG_cudaMemcpy,
119 $CUDA_TAG_cudaMemcpyAsync,
120 // Generated tags (by transformer)
121 $CUDA_TAG_LAUNCH_kernel_1
122};
123
124////////////////////////////////////////////
125// CUDA API Functions (For Host-use Only) //
126////////////////////////////////////////////
127
128/*
129cudaError_t cudaMalloc(void** devPtr, size_t size) {
130 $comm_enqueue($cuda_host_comm, $message_pack($CUDA_PLACE_HOST, $CUDA_PLACE_DEVICE, $CUDA_TAG_cudaMalloc, &size, sizeof(size_t)));
131 $message response = $comm_dequeue($cuda_host_comm, $CUDA_PLACE_DEVICE, $CUDA_TAG_cudaMalloc);
132 $message_unpack(response, devPtr, sizeof(void*));
133
134 return cudaSuccess;
135}
136*/
137
138$scope $cuda_host_request_device_scope() {
139 $comm_enqueue($cuda_host_comm, $message_pack($CUDA_PLACE_HOST, $CUDA_PLACE_DEVICE, $CUDA_TAG_SCOPE_REQUEST, NULL, 0));
140 $message response = $comm_dequeue($cuda_host_comm, $CUDA_PLACE_DEVICE, $CUDA_TAG_SCOPE_REQUEST);
141 $scope result;
142 $message_unpack(response, &result, sizeof($scope));
143
144 return result;
145}
146
147cudaError_t cudaFree(void* devPtr) {
148 $comm_enqueue($cuda_host_comm, $message_pack($CUDA_PLACE_HOST, $CUDA_PLACE_DEVICE, $CUDA_TAG_cudaFree, &devPtr, sizeof(void*)));
149 $comm_dequeue($cuda_host_comm, $CUDA_PLACE_DEVICE, $CUDA_TAG_cudaFree);
150
151 return cudaSuccess;
152}
153
154void $cuda_helper_host_memcpy(void* dst, const void* src, size_t count, cudaMemcpyKind kind, _Bool async) {
155 if (kind == cudaMemcpyHostToHost) {
156 memcpy(dst, src, count);
157 } else {
158 $cuda_memcpy_data args;
159 args.dst = dst;
160 args.src = src;
161 args.count = count;
162 args.kind = kind;
163
164 int tag = async ? $CUDA_TAG_cudaMemcpyAsync : $CUDA_TAG_cudaMemcpy;
165
166 $comm_enqueue($cuda_host_comm, $message_pack($CUDA_PLACE_HOST, $CUDA_PLACE_DEVICE, tag, &args, sizeof($cuda_memcpy_data)));
167 $comm_dequeue($cuda_host_comm, $CUDA_PLACE_DEVICE, tag);
168 }
169}
170
171cudaError_t cudaMemcpy(void* dst, const void* src, size_t count, cudaMemcpyKind kind) {
172 $cuda_helper_host_memcpy(dst, src, count, kind, false);
173 return cudaSuccess;
174}
175
176cudaError_t cudaMemcpyAsync(void* dst, const void* src, size_t count,
177 cudaMemcpyKind kind, cudaStream_t stream) {
178 $cuda_helper_host_memcpy(dst, src, count, kind, true);
179 return cudaSuccess;
180}
181
182/**
183 * TODO:
184 * - test
185 * - atomic?
186 */
187/*
188cudaError_t cudaStreamCreate(cudaStream_t * pStream) {
189 // Create new stream node in linked list
190 $cuda_stream_node_t newHead = $create_new_stream_node();
191 newHead->next = $cuda_global_context.head;
192 $cuda_global_context.head->prev = newHead;
193
194 // Update cuda context's head to be the new node we created
195 $cuda_global_context.head = newHead;
196 $cuda_global_context.numStreams++;
197
198 return cudaSuccess;
199}
200*/
201
202/**
203 * TODO:
204 * - test
205 * - atomic?
206 */
207/*
208cudaError_t cudaStreamSynchronize(cudaStream_t stream) {
209 stream = $default_stream_if_null(stream);
210 $assert(stream->alive, "Attempt to synchronize with a destroyed stream");
211 $when(stream->head == NULL) return cudaSuccess;
212}
213*/
214
215// TODO: atomic
216/*
217cudaError_t cudaStreamDestroy(cudaStream_t stream) {
218 $assert(stream != NULL && stream != $cuda_default_stream, "Attempt to destroy default stream");
219 $assert(stream->alive, "Attempt to destroy an already destroyed stream");
220 $destroy_stream_node(stream->containingNode);
221 return cudaSuccess;
222}
223*/
224
225/*
226cudaError_t cudaDeviceSynchronize() {
227 $proc* opsToWaitOn;
228 int numOps = 0;
229
230 $atomic {
231 opsToWaitOn = ($proc*) malloc(sizeof($proc) * $cuda_global_context.numStreams);
232
233 for ($cuda_stream_node_t node = $cuda_global_context.head;
234 node != NULL;
235 node = node->next) {
236 if (node->stream->tail != NULL) {
237 opsToWaitOn[numOps] = node->stream->tail->opState->op;
238 numOps++;
239 }
240 }
241 }
242 $waitall(opsToWaitOn, numOps);
243
244 return cudaSuccess;
245}
246*/
247
248void $cuda_host_launch_kernel_1(dim3 gridDim, dim3 blockDim, size_t $cudaMemSize, cudaStream_t $cudaStream,
249 const float* A, const float* B, float* C, int numElements) {
250 $cuda_kernel_1_data args;
251 args.gridDim = gridDim;
252 args.blockDim = blockDim;
253 args.$cudaMemSize = $cudaMemSize;
254 args.$cudaStream = $cudaStream;
255 args.A = A;
256 args.B = B;
257 args.C = C;
258 args.numElements = numElements;
259
260 $comm_enqueue($cuda_host_comm, $message_pack($CUDA_PLACE_HOST, $CUDA_PLACE_DEVICE, $CUDA_TAG_LAUNCH_kernel_1, &args, sizeof($cuda_kernel_1_data)));
261 $comm_dequeue($cuda_host_comm, $CUDA_PLACE_DEVICE, $CUDA_TAG_LAUNCH_kernel_1);
262}
263
264//////////////
265// CUDA Ops //
266//////////////
267/*
268struct $_cuda_op_state_h {
269 $cuda_op_state_t opState;
270};
271typedef struct $_cuda_op_state_h * $cuda_op_state_h;
272*/
273
274/*
275 * Enqueues the calling $proc as a new cuda operation onto stream. Then blocks until the cuda operation is allowed to execute.
276 *
277 * Reasoning behind using enqueuedFlag:
278 * + Enforces in the interface more explicitly that device proc shouldn't continue until new op state is created and properly filled out (including the $proc field)
279 * + Keeps symmetry since this method means the op does both enqueueing and dequeueing. The alternative technique would have device enqueuing and op dequeuing.
280 * + Reduces dependencies since device proc will not be manipulating the streams nor have direct access to the newly created op state.
281 * + Keeps responsibility of device proc strictly to interpreting messages, spawning appropriate ops and sending message.
282 */
283/*@ depends_on \access(stream);
284 @ assigns stream;
285 @ reads \nothing;
286 @*/
287$atomic_f $proc $stream_enqueue($scope $cuda_scope, cudaStream_t stream, $message opParams, void(*opProc)($message, $cuda_op_state_t, cudaStream_t)) {
288 $assert(stream->alive, "Attempt to enqueue a CUDA operation onto a destroyed stream");
289
290 $cuda_op_state_t newOpState = ($cuda_op_state_t) $malloc($cuda_scope, sizeof(struct $cuda_op_state));
291 newOpState->start = false;
292 newOpState->op = $spawn opProc(opParams, newOpState, stream);
293
294 $cuda_op_state_node_t newOpStateNode = ($cuda_op_state_node_t) $malloc($cuda_scope, sizeof(struct $cuda_op_state_node));
295 newOpStateNode->opState = newOpState;
296 newOpStateNode->next = NULL;
297
298 if (stream->tail == NULL) {
299 stream->head = newOpStateNode;
300 stream->tail = newOpStateNode;
301 newOpState->start = true;
302 } else {
303 stream->tail->next = newOpStateNode;
304 stream->tail = newOpStateNode;
305 }
306 stream->numOps++;
307
308 //$cuda_op_state_h result = ($cuda_op_state_h) $malloc($cuda_scope, sizeof(struct $_cuda_op_state_h));
309 //result->opState = newOpState;
310 return newOpState->op;
311}
312
313/*@ depends_on \nothing;
314 @ assigns \nothing;
315 @ reads \nothing;
316 @*/
317$atomic_f void $stream_dequeue(cudaStream_t stream) {
318 //stream = $default_stream_if_null(stream);
319 $assert(stream->head != NULL, "Attempt to dequeue an empty stream");
320
321 if (stream->head == stream->tail) {
322 stream->tail = NULL;
323 }
324
325 $cuda_op_state_node_t oldHead = stream->head;
326 stream->head = oldHead->next;
327 if (stream->head != NULL) {
328 stream->head->opState->start = true;
329 }
330
331 stream->numOps--;
332 free(oldHead->opState);
333 free(oldHead);
334 //free(opHandle);
335}
336
337void $cuda_memcpy_proc($message m, $cuda_op_state_t opState, cudaStream_t stream) {
338
339 $when(opState->start);
340 $cuda_memcpy_data args;
341 $message_unpack(m, &args, sizeof($cuda_memcpy_data));
342
343 if (args.kind == cudaMemcpyHostToDevice || cudaMemcpyDeviceToDevice) {
344 args.dst = $reveal(args.dst);
345 }
346 if (args.kind == cudaMemcpyDeviceToHost || cudaMemcpyDeviceToDevice) {
347 args.src = $reveal(args.src);
348 }
349 memcpy(args.dst, args.src, args.count);
350
351 $stream_dequeue(stream);
352}
353
354// Helper function
355 int $dim3_index(dim3 size, uint3 location) {
356 return location.x + size.x * (location.y + size.y * location.z);
357 }
358
359 // Helper function
360 int $cuda_kernel_index (dim3 gDim, dim3 bDim, uint3 bIdx, uint3 tIdx) {
361 return $dim3_index(gDim, bIdx) * (bDim.x * bDim.y * bDim.z) + $dim3_index(bDim, tIdx);
362 }
363
364void $cuda_run_and_wait_on_procs(dim3 dim, void spawningFunction(uint3)) {
365 //TODO: calculate length and index, replace this function in the kernel
366 $local_start();
367 int length = dim.x * dim.y * dim.z;
368 $proc proc_array[length];
369 $range rx = 0 .. dim.x - 1;
370 $range ry = 0 .. dim.y - 1;
371 $range rz = 0 .. dim.z - 1;
372 $domain(3) dom = ($domain(3)){rx, ry, rz};
373 $for(int x,y,z : dom){
374 uint3 id = { x, y, z };
375 int index = $dim3_index(dim, id);
376 proc_array[index] = $spawn spawningFunction(id);
377 }
378 $local_end();
379 $waitall(proc_array,length);
380}
381
382// Generated from kernel_1 definition
383void $cuda_kernel_1(dim3 gridDim, dim3 blockDim, size_t _cuda_mem_size,
384 const float *A, const float *B, float *C, int numElements) {
385 void _cuda_block(uint3 blockIdx) {
386 int numThreads = (blockDim.x * blockDim.y) * blockDim.z;
387 $scope _block_root = $here;
388 $gbarrier _cuda_block_barrier = $gbarrier_create($here, blockDim.x * blockDim.y * blockDim.z);
389 void _cuda_thread(uint3 threadIdx) {
390 int _cuda_tid = $dim3_index(blockDim, threadIdx);
391 int _cuda_kid = $cuda_kernel_index(gridDim, blockDim, blockIdx, threadIdx);
392 $barrier _cuda_thread_barrier = $barrier_create($here, _cuda_block_barrier, _cuda_tid);
393 $local_start();
394 // Kernel definition start
395
396 int i = blockDim.x * blockIdx.x + threadIdx.x;
397
398 if (i < numElements)
399 {
400 C[i] = A[i] + B[i];
401 }
402
403 // Kernel definition end
404 $local_end();
405 $barrier_destroy(_cuda_thread_barrier);
406 }
407 $cuda_run_and_wait_on_procs(blockDim, _cuda_thread);
408 $gbarrier_destroy(_cuda_block_barrier);
409 }
410 $cuda_run_and_wait_on_procs(gridDim, _cuda_block);
411}
412
413void $cuda_kernel_1_proc ($message m, $cuda_op_state_t opState, cudaStream_t $cudaStream) {
414 $when(opState->start);
415
416 $cuda_kernel_1_data args;
417 $message_unpack(m, &args, sizeof($cuda_kernel_1_data));
418
419 $cuda_kernel_1(args.gridDim, args.blockDim, args.$cudaMemSize, $reveal(args.A), $reveal(args.B), $reveal(args.C), args.numElements);
420 $stream_dequeue($cudaStream);
421}
422
423/////////////////
424// CUDA "file" //
425/////////////////
426
427void _cuda_main() {
428
429 //////////////////////
430 // Device Variables //
431 //////////////////////
432
433 $scope $cuda_scope = $here;
434
435 $comm $cuda_device_comm = $comm_create($cuda_scope, $cuda_gcomm, 1);
436 $cuda_context $cuda_global_context;
437 cudaStream_t $cuda_default_stream;
438
439
440 /////////////////////////////////
441 // Context & Stream Management //
442 /////////////////////////////////
443
444 // Helper function to get the default stream if passed NULL, and just returns stream otherwise
445 cudaStream_t $default_stream_if_null(cudaStream_t stream) {
446 return stream == NULL ? $cuda_default_stream : stream;
447 }
448
449 $cuda_stream_node_t $create_new_stream_node() {
450 cudaStream_t newStream = (cudaStream_t) malloc(sizeof(struct cudaStream));
451 newStream->head = NULL;
452 newStream->tail = NULL;
453 newStream->numOps = 0;
454 newStream->alive = true;
455
456 $cuda_stream_node_t newHead = ($cuda_stream_node_t) malloc(sizeof(struct $cuda_stream_node));
457 newHead->stream = newStream;
458 newStream->containingNode = newHead;
459 newHead->prev = NULL;
460 newHead->next = NULL;
461
462 return newHead;
463 }
464
465 /*@ depends_on \nothing;
466 @ assigns \nothing;
467 @ reads \nothing;
468 @*/
469 $atomic_f $proc $destroy_stream_node($cuda_stream_node_t node) {
470 $proc lastOpProc = $proc_null;
471 cudaStream_t stream = node->stream;
472
473 if (node->prev != NULL) {
474 node->prev->next = node->next;
475 }
476 if (node->next != NULL) {
477 node->next->prev = node->prev;
478 }
479 free(node);
480
481 stream->alive = false;
482 if(stream->tail != NULL)
483 lastOpProc = stream->tail->opState->op;
484
485 void $destroy_stream_when_complete($proc lastOpProc, cudaStream_t stream) {
486 $wait(lastOpProc);
487 free(stream);
488 }
489
490 return $spawn $destroy_stream_when_complete(lastOpProc, stream);
491 }
492
493 ///////////////////////////////
494 // CUDA Function Definitions //
495 ///////////////////////////////
496
497 /**
498 * Only called at start of program
499 */
500 void $cuda_setup() {
501 $cuda_stream_node_t defaultStreamNode = $create_new_stream_node();
502 $cuda_default_stream = defaultStreamNode->stream;
503
504 $cuda_global_context.head = defaultStreamNode;
505 $cuda_global_context.numStreams = 1;
506 }
507
508 /**
509 * Only called at end of program
510 */
511 void $cuda_teardown() {
512 $proc destructor = $destroy_stream_node($cuda_default_stream->containingNode);
513 $wait(destructor);
514 $comm_destroy($cuda_device_comm);
515 }
516
517 $message $cuda_free($message request) {
518 void* devPtr;
519 $message_unpack(request, &devPtr, sizeof(void*));
520 free($reveal(devPtr));
521 //free(devPtr);
522
523 return $message_pack($CUDA_PLACE_DEVICE, $CUDA_PLACE_HOST, $CUDA_TAG_cudaFree, NULL, 0);
524 }
525
526 $message $cuda_memcpy($message request, _Bool async) {
527 $cuda_memcpy_data args;
528 $message_unpack(request, &args, sizeof($cuda_memcpy_data));
529
530 $proc memcpyProc = $stream_enqueue($cuda_scope, $default_stream_if_null($cuda_default_stream), request, $cuda_memcpy_proc);
531
532 if (!async && args.kind != cudaMemcpyDeviceToDevice) {
533 $wait(memcpyProc);
534 }
535 int tag = async ? $CUDA_TAG_cudaMemcpyAsync : $CUDA_TAG_cudaMemcpy;
536
537 return $message_pack($CUDA_PLACE_DEVICE, $CUDA_PLACE_HOST, tag, NULL, 0);
538 }
539
540 ////////////////////////
541 // Kernel Definitions //
542 ////////////////////////
543
544 $message $cuda_device_launch_kernel_1($message request) {
545 // TODO: Unpack message for passing in stream parameter
546 $stream_enqueue($cuda_scope, $default_stream_if_null($cuda_default_stream), request, $cuda_kernel_1_proc);
547
548 return $message_pack($CUDA_PLACE_DEVICE, $CUDA_PLACE_HOST, $CUDA_TAG_LAUNCH_kernel_1, NULL, 0);
549 }
550
551 /////////////////
552 // Device main //
553 /////////////////
554
555 $cuda_setup();
556
557 while (true) {
558 $message request = $comm_dequeue($cuda_device_comm, $CUDA_PLACE_HOST, $COMM_ANY_TAG);
559 $message response;
560 const int tag = $message_tag(request);
561
562 switch(tag) {
563 case $CUDA_TAG_SCOPE_REQUEST :
564 response = $message_pack($CUDA_PLACE_DEVICE, $CUDA_PLACE_HOST, $CUDA_TAG_SCOPE_REQUEST, &$cuda_scope, sizeof($scope));
565 break;
566 case $CUDA_TAG_cudaFree :
567 response = $cuda_free(request);
568 break;
569 case $CUDA_TAG_cudaMemcpy :
570 response = $cuda_memcpy(request, false);
571 break;
572 case $CUDA_TAG_cudaMemcpyAsync :
573 response = $cuda_memcpy(request, true);
574 break;
575 case $CUDA_TAG_LAUNCH_kernel_1 :
576 response = $cuda_device_launch_kernel_1(request);
577 break;
578 case $CUDA_TAG_TEARDOWN :
579 $cuda_teardown();
580 return;
581 default :
582 $assert(false, "Unknown CUDA request");
583 }
584
585 $comm_enqueue($cuda_device_comm, response);
586 }
587}
588
589///////////////
590// Host file //
591///////////////
592
593$input int N;
594$assume (N > 0);
595$input float A[N];
596$input float B[N];
597
598void _host_main() {
599 int size = N * sizeof(float);
600 int numBlocks = 2;
601 int numThreads = N%2 == 0? N/2 : (N+1)/2;
602
603 float* cuda_A;
604 // cudaMalloc((void **)&cuda_A, size);
605 {
606 $scope deviceScope = $cuda_host_request_device_scope();
607 cuda_A = $hide((float*)$malloc(deviceScope, size));
608 //cuda_A = (float*)$malloc(deviceScope, size);
609 }
610 cudaMemcpy(cuda_A, A, size, cudaMemcpyHostToDevice);
611
612 float* cuda_B;
613 // cudaMalloc((void **)&cuda_B, size);
614 {
615 $scope deviceScope = $cuda_host_request_device_scope();
616 cuda_B = $hide((float*)$malloc(deviceScope, size));
617 //cuda_B = (float*)$malloc(deviceScope, size);
618 }
619 cudaMemcpy(cuda_B, B, size, cudaMemcpyHostToDevice);
620
621 float* cuda_C;
622 // cudaMalloc((void **)&cuda_C, size);
623 {
624 $scope deviceScope = $cuda_host_request_device_scope();
625 cuda_C = $hide((float*)$malloc(deviceScope, size));
626 //cuda_C = (float*)$malloc(deviceScope, size);
627 }
628
629 dim3 gridDim = {numBlocks, 1, 1};
630 dim3 blockDim = {numThreads, 1, 1};
631 // kernel_1<<<gridDim, blockDim>>>(cuda_A, cuda_B, cuda_C, N);
632 $cuda_host_launch_kernel_1(gridDim, blockDim, 0, NULL, cuda_A, cuda_B, cuda_C, N);
633
634 //Checking correctness
635 float* C = (float *)malloc(size);
636
637 cudaMemcpy(C, cuda_C, size, cudaMemcpyDeviceToHost);
638
639 for(int i = 0; i < N; i++)
640 $assert(C[i] == A[i] + B[i]);
641
642 free(C);
643
644 cudaFree(cuda_A);
645 cudaFree(cuda_B);
646 cudaFree(cuda_C);
647
648 // inserted by transformer
649 $comm_enqueue($cuda_host_comm, $message_pack($CUDA_PLACE_HOST, $CUDA_PLACE_DEVICE, $CUDA_TAG_TEARDOWN, NULL, 0));
650 $comm_destroy($cuda_host_comm);
651}
652
653int main() {
654 $proc host = $spawn _host_main();
655 $proc cuda = $spawn _cuda_main();
656 $wait(host);
657 $wait(cuda);
658 $gcomm_destroy($cuda_gcomm, NULL);
659}
Note: See TracBrowser for help on using the repository browser.