Как использовать Google Perf Tools
Я только начал использовать инструменты производительности Google (google-perftools
а также libgoogle-perftools4
пакеты в Ubuntu), я клянусь, что я гуглю в течение дня, и я не нашел ответа! Проблема в том, что я не получаю результат для ВСЕХ моих функций с профилированием процессора. Это мой код:
#include "gperftools/profiler.h"
#include <iostream>
#include <math.h>
using namespace std;
void bar()
{
int a,b,c,d,j,k;
a=0;
int z=0;
b = 1000;
while(z < b)
{
while (a < b)
{
d = sin(a);
c = cos(a);
j = tan(a);
k = tan(a);
k = d * c + j *k;
a++;
}
a = 0;
z++;
}
}
void foo()
{
cout << "hey " << endl;
}
int main()
{
ProfilerStart("/home/mohammad/gperf/dump.txt");
int a = 1000;
while(a--){foo();}
bar();
ProfilerFlush();
ProfilerStop();
}
Составлено как g++ test.cc -lprofiler -o a.out
вот как я запускаю код:
CPUPROFILE=dump.txt ./a.out
Я также попробовал это:
CPUPROFILE_FREQUENCY=10000 LD_PRELOAD=/usr/local/lib/libprofiler.so.0.3.0 CPUPROFILE=dump.txt ./a.out
И это то, что я получаю от google-pprof --text a.out dump.txt
:
Using local file ./a.out.
Using local file ./dump.txt.
Total: 22 samples
8 36.4% 36.4% 8 36.4% 00d8cb04
6 27.3% 63.6% 6 27.3% bar
3 13.6% 77.3% 3 13.6% __cos (inline)
2 9.1% 86.4% 2 9.1% 00d8cab4
1 4.5% 90.9% 1 4.5% 00d8cab6
1 4.5% 95.5% 1 4.5% 00d8cb06
1 4.5% 100.0% 1 4.5% __write_nocancel
0 0.0% 100.0% 3 13.6% __cos
Но информации о функции foo нет!
Информация о моей системе: Ubuntu 12.04 g++ 4.6.3
Это все!
2 ответа
TL;DR: foo
это быстро и мало, чтобы получить профилирование событий, запустить его еще 100 раз. Установка частоты была с опечаткой, и pprof
не будет пробовать чаще, чем CONFIG_HZ (обычно 250). Лучше перейти на более современный Linux perf
Profiler ( учебник от авторов, википедия).
Длинная версия:
Ваш foo
Функция слишком коротка и проста - просто вызовите две функции. Скомпилировал тест с g++ test.cc -lprofiler -o test.s -S -g
с фильтрацией test.s
с c++filt
программа для чтения имен C++:
foo():
.LFB972:
.loc 1 27 0
pushq %rbp
movq %rsp, %rbp
.loc 1 28 0
movl $.LC0, %esi
movl std::cout, %edi
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
movl std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&), %esi
movq %rax, %rdi
call std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))
.loc 1 29 0
popq %rbp
ret
.LFE972:
.size foo(), .-foo()
Итак, чтобы увидеть его в профиле, вы должны запустить foo
для большего количества раз, изменяя int a = 1000;
в основном к чему-то гораздо большему, например, 10000 или лучше 100000 (как я сделал для теста).
Также вы можете исправить неправильноеCPUPROFILE_FREQUENC=10000
" исправлять CPUPROFILE_FREQUENCY
(Обратите внимание Y
). Я должен сказать, что 10000 - слишком высокая настройка для CPUPROFILE_FREQUENCY, потому что он обычно может генерировать только 1000 или 250 событий в секунду в зависимости от конфигурации ядра CONFIG_HZ
(большинство ядер 3.x имеют 250, проверьте grep CONFIG_HZ= /boot/config*
). И значение по умолчанию для CPUPROFILE_FREQUENCY в pprof равно 100.
Я тестировал разные значения CPUPROFILE_FREQUENCY: 100000, 10000, 1000, 250 с помощью bash-скрипта в Ubuntu 14.04
for a in 100000 100000 10000 10000 1000 1000 300 300 250 250; do
echo -n "$a ";
CPUPROFILE_FREQUENCY=$a CPUPROFILE=dump$a.txt ./test >/dev/null;
done
И результаты были теми же 120-140 событиями и временем выполнения каждого./test около 0,5 секунды, поэтому cpuprofiler из google-perftools не может делать больше событий в секунду для одного потока, чем CONFIG_HZ, установленный в ядре (my имеет 250).
100000 PROFILE: interrupts/evictions/bytes = 124/1/6584
100000 PROFILE: interrupts/evictions/bytes = 134/0/7864
10000 PROFILE: interrupts/evictions/bytes = 125/0/7488
10000 PROFILE: interrupts/evictions/bytes = 123/0/6960
1000 PROFILE: interrupts/evictions/bytes = 134/0/6264
1000 PROFILE: interrupts/evictions/bytes = 125/2/7272
300 PROFILE: interrupts/evictions/bytes = 137/2/7984
300 PROFILE: interrupts/evictions/bytes = 126/0/7216
250 PROFILE: interrupts/evictions/bytes = 123/3/6680
250 PROFILE: interrupts/evictions/bytes = 137/2/7352
С оригинальным а =1000 foo
и функции cout работают слишком быстро, чтобы получать любое событие профилирования (даже при 250 событиях / с) на них при каждом запуске, поэтому у вас нет foo
ни какие-либо функции ввода / вывода. В небольшом количестве прогонов __write_nocancel
может получить событие выборки, а затем foo
и о функциях ввода / вывода из libstdC++ будет сообщено (где-то не в самом верху, поэтому используйте --text
вариант pprof
или же google-pprof
) с нулевым количеством собственных событий и ненулевым количеством дочерних событий:
....
1 0.9% 99.1% 1 0.9% __write_nocancel
....
0 0.0% 100.0% 1 0.9% _IO_new_file_overflow
0 0.0% 100.0% 1 0.9% _IO_new_file_write
0 0.0% 100.0% 1 0.9% __GI__IO_putc
0 0.0% 100.0% 1 0.9% foo
0 0.0% 100.0% 1 0.9% new_do_write
0 0.0% 100.0% 1 0.9% std::endl
0 0.0% 100.0% 1 0.9% std::ostream::put
С a=100000
foo все еще слишком короток и быстр, чтобы получать собственные события, но у функций ввода / вывода есть несколько. Это список, который я вычеркнул из длинных --text
выход:
34 24.6% 24.6% 34 24.6% __write_nocancel
1 0.7% 95.7% 35 25.4% __GI__IO_fflush
1 0.7% 96.4% 1 0.7% __GI__IO_putc
1 0.7% 97.8% 2 1.4% std::operator<<
1 0.7% 98.6% 36 26.1% std::ostream::flush
1 0.7% 99.3% 2 1.4% std::ostream::put
0 0.0% 100.0% 34 24.6% _IO_new_file_sync
0 0.0% 100.0% 34 24.6% _IO_new_file_write
0 0.0% 100.0% 40 29.0% foo
0 0.0% 100.0% 34 24.6% new_do_write
0 0.0% 100.0% 2 1.4% std::endl
Функции с нулевыми собственными счетчиками видны только благодаря pprof
возможность чтения цепочек вызовов (он знает, кто вызывает функции, получившие выборку, если информация о кадре не опущена).
Я также могу порекомендовать более современные, более функциональные (как программные, так и аппаратные события, с частотой до 5 кГц или более; профилирование как в пользовательском пространстве, так и в ядре) и лучше поддерживаемый профилировщик, Linux. perf
Profiler ( учебник от авторов, википедия).
Есть результаты из perf
с a=10000
:
$ perf record ./test3 >/dev/null
... skip some perf's spam about inaccessibility of kernel symbols
... note the 3 kHz frequency here VVVV
Lowering default frequency rate to 3250.
Please consider tweaking /proc/sys/kernel/perf_event_max_sample_rate.
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.078 MB perf.data (~3386 samples) ]
Чтобы увидеть текстовый отчет от perf.data
выходной файл я буду использовать меньше (потому что perf report
по умолчанию запускается интерактивный браузер профиля):
$ perf report |less
... skip some extra info about the machine, kernel, and perf starting command
# Samples: 1K of event 'cycles'
# Event count (approx.): 1155264208
# Overhead Command Shared Object Symbol
41.94% test3 libm-2.19.so [.] __tan_sse2
16.95% test3 libm-2.19.so [.] __sin_sse2
13.40% test3 libm-2.19.so [.] __cos_sse2
4.93% test3 test3 [.] bar()
2.90% test3 libc-2.19.so [.] __GI___libc_write
....
0.20% test3 test3 [.] foo()
Или же perf report -n | less
чтобы увидеть необработанные счетчики событий (образцов):
# Overhead Samples Command Shared Object
41.94% 663 test3 libm-2.19.so [.] __tan_sse2
16.95% 268 test3 libm-2.19.so [.] __sin_sse2
13.40% 212 test3 libm-2.19.so [.] __cos_sse2
4.93% 78 test3 test3 [.] bar()
2.90% 62 test3 libc-2.19.so [.] __GI___libc_write
....
0.20% 4 test3 test3 [.] foo()
Попробуйте установить LD_PRELOAD просто
LD_PRELOAD=/ USR / местные / Библиотека /libprofiler.so
Похоже, есть проблемы при передаче разделяемой библиотеки, которая не заканчивается суффиксом.so.