Оценка производительности программ

    «Наука начинается с тех пор,
    как начинают измерять.
    Точная наука немыслима без меры»

    Д.И.Менделеев

    Измерение производительности программ

    Зачем, что и как измерять?

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

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

    • в 4.1. было введено понятие трудоемкости: зависимость количества массовых операций в программе (например, сравнение, сдвиг, обмен) от размерности входных данных N. При оценке трудоемкости обычно используется грубая оценка степени роста полученной функции трудоемкости T(N), что справедливо при достаточно больших N. Но с какого именно значения N это соблюдается, в общем случае сказать невозможно;

    • для получения трудоемкости в программу включаются переменные-счетчики, изменяющие свое значение в той точке программы, где присутствует соответствующая операция;

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

    • измерения можно проводить разными способами. Можно, например, брать файлы различных размеров и измерять итоговые характеристики, а можно получать значения измеряемых величин на одном большом файле через m равных значений размерности.

    В качестве примера посмотрим, как выглядит процесс сбора и обработки данных на программе, читающей пословно файл и включающей слова в динамический массив указателей с сохранением порядка. Сразу же заметим, что трудоемкость по операциям сравнения имеет вид T=N2/4, в лучшем случае, при вставке прямо упорядоченного текста, T=N2/2, в лучшем случае T=N – если входной текст обратно упорядочен. В качестве входного файла используется текст литературного произведения, имеющий достаточно «случайный» вид.

    C
    //------------------------------------------------------62-06.cpp
    
    //------- Создание упорядоченного ДМУ из строк файла
    
     #include <stdio.h>
    
     #include <string.h>
    
     #include <malloc.h>
    
     #include <time.h>                  // Для функции clock
    
     #include <windows.h>               // Для функции CharToOemA
    
     #define SIZE0 100                 
    
     char **loadfile(FILE *fd){
    
     int cnt=0;                         // Счетчик сравнений
    
     long T0=clock();                   // Начальное значение времени
    
     char str[1000];                    // в миллисекундах
    
     int i,j,n=0,sz=SIZE0;              // Кол-во строк и размерность ДМУ
    
     char **pp = new char*[sz];         // Создать ДМУ
    
     while (fscanf(fd,"%s",str)==1){
    
          for (i=0;i<n;i++){
    
                cnt++;                  // Увеличение счетчика сравнений
    
                if (strcmp(str,pp[i])<0) break;
    
                }
    
                for (j=n-1;j>=i;j--)
    
                      pp[j+1]=pp[j];
    
                pp[i]=strdup(str);      // Копия строки в ДМ
    
                n++;                    // Вывод статистики на каждые 5000 слов
    
                if (n%5000==0) printf("%d\t%d\t%ld\n",n,cnt,clock()-T0);
    
                if (n+1==sz){           // Будет переполнение -
    
                      sz*=2;            // удвоить размерность
    
                      pp=(char**)realloc(pp,sizeof(char*)*sz);
    
                      }}
    
     pp[n] = NULL;                      // Ограничитель массива указателей
    
     printf("%d\t%d\t%ld\n",n,cnt,clock()-T0);
    
     return pp; }
    
     
    
     void main(){
    
     int i;
    
     FILE *fd=fopen("text.txt","r");
    
     char cc[80];
    
     char **pp=loadfile(fd);
    
     puts("---------------------------------------------");
    
     gets(cc);
    
     for (i=0; pp[i]!=NULL; i++){
    
          CharToOemA(pp[i],cc);         // Преобразование кодировки для вывода
    
          printf("%s\n",cc);
    
          }
    
     for (i=0; pp[i]!=NULL; i++) delete []pp[i];
    
     delete []pp; fclose(fd);}

    Проведение измерений и сохранение результатов

    Значения счетчика операций сравнения cnt и «грязного» времени работы программы clock()-T0 выводятся в окно консольного приложения, в принципе, их легко можно было бы перенаправить в тестовый файл. При форматном выводе в качестве разделителей используются знаки табуляции (\t), чтобы облегчить последующий импорт данных в Excel. Для переноса данных из окна консольного приложения необходимо правой кнопкой мыши в левом верхнем углу окна вызвать контекстное меню, в котором последовательно выполнить команды «изменить – пометить(выделить все)» и «изменить-копировать», затем скопированные в буфер обмена данные вставить в окно текстового файла.

    Для включения полученных данных в Excel необходимо выполнить последовательность выбором меню и действий мастера:

    • «Данные»;

    • «Импорт внешних данных»;

    • «Импортировать внешние данные»;

    • Выбор текстового файла в диалоговом окне;

    • «Формат данных – с разделителями» в мастере импорта;

    • «Символы-разделители – табуляция и пробел» в мастере импорта;

    • «Формат данных столбца - общий» в мастере импорта;

    • Выбрать ячейку диапазона, куда будут импортированы данные.

    Оценка полученных зависимостей

    Для грубой оценки полученной зависимости необходимо разместить полученные данные в электронной таблице Ozenka.xls. Оценка производится следующим образом:

    • В первый столбец таблицы вносится размерность входных данных (число слов);

    • Во второй столбец вносится экспериментальное значение (в первую таблицу – время работы, во вторую – количество операций сравнения).

    • Идея оценки очень проста. Проверяется, насколько экспериментальные данные F1(N) соответствуют виду степенной функции F(N)=KNP. Показатель степени P подбирается опытным путем, исходя из предварительного анализа трудоемкости программы: в нашем случае простая упорядоченная вставка должны давать квадратичную зависимость. Коэффициент пропорциональности K подбирается таким образом, чтобы значения экспериментальной и математической зависимости совпали в некоторой точке (N0) – можно взять одну из первых точек N0=5000, тогда F1(N0)=F(N0)= KN0P, откуда следует, что K= F1(N0)/( N0P).

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

    N

    cmp(эскп)

    cmp*(матем)

    отклонение

    коэф по N1

    Степень

    5000

    6217209

    6217209

    0%

    0,248688

    2

    10000

    24945611

    24868836

    0%

    15000

    57045716

    55954881

    2%

    20000

    100133518

    99475344

    1%

    25000

    156160970

    155430225

    0%

    30000

    225396760

    223819524

    1%

    35000

    304565494

    304643241

    0%

    40000

    396710798

    397901376

    0%

    45000

    501222764

    503593929

    0%

    50000

    621272463

    621720900

    0%

    55000

    754949505

    752282289

    0%

    60000

    897124300

    895278096

    0%

    65000

    1049094335

    1050708321

    0%

    70000

    1220253187

    1218572964

    0%

    75000

    1401604268

    1398872025

    0%

    80000

    1597098280

    1591605504

    0%

    85000

    1803745315

    1796773401

    0%

    90000

    2025588151

    2014375716

    1%

    91479

    2088624766

    2081125522

    0%

    Какие же выводы можно сделать из полученных результатов:

    • Количество операций сравнения практически точно имеет зависимость T(N)=N2/4 (K=0.25 N=2), что соответствует нашим предварительным теоретическим оценкам;

    • «Грязное» время работы программы подчиняется степенной зависимости с показателем P=2.4, т.е. примерно N2√N, т.е. хуже, чем квадратичная. Отклонение сначала достигает 30% в лучшую сторону, а затем меняется на 20% в худшую сторону, постепенно снижаясь с увеличением длины файла. Очевидно, что при работе программы имеют место процессы (ввод-вывод, работа с динамической памятью), которые имеют характеристики худшие, чем используемый в программе алгоритм.