【问题标题】:rdtsc accuracy across CPU cores跨 CPU 内核的 rdtsc 精度
【发布时间】:2011-03-24 05:18:11
【问题描述】:

我从一个线程发送网络数据包,并在另一个 CPU 内核上运行的第二个线程接收回复。我的过程测量每个数据包的发送和接收之间的时间(类似于 ping)。我正在使用 rdtsc 来获得高分辨率、低开销的时序,这是我的实现所需要的。

所有测量结果看起来都很可靠。尽管如此,我还是担心跨内核的 rdtsc 准确性,因为我一直在阅读一些暗示 tsc 在内核之间不同步的文本。

我找到了关于TSC in wikipedia的以下信息

恒定的 TSC 行为确保 每个时钟滴答的持续时间是 统一并支持使用 TSC 作为挂钟定时器,即使 处理器内核改变频率。这 建筑行为是否在移动 转发所有英特尔处理器。

我仍然担心跨核心的准确性,这是我的问题

更多信息

  • 我在 Intel nehalem 机器上运行我的进程。
  • 操作系统是 Linux。
  • 为所有内核设置了“constant_tsc”cpu 标志。

【问题讨论】:

  • 您考虑过使用 HPET 吗?
  • 我不知道 HPET。我刚刚读到它,它似乎是一种高精度计时器(基于中断)而不是时钟。我需要能够根据需要读取高分辨率时钟(例如:网络数据包到达时)

标签: linux multicore rdtsc


【解决方案1】:

我建议你不要使用 rdtsc。它不仅不可移植,而且不可靠并且通常无法工作 - 在某些系统上,rdtsc 不会统一更新(例如,如果您使用的是 speedstep 等)。如果您想要准确的时间信息,您应该在套接字上设置 SO_TIMESTAMP 选项并使用 recvmsg() 来获取带有(微秒分辨率)时间戳的消息。

此外,您使用 SO_TIMESTAMP 获得的时间戳实际上是内核获取数据包的时间,而不是您的任务碰巧注意到的时间。

【讨论】:

  • 感谢您的回答。请注意,使用 constant_tsc 标志,rdtsc 确实会均匀更新;请参阅我添加到我的问题中的报价。 SO_TIMESTAMP 处于毫秒精度,而 rdtsc 处于 nsec 精度,这是我需要的精度。我对数据包到达内核的时间不感兴趣,但对用户收到它的时间不感兴趣,因为这是我的应用程序加速的部分。
【解决方案2】:

在 linux 上,您可以将 clock_gettime(3) 与 CLOCK_MONOTONIC_RAW 一起使用,它可以为您提供纳秒级的结果,并且不受 ntp 更新(如果发生的话)的影响。

【讨论】:

  • 谢谢,还是不好。 CLOCK_MONOTONIC_RAW 在我的环境中未定义。我在 time.h 中确实有 CLOCK_MONOTONIC,我已经尝试过了。 struct timespec 确实具有纳秒级的分辨率;仍然在为 CLOCK_MONOTONIC 调用 clock_gettime 时,最后 3 位数字始终具有相同的值;因此,实际上它只有微秒级的分辨率。
  • 可能您的系统不支持高分辨率计时器?运行此代码时会得到什么: #include #include #include int main() { while (1){ struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); printf("%ld %ld\n", ts.tv_sec, ts.tv_nsec);睡眠(1); } 返回 0; }
  • 1) 下面是您的代码输出的前 5 行(您可以看到 nsec 始终为 246)279595 629885246 279596 630958246 279597 631777246 279598 633596246 2)clock_gettime 的其他问题根据我的统计(在没有睡眠的情况下重复使用时钟 1001 次),在强 nehalem 机器上,clock_gettime(CLOCK_MONOTONIC, &ts) 的平均开销为 281 纳秒;而使用 rdtsc 在同一台机器上只消耗 8 纳秒。
  • 我的问题是关于 rdtsc,因为我需要高分辨率和低开销。我只想确保它跨核心的可靠性,因为我找不到关于它的文档。不过,我的感觉很好。此外,为了使用高分辨率计时器(可能是 CLOCK_MONOTONIC_HR),我需要重新编译内核。这不是一个选项,因为我不能要求所有客户都这样做。
  • 我已经设置了 cpu 亲和力:) 我的问题是关于 2 个不同 CPU 内核上的 2 个线程。我的 recv 线程在没有上下文切换的情况下轮询 NIC 的数据包,并且没有延迟发送出站数据包。在我的环境中,微秒非常重要!
【解决方案3】:

您可以使用sched_set_affinity() API 设置线程关联性,以便在一个 CPU 内核上运行您的线程。

