一、PAPI簡介
PAPI是田納西大學創新計算實驗室開發的一組可以在多個處理器平台上對硬件性能計數器進行訪問的標准接口,它的目標是方便用戶在程序運行時監測和采集由硬件性能計數器記錄的處理器事件信息。用戶可以使用其提供的high/low api對程序某一段的使用時鍾周期數,執行指令數,L1/L2 cache miss/access數,TLB miss數等等都統計出來,使用戶能夠直觀的了解到程序的局部性如何。
不同的處理器會根據自身的體系結構特征定義出不同的處理器事件集合,在 PAPI 中這些事件被稱為原生事件(Native Event)。同時,不同的處理器也會具有不同數量的硬件性能計數器,而在任意時刻一個計數器只能對一個指定的原生事件進行監測。考慮到事件監測和性能分析的需求,不同處理器的原生事件集合往往在功能上會有交集(例如那些和存儲層次訪問、Cache 一致性協議、周期和指令計數、功能單元和流水線狀態等方面相關的事件),但是其對應的原生事件名稱卻未必相同。為了便於事件甄別,PAPI 將這些在不同處理器中存在功能共性的原生事件抽象成了 PAPI 接口專用的預制事件(Preset Event)並統一命名,所以具有一定的可移植性。
1、支持的性能計數器事件:
PAPI 支持一百多個事件。其標准事件分為 4 類:存儲層次訪問事件;周期與指令計數;功能部件與流水線狀態事件; Cache 一致性事件,與 SMP 系統的 cache 一致性協議相關。
PAPI 包含一個工具程序papi_avail,,可以檢測用戶平台具有哪些事件,如:
此外還有papi_mem_info和papi_cost等命令,用來查看TLB,Cache信息和 查看papi開銷等。
2、PAPI提供了兩個向底層硬件的接口:
高層接口:比較簡單,用於完成基本的計數測量(提供啟動、停止和讀取特定事件的能力)
PAPI 高層接口提供了一些訪問硬件性能計數器所需的基本功能,例如配置計數器、啟動計數、停止計數、讀取計數器的數值等。高層接口只能利用 PAPI 預制事件,而不能夠通過配置計數器去監測超出預制事件覆蓋范圍以外的處理器原生事件。不過,PAPI 高層接口能夠直接返回在程序測評中最經常使用的一些性能指標,例如每個周期執行完成的指令數、每秒執行完成的浮點指令 / 浮點操作數、程序的運行時間等;另外,高層接口還能獲取一些系統信息,例如處理器能夠支持的硬件性能計數器的個數等。
底層接口:管理用戶定義的事件組(稱為 EventSet)中的事件,完全可編程,線程安全,為工具開發人員和高級用戶提供方便。
高層接口獲得的數據很多是未經過加工的原始數據,從中不能直接看出應用程序的存儲層次與流水線利用情況的好壞。不同於高層接口只能使用 PAPI 預制事件,PAPI 底層接口能夠直接使用原生事件對程序運行時的處理器硬件行為進行監測。用戶可以將一個或多個原生事件組成一個事件組(Event Set),然后通過設置硬件性能計數器對事件組中所有的原生事件同時進行監測,進而根據監測結果分析程序的性能問題,例如通過同時采集每秒執行完成的浮點 指令數和 L1 Cache 失次數就有助於分析是否是因為 L1 Cache 的命中率不高導致了程序浮點性能的下降。需要注意的是,事件組中的原生事件個數不能夠超過處理器所能支持的硬件性能計數器個數。
list . 常用的 PAPI 底層接口(C語言)
PAPI_library_init
初始化 PAPI 接口庫
PAPI_create_eventset
創建事件組
PAPI_add_event / PAPI_add_events
向事件組中添加原生事件或者 PAPI 預制事件
PAPI_remove_event PAPI_remove_events
從事件組中刪除事件
PAPI_start
啟動計數器對事件組的計數
PAPI_read
讀取計數器數值
PAPI_stop
停止計數器計數並讀取當前的計數器數值
PAPI_cleanup_eventset
清除事件組中的事件
PAPI_destroy_eventset
銷毀事件組
PAPI_shutdown
終止使用 PAPI 並釋放所有相關資源
//具體使用見后面實例2
PAPI是一組可以在多個處理器平台上對硬件性能計數器進行訪問的本地接口,所以對於不同的處理器,安裝方法也不同,部分AMD的CPU和INTEL的奔騰系列,至強系列都需要打補丁。補丁使用perfctr-2.6.x,對內核打補丁,重新編譯的步驟比較繁瑣,安裝步驟在PAPI壓縮包的Install.txt文件里有說明。
二、PAPI安裝方法
在Ubuntu14.10上安裝:
1、到PAPI官網下載PAPI 5.4.1壓縮包。http://icl.cs.utk.edu/papi/software/index.html
2、將壓縮包拷貝至/usr/local/下並解壓。
3、測試PAPI
進入src目錄並依次執行以下指令。
% ./configure % make % make test
如果出現PASSED則是通過測試,可以安裝。
% make fulltest (相當於./run_tests.sh,簡單測試,可忽略)
% ./run_tests.sh -v(詳細測試)
如果提示沒有權限就使用chmod命令更改權限即可。
這個過程比較緩慢,下面是部分過程截圖。
4、安裝PAPI
a) To install PAPI libraries and header files from the build tree:
% make install
b) To install PAPI manual pages from the build tree:
% make install-man
c) To install PAPI test programs from the build tree:
% make install-tests
d) To install all of the above in one step from the build tree:
% make install-all
e) To create a binary kit, papi-<arch>.tgz:
% make dist
papi默認是安裝在 /usr/local 下的,
papi可執行命令安裝在bin下
papi的頭文件放在include下
papi的庫文件放在lib下,包括靜態庫libpapi.a,動態庫libpapi.so.5.4.1.0
還有share、man目錄下也安裝了一些東西
5、由於papi的相關庫安裝在 /usr/local/lib 下,而我們編譯程序鏈接動態或者靜態庫時,默認不會再 /usr/local/lib 下查找相關庫的,所以為了在編譯程序時可以自動鏈接/usr/local/lib下的papi相關庫(例如: gcc ** -lpapi),我們做如下操作:
將 /usr/local/lib 加入文件 /etc/ld.so.conf 的最后一行,然后執行 ldconfig 命令,在編譯時需要加入編譯選項 -lpapi,使得程序可以動態鏈接到相應的函數實現。
6、編寫一個C程序,調用 papi 相關函數,使用命令 gcc *.c -lpapi 命令編譯,查看是否能夠正常使用 PAPI .
三、使用PAPI
使用PAPI需要在源碼進行插樁, 但是非常簡單,一共5個階段
/* Initialize the PAPI library */ if (PAPI_library_init(PAPI_VER_CURRENT) != PAPI_VER_CURRENT) exit(1); /*Initialize the papi thread support:初始化線程支持,不使用線程這個部分可以省略。*/ if (PAPI_thread_init(pthread_self) != PAPI_OK) exit(1);
創建事件集
事件就是指的是你要測試哪些性能數據,可以選擇的性能指標通過papi_avail可以查看。例如L1,L2,L3 cache miss,clock cycle等等。
/* Create an EventSet */ int EventSet = PAPI_NULL; int retval = PAPI_create_eventset (&EventSet); assert(retval==PAPI_OK); /* Add an event*/ retval = PAPI_add_event(EventSet, PAPI_L3_TCM); assert(retval==PAPI_OK);
有些Event是支持一起測試的,如果想一次測試多個數據,再增加一個Event,至於哪些可以一起測試,需要自我探索一下。有一些可以一起測,有些是不可以的。
/*Add another event*/ retval = PAPI_add_event(EventSet, PAPI_TOT_INS); assert(retval==PAPI_OK); 開始計數 /* Start counting events */ if (PAPI_start(EventSet) != PAPI_OK) retval = PAPI_start (EventSet); assert(retval==PAPI_OK);
讀取數據
long long values1[2]; long long values2[2]; PAPI_read(EventSet, values1); assert(retval==PAPI_OK); //Do something /* Stop counting events */ retval = PAPI_stop (EventSet,values2); assert(retval==PAPI_OK);
得到結果
L3_TCM: values2[0] – values1[0] L3 cache miss
TOT_INS: values2[1] – values1[1] total instruction counts
釋放事件,清理工作
/* Clean up EventSet */ if (PAPI_cleanup_eventset(EventSet) != PAPI_OK) exit(-1); /* Destroy the EventSet */ if (PAPI_destroy_eventset(&EventSet) != PAPI_OK) exit(-1); /* Shutdown PAPI */ PAPI_shutdown();
四、實例測試
實例一
http://icl.cs.utk.edu/projects/papi/wiki/PAPITopics:Getting_Started 官網的例子
我的電腦輸出的結果為:
實例二
1 #include "papi.h" 2 3 #include <stdlib.h> 4 5 #include <stdio.h> 6 7 int main() { 8 9 int EventSet; 10 11 int i, sum; 12 13 long_long values[2], values1[2], values2[2]; 14 15 if (PAPI_library_init(PAPI_VER_CURRENT) != PAPI_VER_CURRENT) 16 17 exit(-1); 18 19 EventSet = PAPI_NULL; 20 21 if (PAPI_create_eventset(&EventSet) != PAPI_OK) 22 23 exit(-1); 24 25 if (PAPI_add_event(EventSet, PAPI_TOT_INS) != PAPI_OK) 26 27 exit(-1); 28 29 if (PAPI_add_event(EventSet, PAPI_L1_DCM) != PAPI_OK) 30 31 exit(-1); 32 33 if (PAPI_start(EventSet) != PAPI_OK) 34 35 exit(-1); 36 37 if (PAPI_read(EventSet, values1) != PAPI_OK) 38 39 exit(-1); 40 41 for (i=0;i<10000;i++) 42 43 sum+=i; 44 45 if (PAPI_stop(EventSet, values2) != PAPI_OK) 46 47 exit(-1); 48 49 if (PAPI_cleanup_eventset(EventSet) != PAPI_OK) 50 51 exit(-1); 52 53 if (PAPI_destroy_eventset(&EventSet) != PAPI_OK) 54 55 exit(-1); 56 57 PAPI_shutdown(); 58 59 /* Get value */ 60 61 values[0]=values2[0]-values1[0]; 62 63 values[1]=values2[1]-values1[1]; 64 65 printf("TOT_INS:%lld\nL1_DCM: %lld\n",values[0], values[1]); 66 67 return 0; 68 69 }
我的電腦輸出的結果為:
TOT_INS:50919
L1_DCM:14
實例三
1 #include "papi.h" 2 3 #include <stdlib.h> 4 5 #include <stdio.h> 6 7 int main() { 8 9 int EventSet; 10 11 int i, sum; 12 13 long_long values[1], values1[1], values2[1]; 14 15 /* Initialize the PAPI library */ 16 17 if (PAPI_library_init(PAPI_VER_CURRENT) != PAPI_VER_CURRENT) 18 19 exit(-1); 20 21 // exit(-1); 22 23 /* Create an EventSet */ 24 25 EventSet = PAPI_NULL; 26 27 if (PAPI_create_eventset(&EventSet) != PAPI_OK) 28 29 exit(-1); 30 31 /* Add an event about Total Instructions Executed (PAPI_TOT_INS) to EventSet */ 32 33 if (PAPI_add_event(EventSet, PAPI_TOT_INS) != PAPI_OK) 34 35 exit(-1); 36 37 /* Start counting events */ 38 39 if (PAPI_start(EventSet) != PAPI_OK) 40 41 exit(-1); 42 43 /* Read counters before workload running*/ 44 45 if (PAPI_read(EventSet, values1) != PAPI_OK) 46 47 exit(-1); 48 49 /* Do some computation here */ 50 51 for (i=0;i<10000;i++) 52 53 sum+=i; 54 55 /* Stop counting events */ 56 57 /* Stop counting events */ 58 59 if (PAPI_stop(EventSet, values2) != PAPI_OK) 60 61 exit(-1); 62 63 /* Clean up EventSet */ 64 65 if (PAPI_cleanup_eventset(EventSet) != PAPI_OK) 66 67 exit(-1); 68 69 /* Destroy the EventSet */ 70 71 if (PAPI_destroy_eventset(&EventSet) != PAPI_OK) 72 73 exit(-1); 74 75 /* Shutdown PAPI */ 76 77 PAPI_shutdown(); 78 79 /* Get value */ 80 81 // values[0] = values2[0] – values1[0]; 82 83 values[0]=values2[0]-values1[0]; 84 85 printf("%lld\n",values[0]); 86 87 return 0; 88 89 }
我的電腦輸出的結果為50891
實例四
#include "papi.h" #include<stdlib.h> #include<stdio.h> int a[1000][1000]; int c[1000][1000]; int main() { int EventSet = PAPI_NULL; int retval; long_long values[1]; /* Initialize the PAPI library */ retval = PAPI_library_init(PAPI_VER_CURRENT); if (retval != PAPI_VER_CURRENT) { fprintf(stderr, "PAPI library init error!\n"); exit(1); } /* Create an EventSet */ if (PAPI_create_eventset(&EventSet) != PAPI_OK) { printf("create error\n"); exit(1); } /* Add Total Instructions Executed to our EventSet */ if (PAPI_add_event(EventSet, PAPI_L1_DCM) != PAPI_OK) { printf("add error\n"); exit(1); } if (PAPI_start(EventSet) != PAPI_OK) { printf("start error\n"); exit(1); } if (PAPI_read(EventSet, values) != PAPI_OK) { printf("read error\n"); exit(1); } int m,n; for(m=0;m<1000;m++) { for(n=0;n<1000;n++) { a[n][m]=a[n][m]+m+n; } } /* Do some computation here */ if (PAPI_read(EventSet, values) != PAPI_OK) { printf("read error\n"); exit(1); } long_long b=values[0]; if(PAPI_reset(EventSet)!=PAPI_OK) { printf("reset error\n"); exit(1); } for(m=0;m<1000;m++) { for(n=0;n<1000;n++) { c[m][n]=c[m][n]+m+n; } } /* Do some computation here */ if (PAPI_stop(EventSet, values) != PAPI_OK) { printf("stop error\n"); exit(1); } if (PAPI_remove_event(EventSet, PAPI_L1_DCM) != PAPI_OK) { printf("stop error\n"); exit(1); } if (PAPI_destroy_eventset(&EventSet) != PAPI_OK) { printf("stop error\n"); exit(1); } PAPI_shutdown(); printf("%lld\n",b); printf("%lld",values[0]); getchar(); return 0; }
我的電腦輸出的結果為:
1364841
34786
最后推薦 http://www.ibm.com/developerworks/cn/java/j-lo-papi/ 這篇文章寫得很好。