4

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

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

这个特定的问题与使用 C 或 C++ 编程的嵌入式系统有关。

4

12 回答 12

10

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

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

于 2008-09-16T02:36:25.263 回答
9

有三种可能的解决方案:

硬件解决方案

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


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

书虫解决方案

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

对于较小的功能或用汇编程序编写的代码(例如,对于 PIC 微控制器),这更容易做到

时间戳计数器解决方案

一些处理器有一个时间戳计数器,它会快速递增(每隔几个处理器时钟滴答声)。只需读取函数前后的时间戳即可。这将为您提供经过的时间,但请注意您可能必须处理计数器翻转。

于 2008-09-16T02:44:45.180 回答
4

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

所以:

// begin timing
for (int i = 0; i < 10000; i++) {
    invokeFunction();
}
// end time
// divide by 10000 to get actual time.
于 2008-09-16T02:33:51.197 回答
4

如果您使用的是 linux,您可以通过在命令行中键入来计时程序的运行时间:

time [funtion_name]

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

于 2008-09-16T02:42:11.067 回答
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() 两次,在第二个循环中调用一次,您可以在第一个循环中调用它一次,并且在第二个循环中根本不调用它(即空循环),但是空循环可以由编译器优化,给你负时序结果:)

于 2008-10-01T15:47:57.430 回答
2
start_time = timer
function()
exec_time = timer - start_time
于 2008-09-16T02:32:30.373 回答
2

Windows XP/NT Embedded 或 Windows CE/Mobile

您可以使用 QueryPerformanceCounter() 在函数之前和之后获取 VERY FAST 计数器的值。然后你减去那些 64 位值并得到一个增量“滴答声”。使用 QueryPerformanceCounterFrequency() 您可以将“增量刻度”转换为实际时间单位。您可以参考有关这些 WIN32 调用的 MSDN 文档。

其他嵌入式系统

如果没有操作系统或只有基本操作系统,您将不得不:

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

非常重要不要忘记在获取这些计时器值(进位和寄存器值)之前禁用并恢复中断,否则您可能会保存不正确的值。

笔记

  • 这非常快,因为只需要几条汇编指令就可以禁用中断、保存两个整数值并重新启用中断。实际减法和转换为实时单位发生在时间测量区域之外,即在您的函数之后。
  • 您可能希望将该代码放入一个函数中以重用该代码,但由于函数调用和将所有寄存器推入堆栈,加上参数,然后再次弹出它们,它可能会减慢速度。在嵌入式系统中,这可能很重要。在 C 中使用 MACROS 或编写自己的汇编例程仅保存/恢复相关寄存器可能会更好。
于 2008-09-16T03:01:05.827 回答
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来强制仅在一个核心上执行代码。

如果您正在寻找测量用户和系统时间,那么您可以使用times(NULL)which 返回类似 jiffies 的东西。或者您可以将参数clock_gettime()from更改CLOCK_THREAD_CPUTIME_IDCLOCK_MONOTONIC...,但请注意使用CLOCK_MONOTONIC.

对于其他平台,您就靠自己了。

德鲁

于 2008-09-16T03:27:17.433 回答
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);
}
于 2008-10-01T15:28:38.240 回答
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 源代码。

于 2008-09-16T02:36:43.700 回答
1

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

time python function.py
于 2008-09-16T02:37:23.527 回答
1

如果代码是 .Net,请使用秒表类 (.net 2.0+) 而不是 DateTime.Now。DateTime.Now 更新不够准确,会给你带来疯狂的结果

于 2008-09-16T02:39:15.630 回答