【讨论】:

  • 我已经设置了 cpu 亲和性 :( 我的问题是关于 2 个不同 CPU 内核上的 2 个线程。我的 recv 线程轮询 NIC 的数据包,没有上下文切换,也没有延迟发送出站数据包。在我的环境中微秒算了很多!
  • HEPT 听起来不符合我的需求 - 请参阅我之前关于 HEPT 的评论。在我在多核环境中进行的数百次测试中,RDTSC 看起来很棒且可靠(即使对于运行了数周的机器)。此外,请在我的查询中阅读有关“TSC 作为挂钟计时器”的引文。归根结底,我只寻求正式批准。实际上,RDTSC 似乎确实可以完成这项工作。
  • 内核之间可能会发生漂移(数百毫秒)。
【解决方案4】:

X86_FEATURE_CONSTANT_TSC + X86_FEATURE_NONSTOP_TSC cpuid 中的位(edx=x80000007,位 #8;检查 linux 内核的 unsynchronized_tsc function 以获得更多检查)

Intel 的 Designer 的 vol3b,第 16.11.1 节 Invariant TSC 它说如下

"16.11.1 不变 TSC

较新处理器中的时间戳计数器可能支持增强功能,称为不变 TSC。处理器对不变 TSC 的支持由 CPUID.80000007H:EDX[8] 指示。

不变的 TSC 将在所有 ACPI P-、C- 中以恒定速率运行。和 T 状态。这是向前发展的架构行为。在具有不变 TSC 支持的处理器上,操作系统可以将 TSC 用于挂钟计时器服务(而不是 ACPI 或 HPET 计时器)。 TSC 读取效率更高,并且不会产生与环转换或访问平台资源相关的开销。”

因此,如果 TSC 可用于挂钟,则可以保证它们是同步的。

【讨论】:

  • @avner,可以通过简单的 2 线程测试检查 cpu 内核/ cpu 包之间的 tsc 变化,该测试使用共享变量和忙于等待事件(没有互斥锁)进行“乒乓” ,仅读/写;也是 rdtsc 读取)。当线程固定到不同的核心时,它们会给你 tsc0-tsc1。然后逆序设置affinity,得到tsc1-tsc0。如果两者相等,则您有一个同步的 TSC
  • 感谢 osgx。你的回答听起来很有趣。我找到了相同的答案 gossamer-threads.com/lists/xen/devel/185419 。据我了解,constant_tsc + nonstop_tsc 等效于不变量(跨处理器),而关于 BIOS/mobo 的假设更少。我的旧测试显示 cpu 之间没有漂移 - 我只关心未来的测试和保证在客户现场工作。归根结底,有了你的回答,我比以前更有信心了;因此,我会接受这个答案并希望好:-)
  • @avner,某些现代 CPU 可能没有“不变 TSC”,必须检查此功能。
  • 您仍应注意:虽然 tsc 通过此标志保证在多个内核之间保持一致,但系统可能配备多个 CPU。
  • @Suma 这个答案的理由是文档说你可以依靠使用 rdtsc 作为步行时钟时间意味着你必须能够依靠它在核心之间同步。如果这个推理成立,CPU 之间不也适用吗?
【解决方案5】:

在最近的处理器上,您可以在同一包的不同内核之间执行此操作(即只有一个核心 iX 处理器的系统),您不能在单独的包(处理器)中执行此操作,因为它们不会共享实时时钟。您可以通过 cpu 亲和性(将相关线程锁定到特定内核)来摆脱它,但这又取决于您的应用程序的行为方式。

在 linux 上,您可以检查 /proc/cpuinfo 上的 constant_tsc 以查看处理器是否有一个对整个包有效的 tsc。原始寄存器在 CPUID.80000007H:EDX[8]

我读到但尚未以编程方式确认的是,从修订版 11h 开始的 AMD cpus 对这个 cpuid 位具有相同的含义。

【讨论】:

  • IRQ 余额与它有什么关系?
  • @JosephGarvin 绝对没有,rdtsc 紧密耦合。我可能喝了太多咖啡——或者完全在想别的事情,但这些年来很难记住。不错的想法,我会编辑它。
【解决方案6】:

事实上,内核似乎不共享 TSC,请查看此线程: http://software.intel.com/en-us/forums/topic/388964

总结一下,不同的核心不共享 TSC,有时如果核心改变到特定的能量状态,TSC 可能会失去同步,但这取决于 CPU 的种类,所以你需要查看 Intel 文档。似乎大多数操作系统会在启动时同步 TSC。
我在具有核心 i5 处理器的 Linux Debian 机器上使用令人兴奋的反应算法检查了不同内核上的 TSC 之间的差异。激励器进程(在一个内核中)将 TSC 写入共享变量中,当反应进程检测到该变量发生变化时,它会比较其值并将其与自己的 TSC 进行比较。这是我的测试程序的示例输出:

TSC ping-pong test result:
TSC cores (exciter-reactor): 0-1
100 records, avrg: 159, range: 105-269
Dispersion: 13
TSC ping-pong test result:
TSC cores (exciter-reactor): 1-0
100 records, avrg: 167, range: 125-410
Dispersion: 13

exciter CPU 为 0(平均 159 tic)时的反应时间与 exciter CPU 为 1(167 tic)时的反应时间几乎相同。这表明它们非常同步(可能有一些差异)。在其他核心对上,结果非常相似。
另一方面,rdtscp 汇编指令返回一个值,指示读取 TSC 的 CPU。这不是你的情况,但当你想在一个简单的代码段中测量时间并且你想确保进程没有在代码中间移动 CPU 时,它会很有用。

【讨论】:

  • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效。
  • @MatthewGreen 我用自己研究的一些结果扩展了我的答案。我留下了以前的 url,因为它仍然有用并且不太可能变得无效。
猜你喜欢
  • 2010-10-18
  • 1970-01-01
  • 1970-01-01
  • 2023-02-19
  • 2023-04-01
  • 2018-02-16
  • 2012-01-26
  • 2012-11-06
  • 2016-05-08
相关资源
最近更新 更多