ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы.



 

Часть 3

                                   ГЛАВА 2: ВНУТРИ ПРОФИЛИРОВЩИКА


     Для того, чтобы извлечь из системы Turbo Profiler наибольшую
пользу, необходимо  понимать внутренние механизмы ее работы. Зна-
ние того, каким образом действует  профилировщик, встретив маркер
"области", или  того, что  происходит каждый раз, когда  профили-
ровщик прерывает  выполнение программы позволит Вам отточить вашу
технику профилирования,  а также   более  адекватно задавать  тип
статистической информации, которую необходимо собрать для той или
иной "области", и интерпретировать полученные данные.

     Рассмотрим исходный текст PTOLL и PTOLLPAS:


* Copyright (c) 1990, Borland International */
#include 
#include    /* contains prototype for delay() */

main()
{
    printf("Entering main\n");
    route66();
    printf("Back in main\n");
    delay(1000);
    highway80();
    printf("Back in main\n");
    delay(1000);
    printf("Leaving main\n\n");
}

route66()
{
    printf("Entering Route 66\n");
    delay(2000);
    printf("Leaving  Route 66\n");
}

highway80()
{
    printf("Entering Highway 80\n");
    delay(2000);
    printf("Leaving Highway 80\n");
}

{ Copyright (c) 1990, Borland International }
Uses Crt;

Procedure Route66;
Begin
  Writeln( 'Entering Route 66' );
  Delay(2000);
  Writeln( 'Leaving  Route 66' );
End;

Procedure Highway80;
Begin
  Writeln( 'Entering Highway 80' );
  Delay(2000);
  Writeln( 'Leaving  Highway 80' );
End;

Begin
  Writeln( 'Entering main' );
  route66;
  Writeln( 'back in main' );
  Delay(1000);
  highway80;
  Writeln( 'back in main' );
  delay(100);
  Writeln( 'Leaving main' );
End.



     Задание в  качестве "областей" всех подпрограмм в рассматри-
ваемом модуле ведет к образованию четырех переменных для подсчета
времени и четырех переменных для подсчета количества вызовов.

     Рис.2.1 Переменные  для подсчета  времени выполнения и числа
вызовов подпрограмм для PTOLL/PTOLLPAS

     Воображаемые пункты сбора подорожной пошлины.

     В данном  разделе мы  проследим за  выполнением программы  и
рассмотрим, что  происходит при  встрече маркеров "области". Этот
процесс можно  представлять себе  как проход через ряд пунктов, в
которых собирается  подорожная пошлина.  Как только  Вы проходите
через такой пункт, Вы сразу же попадаете на участок дороги, отно-
сящийся к  данному пункту и продвигаетесь по этому участку до тех
пор, пока не попадете на следующий пункт сбора пошлины.

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

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

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

     Как только  Вы преодолели  маркер "области" (пункт сбора по-
шлины), которым  помечена функция  main переменная, отвечающая за
подсчет числа вызовов main, автоматически увеличивается на едини-
цу и  каждый импульс таймера, происходящий с момента входа в main
до вызова  route66, увеличивает  значение переменной,  в  которой
учитывается время выполнения функции main.

     На следующем  этапе выполнения main вызывает функцию route66
и Вы  попадаете на  новый участок дороги. В момент перехода через
маркер (пункт  сбора пошлины),  которым отмечена функция route66,
происходит несколько вещей:

*    Текущей "областью" начинает считаться route66;

*    Отправляются в стек переменные, в которых накапливаются дан-
     ные для вызывающей функции (в данном случае - функции main);

*    Переменная,  в  которой  подсчитывается  количество  вызовов
     route66, увеличивается на единицу.

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

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

     ..............................Переменная, накапливающая  ко-
