Доступ к счетчикам производительности не сложен, но вы должны включить их из режима ядра.По умолчанию счетчики отключены.
В двух словах вы должны выполнить следующие две строки внутри ядра.Либо как загружаемый модуль, либо просто добавление двух строк где-то в board-init сделает:
/* enable user-mode access to the performance counter*/
asm ("MCR p15, 0, %0, C9, C14, 0\n\t" :: "r"(1));
/* disable counter overflow interrupts (just in case)*/
asm ("MCR p15, 0, %0, C9, C14, 2\n\t" :: "r"(0x8000000f));
Как только вы это сделаете, счетчик циклов начнет увеличиваться для каждого цикла.Переполнения регистра останутся незамеченными и не вызовут никаких проблем (за исключением того, что они могут испортить ваши измерения).
Теперь вы хотите получить доступ к счетчику циклов из пользовательского режима:
Мы начинаем с функции, которая читает регистр:
static inline unsigned int get_cyclecount (void)
{
unsigned int value;
// Read CCNT Register
asm volatile ("MRC p15, 0, %0, c9, c13, 0\t\n": "=r"(value));
return value;
}
И вы, скорее всего, хотите сбросить и установить делитель:
static inline void init_perfcounters (int32_t do_reset, int32_t enable_divider)
{
// in general enable all counters (including cycle counter)
int32_t value = 1;
// peform reset:
if (do_reset)
{
value |= 2; // reset all counters to zero.
value |= 4; // reset cycle counter to zero.
}
if (enable_divider)
value |= 8; // enable "by 64" divider for CCNT.
value |= 16;
// program the performance-counter control-register:
asm volatile ("MCR p15, 0, %0, c9, c12, 0\t\n" :: "r"(value));
// enable all counters:
asm volatile ("MCR p15, 0, %0, c9, c12, 1\t\n" :: "r"(0x8000000f));
// clear overflows:
asm volatile ("MCR p15, 0, %0, c9, c12, 3\t\n" :: "r"(0x8000000f));
}
do_reset
установит цикл-счет до нуля.Все просто.
enable_diver
включит делитель цикла 1/64.Без этого флага вы будете измерять каждый цикл.При включении счетчик увеличивается на каждые 64 цикла.Это полезно, если вы хотите измерить длительное время, которое могло бы вызвать переполнение счетчика.
Как его использовать:
// init counters:
init_perfcounters (1, 0);
// measure the counting overhead:
unsigned int overhead = get_cyclecount();
overhead = get_cyclecount() - overhead;
unsigned int t = get_cyclecount();
// do some stuff here..
call_my_function();
t = get_cyclecount() - t;
printf ("function took exactly %d cycles (including function call) ", t - overhead);
Должно работать на всех процессорах Cortex-A8 ..
О - и некоторые примечания:
Используя эти счетчики, вы измерите точное время между двумя вызовами get_cyclecount()
, включая все, что потрачено в других процессах или в ядре.Невозможно ограничить измерение вашим процессом или одним потоком.
Также вызов get_cyclecount()
не бесплатен.Он скомпилируется в одну asm-инструкцию, но перемещение из сопроцессора остановит весь конвейер ARM.Накладные расходы довольно высоки и могут исказить ваши измерения.К счастью, накладные расходы также фиксированы, так что вы можете измерить их и вычесть из своих временных интервалов.
В моем примере я делал это для каждого измерения.Не делай этого на практике.Между двумя вызовами рано или поздно произойдет прерывание, что приведет к еще большему искажению результатов измерений.Я предлагаю вам пару раз измерить накладные расходы в режиме ожидания, игнорировать всех посторонних и вместо этого использовать фиксированную константу.