Несоответствие между gprof и (unix) временем; gprof сообщает о более низком времени выполнения

У меня есть простая программа-сортировщик, которую я профилирую, чтобы иметь кейс для изучения gprof; Позже я планирую профилировать гораздо более крупный алгоритм.

Я скомпилировал с -pg и запустил ./sort для создания файла gmon.out. Однако, когда я запускаю gprof ./sort gmon.out, значения, полученные в совокупных секундах и собственных секундах, как я полагаю, неточны.

Во-первых, запустив time(./sort), я получаю:

real    0m14.352s
user    0m14.330s
sys     0m0.005s

Что точно с моим секундомером.

Однако результаты gprof для плоского профиля таковы:

Each sample counts as 0.01 seconds.
%   cumulative   self              self     total           
time   seconds   seconds    calls   s/call   s/call  name    
56.18      2.76     2.76        1     2.76     4.71  sort(std::vector<int, std::allocator<int> >&)
35.01      4.49     1.72 1870365596     0.00     0.00  std::vector<int, std::allocator<int> >::operator[](unsigned long)
8.96      4.93     0.44   100071     0.00     0.00  std::vector<int, std::allocator<int> >::size() const
0.00      4.93     0.00    50001     0.00     0.00  __gnu_cxx::new_allocator<int>::construct(int*, int const&)
0.00      4.93     0.00    50001     0.00     0.00  void __gnu_cxx::__alloc_traits<std::allocator<int> >::construct<int>(std::allocator<int>&, int*, int const&)
0.00      4.93     0.00    50001     0.00     0.00  std::vector<int, std::allocator<int> >::push_back(int const&)
0.00      4.93     0.00    50001     0.00     0.00  operator new(unsigned long, void*)
0.00      4.93     0.00      170     0.00     0.00  std::_Iter_base<int*, false>::_S_base(int*)
0.00      4.93     0.00      102     0.00     0.00  std::_Niter_base<int*>::iterator_type std::__niter_base<int*>(int*)
0.00      4.93     0.00       68     0.00     0.00  __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >::base() const
0.00      4.93     0.00       68     0.00     0.00  std::_Miter_base<int*>::iterator_type std::__miter_base<int*>(int*)
0.00      4.93     0.00       52     0.00     0.00  std::_Vector_base<int, std::allocator<int> >::_M_get_Tp_allocator()
0.00      4.93     0.00       51     0.00     0.00  __gnu_cxx::new_allocator<int>::max_size() const
0.00      4.93     0.00       34     0.00     0.00  __gnu_cxx::__alloc_traits<std::allocator<int> >::max_size(std::allocator<int> const&)
0.00      4.93     0.00       34     0.00     0.00  __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >::__normal_iterator(int* const&)
0.00      4.93     0.00       34     0.00     0.00  std::_Vector_base<int, std::allocator<int> >::_M_get_Tp_allocator() const
0.00      4.93     0.00       34     0.00     0.00  std::vector<int, std::allocator<int> >::max_size() const
0.00      4.93     0.00       34     0.00     0.00  int* std::__copy_move<false, true, std::random_access_iterator_tag>::__copy_m<int>(int const*, int const*, int*)
0.00      4.93     0.00       34     0.00     0.00  int* std::__uninitialized_copy<true>::__uninit_copy<int*, int*>(int*, int*, int*)
0.00      4.93     0.00       34     0.00     0.00  int* std::__copy_move_a<false, int*, int*>(int*, int*, int*)
0.00      4.93     0.00       34     0.00     0.00  int* std::__copy_move_a2<false, int*, int*>(int*, int*, int*)
0.00      4.93     0.00       34     0.00     0.00  int* std::uninitialized_copy<int*, int*>(int*, int*, int*)
0.00      4.93     0.00       34     0.00     0.00  int* std::__uninitialized_copy_a<int*, int*, int>(int*, int*, int*, std::allocator<int>&)
0.00      4.93     0.00       34     0.00     0.00  int* std::__uninitialized_move_if_noexcept_a<int*, int*, std::allocator<int> >(int*, int*, int*, std::allocator<int>&)
0.00      4.93     0.00       34     0.00     0.00  int* std::copy<int*, int*>(int*, int*, int*)
0.00      4.93     0.00       18     0.00     0.00  void std::_Destroy_aux<true>::__destroy<int*>(int*, int*)
0.00      4.93     0.00       18     0.00     0.00  std::_Vector_base<int, std::allocator<int> >::_M_deallocate(int*, unsigned long)
0.00      4.93     0.00       18     0.00     0.00  void std::_Destroy<int*>(int*, int*)
0.00      4.93     0.00       18     0.00     0.00  void std::_Destroy<int*, int>(int*, int*, std::allocator<int>&)
0.00      4.93     0.00       17     0.00     0.00  __gnu_cxx::new_allocator<int>::deallocate(int*, unsigned long)
0.00      4.93     0.00       17     0.00     0.00  __gnu_cxx::new_allocator<int>::allocate(unsigned long, void const*)
0.00      4.93     0.00       17     0.00     0.00  __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >::difference_type __gnu_cxx::operator-<int*, std::vector<int, std::allocator<int> > >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > const&, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > const&)
0.00      4.93     0.00       17     0.00     0.00  std::vector<int, std::allocator<int> >::_M_check_len(unsigned long, char const*) const
0.00      4.93     0.00       17     0.00     0.00  std::_Vector_base<int, std::allocator<int> >::_M_allocate(unsigned long)
0.00      4.93     0.00       17     0.00     0.00  std::vector<int, std::allocator<int> >::_M_insert_aux(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, int const&)
0.00      4.93     0.00       17     0.00     0.00  std::vector<int, std::allocator<int> >::end()
0.00      4.93     0.00       17     0.00     0.00  std::vector<int, std::allocator<int> >::begin()
0.00      4.93     0.00       17     0.00     0.00  unsigned long const& std::max<unsigned long>(unsigned long const&, unsigned long const&)
0.00      4.93     0.00        2     0.00     0.00  std::operator|(std::_Ios_Openmode, std::_Ios_Openmode)
0.00      4.93     0.00        1     0.00     0.00  _GLOBAL__sub_I_main
0.00      4.93     0.00        1     0.00     0.00  generateData(std::basic_ofstream<char, std::char_traits<char> >&)
0.00      4.93     0.00        1     0.00     0.22  writeSortedFile(std::vector<int, std::allocator<int> >&)
0.00      4.93     0.00        1     0.00     0.00  __static_initialization_and_destruction_0(int, int)
0.00      4.93     0.00        1     0.00     0.00  loadBuf(std::vector<int, std::allocator<int> >&, std::basic_ifstream<char, std::char_traits<char> >&)
0.00      4.93     0.00        1     0.00     0.00  __gnu_cxx::new_allocator<int>::new_allocator()
0.00      4.93     0.00        1     0.00     0.00  __gnu_cxx::new_allocator<int>::~new_allocator()
0.00      4.93     0.00        1     0.00     0.00  std::allocator<int>::allocator()
0.00      4.93     0.00        1     0.00     0.00  std::allocator<int>::~allocator()
0.00      4.93     0.00        1     0.00     0.00  std::_Vector_base<int, std::allocator<int> >::_Vector_impl::_Vector_impl()
0.00      4.93     0.00        1     0.00     0.00  std::_Vector_base<int, std::allocator<int> >::_Vector_impl::~_Vector_impl()
0.00      4.93     0.00        1     0.00     0.00  std::_Vector_base<int, std::allocator<int> >::_Vector_base()
0.00      4.93     0.00        1     0.00     0.00  std::_Vector_base<int, std::allocator<int> >::~_Vector_base()
0.00      4.93     0.00        1     0.00     0.00  std::vector<int, std::allocator<int> >::vector()
0.00      4.93     0.00        1     0.00     0.00  std::vector<int, std::allocator<int> >::~vector()

