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

Чтобы программа не зависала, внутри цикла желательно ставить вызов метода ProcessMessages объекта Appl i cation:

Appli cati on.ProcessMessages():

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

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

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

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

1. Установить первоначальное значение счетчика

2. Взять очередное значение.

3. Выполнить тело цикла.

4. Увеличить счетчик.

5. Проверить, не вышло ли значение за допустимое

6. Перейти на шаг 2.

Если этот цикл будет выполняться 1 ООО ООО раз, то во время его выполнения будет происходить 1 ООО ООО переходов с шага 5 на шаг 2 Это очень много, поэтому если тело цикла небольшое, то можно ускорить выполнение кода в два раза следующим образом:

1.

Установить первоначальное значение счетчика.

2.

Взять очередное значение.

3.

Выполнить тело цикла.

4.

Увеличить счетчик.

5.

Проверить, не вышло ли значение за допустимое

6.

Взять очередное значение.

7.

Выполнить тело цикла.

8.

Увеличить счетчик.

9.

Проверить, не вышло ли значение за допустимое

10.

Перейти на шаг 2.

В данном случае за один проход цикла дважды выполняется его тело и только потом осуществляется переход на начало. Таким образом, переходов будет в два раза меньше. Если тело цикла выполнять трижды, то количество переходов будет в 3 раза меньше. Так как мы знаем, что цикл будет выполняться 1 ООО ООО раз, можно быть уверенными, что пункт 5 не нужен, потому что число выполнений четное и в середине тела мы никогда не превысим допустимое значение. В этом случае мы избавляемся от 500 ООО проверок и 500 000 переходов, что в общей сложности составляет 1 000 000 операций.

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

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

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

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

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

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

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

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

2.5. Слабые места || Оглавление || 2.7. Процедуры и функции


Delphi в шутку и всерьез: что умеют хакеры



Новости за месяц

  • Декабрь
    2021
  • Пн
  • Вт
  • Ср
  • Чт
  • Пт
  • Сб
  • Вс
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31