Как вы измеряете время, необходимое для выполнения функции?
Вопрос
Как вы можете измерить количество времени, которое потребуется для выполнения функции?
Это относительно короткая функция, и время выполнения, вероятно, будет находиться в диапазоне миллисекунд.
Этот конкретный вопрос относится к встроенной системе, запрограммированной на C или C ++.
Решение
Лучший способ сделать это во встроенной системе - установить внешний аппаратный PIN-код при входе в функцию и сбросить его при выходе из функции.Желательно, чтобы это делалось с помощью небольшой инструкции по сборке, чтобы вы не слишком искажали свои результаты.
Редактировать:Одним из преимуществ является то, что вы можете сделать это в своем реальном приложении, и вам не нужен какой-либо специальный тестовый код.Подобные внешние отладочные контакты являются (должны быть!) стандартной практикой для каждой встроенной системы.
Другие советы
Есть три возможных решения:
Аппаратное решение:
Используйте свободный выходной вывод процессора и подсоедините к нему осциллограф или логический анализатор.Инициализируйте pin-код в низкое состояние, непосредственно перед вызовом функции, которую вы хотите измерить, подтвердите pin-код в высокое состояние и сразу после возврата из функции снимите pin-код.
*io_pin = 1;
myfunc();
*io_pin = 0;
Решение проблемы книжного червя:
Если функция довольно мала, и вы можете управлять дизассемблированным кодом, вы можете взломать книгу данных архитектуры процессора и подсчитать циклы, которые потребуются процессору для выполнения каждой инструкции.Это даст вам необходимое количество циклов.
Time = # циклов * Тактовая частота процессора / тактовые тики в соответствии с инструкциями
Это проще сделать для небольших функций или кода, написанного на ассемблере (например, для микроконтроллера PIC).
Решение для счетчика временных меток:
Некоторые процессоры имеют счетчик временных меток, который увеличивается с высокой скоростью (каждые несколько тактов процессора).Просто прочитайте временную метку до и после выполнения функции.Это даст вам время, затраченное на проверку, но помните, что вам, возможно, придется иметь дело с опрокидыванием счетчика.
Вызовите его в цикле с кучей вызовов, затем разделите на количество вызовов, чтобы получить среднее время.
итак:
// begin timing
for (int i = 0; i < 10000; i++) {
invokeFunction();
}
// end time
// divide by 10000 to get actual time.
если вы используете Linux, вы можете установить время выполнения программы, введя в командной строке:
time [funtion_name]
если вы запускаете только функцию в main() (предполагая C ++), остальное время работы приложения должно быть незначительным.
Я повторяю вызов функции много раз (миллионы), но также использую следующий метод, чтобы уменьшить накладные расходы цикла:
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
Вместо того чтобы вызывать функцию() дважды в первом цикле и один раз во втором цикле, вы могли бы просто вызвать ее один раз в первом цикле и не вызывать ее вообще (т. Е.пустой цикл) во втором, однако пустой цикл может быть оптимизирован компилятором, что даст вам отрицательные результаты синхронизации :)
start_time = timer
function()
exec_time = timer - start_time
Windows XP/ NT Embedded или Windows CE/Mobile
Вы можете использовать QueryPerformanceCounter(), чтобы получить значение ОЧЕНЬ БЫСТРОГО счетчика до и после вашей функции.Затем вы вычитаете эти 64-битные значения и получаете дельта-"тики".Используя QueryPerformanceCounterFrequency(), вы можете преобразовать "дельта-тики" в фактическую единицу времени.Вы можете обратиться к документации MSDN об этих вызовах WIN32.
Другие встроенные системы
Без операционных систем или только с базовыми операционными системами вам придется:
- запрограммируйте один из внутренних процессорных таймеров на свободный запуск и подсчет.
- настройте его так, чтобы он генерировал прерывание при переполнении таймера, и в этой процедуре прерывания увеличьте переменную "переноса" (это для того, чтобы вы могли фактически измерять время, превышающее разрешение выбранного таймера).
- перед выполнением функции вы сохраняете КАК значение "переноса", так и значение регистра процессора, содержащего текущие такты для настроенного вами таймера подсчета.
- то же самое после вашей функции
- вычтите их, чтобы получить значение дельта-счетчика.
- оттуда просто нужно знать, сколько времени означает тик на вашем процессоре / оборудовании, учитывая внешние часы и умножение, которое вы настроили при настройке вашего таймера.Вы умножаете эту "длину тика" на "дельта-тики", которые вы только что получили.
ОЧЕНЬ ВАЖНЫЙ Не забудьте отключить прерывания до и восстановить их после получения этих значений таймера (включая перенос и значение регистра), иначе вы рискуете сохранить неправильные значения.
Примечания
- Это очень быстро, потому что для отключения прерываний, сохранения двух целых значений и повторного включения прерываний требуется всего несколько инструкций по сборке.Фактическое вычитание и преобразование в единицы реального времени происходит ЗА ПРЕДЕЛАМИ зоны измерения времени, то есть ПОСЛЕ ВЫПОЛНЕНИЯ вашей функции.
- Возможно, вы захотите поместить этот код в функцию, чтобы повторно использовать этот код повсюду, но это может немного замедлить работу из-за вызова функции и переноса всех регистров в стек, плюс параметров, а затем повторного их использования.Во встроенной системе это может быть существенно.Возможно, было бы лучше вместо этого использовать МАКРОСЫ на C или написать свою собственную процедуру сборки, сохраняющую / восстанавливающую только соответствующие регистры.
Зависит от вашей встроенной платформы и от того, какой тип синхронизации вы ищете.Для встроенного 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
.
Что касается других платформ, вы действуете сами по себе.
Нарисовал
Я всегда внедряю процедуру тикера, управляемую прерываниями.Затем при этом обновляется счетчик, который подсчитывает количество миллисекунд с момента запуска.Затем доступ к этому счетчику осуществляется с помощью функции 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);
}
В терминале OS X (и, вероятно, в Unix тоже) используйте "time".:
time python function.py
Если код является .Net, используйте класс stopwatch (.net 2.0+), А НЕ DateTime.Now.Дата и время.Сейчас обновляется недостаточно точно и даст вам сумасшедшие результаты
Если вы ищете разрешение менее миллисекунды, попробуйте один из этих методов синхронизации.Все они обеспечат вам разрешение по крайней мере за десятки или сотни микросекунд:
Если это встроенный Linux, посмотрите на таймеры Linux:
http://linux.die.net/man/3/clock_gettime
Встроенная Java, посмотрите на nanoTime (), хотя я не уверен, что это есть во встроенной версии:
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/System.html#nanoTime ()
Если вы хотите разобраться со счетчиками оборудования, попробуйте PAPI:
В противном случае вы всегда можете перейти на ассемблер.Вы могли бы взглянуть на исходный код PAPI для вашей архитектуры, если вам нужна некоторая помощь в этом.