如何在内部函数中使用 if 条件

How to use if condition in intrinsics

我想使用内部函数比较两个浮点变量。如果比较是真的,做点别的做点什么。我想按照正常的 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 执行此操作?

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 次操作时则不那么强大)。

如果您认为 v1[i] < v2[i] 几乎从不为真,几乎总是为真,或者通常在很长一段时间内保持不变 运行(即使总体上可能没有特别的偏见),那么其他技术也适用,它提供 "true conditionality"(即不是 "do both, discard one result"),当然要付出代价,但你也可以实际跳过工作而不是仅仅忽略一些结果。

该技术相当简单,进行比较(向量化),使用 _mm_movemask_ps 收集比较掩码,然后您有 3 种情况:

  • 所有比较都以相同的方式进行,它们都是 false,执行适当的 "do something" 代码,由于条件消失,现在可能更容易矢量化。
  • 所有比较都以相同的方式进行,它们都是 true,相同。
  • 混合,使用更复杂的逻辑。根据您的需要,您可以分别检查所有位(回退到标量代码,但现在整个批次只需 1 FP 比较),或使用 "iterate only over (un)set bits" 技巧之一(与位扫描很好地结合以恢复实际索引),或者有时您可以退回到像往常一样进行屏蔽和合并。

并非所有 3 种情况都总是相关的,通常你应用它是因为谓词几乎总是以相同的方式进行,使得 "all the same" 情况之一非常罕见,你可以将它与 "mixed".

这个技巧并不总是有用的。 "mixed" 案例复杂而缓慢。快速路径必须是通用的并且足够快以值得测试你是否可以接受它。

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

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

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

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

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