Почему условные точки останова так сильно замедляют мою программу?

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


person Community    schedule 06.08.2009    source источник
comment
Это никогда не было проблемой. Я не помню, когда это началось, но я, кажется, припоминаю, что Delphi 2009 и более ранние версии не замедлялись каким-либо заметным образом для условных точек останова. Я считаю, что с тех пор что-то испортилось в одном из обновлений. Я открыл дело в Embarcadero, чтобы посмотреть, можно ли это решить. Я уверен, что это какая-то ошибка или супер-неэффективность.   -  person lkessler    schedule 25.10.2018


Ответы (6)


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

Если вы хотите немного повозиться со своим кодом, есть способ сделать условные точки останова без всех этих накладных расходов.

if <condition here> then
  asm int 3 end;

Это простая инструкция по сборке, которая вручную отправляет ОС уведомление о точке останова. Теперь вы можете оценить свое состояние внутри программы, не переключая контексты. Просто не забудьте вынуть его снова, когда закончите с ним. Если int 3 сработает внутри программы, которая не подключена к отладчику, это вызовет исключение.

person Mason Wheeler    schedule 06.08.2009
comment
Мейсон: хорошее резюме; обратите внимание, что некоторые из этих конструкций легко оставить в коде. Поэтому я предлагаю сделать это с некоторыми замечаниями (я использую что-то с @@@) или макросами, которые гарантированно не попадут в реальный продукт. - person Adriaan; 06.08.2009
comment
Это тег Delphi, в котором нет макросов препроцессора или примечаний @@@. Один простой способ убедиться, что вы ничего не оставили, - это выполнить grep вашей кодовой базы для int 3, прежде чем создавать релизную версию. - person Mason Wheeler; 06.08.2009
comment
вместо добавления языка ассемблера я обычно добавляю оператор volatile int e = 9;. Это дает мне оператор для установки точки останова. Также добавьте #pragma (если Delphi позволяет), чтобы предупредить вас о наличии кода отладки. - person Robert; 06.08.2009
comment
Вы могли бы сделать что-то подобное. Эквивалентом вашей #pragma будет что-то вроде: {$MESSAGE HINT Здесь код отладки.} - person Mason Wheeler; 06.08.2009
comment
Это плохой ответ! Вы не хотите рисковать, оставляя ошибочный int 3 в программе. Посмотрите на ответ Пакса — безобидная строка, на которой вы установили точку останова. Это не вызовет проблем, если окажется в производственном коде. - person Loren Pechtel; 06.08.2009
comment
Вот почему я предложил 2 разных способа предотвратить это. Вот третий: поместите строку в блок {$IFDEF DEBUG}. - person Mason Wheeler; 06.08.2009

Это замедляет его, потому что каждый раз, когда вы достигаете этой точки, он должен проверять ваше состояние.

Что я обычно делаю, так это временно создаю другую переменную, подобную этой (в C, но это должно быть выполнимо в Delphi).

int xyzzynum = 600;
while (true) {
    doSomething();
    if (--xyzzynum == 0)
        xyzzynum = xyzzynum;
}

затем я ставлю безусловную точку останова на строку "xyzzynum = xyzzynum;".

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

Вы можете сделать условие настолько сложным, насколько хотите.

person paxdiablo    schedule 06.08.2009
comment
+1 В этом случае оператор, вероятно, мог бы использовать что-то простое, например, если я = 600, то напишите; и поставьте точку останова в предложении записи. - person Lieven Keersmaekers; 06.08.2009

В дополнение к ответу Мейсона вы можете компилировать сборку int 3 только в том случае, если программа построена с определенным условием отладки:

{$ifdef debug}
{$message warn 'debug breakpoint present in code'}
if <condition here> then
  asm int 3 end;
{$endif}

Итак, когда вы отлаживаете в ide, у вас есть условие отладки в параметрах проекта. Когда вы создаете конечный продукт для своих клиентов (со своим скриптом сборки?), вы не включаете этот символ, поэтому он не будет скомпилирован.

Я также включил директиву компилятора $message, поэтому при компиляции вы увидите предупреждение о том, что код все еще существует. Если вы сделаете это везде, где используете int 3, у вас будет хороший список мест, по которым вы можете дважды щелкнуть, чтобы перейти прямо к оскорбительному коду.

N@

person Nat    schedule 07.08.2009

Объяснения Мэйсона довольно хороши.
Его код можно сделать немного более безопасным, протестировав его под отладчиком:

if (DebugHook <> 0) and <your specific condition here> then
  asm int 3 end;

Это ничего не сделает, когда приложение работает нормально, и остановится, если оно работает под отладчиком (независимо от того, запущено ли оно из IDE или подключено к отладчику).
А с логическим ярлыком <your specific condition here> даже не будет оцениваться, если вы' не под отладчиком.

person Francesca    schedule 06.08.2009
comment
И это также даст [Предупреждение Pascal] Dist.dpr(72): W1002 Символ «DebugHook» зависит от платформы, что упрощает их поиск перед регистрацией. - person Gerry Coll; 07.08.2009

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

person Daniel A. White    schedule 06.08.2009

Обычно точки останова по условию работают, вставляя в код соответствующую инструкцию останова, а затем проверяя указанные вами условия. Он будет проверять на каждой итерации, и вполне возможно, что способ реализации проверки является причиной задержки, поскольку маловероятно, что отладчик скомпилирует и вставит полный код проверки и точки останова в существующий код.

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

person Timo Geusch    schedule 06.08.2009
comment
Вероятно, это не сама условная проверка, а переключение контекста и накладные расходы на остановку и перезапуск процесса. Вы не заметите этого с обычной точкой останова, так как вы все равно попадаете в отладчик, но в условном, где он продолжает работать, это становится очень очевидным очень быстро. - person Mason Wheeler; 06.08.2009