SIMD---SSE系列及效率對比

SSE(即Streaming SIMD Extension),是對由MMX指令集引進的SIMD模型的擴展。咱們知道MMX有兩個明顯的缺點:git

  • 只能操做整數。
  • 不能與浮點數同時運行(MMX使用FPU寄存器做爲別名)。

而SSE則解決了這個問題,SSE引進了8個專用的浮點寄存器MMX0~MMX7。後來Intel又陸續推出了SSE二、SSE三、SSE4,這使得SSE指令系列同時擁有了浮點數學運算功能和整數運算功能,所以早先的MMX指令就顯得有點多餘了(雖然但是並行執行SSE、MMX指令來提升性能)。github

SSE系列功能特色

SSE1

  • 添加8個128位寄存器XMM0~MMX7.
  • 操做4個單精度浮點數。
  • 支持打包(Packed)標量(Scaler)操做。

SSE2

  • 添加整數向量運算,提高運行性能。
  • 支持雙精度浮點向量運算。
  • 打包類型取消限制,支持8位、16位、32位、64位,包括整數和浮點數。
  • 添加緩存控制指令
  • 添加浮點數到整數的轉換指令

SSE3

  • 寄存器內水平操做,例如打包在一個128位MMX寄存器內的2個64位整數,能夠利用新指令對這兩個整數進行算數運算。
  • 多線程優化指令,在超線程下提升性能。

SSSE3

  • 打包整形數據的運算加速

SSE4

SSE4包含兩個子集:SSE4.1和SSE4.2,併兼容之前的64位和IA-32指令集架構。值得指出的是SSE4增長了:1)STTNI(String and Text New Instructions)指令來幫助開發者處理字符搜索和比較,旨在加速對XML文件的解析;2)CRC32指令,幫助計算循環冗餘校驗值緩存

SSE效率對比

這裏咱們就只簡單比較下這兩個指令集的計算效率,其餘功能就不在本次考慮範圍內了。像前一篇博客同樣,咱們一樣用對10000000個字符進行加操做來進行對比操做。這裏咱們所有用Intrinsics來對比,方便編寫,不用考慮調用約定。多線程

整數計算 Mmx vs Sse

由於整數操做只在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.
}

浮點計算 Asm vs Sse

因爲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使用致使的略微性能缺失。上面兩種計算方式的效率對比結果以下:

result-pic

完整代碼見連接

相關文章
相關標籤/搜索