如何衡量函数执行的时间?

时间:2008-09-16 02:30:57

标签: time embedded profiling

如何衡量一项功能执行所需的时间?

这是一个相对较短的函数,执行时间可能在毫秒范围内。

这个特殊问题涉及一个用C或C ++编程的嵌入式系统。

12 个答案:

答案 0 :(得分:10)

在嵌入式系统上执行此操作的最佳方法是在进入功能时设置外部硬件引脚,并在离开功能时将其清除。这最好通过一个小的汇编指令来完成,这样你就不会过多地扭曲你的结果。

编辑:其中一个好处是您可以在实际应用程序中执行此操作,而不需要任何特殊的测试代码。像这样的外部调试引脚(应该是!)是每个嵌入式系统的标准做法。

答案 1 :(得分:9)

有三种可能的解决方案:

硬件解决方案

使用处理器上的空闲输出引脚,将示波器或逻辑分析仪连接到引脚。在调用要测量的函数之前,将引脚初始化为低电平状态,将引脚置为高电平状态,并在从函数返回后,将引脚置为无效。


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

书虫解决方案

如果函数相当小,并且您可以管理反汇编的代码,则可以破解处理器体系结构数据手册,并计算处理器执行每条指令所需的周期。这将为您提供所需的循环次数 时间= #cycle *每个指令的处理器时钟速率/时钟滴答

对于较小的函数或用汇编语言编写的代码(例如PIC单片机)更容易实现

时间戳计数器解决方案

某些处理器具有时间戳计数器,该计数器以快速递增(每几个处理器时钟周期)。只需读取函数前后的时间戳即可。 这将为您提供已用时间,但要注意您可能必须处理计数器翻转。

答案 2 :(得分:4)

在一个包含大量调用的循环中调用它,然后除以调用次数以获得平均时间。

这样:

// begin timing
for (int i = 0; i < 10000; i++) {
    invokeFunction();
}
// end time
// divide by 10000 to get actual time.

答案 3 :(得分:4)

如果你正在使用linux,你可以通过输入命令行来计算程序的运行时间:

time [funtion_name]

如果你只运行main()中的函数(假设是C ++),应用程序的其余时间应该可以忽略不计。

答案 4 :(得分:3)

我重复多次调用函数(数百万),但也使用以下方法来减少循环开销:

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()两次而在第二个循环中调用一次,而是可以在第一个循环中调用它一次,而在第二个循环中根本不调用它(即空循环),但是编译器可以优化空循环,为您提供负时序结果:)

答案 5 :(得分:2)

start_time = timer
function()
exec_time = timer - start_time

答案 6 :(得分:2)

Windows XP / NT Embedded或Windows CE / Mobile

您可以使用QueryPerformanceCounter()在函数之前和之后获取非常快速计数器的值。然后,您减去这些64位值并获得delta“ticks”。使用QueryPerformanceCounterFrequency()可以将“delta ticks”转换为实际时间单位。您可以参考有关这些WIN32调用的MSDN文档。

其他嵌入式系统

如果没有操作系统或仅使用基本操作系统,则必须:

  • 编程其中一个内部CPU计时器以自由运行和计数。
  • 将其配置为在定时器溢出时产生中断,并在此中断例程中递增“进位”变量(这样您实际上可以测量的时间长于所选定时器的分辨率。)
  • 在你的功能之前你保存“进位”值和保存你配置的计数定时器的运行标记的CPU寄存器的值。
  • 在你的职能之后相同
  • 将它们减去以获得增量计数器滴答。
  • 从那里开始,只需知道在给定外部时钟和设置定时器时配置的去乘的情况下,CPU /硬件上的滴答有多长。你将“滴答长度”乘以你刚刚得到的“delta ticks”。

非常重要在获取这些定时器值(运算符和寄存器值)之后,不要忘记在禁用之前禁用并恢复中断,否则可能会存储不正确的值。

备注

  • 这非常快,因为只有少数汇编指令可以禁用中断,保存两个整数值并重新启用中断。实际的减法和转换为实时单位发生在时间测量区域之外,即在您的功能之后。
  • 您可能希望将该代码放入一个函数中以重用该代码,但由于函数调用和将所有寄存器推送到堆栈,加上参数,然后再次弹出它们,可能会减慢一些因素。在嵌入式系统中,这可能很重要。然后在C中使用MACROS或者编写自己的汇编例程来保存/恢复相关的寄存器可能会更好。

答案 7 :(得分:2)

取决于您的嵌入式平台以及您正在寻找的计时类型。对于嵌入式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之类的东西来强制只在一个核心上使用代码。

如果您要测量用户和系统时间,那么您可以使用返回jiffies之类的times(NULL)。或者您可以将clock_gettime()的参数从CLOCK_THREAD_CPUTIME_ID更改为CLOCK_MONOTONIC ...但请注意使用CLOCK_MONOTONIC

对于其他平台,您可以独立完成。

德鲁

答案 8 :(得分:2)

我总是实现一个中断驱动的自动收报程序。然后,它会更新一个计数器,该计数器计算自启动以来的毫秒数。然后使用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);
}

答案 9 :(得分:1)

如果您正在寻找亚毫秒级分辨率,请尝试使用其中一种计时方法。他们都会在至少数十或数百微秒内获得分辨率:

如果是嵌入式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源。

答案 10 :(得分:1)

在OS X终端(也可能是Unix)中,使用“time”:

time python function.py

答案 11 :(得分:1)

如果代码是.Net,请使用秒表类(.net 2.0+)NOT DateTime.Now。 DateTime.Now没有足够准确地更新,会给你疯狂的结果