SSE 介紹 [Page 4]
現在我們就先以一個很簡單的例子,來說明 SSE 浮點運算指令的使用。下面是一個簡單的程式片斷:
- float input1[4] = { 1.2f, 3.5f, 1.7f, 2.8f };
- float input2[4] = { -0.7f, 2.6f, 3.3f, -0.8f };
- float output[4];
- for(int i = 0; i < 4; i++) {
- output[i] = input1[i] + input2[i];
- }
這個程式片斷的動作非常簡單,只是把 input1 和 input2 中的四組數字兩兩相加,並把結果寫到 output 中。在執行完後,output 的內容應該是 { 0.5f, 6.1f, 5.0f, 2.0f }。
上面的程式很適合用 SSE 浮點運算指令來做。它所進行的四個加法運算,剛好可以用一個 SSE 浮點運算指令,也就是 addps 來完成。以下是修改後的程式:
- float input1[4] = { 1.2f, 3.5f, 1.7f, 2.8f };
- float input2[4] = { -0.7f, 2.6f, 3.3f, -0.8f };
- float output[4];
- __m128 a = _mm_load_ps(input1);
- __m128 b = _mm_load_ps(input2);
- __m128 t = _mm_add_ps(a, b);
- _mm_store_ps(output, t);
上面的程式中,先用兩個 _mm_load_ps 把浮點數資料載入 __m128 的變數中。要注意的是,這裡是假設這兩個浮點數陣列都是對齊在 16 bytes 的邊上。如果不是的話,就不能用 _mm_load_ps 這個 intrinsic 來載入,而要改用 _mm_loadu_ps 這個 intrinsic。它是專門用來處理沒有對齊在 16 bytes 邊上的資料的。但是,它的速度會比較慢。
另外,因為 x86 的 little endian 特性,位址較低的 byte 會放在暫存器的右邊。也就是說,若以上面的 input1 為例,在載入到 XMM 暫存器後,暫存器中的 DATA0 會是 1.2,而 DATA1 是 3.5,DATA2 是 1.7,DATA3 是 2.8。如果需要以相反的順序載入的話,可以用 _mm_loadr_ps 這個 intrinsic。當然,在這個例子中,順序並不影響結果,所以不需要利用這個 intrinsic。
一般來說,宣告一個 float 的陣列,並不會對齊在 16 bytes 的邊上。如果希望它會對齊在 16 bytes 的邊上,以便利用 SSE 指令的話,Visual C++ 6.0 Processor Pack 和 Intel C++ compiler 都可以指定對齊方式。指定的方式如下:
- __declspec(align(16)) float input1[4];
在上例中,input1 會被對齊在 16 bytes 的邊上。這樣就可以直接用較快的 _mm_load_ps 來載入資料了。因為 SSE 浮點數指令常常需要資料對齊在 16 bytes 的邊上,所以在 xmmintrin.h 也定義了一個巨集 _MM_ALIGN16,是同樣的意義。因此,上面的程式也可以寫成:
- _MM_ALIGN16 float input1[4];
利用 SSE 浮點數指令會帶來多大的差別呢?對 1,024 個浮點數進行測試的結果,結果如下(使用 Intel C++ Compiler 5.0 for Windows 編譯,在 Intel Pentium III 1.0B Ghz 上執行):
可以看到利用 SSE 浮點運算可以得到大約兩倍於 x87 浮點運算的效率。事實上,因為 Pentium III 架構上的因素,所以,如果同時使用加法和乘法運算,最多可以得到四倍的效率。在後面會有一個例子。
[Part 1] [Page 2] [Part 3] [Part 4] [Part 5] [Part 6] [Part 7] [Part 8]
11/7/2001, Ping-Che Chen
Sorry, Traditional Chinese only. This page is encoded in UTF-8.
Copyright© 2000, 2001 Ping-Che Chen
