@@ -255,4 +255,81 @@ int main()
255255
256256<FeishuImage src="/feishu/wiki/WHFXw13vEiD8Hikfp3ScU33JnXe/389b26f16497cb84df57d9a7.png" caption="warp 中的分支会串行化" width="2650" height="1238" transparent />
257257
258- 同⼀个 block ⾥的 thread 被分到哪个 warp 由如下的 thread ID 决定:`ID = threadIdx.x + threadIdx.y * blockDim.x + threadIdx.z * blockDim.x * blockDim.y`,
258+ 同⼀个 block ⾥的 thread 被分到哪个 warp 由如下的 thread ID 决定:`ID = threadIdx.x + threadIdx.y * blockDim.x + threadIdx.z * blockDim.x * blockDim.y`,其中 `ID/32` (向下取整)相同的线程放到一个 wrap 中。
259+
260+ ### SIMT 与 SIMD
261+
262+ SIMD: Single Instruction Multiple **Data**
263+
264+ SIMT: Single Instruction Multiple **Thread**
265+
266+ 数据是被动的,线程是主动的。从 CC 7.0 起,每个 thread 有自己的 program counter,也就可以执执行不同的指令。而 SIMD 只有一个 program counter。但是,一个 warp 里每次激活的 thread 应该有相同的 program counter。如果条件分支太多,将导致 program counter 的不同取值太多,那么 SIMT 也是低效的。最有利于 SIMT 发挥效率的编程方式仍然是 SIMD。
267+
268+ ### Warp Divergence 案例
269+
270+ 下图代码中 betterKernel 尽量使得所有线程走相同的分支,即数据分布于 warp 边界对齐。
271+
272+ ```C++
273+ __global__ void badKernel(float* data, int N) {
274+ int i = blockIdx.x * blockDim.x + threadIdx.x;
275+ if (i < N) {
276+ if (i % 2) data[i] = sqrt(data[i]);
277+ else data[i] = exp(data[i]);
278+ }
279+ }
280+
281+ __global__ void betterKernel(float* data, int N) {
282+ int i = blockIdx.x * blockDim.x + threadIdx.x;
283+ int half = (N + 1) / 2;
284+ if (i < half) {
285+ int target = 2 * i;
286+ data[target] = exp(data[target]);
287+ } else if (i < N) {
288+ int target = 2 * (i - half) + 1;
289+ data[target] = sqrt(data[target]);
290+ }
291+ }
292+ ```
293+
294+ ### Warp-Level Primitives
295+
296+ Warp 内可以高效地进行数据交换和同步,这段代码实现了 reduce 操作,操作后 ` lane 0 ` 的 ` val ` 是操作前的32个 lane 的 ` val ` 只和,其他的 lane 的 ` val ` 未定义。
297+
298+ ``` C++
299+ #define FULL_MASK 0xffffffff
300+
301+ for (int offset = 16 ; offset > 0 ; offset /= 2 )
302+ val += __shfl_down_sync(FULL_MASK , val, offset);
303+ ```
304+
305+ <FeishuImage src =" /feishu/wiki/WHFXw13vEiD8Hikfp3ScU33JnXe/9ff1f01fffa641fa42b6db5d.png " caption =" warp level primitives " width =" 1138 " height =" 501 " transparent />
306+
307+ ## Thread Block
308+
309+ ### Block 协作
310+
311+ Block 内的 thread 比较类似 CPU 的 thread,可以用 shared memory,也有原子操作、内存屏障,线程同步等指令。
312+
313+ ``` C++
314+ __global__ void syncthreads_valid_behavior (int* input_data, int* output_data) {
315+ __ shared__ int shared_data[ 128] ;
316+
317+ shared_data[threadIdx.x] = input_data[threadIdx.x];
318+ if (blockIdx.x > 0) { // CORRECT, uniform condition across all block threads
319+ __syncthreads();
320+ output_data[threadIdx.x] = shared_data[127 - threadIdx.x];
321+ }
322+ }
323+
324+ __ global__ void syncthreads_invalid_behavior(int* input_data, int* output_data) {
325+ __ shared__ int shared_data[ 128] ;
326+
327+ shared_data[threadIdx.x] = input_data[threadIdx.x];
328+ for (int i = 0; i < blockDim.x; ++i) {
329+ if (i == threadIdx.x) { // WRONG, non-uniform condition
330+ __syncthreads(); // Undefined Behavior
331+ }
332+ }
333+ output_data[threadIdx.x] = shared_data[127 - threadIdx.x];
334+ }
335+ ```
0 commit comments