Я использую ARM основанный на коре-A8 процессор, названный как я. MX515. Существует Linux распределение Ubuntu 9.10. Я запускаю очень большое приложение, записанное в C, и я использую gettimeofday();
функции для измерения времени мое приложение берут.
main()
{
gettimeofday(start);
....
....
....
gettimeofday(end);
}
Этот метод был достаточен для взгляда на то, какие блоки моего приложения брал что количество времени. Но, теперь, когда, я пытаюсь оптимизировать свой код очень совершенно с gettimeofday () метод вычисления времени, я вижу большое колебание между последовательными выполнениями (Выполненный прежде и после моей оптимизации), таким образом, я не могу определить фактическое время выполнения, следовательно влияние моих улучшений.
Кто-либо может предложить меня, что я должен сделать?
Если путем доступа к счетчику циклов (Идея, предложенная на веб-сайте ARM для Коры-M3), кто-либо может указать на меня на некоторый код, который дает мне шаги, которые я должен выполнить для доступа к регистрам таймера на Коре-A8?
Если этот метод не очень точен, затем предложите некоторые альтернативы.
Спасибо
Продолжите 1: Записал следующую программу на Колдовстве Кода, исполняемый файл был сгенерирован, который, когда я пытался работать на плате, я получил - сообщение Запрещенной команды :(
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));
}
int main()
{
/* 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));
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..
printf("\nHello World!!");
t = get_cyclecount() - t;
printf ("function took exactly %d cycles (including function call) ", t - overhead);
get_cyclecount();
return 0;
}
Продолжите 2: Я записал в Freescale для поддержки, и они отправили мне назад следующий ответ и программу (я действительно не совсем понимал много от него),
Вот то, с чем мы можем помочь Вам прямо сейчас: Я отправляю Вас, присоединяют пример кода, который отправляет поток с помощью UART, от того, что код, кажется, что Вы не init правильно MPU.
(hash)include <stdio.h>
(hash)include <stdlib.h>
(hash)define BIT13 0x02000
(hash)define R32 volatile unsigned long *
(hash)define R16 volatile unsigned short *
(hash)define R8 volatile unsigned char *
(hash)define reg32_UART1_USR1 (*(R32)(0x73FBC094))
(hash)define reg32_UART1_UTXD (*(R32)(0x73FBC040))
(hash)define reg16_WMCR (*(R16)(0x73F98008))
(hash)define reg16_WSR (*(R16)(0x73F98002))
(hash)define AIPS_TZ1_BASE_ADDR 0x70000000
(hash)define IOMUXC_BASE_ADDR AIPS_TZ1_BASE_ADDR+0x03FA8000
typedef unsigned long U32;
typedef unsigned short U16;
typedef unsigned char U8;
void serv_WDOG()
{
reg16_WSR = 0x5555;
reg16_WSR = 0xAAAA;
}
void outbyte(char ch)
{
while( !(reg32_UART1_USR1 & BIT13) );
reg32_UART1_UTXD = ch ;
}
void _init()
{
}
void pause(int time)
{
int i;
for ( i=0 ; i < time ; i++);
}
void led()
{
//Write to Data register [DR]
*(R32)(0x73F88000) = 0x00000040; // 1 --> GPIO 2_6
pause(500000);
*(R32)(0x73F88000) = 0x00000000; // 0 --> GPIO 2_6
pause(500000);
}
void init_port_for_led()
{
//GPIO 2_6 [73F8_8000] EIM_D22 (AC11) DIAG_LED_GPIO
//ALT1 mode
//IOMUXC_SW_MUX_CTL_PAD_EIM_D22 [+0x0074]
//MUX_MODE [2:0] = 001: Select mux mode: ALT1 mux port: GPIO[6] of instance: gpio2.
// IOMUXC control for GPIO2_6
*(R32)(IOMUXC_BASE_ADDR + 0x74) = 0x00000001;
//Write to DIR register [DIR]
*(R32)(0x73F88004) = 0x00000040; // 1 : GPIO 2_6 - output
*(R32)(0x83FDA090) = 0x00003001;
*(R32)(0x83FDA090) = 0x00000007;
}
int main ()
{
int k = 0x12345678 ;
reg16_WMCR = 0 ; // disable watchdog
init_port_for_led() ;
while(1)
{
printf("Hello word %x\n\r", k ) ;
serv_WDOG() ;
led() ;
}
return(1) ;
}
Доступ к счетчикам производительности несложен, но вы должны включить их в режиме ядра. По умолчанию счетчики отключены.
Вкратце, вам нужно выполнить следующие две строки внутри ядра. Подойдет либо загружаемый модуль, либо просто добавление двух строк где-нибудь в 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. Накладные расходы довольно высоки и могут исказить ваши измерения. К счастью, накладные расходы также фиксированы, так что вы можете измерить их и вычесть из своего тайминга.
В моем примере я делал это для каждого измерения. Не делайте этого на практике. Рано или поздно между двумя вызовами произойдет прерывание, что еще больше исказит ваши измерения. Я предлагаю вам пару раз измерить накладные расходы на простаивающей системе, игнорировать всех посторонних и вместо этого использовать фиксированную константу.
Вам нужно профилировать свой код с помощью инструментов анализа производительности до и после оптимизации.
Acct - это командная строка и функция, которую вы можете использовать для мониторинга своих ресурсов. Вы можете погуглить больше об использовании и просмотре файла dat, следовательно, сгенерированного acct.
Я обновлю этот пост с помощью других инструментов анализа производительности с открытым исходным кодом.
Gprof является еще одним таким инструментом. Пожалуйста, проверьте документацию для того же.
Я работал в инструментарии для ARM7, который имел симулятор уровня инструкций. Запуск приложений в нем мог дать тайминги для отдельных строк и/или asm-инструкций. Это было здорово для микрооптимизации конкретной процедуры. Однако такой подход, вероятно, не подходит для оптимизации всего приложения/ всей системы.