Giter Club home page Giter Club logo

cuda-programming's Introduction

[toc]

English version

I am writing a simplified English version (about half size of the Chinese version) of the book:

友情链接

关于本书第一版:

封面

注意:本书仅有 184 页,没有如下封面展示的那么厚。该封面只是编辑发给我的一个示意图,而不是实物图。要点:不要对本书的厚度(及深度)有太高的期望,否则会有点失望。

cover

购买渠道

已于2020年10月由清华大学出版社出版,语言为中文。在京东或者淘宝搜索“CUDA 编程 樊哲勇”可找到本书。

第一版勘误

本仓库的 master 分支将对应开发版本,与第一版对应的源代码见此发布版本。欢迎读者针对本书找错。找到一个其他人没有报告的错误并说服我改正者,我承诺送您此书第二版一本。目前收到的错误报告如下:

报告者 错误类型 页码信息 更正信息
GPUSLady 笔误 前言 “苏州吉浦科技有限公司”应改为“苏州吉浦科技有限公司”。
EverNorif 笔误 第 34 页 “调用该核函时” 应改为 “调用该核函数时”。
Ebrece 笔误 第 52-53 页 $ nvcc -O3 -arch=sm_75 -arithmetic1cpu.cu 应改为 $ nvcc -O3 -arch=sm_75 arithmetic1cpu.cu。类似地,$ nvcc -O3 -arch=sm_75 -arithmetic2gpu.cu 应改为 $ nvcc -O3 -arch=sm_75 arithmetic2gpu.cu
Ebrece 笔误 第 135 页 倒数第二行的“函数将退化同步的”应改为“函数将退化同步的”。
我自己 不安全的代码 第 143 页 程序中第 23 行核函数的第二个参数 size 应改为 size / sizeof(uint64_t)。我已修改本仓库中 对应的程序
某网友 笔误 第 106 页 “在10.1节”应改为“在第10.2节”。
静听风吟 认知错误 第 3 页 表 1.2 中,Jetson 系列的显卡没有图灵架构的,应把 “AGX Xavier” 改成 “无”。
静听风吟 认知错误 第 12 章 本章中关于统一内存的使用,错误地认为在使用第二代统一内存(在 Linux 系统中使用帕斯卡及以上架构的 GPU)的情况下,在调用核函数之后不需要进行主机与设备的同步即可 1)从主机访问任何统一内存数据;2)并总是得到正确的结果。以上论断中,1)是正确的,而 2)不正确,因为无论是使用第一代统一内存,还是使用第二代统一内存,在从主机访问被核函数修改的统一内存数据之前,都需要某种(显式的或隐式的)同步操作,才能避免读写竞争,从而保证结果的正确性。
Zhenkun Li 错误的初始化 第 10 章 第 108 页尾部,语句 real v = 0; 应改为 real v = s_y[tid];

目录和源代码条目

第 1 章:GPU 硬件和 CUDA 工具

本章无源代码。

第 2 章:CUDA 中的线程组织

文件 知识点
hello.cpp C++ 写一个 Hello World 程序
hello1.cu 一个正确的 C++ 程序也是一个正确的 CUDA 程序
hello2.cu 写一个打印字符串的 CUDA 核函数并调用
hello3.cu 使用含有多个线程的线程块
hello4.cu 使用多个线程块
hello5.cu 使用两维线程块

第 3 章:CUDA 程序的基本框架

文件 知识点
add.cpp 数组相加的 C++ 版本
add1.cu 数组相加的 CUDA 版本
add2wrong.cu 如果数据传输方向搞错了会怎样?
add3if.cu 什么时候必须在核函数使用 if 语句?
add4device.cu 定义与使用 __device__ 函数

第 4 章:CUDA 程序的错误检测

文件 知识点
check1api.cu 检测 CUDA 运行时 API 函数的调用
check2kernel.cu 检测 CUDA 核函数的调用
memcheck.cu cuda-memcheck 检测内存方面的错误
error.cuh 本书常用的用于检测错误的宏函数

第 5 章:获得 GPU 加速的前提

