更快相当于gettimeofday

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

你有没有真正的基准测试,并发现gettimeofday是慢得令人无法接受的?

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

虽然使用TSC是一个合理的想法,现代Linux已经有一个基于TSC的用户空间gettimeofday – 在可能的情况下,vdso将引入gettimeofday的实现,该实现将偏移量(从共享内核用户内存段读取)应用于rdtsc ‘ s值,因此计算一天中的时间而不进入内核。 但是,某些CPU型号没有在不同内核或不同软件包之间同步的TSC,因此最终可能会被禁用。 如果您想要高性能计时,您可能首先要考虑查找具有同步TSC的CPU模型。

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

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很难做缓存方法。 可能只是确保跨平台的性能更具可预测性,或者使用多个内核进行更好的扩展。 它也可能不值得进行优化。

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

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

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

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

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

以下是基准。 我看到大约30ns。 来自rashad的printTime() 如何在C ++中获取当前时间和日期?

 #include  #include  #include  using namespace std; void printTime(time_t now) { struct tm tstruct; char buf[80]; tstruct = *localtime(&now); strftime(buf, sizeof(buf), "%Y-%m-%d.%X", &tstruct); cout << buf << endl; } int main() { timeval tv; time_t tm; gettimeofday(&tv,NULL); printTime((time_t)tv.tv_sec); for(int i=0; i<100000000; i++) gettimeofday(&tv,NULL); gettimeofday(&tv,NULL); printTime((time_t)tv.tv_sec); printTime(time(NULL)); for(int i=0; i<100000000; i++) tm=time(NULL); printTime(time(NULL)); return 0; } 

100,000秒或30ns的3秒;

 2014-03-20.09:23:35 2014-03-20.09:23:38 2014-03-20.09:23:38 2014-03-20.09:23:41 

你需要毫秒精度吗? 如果不是,您可以简单地使用time()并处理unix时间戳。