Skip to content

Commit ad6da82

Browse files
committed
detach vs kernel launch
1 parent 579cc0c commit ad6da82

File tree

2 files changed

+96
-1
lines changed

2 files changed

+96
-1
lines changed

cudaguide/1_moderncuda/README.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,3 +355,97 @@ int main() {
355355
host_lambda(1);
356356
}
357357
```
358+
359+
### 第一个 CUDA 程序
360+
361+
```cuda
362+
#include <cuda_runtime.h>
363+
#include <nvfunctional>
364+
#include "cudapp.cuh" // 小彭老师现代 CUDA 框架,更符合现代 C++ 风格,减少官方 C 风格接口的繁琐
365+
366+
using namespace cudapp;
367+
368+
__global__ void kernel(int x) {
369+
printf("线程编号 (%d, %d)\n", blockIdx.x, threadIdx.x);
370+
}
371+
372+
int main() {
373+
// 三箭头语法糖启动内核
374+
// kernel<<<blockDim, gridDim>>>(...)
375+
kernel<<<3, 4>>>();
376+
377+
// 强制同步:等待此前启动过的所有内核执行完成
378+
cudaDeviceSynchronize();
379+
380+
return 0;
381+
}
382+
```
383+
384+
重要知识点:CUDA 为了极致性能,会使用**异步**的方式启动内核。
385+
386+
使用三箭头语法调用内核函数,只是把内核提交到 GPU 上去而已,不代表内核已经在 GPU 上执行完毕。
387+
388+
而 `cudaDeviceSynchronize()` 的作用就是等待**此前提交的所有内核**执行完毕,然后才能继续往下执行 `return 0`。
389+
390+
> {{ icon.warn }} 如果不在程序退出前强制同步,则内核可能未执行!printf 语句会不生效!
391+
392+
### 类比
393+
394+
为了方便理解,可以把 `kernel<<<3, 4>>>()` 看作是启动了一个**后台线程**,而且还是以 `detach` 的方式,启动后就在后台默默运行,不会阻塞启动了内核的 CPU 线程。
395+
396+
| GPU 操作 | CPU 类比 |
397+
|-|-|
398+
| 启动内核 `kernel<<<3, 4>>>()` | `std::async` 或 `std::thread` |
399+
| `cudaDeviceSynchronize` | `future.wait()` 或 `thread.join()` |
400+
401+
### 小彭老师为你准备的 CUDA 框架
402+
403+
```cuda
404+
#include <cuda_runtime.h>
405+
#include <nvfunctional>
406+
#include "cudapp.cuh" // 小彭老师现代 CUDA 框架,更符合现代 C++ 风格,减少官方 C 风格接口的繁琐
407+
408+
using namespace cudapp;
409+
410+
__global__ void kernel(int x) {
411+
printf("内核参数 x = %d\n", x);
412+
printf("线程编号 (%d, %d)\n", blockIdx.x, threadIdx.x);
413+
}
414+
415+
int main() {
416+
// 启动内核的3种方式
417+
// 1. 官方三箭头语法糖(常用)
418+
// kernel<<<blockDim, gridDim, dynamicSmemBytes, stream>>>(...)
419+
int x = 42;
420+
kernel<<<3, 4, 0, 0>>>(x);
421+
422+
// 2. cudaLaunchKernel
423+
void *args[] = {&x};
424+
CHECK_CUDA(cudaLaunchKernel(kernel, dim3(3), dim3(4), args, 0, 0));
425+
426+
// 3. cudaLaunchKernelEx
427+
cudaLaunchConfig_t cfg{};
428+
cfg.blockDim = dim3(3);
429+
cfg.gridDim = dim3(4);
430+
cfg.dynamicSmemBytes = 0;
431+
cfg.stream = 0;
432+
cfg.attrs = nullptr;
433+
cfg.numAttrs = 0;
434+
CHECK_CUDA(cudaLaunchKernelEx(&cfg, kernel, x));
435+
436+
const char *name;
437+
CHECK_CUDA(cudaFuncGetName(&name, kernel));
438+
printf("内核名字:%s\n", name);
439+
440+
// 1. 强制同步:等待此前启动过的所有内核执行完成
441+
CHECK_CUDA(cudaDeviceSynchronize());
442+
443+
// 2. 仅同步 0 号流(null-stream)
444+
CHECK_CUDA(cudaStreamSynchronize(0));
445+
446+
// 3. 仅同步 0 号流,但使用小彭老师现代 CUDA 框架
447+
CudaStream::nullStream().join();
448+
449+
return 0;
450+
}
451+
```

cudaguide/1_moderncuda/main.cu

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ __global__ void kernel(int x) {
1111

1212
int main() {
1313
// 启动内核的3种方式
14-
// 1. 三箭头语法糖(常用)
14+
// 1. 官方三箭头语法糖(常用)
15+
// kernel<<<blockDim, gridDim, dynamicSmemBytes, stream>>>(...)
1516
int x = 42;
1617
kernel<<<3, 4, 0, 0>>>(x);
1718

0 commit comments

Comments
 (0)