更快相当于gettimeofday

Hum*_*ger 25 c optimization clock gettimeofday

在尝试构建一个对延迟敏感的应用程序时,需要每秒发送100条消息,每条消息都有时间字段,我们要考虑优化gettimeofday.首先想到的是rdtsc基于优化.有什么想法吗 ?还有其他指针吗?返回的时间值所需的准确度以毫秒为单位,但如果该值偶尔与接收器不同步1-2毫秒,则不是很大.试图比62纳秒的gettimeofday做得更好

Dav*_*rei 55

POSIX时钟

我为POSIX时钟源写了一个基准:

  • 时间=> 3个周期
  • ftime(ms)=> 54个周期
  • gettimeofday(us)=> 42个周期
  • clock_gettime(ns)=> 9个周期(CLOCK_MONOTONIC_COARSE)
  • clock_gettime(ns)=> 9个周期(CLOCK_REALTIME_COARSE)
  • clock_gettime(ns)=> 42个周期(CLOCK_MONOTONIC)
  • clock_gettime(ns)=> 42个周期(CLOCK_REALTIME)
  • clock_gettime(ns)=> 173个周期(CLOCK_MONOTONIC_RAW)
  • clock_gettime(ns)=> 179个周期(CLOCK_BOOTTIME)
  • clock_gettime(ns)=> 349个周期(CLOCK_THREAD_CPUTIME_ID)
  • clock_gettime(ns)=> 370个周期(CLOCK_PROCESS_CPUTIME_ID)
  • rdtsc(周期)=> 24个周期

这些数字来自Linux 4.0上的Intel Core i7-4771 CPU @ 3.50GHz.这些测量是使用TSC寄存器进行的,并且每个时钟方法运行数千次,并采用最小成本值.

您将要在要运行的计算机上进行测试,但这些计算机的实现方式因硬件和内核版本而异.代码可以在这里找到.它依赖于TSC寄存器进行循环计数,它在同一个repo(tsc.h)中.

TSC

访问TSC(处理器时间戳计数器)是最准确和最便宜的时间方式.通常,这是内核自己使用的内容.它在现代英特尔芯片上也非常简单,因为TSC在内核之间同步,不受频率调整的影响.因此它提供了一个简单的全球时间源.你可以看到使用它的一个例子在这里与汇编代码的演练在这里.

这个问题的主要问题(可移植性除外)似乎没有从循环到纳秒的好方法.英特尔文档据我所知,TSC以固定频率运行,但该频率可能与处理器规定的频率不同.英特尔似乎没有提供可靠的方法来确定TSC频率.Linux内核似乎通过测试两个硬件定时器之间出现的TSC周期数来解决这个问题(见这里).

Memcached的

Memcached很难做到缓存方法.它可能只是为了确保跨平台的性能更具可预测性,或者使用多个内核进行更好的扩展.它也可能不值得进行优化.

  • 你怎么能以纳秒精度进行基准测试?有没有办法确保您的程序是唯一执行的程序,并且不允许上下文切换? (2认同)

bdo*_*lan 46

您是否真的进行过基准测试,发现gettimeofday速度慢得令人无法接受?

以每秒100条消息的速率,每条消息有10毫秒的CPU时间.如果你有多个核心,假设它可以完全并行化,你可以轻松地将其增加4-6倍 - 每条消息40-60毫秒!gettimeofday的成本不太可能接近10毫秒 - 我怀疑它更像1-10微秒(在我的系统上,微基准测试它每次通话大约1微秒 - 自己试试).您的优化工作将更好地用于其他地方.

当使用TSC是一个合理的想法,现代的Linux已经有一个基于TSC用户空间gettimeofday的 -如果可能的话,在VDSO会的gettimeofday的实现,应用的偏移拉(从共享内核-用户内存段读)来rdtsc的值,因此计算一天中的时间而不进入内核.但是,某些CPU型号没有在不同内核或不同软件包之间同步的TSC,因此最终可能会被禁用.如果您需要高性能计时,您可能首先要考虑查找具有同步TSC的CPU模型.

也就是说,如果你愿意牺牲大量的分辨率(你的时间只能精确到最后一个滴答,意味着它可以关闭几十毫秒),你可以使用CLOCK_MONOTONIC_COARSE或CLOCK_REALTIME_COARSEclock_gettime.这也是用vdso实现的,并且保证不会调用内核(对于最近的内核和glibc).

  • @Humble,检查你的dmesg中的"标记TSC不稳定".如果它在那里,你就不会使用TSC.但总是在尝试优化之前始终是基准测试.你不仅不知道它是否足够快开始,如果你没有基准,你永远不会知道你是否有所改善...... (2认同)

小智 5

就像 bdonian 所说,如果你每秒只发送几百条消息,gettimeofday那就足够快了。

但是,如果您每秒发送数百万条消息,情况可能会有所不同(但您仍然应该衡量它是一个瓶颈)。在这种情况下,您可能需要考虑这样的事情:

  • 有一个全局变量,以您所需的精度给出当前时间戳
  • 有一个专门的后台线程,除了更新时间戳之外什么都不做(如果时间戳应该每 T 个时间单位更新一次,那么让线程休眠 T 的一部分,然后更新时间戳;如果需要,请使用实时功能)
  • 所有其他线程(或主进程,如果您不使用线程)只是读取全局变量

如果时间戳值大于 ,C 语言不保证您可以读取它sig_atomic_t。您可以使用锁定来处理这个问题,但是锁定很重。相反,您可以使用volatile sig_atomic_t类型变量来索引时间戳数组:后台线程更新数组中的下一个元素,然后更新索引。其他线程读取索引,然后读取数组:它们可能会得到一点点过时的时间戳(但下次它们会得到正确的时间戳),但它们不会遇到读取时间戳的问题同时它被更新,并获取旧值的一些字节和新值的一些字节。

但对于每秒数百条消息来说,这一切都太过分了。

  • “有一个专用的后台线程,除了更新时间戳之外什么也不做(如果时间戳应该每 T 个时间单位更新一次” <-- 这正是 CLOCK_*_COARSE 所做的,除了专用线程实际上是一个中断处理程序并且是系统的 -宽,内核人员已经为您处理了读取撕裂和其他问题:) (4认同)
  • 我不确定这会比 Linux 的“gettimeofday()”更快:每次写入都可能导致 SMP 上的每个读取器发生缓存未命中。 (3认同)