Профилятор gprof: Как разобраться в графе вызовов
Вперед Назад Содержание7. Как разобраться в графе вызовов
* Первичная строка:: Описание содержимого первичной строки.
* Вызывающие:: Описание содержимого строк вызывающих функций.
* Подпрограммы:: Описание содержимого строк вызываемых функций.
* Циклы:: Описание взаимно-рекурсивных функций, таких, как
`a' вызывает `b', а `b' вызывает `a'.
7.1 Первичная строка
"Первичная строка" каждой записи описывает функцию, к которой относится эта запись и представляет общую статистику для этой функции.
Для изучения мы повторим первичную строку записи для функции `report' из вышеприведенного примера, вместе со строками заголовков, содержащих имена полей:
index % time self children called name
...
[3] 100.0 0.00 0.05 1 report [3]
Что же означают поля в первичной строке:
- `index' (`индекс')
Записи нумеруются последовательными целыми числами. Поэтому каждая функция имеет индексный номер, указываемый в начале первичной строки ее записи.
Любая перекрестная ссылка на функцию, будь то вызывающая или вызываемая, содержит индекс функции, так же, как и ее имя. Индекс поможет вам отыскать запись для этой функции.
- `% time' (`% времени')
Это процент от общего времени исполнения вашей программы, затраченный на выполнение этой функции и вызванных из нее подпрограмм.
Время, затраченное на эту функцию, также подсчитывается для функций, вызвавших данную. Следовательно, простое суммирование этих процентов лишено всякого смысла.
- `self' (`собственных секунд')
Это количество секунд выполнения, подсчитанных только для этой функции. Оно должно быть идентично числу, указанному в поле `собственных секунд' в простом профиле для этой функции.
- `children' (`секунд в подпрограммах')
Это количество секунд выполнения, подсчитанных только для вызванных этой функцией подпрограмм. Оно должно быть идентично сумме `собственных секунд' и `секунд в подпрограммах' для всех укзанных ниже вызываемых функций [в этой записи].
- `called' (`вызывалась')
Это общее количество вызовов этой функции--сколько раз она была вызвана.
Если эта функция вызывает себя рекурсивно, то будут указаны два числа, разделенные `+'. Первое число показывает количество нерекурсивных вызовов, а второе--рекурсивных.
В вышеприведенном примере функция `report' была один раз вызвана из функции `main'.
- `name' (`имя функции')
Это имя текущей функции. Индексный номер повторяется после него.
Если функция является частью цикла взаимной рекурсии, то номер цикла выводится между именем функции и индексным номером (*См. Циклы::). Например, если функция `gnurr'--часть цикла номер один и имеет индекс `двенадцать', то первичная строка ее записи будет завершена так:
gnurr <cycle 1> [12]
7.2 Строки для вызывающей функции
Запись функции содержит строку для каждой функции, которая вызывала данную. Их поля соответствуют полям в первичной строке, но смысл их отличен из-за различий в содержании.
Для изучения мы повторим две строки из записи для функции `report'--первичную строку и одну из предшествующих строк вызывающей функции, вместе со строками заголовков, содержащих имена полей:
index % time self children called name
...
0.00 0.05 1/1 main [2]
[3] 100.0 0.00 0.05 1 report [3]
Что же означают поля в строке вызывающей функции (для `report',
вызванной из `main'):
- `self' (`собственных секунд')
Оценка времени, потраченного самой функцией `report', когда она была вызвана из функции `main'.
- `children' (`секунд в подпрограммах')
Оценка времени, потраченного подпрограммами функции `report', когда `report' была вызвана из функции `main'.
Сумма `собственных секунд' и `секунд в подпрограммах' является приблизительной оценкой времени выполнения `report', вызываемой из `main'.
- `called' (`вызывалась')
Два числа: количество вызовов `report' из `main', за которым следует общее количество нерекурсивных вызовов `report' изо всех функций, которые ее вообще вызывали.
- `name and index number' (`имя функции и ее индекс')
Имя функции, вызвавшей `report', к которой относится эта строка, и следом указан индекс этой функции.
Не все функции имеют записи в графе вызовов; некоторые из опций `gprof' требуют пропустить некоторые функции. Если вызывающая функция не имеет записи о себе, она, тем не менее, имеет строку вызывающей функции в записях тех функций, которые она вызывает.
Если вызывающая функция является частью рекурсивного цикла, то номер цикла выводится между именем и индексным номером.
7.3 Строки для подпрограмм функции
Запись функции содержит по строке на каждую из ее подпрограмм - другими словами, по строке на каждую другую функцию, которую она вызывает. Их поля соответствуют полям в первичной строке, но смысл их отличен из-за различий в содержании.
Для изучения мы повторим две строки из записи для функции `main'-- первичную строку и одну из последующих строк вызванной ею функции, вместе со строками заголовков, содержащих имена полей:
index % time self children called name
...
[2] 100.0 0.00 0.05 1 main [2]
0.00 0.05 1/1 report [3]
Что же означают поля в строке вызванной функции (для `main',
вызывающей `report'):
- `self' (`собственных секунд')
Оценка времени, потраченного самой функцией `report', когда она была вызвана из функции `main'.
- `children' (`секунд в подпрограммах')
Оценка времени, потраченного подпрограммами функции `report', когда `report' была вызвана из функции `main'.
Сумма `собственных секунд' и `секунд в подпрограммах' является приблизительной оценкой времени выполнения `report', вызываемой из `main'.
- `called' (`вызывалась')
Два числа: количество вызовов `report' из `main', за которым следует общее количество нерекурсивных вызовов `report'.
- `name' (`имя функции')
Имя функции, вызванной из `main', к которой относится эта строка, и следом указан индекс этой функции.
Если вызывающая функция является частью рекурсивного цикла, то номер цикла выводится между именем и индексным номером.
7.4 Как описываются взаимно-рекурсивные функции
Картина может быть осложнена наличием "циклической рекурсии" в графе вызовов. Цикл возникает, когда некоторая функция вызывает другую функцию, которая (прямо или опосредованно) вызывает (или пытается это сделать) первую. Например, если `a' вызывает `b', а затем `b' вызывает `a', то `a' и `b' организуют циклическую рекурсию.
Когда бы вызовы ни прошли по обоим путям между двумя функциями, они принадлежат к одному и тому же циклу. Если `a' и `b' вызывают друг друга, и `b' и `c' вызывают друг друга, то все три функции находятся в одном и том же цикле. Однако следует заметить, что даже если только `b' вызывает `a', но при этом сама не была вызвана из `a', то `gprof' не способен распознать это, и, таким образом, `a' и `b' все же считаются в цикле.
Циклы нумеруются последовательными целыми числами. Если функция принадлежит циклу, всякий раз, когда ее имя встречается в графе вызовов, за ним следует `<cycle NUMBER>' (`<цикл НОМЕР>')
Циклы создают проблему, заключающуюся в том, что циклы делают парадоксальными значения времен в графе вызовов. Поле `время в подпрограммах' для `a' должно включать в себя время, затраченное на вызываемую `b' и все ее подпрограммы--но одной из подпрограмм `b' является `a'! А сколько времени выполнения `a' должно быть включено во `время в подпрограммах' функции `a', если `a' непосредственно рекурсивна?
Спсоб, которым `gprof' разрешает этот парадокс, заключается в создании простой записи для цикла как для единого целого. Первичная строка этой записи описывает общее время, затраченное на непосредственное выполнение функций цикла. "Подпрограммами" цикла являются отдельные его функции, а также все вызываемые из них функции. "Вызывающими" функциями для цикла являются такие функции, которые не входят в сам цикл, но вызывают функции цикла.
Вот пример части графа вызовов, в котором показан цикл, содержащий функции `a' и `b'. Цикл начинался вызовом `a' из `main'; обе, `a' и `b', вызывали `c'.
index % time self children called name
----------------------------------------
1.77 0 1/1 main [2]
[3] 91.71 1.77 0 1+5 <cycle 1 as a whole> [3]
1.02 0 3 b <cycle 1> [4]
0.75 0 2 a <cycle 1> [5]
----------------------------------------
3 a <cycle 1> [5]
[4] 52.85 1.02 0 0 b <cycle 1> [4]
2 a <cycle 1> [5]
0 0 3/6 c [6]
----------------------------------------
1.77 0 1/1 main [2]
2 b <cycle 1> [4]
[5] 38.86 0.75 0 1 a <cycle 1> [5]
3 b <cycle 1> [4]
0 0 3/6 c [6]
----------------------------------------
(Полный граф вызовов для этой программы содержит также запись для
`main', которая вызывает `a', и запись для `c', которую вызывают `a' и
`b').
index % time self children called name
<spontaneous>
[1] 100.00 0 1.93 0 start [1]
0.16 1.77 1/1 main [2]
----------------------------------------
0.16 1.77 1/1 start [1]
[2] 100.00 0.16 1.77 1 main [2]
1.77 0 1/1 a <cycle 1> [5]
----------------------------------------
1.77 0 1/1 main [2]
[3] 91.71 1.77 0 1+5 <cycle 1 as a whole> [3]
1.02 0 3 b <cycle 1> [4]
0.75 0 2 a <cycle 1> [5]
0 0 6/6 c [6]
----------------------------------------
3 a <cycle 1> [5]
[4] 52.85 1.02 0 0 b <cycle 1> [4]
2 a <cycle 1> [5]
0 0 3/6 c [6]
----------------------------------------
1.77 0 1/1 main [2]
2 b <cycle 1> [4]
[5] 38.86 0.75 0 1 a <cycle 1> [5]
3 b <cycle 1> [4]
0 0 3/6 c [6]
----------------------------------------
0 0 3/6 b <cycle 1> [4]
0 0 3/6 a <cycle 1> [5]
[6] 0.00 0 0 6 c [6]
----------------------------------------
Поле `self' в первичной строке цикла--общее время выполнения всех
функций цикла. Оно равно сумме полей `self' для каждой отдельной функции
цикла, взятых из записей вызываемых функций.
Поле `children' в первичной строке цикла и строки подпрограмм содержат времена только тех функций, которые не входят в цикл. Хотя `a' и вызывает `b', время таких вызовов `b' не подсчитывается для `a' в поле `children'. Таким образом, мы не натыкаемся на проблему: что делать, если время в этих вызовах `b' включает опосредованные рекурсивные вызовы `a'.
Поле `children' в строках вызывающих функций в записи цикла оценивают время, затрачиваемое *на весь цикл в целом*, и на другие его подпрограммы, в том случае, когда эта функция вызывает одну из функций цикла.
Поле `calls' в первичной строке цикла содержит два числа: во-первых, количество вызовов функций цикла функциями извне цикла; во-вторых, количество вызовов функций цикла функциями цикла (включая количество вызовов самой себя). Это обобщение обычного разделения вызовов на рекурсивные и нерекурсивные.
Поле `calls' в строке вызываемой подпрограммы, входящей в цикл, в записи цикла сообщает, сколько раз эта функция была вызвана из функций цикла. Общее же их количество показано вторым числом в поле `calls' в первичной строке.
В отдельных записях для каждой функции цикла могут встречаться другие функции, указанные и как вызывающие, и как вызываемые. Эти строки показывают, сколько раз каждая из функций цикла вызывала или была вызвана из других функций цикла. Поля `self' и `children' таких строк не заполняются, так как затруднительно определить их смысл (значение), когда начинается рекурсия.
Вперед Назад Содержание