文件 知识点
add1cpu.cu C++ 版的数组相加函数计时
add2gpu.cu 为数组相加核函数计时
add3memcpy.cu 如果把数据传输的时间也包含进来,还有加速吗?
arithmetic1cpu.cu 提高算术强度的 C++ 函数
arithmetic2gpu.cu 提高算术强度的核函数;GPU/CPU 加速比是不是很高?

第 6 章: CUDA 中的内存组织

文件 知识点
static.cu 如何使用静态全局内存
query.cu 如何在 CUDA 程序中查询所用 GPU 的相关技术指标

第 7 章:全局内存的合理使用

文件 知识点
matrix.cu 合并与非合并读、写对程序性能的影响

第 8 章:共享内存的合理使用

文件 知识点
reduce1cpu.cu C++ 版本的归约函数
reduce2gpu.cu 仅使用全局内存和同时使用全局内存和共享内存的归约核函数
bank.cu 使用共享内存实现矩阵转置并避免共享内存的 bank 冲突

第 9 章:原子函数的合理使用

文件 知识点
reduce.cu 在归约核函数中使用原子函数 atomicAdd
neighbor1cpu.cu CPU 版本的邻居列表构建函数
neighbor2gpu.cu GPU 版本的邻居列表构建函数,分使用和不使用原子函数的情况

第 10 章: 线程束内部函数

文件 知识点
reduce.cu 线程束同步函数、线程束洗牌函数以及协作组的使用
reduce1parallelism.cu 提高线程利用率
reduce2static.cu 利用静态全局内存加速

第 11 章: CUDA

文件 知识点
host-kernel.cu 重叠主机与设备计算
kernel-kernel.cu 重叠核函数之间的计算
kernel-transfer.cu 重叠核函数执行与数据传输

第 12 章:统一内存

文件 知识点
add.cu 使用统一内存可以简化代码
oversubscription1.cu 统一内存在初始化时才被分配
oversubscription2.cu 用 GPU 先访问统一内存时可以超过显存的容量
oversubscription3.cu 用 CPU 先访问统一内存时不可超过主机内存容量
prefetch.cu 使用 cudaMemPrefetchAsync 函数

第 13 章:分子动力学模拟(MD)

文件夹 知识点
cpp C++ 版本的 MD 程序
force-only 仅将求力的函数移植到 CUDA
whole-code 全部移植到 CUDA

第 14 章:CUDA 库

文件 知识点
thrust_scan_vector.cu 使用 thrust 中的设备矢量
thrust_scan_pointer.cu 使用 thrust 中的设备指针
cublas_gemm.cu cuBLAS 实现矩阵相乘
cusolver.cu cuSolver 求矩阵本征值
curand_host1.cu cuRAND 产生均匀分布的随机数
curand_host2.cu cuRAND 产生高斯分布的随机数

我的部分测试结果

我的测试系统

  • Linux: 主机编译器用的 g++
  • Windows: 仅使用命令行解释器 CMD,主机编译器用 Visual Studio 中的 cl。在用 nvcc 编译 CUDA 程序时,可能需要添加 -Xcompiler "/wd 4819" 选项消除和 unicode 有关的警告。
  • 全书代码可在 CUDA 9.0-10.2 (包含)之间的版本运行。

矢量相加 (第 5 章)

  • 数组元素个数 = 1.0e8。
  • CPU (我的笔记本) 函数的执行时间是 60 ms (单精度)和 120 ms (双精度)。
  • GPU 执行时间见下表:
V100 (S) V100 (D) 2080ti (S) 2080ti (D) P100 (S) P100 (D) laptop-2070 (S) laptop-2070 (D) K40 (S) K40 (D)
1.5 ms 3.0 ms 2.1 ms 4.3 ms 2.2 ms 4.3 ms 3.3 ms 6.8 ms 6.5 ms 13 ms
  • 如果包含 cudaMemcpy 所花时间,GeForce RTX 2070-laptop 用时 180 ms (单精度)和 360 ms (双精度),是 CPU 版本的三倍慢!

