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