Литмир - Электронная Библиотека
A
A

 

Программирование. Принципы и практика использования C++ Исправленное издание - _001.png
 Варианты, продемонстрировавшие худшие показатели производительности, обычно обусловлены неудачным выбором алгоритма и могут быть обнаружены на этапе отладки. Одна из целей тестирования программ на крупных наборах данных заключается в выявлении неэффективных алгоритмов. В качестве примера предположим, что приложение должно суммировать элементы, стоящие в строках матрицы (используя класс
Matrix
из главы 26).

Некто предложил использовать подходящую функцию.

double row_sum(Matrix<double,2> m, int n); // суммирует элементы в m[n]

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

v[n]
— сумма элементов в первых
n
строках.

double row_accum(Matrix<double,2> m, int n) // сумма элементов

                                            // в m[0:n)

{

  double s = 0;

  for (int i=0; i<n; ++i) s+=row_sum(m,i);

  return s;

}

 // вычисляет накопленные суммы по строкам матрицы m:

 vector<double> v;

 for (int i = 0; i<m.dim1(); ++i)

 v.push_back(row_accum(m,i+1));

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

m
. Почему? Дело в том, что мы просуммировали все элементы в первой строке, затем добавили элементы из второй строки (снова перебрав все элементы из первой строки), потом все элементы из третьей строки (перебрав все элементы из первой и второй строк) и т.д. Если вы считаете этот пример неудачным, посмотрите, что произойдет, если функция
row_sum()
обратится к базе данных за данными. Чтение данных с диска во много тысяч раз медленнее, чем чтение из оперативной памяти.

Вы можете возразить: “Никто никогда не сможет сделать нечто настолько глупое!” Извините, но мы видели вещи и похуже, и, как правило, плохой (с точки зрения производительности) алгоритм очень нелегко выявить, если он глубоко скрыт в коде приложения. Заметили ли вы проблемы с производительностью, когда в первый раз увидели этот код? Проблему бывает трудно выявить, если не искать ее целенаправленно. Рассмотрим простой реальный пример, найденный на одном сервере.

for (int i=0; i<strlen(s); ++i) { /* что-то делаем с s[i] */ }

Часто переменная

s
представляет собой строку размером примерно 20 K.

Не все проблемы, связанные с производительностью программы, объясняются плохим алгоритмом. Фактически (как мы указывали в разделе 26.3.3) большую часть кода, который мы пишем, нельзя квалифицировать как плохой алгоритм.

Такие “неалгоритмические” проблемы обычно связаны с неправильным проектированием. Перечислим некоторые из них.

• Повторяющееся перевычисление информации (как, например, в приведенном выше примере).

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

• Повторяющиеся обращения к диску (или к сети).

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

26.6.1. Измерение времени

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

 

Программирование. Принципы и практика использования C++ Исправленное издание - _003.png
 Если вам необходимо измерять более мелкие интервалы времени или вы не хотите сидеть с секундомером, вам следует научиться использовать возможности компьютера, так как он знает, как измерить время. Например, в системе Unix достаточно просто поставить перед командой слово
time
, чтобы система вывела продолжительность ее выполнения. Можете также использовать команду
time
, чтобы выяснить, сколько времени заняла компиляция исходного файла
x.cpp
. Обычно компиляция выполняется по команде

g++ x.cpp

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

time
.

time g++ x.cpp

Система откомпилирует файл

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

 

Программирование. Принципы и практика использования C++ Исправленное издание - _001.png
 А что, если вы хотите измерить интервал времени, длящийся всего несколько миллисекунд? Что, если вы хотите выполнить свои собственные, более подробные измерения, связанные с работой части вашей программы? Продемонстрируем использование функции
clock()
из стандартной библиотеки, позволяющей измерить продолжительность выполнения функции
do_something()
.

#include <ctime>

#include <iostream>

381
{"b":"847443","o":1}