Таким образом, кумулятивные секунды не накапливаются до истинного значения (~ 14 с), как может показаться. Результаты показывают, что sort() требует больше всего времени, но фактические значения времени не складываются. -z не меняет этого, но это ожидаемо. График вызовов (не включен), кажется, не показывает ничего, что указывало бы на то, где находятся пропущенные секунды; то есть лишние секунды не у детей.

Кажется, я получаю аналогичные результаты (где gprof дает гораздо меньшие значения времени, чем ожидалось), когда я пытаюсь профилировать свой более крупный алгоритм, о котором я упоминал выше - gprof говорит, что время выполнения составляет ~ 450 с, тогда как на самом деле это занимает более 3 часов. Я думал, что это произошло из-за того, что gprof не мог обрабатывать MPI, который широко используется в более крупном алгоритме, но теперь я думаю, что либо неправильно интерпретирую результаты gprof, либо упускаю некоторые флаги.

Возможно ли, что я на самом деле не учитываю свой файл gmon.out? Причина, по которой я так думаю, заключается в том, что когда я запускаю gprof ./sort, я получаю те же результаты, что и gprof ./sort gmon.out. Поэтому кажется, что он даже не использует gmon.out. Я думал, что gmon.out нужен в сочетании с исполняемым файлом для сопоставления времени с функциями. Как gprof может выдать результат без gmon.out?

