單指令多數據流,即SIMD(Single Instruction, Multiple Data)指一類可以在單個指令週期內同時處理多個數據元素的指令集,利用的是數據級並行來提升運行效率,典型的表明由Intel的MMX和SSE指令系列。這類指令的使用環境是對多個數據進行同一種處理,所以典型的應用場景就是多媒體領域,特別是在其中的編解碼流程中。html
MMX指令有8個64位寄存器(MM0~MM7),但MMX實際上並無硬件支持的新寄存器,它使用浮點寄存器來模擬MMX指令寄存器。git
當使用MMX指令的時候,一個叫作FP(Floating Point) Tag 的Word(2字節)被用來映射浮點寄存器到MMX寄存器。這樣浮點寄存器就成了MMX寄存器的容器,用來執行計算。從浮點指令切換到MMX指令實在處理器內部完成的,不須要人爲的操做;相反,從MMX切換到浮點指令時,須要手動調用emms或者**__mm_empty()**Intrinsics。github
MMX指令與x86指令相似能夠分爲幾類,具體使用及介紹能夠參考Oracle的手冊,這裏再也不重複介紹:算法
咱們能夠用一般的彙編嵌入方式在C/C++代碼中調用mmx指令,可是這樣一來C/C++開發者可能不是很習慣,尤爲是它們沒有接觸過彙編語言的狀況下;Intel提供了另外一種方式來供開發者選擇----Compiler Intrinsics。oracle
Compiler Intrinsics是內建在編譯器裏的函數,Intrinsics一般會以彙編代碼的形式被內聯到代碼中且具備較高的執行效率,由於編譯器知道intrinsics的表現,相比內嵌彙編代碼編譯器能作更多的優化。函數
同時,Intrinsics的使用方式是停留在宿主語言層的,因此C語言(一般狀況下)相比彙編語言擁有的全部優勢,Intrinsics都有(好比我能夠對Intrinsics數據類型作類型單位的遞增遞減)。oop
咱們這裏分別簡單測試C++、Intrinsics(使用MMX)、Asm(使用MMX)三種形式代碼的執行效率,示例中咱們分別對內存中的100 000 000個字節進行加
算數運算:測試
void calculateUsingCpp(char* data, unsigned size) { assert(size % 8 == 0); unsigned step = 10; for (unsigned i = 0; i < size; ++i) { *data++ += step; } }
Intrinsics代碼中,咱們每次執行mmx Intrinsics時都打包8個字節的數據並執行加操做,執行完mmx指令後咱們須要調用**_mm_empty()** Intrinsics來取消mmx指令對浮點寄存器的別名映射:優化
void calculateUsingIntrinsics(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(); }
Intel彙編語法在嵌入到高級語言代碼中時能夠直接使用上下文中的變量,這一點很是方便:翻譯
void calculateUsingAsm(char* data, unsigned size) { assert(size % 8 == 0); unsigned loopCount = size / 8; __int64 value = 0x0a0a0a0a0a0a0a0a; __asm { push eax push ecx mov eax, data mov ecx, loopCount movq mm1, value startLoop: movq mm0, [eax] paddb mm0, mm1 movq [eax], mm0 add eax, 8 dec ecx jnz startLoop emms pop ecx pop eax } }
能夠看出運行時間比是 8 : 1.5 : 1左右,完整代碼見連接。