↓ Skip to main content

使用 timex() 与时间戳测量 VxWorks 执行时间

使用 timex() 与时间戳测量 VxWorks 执行时间

在 VxWorks 上优化实时应用程序时,准确测量执行时间至关重要。timex() 工具提供了一种便捷的方式,可从 VxWorks shell 或应用程序代码中对函数进行基准测试;而 sysTimestamp() 等 API 则提供显著更精细的计时分辨率,适用于详细性能分析。

合适的计时机制取决于工作负载和所需精度。基于系统时钟节拍的 API 足以进行粗粒度测量,而硬件时间戳计数器更适合短代码路径、中断服务例程以及性能敏感的内核代码。

🔧 timex() 如何测量执行时间
#

VxWorks 的 timex() 函数通过 usrLib 实用程序库提供。它执行目标函数、测量经过的系统时钟节拍、估算测量开销,并报告最终的执行时间。

测量工作流程
#

基本过程如下:

  1. 捕获当前系统时钟节拍计数。
  2. 执行目标函数。
  3. 捕获结束时的节拍计数。
  4. 单独测量计时开销。
  5. 从测量的时间间隔中减去估算的开销。
  6. 使用系统时钟频率将所得节拍数转换为时间值。
  7. 将测量结果打印到 VxWorks 控制台。

基本转换公式为:

$$ \text{执行时间(秒)}
#

\frac{\Delta\text{ticks}}{\text{sysClkRateGet()}} $$

例如,在 1000 Hz 系统时钟下,一个节拍约代表 1 ms;在 100 Hz 时钟下,标称节拍间隔约 10 ms。

由于测量本质上与系统时钟节拍绑定,timex() 更适合相对粗粒度的基准测试,而非纳秒级性能分析。

🧩 timex() API 与调用约定
#

该 API 接受一个函数指针,后跟最多八个整数参数:

void timex(
FUNCPTR function_name,
int arg1,
int arg2,
int arg3,
int arg4,
int arg5,
int arg6,
int arg7,
int arg8
);

因此,目标函数可通过 timex() 接口接收最多八个参数。在 32 位 VxWorks 目标上,整数参数也可在适当情况下用于传递指针大小的值。

未使用的参数应初始化为 0。

例如:

timex((FUNCPTR)myWorkload,
1000000,
42,
0,
0,
0,
0,
0,
0);

这将调用:

myWorkload(1000000, 42);

目标函数在调用任务的上下文中执行。当从 VxWorks shell 调用时,这通常是 shell 任务,而非专用的基准测试任务。

📊 比较 VxWorks 计时机制
#

| 计时方法            | 分辨率                              | 最佳用例                               | 优点                        | 局限性                                    |
| :------------------ | :---------------------------------- | :------------------------------------- | :-------------------------- | :---------------------------------------- |
| `timex()`           | 系统时钟节拍级                      | 快速函数基准测试                       | 简单且 shell 友好           | 受系统时钟节拍分辨率限制                  |
| `tickGet()`         | 系统时钟节拍级                      | 长时任务计时                           | 轻量级内核 API              | 对短操作过于粗糙                          |
| `sysTimestamp()`    | 硬件计数器级                        | 细粒度分析与 ISR 计时                  | 高分辨率测量                | 依赖 BSP 与硬件                           |
| `clock_gettime()`   | POSIX 时钟,通常为纳秒级 API        | 可移植的应用层分析                     | 标准 POSIX 接口             | 需要相应的 POSIX 定时器支持               |

sysTimestamp() 报告的分辨率取决于底层 BSP 和硬件定时器。其 API 暴露的是硬件计数器,而非相对粗糙的调度器时钟节拍。

⚡ 使用 sysTimestamp() 进行高分辨率分析
#

对于短执行路径,sysTimestamp() 通常比 timex() 更有用,因为它读取高分辨率时间戳计数器。

典型的测量序列如下:

startTs = sysTimestamp();

myWorkload(1000000, 42);

endTs = sysTimestamp();

