http://blog.csdn.net/gengshenghong/article/details/7007100
我們知道,在C/C++代碼中,可以插入匯編代碼提高性能。現在的指令集有了很多的高級指令,如果我們希望使用這些高級指令來實現一些高效的算法,就可以在代碼中嵌入匯編,使用SSE等高級指令,這是可行的,但是如果對匯編不太熟悉,不愿意使用匯編的人來說,其實也是可以的,這就是Compiler Intrinsics(http://msdn.microsoft.com/zh-cn/site/26td21ds)。
PS:下面的內容以Windows平臺為主,對于Linux下,也有類似的方法。
(1)什么是Intrinsics
Intrinsics是對MMX、SSE等指令集的指令的一種封裝,以函數的形式提供,使得程序員更容易編寫和使用這些高級指令,在編譯的時候,這些函數會被內聯為匯編,不會產生函數調用的開銷。在理解intrinsics指令之前,先理解intrinsics函數。
(3)#pragma intrinsic和#pragma function
#pragma intrinsic(function[,function][,function]...):表示后面的函數將進行intrinsic,替換為內部函數,去掉了函數調用的開銷,注意:有些地方解釋為內聯,但是和內聯并不完全相同,對于內聯,可以指定任意函數為內聯,但是此pragma intrinsic只能適用于編譯器規定的一部分函數,不是所有函數都能使用,而且,inline關鍵字一般用于指定自定義的函數,intrinsic則是系統庫函數的一部分。參考http://technet.microsoft.com/zh-cn/library/tzkfha43.aspx獲取詳細的說明。
下面分析這個例子:
#include?<math.h> ?? void ?foo()?? {?? ????double ?var?=?cos(10);?? }?? 使用VS2010的32bit的command line編譯:
cl /c test.c /FA
輸出得到其匯編文件:
;?Listing?generated?by?Microsoft?(R)?Optimizing?Compiler?Version?16.00.30319.01??? ?? ????TITLE???C:\tempLab\test.c?? ????.686P?? ????.XMM?? ????include?listing.inc?? ????.model??flat?? ?? INCLUDELIB?LIBCMT?? INCLUDELIB?OLDNAMES?? ?? PUBLIC??__real@4024000000000000?? PUBLIC??_foo?? EXTRN???_cos:PROC?? EXTRN???__fltused:DWORD ?? ;???COMDAT?__real@4024000000000000?? ;?File?c:\templab\test.c?? CONST???SEGMENT?? __real@4024000000000000?DQ?04024000000000000r???;?10?? ;?Function?compile?flags:?/Odtp?? CONST???ENDS?? _TEXT???SEGMENT?? _var$?=?-8??????????????????????;?size?=?8?? _foo????PROC?? ;?Line?3?? ????push????ebp?? ????mov?ebp,?esp?? ????sub?esp,?8?? ;?Line?4?? ????sub?esp,?8?? ????fld?QWORD?PTR?__real@4024000000000000?? ????fstp????QWORD?PTR?[esp]?? ????call????_cos?? ????add?esp,?8?? ????fstp????QWORD?PTR?_var$[ebp]?? ;?Line?5?? ????mov?esp,?ebp?? ????pop?ebp?? ????ret?0?? _foo????ENDP?? _TEXT???ENDS?? END?? 可以看到,這里調用了call
_cos函數進行運算,下面代碼修改如下:
#include?<math.h> ?? #pragma?intrinsic(cos) ?? void ?foo()?? {?? ????double ?var?=?cos(10);?? }?? 同樣的命令編譯,得到匯編如下:
;?Listing?generated?by?Microsoft?(R)?Optimizing?Compiler?Version?16.00.30319.01??? ?? ????TITLE???C:\tempLab\test.c?? ????.686P?? ????.XMM?? ????include?listing.inc?? ????.model??flat?? ?? INCLUDELIB?LIBCMT?? INCLUDELIB?OLDNAMES?? ?? PUBLIC??__real@4024000000000000?? PUBLIC??_foo?? EXTRN???__fltused:DWORD ?? EXTRN???__CIcos:PROC?? ;???COMDAT?__real@4024000000000000?? ;?File?c:\templab\test.c?? CONST???SEGMENT?? __real@4024000000000000?DQ?04024000000000000r???;?10?? ;?Function?compile?flags:?/Odtp?? CONST???ENDS?? _TEXT???SEGMENT?? _var$?=?-8??????????????????????;?size?=?8?? _foo????PROC?? ;?Line?4?? ????push????ebp?? ????mov?ebp,?esp?? ????sub?esp,?8?? ;?Line?5?? ????fld?QWORD?PTR?__real@4024000000000000?? ????call????__CIcos?? ????fstp????QWORD?PTR?_var$[ebp]?? ;?Line?6?? ????mov?esp,?ebp?? ????pop?ebp?? ????ret?0?? _foo????ENDP?? _TEXT???ENDS?? END?? 對比之后,它們的主要區別的代碼段如下:
sub?esp,?8?? ????fld?QWORD?PTR?__real@4024000000000000?? ?? ????fstp????QWORD?PTR?[esp]?? ????call????_cos?? ????add?esp,?8?? fld?QWORD?PTR?__real@4024000000000000?? call????__CIcos?? 顯然,使用了Intrinsics之后的cos函數的指令少了很多,其調用的內部函數是_CIcos(http://msdn.microsoft.com/zh-cn/library/ff770589.aspx),此函數會計算對棧頂的元素直接進行cos運算,所以節省了很多函數調用參數傳遞等的指令。
仍然參考MSDN(http://technet.microsoft.com/zh-cn/library/tzkfha43.aspx)可以看到其中一段話:
The floating-point functions listed below do not have true intrinsic forms. Instead they have versions that?pass arguments directly to the floating-point chip rather than pushing them onto the program stack .
當然,這是描述其中一部分Intrinsics函數的,Intrinsics也有不同的方式進行優化/內聯,具體參考MSDN查詢哪些函數可以使用Intrinsics以及是如何工作的(http://msdn.microsoft.com/zh-cn/site/26td21ds)。
#pragma function:使用格式和intrinsics一樣,pragma function用于指定函數不進行intrinsics操作,也就是不生成內部函數。
最后,要知道的一個內容是一個相關的編譯選項:/Oi
http://technet.microsoft.com/zh-cn/library/f99tchzc.aspx
/Oi 僅作為對編譯器的請求,用于將某些函數調用替換為內部函數;為產生更好的性能,編譯器可能會調用函數(而不會將該函數調用替換為內部函數)。
簡單的理解,就是告訴編譯器盡量使用intrinsics版本的調用,當然,最終的實際調用依賴于編譯器的判斷。
也可以參考wiki中(http://en.wikipedia.org/wiki/Intrinsic_function)關于intrinsic functions來幫助理解其作用。簡單來說,可以理解為編譯器的“內置函數”,編譯器會根據情況進行一些優化。
(4)指令集相關的intrinsics介紹
上面介紹的是pragma對intrinsic函數的使用,其中介紹了cos,還有很多類似的“內置函數版本”。有時候將上面的這些稱之為”intrinsics函數“,除此之外,intrinsics更廣泛的使用是指令集的封裝,能直接映射到高級指令集,從而使得程序員可以以函數調用的方式來實現匯編能達到的功能,編譯器會生成為對應的SSE等指令集匯編。
1. 如何使用這類函數
在windows上,包含#include <**mmintrin.h>頭文件即可(不同的指令集擴展的函數可能前綴不一樣),也可以直接包含#include <intrin.h>(這里面會根據使用環境判斷使用ADM的一些兼容擴展)。
2.?關于數據類型
這些和指令集相關的函數,一般都有自己的數據類型,不能使用一般的數據類型傳遞進行計算,一般來說,MMX指令是__m64(http://msdn.microsoft.com/zh-cn/library/08x3t697(v=VS.90).aspx)類型的數據,SSE是__m128類型的數據等等。
3. 函數名:
這類函數名一般以__m開頭。函數名稱和指令名稱有一定的關系。
4. 加法實例:
下面使用SSE指令集進行加法運算,一條指令對四個浮點數進行運算:
#include?<stdio.h> ?? #include?<intrin.h> ?? ?? int ?main( int ?argc,? char *?argv[])?? {?? ????__m128??a;?? ????__m128??b;?? ?????? ????a?=?_mm_set_ps(1,2,3,4);?????????? ????b?=?_mm_set_ps(1,2,3,4);?????????? ?? ????__m128?c?=?_mm_add_ps(a,?b);?????? ?? ????printf("0:?%lf\n" ,?c.m128_f32[0]);?? ????printf("1:?%lf\n" ,?c.m128_f32[1]);?? ????printf("2:?%lf\n" ,?c.m128_f32[2]);?? ????printf("3:?%lf\n" ,?c.m128_f32[3]);?? ?? ????return ?0;?? }?? 從代碼看,好像很復雜,但是生成的匯編的效率會比較高。一條指令就完成了四個浮點數的加法,其運行結果如下:
(5)總結:
1.?Intrinsics函數:能提高性能,會增大生成代碼的大小,是編譯器的”內置函數“。
2. Intrinsics對指令的封裝函數:直接映射到匯編指令,能簡化匯編代碼的編寫,另外,隱藏了寄存器分配和調度等。由于涉及到的數據類型、函數等內容較多,這里只是一個簡單的介紹。
總結
以上是生活随笔 為你收集整理的在C/C++代码中使用SSE等指令集的指令(1)介绍 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。