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

Я учусь профилировать мой код с помощью gprof. Для одного из моих приложений у меня есть следующий вывод:

Each sample counts as 0.01 seconds.
%   cumulative   self              self     total
time   seconds   seconds    calls  ms/call  ms/call  name
10.27      1.20     1.20                             Location::get_type() const (location.cpp:20 @ 40a4bd)

Дальше я вижу это

  1.20      4.98     0.14 34662692     0.00     0.00  Location::get_type() const (location.cpp:19 @ 40a4ac)

Вот функция

char Location::get_type() const {
return type;
}

Я предполагаю, что первая строка от gprof указывает на общее время, необходимое для выполнения функции, а вторая строка указывает только на время, необходимое для оператора return. У меня есть другие функции, которые являются получателями для того же класса, которые возвращают ints, но разница между временем функции и оператором возврата составляет всего около 0,1 секунды, тогда как, как и в случае с опубликованным мной временем, разница во времени составляет 1,06 секунды (другие методы получения вызываются примерно в 2 миллиона раз меньше, что мало по сравнению с общее количество звонков). Чем можно объяснить более высокие времена вызова функции по сравнению с одной строкой кода в ней?

Возможно, стоит упомянуть, что я скомпилировал с -g -pg, так как я использую gprof в построчном режиме.

Редактировать:
Один из ответов предложил мне посмотреть на вывод сборки. Я не могу понять это, поэтому я опубликую это здесь. Я разместил код сборки для двух вызовов функций. Первый — get_floor (), который относительно быстрый (~ .10 секунд). Второй — get_type (), который работает медленно.

_ZNK8Location9get_floorEv:
.LFB5:
.cfi_startproc
pushq   %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
movq    %rdi, -8(%rbp)
movq    -8(%rbp), %rax
movl    8(%rax), %eax
popq    %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE5:
.size   _ZNK8Location9get_floorEv, .-_ZNK8Location9get_floorEv
.align 2
.globl  _ZNK8Location8get_typeEv
.type   _ZNK8Location8get_typeEv, @function
_ZNK8Location8get_typeEv:
.LFB6:
.cfi_startproc
pushq   %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
movq    %rdi, -8(%rbp)
movq    -8(%rbp), %rax
movzbl  12(%rax), %eax
popq    %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc

0

Решение

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

Причина в том, что время выполнения на современных процессорах является очень сложной функцией большого количества параметров, и инструментарий (код, добавленный в функцию для обновления статистики) все испортит. Для коротких функций, подобных этой, одни только инструменты, вероятно, намного больше, чем сам кодируемый код.

Для профилирования быстрой функции вы должны использовать пассивные профилировщики, такие как OProfile это просто запустить программу и проверить с квазирегулярными интервалами, где она есть. Вы не получаете точных счетчиков, но приближение, которое вы можете получить из случайной выборки, намного ближе к реальной, потому что поведение программы сильно не изменилось.

Другой вариант увидеть, где программа тратит время, — просто поэкспериментировать. Если вы подозреваете, что определенная часть кода является узким местом, попробуйте выполнить ее 10 раз (это часто не сложно) и проверьте, насколько изменяется общее время выполнения программы. Если замедление невелико, то вы знаете, что даже возможность полного удаления этого кода не спасет вас.

1

Другие решения

первый В моей голове всплывает тот факт, что стоимость вызова функции не равна нулю — вы должны устанавливать и разрушать стековый фрейм каждый раз, когда вызываете его.

Тот может быть источником вашего несоответствия, вы должны изучить выходные данные ассемблера (например, с gcc -S) чтобы увидеть, что лежит в основе кода.

0

Если вы хотите измерить температуру воздуха внутри коробки объемом 1 кубический сантиметр и поместить в нее термометр с головкой объемом 0,8 см3, вы не будете измерять температуру. воздуха, но один из термометра.

Ваша функция настолько мала, что профилировщик фактически измеряет свои накладные расходы.

0
По вопросам рекламы [email protected]