一个高算术强度的函数(第 5 章)

  • CPU 函数(数组长度为 10^4)用时 320 ms (单精度)和 450 ms (双精度)。
  • GPU 函数(数组长度为 10^6)用时情况如下表:
V100 (S) V100 (D) 2080ti (S) 2080ti (D) laptop-2070 (S) laptop-2070 (D)
11 ms 28 ms 15 ms 450 ms 28 ms 1000 ms
  • 用 GeForce RTX 2070-laptop 时核函数执行时间与数组元素个数 N 的关系见下表(单精度):
N 时间
1000 0.91 ms
10000 0.99 ms
100000 3.8 ms
1000000 28 ms
10000000 250 ms
100000000 2500 ms

矩阵复制和转置 (第 7-8 章)

  • 矩阵维度为 10000 乘 10000。
  • 核函数执行时间见下表:
计算 V100 (S) V100 (D) 2080ti (S) 2080ti (D) K40 (S)
矩阵复制 1.1 ms 2.0 ms 1.6 ms 2.9 ms
读取为合并、写入为非合并的矩阵转置 4.5 ms 6.2 ms 5.3 ms 5.4 ms 12 ms
写入为合并、读取为非合并的矩阵转置 1.6 ms 2.2 ms 2.8 ms 3.7 ms 23 ms
在上一个版本的基础上使用 __ldg 函数 1.6 ms 2.2 ms 2.8 ms 3.7 ms 8 ms
利用共享内存转置,但有 bank 冲突 1.8 ms 2.6 ms 3.5 ms 4.3 ms
利用共享内存转置,且无 bank 冲突 1.4 ms 2.5 ms 2.3 ms 4.2 ms

数组归约(第 8-10 章)

  • 数组长度为 1.0e8,每个元素为 1.23。
  • 归约的精确结果为 123000000。
  • GPU 为笔记本版本的 GeForce RTX 2070。
  • 下面是用单精度浮点数测试的结果:
