【问题标题】:Speed of cos() and sin() function in GLSL shaders?GLSL 着色器中 cos() 和 sin() 函数的速度?
【发布时间】:2012-04-14 15:54:38
【问题描述】:

我对Open GL Shader Languagesin()cos() 的速度信息感兴趣。

GLSL Specification Document 表示:

内置函数基本分为三类:

  • ...
  • ...
  • 它们代表一个操作图形硬件可能会在某个时候加速。三角函数属于这个 类别。

编辑:

正如已经指出的那样,计算 sin()cos() 等单个操作的时钟周期并不能真正说明整个性能故事。

所以为了澄清我的问题,我真正感兴趣的是是否值得优化掉 sin()cos() 对常见情况的调用。

例如,在我的应用程序中,参数为0 是很常见的。那么这样的事情是否有意义:

float sina, cosa;

if ( rotation == 0 )
{
   sina = 0;
   cosa = 1;
}
else
{
   sina = sin( rotation );
   cosa = cos( rotation );
}

或者GLSL 编译器或sin()cos() 实现会为我处理类似的优化吗?

【问题讨论】:

  • “现代 GPU 为sin()cos() 提供硬件加速”是什么意思?如果它在 GPU 上运行,则可以说是硬件加速的。无论如何,您最好的选择是尝试并对其进行分析,因为如果没有更多关于您正在做什么的上下文,GPU 上的时钟周期在某种程度上毫无意义。即使在来自同一供应商的不同卡之间,执行单元的数量也可能存在差异,因此循环只能告诉您部分情况。
  • 有了这些 GPU,我认为您将拥有这些三角函数的最快执行时间。有趣的问题...
  • 正如thisthis 问题中所指出的,这个问题基本上是无法回答的。 sin 的特定用途可能没有,具体取决于您使用它的位置和硬件。
  • @user1118321 好点。我修改了我的问题,试图让它更明确一点。
  • 对于上述情况,您可能会发现着色器执行两个分支,然后才决定使用哪个结果。在我看来,您在这里进行的这种优化不值得麻烦,甚至可能导致性能下降,而不是提高。

标签: optimization opengl glsl shader jogl


【解决方案1】:

例如,在我的应用程序中,参数为 0 是很常见的。所以这样的事情是否有意义:

没有。

你的编译器会做两件事之一。

  1. 它将发出一个实际的条件分支。在最好的情况下,如果 0 是一个局部一致的值(这样一组着色器通常会一起达到 0 或非零),那么您可能会获得改进的性能。
  2. 它将评估条件的双方,并且只存储其中正确一方的结果。在这种情况下,您一无所获。

一般来说,使用条件逻辑来围绕这样的小型表演跳舞并不是一个好主意。它需要非常大才有价值,例如 discard 或其他东西。

另外,请注意浮点等价不太可能起作用。除非您实际上将包含 0.0 的统一或顶点属性传递给着色器,否则不会。即使在 0 和非零之间进行插值,也可能永远不会为任何片段产生准确的 0。

【讨论】:

  • 我实际上会将 0.0 值作为顶点属性传递给着色器。但是好的一点,如果我没有测试这个值是一些远离 0 的小 epsilon 可能是必要的。但首先提出的观点可能不值得。
  • 根据每个着色器必须完成的工作量,您可能会通过拥有两种变体来取胜,一种用于您知道它为零的情况,另一种用于不为零的情况。但是切换着色器并不便宜,所以这取决于工作量。
  • @NicolBolas 实际上,在阅读了您的答案并记住了我的一些 CUDA 之后,我认为还有第三种选择:着色器可能会评估条件的第一侧,其中 rotation==0 而其他人阻止(或noop),然后在第一个阻止时评估第二面。这显然也很糟糕。虽然这是假设着色器评估类似于 CUDA 内核。
  • 有时discard 也很贵。如果您不介意写 Z,或者无论如何都不写 Z,零 alpha 写入会快得多。 (我已经获得了 100% 以上的加速,用 0 alpha 绘制替换丢弃。)当所有线程都在做同样的事情时,GPU 喜欢它。
【解决方案2】:

这是个好问题。我也想知道这个。

Google'd links say cossin 从 2005 年左右开始在主流卡上都是单周期的。

【讨论】:

    【解决方案3】:

    您必须自己对此进行测试,但我很确定在着色器中进行分支比 sincos 计算要昂贵得多。 GLSL 编译器在优化着色器方面做得很好,担心这是过早的优化。如果你后来发现,在整个程序中,你的着色器是瓶颈,那么你可以考虑优化它。

    如果您想查看特定平台的着色器的汇编代码,我建议您使用AMD GPU ShaderAnalyzer

    【讨论】:

    • "在 an 汇编代码"。着色器没有“ 程序集”。它随着平台的变化而变化。甚至从驱动程序修订到驱动程序修订。
    • 布尔制服上的分支可能是免费的。在适当的时候,我在这种情况下使用了这种技术。
    • @RobertRouhani 感谢 AMD GPU ShaderAnalyzer 链接。
    • 链接失效,这里是对 URL 的更新:developer.amd.com/tools-and-sdks/graphics-development/…
    【解决方案4】:

    不确定这是否能回答您的问题,但很难告诉您一条指令需要多少个时钟/插槽,因为它在很大程度上取决于 GPU。通常是一个循环。但即使没有,编译器也可能重新排列指令执行的顺序以隐藏真正的成本。对 sin/cos 使用纹理查找肯定会更慢,因为它是执行指令。

    【讨论】:

    • 我在规范opengl.org/registry/doc/GLSLangSpec.Full.1.40.05.pdf 中没有看到任何提及 sincos() 的内容,实际的函数名称是什么?那是扩展名吗?
    • 抱歉,实际上我认为这可能只是 D3D,即使这样我认为编译器会隐含地为它生成一个 sin 和一个 cos 指令。
    • FWIW,有一个 ARB 片段指令 SCS <operand> 在 x 分量中返回 sine(input.x),在 y 分量中返回 cos(input.x)。
    【解决方案5】:

    看看你可以在一个着色器中连续获得多少罪孽,与 math.abs、frac 等相比...我认为 gtx 470 每个片段可以处理 200 个罪孽函数没有问题,帧将是 10%比空着色器慢。它非常快,您可以发送结果。它将是计算效率的一个很好的指标。

    【讨论】:

      【解决方案6】:

      编译器评估两个分支,这使得条件非常昂贵。如果在着色器中同时使用 sin 和 cos,则只能计算 sin(a) 和 cos(a) = sqrt(1.0 - sin(a)),因为 sin(x)*sin(x) + cos(x)* cos(x) 始终为 1.0

      【讨论】:

      • sin(x) + cos(x) 通常不是 1.0。您可能正在考虑 sin(x) * sin(x) + cos(x) * cos(x) 为 1.0 的恒等式。虽然该身份可用于从另一个值计算一个值,但这涉及平方根,这可能与计算值一样昂贵。所以它并不是真的有用。此外,只要一起处理的所有片段值的条件值相同,现代 GPU 通常不会评估两个分支。
      • 是的,我在考虑毕达哥拉斯定理中的 cos^2(x)+ sin^2(x) = 1。我的错。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多