在尝试构建一个对延迟敏感的应用程序时,需要每秒发送100条消息,每条消息都有时间字段,我们要考虑优化gettimeofday。
首先想到的是基于rdtsc
的优化。有什么想法吗 ?还有其他指针吗?
返回的时间值所需的准确度以毫秒为单位,但如果该值偶尔与接收器不同步1-2毫秒,则不是很大。
试图比62纳秒gettimeofday做得更好
答案 0 :(得分:50)
我为POSIX时钟源写了一个基准:
这些数字来自Linux 4.0上的Intel Core i7-4771 CPU @ 3.50GHz。这些测量是使用TSC寄存器进行的,每个时钟方法运行数千次,并采用最低成本值。
您希望在要运行的计算机上进行测试,但这些计算机的实现方式因硬件和内核版本而异。代码可以找到here。它依赖于TSC寄存器进行循环计数,该寄存器位于相同的仓库中(tsc.h)。
访问TSC(处理器时间戳计数器)是时间最准确,最便宜的方式。通常,这是内核自己使用的内容。它在现代英特尔芯片上也非常简单,因为TSC在内核之间同步,不受频率调整的影响。因此它提供了一个简单的全球时间源。您可以查看使用它here的示例以及汇编代码here的演练。
这个问题的主要问题(可移植性除外)似乎没有一个好的方法可以从周期到纳秒。英特尔文档据我所知,TSC以固定频率运行,但该频率可能与处理器规定的频率不同。英特尔似乎没有提供可靠的方法来确定TSC频率。 Linux内核似乎通过测试两个硬件定时器之间出现的TSC周期数来解决这个问题(参见here)。
Memcached很想做缓存方法。它可能只是为了确保跨平台的性能更具可预测性,或者使用多个内核进行更好的扩展。它也可能不值得进行优化。
答案 1 :(得分:44)
您是否真的进行了基准测试,发现gettimeofday
速度慢得令人无法接受?
以每秒100条消息的速率,每条消息有10毫秒的CPU时间。如果你有多个核心,假设它可以完全并行化,你可以轻松地将其增加4-6倍 - 每条消息40-60毫秒! gettimeofday的成本不太可能接近10毫秒 - 我怀疑它更像1-10微秒(在我的系统上,微基准测试它给每个呼叫大约1微秒 - try it for yourself)。您的优化工作将更好地用于其他地方。
虽然使用TSC是一个合理的想法,但现代Linux已经有userspace TSC-based gettimeofday - 在可能的情况下,vdso将引入应用偏移的gettimeofday实现(从共享内核用户内存段读取)到rdtsc
的值,从而计算一天中没有进入内核的时间。但是,某些CPU型号没有在不同内核或不同软件包之间同步的TSC,因此最终可能会被禁用。如果您想要高性能计时,您可能首先要考虑查找具有同步TSC的CPU模型。
那就是说,如果你愿意牺牲大量的分辨率(你的时间只能精确到最后一个滴答,意味着它可以关闭几十毫秒),你可以使用CLOCK_MONOTONIC_COARSE or CLOCK_REALTIME_COARSE clock_gettime。这也是用vdso实现的,并且保证不会调用内核(对于最近的内核和glibc)。
答案 2 :(得分:4)
就像bdonian所说,如果你每秒只发送几百条消息,那么gettimeofday
就会足够快。
但是,如果您每秒发送数百万条消息,则可能会有所不同(但您仍应衡量它是一个瓶颈)。在这种情况下,您可能需要考虑这样的事情:
如果C语言大于sig_atomic_t
,则不保证您可以读取时间戳值。您可以使用锁定来处理它,但锁定很重。相反,您可以使用volatile sig_atomic_t
类型变量来索引时间戳数组:后台线程更新数组中的下一个元素,然后更新索引。其他线程读取索引,然后读取数组:它们可能会得到一点点过时的时间戳(但下次它们会得到正确的时间戳),但它们不会遇到问题,因为它们会读取时间戳。在更新的同时,获取旧值的一些字节和一些新值。
但是这一切对于每秒数百条消息来说太过分了。
答案 3 :(得分:1)
以下是基准。我看到约30ns。来自rashad How to get current time and date in C++?
的printTime()#include <string>
#include <iostream>
#include <sys/time.h>
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,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
答案 4 :(得分:0)
您需要毫秒精度吗?如果不是,您可以简单地使用time()
并处理unix时间戳。