личество обращений  к вызывающей функции в этот момент не изменя-
ется. Тем  не менее, каждый импульс таймера, происходящий с этого
момента до  вызова highway80 добавляется, как к переменной, в ко-
торой накапливается  время работы  функции main, так и к перемен-
ной, в которой накапливается время выполнения всей программы. Для
того, чтобы  проверить этот факт, попробуйте убрать маркер "обла-
сти", отмечающий route66 и затем сравнить данные, которые получа-
ются в  после этого, c профилем для случая, когда маркер установ-
лен. Вы  должны заметить, что общее время выполнения осталось тем
же. Время же выполнения main должно увеличиться как раз на столь-
ко, сколько раньше требовалось для выполнения route 66.

     Определение затрат времени на вызов подпрограмм

      У  вас может возникнуть желание измерить время,которое тра-
тится непосредственно на вызов подпрограммы (например подпрограм-
мы route66), без учета времени работы самой подпрограммы. В нашем
случае самым  простым способом  сделать это  является прекращение
сбора статистических данных во входной точке route66, и возобнов-
ление сбора  этих данных   сразу  же после  возврата из  route66.
(Также вы  можете получить информацию такого рода при работе про-
филировщика в пассивном режиме анализа, обсуждаемом в Главе 3). А
теперь, поместите курсор на первую строку route66, затем выберите
команду Operation (Работа) в локальном меню окна Module (Модуль),
для того, чтобы открыть блок диалога Area Options (Параметры "об-
ласти"), установите  Operation (Работа)  в состояние Disable (От-
ключить), нажмите клавишу Enter.

     Когда вы отключаете сбор статистики при входе в route66, при
возврате из  этой подпрограммы, сбор данных автоматически не воз-
обновляется. Вы должны установить маркер "области" на закрывающую
фигурную скобку  функции route66,  и  снова  установить  параметр
Operation (Работа)  для данного  маркера  "области"  в  состояние
Enable.

     Кто расплачивается за циклы

      Метафора  пунктов сбора  подорожной пошлины помогает объяс-
нить почему не изменяется текущая "область" после перехода марке-
ра "области"  с последующим  возвратом назад  (происходящим в ре-
зультате выполнения  оператора goto или цикла) по адресу, предше-
ствующему этому маркеру, хотя, c лексической точки зрения, вы на-
ходитесь вне  зоны действия маркера, вы еще не встретили на своем
пути какой-либо  маркер. И  поэтому каждый  генерируемый  импульс
таймера продолжает  ассоциироваться с последним пройденным марке-
ром.


/* Copyright (c) 1990, Borland International */
#include 
#include    /* contains prototype for delay() */

main()
{
    printf("Entering main\n");
    lost_in_town();
    delay(1000);
    printf("Leaving main\n\n");
    delay(1000);
}

lost_in_town()
{
    int i;
    printf("Looking for highway...\n");
    delay(100);
    for (i=0; i<10; i++)
    {
                         printf("Ask for directions\n");
                         printf("Wrong turn\n\n");
                         delay(1000);
    }
    printf("On the road again\n");
}

{ Copyright (c) 1990, Borland International }
Uses Crt;

Procedure Lost_in_town;
Var
  I : integer;
Begin
  writeln( 'Looking for highway...' );
  delay(100);
  for I := 0 to 9 do
  Begin
    Writeln( 'Ask for directions' );
    Writeln( 'Wrong turn' );
    Writeln;
    delay(1000);
  End;
  Writeln( 'on the road again' );
End;

Begin
  Writeln( 'Entering Main' );
  Lost_in_town;
  Delay(1000);
  writeln( 'Leaving main' );
  writeln;
  delay(1000);
End.

     В программе plost мы усложнили подпрограмму lost_in_town ис-
пользованием составного  оператора внутри цикла. Предположим, что
были установлены  3 маркера:  один для  функции  main,  один  для
lost_in_town и еще один для оператора, печатающего Wrong turn.

     После того, как вы попадаете в lost_in_town, начинают проис-