Любая поучительная информация приветствуется, заранее спасибо!

ПРИМЕЧАНИЕ: читая, например, (этот пост), я нашел информацию, предлагающую что у gprof проблемы с выделением кучи для анализа и т. д. (new). Я должен отметить, что ./sort использует std::vector для хранения элементов, которые будут размещены в куче. Пожалуйста, дайте мне знать, если это возможная проблема.


person izaak_pyzaak    schedule 21.05.2016    source источник
comment
Изучаете гпроф? У меня есть кое-что для вас: это, это и это , для начала.   -  person Mike Dunlavey    schedule 21.05.2016


Ответы (1)


gprof не знает о функциях, к которым у него нет доступа к отладочной информации, то есть о стандартной библиотеке. Если вы хотите получить точное прошедшее время и при этом получить график вызовов, вы можете использовать perf.

В качестве примера я написал программу, которая зацикливается 10000 раз. В этом цикле я заполняю вектор случайными значениями, а затем сортирую его. Для gprof я делаю следующие шаги:

g++ -std=c++11 -O2 -pg -g
./a.out
gprof -b ./a.out

gmon.out создается, если он не существует, и перезаписывается, если существует, и автоматически используется gprof, если вы не укажете файл для использования. -b подавляет текстовые аннотации.

Вот пример вывода:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  Ts/call  Ts/call  name    
100.52      4.94     4.94                             frame_dummy
  0.00      4.94     0.00       26     0.00     0.00  void std::__adjust_heap<__gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >, long, double, __gnu_cxx::__ops::_Iter_less_iter>(__gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >, long, long, double, __gnu_cxx::__ops::_Iter_less_iter)
  0.00      4.94     0.00        1     0.00     0.00  _GLOBAL__sub_I_main

Как видите, он записывает только время реализации векторной кучи и вообще не знает о сортировке (или чем-то еще). Теперь попробуем perf:

perf record -g ./a.out
perf report --call-graph --stdio

# Total Lost Samples: 0
#
# Samples: 32K of event 'cycles'
# Event count (approx.): 31899806183
#
# Children      Self  Command  Shared Object        Symbol                                                                            
# ........  ........  .......  ...................  ..................................................................................
#
    99.98%    34.46%  a.out    a.out                [.] main                                                                          
              |          
              |--65.52%-- main
              |          |          
              |          |--65.29%-- std::__introsort_loop<__gnu_cxx::__normal_iterator<double*, std::vector<double

[Остальной текст опущен]

