Как вы измеряете время выполнения функции? - PullRequest
4 голосов
/ 16 сентября 2008

Как вы можете измерить количество времени, которое займет выполнение функции?

Это относительно короткая функция, и время выполнения, вероятно, будет в миллисекундном диапазоне.

Этот конкретный вопрос относится к встроенной системе, запрограммированной на C или C ++.

Ответы [ 12 ]

10 голосов
/ 16 сентября 2008

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

Редактировать. Одним из преимуществ является то, что вы можете сделать это в вашем реальном приложении, и вам не нужен специальный тестовый код. Подобные внешние выводы отладки являются (должны быть!) Стандартными для каждой встроенной системы.

9 голосов
/ 16 сентября 2008

Есть три возможных решения:

Аппаратное решение :

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


    *io_pin = 1;
    myfunc();
    *io_pin = 0;

Книжный червь :

Если функция довольно мала, и вы можете управлять разобранным кодом, вы можете взломать книгу данных архитектуры процессора и сосчитать циклы, которые потребует процессор для выполнения каждой инструкции. Это даст вам необходимое количество циклов.
Время = # циклов * Тактовая частота процессора / тактовые частоты в соответствии с инструкциями

Это проще сделать для небольших функций или кода, написанного на ассемблере (например, для микроконтроллера PIC)

Решение счетчика меток времени :

Некоторые процессоры имеют счетчик меток времени, который быстро увеличивается (каждые несколько тактов процессора). Просто прочитайте метку времени до и после функции. Это даст вам истекшее время, но помните, что вам, возможно, придется иметь дело с контрролловером.

4 голосов
/ 16 сентября 2008

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

time [funtion_name]

если вы запускаете только функцию в main () (при условии C ++), оставшееся время приложения должно быть незначительным.

4 голосов
/ 16 сентября 2008

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

так:

// begin timing
for (int i = 0; i < 10000; i++) {
    invokeFunction();
}
// end time
// divide by 10000 to get actual time.
3 голосов
/ 01 октября 2008

Я повторяю вызов функции много раз (миллионы), но также применяю следующий метод для дисконтирования накладных расходов цикла:

start = getTicks();

repeat n times {
    myFunction();
    myFunction();
}

lap = getTicks();

repeat n times {
    myFunction();
}

finish = getTicks();

// overhead + function + function
elapsed1 = lap - start;

// overhead + function
elapsed2 = finish - lap;

// overhead + function + function - overhead - function = function
ntimes = elapsed1 - elapsed2;

once = ntimes / n; // Average time it took for one function call, sans loop overhead

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

2 голосов
/ 01 октября 2008

Я всегда реализую подпрограмму, управляемую прерыванием. Затем обновляется счетчик, который считает количество миллисекунд с момента запуска. Затем к этому счетчику осуществляется доступ с помощью функции GetTickCount ().

Пример:

#define TICK_INTERVAL 1    // milliseconds between ticker interrupts
static unsigned long tickCounter;

interrupt ticker (void)  
{
    tickCounter += TICK_INTERVAL;
    ...
}

unsigned in GetTickCount(void)
{
    return tickCounter;
}

В вашем коде вы бы указали следующий код:

int function(void)
{
    unsigned long time = GetTickCount();

    do something ...

    printf("Time is %ld", GetTickCount() - ticks);
}
2 голосов
/ 16 сентября 2008

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

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

#define SEC_TO_NSEC(s) ((s) * 1000 * 1000 * 1000)

int work_function(int c) {
    // do some work here
    int i, j;
    int foo = 0;
    for (i = 0; i < 1000; i++) {
        for (j = 0; j < 1000; j++) {
            for ^= i + j;
        }
    }
}

int main(int argc, char *argv[]) {
    struct timespec pre;
    struct timespec post;
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &pre);
    work_function(0);
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &post);

    printf("time %d\n",
        (SEC_TO_NSEC(post.tv_sec) + post.tv_nsec) -
        (SEC_TO_NSEC(pre.tv_sec) + pre.tv_nsec));
    return 0;
}

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

gcc -o test test.c -lrt

Вы также можете прочитать справочную страницу на clock_gettime. Есть некоторые проблемы с запуском этого кода в системе на основе SMP, которые могут сделать тестирование недействительным. Вы можете использовать что-то вроде sched_setaffinity() или командную строку cpuset, чтобы заставить код только на одном ядре.

Если вы хотите измерить пользовательское и системное время, то вы можете использовать times(NULL), который возвращает что-то вроде jiffies. Или вы можете изменить параметр для clock_gettime() с CLOCK_THREAD_CPUTIME_ID на CLOCK_MONOTONIC ... но будьте осторожны с CLOCK_MONOTONIC.

Для других платформ вы самостоятельно.

Drew

2 голосов
/ 16 сентября 2008

Windows XP / NT Embedded или Windows CE / Mobile

Вы используете QueryPerformanceCounter (), чтобы получить значение ОЧЕНЬ БЫСТРОГО счетчика до и после вашей функции. Затем вы вычитаете эти 64-битные значения и получаете дельта-тики. С помощью QueryPerformanceCounterFrequency () вы можете преобразовать «дельта-тики» в фактическую единицу времени. Вы можете обратиться к документации MSDN об этих вызовах WIN32.

Другие встроенные системы

Без операционных систем или только с базовыми ОС вам придется:

  • Запрограммируйте один из внутренних таймеров ЦП на свободный запуск и подсчет.
  • сконфигурируйте его для генерации прерывания при переполнении таймера, и в этой подпрограмме прерывания увеличивайте переменную «перенос» (это так, чтобы вы могли фактически измерить время, превышающее разрешение выбранного таймера).
  • перед выполнением своей функции вы сохраняете ОБА значение «переноса» и значение регистра ЦП, содержащего текущие такты для настроенного вами таймера подсчета.
  • то же самое после вашей функции
  • вычтите их, чтобы получить отметку дельта-счетчика.
  • оттуда это просто вопрос того, как долго тик означает на вашем ЦП / оборудовании, учитывая внешние часы и демультипликацию, которые вы настроили при настройке таймера. Вы умножаете эту «длину тиков» на только что полученные «дельта-тики».

ОЧЕНЬ ВАЖНО Не забудьте отключить до и восстанавливать прерывания после получения этих значений таймера (бот переноса и значения регистра), иначе вы рискуете сохранить неправильные значения.

ПРИМЕЧАНИЯ

  • Это очень быстро, потому что это всего лишь несколько инструкций по сборке для отключения прерываний, сохранения двух целочисленных значений и повторного включения прерываний. Фактическое вычитание и преобразование в единицы реального времени происходит вне зоны измерения времени, то есть ПОСЛЕ вашей функции.
  • Возможно, вы захотите поместить этот код в функцию, чтобы повторно использовать этот код повсюду, но это может немного замедлить работу из-за вызова функции и отправки всех регистров в стек, а также параметров, а затем выгрузить их снова , Во встроенной системе это может быть значительным. Лучше, чем в C, использовать вместо этого MACROS или написать свою собственную процедуру сборки, сохраняя / восстанавливая только соответствующие регистры.
2 голосов
/ 16 сентября 2008
start_time = timer
function()
exec_time = timer - start_time
1 голос
/ 16 сентября 2008

Если код .Net, используйте класс секундомера (.net 2.0+), а не DateTime.Now. DateTime.Now не обновляется достаточно точно и даст вам безумные результаты

...