Выбрать главу

По схеме вызовов несложно определить время работы той или иной функции. Однако рекурсивные функции требуют особого подхода. Например, функция even() вызывает функцию odd(), а та — снова функцию even(). Самому длинному из таких циклов присваивается номер и выделяется отдельная секция отчета. Следующий фрагмент профильных данных получен в результате проверки того, является ли результат операции 1787 × 13 × 3 четным:

--------------------------------------

              0.00 0.02    1/1        main [1]

[9]       0.1 0.00 0.02    1         apply_unary_function [9]

              0.01 0.00    1/1        even <cycle 1> [13]

              0.00 0.00    1/1806     destroy_number [5]

              0.00 0.00    1/13       empty_stack [17]

              0.00 0.00    1/6        pop_stack [16]

              0.00 0.00    1/6        push_stack [19]

--------------------------------------

[10]       0.1 0.01 0.00   1+69993   <cycle 1 as a whole> [10]

               0.00 0.00 34647        even <cycle 1> [13]

--------------------------------------

                         34847        even <cycle 1> [13]

[11]       0.1 0.01 0.00 34847       odd <cycle 1> [11]

               0.00 0.00 34847/186997954 zerop [7]

               0.00 0.00   1/1806     make_zero [16]

                         34846        even <cycle 1> [13]

Выражение 1+69693 в секции 10 сообщает о том что цикл 1 выполнялся один раз и в нем насчитывается 69693 обращений к функциям. Первой в цикле вызывалась функция even(), а из нее — функция odd. Обе функции вызывались по 34847 раз.

Утилита gprof располагает рядом полезных опций.

■ При задании опции -s будут суммироваться результаты нескольких запусков программы.

■ С помощью опции -c можно узнать, какие дочерние функции могли быть, но так и не были вызваны

■ При задании опции -l отображается построчная профильная информация.

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

А.3.4. Как работает утилита gprof

Схема работы утилиты gprof выглядит следующим образом. Когда в ходе выполнения программы происходит вызов функции, счётчик обращений к функции увеличивается на единицу. Утилита периодически прерывает программу, чтобы выяснить, какая функция выполняется в данный момент. На основании этих '"выборок" и определяется время выполнения. В Linux тактовые импульсы генерируются с интервалом 0,01 с, следовательно, это наименьший промежуток между прерываниями. Таким образом, профильные данные о слишком быстро выполняющихся функциях могут оказаться неточными. Во избежание погрешностей рекомендуется запускать программу на длительные периоды времени или суммировать профильные данные по результатам нескольких запусков (это делается с помощью опции -s).

А.3.5. Исходные тексты программы-калькулятора

В листинге А.3 показан текст программы, вычисляющей значения постфиксных выражений.

Листинг А.3. (calculator.c) Основная часть программы-калькулятора

/* Вычисления в унарном формате. */

/* На вход программы подаются однострочные выражения

   в обратной польской (постфиксной) записи, например:

   602 7 5 - 3 * +

   Вводимые числа должны быть неотрицательными

   десятичными числами. Поддерживаются операторы

   "+", "-" и "*". Унарные операторы "even" и "odd"

   возвращают значение 1 в том случае, когда операнд

   является четным или нечетным соответственно.

   Лексемы разделяются пробелами. Отрицательные числа

   не поддерживаются. */

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <ctype.h>

#include "definitions.h"

/* Эта функция выполняет указанную бинарную операцию над

   операндами, извлекаемыми из стека, помещая результат

   обратно в стек, в случае успеха возвращается

   ненулевое значение. */

int apply_binary_function(number (*function)(number, number),

 Stack* stack) {