如何优化If/then条件表达式的长序列- SIMD
How to optimization long series of If/then conditional expressions - SIMD
我使用SIMD来提高C代码的性能,但我遇到了一个函数,其中有许多if/then条件,如下所示:
if (Di <= -T3) return -4;
if (Di <= -T2) return -3;
if (Di <= -T1) return -2;
if (Di < -NEAR) return -1;
if (Di <= NEAR) return 0;
if (Di < T1) return 1;
if (Di < T2) return 2;
if (Di < T3) return 3;
return 4;
使用vc++编译器支持的Intel内部函数,处理时间较慢。
那么,有没有更好的方法来优化这一长串条件表达式呢?
我假设了以下几点:
- 你处理int32数据(它可以很容易地改变为float32,虽然)。
- 你可以一次传递4个值给你的函数(不只是一个)。这就是人们通常所说的向量化。
- 常数排序,即0
这是一个向量化函数:
__m128i func4(__m128i D) {
__m128i cmp_m3 = _mm_cmpgt_epi32(D, _mm_set1_epi32(-T3));
__m128i cmp_m2 = _mm_cmpgt_epi32(D, _mm_set1_epi32(-T2));
__m128i cmp_m1 = _mm_cmpgt_epi32(D, _mm_set1_epi32(-T1));
__m128i cmp_p0 = _mm_cmpgt_epi32(D, _mm_set1_epi32(NEAR));
__m128i reduce_true = _mm_add_epi32(_mm_add_epi32(cmp_m3, cmp_m2), _mm_add_epi32(cmp_m1, cmp_p0));
__m128i cmp_m0 = _mm_cmplt_epi32(D, _mm_set1_epi32(-NEAR));
__m128i cmp_p1 = _mm_cmplt_epi32(D, _mm_set1_epi32(T1));
__m128i cmp_p2 = _mm_cmplt_epi32(D, _mm_set1_epi32(T2));
__m128i cmp_p3 = _mm_cmplt_epi32(D, _mm_set1_epi32(T3));
__m128i reduce_false = _mm_add_epi32(_mm_add_epi32(cmp_p3, cmp_p2), _mm_add_epi32(cmp_p1, cmp_m0));
return _mm_sub_epi32(reduce_false, reduce_true);
}
如果输入数据是随机的,那么它的工作速度比MSVC2013 x64的Ivy Bridge上的原始版本快11倍:
Time = 4.436 (-39910000)
Time = 0.409 (-39910000)
完整的测试代码在这里。
这个想法相当简单。您可以在上面链接的函数funcX
中看到建议解决方案的非矢量化版本。它可能比语言更能解释一切。
我们将寄存器D
作为输入,它包含4个打包值。然后我们把它和_mm_cmp*
固有的8个常数进行比较。这个比较产生了8个位掩码cmp_pX
, cmp_mX
。在位掩码中,与一个数字相对应的所有位不是0就是1。每次比较都设置32个0位,这是假的。如果比较条件为真,则32位设置为1。
现在回想一下,所有位都为1的32位整数在有符号表示中是-1。当我们把四个比较结果加在一起时,我们得到一组计数是否定的。最后,取两个计数的差,即为期望的结果。
注:下面是为内循环生成的汇编代码:
movdqa xmm3, XMMWORD PTR [rcx]
movdqa xmm4, xmm10
movdqa xmm0, xmm9
add rcx, 16
pcmpgtd xmm0, xmm3
pcmpgtd xmm4, xmm3
paddd xmm4, xmm0
movdqa xmm2, xmm3
movdqa xmm1, xmm8
pcmpgtd xmm1, xmm3
pcmpgtd xmm2, xmm14
movdqa xmm0, xmm7
pcmpgtd xmm0, xmm3
paddd xmm1, xmm0
paddd xmm4, xmm1
movdqa xmm0, xmm3
movdqa xmm1, xmm3
pcmpgtd xmm1, xmm12
pcmpgtd xmm0, xmm13
pcmpgtd xmm3, xmm11
paddd xmm1, xmm3
paddd xmm2, xmm0
paddd xmm2, xmm1
psubd xmm4, xmm2
paddd xmm4, xmm5
movdqa xmm5, xmm4
cmp rcx, r15
jl SHORT $LL3@main
您可以尝试摆脱所有条件并重新测量时间。代码
if (Di <= -T3) return -4;
if (Di <= -T2) return -3;
if (Di <= -T1) return -2;
if (Di < -NEAR) return -1;
if (Di <= NEAR) return 0;
if (Di < T1) return 1;
if (Di < T2) return 2;
if (Di < T3) return 3;
return 4;
可以转换为无条件形式:
return
(Di <= -T3)*(-4) + (Di > -T3) * (
(Di <= -T2)*(-3) + (Di > -T2) * (
(Di <= -T1)*(-2) + (Di > -T1) * (
(Di < -NEAR)*(-1) + (Di >= -NEAR) * (
(Di <= NEAR)*0 + (Di > NEAR) * (
(Di < T1)*1 + (Di >= T1) * (
(Di < T2)*2 + (Di >= T2) * (
(Di < T3)*3 + (Di >= T3) * (
4
))))))));
也许,您可以进一步优化这段代码,对变量的可能内容有一些了解。
相关文章:
- (C++)分析树以计算返回错误值的简单算术表达式
- 在VS2010-VS2015下编译时,如何使用decltype作为较大类型表达式的LHS
- 提升精神:解析布尔表达式并简化为规范范式
- 不能在初始值设定项列表中将非常量表达式从类型 'int' 缩小到'unsigned long long'
- 使用正则表达式regex_search在字符串中查找字符串
- 如何确认我的constexpr表达式实际上已经在编译时执行
- 概念中的cv限定符需要表达式参数列表
- 为什么constexpr的性能比正常表达式差
- 对于结构,表达式必须是可修改的ivalue
- 当一个值是非常量但用常量表达式初始化时使用constexpr
- 将fold表达式与std::一起用于两个元组
- 断言中的Fold表达式在某些计算机上编译,但在其他计算机上不编译
- 标记 '","' 之前的预期主表达式
- gcc和clang在表达式是否为常量求值的问题上存在分歧
- 如何计算具有指定类型的表达式的相对精度和绝对精度
- 带有用户定义类的c++折叠表达式
- 即使使用调试编译标志,表达式也是"optimized out"
- holeMenuProgram.cpp:38:1 错误:'}'令牌之前的预期主表达式
- 如何优化If/then条件表达式的长序列- SIMD
- 用于延迟计算SIMD/SSE表达式的C/ c++库