SSE 的条件语句
Conditional statements with SSE
我正在尝试为我的游戏做一些计算,我正在尝试计算两点之间的距离。本质上,我正在使用圆的方程来查看这些点是否在我定义的半径内。
(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);
一旦我得到结果变量,我就迷路了。如何使用刚刚得到的结果变量?我的计划是,如果评估的条件为真,则进行一些照明计算,然后在屏幕上绘制像素。在这种情况下,我如何解释真与假?
非常感谢任何朝着正确方向提供的帮助!
My plan was, if the condition evaluated turned out to be true, to do some lighting calculations and then draw the pixel on the screen.
那你真的别无选择只能分支
使用 SSE 进行条件测试的一大优势在于,它允许您编写 无分支 代码,从而显着提高速度。但在你的情况下,你几乎必须分支,因为如果我对你的理解正确,你 never 如果条件评估为假,你不想在屏幕上输出任何东西。
我的意思是,我猜你可以无条件地(推测性地)进行所有计算,然后只使用条件的结果来旋转像素值中的位,基本上导致你离开屏幕。那会给你无分支的代码,但它很傻。分支预测错误会受到惩罚,但它不会像所有计算和绘图代码那样昂贵。
换句话说,一旦您获得最终结果,您使用 SIMD 来利用的并行性就会耗尽。这只是一个简单的标量比较和分支。首先,您测试条件评估是否为真。如果没有,您将跳过 执行照明计算和像素绘制的代码。否则,您将无法执行该代码。
棘手的部分是编译器不允许您在常规的旧 if
语句中使用 __m128
变量,因此您需要 "convert" result
到一个整数,您可以将其用作条件的基础。最简单的方法是 _mm_movemask_epi8
intrinsic.
所以你基本上会这样做:
__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
内在函数。在 GCC 上,它坚持 __m128i
值。您可以通过强制转换来处理此问题:_mm_movemask_epi8((__m128i)result)
.
当然,我在这里假设您只在 所有 距离小于或等于最大距离时才进行绘图.如果你想独立对待四个距离中的每一个,那么你需要在mask上添加更多条件测试:
__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
块的 inside 中的一些光照计算。我不能确定这是否可行,因为我没有足够的关于你的 algorithm/design.
的详细信息
否则,如果您看不到从绘图代码中获得更多并行性的任何方法,那么使用显式 SSE 内在函数在这里不会给您带来太多好处。您能够并行化 一个 比较 (_mm_cmple_ps
),但设置该比较的开销 (_mm_set_ps
,可能会编译成 vinsertps
或 unpcklps
+movlhps
指令,假设输入已经在 XMM 寄存器中)将抵消您可能获得的任何微不足道的收益。可以说,您也可以像这样编写代码:
float maxDistSqr = k->getMaxDistance() * k->getMaxDistance();
if (distSqr1 <= maxDistSqr)
{
CalcLightingAndDraw(distSqr1);
}
if (distSqr2 <= maxDistSqr)
{
CalcLightingAndDraw(distSqr2);
}
if (distSqr3 <= maxDistSqr)
{
CalcLightingAndDraw(distSqr3);
}
if (distSqr4 <= maxDistSqr)
{
CalcLightingAndDraw(distSqr4);
}
我正在尝试为我的游戏做一些计算,我正在尝试计算两点之间的距离。本质上,我正在使用圆的方程来查看这些点是否在我定义的半径内。
(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);
一旦我得到结果变量,我就迷路了。如何使用刚刚得到的结果变量?我的计划是,如果评估的条件为真,则进行一些照明计算,然后在屏幕上绘制像素。在这种情况下,我如何解释真与假?
非常感谢任何朝着正确方向提供的帮助!
My plan was, if the condition evaluated turned out to be true, to do some lighting calculations and then draw the pixel on the screen.
那你真的别无选择只能分支
使用 SSE 进行条件测试的一大优势在于,它允许您编写 无分支 代码,从而显着提高速度。但在你的情况下,你几乎必须分支,因为如果我对你的理解正确,你 never 如果条件评估为假,你不想在屏幕上输出任何东西。
我的意思是,我猜你可以无条件地(推测性地)进行所有计算,然后只使用条件的结果来旋转像素值中的位,基本上导致你离开屏幕。那会给你无分支的代码,但它很傻。分支预测错误会受到惩罚,但它不会像所有计算和绘图代码那样昂贵。
换句话说,一旦您获得最终结果,您使用 SIMD 来利用的并行性就会耗尽。这只是一个简单的标量比较和分支。首先,您测试条件评估是否为真。如果没有,您将跳过 执行照明计算和像素绘制的代码。否则,您将无法执行该代码。
棘手的部分是编译器不允许您在常规的旧 if
语句中使用 __m128
变量,因此您需要 "convert" result
到一个整数,您可以将其用作条件的基础。最简单的方法是 _mm_movemask_epi8
intrinsic.
所以你基本上会这样做:
__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
内在函数。在 GCC 上,它坚持 __m128i
值。您可以通过强制转换来处理此问题:_mm_movemask_epi8((__m128i)result)
.
当然,我在这里假设您只在 所有 距离小于或等于最大距离时才进行绘图.如果你想独立对待四个距离中的每一个,那么你需要在mask上添加更多条件测试:
__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
块的 inside 中的一些光照计算。我不能确定这是否可行,因为我没有足够的关于你的 algorithm/design.
否则,如果您看不到从绘图代码中获得更多并行性的任何方法,那么使用显式 SSE 内在函数在这里不会给您带来太多好处。您能够并行化 一个 比较 (_mm_cmple_ps
),但设置该比较的开销 (_mm_set_ps
,可能会编译成 vinsertps
或 unpcklps
+movlhps
指令,假设输入已经在 XMM 寄存器中)将抵消您可能获得的任何微不足道的收益。可以说,您也可以像这样编写代码:
float maxDistSqr = k->getMaxDistance() * k->getMaxDistance();
if (distSqr1 <= maxDistSqr)
{
CalcLightingAndDraw(distSqr1);
}
if (distSqr2 <= maxDistSqr)
{
CalcLightingAndDraw(distSqr2);
}
if (distSqr3 <= maxDistSqr)
{
CalcLightingAndDraw(distSqr3);
}
if (distSqr4 <= maxDistSqr)
{
CalcLightingAndDraw(distSqr4);
}