Схема процесора M68000. Взято тут. |
Звичайно, до цієї інформації можна доступатися безпосередньо -- якщо ОС надає відповідні права працювати із моделе-специфічними регістрами процесора (MSR) або є можливість завантажити відповідний драйвер. Однак, це складно -- набір регістрів прив'язаний не тільки до архітектури процесора (x86 чи ARM), але й виробника (Intel vs AMD) і конкретної моделі процесора. Код виходить дуже непортабельним, або дуже громіздким. Зате все під контролем. Детальніше про такий підхід -- див., наприклад, сайт славнозвісного Агнера Фога: "Test programs for measuring clock cycles and performance monitoring".
Однак, розвинені ОС надають і звичайним програмам доступ до цих інструментів. Про відповідні API MS Windows трішки говорилося в попередньому пості. Для повноти коротко (дуже коротко!) розглянемо спеціалізоване API для POSIX систем -- PAPI та кросплатформову бібліотеку від Intel Processor Counter Monitor (PCM).
Як бачимо, повної кросплатформовості немає -- або тільки POSIX (з обмовками), або тільки процесори Intel, при тому -- достатньо сучасні (конкретніше -- див. далі).
Linux -- PAPI
PAPI (Performance API) -- бібліотека читання
performance counter-ів процесорів, яка з часом навчилася також працювати із іншими підсистемами -- мережею, вводом-виводом, тощо. Портабельна, існує під багато
POSIX-систем, старіші версії (3.7) вміли працювати під Windows, хоч і обмежено.
Щоб
заінсталювати під Linux, шукайте у вашому менеджері пакетів пакети типу
libpapi-dev (Debian), чи papi-delev (RedHat). У деяких дистрибутивах може бути окремий
пакет типу papi-tools (Debian), із command-line інструментами. Див. також офіційний сайт.
Існує у варіантах для С та для Fortran. Із С++, відповідно, проблем немає, а для інших мов будуть потрібні прив'язки -- bindings.
Бібліотека підраховує кількість якихось подій системи. Ідентифікуються події іменами та внутрішніми кодами (є функції трансляції одного в інше і назад). Події поділяються на "нативні" (native events) -- присутні у конкретному процесорі і задані наперед (preset events) -- події, інформація про яких може бути корисна всім, і які є узагальненням більш низькорівневих нативних подій. Наприклад, "Total L1 Cache Misses" (PAPI_L1_TCM) -- сумарна кількість промахів кешу першого рівня на одних процесорах може відображатися на нативну подію (вміст якогось MSR, грубо кажучи), а на інших розраховуватися з кількості промахів кешу інструкцій і кешу даних.
Є відповідні функції API для перевірки, чи конкретні події підтримуються:
1 2 3 | int PAPI_query_event( int EventCode ) int PAPI_get_event_info( int EventCode, PAPI_event_info_t *info ) int PAPI_enum_event( int *EventCode, int modifier ) |
Також є утилітка papi_avail, яка виводить інформацію про події, підтримувані даною реалізацією.
Приклад її роботи на Raspberry Pi 3:
Якись-там x86-64 (від моделі процесора та, можливо, версії ядра, результати змінюються):
(Результати відфільтровано, по замовчуванню показує повний список подій, біля багатьох із яких пише No -- не підтримується, але у текстах вище додано оригінальний заголовок).
Інші утиліти:
Обробка помилок описана тут: "PAPI Error Handling". Ідея наступна -- функція повертає код завершення, якщо він не рівний PAPI_OK, сталася та чи інша помилка. Список є за посиланням вище, а її текстовий опис можна отримати викликом PAPI_perror() чи PAPI_strerror().
Підтримуються потоки, однак, кожен потік повинен ініціалізувати підтримку підрахунку подій самостійно. Для ввімкнення підтримки потоків, слід викликати PAPI_thread_init(pthread_self), а кожен потік реєструвати викликом PAPI_register_thread(). Підтримується також MPI! Детальніше див. "Using PAPI with Parallel Programs".
Можна оголошувати групи подій (event sets) для подій, які слід вимірювати одночасно. Детальніше див. "EVENT SETS".
Кількість лічильників, які можна використовувати, обмежена апаратно. (Є можливість мультиплексування, як спосіб обійти це обмеження). Взнати їх кількість можна за допомогою функції PAPI_num_counters(). Однак, тут є важливий нюанс -- для деяких подій треба зразу декілька апаратних лічильників, тому ще що їх у вас 6 не означає, що вдасться спостерігати за 6-ма довільними подіями зразу.
Приклад, як цим всім користуватися, можна побачити в репозиторії до цієї серії постів. Код виглядає якось так:
Через обмеження часу і мізерну кількість апаратних лічильників, код у репозиторії дещо дивний -- він випадковим чином вибирає декілька подій, і намагається їх підраховувати. Якщо лічильників для цієї комбінації бракує -- виводить повідомлення про помилку і завершується. Адекватним рішення було б або хитро взяти 1-2 події, щоб лічильників завжди вистачало, або використати мультплексування, але перше -- не весело, а на друге згаданого часу не вистачило.
git clone https://github.com/opcm/pcm
cd pcm
make
Можна пробувати запускати. На моїх системах часто з'являється повідомлення:
Для ілюстрації -- приклад виводу pcm-numa на системі із 24 логічними ядрами:
Якщо ваші програми скаржаться багато-багато раз на:
Error while reading perf data. Result is 0
Check if you run other competing Linux perf clients.
варто скомпілювати,прибравши із Makefile наступні рядки, вказавши не використовувати perf:
ifneq ($(wildcard /usr/include/linux/perf_event.h),)
CXXFLAGS += -DPCM_USE_PERF
endif
Правда, при цьому доведеться іноді безпосередньо перевантажувати PMU (Performance Monitoring Unit), див. код далі. Чому йому іноді perf API не подобається -- не знаю, за безпосередньої роботи із ним проблем немає.
За допомогою проектів Visual Studio, вдалося зібрати бібліотеку і під Windows. Правда, не без напильника -- воно скаржилося на опції, несумісні із /clr (хоча останнє якраз не ввімкнене, якщо вірити GUI!), то довелося забрати /Gm i ввімкнути /RTC1.
Використання у власному коді може виглядати якось так:
Згідно офіційної документації, компілюється програма, що використовує PCM, трохи нетривіально:
g++ -std=c++11 -I<pcm-path> <pcm-path>/cpucounters.o <pcm-path>/pci.o <pcm-path>/msr.o <pcm-path>/client_bw.o -lpthread pcm_test.cpp -o pcm_test
де список об'єктних файлів -- файли, що входять в PCM. Але окрема ціль побудови бібліотеки існує, якщо зробити:
make libPCM.a
Файл libPCM.a з'явиться, і з ним можна буде успішно лінкуватися:
g++ -std=c++11 -I<pcm-path> -L<pcm-path> -lPCM -lpthread pcm_test.cpp -o pcm_test
Вивід буде якимось таким:
Зауважте "преамбулу", яку виводить сама бібліотека.
На загал, хоча інструмент потужний, враження від нього неоднозначні... Треба буде поспостерігати за розвитком.
Додатково, див. "Intel Performance Counter Monitor".
А на разі,
Приклад її роботи на Raspberry Pi 3:
root@pi3:~# papi_avail | grep Yes Name Code Avail Deriv Description (Note) # Copied later PAPI_L1_DCM 0x80000000 Yes Yes Level 1 data cache misses PAPI_L1_ICM 0x80000001 Yes No Level 1 instruction cache misses PAPI_L2_DCM 0x80000002 Yes No Level 2 data cache misses PAPI_TLB_DM 0x80000014 Yes No Data translation lookaside buffer misses PAPI_TLB_IM 0x80000015 Yes No Instruction translation lookaside buffer misses PAPI_HW_INT 0x80000029 Yes No Hardware interrupts PAPI_BR_MSP 0x8000002e Yes No Conditional branch instructions mispredicted PAPI_TOT_INS 0x80000032 Yes No Instructions completed PAPI_LD_INS 0x80000035 Yes No Load instructions PAPI_SR_INS 0x80000036 Yes No Store instructions PAPI_BR_INS 0x80000037 Yes No Branch instructions PAPI_TOT_CYC 0x8000003b Yes No Total cycles PAPI_L1_DCA 0x80000040 Yes No Level 1 data cache accesses PAPI_L2_DCA 0x80000041 Yes No Level 2 data cache accesses
Якись-там x86-64 (від моделі процесора та, можливо, версії ядра, результати змінюються):
[root@west papi_test]# papi_avail | grep Yes Name Code Avail Deriv Description (Note) # Copied later PAPI_L1_DCM 0x80000000 Yes No Level 1 data cache misses PAPI_L1_ICM 0x80000001 Yes No Level 1 instruction cache misses PAPI_L2_DCM 0x80000002 Yes Yes Level 2 data cache misses PAPI_L2_ICM 0x80000003 Yes No Level 2 instruction cache misses PAPI_L1_TCM 0x80000006 Yes No Level 1 cache misses PAPI_L2_TCM 0x80000007 Yes No Level 2 cache misses PAPI_CA_SHR 0x8000000a Yes No Requests for exclusive access to shared cache line PAPI_CA_CLN 0x8000000b Yes No Requests for exclusive access to clean cache line PAPI_CA_ITV 0x8000000d Yes No Requests for cache line intervention PAPI_TLB_DM 0x80000014 Yes No Data translation lookaside buffer misses PAPI_TLB_IM 0x80000015 Yes No Instruction translation lookaside buffer misses PAPI_L1_LDM 0x80000017 Yes No Level 1 load misses PAPI_L1_STM 0x80000018 Yes No Level 1 store misses PAPI_L2_LDM 0x80000019 Yes Yes Level 2 load misses PAPI_L2_STM 0x8000001a Yes No Level 2 store misses PAPI_HW_INT 0x80000029 Yes No Hardware interrupts PAPI_BR_CN 0x8000002b Yes No Conditional branch instructions PAPI_BR_TKN 0x8000002c Yes No Conditional branch instructions taken PAPI_BR_NTK 0x8000002d Yes No Conditional branch instructions not taken PAPI_BR_MSP 0x8000002e Yes No Conditional branch instructions mispredicted PAPI_BR_PRC 0x8000002f Yes Yes Conditional branch instructions correctly predicted PAPI_TOT_IIS 0x80000031 Yes No Instructions issued PAPI_TOT_INS 0x80000032 Yes No Instructions completed PAPI_FP_INS 0x80000034 Yes No Floating point instructions PAPI_LD_INS 0x80000035 Yes No Load instructions PAPI_SR_INS 0x80000036 Yes No Store instructions PAPI_BR_INS 0x80000037 Yes No Branch instructions PAPI_VEC_INS 0x80000038 Yes No Vector/SIMD instructions (could include integer) PAPI_RES_STL 0x80000039 Yes No Cycles stalled on any resource PAPI_TOT_CYC 0x8000003b Yes No Total cycles PAPI_L1_DCH 0x8000003e Yes Yes Level 1 data cache hits PAPI_L1_DCA 0x80000040 Yes No Level 1 data cache accesses PAPI_L2_DCA 0x80000041 Yes Yes Level 2 data cache accesses PAPI_L2_DCR 0x80000044 Yes No Level 2 data cache reads PAPI_L2_DCW 0x80000047 Yes No Level 2 data cache writes PAPI_L1_ICH 0x80000049 Yes Yes Level 1 instruction cache hits PAPI_L2_ICH 0x8000004a Yes Yes Level 2 instruction cache hits PAPI_L1_ICA 0x8000004c Yes No Level 1 instruction cache accesses PAPI_L2_ICA 0x8000004d Yes No Level 2 instruction cache accesses PAPI_L2_TCH 0x80000056 Yes Yes Level 2 total cache hits PAPI_L1_TCA 0x80000058 Yes Yes Level 1 total cache accesses PAPI_L2_TCA 0x80000059 Yes No Level 2 total cache accesses PAPI_L2_TCR 0x8000005c Yes Yes Level 2 total cache reads PAPI_L2_TCW 0x8000005f Yes No Level 2 total cache writes PAPI_FML_INS 0x80000061 Yes No Floating point multiply instructions PAPI_FDV_INS 0x80000063 Yes No Floating point divide instructions PAPI_FP_OPS 0x80000066 Yes No Floating point operations PAPI_SP_OPS 0x80000067 Yes Yes Floating point operations; optimized to count scaled single precision vector operations PAPI_DP_OPS 0x80000068 Yes Yes Floating point operations; optimized to count scaled double precision vector operations PAPI_VEC_SP 0x80000069 Yes No Single precision vector/SIMD instructions PAPI_VEC_DP 0x8000006a Yes No Double precision vector/SIMD instructions PAPI_REF_CYC 0x8000006b Yes No Reference clock cycles
(Результати відфільтровано, по замовчуванню показує повний список подій, біля багатьох із яких пише No -- не підтримується, але у текстах вище додано оригінальний заголовок).
Інші утиліти:
- papi_mem_info виводить інформацію про архітектуру пам'яті -- розмір кешів, TLB, тощо.
- papi_clockres -- роздільна здатність доступних таймерів.
- papi_command_line -- отримання інформації про події із командного рядка.
- Та багато інших. Детальніше див. документацію.
Обробка помилок описана тут: "PAPI Error Handling". Ідея наступна -- функція повертає код завершення, якщо він не рівний PAPI_OK, сталася та чи інша помилка. Список є за посиланням вище, а її текстовий опис можна отримати викликом PAPI_perror() чи PAPI_strerror().
Підтримуються потоки, однак, кожен потік повинен ініціалізувати підтримку підрахунку подій самостійно. Для ввімкнення підтримки потоків, слід викликати PAPI_thread_init(pthread_self), а кожен потік реєструвати викликом PAPI_register_thread(). Підтримується також MPI! Детальніше див. "Using PAPI with Parallel Programs".
Можна оголошувати групи подій (event sets) для подій, які слід вимірювати одночасно. Детальніше див. "EVENT SETS".
Кількість лічильників, які можна використовувати, обмежена апаратно. (Є можливість мультиплексування, як спосіб обійти це обмеження). Взнати їх кількість можна за допомогою функції PAPI_num_counters(). Однак, тут є важливий нюанс -- для деяких подій треба зразу декілька апаратних лічильників, тому ще що їх у вас 6 не означає, що вдасться спостерігати за 6-ма довільними подіями зразу.
Приклад, як цим всім користуватися, можна побачити в репозиторії до цієї серії постів. Код виглядає якось так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | int ret = PAPI_library_init(PAPI_VER_CURRENT); if (ret != PAPI_VER_CURRENT) { cout << "PAPI library init error!" << endl; exit(1); } int Events[] = {PAPI_L1_DCM, PAPI_L1_ICM}; const size_t NUM_EVENTS = sizeof(Events)/sizeof(Events[0]); long_long values[NUM_EVENTS]; if ( (ret = PAPI_start_counters(Events, NUM_EVENTS)) != PAPI_OK) { cout << "Error starting counters, code: " << ret << ", " << PAPI_strerror(ret) << endl; return 1; } ...................code............................................ if ( (ret = PAPI_read_counters(values, NUM_EVENTS)) != PAPI_OK) { cout << "Err code: " << ret << ", " << PAPI_strerror(ret) << endl; return 1; } char EventCodeStr[PAPI_MAX_STR_LEN]; for(size_t i = 0; i<NUM_EVENTS; ++i) { cout << "Event code " << hex << SupportedEvents[i] << dec; if (PAPI_event_code_to_name(Events[i], EventCodeStr) == PAPI_OK) { cout << ", " << EventCodeStr; } cout << ": " << values[i]; PAPI_event_info_t info; if (PAPI_get_event_info(Events[i], &info) == PAPI_OK) { cout << "\t" << info.long_descr; }else{ cout << "\t" << "No description"; } cout << endl; } |
Через обмеження часу і мізерну кількість апаратних лічильників, код у репозиторії дещо дивний -- він випадковим чином вибирає декілька подій, і намагається їх підраховувати. Якщо лічильників для цієї комбінації бракує -- виводить повідомлення про помилку і завершується. Адекватним рішення було б або хитро взяти 1-2 події, щоб лічильників завжди вистачало, або використати мультплексування, але перше -- не весело, а на друге згаданого часу не вистачило.
Processor Counter Monitor (PCM)
PCM, як вже згадувалося -- кросплатформова бібліотека роботи із подіями процесорів та деяких інших системних метрик. Правда, лише від Intel. На жаль, потребує зовсім нових процесорів:Error: unsupported processor. Only Intel(R) processors are supported (Atom(R) and microarchitecture codename Nehalem/Nehalem-EP, Atom(tm), Westmere/Clarkdale, Sandy Bridge, Westmere-EP, Sandy Bridge-EP/Jaketown, Nehalem-EX, Westmere-EX, Ivy Bridge, Haswell, Broadwell, Ivy Bridge-EP/EN/EX/Ivytown, Haswell-EP/EN/EX, Broadwell-EP/EX, Skylake-SP, Broadwell-DE, Knights Landing, Skylake, Kabylake). CPU model number: 23 Brand: "Intel(R) Xeon(R) CPU E5405 @ 2.00GHz"Як і PAPI, включає багато готових утиліт (точніше, навпаки, бібліотека була написана для їх підтримки, але придатна для незалежного використання):
- pcm -- загальний моніторинг процесора (кількість виконаних команд, тактова частота, пропускна здатність QPI (Quick Path Interconnect), і т.д. і т.п.
- pcm-memory -- пропускна здатність пам'яті.
- pcm-pcie і pcm-iio -- моніторинг PCIe.
- pcm-numa -- відстеження локальних та нелокальних звертань на NUMA-системах.
- pcm-core і pmu-query -- відстежування довільних подій процесорів.
- Та пара інших.
git clone https://github.com/opcm/pcm
cd pcm
make
Можна пробувати запускати. На моїх системах часто з'являється повідомлення:
[root@computer pcm]# ./pcm-core.x Error: NMI watchdog is enabled. This consumes one hw-PMU counter to disable NMI watchdog please run under root: echo 0 > /proc/sys/kernel/nmi_watchdog or to disable it permanently: echo 'kernel.nmi_watchdog=0' >> /etc/sysctl.conf Processor Counter Monitor: Core Monitoring Utility Access to Processor Counter Monitor has denied (no MSR or PCI CFG space access).Про NMI Watchog можна почитати окремо, але вказана в повідомленні інструкція діє, даючи можливість запускати згадані утиліти.
Для ілюстрації -- приклад виводу pcm-numa на системі із 24 логічними ядрами:
Core | IPC | Instructions | Cycles | Local DRAM accesses | Remote DRAM Accesses 0 0.48 835 K 1728 K 133 130 1 0.20 88 K 432 K 17 20 2 0.15 43 K 287 K 20 17 3 0.22 154 K 703 K 73 44 4 0.27 114 K 422 K 41 87 5 0.23 101 K 441 K 31 85 6 0.27 108 K 399 K 19 20 7 0.24 178 K 749 K 58 103 8 0.33 149 K 454 K 46 34 9 0.30 182 K 617 K 37 56 10 0.27 91 K 344 K 29 45 11 0.22 83 K 377 K 56 90 12 0.27 174 K 657 K 10 21 13 0.24 219 K 920 K 98 241 14 0.33 134 K 411 K 40 58 15 0.31 231 K 737 K 56 27 16 0.26 67 K 265 K 21 14 17 0.23 88 K 378 K 21 94 18 0.31 195 K 632 K 90 93 19 0.30 237 K 804 K 27 57 20 0.24 60 K 254 K 28 47 21 0.28 119 K 421 K 24 38 22 0.33 105 K 324 K 40 41 23 0.30 153 K 505 K 41 68 ------------------------------------------------------------------------------------------------------------------- * 0.30 3920 K 13 M 1056 1530
Якщо ваші програми скаржаться багато-багато раз на:
Error while reading perf data. Result is 0
Check if you run other competing Linux perf clients.
варто скомпілювати,прибравши із Makefile наступні рядки, вказавши не використовувати perf:
ifneq ($(wildcard /usr/include/linux/perf_event.h),)
CXXFLAGS += -DPCM_USE_PERF
endif
Правда, при цьому доведеться іноді безпосередньо перевантажувати PMU (Performance Monitoring Unit), див. код далі. Чому йому іноді perf API не подобається -- не знаю, за безпосередньої роботи із ним проблем немає.
За допомогою проектів Visual Studio, вдалося зібрати бібліотеку і під Windows. Правда, не без напильника -- воно скаржилося на опції, несумісні із /clr (хоча останнє якраз не ввімкнене, якщо вірити GUI!), то довелося забрати /Gm i ввімкнути /RTC1.
Використання у власному коді може виглядати якось так:
Якщо не викликати cleanup(), при наступному запуску скаржитиметься: "Looks like Performance Monitoring Unit is occupied by other application. Reset it? (y/n)" -- якщо відповісти `y`, при наступному запуску програма має шанс успішно відпрацювати. (Звичайно, так само скаржитиметься, якщо лічильниками користується хтось інший!)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 #include <iostream> #include "cpucounters.h" using namespace std; int main() { PCM * m = PCM::getInstance(); PCM::ErrorCode returnResult = m->program(); switch (returnResult) { case PCM::Success: break; case PCM::MSRAccessDenied: cerr << "MSRAccessDenied." << endl; exit(EXIT_FAILURE); case PCM::PMUBusy: cerr << "Looks like Performance Monitoring Unit is occupied by other application" << endl; cerr << "Reset it? (y/n)" << endl; char yn; std::cin >> yn; if ('y' == yn) { m->resetPMU(); cerr << "PMU configuration has been reset. Try to rerun the program again." << endl; } exit(EXIT_FAILURE); default: cerr << "Unknown error." << endl; exit(EXIT_FAILURE); } SystemCounterState before_sstate = getSystemCounterState(); ........................Code.for.testing............................... SystemCounterState after_sstate = getSystemCounterState(); std::cout << fixed << left; int w = 35; std::cout << setw(w) << "Core cycles:" << getCycles(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "Average retired instructions:" << getExecUsage(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "Retired instructions:" << getInstructionsRetired(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "Invariant TSC:" << getInvariantTSC(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "Referen clock cycles:" << getRefCycles(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "Instructions per clock cycle:" << getIPC(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "Average core frequency:" << getAverageFrequency(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "Average relative core frequency:" << getRelativeFrequency(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "Core cycles lost due to L3 cache misses:" << getCyclesLostDueL3CacheMisses(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "Core cycles lost due to L2 cache misses:" << getCyclesLostDueL2CacheMisses(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "Core cycles lost due to L2 cache misses:" << getCyclesLostDueL2CacheMisses(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "L2 cache hit ratio:" << getL2CacheHitRatio(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "L2 cache hits:" << getL2CacheHits(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "L2 cache misses:" << getL2CacheMisses(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "L3 cache hit ratio:" << getL3CacheHitRatio(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "L3 cache hits:" << getL3CacheHits(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "L3 cache misses:" << getL3CacheMisses(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "L3 cache occupancy before:" << getL3CacheOccupancy(before_sstate) << std::endl; std::cout << setw(w) << "L3 cache occupancy after:" << getL3CacheOccupancy(after_sstate) << std::endl; std::cout << setw(w) << "SMI interrupts:" << getSMICount(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "Local Memory Bandwidth:" << getLocalMemoryBW(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "Remote Memory Bandwidth:" << getRemoteMemoryBW(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "Bytes read from RAM:" << getBytesReadFromMC(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "Bytes written to RAM:" << getBytesWrittenToMC(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "I/O Bytes:" << getIORequestBytesFromMC(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "Consumed by CPU energy (Joules):" << getConsumedJoules(before_sstate,after_sstate) << std::endl; std::cout << setw(w) << "Consumed by DRAM energy (Joules):" << getDRAMConsumedJoules(before_sstate,after_sstate) << std::endl; m->cleanup(); }
Згідно офіційної документації, компілюється програма, що використовує PCM, трохи нетривіально:
g++ -std=c++11 -I<pcm-path> <pcm-path>/cpucounters.o <pcm-path>/pci.o <pcm-path>/msr.o <pcm-path>/client_bw.o -lpthread pcm_test.cpp -o pcm_test
де список об'єктних файлів -- файли, що входять в PCM. Але окрема ціль побудови бібліотеки існує, якщо зробити:
make libPCM.a
Файл libPCM.a з'явиться, і з ним можна буде успішно лінкуватися:
g++ -std=c++11 -I<pcm-path> -L<pcm-path> -lPCM -lpthread pcm_test.cpp -o pcm_test
Вивід буде якимось таким:
[root@compute-0-5 pcm]# g++ -std=c++11 -I. -L. cpucounters.o pci.o msr.o client_bw.o -lpthread sample.cpp -o pcm_test && ./pcm_test Number of physical cores: 12 Number of logical cores: 24 Number of online logical cores: 24 Threads (logical cores) per physical core: 2 Num sockets: 2 Physical cores per socket: 6 Core PMU (perfmon) version: 3 Number of core PMU generic (programmable) counters: 4 Width of generic (programmable) counters: 48 bits Number of core PMU fixed counters: 3 Width of fixed counters: 48 bits Nominal core frequency: 2100000000 Hz Package thermal spec power: 80 Watt; Package minimum power: 51 Watt; Package maximum power: 110 Watt; ERROR: QPI LL monitoring device (0:7f:8:2) is missing. The QPI statistics will be incomplete or missing. ERROR: QPI LL monitoring device (0:7f:9:2) is missing. The QPI statistics will be incomplete or missing. Socket 0: 1 memory controllers detected with total number of 4 channels. 0 QPI ports detected. ERROR: QPI LL monitoring device (0:ff:8:2) is missing. The QPI statistics will be incomplete or missing. ERROR: QPI LL monitoring device (0:ff:9:2) is missing. The QPI statistics will be incomplete or missing. Socket 1: 1 memory controllers detected with total number of 4 channels. 0 QPI ports detected. Core cycles: 4048298393 Average retired instructions: 0.053560 Retired instructions: 4318553674 Invariant TSC: 80630013261 Referen clock cycles: 3396284619 Instructions per clock cycle: 1.066758 Average core frequency: 105437495.065030 Average relative core frequency: 0.050208 Core cycles lost due to L3 cache misses:0.004025 Core cycles lost due to L2 cache misses:0.000733 Core cycles lost due to L2 cache misses:0.000733 L2 cache hit ratio: 0.171535 L2 cache hits: 35214 L2 cache misses: 170074 L3 cache hit ratio: 0.467785 L3 cache hits: 79558 L3 cache misses: 90516 L3 cache occupancy before: 0 L3 cache occupancy after: 0 SMI interrupts: 0 Local Memory Bandwidth: 0 Remote Memory Bandwidth: 0 Bytes read from RAM: 117596096 Bytes written to RAM: 111231296 I/O Bytes: 0 Consumed by CPU energy (Joules): 71.988235 Consumed by DRAM energy (Joules): 23.961838
Зауважте "преамбулу", яку виводить сама бібліотека.
На загал, хоча інструмент потужний, враження від нього неоднозначні... Треба буде поспостерігати за розвитком.
Додатково, див. "Intel Performance Counter Monitor".
А на разі,
Дякую за увагу!
Немає коментарів:
Дописати коментар