Когда-то, давным-давно (по меркам мира информационных технологий), мне на глаза попала фраза примерно такого содержания: "Люди должны думать, а компьютеры работать". Прошло время, и первая часть фразы безвозвратно забыта. Мощь современных компьютеров скрывает недоделки людей. Настало время освободить здоровые ноги от деспотизма и дури больной головы. В благодарность ноги заведут вас дальше, за то же время. И это не сказка, а самая настоящая быль.
Скорость выполнения программ можно поднять двумя путями. Первый - использовать систему с новым, более мощным процессором. Кто думает, что это правильное решение, пусть еще раз внимательно прочтет первый абзац. Второй путь - научиться писать качественные программы.
Каждая программа представляет собой набор данных и операций, данные хранятся в переменных, а операции манипулируют переменными. В свою очередь, тип переменной определяет перечень допустимых операций и скорость их выполнения.
Поясню это на примере. Вы разрабатываете программу, автоматизирующую учет материалов на складе. Предположим, учету подлежит вес материалов. Зная точность взвешивания материалов, можно определить тип переменной, учитывающей вес, например, вещественное одинарной точности. Если материалы учитываются в штуках, то имеет смысл использовать целые переменные вместо вещественных. Чем короче тип переменной, тем быстрее она может быть обработана.
Продолжим тему расстояний. Расстояние между регистрами микропроцессора значительно меньше расстояния между микропроцессором и памятью. Кроме того, данные внутри микропроцессора передаются на значительно большей скорости, чем при взаимодействии с внешними устройствами (читай памятью). Следовательно, скорость выполнения программы можно значительно увеличить, если хранить как можно больше данных в регистрах микропроцессора.
Пойдем дальше. В процессе хранения на складе материалы могут изменяться. Для учета этих изменений приходится вводить поправки на усушку, утруску, распыл и прочие неприятности. Пусть на складе хранится 1000 партий одного и того же материала. Информация об объеме каждой партии хранится в массиве x(i). Исходя из условий задачи, необходимо умножить каждый элемент массива на три коэффициента (k1, k2 и k3). Это действие может быть запрограммировано следующим образом:
For i=1 To 1000
x(i)=k1*k2*k3*x(i)
Next
Программа написана правильно. Она заработает с первого раза. Но давайте зададим себе вопрос: "Насколько эта программа эффективна?" Не поленимся и сосчитаем количество операций умножения, которые необходимо выполнить для получения конечного результата. Для пересчета одного элемента массива требуется выполнить 3 операции умножения. Значит, для пересчета всех элементов массива операцию умножения необходимо выполнить 3000 раз. Это довольно значительный объем работы. Заставить бы человека, написавшего эту программу, выполнить ее вручную - это побудило бы его искать рациональное решение.
Попытаемся сократить объем работы. Присмотревшись внимательно, можно увидеть, что коэффициенты k1,k2 и k3 не изменяют своего значения в течение цикла, т.е. являются постоянными величинами. Это позволяет вынести их вычисление за пределы цикла. В результате получим:
k4=k1*k2*k3
For i=1 To 1000
x(i)=k4*x(i)
Next
Теперь для вычисления одного элемента массива требуется выполнить только одну операцию умножения. Значит, чтобы получить те же результаты расчета, нам потребуется выполнить 1002 операции умножения (две операции умножения необходимы для вычисления вспомогательного коэффициента k4). Итого, выполнение расчетов с использованием второго варианта программы позволяет сократить 1998 операций умножения.
В любом учебнике по языкам программирования Вы можете прочитать о том, что операции умножения и деления выполняются раньше, чем операции сложения и вычитания. Но, к сожалению, в учебниках Вы не прочтете о том, что операция сложения выполняется быстрее, чем операция умножения, а умножение быстрее деления. Чтобы не быть голословным, приведу таблицу, в которой представлено потребное количество тактов микропроцессора Intel 80386 для выполнения операций сложения, умножения и деления. Эти знания могут пригодиться для написания быстро работающих программ. Например, конструкции 2*a и a/2 можно заменить аналогичными по получаемому результату, но более быстрыми a+a и a*0.5.
Потребное количество тактов микропроцессора Intel 80386 для выполнения операций: сложение, умножение, деление.
Название операции | Требуемое количество тактов микропроцессора |
Сложение (+) | от 2 до 7 |
Умножение (*) | от 9 до 41 |
Деление (/) | от14 до 41 |
Об использовании эффективно работающих алгоритмов поговорим в другой раз. Эта тема обширна и заслуживает отдельного разговора.
Подведем итоги. Для написания быстро работающих программ необходимо помнить о том, что:
- на скорость выполнения операций влияет тип используемых переменных. Следует стремиться использовать целые числа вместо вещественных, и вещественные одинарной точности вместо вещественных двойной точности;
- необходимо хранить как можно больше данных в регистрах микропроцессора;
- вынесение за пределы цикла статичных операций снижает общее количество операций, необходимых для достижения результата;
- использование операции сложения вместо операции умножения, а умножения вместо деления, в отдельных случаях позволяет сократить время, затрачиваемое на получение результата.
В заключение не могу умолчать о том, что оптимизирующие компиляторы делают за вас кое-что из вышеперечисленного. Но, как говорится, на компилятор надейся, а сам не плошай.
Сергей ОСОКО
Горячие темы