SSE 介紹 [Page 5]
看了簡單的例子之後,我們現在列出一些 SSE 浮點運算指令所支援的一些基本的運算:
| 指令 | Intrinsic | 功能 |
addps/addss | _mm_add_ps/_mm_add_ss | 加法 |
subps/subss | _mm_sub_ps/_mm_sub_ss | 減法 |
mulps/mulss | _mm_mul_ps/_mm_mul_ss | 乘法 |
divps/divss | _mm_div_ps/_mm_div_ss | 除法 |
sqrtps/sqrtss | _mm_sqrt_ps/_mm_sqrt_ss | 平方根 |
maxps/maxss | _mm_max_ps/_mm_max_ss | 逐項取最大值 |
minps/minss | _mm_min_ps/_mm_min_ss | 逐項取最小值 |
這些運算基本上都符合 IEEE 754 中的規範,根據其計算結果,設定適當的旗標(divide-by-zero、invalid 等等),或是產生 exception。這可以由一個 MXCSR 暫存器來設定。MXCSR 暫存器是一個 32 位元的旗標暫存器,可以設定是否要產生各種 exception,並會記錄上次的計算中,發生了哪些情況。下面是 MXCSR 暫存器的說明圖:
以下是 MXCSR 各欄位的說明:
| 欄位 | 名稱 | Intrinsic 巨集 | 說明 |
| IE | Invalid Operation Flag | _MM_EXCEPT_INVALID | 運算中發生 invalid operation |
| DE | Denormal Flag | _MM_EXCEPT_DENORMA | 試圖載入 denormal value 或對 denormal value 進行運算 |
| ZE | Divide-by-Zero Flag | _MM_EXCEPT_DIV_ZERO | 運算中發生 divide by zero |
| OE | Overflow Flag | _MM_EXCEPT_OVERFLOW | 運算中發生 overflow |
| UE | Underflow Flag | _MM_EXCEPT_UNDERFLOW | 運算中發生 underflow |
| PE | Precision Flag | _MM_EXCEPT_INEXACT | 運算結果不精確(inexact) |
| DAZ | Denormals Are Zeros | N/A | 強制將 denormals 視為 0 |
| IM | Invalid Operation Mask | _MM_MASK_INVALID | 關閉 invalid operation exception |
| DM | Denormal Operation Mask | _MM_MASK_DENORM | 關閉 denormal operation exception |
| ZM | Divide-by-Zero Mask | _MM_MASK_DIV_ZERO | 關閉 divide by zero exception |
| OM | Overflow Mask | _MM_MASK_OVERFLOW | 關閉 overflow exception |
| UM | Underflow Mask | _MM_MASK_UNDERFLOW | 關閉 underflow exception |
| PM | Precision Mask | _MM_MASK_INEXACT | 關閉 inexact exception |
| RC | Rounding Control | N/A | 設定捨去方式 |
| FZ | Flush to Zero | N/A | 打開 Flush-to-zero 模式 |
上表中有幾個地方是需要特別注意的:
- DAZ 若設為 1,則會開啟 Denormals Are Zeros 模式。當此模式開啟時,若來源參數中有任何 denormals,都會被視為 0,而且不會設定 DE 或產生 denormal operation exception。這個模式和 IEEE 754 規範不相容,但是通常會得到更好的效率。另外 DAZ 目前只在 Intel Pentium 4 和 Intel Xeon 處理器上有支援。有關判斷 DAZ 是否支援的方式,請參考 IA-32 Intel Architecture Software Developer's Manual, Volume 1: Basic Architecture, Section 11.6.3。
- RC 可以設定成四種方式:00 為 Round to nearest (even),即將運算結果捨去至最接近數值。如果兩個數值同樣接近,則捨去至偶數。01 為 Round down,即往負無限大方向捨去。10 為 Round up,即往正無限大方向捨去。11 則是 Round toward zero (truncate),即往零的方向捨去。
- FZ 若設為 1,則會開啟 Flush-to-zero 模式。當此模式開啟,且 underflow exception 被關閉時,若在運算時發生 underflow,則結果為 0(符號同真正結果),且會設定 PE 和 UE。若 underflow exception 未關閉,則此模式無任何影響。注意此模式和 IEEE 754 規範不相容(規範要求 underflow 需產生 denormal 結果),但通常會得到更好的效率。
設定和讀取 MXCSR 同樣有 intrinsic 可以用,即 _mm_getcsr 和 _mm_setcsr。不過,因為 MXCSR 基本上是由 bit field 組成,因此在 xmmintrin.h 裡面也定義了一些方便的巨集。這些巨集包括:
| 巨集 | 功用 |
_MM_SET_EXCEPTION_STATE | 設定 exception 狀態(IE ~ PE) |
_MM_GET_EXCEPTION_STATE | 取得 exception 狀態(IE ~ PE) |
_MM_SET_EXCEPTION_MASK | 設定 exception mask(IM ~ PM) |
_MM_GET_EXCEPTION_MASK | 取得 exception mask(IM ~ PM) |
_MM_SET_ROUNDING_MODE | 設定捨去方式 |
_MM_GET_ROUNDING_MODE | 取得目前的捨去方式 |
_MM_SET_FLUSH_ZERO_MODE | 設定 Flush-to-zero 模式(_MM_FLUSH_ZERO_ON 和 _MM_FLUSH_ZERO_OFF) |
_MM_GET_FLUSH_ZERO_MODE | 取得 Flush-to-zero 模式設定 |
捨去方式的設定包括:
_MM_ROUND_NEAREST | Round to nearest (even) |
_MM_ROUND_DOWN | Round down |
_MM_ROUND_UP | Round up |
_MM_ROUND_TOWARD_ZERO | Round toward zero (truncate) |
在開機時,所有的 exception mask 都是設定為 1,也就是所有的 exception 都是關閉的。如果有需要的話,可以將其開啟。下面的程式片斷是一個例子:
- _MM_ALIGN16 float test1[4] = { 0, 0, 0, 1 };
- _MM_ALIGN16 float test2[4] = { 1, 2, 3, 0 };
- _MM_ALIGN16 float out[4];
- _MM_SET_EXCEPTION_MASK(0); // 打開所有的 exception
- __try {
- __m128 a = _mm_load_ps(test1);
- __m128 b = _mm_load_ps(test2);
- a = _mm_div_ps(a, b);
- _mm_store_ps(out, a);
- }
- __except(EXCEPTION_EXECUTE_HANDLER) { // exception handler
- if(_mm_getcsr() & _MM_EXCEPT_DIV_ZERO) {
- cout << "Divide by zero" << endl;
- }
- return;
- }
- cout << out[3] << endl;
上面的程式在執行時,會產生 divide by zero 的 exception,並進入 exception handler。在 exception handler 中,因為 ZE 會被設定,因此會顯示出 "Divide by zero" 的錯誤訊息。如果把 _MM_SET_EXCEPTION_MASK(0); 這一行去掉,則不會產生 exception,而會直接輸出正無限大(1.#INF)的結果。
[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
