Skip to content

Commit 421c415

Browse files
chore(content): sync from Feishu
1 parent 7d85630 commit 421c415

3 files changed

Lines changed: 79 additions & 2 deletions

File tree

docs/.vitepress/data/generated/feishu.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"order": 2,
4141
"documentId": "H5x1dK6LzoVFmuxUzDicmuQ1nbb",
4242
"objectType": "docx",
43-
"revisionId": 1532
43+
"revisionId": 1746
4444
}
4545
]
4646
}
103 KB
Loading

docs/wiki/WHFXw13vEiD8Hikfp3ScU33JnXe.md

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)