计算方法与机器 计算时间 结果
CPU 中循环累加 100 ms 33554432 (完全错误
全局内存+线程块同步函数 5.8 ms 123633392 (三位正确的有效数字)
静态共享内存+线程块同步函数 5.8 ms 123633392 (三位正确的有效数字)
动态共享内存+线程块同步函数 5.8 ms 123633392 (三位正确的有效数字)
共享内存+原子函数+线程块同步函数 3.8 ms 123633392 (三位正确的有效数字)
共享内存+原子函数+线程束同步函数 3.4 ms 123633392 (三位正确的有效数字)
共享内存+原子函数+线程束洗牌函数 2.8 ms 123633392 (三位正确的有效数字)
共享内存+原子函数+协作组 2.8 ms 123633392 (三位正确的有效数字)
共享内存+协作组+两个核函数 2.0 ms 123000064 (七位正确的有效数字)
共享内存+协作组+两个核函数+静态全局内存 1.5 ms 123000064 (七位正确的有效数字)

邻居列表(第 9 章)

  • 原子数为 22464。
  • 使用单精度或双精度时,CPU 都用时约 250 毫秒。
  • GPU 测试结果见下表:
是否使用原子函数 V100 (S) V100 (D) RTX 2070 (S) RTX 2070 (D)
1.9 ms 2.6 ms 2.8 ms 23 ms
1.8 ms 2.6 ms 2.5 ms 16 ms

分子动力学模拟(第 13 章)

  • 模拟体系为固态氩
  • GPU 为笔记本中的 RTX 2070,使用单精度浮点数
  • CPU 为 Intel i7-8750H 处理器

CPU 版本计算速度测试

  • 原子数 N = 10^3 * 4 = 4000
  • 产出步数 = 20000
  • 各个部分所花时间见下表
求力部分 运动方程积分部分 全部
62 s 0.7 s 62.7 s

force-only 版本的计算速度测试

原子数 产出步数 求力和数据传输的时间 运动方程积分的时间 全部时间 整体速度
4000 20000 5.8 s 0.7 s 6.5 s 1.2e7 原子步每秒
32000 10000 5.0 s 2.5 s 7.5 s 4.3e7 原子步每秒
108000 4000 5.4 s 3.3 s 8.7 s 5.0e7 原子步每秒
256000 2000 5.4 s 4.6 s 10 s 5.1e7 原子步每秒

whole-code 版本的计算速度测试

原子数 产出步数 求力的时间 运动方程积分的时间 全部时间 整体速度
4000 20000 1.5 s 0.6 s 2.1 s 3.8e7 原子步每秒
32000 10000 1.6 s 0.3 s 1.9 s 1.7e8 原子步每秒
108000 4000 2.0 s 0.4 s 2.4 s 1.8e8 原子步每秒
256000 2000 2.2 s 0.4 s 2.6 s 2.0e8 原子步每秒

cuda-programming's People

Contributors

brucefan1983 avatar tomguluson92 avatar youqixiaowu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cuda-programming's Issues

4.1.1节运行结果似乎有问题

我将该仓库中的check1api.cu及error.cuh代码直接拷贝在ubuntu 22.04 LTS的VS code中并保存在同一文件夹下,后用命令nvcc check1api.cu,编译结果是no error。GPU是GeForce RTX 4090。不知道具体原因为何,希望查看解答一下。

English?

Why Chinese people don't write English. It becomes totally hard to translate each sentence.

请问有pdf吗?

你好,非常nice的一本书,但是平时习惯了看电子书,且电子书做笔记方便,所以想问一下,有没有pdf版的呢?

97页 印刷错误

97页(第9章 原子函数的合理使用, 9.3.1 C++版本的开发)cutoff_square 取为 $1.9^2 A^2$。 应为 $ 1.9^2$:
error

英文版第三章有错误

这一行有问题,__host__运行在主机端

* Functions decorated as `__host__` are device functions, which are called from kernels and executed in device. This is usually used together with `__host__` to indicate that a function is simultaneously a host function and a device function. Compilers will generate both versions.

出版时间

老师您好,我非常期待您的CUDA编程书籍,迫不及待想问问您何时能出版,谢谢!

书与GitHub代码不一致(及再勘误)

针对最新的勘误表核对了下2022年10月第5次印刷的书籍。
大部分已经该改正。除了以下几处。

  1. 书中Listing 12.4代码与现GitHub项目中的oversubscription2.cu代码差别较大,与之前CUDA-Programming-v1.0的代码也有出入。至少“size 应改为 size / sizeof(uint64_t)”改的不成功,改成了“size) / sizeof(uint64_t)”。至少是笔误了。
我自己 不安全的代码 第 143 页 程序中第 23 行核函数的第二个参数 size 应改为 size / sizeof(uint64_t)。我已修改本仓库中 对应的程序
  • 2022年10月第5次印刷的书中代码:
    CUDA勘误-fotor-20230628112433

  • 最新oversubscription2.cu代码:

int main(void)
{
    for (int n = 1; n <= N; ++n)
    {
        const size_t memory_size = size_t(n) * 1024 * 1024 * 1024;
        const size_t data_size = memory_size / sizeof(uint64_t);
        uint64_t *x;
        CHECK(cudaMallocManaged(&x, memory_size));
        gpu_touch<<<(data_size - 1) / 1024 + 1, 1024>>>(x, data_size);
        CHECK(cudaGetLastError());
        CHECK(cudaDeviceSynchronize());
        CHECK(cudaFree(x));
        printf("Allocated %d GB unified memory with GPU touch.\n", n);
    }
    return 0;
}
  • 老版本oversubscription2.cu代码:
int main(void)
{
    for (int n = 1; n <= N; ++n)
    {
        const size_t size = size_t(n) * 1024 * 1024 * 1024;
        uint64_t *x;
        CHECK(cudaMallocManaged(&x, size));
        gpu_touch<<<size / sizeof(uint64_t) / 1024, 1024>>>(x, size);
        CHECK(cudaGetLastError());
        CHECK(cudaDeviceSynchronize());
        CHECK(cudaFree(x));
        printf("Allocated %d GB unified memory with GPU touch.\n", n);
    }
    return 0;
}
  1. 核函数的笔误还没改。(虽然没啥影响)
