使用Intel 向量化編譯器最佳化效能(1) (轉)
使用 向量化(1):namespace prefix = o ns = "urn:schemas--com::office" />
本文節選翻譯自Intel網站
使用Intel c++編譯器的時候,怎麼也找不到相關的資料可以參考,在Intel的網站找到了一些教程,我C++的水平很一般,對這個編譯器的使用也是剛,大家看了請多指正,這是第一部分,餘下的我會在這幾天繼續整理完.
學習如何增強c/c++以及Fortran程式碼在和下的效能,使用Intel的向量化編譯器來為特定的迴圈操作自動產生SIMD程式碼,這些SIMD指令包括MMX/SIMD Extensions /SIMD Extensions 2.
1. 什麼是向量器
向量器是intel c++/Fortran編譯器的一個特性,使用intel向量編譯特性透過自動生成SIMD程式碼加速你的程式碼中特定的迴圈, 這些SIMD指令包括MMX/SIMD Extensions /SIMD Extensions 2.
2. 什麼是向量化
a) 主要改善效能的地方
當包含大量迴圈時透過使用向量化編譯器使用SIMD指令能夠獲取較大的效能上的提高,一段以向量化迴圈為特徵的程式足以快過那些同樣執行方式的一般的程式或類庫.對比一個向量化的迴圈和一個同樣方式的標量迴圈,向量化會提供等同於使用底層實現這迴圈的效能(通常使用在使用Streaming SIMD Extensions時會有25%-40%的效能提高).向量器也可能開啟這些迴圈插入一些預取和 streaming store(不知道怎麼翻譯) 程式碼在你的迴圈中,這也可能獲得一些額外的效能上的提高.
b) 使用Intel的編譯器可以使你從單調乏味的最佳化工作中解脫出來,編譯器可以節省你很多時間,可以代替你做更多的工作.
c) Intel編譯器從4.5版本開始支援向量化,5.0版本增強了向量化的功能包括perfong simple statement reordering automatically(這是什麼,也不知道)和對SIMD2的支援
3. 為什麼要使用向量器
a) 提高效能, 比如向量化一個有浮點操作的並被頻繁的迴圈將會極大提高程式的效能
b) 編寫單一版本的程式碼, 減少使用匯編使編碼工作簡化,較少的彙編意味著會大大減少你為特定的的工作,你的程式將很容易升級並使用於最新的主流系統而不必重新編寫那些彙編程式碼.
4. 什麼樣的迴圈是可以向量化的
a) 對於一個迴圈,如果編譯器認為迴圈內的每一個語句都沒有依賴於另一個語句並且沒有迴圈的依賴關係,那麼這個迴圈就是向量的.換句話說,每一個語句必須能獨立,讀寫資料的操作必須中立於迴圈的每次迭代
看這個迴圈
for (int i=0; i<1000; i++) {
s1: a[i] = b[i] * T + d[i] ;
s2: b[i] = a[i] + b[i])/2;
s3: c = c + b[i];
}
如果等價於下面的操作
for (int i=0; i<1000; i++) a[i] = b[i] * T + d[i] ;
for (int i=0; i<1000; i++) b[i] = (a[i] + b[i])/2;
for (int i=0; i<1000; i++) c = c + b[i];
那麼就認為這個迴圈是可以被向量化的.
再看一個例子
for (int i=1; i<1000; i++)
{
s1: a[i] = a[i-1] * b[i];
}
無論如何,這個迴圈是不能被向量化的,因為a[i]在每次迭代中都讀取了前一次迭代中的a[i-1].我們稱這是一個交叉迭代的資料依賴或者”flow dependence”,這樣的迴圈不能被編譯器向量化.
假定在第一個例子中使用的是浮點資料-向量器能夠透過使用SIMD Extensions同時操作4個浮點數,在這裡,為了能夠向量化,這個迴圈的次數必須大於4.
b) 向量器只能作用在最內層的迴圈
在一個巢狀的迴圈中,向量器只能嘗試向量化最內層的迴圈,檢視向量器的輸出資訊可以知道迴圈是否能被向量化以及原因,如果影響效能的關鍵迴圈沒有向量化,你可能需要做一些更深層的打算,比如像下面描述的內容那樣來幫助向量器做出正確的決定.
c) 向量化不是並行化
看這個例子
for (int i=0; i<1000; i++)
{
s1: a[i] = b[i] * T + d[i] ;
s2: b[i] = (a[i] + b[i])/2;
s3: c = c + b[i];
}
這個向量化程式依次s1,s2,s3執行所有的迴圈迭代.
並行化意味在語句的不同的迭代迴圈中可能被另一個處理過程所干擾,如果語句請求共享資源(比如讀寫同一個資料),那這個輸出就不能保證正確,所以這個迴圈是不能正確向量化的.
5. 如何開啟向量器
引數
Windows*
Linux*
在編譯器指定型別時開啟向量器
/Qx[M,K,W]
-x[M,K,W]
開啟向量器並自動檢測cpu型別,編譯器能生成向量化的程式碼為最新的IA-32,但同時也生成非向量化的程式碼供舊型號的處理器使用,這樣使執行可以執行在多種處理器上
/Qax[M,K,W]
-ax[M,K,W]
-W 開啟對P4的Streaming SIMD Extensions 2的支援, -K開啟對Pentium® III process的支援,-M是對MMX技術提供支援
6. 關於#pragma ivdep和restrict的使用
a) 為了向量化一個包含或可能包含依賴關係的迴圈,加上#pragma ivdep (ignore vector dependencies),如果需要的話.
Void foo(int k)
{
#pragma ivdep
for(int j=0; j<1000; j++)
{
a[j] = b[j+k] * c[j];
b[j] = (a[j] + b[j])/2;
b[j] = b[j] * c[j];
}
}
當向量化這個迴圈時.編譯器會認為陣列b依賴了交叉迭代,原因就是是使用了變數k,如果你知道k不會干涉資料訪問,加上#pragma ivdep向量器忽略向量的依賴性並嘗試進行向量化,根本說,你必須知道這個依賴性是什麼,並確信他們不會成為問題
b) 使用指標的迴圈
使用指標的迴圈可能造成依賴性,如果為了向量化這樣的迴圈,可以使用restrict關鍵字,如果需要的話
Void foo(float *restrict a, float *restrict b, float * restrict c)
{
for(int j=0; j<1000; j++)
{
a[j] = b[j] * c[j];
b[j] = (a[j] + b[j])/2;
b[j] = b[j] * c[j];
}
}
注意,使用了restrict關鍵字舊需要使用/ Qrestrict開關.如果不使用restrict關鍵字,編譯器會認為陣列的引用可能有交叉迭代的依賴性
這是因為指標在迴圈中用來訪問資料,編譯器無法知道是否指向了相同的地址(一般就是別名),為了必須阻止這樣程式向量化, restrict關鍵字告訴編譯器指標指向的地址是受限的,只能透過這個指標訪問,換句話說,這裡沒有別名
7. Verbose 引數
在windows下使用/Qvec_report[0,1,2,3]引數來產生詳細的向量化報告,在Linux下使用-vec_report[0,1,2,3]引數, [0,1,2,3]指定輸出資訊的詳細等級,在最詳細的等級3,編譯器會指出向量器是產生程式碼的具體資訊.
下面是一次開啟了向量器的編譯操作的部分輸出資訊:
C:TEMPText1.cpp(16) : (col. 1) remark:
Below is an example of vectorizer output using the level 3 switch.
char *p;
p = "aeiou";
while (*p++) {
cout << *p;
}
C:TEMPText1.cpp(20) : (col. 1) remark: loop was not vectorized: nonstandard loop is not a vectorization candidate.
float x[100], y[100];
int z[100];
for(int j=0; j<100; j++)
{
x[j] = y[j] + 3;
z[j] = z[j+1];
}
C:TEMPText1.cpp(29) : (col. 5) remark: loop was not vectorized: mixed data types.
float x[100], y[100];
int z[100];
for(int j=0; j<100; j++)
{
x[j] = y[j] + 3;
y[j+1] = z[j+1];
}
C:TEMPText1.cpp(26) : (col. 1) remark: loop was not vectorized: existence of vector dependence.
注意這裡有2處問題,2處迴圈都有向量依賴性,還有一個迴圈有資料型別不一致的問題,向量器會根據檢測到的這些問題的優先等級給出資訊.
待續………………….
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-956551/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 深入瞭解Java JIT編譯器:原理與效能最佳化Java編譯
- 編譯器最佳化丨Cache最佳化編譯
- 探索gcc編譯最佳化細節 編譯器最佳化gcc -o3GC編譯
- OpenBSD核心編譯和最佳化(轉)編譯
- OpenBSD 核心編譯和最佳化(轉)編譯
- 使用智慧最佳化器提高Oracle的效能極限 (轉)Oracle
- FreeBSD中的GNU C編譯器--編譯器GCC(轉)編譯GC
- 效能最佳化:編譯器最佳化選項 -O2/-O3 究竟有多強大?編譯
- GCC編譯器的使用GC編譯
- 安裝 GCC 編譯器(轉)GC編譯
- 編譯器-Javac.exe(轉)編譯Java
- 使用C編譯器編寫shellcode編譯
- 使用lmbench測試linux效能-編譯Linux編譯
- Rust 編譯器探索使用 PGORust編譯Go
- gcc 編譯器與 clang 編譯器GC編譯
- 程式語言和編譯器書單(1)編譯
- 使用 LLVM 框架建立一個工作編譯器,第 1 部分LVM框架編譯
- C++編譯器怎麼實現異常處理1 (轉)C++編譯
- 【譯】使用 Python 編寫虛擬機器直譯器Python虛擬機
- 使用ZendEncode編譯PHP程式(轉)編譯PHP
- 淺談彙編器、編譯器和直譯器編譯
- [譯]iOS編譯器iOS編譯
- 表示式編譯計算器(下) (轉)編譯
- gcc編譯器小知識FAQ(轉)GC編譯
- 編譯器最佳化記錄(Mem2Reg+SSA Destruction)編譯Struct
- 從lex&yacc說到編譯器(2.flex的使用) (轉)編譯Flex
- 前端工具 | JS編譯器Monaco使用教程前端JS編譯
- makefile教程---nmake命令編譯器的使用編譯
- 編譯器的編譯基本過程編譯
- 一張圖解析 編譯器編譯流程圖解編譯
- 親密接觸VC6.0編譯器 (轉)編譯
- 使用智慧最佳化器提高Oracle的效能極限Oracle
- 教你使用智慧最佳化器提高Oracle效能極限Oracle
- CUDAFORTRAN編譯器編譯
- vue編譯器Vue編譯
- Nginx配置效能最佳化(轉)Nginx
- 程式碼線上編譯器(上)- 編輯及編譯編譯
- Oracle效能最佳化之SQL最佳化(轉)OracleSQL