Почему вызов функции занимает значительно больше времени, чем код, выполняемый согласно 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 (), относительно быстро (~ 0,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

person gsingh2011    schedule 28.09.2012    source источник
comment
Get_type - это виртуальная функция?   -  person casablanca    schedule 28.09.2012
comment
Одна из возможностей состоит в том, что существует проблема детализации; механизм профилирования работает в течение 0,01 (1/100) секунды, а затем проверяет, где находится счетчик программы, и принимает вас в функцию. Однако, если функции небольшие, я считаю, что вы можете в конечном итоге получить неправильную функцию, потому что профиль обрабатывает отрезок машинного кода из 16 или 32 байтов как все находящиеся в одной функции.   -  person Jonathan Leffler    schedule 28.09.2012
comment
Вы получаете хорошие ответы, но мне любопытно узнать, что заставляет вас сосредоточиться на gprof. Учитель рекомендовал это? Рекомендовали ли это блог, книгу или какую-то документацию? Имеет ряд проблем.   -  person Mike Dunlavey    schedule 28.09.2012
comment
@MikeDunlavey Это был один из первых, что я увидел, и получил здесь довольно много голосов: stackoverflow.com/questions/375913/. Я не понимал, что у него так много проблем, я попробую другой профилировщик и вернусь к этому вопросу.   -  person gsingh2011    schedule 28.09.2012


Ответы (3)


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

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

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

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

person 6502    schedule 28.09.2012

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

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

person paxdiablo    schedule 28.09.2012
comment
Спасибо за совет. Я опубликовал результат сборки, так как не могу понять его. - person gsingh2011; 28.09.2012

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

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

person Emilio Garavaglia    schedule 28.09.2012