UPL9-17-d——组内线程数量
UPL9-17-d——组内线程数量
Compute Shader 中的执行模型
Compute Shader 的执行模型可分为三层结构
-
第一层 —— 线程
HLSL 函数内部自动生成,由 GPU 并行执行的最小计算单元,负责执行入口函数的一次调用
-
第二层 —— 线程组
Compute Shader 中由
[numthreads(x, y, z)]定义,
每个组内包含固定数量的线程,共享组内内存、可进行同步 -
第三层 —— 线程组网格
C# 侧
Dispatch()调用,定义多少个组要被 GPU 启动,控制全局总线程数
相当于:
- GPU 线程会执行入口函数
- 入口函数前的
[numthreads(x, y, z)]决定单个 线程组内 线程数量 - C# 侧调用
Dispatch 时传入的x、y、z决定要启动多少个 线程组
三个轴向的数字代表什么
-
入口函数前的
[numthreads(x, y, z)]用于控制 每个线程组一共有多少个线程比如:
[numthreads(8, 8, 1)],表示每个线程组有 8 * 8 * 1 = 64 个线程 -
C# 侧调用
Dispatch 时传入的x、y、z决定一共有多少个线程组比如:入口函数:
[numthreads(8, 8, 1)],C# 调用:Dispatch(kernel, 64, 64, 1);
表示一共会开启(64 * 8, 64 * 8, 1 * 1) = (512, 512, 1) = 512 * 512 * 1 = 262144个线程
也就是说他们两共同决定了线程总数:
1 | 总线程数 = numthreads(x,y,z) * Dispatch(groupsX,groupsY,groupsZ) |
注意:每个轴的线程或线程组数不能设置为 0,最小为 1,即 x, y, z >= 1
- 如果是 1D 任务(一维数据,比如处理线性数组计算):x,1,1
- 如果是 2D 任务(二维数据,比如处理图像、纹理等数据):x, y, 1
- 如果是 3D 任务(三维数据,比如处理体积型数据,3D 纹理、网格等数据):x, y, z
数值规则
-
[numthreads(x, y, z)]的规则三个参数都是常量整数,取值范围:
- 每个维度 >= 1
- 每个维度 <= 1024
- x * y * z <= 1024
常见组合:
(8, 8, 1)、(16, 16, 1)、(32, 1, 1) -
Dispatch(groupsX, groupsY, groupsZ)的规则三个参数都是整数,取值范围:
- 每个维度 ≥ 1,不能为 0
- 没有硬性上限(取决于 GPU 支持的最大 Dispatch 数量),但通常非常大(几十万没问题)
匹配规则
当我们要处理二维纹理、数组、体积数据时,应该让线程数覆盖整个数据区域
一般情况下,线程数和分组数的公式如下:
1 | int groupsX = Mathf.CeilToInt(width / (float)numThreadX); |
其中:
-
groups:Dispatch传入的值 -
numThreadX:numthreads传入的值 -
width、height、depth:代表你要处理的数据维度数量 -
CeilToInt:向上取整的目的是为了即使不是整数倍也能完全覆盖,超出部分的线程可以在 HLSL 中用边界判断过滤掉
举例说明:比如我们要利用 Compute Shader 处理一个 512 * 512 像素的二维图像,我们需要在大于等于 512 * 512 个线程去处理这个图像
-
Compute Shader 的
numthreads 为:[numthreads(8, 8, 1)] -
那么在 C# 侧:
1
2
3int groupsX = Mathf.CeilToInt(512f / 8) = 64;
int groupsY = Mathf.CeilToInt(512f / 8) = 64;
Dispatch(kernel, groupsX, groupsY, 1); -
实际启动的线程为:
(64 * 8, 64 * 8, 1 * 1) = 512 * 512 = 262144
推荐规则
- 1D 数据(例如:数组):
numthreads -(64,1,1) 或(128,1,1) - 2D 数据(例如:贴图):
numthreads -(8,8,1) 或(16,16,1) - 3D 数据(例如:网格):
numthreads -(4,4,4) 或(8,8,8)
主要原因:GPU 线程调度单位是 Warp(英伟达) / Wavefront(AMD)(32 或 64 个线程)
所以最好让 x * y * z 是 32 或 64 的倍数
总结
numthreads 和 Dispatch 中的 xyz 三个轴
numthreads 定义线程组的大小(最多 1024 个线程)
Dispatch 定义要开启多少个组,一般按 数据尺寸 / numthreads 向上取整计算
总线程数 = 所有线程组的线程的乘积,因此所有维度都必须 ≥1
- 1D:处理线性数据(数组、粒子)
- 2D:处理平面数据(图像、屏幕)
- 3D:处理体积数据(3D 纹理、网格)