Как видите, perf перехватывает функцию сортировки. Если бы я запустил perf stat, я бы также получил точное время выполнения.

Если вы используете GCC, вы можете передать -D_GLIBCXX_DEBUG, чтобы он использовал реализацию библиотеки отладки. Это заставит ваш код работать намного медленнее, но это необходимо для того, чтобы gprof мог видеть эти функции. Пример:

g++ -std=c++11 -O2 test.cpp -D_GLIBCXX_DEBUG -pg -g
./a.out
gprof -b ./a.out

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  us/call  us/call  name    
 88.26      0.15     0.15   102875     1.46     1.46  __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<double*, std::__cxx1998::vector<double, std::allocator<double> > >, std::__debug::vector<double, std::allocator<double> > > std::__unguarded_partition<__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<double*, std::__cxx1998::vector<double, std::allocator<double> > >, std::__debug::vector<double, std::allocator<double> > >, __gnu_cxx::__ops::_Iter_less_iter>(__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<double*, std::__cxx1998::vector<double, std::allocator<double> > >, std::__debug::vector<double, std::allocator<double> > >, __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<double*, std::__cxx1998::vector<double, std::allocator<double> > >, std::__debug::vector<double, std::allocator<double> > >, __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<double*, std::__cxx1998::vector<double, std::allocator<double> > >, std::__debug::vector<double, std::allocator<double> > >, __gnu_cxx::__ops::_Iter_less_iter)
 11.77      0.17     0.02   996280     0.02     0.02  void std::__unguarded_linear_insert<__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<double*, std::__cxx1998::vector<double, std::allocator<double> > >, std::__debug::vector<double, std::allocator<double> > >, __gnu_cxx::__ops::_Val_less_iter>(__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<double*, std::__cxx1998::vector<double, std::allocator<double> > >, std::__debug::vector<double, std::allocator<double> > >, __gnu_cxx::__ops::_Val_less_iter)
  0.00      0.17     0.00   417220     0.00     0.00  frame_dummy
  0.00      0.17     0.00   102875     0.00     0.00  void std::__move_median_to_first<__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<double*, std::__cxx1998::vector<double, std::allocator<double> > >, std::__debug::vector<double, std::allocator<double> > >, __gnu_cxx::__ops::_Iter_less_iter>(__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<double*, std::__cxx1998::vector<double, std::allocator<double> > >, std::__debug::vector<double, std::allocator<double> > >, __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<double*, std::__cxx1998::vector<double, std::allocator<double> > >, std::__debug::vector<double, std::allocator<double> > >, __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<double*, std::__cxx1998::vector<double, std::allocator<double> > >, std::__debug::vector<double, std::allocator<double> > >, __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<double*, std::__cxx1998::vector<double, std::allocator<double> > >, std::__debug::vector<double, std::allocator<double> > >, __gnu_cxx::__ops::_Iter_less_iter)
  0.00      0.17     0.00     1000     0.00     0.25  void std::__insertion_sort<__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<double*, std::__cxx1998::vector<double, std::allocator<double> > >, std::__debug::vector<double, std::allocator<double> > >, __gnu_cxx::__ops::_Iter_less_iter>(__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<double*, std::__cxx1998::vector<double, std::allocator<double> > >, std::__debug::vector<double, std::allocator<double> > >, __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<double*, std::__cxx1998::vector<double, std::allocator<double> > >, std::__debug::vector<double, std::allocator<double> > >, __gnu_cxx::__ops::_Iter_less_iter)
  0.00      0.17     0.00        1     0.00     0.00  _GLOBAL__sub_I_main

Я намеренно уменьшил количество итераций, чтобы выполнение выполнялось за разумное время, но вы видите, что gprof теперь показывает функции, которые раньше не учитывались.

person user6364653    schedule 21.05.2016
comment
Значит, у perf есть доступ к stl? В настоящее время я запускаю perf, пока мы говорим, так как он, кажется, дает правильные результаты с ./sort - person izaak_pyzaak; 21.05.2016