ходить  странные  вещи.  Когда  вы  входите  в  эту  подпрограмму
lost_in_town становиться  текущей "областью". Время, приходящиеся
на печать фразы "looking for highway" ассоциируется с этим марке-
ром.

     При входе в цикл время продолжает ассоциироваться с маркером
подпрограммы и  в первый  раз,когда вы выполняете оператор печати
строки "Ask  for direction", время все еще относится к этому мар-
керу. Тем  не менее  стоит вам  пройти  маркер  оператора  печати
"Wrong turn",  все оставшееся время работы подпрограммы будет ас-
социироваться с этим маркером.

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

     Например, если  вы  установили  три  маркера  для  программы
plost, так  как мы  описали (по  одному маркеру для функций main,
lost_in_town и оператора печати строки "Wrong Turn") , то прибли-
зительно 84% общего времени выполнения программы будет ассоцииро-
вано с  печатью строки "Wrong turn", и только 1% времени выполне-
ния будет  связан с  lost_in_town. Это происходит потому, что де-
вять из  десяти  выполнений  оператора  печати  строки  "Ask  for
direction", а также все последующие операторы delay ассоциируются
с маркером, помечающим оператор печати строки "Wrong Turn".

     Если вы  уберете маркер  "области", которым помечен оператор
печати строки  "Wrong Turn", то 84% остающегося времени будет за-
трачено на выполнение подпрограммы lost_in_town.

     Рассмотрим следующую программу:


main
{
   while(!kbhit() )
   {
      func1();
      statement1;
      statement2;
      func2();
    }
}

func1()
{
}

func2()
{
}

     Предположим, что  маркеры области в рассматриваемой програм-
ме, установлены  для всех  подпрограмм. Каждое  из  имен  функции
main, func1 и func2 в описаниях этих функций соответствует началу
"области" и  таймер генерирует  импульсы с частотой 100 раз в се-
кунду.(Предположим,что 1/100  секунды это достаточно большой про-
межуток времени  для того, чтобы вы могли наблюдать за происходя-
щим процессом.)

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

     Область действия  маркеров "области" определяется скорее ди-
намически, чем  лексически. Это означает, что main является теку-
щей "областью"  до тех  пор, пока процесс выполнения программы не
дойдет до  вызова функции func1. Как только вы попадаете в func1,
вы тем самым входите в новую "область", в которой вы остаетесь до
тех пор,  пока не натолкнетесь на вызов какой-нибудь другой функ-
ции или  на оператор возврата из функции func1. Вышесказанное оз-
начает,что система Turbo Profiler помещает имена вызывающих моду-
лей ( в данном случае функции main) в стек.

     Когда вы возвращаетесь из функции func1, вы проходите маркер
возврата, установленный  профилировщиком в  момент входа в func1.
Функция main  снова становится  текущей "областью".  Каждый  такт
таймера, во время которого выполняется statement1 или statement2,
считается относящимся к "области", задаваемой функцией main.

     На этом этапе происходят две вещи:

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

2.   На каждом такте таймера, профилировщик вызывает другую внут-
     реннюю подпрограмму,  которая проверяет, какая из "областей"
     является текущей,  затем  увеличивает  значение  переменной,
     хранящей временные  данные для рассматриваемой "области", на
     соответствующую величину.

     Когда  выполнение  программы  заканчивается,  система  Turbo
Profiler, на  основании значений переменных, в которых накаплива-
лось число  тактов таймера,  вычисляет время работы каждой "обла-
сти".

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

     Что же вам делать в случае нескольких операторов return? От-
вет на  этот вопрос  заключается в использовании единственной на-
стоящей точки возврата только в самом конце тела функции.

     Важное замечание о точках возврата:

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

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

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

     Сбор статистических данных о вызовах подпрограмм.

     Активной в  данный момент  времени  подпрограммой  считается
