Программирование. Принципы и практика использования C++ Исправленное издание, стр. 381
MatrixНекто предложил использовать подходящую функцию.
double row_sum(Matrix<double,2> m, int n); // суммирует элементы в m[n]Потом этот некто стал использовать эту функцию для того, чтобы сгенерировать вектор сумм, где
v[n]ndouble 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));Представьте себе, что этот код является частью модульного теста или выполняется как часть системного теста. В любом случае вы заметите нечто странное, если матрица станет действительно большой: по существу, время, необходимое для выполнения программы, квадратично зависит от размера матрицы
mrow_sum()Вы можете возразить: “Никто никогда не сможет сделать нечто настолько глупое!” Извините, но мы видели вещи и похуже, и, как правило, плохой (с точки зрения производительности) алгоритм очень нелегко выявить, если он глубоко скрыт в коде приложения. Заметили ли вы проблемы с производительностью, когда в первый раз увидели этот код? Проблему бывает трудно выявить, если не искать ее целенаправленно. Рассмотрим простой реальный пример, найденный на одном сервере.
for (int i=0; i<strlen(s); ++i) { /* что-то делаем с s[i] */ }Часто переменная
sНе все проблемы, связанные с производительностью программы, объясняются плохим алгоритмом. Фактически (как мы указывали в разделе 26.3.3) большую часть кода, который мы пишем, нельзя квалифицировать как плохой алгоритм.
Такие “неалгоритмические” проблемы обычно связаны с неправильным проектированием. Перечислим некоторые из них.
• Повторяющееся перевычисление информации (как, например, в приведенном выше примере).
• Повторяющаяся проверка одного и того же факта (например, проверка того, что индекс не выходит за пределы допустимого диапазона при каждом его использовании в теле цикла, или повторяющаяся проверка аргумента, который передается от одной функции другой без каких-либо изменений).
• Повторяющиеся обращения к диску (или к сети).
Обратите внимание на слово “повторяющиеся”. Очевидно, что мы имеем в виду “напрасно повторяющееся”, поскольку на производительность оказывают влияние лишь те действия, которые выполняются много раз. Мы являемся горячими сторонниками строгой проверки аргументов функций и переменных циклов, но если мы миллионы раз проверяем одну и ту же переменную, то такие излишние проверки могут нанести ущерб производительности программы. Если в результате измерений выяснится, что производительность упала, мы должны изыскать возможность удалить повторяющиеся действия. Не делайте этого, пока не убедитесь, что производительность программы действительно стала неприемлемо низкой. Дело в том, что преждевременная оптимизация часто является источником многих ошибок и занимает много времени.
26.6.1. Измерение времени
Как понять, достаточно ли быстро работает фрагмент кода? Как узнать, насколько быстро работает данная операция? Во многих ситуациях, связанных с измерением времени, можете просто посмотреть на часы (секундомер, стенные или наручные часы). Это не научно и не точно, но, если не произойдет чего-то непредвиденного, вы можете прийти к выводу, что программа работает достаточно быстро. Тем не менее этот подход неприемлем для тех, кого беспокоят вопросы производительности программ.
timetimex.cppg++ x.cppДля того чтобы измерить продолжительность компиляции, поставьте перед ней слово
timetime g++ x.cppСистема откомпилирует файл
x.cpp
clock()do_something()#include <ctime>#include <iostream>