SSE的条件语句

时间:2017-06-23 01:01:13

标签: c++ conditional sse simd intrinsics

我试图为我的游戏做一些计算,我试图计算两点之间的距离。基本上,我使用圆的方程来看看这些点是否在我定义的半径范围内。

(x - x1)^2 + (y - y1)^2 <= r^2

我的问题是:如何使用SSE评估条件语句并解释结果?到目前为止,我有这个:

float distSqr4 = (pow(x4 - k->getPosition().x, 2) + pow(y4 - k->getPosition().y, 2));
float distSqr3 = (pow(x3 - k->getPosition().x, 2) + pow(y3 - k->getPosition().y, 2));
float distSqr2 = (pow(x2 - k->getPosition().x, 2) + pow(y2 - k->getPosition().y, 2));
float distSqr1 = (pow(x1 - k->getPosition().x, 2) + pow(y1 - k->getPosition().y, 2));

__m128 distances  = _mm_set_ps(distSqr1, distSqr2, distSqr3, distSqr4);
__m128 maxDistSqr = _mm_set1_ps(k->getMaxDistance() * k->getMaxDistance());
__m128 result     = _mm_cmple_ps(distances, maxDistSqr);

一旦我得到结果变量,我就迷路了。我如何使用刚刚得到的结果变量?我的计划是,如果评估的条件证明是真的,做一些光照计算,然后在屏幕上绘制像素。在这种情况下,我如何解释true vs false?

非常感谢任何朝着正确方向的帮助!

1 个答案:

答案 0 :(得分:6)

  

我的计划是,如果评估的条件证明是真的,做一些光照计算,然后在屏幕上绘制像素。

然后你真的别无选择,只能去分支。

使用SSE进行条件测试的一大优势是它允许您编写无分支代码,这可以带来大量的速度提升。但在你的情况下,你几乎必须分支,因为如果我正确地理解你,如果条件评估为假,你从不想要在屏幕上输出任何内容。

我的意思是,我猜你可以无条件地(推测性地)完成所有的计算,然后只使用条件的结果来旋转像素值中的位,从而导致你从屏幕中抽出。这会给你无分支代码,但它非常愚蠢。分支误预测会受到惩罚,但它不会像所有计算和绘图代码那样昂贵。

换句话说,一旦获得最终结果,您使用SIMD进行利用的并行性就会耗尽。它只是一个简单的标量比较和分支。首先,您测试条件是否评估为真。如果没有,您将跳过执行照明计算和像素绘制的代码。否则,您只需要执行该代码即可。

棘手的部分是编译器不允许您在常规的旧__m128语句中使用if变量,因此您需要&#34;转换&#34; result为整数,您可以将其用作条件的基础。最简单的方法是_mm_movemask_epi8内在。

所以你基本上就是这样做:

__m128 distances  = _mm_set_ps(distSqr1, distSqr2, distSqr3, distSqr4);
__m128 maxDistSqr = _mm_set1_ps(k->getMaxDistance() * k->getMaxDistance());
__m128 result     = _mm_cmple_ps(distances, maxDistSqr);

if (_mm_movemask_epi8(result) == (unsigned)-1)
{
    // All distances were less-than-or-equal-to the maximum, so
    // go ahead and calculate the lighting and draw the pixels.
    CalcLightingAndDraw(…);
}

这是有效的,因为_mm_cmple_ps如果比较为真,则将每个打包的双字设置为全1,如果比较为假,则设置为全0。然后_mm_movemask_epi8将其折叠为整数大小的掩码并将其移动到整数值。然后,您可以在正常的条件语句中使用该整数值。

注意:使用Clang和ICC,您可以将__m128值传递给_mm_movemask_epi8内在函数。在海湾合作委员会,它坚持__m128i价值。您可以使用强制转换来处理此问题:_mm_movemask_epi8((__m128i)result)

当然,我假设您只是在所有的距离小于或等于最大距离时才会进行绘图。如果您想独立处理的四个距离,则需要在面具上添加更多条件测试:

__m128   distances  = _mm_set_ps(distSqr1, distSqr2, distSqr3, distSqr4);
__m128   maxDistSqr = _mm_set1_ps(k->getMaxDistance() * k->getMaxDistance());
__m128   result     = _mm_cmple_ps(distances, maxDistSqr);
unsigned condition  = _mm_movemask_epi8(result);

if (condition != 0)
{
    // One or more of the distances were less-than-or-equal-to the maximum,
    // so we have something to draw.

    if ((condition & 0x000F) != 0)
    {
        // distSqr1 was less-than-or-equal-to the maximum
        CalcLightingAndDraw(distSqr1);
    }
    if ((condition & 0x00F0) != 0)
    {
        // distSqr2 was less-than-or-equal-to the maximum
        CalcLightingAndDraw(distSqr2);
    }
    if ((condition & 0x0F00) != 0)
    {
        // distSqr3 was less-than-or-equal-to the maximum
        CalcLightingAndDraw(distSqr3);
    }
    if ((condition & 0xF000) != 0)
    {
        // distSqr4 was less-than-or-equal-to the maximum
        CalcLightingAndDraw(distSqr4);
    }
}

这不会导致非常高效的代码,因为您必须执行许多条件测试和分支操作。您可以继续并行化主if块中内部的一些照明计算。我无法确定这是否可行,因为我没有足够的有关您的算法/设计的详细信息。

否则,如果您无法通过任何方式从绘图代码中获取更多并行性,那么使用显式SSE内在函数并不会为您带来太多收益。您可以并行化一个比较(_mm_cmple_ps),但设置进行比较的开销(_mm_set_ps,可能会编译成vinsertps或{假设输入已经在XMM寄存器中,{1}} + unpcklps指令将不仅取消您可能获得的任何微不足道的收益。你可以这样编写代码就像这样:

movlhps