dev*_*ner 1 c++ conditional sse simd intrinsics
我正在尝试为我的游戏做一些计算,我正在尝试计算两点之间的距离.基本上,我正在使用圆的方程来查看这些点是否在我定义的半径范围内.
(x - x1)^2 + (y - y1)^2 <= r^2
Run Code Online (Sandbox Code Playgroud)
我的问题是:如何用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);
Run Code Online (Sandbox Code Playgroud)
一旦我得到结果变量,我就迷路了.我如何使用刚刚得到的结果变量?我的计划是,如果评估的条件证明是真的,做一些光照计算,然后在屏幕上绘制像素.在这种情况下,我如何解释真实与虚假?
任何有关正确方向的帮助都非常感谢!
我的计划是,如果评估的条件证明是真的,做一些光照计算,然后在屏幕上绘制像素.
然后你真的别无选择,只能分支.
使用SSE进行条件测试的一大优势是它允许您编写无分支代码,这可以大大提高速度.但在你的情况下,你几乎必须分支,因为如果我正确理解你,如果条件评估为假,你永远不想在屏幕上输出任何东西.
我的意思是,我猜你可以无条件地(推测性地)进行所有的计算,然后只使用条件的结果来旋转像素值中的位,实际上会导致你从屏幕中抽出.这会给你无分支代码,但它很傻.分支错误预测会受到惩罚,但它不会像所有计算和绘图代码那样昂贵.
换句话说,一旦获得最终结果,您使用SIMD进行利用的并行性就会耗尽.它只是一个简单的标量比较和分支.首先,您测试条件是否评估为真.如果没有,你会跳了,做照明计算和像素绘图代码.否则,您将完全执行该代码.
棘手的部分是编译器不允许您__m128在常规旧if语句中使用变量,因此您需要"转换" 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(…);
}
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为_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);
}
}
Run Code Online (Sandbox Code Playgroud)
这不会产生非常高效的代码,因为您必须执行许多条件测试和分支操作.您可以继续并行化主块内部的一些照明计算if.我不能确定这是否可行,因为我没有足够的有关您的算法/设计的细节.
否则,如果您无法看到任何方法从绘图代码中获取更多并行性,那么使用显式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);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
717 次 |
| 最近记录: |