EverNorif 笔误 第 34 页 “调用该核函时” 应改为 “调用该核函数时”。
  1. 这个认知错误,2022年10月书中好像没看到说明。(或者转述了没看出来?)
静听风吟 认知错误 第 12 章 本章中关于统一内存的使用,错误地认为在使用第二代统一内存(在 Linux 系统中使用帕斯卡及以上架构的 GPU)的情况下,在调用核函数之后不需要进行主机与设备的同步即可 1)从主机访问任何统一内存数据;2)并总是得到正确的结果。以上论断中,1)是正确的,而 2)不正确,因为无论是使用第一代统一内存,还是使用第二代统一内存,在从主机访问被核函数修改的统一内存数据之前,都需要某种(显式的或隐式的)同步操作,才能避免读写竞争,从而保证结果的正确性。

CUDA程序第一次运行时间?

老师好,
P48页,”第一次计算时,机器(无论是CPU还是GPU)都可能处于预热状态,测得的时间往往偏大”
第一次运行CUDA程序,或者运行其他深度学习算法,模型载入后,第一次计算相比之后确实会非常耗时。请问第一次运行程序时,具体都是因为哪些原因导致计算较慢呢?

我查到可能的原因包括:即时编译、将内核传输到GPU内存,缓存内容
https://www.javaroad.cn/questions/137499#

请问此外还包含哪些原因吗?

Github提供的代码与书本所示代码(p129)不一致

在本书的第129页,Listing 11.2 中列举了本章程序 kernel-kernel.cu 的部分内容。
Feishu20230212-213028
另外,在书本的第130页,第二段的末尾,您也说明,“为了计时方便,核函数中故意做了10^6次加法运算。”
但是,在Github相应的代码库中(CUDA-Programming/src/11-stream/kernel-kernel.cu),我们可以发现,其中的add函数仅仅增加了10^5次,也就是说,您展示于库中的代码,与书本展示的代码并不匹配。

void __global__ add(const real *d_x, const real *d_y, real *d_z)
{
    const int n = blockDim.x * blockIdx.x + threadIdx.x;
    if (n < N1)
    {
        for (int i = 0; i < 100000; ++i)
        {
            d_z[n] = d_x[n] + d_y[n];
        }
    }
}

静态全局内存变量如何释放?

老师好,
P59页“全局内存的生命周期不是由核函数决定的,而是由主机端决定的”,第10章代码,reduce2static.cu中,声明静态全局变量如下,
device real static_y[GRID_SIZE];
但该数组并未释放。请问静态全局内存变量的生命周期?采用cudaFree(static_y)释放时报错,请问该如何释放该变量呢?

p39页上方第14行代码

这里的cudaGetErrorString后是否应该添加反斜杠 \ (github中的代码并没有分两行写)
我在vscode里键入同样的代码如果不加反斜杠会报错?

疑似一处笔误

P65 6.3.2节第一段第三行,“获得100%的占有率并不是获得高性能的必要或充分条件”,这句话不就是指100%占有率和获得高性能毫无关系么?感觉和后面矛盾啊。最多也是就说,“获得100%的占有率并不是获得高性能的充分条件”,这样比较合适?还是说有什么更深层次的理解?这里说明力度也不够啊?我觉得读起来很困惑。

bank冲突避免

你好,有一个问题需要请教
在8.3中,通过__shared__ real S[TILE_DIM][TILE_DIM + 1]来解决bank冲突的问题。原先S[TILE_DIM][TILE_DIM]的数据刚好可以存进32个bank(具有32层)。现在数组变大了,那多出来的那部分数据被存到哪里?
有一种解释是,每个bank的层数并不是固定的32,是硬件决定的。所以多出来那部分数据在第33层?

如果这个说法正确的话,那么书上可能要明确写出bank层数不是32,及其层数计算方法。就我自己而言,看到图8.1的时候,就认为bank只会是32层。

