performance - 着色器的 for 循环中的条件中断性能
问题描述
开始学习着色器后,我读到的第一件事是出于性能原因必须尽可能避免(动态)条件分支:显然两个分支都将运行,然后根据条件选择结果。
然而,在查看示例着色器时,我在 Shadertoy 上发现了这个自动立体图着色器。在第 30 行,在 main 函数中,我们可以看到:
for(int count = 0; count < 100; count++) {
if(uv.x < pWid)
break;
float d = getDepth(uv);
//d = 1.;
uv.x -= pWid - (d * maxStep);
}
在这里,我们在for
循环中有一个条件中断。天真地,基于上面的“无条件分支”,人们会认为它的性能很糟糕,因为每个循环出现都有一个分支(这里是 100)。然而,这种情况并非如此。事实上,将最大循环数从 100 增加到任何巨大的数字对性能没有明显影响。
我们可以取消分支,例如使用以下代码:
for(int count = 0; count < 100; count++) {
float d = getDepth(uv);
//d = 1.;
uv.x -= (pWid - (d * maxStep)) * step(0.0, uv.x-pWid);
}
但随后性能受到更大循环的影响:在 1000 或 10000 时,它会减速到爬行。
(类似地,用break
a替换continue
会随着更大的循环而减慢,尽管不会那么多。)
因此,如果它没有运行所有可能的条件分支,那么这里到底发生了什么?在哪些情况下可以使用动态分支而不会影响性能?
解决方案
使用现代 GPU,需要着色的(在这种情况下)像素的总工作量被分成瓦片/组,然后瓦片由“warp”/“wavefront”¹处理,这是一组同步运行的线程这意味着处理 tile 的 warp 中的所有线程都运行相同的指令,但数据不同(SIMD)。
想象有一个处理 2x2 像素的扭曲,其中三个像素需要 10 次迭代,但第四个需要 100 次迭代,因此所有线程都运行 100 次迭代,前三个像素的多余 90 次迭代的结果将被“屏蔽”/丢弃所以它不会影响它们的输出,但是当所有线程都完成处理时,整个扭曲可能只会移动到下一个像素。但是,当所有线程在 10 次迭代后退出时,warp 可能会退出并更早移动,因此您将获得性能提升。
您可以在此处找到对上述内容的(稍微)更长的解释。
在这里一窥现代 GPU 上调度/平铺的内部工作原理。
¹ “warp”是 NVIDIA,“wavefront”是 AMD 术语
推荐阅读
- python - 如何在python3中拆分一个单词字符串?
- bash - 即使我添加了 --jobs 并且 --max-load 甚至没有关闭,为什么 GNU Make 会按顺序运行?
- javascript - “input=text”字段中的引导过滤值
- javascript - 延长
根据里面的组件 - javascript - JavaScript 中的词法环境 vs 作用域 vs 执行上下文
- django - PasswordResetConfirmView 的自定义模板
- jquery - 十月 CMS 前端 ajax 过滤问题(未捕获的 TypeError: $form.request is not a function)
- python - Kivy Python ID 问题
- python - Python 柯里化和部分
- uml - 如何在 UML 2.0 组件图中可视化文件由某些组件创建和使用?