문제

함수가 실행되는 데 걸리는 시간을 어떻게 측정할 수 있나요?

이는 상대적으로 짧은 함수이며 실행 시간은 아마도 밀리초 범위일 것입니다.

이 특정 질문은 C 또는 C++로 프로그래밍된 임베디드 시스템과 관련이 있습니다.

도움이 되었습니까?

해결책

임베디드 시스템에서 이를 수행하는 가장 좋은 방법은 기능에 들어갈 때 외부 하드웨어 핀을 설정하고 기능에서 나갈 때 이를 지우는 것입니다.이 작업은 약간의 조립 지침을 통해 수행하는 것이 바람직하므로 결과가 너무 많이 왜곡되지 않습니다.

편집하다:장점 중 하나는 실제 애플리케이션에서 수행할 수 있으며 특별한 테스트 코드가 필요하지 않다는 것입니다.이와 같은 외부 디버그 핀은 모든 임베디드 시스템의 표준 관행입니다.

다른 팁

세 가지 잠재적인 솔루션이 있습니다.

하드웨어 솔루션:

프로세서의 빈 출력 핀을 사용하고 오실로스코프나 로직 분석기를 핀에 연결합니다.측정하려는 함수를 호출하기 직전에 핀을 낮은 상태로 초기화하고, 핀을 높은 상태로 어설션하고, 함수에서 돌아온 직후에 핀을 어설션 해제합니다.


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

책벌레 솔루션:

기능이 매우 작고 분해된 코드를 관리할 수 있는 경우 프로세서 아키텍처 데이터북을 열어 프로세서가 모든 명령을 실행하는 데 걸리는 주기를 계산할 수 있습니다.그러면 필요한 사이클 수가 제공됩니다.
시간 = # 사이클 * 프로세서 클럭 속도/명령당 클럭 틱

이는 더 작은 기능이나 어셈블러로 작성된 코드(예: 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

첫 번째 루프에서 function()을 두 번 호출하고 두 번째 루프에서 한 번 호출하는 대신 첫 번째 루프에서 한 번만 호출하고 전혀 호출하지 않을 수 있습니다(예:빈 루프) 두 번째에서는 빈 루프가 컴파일러에 의해 최적화되어 부정적인 타이밍 결과를 제공할 수 있습니다. :)

start_time = timer
function()
exec_time = timer - start_time

Windows XP/NT Embedded 또는 Windows CE/Mobile

QueryPerformanceCounter()를 사용하여 함수 전후의 VERY FAST 카운터 값을 가져옵니다.그런 다음 해당 64비트 값을 빼고 델타 "틱"을 얻습니다.QueryPerformanceCounterFrequency()를 사용하면 "델타 틱"을 실제 시간 단위로 변환할 수 있습니다.해당 WIN32 호출에 대한 MSDN 설명서를 참조할 수 있습니다.

기타 임베디드 시스템

운영 체제가 없거나 기본 OS만 있는 경우 다음을 수행해야 합니다.

  • 내부 CPU 타이머 중 하나를 프로그래밍하여 자유롭게 실행하고 계산할 수 있습니다.
  • 타이머가 오버플로될 때 인터럽트를 생성하도록 구성하고 이 인터럽트 루틴에서 "carry" 변수를 증가시킵니다(이렇게 하면 실제로 선택한 타이머의 분해능보다 긴 시간을 측정할 수 있습니다).
  • 함수 전에 "캐리" 값과 구성한 카운팅 타이머의 실행 틱을 보유하는 CPU 레지스터 값을 모두 저장합니다.
  • 당신의 기능 후에도 마찬가지
  • 이를 빼면 델타 카운터 틱을 얻을 수 있습니다.
  • 여기서는 외부 클럭과 타이머를 설정하는 동안 구성한 디멀티플리케이션을 고려하여 CPU/하드웨어에서 틱이 얼마나 오래 지속되는지 아는 것이 중요합니다.방금 얻은 "델타 틱"에 "틱 길이"를 곱합니다.

매우 중요 해당 타이머 값(캐리 및 레지스터 값)을 얻은 후에 인터럽트를 비활성화하고 복원하는 것을 잊지 마십시오. 그렇지 않으면 잘못된 값을 저장할 위험이 있습니다.

노트

  • 인터럽트를 비활성화하고, 두 개의 정수 값을 저장하고, 인터럽트를 다시 활성화하는 몇 가지 어셈블리 명령어만 필요하므로 이는 매우 빠릅니다.실제 빼기 및 실시간 단위로의 변환은 시간 측정 영역 외부, 즉 함수 이후에 발생합니다.
  • 해당 코드를 함수에 넣어서 해당 코드를 전체적으로 재사용할 수 있지만 함수 호출과 모든 레지스터를 스택에 푸시하고 매개변수를 추가한 다음 다시 팝하기 때문에 속도가 약간 느려질 수 있습니다.임베디드 시스템에서는 이것이 중요할 수 있습니다.대신 C에서 MACROS를 사용하거나 관련 레지스터만 저장/복원하는 어셈블리 루틴을 작성하는 것이 더 나을 수 있습니다.

임베디드 플랫폼과 원하는 타이밍 유형에 따라 다릅니다.임베디드 Linux의 경우 여러 가지 방법으로 수행할 수 있습니다.함수에서 사용하는 CPU 시간의 양을 측정하려면 다음을 수행할 수 있습니다.

#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인 경우 DateTime.Now가 아닌 ​​스톱워치 클래스(.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를 사용해 보십시오.

http://icl.cs.utk.edu/papi/

그렇지 않으면 언제든지 어셈블러로 갈 수 있습니다.이에 대한 도움이 필요하면 아키텍처에 대한 PAPI 소스를 살펴볼 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top