希望采纳~

34页缺字

书中34页第四段,“此时,在主机中调用该核函时所用的grid_size为”
这里是不是应该是核函数?

代码优化

08-shared-memory章节中的reduce2gpu.cu中的 50、75、100行中的__syncthreads();可以放在循环外,可以减少运行时间。

关于error.cuh中的CHECK宏报错信息的疑问。

您好,最近看了您的作品收获很多,并使用了您书中提到的一些技巧。在使用过程中我遇到了如下问题。
我在PyTorch框架中编写了一些自定义CUDA算子,其中使用了error.cuh中的CHECK宏进行返回值检测,有如下几个现象。

  int *pts_mask = NULL;
  cudaMalloc(&pts_mask, boxes_num * pts_num * sizeof(int));  // (N, M)
  CHECK(cudaMemset(pts_mask, -1, boxes_num * pts_num * sizeof(int)));

在程序运行过程中(已经稳定运行了很久,cudaMemset函数已经被执行了很多次),被CHECK的这行代码报如下错误:

CUDA Error:
    File:       /torch_custom_ext/torchex/src/sparse_roi_voxelization/sparse_roiaware_pool3d_kernel.cu
    Line:       264
    Error code: 1
    Error text: invalid argument

在某些时候(随机发生),错误会发生在其他自定义算子中,并且提示out of memory错误。

如果将代码改为

  int *pts_mask = NULL;
  CHECK(cudaMalloc(&pts_mask, boxes_num * pts_num * sizeof(int)));  // (N, M)
  cudaMemset(pts_mask, -1, boxes_num * pts_num * sizeof(int));

错误码会变成: out of memory

根据以上现象我初步判断真正的错误原因就是显存不足(我的运行模型确实也比较大),我的问题是为什么在某些情况下会报出上面提到的invalid argument的错误。是否是因为分配内存失败过后将NULL指针传给了cudaMenset?

是否有CUDA交流群?

樊老师,您好!我已经阅读完了您的书籍,现在在使用CUDA开发一种高性能的算法,能比NV的某官方库更快。不知道您是否有类似于读者群之类的HPC交流群?我是某985大学的硕士,应该也可以为社群做出贡献。多谢!

P108 代码有错

樊老师好,读完您的著作,对cuda编程有了很多新的认识。读书过程中发现了一处问题,P108提到”如果想要在循环内去掉对线程号的约束,又要避免出现读-写竞争,可以将相关代码改写如下:

real v = 0for (int offset = 16; offset > 0; offset >>= 1)
{
      v += s_y[tid + offset];
      __syncwarp();
      s_y[tid] = v;
      __syncwarp();
}


问题在于v的初值赋为0是有问题的,比如tid=0的线程,第一次迭代后其值变为第s_y[16],而不是s_y[0]+s_y[16]。
根据我的理解以及与同事的讨论,该处代码改成如下:

real v = s_y[tid];
for (int offset = 16; offset > 0; offset >>= 1)
{
      v += s_y[tid + offset];
      __syncwarp();
      s_y[tid] = v;
      __syncwarp();
}

才能得到正确结果。
为了便于理解,改成如下更妥:

for (int offset = 16; offset > 0; offset >>= 1)
{
      v = s_y[tid + offset];
      __syncwarp();
      s_y[tid] += v;
      __syncwarp();
}

不知我理解是否有问题,请不吝赐教。

block、thread、wrap间的执行顺序

您好,请教几个问题。
我的显卡是2080Ti

当我读到,109页10.3 更多线程束内的基本函数中的Listing 10.2(如下)

#include "error.cuh"
#include <stdio.h>

const unsigned WIDTH = 8;
const unsigned BLOCK_SIZE = 16;
const unsigned FULL_MASK = 0xffffffff;

void __global__ test_warp_primitives(void);

int main(int argc, char **argv)
{
    test_warp_primitives<<<1, BLOCK_SIZE>>>();
    CHECK(cudaDeviceSynchronize());
    return 0;
}

