Nik*_*iki 24
Ski*_*izz 15
以SSE为例的SIMD允许您对多个数据块执行相同的操作.因此,使用SSE作为整数运算的直接替换将不会有任何好处,只有您可以同时对多个数据项执行操作时才会获得优势.这涉及加载一些在内存中连续的数据值,执行所需的处理,然后单步执行数组中的下一组值.
问题:
1如果代码路径依赖于正在处理的数据,则SIMD变得更难实现.例如:
a = array [index];
a &= mask;
a >>= shift;
if (a < somevalue)
{
a += 2;
array [index] = a;
}
++index;
Run Code Online (Sandbox Code Playgroud)
像SIMD一样不容易做到:
a1 = array [index] a2 = array [index+1] a3 = array [index+2] a4 = array [index+3]
a1 &= mask a2 &= mask a3 &= mask a4 &= mask
a1 >>= shift a2 >>= shift a3 >>= shift a4 >>= shift
if (a1<somevalue) if (a2<somevalue) if (a3<somevalue) if (a4<somevalue)
// help! can't conditionally perform this on each column, all columns must do the same thing
index += 4
Run Code Online (Sandbox Code Playgroud)
2如果数据不重要,则将数据加载到SIMD指令中是很麻烦的
3代码是特定于处理器的.SSE仅适用于IA32(Intel/AMD),并非所有IA32 cpus都支持SSE.
您需要分析算法和数据以查看它是否可以进行SSE并且需要了解SSE的工作原理.英特尔网站上有大量文档.
小智 10
这种问题是良好的低级分析器必不可少的完美例子.(像VTune这样的东西)它可以让你更加了解你的热点所在的位置.
我的猜测,从你描述的是你的热点可能是使用if/else计算最小/最大值导致的分支预测失败.因此,使用SIMD内在函数应该允许您使用最小/最大指令,但是,尝试使用无分支最小/最大计算可能是值得的.这可以通过减少痛苦来实现大部分收益.
像这样的东西:
inline int
minimum(int a, int b)
{
int mask = (a - b) >> 31;
return ((a & mask) | (b & ~mask));
}
Run Code Online (Sandbox Code Playgroud)
如果您使用SSE指令,则显然仅限于支持这些指令的处理器.这意味着x86,可以追溯到Pentium 2左右(不记得确切的时候它们被引入,但是很久以前)
SSE2,据我所知,是提供整数运算的,有点近一些(Pentium 3?虽然第一款AMD Athlon处理器不支持它们)
在任何情况下,您都有两种使用这些说明的选项.要么在汇编中编写整个代码块(可能是一个坏主意.这使得编译器几乎不可能优化代码,并且人类很难编写高效的汇编程序).
或者,使用编译器可用的内在函数(如果内存服务,它们通常在xmmintrin.h中定义)
但同样,性能可能无法提高.SSE代码对其处理的数据提出了额外的要求.主要是要记住的是数据必须在128位边界上对齐.加载到同一寄存器中的值之间也应该存在很少或没有依赖关系(128位SSE寄存器可以容纳4个整数.将第一个和第二个加在一起并不是最佳的.但是将所有四个整数添加到相应的4个整数中另一个注册会很快)
使用包含所有低级SSE摆弄的库可能很诱人,但这也可能会破坏任何潜在的性能优势.
我不知道SSE的整数运算支持有多好,所以这也可能是一个可以限制性能的因素.SSE主要针对加速浮点运算.