подпрограмма, находящаяся  на вершине стека вызовов, формируемого
профилировщиком. В  режиме активного анализа (т.е. когда профили-
ровщик собирает информацию о путях вызова, а также другие данные,
не связанные  с подсчетом времени) система Turbo Profiler создает
свой собственный  стек вызовов  подпрограмм.  Этот  стек  устроен
аналогично стеку  вызовов любой  другой программы,  выполняющейся
под управлением  DOS. Стек,  создаваемый профилировщиком,    под-
держивается совершенно  независимо от стека пользовательской про-
граммы и используется исключительно для  сохранения информации  о
вызовах подпрограмм,  в которых еще не был выполнен оператор воз-
врата.

     Для того, чтобы поддерживать стек вызовов  подпрограмм в си-
стеме Turbo Profiler имеется два типа маркеров "области":

*    маркер "области"  типа "точка  входа в подпрограмму" (маркер
     подпрограммы);

*    обычный маркер "области" (маркер типа метка)

     Когда профилировщик  наталкивается на  маркер "области" типа
"точка входа в подпрограмму", он  помещает активную на данный мо-
мент подпрограмму  на вершину  стека активных  подпрограмм. Затем
встреченный маркер становится маркером активной подпрограммы.

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

     Когда, в  процессе выполнения,  в теле активной подпрограммы
встречается оператор  возврата, маркер  "области", относящийся  к
данной подпрограмме, переходит в пассивное состояние.

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

     Таким образом, профилировщик может прослеживать полную пред-
ысторию вызовов для каждой помеченной подпрограммы. Если Вы акти-
вировали параметр  Statistics|  Callers  (Статистика|  Вызывающие
подпрограммы) для  всех помеченных подпрограмм, то каждый раз при
встрече маркера  "области", типа  "точка входа  в  подпрограмму",
профилировщик полностью  сохраняет свой  стек вызовов  в  буфере,
связанном с данной подпрограммой.

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

     Работа профилировщика  со стеком активных подпрограмм, опре-
деляется значениями  двух параметров,  устанавливаемых при помощи
команд его меню:

*    параметр Statistics|  Callers (Статистика|  Вызывающие  под-
     программы) (может  находиться в состоянии Enabled (Включено)
     или Disabled (Выключено);

*    параметр Callers  (Вызывающие подпрограммы) для каждой поме-
     ченной "области" в окне Areas ("Области").

     Более гибкие  возможности для контроля путей вызова подпрог-
рамм Вы получаете при использовании локальных меню в окнах Module
(Модуль) и  Areas ("Области"). Вы можете установить для каждой из
помеченных "областей" свое собственное значение параметра Callers
(Вызывающие подпрограммы).  Как команда  Callers (Вызывающие под-
программы) локального  меню окна  Module (Модуль),  так и команда
Options (Параметры)  локального меню окна Areas ("Области") ведут
к появлению  на экране  блока диалога, в котором Вы можете задать
одно из следующих значений параметра Callers (Вызывающие подпрог-
раммы): All  Callers  (Все  вызывающие  подпрограммы),  Immediate
Caller (Непосредственно  вызывающая подпрограмма) или None (Ника-
кие).

*    All Callers  (Все вызывающие подпрограммы) означает сохране-
     ние полного  стека вызовов  подпрограмм каждый  раз, когда в
     процессе выполнения,  встречается точка  входа в  помеченную
     подпрограмму.

*    Immediate Caller  (Непосредственно вызывающая  подпрограмма)
     означает запоминание  только самого  верхнего элемента стека
     вызовов подпрограмм  при попадании  на точку  входа в данную
     помеченную подпрограмму.

*    None (Никакие)  означает, что при встрече, в процессе выпол-
     нения, маркера  "области" типа  "точка входа в подпрограмму"
     никакая информация из стека вызовов подпрограмм не запомина-
     ется.

     По умолчанию,  при первом  получении профиля программы пара-
метр Callers  (Вызывающие подпрограммы)  для всех маркеров "обла-
сти" типа "точка входа в подпрограмму" имеет значение None (Ника-
кие).

     Установка параметра Statistics| Callers (Статистика| Вызыва-
ющие подпрограммы),  находящегося в  главном  меню,  в  состояние
Enable (Включено) равнозначна установке значения All Callers (Все
вызывающие подпрограммы)  параметра Сallers  (Вызывающие подпрог-
рамм) для  каждого маркера "области", установленного в окне Areas
("Области"). Тем  не менее,  если Вы  вручную установили значения
параметров Callers   (Вызывающие подпрограмм) для каких-либо "об-
ластей" в  окне Areas  ("Области"), то,  в этом случае, установка
параметра Statistics| Callers (Статистика| Вызывающие подпрограм-
мы) в  состояние Enable (Включено), не изменит значение параметра
Callers (Вызывающие подпрограммы) для таких "областей".

     Установка параметра Statistics| Callers (Статистика| Вызыва-
ющие  подпрограммы)  в  некоторой  точке  программы  в  состояние
Disable (Отключено)  указывает профилировщику, чтобы он не сохра-
нял никакой  информации из  стека вызовов,  но это  совершенно не
влияет на  действие параметров  Caller (Вызывающая подпрограмма),
установленных в  окне Areas ("Области") (как и при установке дан-
ного параметра в состояние Enable (Включено)).


     Опрос вместо подсчета.

     На самом  деле, профилировщик  не измеряет время непосредст-
