【问题标题】:How to use if condition in intrinsics如何在内部函数中使用 if 条件
【发布时间】:2016-10-26 16:09:09
【问题描述】:

我想使用内在函数比较两个浮点变量。如果比较是真的,做点别的做点什么。我想将此作为正常的 if..else 条件。有什么方法可以使用内在函数吗?

//normal code
vector<float> v1, v2;
for(int i = 0; i < v1.size(); ++i)
if(v1[i]<v2[i])
{
    //do something
}
else
{
    //do something
)

如何使用 SSE2 或 AVX 做到这一点?

【问题讨论】:

  • 将取决于您使用的内在函数。典型的方法是生成一个掩码,然后使用该掩码来确定两种可能的场景使用哪个值。
  • 你为什么要使用内在函数?为什么您期望编译器不会使用内置的比较运算符生成最佳代码?你试过了吗?
  • 发布一些实际代码肯定会有所帮助...
  • 如果您告诉我们您的目标架构是什么,也会有所帮助。我假设 x86,因为你已经标记了这个 [visual-c++],但是 x86 系列支持很多不同的指令集。您的目标是 32 位还是 64 位处理器?您是否必须将自己限制在 x87 FPU 上,还是可以使用 SSE? SSE2 甚至更新的指令集呢?
  • 我正在使用 win32 控制台应用程序。我可以使用 SSE2 和更新到 AVX 的指令。

标签: c++ visual-c++ compiler-optimization intrinsics


【解决方案1】:

SIMD 条件操作是使用无分支技术完成的。您使用压缩比较指令来获取全零或全一元素的向量。

例如当相应的元素匹配具有如下代码的条件时,您可以有条件地将 4 添加到累加器中的元素:

__m128i match_counts = _mm_setzero_si128();

for (...) {
    __m128  fvec = something;
    __m128i  condition = _mm_castps_si128( _mm_cmplt_ps(fvec, _mm_setzero_ps()) );  // for elements less than zero
    __m128i masked_constant = _mm_and_si128(condition, _mm_set1_epi32(4));
    match_counts = _mm_add_epi32(match_counts, masked_constant);
}

显然,只有当你能想出一种无分支的方式来处理分支的两侧时,这才有效。混合说明通常会有所帮助。

如果在分支的每一侧都有太多工作,您可能根本不会获得任何加速,尤其是当您的元素大小为 4 字节或更大时。 (当您在 16 个单独的字节上并行执行 16 次操作时,SIMD 非常强大,而在对四个 32 位元素执行 4 次操作时功能较弱)。

【讨论】:

  • 当您可以使用_mm_sub_epi32(match_counts, condition)-1 相减并保存指令时,为什么要使用1。我就是这样做的。
  • @Zboson:这就是我与set1(4) 进行与运算的原因,所以我的示例仍然很简单,但这种优化并没有直接应用。 (当然,使用 sub 来计算匹配,然后在循环外使用 _mm_psrli_epi32(match_counts, 2) 会更好。)
【解决方案2】:

如果您期望v1[i] &lt; v2[i] 几乎从不正确,几乎总是正确,或者通常长期保持不变(即使总体上可能没有特别的偏见),那么另一种技术也适用,它提供“真正的条件”(即不是“两者都做,放弃一个结果”),当然是一个代价,但你也可以实际跳过工作,而不是仅仅忽略一些结果。

该技术相当简单,进行比较(矢量化),使用_mm_movemask_ps 收集比较掩码,然后您就有 3 个案例:

  • 所有比较都以相同的方式进行,它们都是false,执行适当的“做某事”代码,由于条件消失,现在可能更容易向量化。
  • 所有的比较都是一样的,都是true,都一样。
  • 混合,使用更复杂的逻辑。根据您的需要,您可以分别检查所有位(回退到标量代码,但现在只比较整个批次的 1 个 FP),或者使用“仅迭代(取消)设置位”技巧之一(结合得很好使用 bitscan 来恢复实际索引),或者有时您可以像往常一样退回到进行屏蔽和合并。

并非所有 3 种情况都总是相关的,通常您会应用它,因为谓词几乎总是以相同的方式进行,这使得“完全相同”的情况之一如此罕见,以至于您可以将其与“混合”混为一谈.

这种技术绝对不是总是有用的。 “混合”情况复杂而缓慢。快速路径必须通用且足够快,值得测试您是否可以接受。

但它可能很有用,也许其中一侧非常缓慢且令人讨厌,而分支的另一侧则是很好的简单矢量化代码,相比之下不会花费那么长时间。例如,对于其他快速逼近的超越函数,慢速端可能需要进行参数缩减,或者它可能必须在获取点积之前对某些向量进行归一化,或者对矩阵进行正交化,甚至可能从磁盘获取数据。..

或者,也许双方都不是很慢,但他们从缓存中驱逐了彼此的数据(也许双方都是一个循环,在一个适合缓存的数组上,但数组不适合在一起)所以无条件地这样做减慢他们俩的速度。这可能是一个真实的东西,但我还没有在野外看到它。

或者,也许一方面不能被无条件地执行,做一些通常具有破坏性的事情,甚至可能是一些 IO。例如,如果您正在检查错误条件并记录它们。

【讨论】:

    【解决方案3】:

    我找到了一个对条件 SIMD 指令非常有用的文档。 这是我问题的完美解决方案。 If...else condition

    文档:http://saluc.engr.uconn.edu/refs/processors/intel/sse_sse2.pdf

    【讨论】:

    • 链接失效了,能否再分享一下文件或链接?
    猜你喜欢
    • 2021-11-30
    • 2016-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-29
    • 1970-01-01
    相关资源
    最近更新 更多