然后根据时间戳频率计算经过的时间间隔:

$$ \text{执行时间}
#

\frac{\text{结束时间戳} - \text{开始时间戳}} {\text{sysTimestampFreq()}} $$

这种方法避免依赖系统调度器时钟节拍,并能揭示 tickGet() 或 timex() 无法察觉的性能差异。

然而,时间戳计数器依赖硬件和 BSP。应用程序应在将其作为分析原语使用前,验证时间戳支持是否可用。

🧪 完整的 C 计时示例
#

以下示例比较了 timex() 的便捷性与更高分辨率的 sysTimestamp() 测量:

#include <vxWorks.h>
#include <stdio.h>
#include <usrLib.h>
#include <sysLib.h>
#include <timestampLib.h>

/* 待基准测试的工作负载 */
void myWorkload(int loopCount, int value)
{
volatile int i;

for (i = 0; i < loopCount; i++) {
value = (value * 3) ^ i;
}
}

/* 演示多种计时机制 */
void testTiming(void)
{
UINT32 startTs;
UINT32 endTs;
UINT32 freq;
double timeInSeconds;

printf("=== Method 1: timex() ===\n");

/*
* 执行:
* myWorkload(1000000, 42)
*/
timex((FUNCPTR)myWorkload,
1000000,
42,
0,
0,
0,
0,
0,
0);

printf("\n=== Method 2: sysTimestamp() ===\n");

if (sysTimestampConnect(NULL, 0) == OK &&
sysTimestampEnable() == OK) {

freq = sysTimestampFreq();

startTs = sysTimestamp();

myWorkload(1000000, 42);

endTs = sysTimestamp();

timeInSeconds =
(double)(endTs - startTs) / (double)freq;

printf("Elapsed Time: %.6f seconds "
"(%u ticks @ %u Hz)\n",
timeInSeconds,
(endTs - startTs),
freq);
}
else {
printf("Hardware timestamp timer is not "
"supported by this BSP.\n");
}
}

这两种测量服务于不同目的。timex() 提供可直接从 shell 调用的便捷基线,而 sysTimestamp() 允许使用平台更高分辨率的时间戳设施测量相同的工作负载。

🧠 实用性能分析注意事项
#

孤立地对函数进行计时,并不一定代表其在生产环境中的执行时间。VxWorks 任务调度、缓存状态、中断、分支预测、内存争用以及编译器优化都可能影响结果。

对短工作负载进行重复测量
#

如果目标函数仅在几微秒内完成,测量开销相对于工作负载本身可能变得显著。多次重复操作并测量总时间间隔,可产生更稳定的结果。

控制编译器优化
#

如果编译器移除或转换了工作负载,基准测试可能被扭曲。示例中使用了 volatile 循环变量,使循环的可观察行为更难被消除,但当编译器优化很重要时,生产基准测试仍应在生成的代码级别进行检查。

考虑缓存状态
#

第一次调用可能包含后续调用可避免的指令缓存和数据缓存未命中。对于有意义的稳态测量,应分别进行冷缓存和热缓存测试,而非将其视为等价。

考虑中断与调度干扰
#

VxWorks 是实时操作系统,但正常执行仍可能被 ISR 和更高优先级任务打断。对于延迟敏感的基准测试,应测量多次迭代并检查分布,而非仅依赖单次结果。

🎯 选择合适的计时 API
#

当主要需求是快速便捷的函数基准测试(尤其是从 VxWorks shell)时,使用 timex()。

当测量相对长时间运行的操作且系统时钟节拍精度足够时,使用 tickGet()。

当测量短执行路径、驱动例程、ISR 相关代码或微架构性能且 BSP 提供合适的硬件时间戳源时,使用 sysTimestamp()。

当POSIX 可移植性和应用层计时比直接访问平台特定时间戳硬件更重要时,使用 clock_gettime()。

对于严肃的性能分析,将粗粒度系统级测量与高分辨率时间戳分析相结合,可提供更完整的 VxWorks 应用程序行为视图。

Related