венно, а  очень точно вычисляет его на основании информации о ко-
личестве прерываний таймера.

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

     Примечание: Все вышесказанное имеет место только в слу-
     чае работы в пассивном режиме профилирования.

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

     <время выполнения  "области"> = <общее время выполнения про-
граммы> * <значение счетчика "области"> / <значение счетчика про-
граммы>

     Но, на  самом деле, нельзя утверждать, что значение времени,
вычисленное по  приведенной формуле, в точности совпадает с коли-
чеством времени, затраченным на выполнение данной "области". Если
Ваша программа содержит в себе цикл, в котором, с частотой, крат-
ной частоте  прерывания таймера  происходит обращение к некоторой
подпрограмме (например  к подпрограмме  генерации звука одной то-
нальности), то  большинство моментов времени, в которые будут ге-
нерироваться импульсы  таймера может прийтись на выполнения неко-
торой строки  (или "области").  Описанное явление резонанса может
возникнуть даже в случае, когда рассматриваемая строка не принад-
лежит к числу основных потребителей времени выполнения Вашей про-
граммы. Подобные ситуации редки, но вполне возможны.

     Если Вы подозреваете, что возникла подобная ситуация с нало-
жением  частот,   то  измените   значение  параметра  Statistics|
Profiling Options|  Clock speed value (Статистика| Параметры про-
филирования| Скорость  работы часов) и сравните профиль, получен-
ный после внесения этого изменения, с предыдущим профилем.

     Использование памяти профилировщиком.

     На следующем рисунке показано распределение памяти при полу-
чении профиля программы с помощью системы Turbo Profiler.

     Рис.2.2 Карта памяти системы Turbo Profiler.

     Для запоминания  информации об  "областях" профилировщик ис-
пользует динамически-распределяемую  область памяти  (far  heap).
Если Вы,  в процессе  выполнения программы, добавите новые "обла-
сти", то  эта, динамически-распределяемая  область памяти,  может
распространиться на  ту часть памяти, которая зарезервирована для
программы пользователя,  для того,  чтобы разместить  в ней новые
переменные "областей"  и буферы. Поэтому, если Вы вносите измене-
ния в  разметку "областей" во время выполнения программы, Вам не-
обходимо повторно  запустить программу  с  помощью  Run|  Program
Reset (Выполнение| Повторное выполнение программы). Если Вы этого
не сделаете,  то результаты  профилирования будут  совершенно не-
предсказуемы, Ваш компьютер может просто "зависнуть".


Яндекс цитирования