prosource

ARM Cortex-A8 프로세서에서 프로그램 실행 시간을 측정하는 방법?

probook 2023. 10. 10. 20:38
반응형

ARM Cortex-A8 프로세서에서 프로그램 실행 시간을 측정하는 방법?

저는 i로 불리는 ARM Cortex-A8 기반 프로세서를 사용하고 있습니다.MX515. 리눅스 Ubuntu 9.10 배포판이 있습니다.큰는 C로며,다를 gettimeofday();응용 프로그램이 걸리는 시간을 측정하는 기능입니다.

main()

{

gettimeofday(start);
....
....
....
gettimeofday(end);

}

이 방법은 제 애플리케이션의 어떤 블록이 얼마나 시간이 걸리는지 확인하기에 충분했습니다.하지만 지금은 gettime of day() 시간을 계산하는 방법으로 코드를 최적화하려고 노력하고 있기 때문에 연속적인 실행(최적화 전후에 실행) 간에 변동이 심하여 실제 실행 시간을 결정할 수 없어 개선 효과가 있습니다.

제가 무엇을 해야 하는지 제안해 줄 수 있는 사람이 있습니까?

사이클 카운터(Cortex-M3용 ARM사이트에서 제안하는 아이디어)에 액세스하면 Cortex-A8의 타이머 레지스터에 액세스하기 위해 따라야 할 단계를 알려주는 코드를 알려줄 수 있는 사람이 있습니까?

만약 이 방법이 매우 정확하지 않다면 몇 가지 대안을 제시해 주시기 바랍니다.

감사해요.


후속 조치

Following 1 : Code Sorcery에 다음 프로그램 작성, 보드에서 실행을 시도하면 실행파일이 생성되어 - 불법 지시 메시지 :(

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: 프리스케일에 지원을 요청하는 편지를 썼으며, 그들은 다음 답장과 프로그램을 다시 보냈습니다(저는 그것에 대해이해하지 못했습니다).

우리가 지금 당장 도와드릴 수 있는 것은 다음과 같습니다. UART를 사용하여 스트림을 전송하는 코드의 예시를 첨부합니다. 당신의 코드에서 당신이 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) ;
}

성능 카운터에 액세스하는 것은 어렵지 않지만 커널 모드에서 활성화해야 합니다.기본적으로 카운터는 비활성화됩니다.

간단히 말해서 커널 내부에서 다음 두 줄을 실행해야 합니다.로드 가능한 모듈로 사용하거나 보드 초기의 어딘가에 두 줄을 추가하는 것만으로도 다음과 같은 효과가 있습니다.

  /* 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는 사이클 카운터를 0으로 설정합니다.그렇게 쉬운.

enable_diver1/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 CPU에서 작동해야 합니다.

아 - 그리고 몇가지 메모:

두 할 수 get_cyclecount()다른 프로세스나 커널에서 사용되는 모든 것을 포함합니다.측정을 공정 또는 단일 스레드로 제한할 수 있는 방법은 없습니다.

get_cyclecount()무료가 아닙니다.단일 asm-명령어로 컴파일되지만 공동 프로세서에서 이동하면 전체 ARM 파이프라인이 정지됩니다.오버헤드가 상당히 높아서 측정값이 왜곡될 수 있습니다.다행히 오버헤드도 고정되어 있어서 측정을 하고 타이밍에서 뺄 수 있습니다.

예를 들어 저는 모든 측정에 대해 그렇게 했습니다.연습할 때는 그러지 마세요.조만간 두 통화 사이에 인터럽트가 발생하여 측정값을 더욱 왜곡할 수 있습니다.유휴 시스템에서 오버헤드를 몇 번 측정하고 모든 외부자를 무시하고 대신 고정 상수를 사용하는 것을 제안합니다.

최적화 전후에 성능 분석 도구를 사용하여 코드를 프로파일링해야 합니다.

acct는 명령줄이며 리소스를 모니터링하는 데 사용할 수 있는 기능입니다.acct에 의해 생성된 dat 파일의 사용 및 보기에 대해 구글에서 자세히 확인할 수 있습니다.

다른 오픈소스 성능 분석 도구로 이 게시물을 업데이트하겠습니다.

Gprof는 또 다른 그러한 도구입니다.동일한 서류 확인 부탁드립니다.

이제 2년이 지났으니 닐스가 답을 좀 더 자세히 설명해 드리자면, 이 카운터들에 접근하기 쉬운 방법은 게이터로 커널을 구축하는 것입니다.그런 다음 ARM의 성능 분석 도구인 Streamline에 사용할 카운터 값을 보고합니다.

각 기능이 타임라인에 표시되며(시스템이 어떻게 수행되고 있는지에 대한 개요를 보여줌), 실행에 걸린 시간과 CPU가 차지한 %를 정확하게 보여줍니다.CPU 집약적인 작업을 수집하고 소스 코드 수준까지 추적하도록 설정한 각 카운터의 차트와 이를 비교할 수 있습니다.

유선형은 모든 Cortex-A 시리즈 프로세서와 함께 작동합니다.

저는 ARM7의 툴체인에서 근무한 적이 있는데 명령어 수준 시뮬레이터가 있었습니다.개별 회선 및/또는 ASM 지시에 대한 시간을 제공할 수 있는 앱을 실행합니다.그것은 주어진 루틴의 미세한 최적화를 위해 아주 좋았습니다.그러나 이러한 접근 방식은 전체 앱/전체 시스템 최적화에는 적합하지 않을 수 있습니다.

언급URL : https://stackoverflow.com/questions/3247373/how-to-measure-program-execution-time-in-arm-cortex-a8-processor

반응형