SSE(即Streaming SIMD Extension),是对由MMX指令集引进的SIMD模型的扩展。咱们知道MMX有两个明显的缺点:git
而SSE则解决了这个问题,SSE引进了8个专用的浮点寄存器MMX0~MMX7。后来Intel又陆续推出了SSE二、SSE三、SSE4,这使得SSE指令系列同时拥有了浮点数学运算功能和整数运算功能,所以早先的MMX指令就显得有点多余了(虽然但是并行执行SSE、MMX指令来提升性能)。github
SSE4包含两个子集:SSE4.1和SSE4.2,并兼容之前的64位和IA-32指令集架构。值得指出的是SSE4增长了:1)STTNI(String and Text New Instructions)指令来帮助开发者处理字符搜索和比较,旨在加速对XML文件的解析;2)CRC32指令,帮助计算循环冗余校验值。缓存
这里咱们就只简单比较下这两个指令集的计算效率,其余功能就不在本次考虑范围内了。像前一篇博客同样,咱们一样用对10000000个字符进行加操做来进行对比操做。这里咱们所有用Intrinsics来对比,方便编写,不用考虑调用约定。多线程
由于整数操做只在SSE2中支持,因此实际上咱们用的是sse2指令。架构
mmx代码:oop
void calculateUsingMmx(char* data, unsigned size) { assert(size % 8 == 0); __m64 step = _mm_set_pi8(10, 10, 10, 10, 10, 10, 10, 10); __m64* dst = reinterpret_cast<__m64*>(data); for (unsigned i = 0; i < size; i += 8) { auto sum = _mm_adds_pi8(step, *dst); *dst++ = sum; } _mm_empty(); }
sse代码:性能
void calculateUsingSseInt(char* data, unsigned size) { assert(size % 16 == 0); __m128i step = _mm_set_epi8(10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10); __m128i* dst = reinterpret_cast<__m128i*>(data); for (unsigned i = 0; i < size; i += 16) { auto sum = _mm_add_epi8(step, *dst); *dst++ = sum; } // no need to clear flags like mmx because SSE and FPU can be used at the same time. }
因为MMX指令集不包含浮点指令,所以咱们x86浮点指令来对比,一样对10000000个flaot值进行加操做。优化
Asm代码:线程
void calculateUsingAsmFloat(float* data, unsigned count) { auto singleFloatBytes = sizeof(float); auto step = 10.0; __asm { push ecx push edx mov edx, data mov ecx, count fld step // fld only accept FPU or Memory calcLoop: fld [edx] fadd st(0), st(1) fstp [edx] add edx, singleFloatBytes dec ecx jnz calcLoop pop edx pop ecx } }
Sse代码:code
void calculateUsingSseFloat(float* data, unsigned count) { assert(count % 4 == 0); assert(sizeof(float) == 4); __m128 step = _mm_set_ps(10.0, 10.0, 10.0, 10.0); __m128* dst = reinterpret_cast<__m128*>(data); for (unsigned i = 0; i < count; i += 4) { __m128 sum = _mm_add_ps(step, *dst); *dst++ = sum; } }
SSE2的浮点计算对比x86浮点计算性能提高不是很是明显,可是也要考虑Intrinsics使用致使的略微性能缺失。上面两种计算方式的效率对比结果以下:
完整代码见连接。