void __global__ test_warp_primitives(void)
{
    int tid = threadIdx.x;
    int lane_id = tid % WIDTH;

    if (tid == 0) printf("threadIdx.x: ");
    printf("%2d ", tid);
    if (tid == 0) printf("\n");

    if (tid == 0) printf("lane_id:     ");
    printf("%2d ", lane_id);
    if (tid == 0) printf("\n");

    unsigned mask1 = __ballot_sync(FULL_MASK, tid > 0);
    unsigned mask2 = __ballot_sync(FULL_MASK, tid == 0);
    if (tid == 0) printf("FULL_MASK = %x\n", FULL_MASK);
    if (tid == 1) printf("mask1     = %x\n", mask1);
    if (tid == 0) printf("mask2     = %x\n", mask2);

    int result = __all_sync(FULL_MASK, tid);
    if (tid == 0) printf("all_sync (FULL_MASK): %d\n", result);

    result = __all_sync(mask1, tid);
    if (tid == 1) printf("all_sync     (mask1): %d\n", result);

    result = __any_sync(FULL_MASK, tid);
    if (tid == 0) printf("any_sync (FULL_MASK): %d\n", result);

    result = __any_sync(mask2, tid);
    if (tid == 0) printf("any_sync     (mask2): %d\n", result);

    int value = __shfl_sync(FULL_MASK, tid, 2, WIDTH);
    if (tid == 0) printf("shfl:      ");
    printf("%2d ", value);
    if (tid == 0) printf("\n");

    value = __shfl_up_sync(FULL_MASK, tid, 1, WIDTH);
    if (tid == 0) printf("shfl_up:   ");
    printf("%2d ", value);
    if (tid == 0) printf("\n");

    value = __shfl_down_sync(FULL_MASK, tid, 1, WIDTH);
    if (tid == 0) printf("shfl_down: ");
    printf("%2d ", value);
    if (tid == 0) printf("\n");

    value = __shfl_xor_sync(FULL_MASK, tid, 1, WIDTH);
    if (tid == 0) printf("shfl_xor:  ");
    printf("%2d ", value);
    if (tid == 0) printf("\n");
}

输出是

threadIdx.x:  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 
lane_id:      0  1  2  3  4  5  6  7  0  1  2  3  4  5  6  7 
FULL_MASK = ffffffff
mask1     = fffe
mask2     = 1
all_sync (FULL_MASK): 0
all_sync     (mask1): 1
any_sync (FULL_MASK): 1
any_sync     (mask2): 0
shfl:       2  2  2  2  2  2  2  2 10 10 10 10 10 10 10 10 
shfl_up:    0  0  1  2  3  4  5  6  8  8  9 10 11 12 13 14 
shfl_down:  1  2  3  4  5  6  7  7  9 10 11 12 13 14 15 15 
shfl_xor:   1  0  3  2  5  4  7  6  9  8 11 10 13 12 15 14 

其中的

if (tid == 0) printf("threadIdx.x: ");
printf("%2d ", tid);

想请问:

  1. 对于线程束中的不同线程,为什么第二行一定在第一行之后执行?
  2. 对于线程束中的不同线程,为什么会顺序执行第二行?
    我尝试加入如下代码
     int k = 0;
     if (tid == 0) {
         for ( int i = 0; i < 100000000; i++ ) {
             k = k + 1;
         }
     }
     if (tid == 0) printf("threadIdx.x: ");
     printf("%2d ", tid);
     if (tid == 0) printf("\n");
    但是仍然如下输出,我期待线程0应该是最晚执行完成的。
     threadIdx.x:  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 
    

书中106页讲从伏特架构开始,引入了独立线程调度(independent thread scheduling)机制。 每个线程有自己的程序计数器。我的理解是线程之间的执行顺序应该是随机的。这也正是线程束内同步函数 _syncwarp()存在的意义。

extern __shared__ real s_y[];

08-shared-memory/reduce2gpu.cu文件中有

extern __shared__ real s_y[];

请问这里为什么可以使用extern呢?该数组是在哪里定义的呢?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.