Skip to main content

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

·438 words·3 mins
VxWorks 实时系统 性能分析 嵌入式系统 C 编程 内核开发
Table of Contents

使用 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

Linux 内核编译与模块开发指南
·772 words·4 mins
Linux Linux Kernel Kernel Compilation Kernel Modules System Calls Kernel Development Linux 0.11 QEMU X86
Dell Technology Summit 2026:6K OLED 与专业显示器
·523 words·3 mins
Dell Dell Technology Summit 2026 UltraSharp Alienware OLED 显示器 6K 显示 游戏显示器 企业级显示器
Qualcomm Oryon CPU 以全新 FlexCache 设计达到 5GHz
·280 words·2 mins
Qualcomm Oryon Snapdragon 移动 CPU FlexCache 智能体 AI 端侧 AI CPU 架构 移动计算