SSE (Streaming SIMD Extensions) 是 Intel 自 Pentium III 起引入的 128-bit SIMD 指令集,單指令並行處理 4 個 float (32-bit),廣泛用於多媒體、科學計算、圖形渲染、機器學習推理等資料並行場景。本文系統介紹 SSE 資料型別、核心內建函式、對齊需求、效能優化模式與實戰基準測試。
XMM0~XMM15 (x64) / XMM0~XMM7 (x86),每個可視為 4×float、2×double、4×int32、2×int64、16×int8。__m128 (4×float)、__m128d (2×double)、__m128i (整數向量)。__m128 變數與記憶體操作必須 16-byte 對齊,否則觸發 General Protection Fault (#GP) 或效能大幅下降。| 分類 | 內建函式 | 對應指令 | 說明 |
|---|---|---|---|
| 載入/儲存 | _mm_load_ps / _mm_store_ps | MOVAPS | 16-byte 對齊載入/儲存 4 float |
| _mm_loadu_ps / _mm_storeu_ps | MOVUPS | 非對齊載入/儲存 (較慢) | |
| _mm_set_ps / _mm_set1_ps / _mm_setzero_ps | SHUFPS + MOVAPS | 建構向量:4值/單值廣播/全零 | |
| 算術 | _mm_add_ps / _mm_sub_ps / _mm_mul_ps / _mm_div_ps | ADDPS/SUBPS/MULPS/DIVPS | 四元素並行 + - * / |
| _mm_sqrt_ps / _mm_rcp_ps / _mm_rsqrt_ps | SQRTPS/RCPPS/RSQRTPS | 平方根/倒數/倒數平方根 (近似) | |
| 比較 | _mm_cmpeq_ps / _mm_cmplt_ps / _mm_cmpgt_ps | CMPPS | 逐元素比較,產生 0xFFFFFFFF/0 遮罩 |
| 邏輯 | _mm_and_ps / _mm_or_ps / _mm_xor_ps / _mm_andnot_ps | ANDPS/ORPS/XORPS/ANDNPS | 逐位元邏輯運算 |
| 洗牌/排列 | _mm_shuffle_ps / _mm_permute_ps / _mm_blend_ps | SHUFPS/PERMPS/BLENDPS | 元素重排、混合 |
| 水平運算 | _mm_hadd_ps / _mm_dp_ps (SSE3/4) | HADDPS/DPPS | 向量內元素相加/點積 |
| 轉換 | _mm_cvtepi32_ps / _mm_cvtps_epi32 | CVTDQ2PS/CVTPS2DQ | int32 ↔ float 轉換 |
所有內建函式定義於 <xmmintrin.h> (SSE)、<emmintrin.h> (SSE2)、<pmmintrin.h> (SSE3)、<smmintrin.h> (SSE4.1/4.2)、<immintrin.h> (AVX/AVX2/AVX-512 總覽)。
__declspec(align(16)) float arr[4] = {1,2,3,4};
__m128 v = _mm_load_ps(arr); // 安全
// C++17 aligned_alloc / _aligned_malloc
float* p = (float*)_aligned_malloc(1000 * sizeof(float), 16);
__m128 v = _mm_load_ps(p + i*4); // 安全
_aligned_free(p);
struct alignas(16) Vec4 { float x,y,z,w; }; // C++11
// 或 __declspec(align(16)) struct Vec4 { float x,y,z,w; };
#include <xmmintrin.h>
#include <windows.h>
#include <iostream>
using namespace std;
class Timer {
LARGE_INTEGER freq, start;
public:
Timer() { QueryPerformanceFrequency(&freq); Reset(); }
void Reset() { QueryPerformanceCounter(&start); }
double Elapsed() {
LARGE_INTEGER now; QueryPerformanceCounter(&now);
return double(now.QuadPart - start.QuadPart) / freq.QuadPart;
}
};
void Scale_SSE(float* __restrict p, size_t n, float s) {
__m128 vs = _mm_set1_ps(s);
size_t i = 0;
for (; i + 3 < n; i += 4) {
__m128 v = _mm_load_ps(p + i); // 假設 16-byte 對齊
_mm_store_ps(p + i, _mm_mul_ps(v, vs));
}
// 剩餘 1-3 元素純量處理
for (; i < n; ++i) p[i] *= s;
}
void Scale_Scalar(float* p, size_t n, float s) {
for (size_t i = 0; i < n; ++i) p[i] *= s;
}
int main() {
const size_t N = 10000;
const int ITER = 100000;
__declspec(align(16)) float data[N];
Timer t;
for (int k = 0; k < ITER; ++k) Scale_SSE(data, N, 1000.0f);
cout << "SSE: " << t.Elapsed() << " s\n";
t.Reset();
for (int k = 0; k < ITER; ++k) Scale_Scalar(data, N, 1000.0f);
cout << "Scalar: " << t.Elapsed() << " s\n";
return 0;
}
SSE: 0.42 s
Scalar: 1.35 s
加速比: ~3.2x
分析:理論 4x,實際受限於記憶體頻寬、迴圈開銷、尾部純量處理、指令吞吐率。
| 面向 | 自動向量化 (/O2 /arch:SSE2) | 手寫內建函式 |
|---|---|---|
| 開發效率 | 高 (純 C++ 迴圈) | 低 (需精通指令集) |
| 可移植性 | 高 (重編譯即支援 AVX/ARM NEON) | 低 (需重寫) |
| 最佳化上限 | 受限於編譯器分析能力 | 完全控制指令選擇、排程、暫存器分配 |
| 除錯難度 | 低 | 高 (需查看反組譯) |
建議策略:優先撰寫清晰的 C++ 迴圈,開啟 /O2 /arch:AVX2 讓編譯器自動向量化;熱點函式經 Profiler 確認瓶頸後,再手寫內建函式微調。
__m128d (2×double)、__m128i (整數 SIMD)、CVTSI2SD 等,現代基準指令集。__m256、VEX 前綴、三操作數指令 (非破壞源操作數)。__m512、遮罩暫存器 (k0-k7)、嵌入式廣播/轉換/彈性彈性。編譯器選項 /arch:AVX2 或 /arch:AVX512 可自動生成對應指令,內建函式命名規則統一 (_mm256_*, _mm512_*)。
Structure of Arrays (SoA) 而非 Array of Structures (AoS),利於向量化載入。__restrict (C99) / [[gnu::always_inline]] 協助編譯器別名分析與內聯。static_assert(alignof(Vec4) == 16, "SSE alignment");。simd::float4 類別,內部根據 __SSE__/__AVX__/__ARM_NEON 切換實作。SSE 為現代高效能計算基石,掌握內建函式、對齊規則、自動向量化協作技巧,能讓 C/C++ 程式在科學計算、影像處理、遊戲物理等領域釋放 CPU 最大算力。