使用std::chrono::system_clock/std::chrono::high_resolution_clock时的时间差异

12

考虑以下代码片段

#include <chrono>
#include <iostream>
#include <thread>

int main()
{
   using std::chrono::system_clock;
   using std::chrono::milliseconds;
   using std::chrono::nanoseconds;
   using std::chrono::duration_cast;
   const auto duration = milliseconds(100);
   const auto start = system_clock::now();
   std::this_thread::sleep_for(duration);
   const auto stop = system_clock::now();
   const auto d_correct = duration_cast<nanoseconds>(duration).count();
   const auto d_actual = duration_cast<nanoseconds>(stop - start).count();
   std::cout << "Difference is " << d_actual << ", and it should be roughly " << d_correct << "\n";
}
我们期望的是差异大约在100000000左右,但实际上差了100039989。您可以查看此演示,在那里它完全正常运行。然而,在我的机器上安装了几个编译器,根据Stack Overflow上的这个答案,可能会导致配置错误。因此,我尝试了建议的修复方法:设置正确的LD_LIBRARY_PATH。这是我尝试过的带有输出的组合之一(还有4.4和4.6等其他组合)。
g++-4.7 time.cpp -pthread -std=c++11; LD_LIBRARY_PATH=/usr/lib/gcc/i686-linux-gnu/4.7/ ./a.out

差异为100126,应该大致为100000000

g++-4.7 time.cpp -pthread -std=c++11; LD_LIBRARY_PATH=/usr/lib/gcc/i686-linux-gnu/4.8/ ./a.out

差值为100132,应该大约是100000000

g++-4.8 time.cpp -pthread -std=c++11; LD_LIBRARY_PATH=/usr/lib/gcc/i686-linux-gnu/4.7/ ./a.out

差值为100085953,大致应该是100000000

g++-4.8 time.cpp -pthread -std=c++11; LD_LIBRARY_PATH=/usr/lib/gcc/i686-linux-gnu/4.8/ ./a.out

差异为100156418,应该大约为100000000。

无论如何,使用任何一个libstdc++进行g++-4.8编译都可以正常工作,而使用g++-4.7编译则会导致破损的情况。

我在编译器/二进制调用方面有什么问题吗,还是g++-4.7中存在Bug?(具体来说,这是g++-4.7.3g++-4.8.1)。

对于(可能最丑陋的)解决方法,当然我可以测量一小段时间,将其与预期差异进行比较并得出因子。但是我非常希望以优雅的方式解决这个问题。


如果有帮助的话,这个页面提到了C++11时钟ABI在GCC 4.8.1中发生了变化:http://gcc.gnu.org/gcc-4.8/changes.html - John Zwinck
@JohnZwinck 这可能是相关的,但我不知道如何解决4.7的情况(我想至少为我正在开发的产品拥有两个编译器版本..在工作中,只有我的开发机器有4.8,其他所有机器都运行<= 4.7) - stefan
GCC 4.7 对 C++11 的支持并不完全,所以你可能会遇到问题,或者至少需要一些麻烦的解决方法。我不是很确定,并且也没有找到 GCC 网站上更具体的问题报告。 - John Zwinck
除了在4.7版本发布后修复了一个错误之外,还有什么可行的结论吗?如果4.7曾经正常工作过,那么您安装的stdlib二进制文件和头文件之间存在不兼容性。但这将是一种异常的配置实例。 - Potatoswatter
@Potatoswatter 显然,这就是我要求修复的原因。但它几乎肯定与此有关。数量级的差异太相似了,不可能只是巧合。无论如何,我只是在我的Ubuntu系统上使用标准的apt-get安装了一些gcc版本。所以某个地方存在一个错误,我想知道是哪里。 - stefan
显示剩余4条评论
3个回答

8

我不能评论,但似乎只是duration_cast的问题...我把你的睡眠时间增加到1000毫秒,并运行它对时间实用程序进行测试。确实,它会睡眠1秒钟。

#include <chrono>
#include <iostream>
#include <thread>

int main()
{
   using std::chrono::system_clock;
   using std::chrono::milliseconds;
   using std::chrono::nanoseconds;
   using std::chrono::duration_cast;
   const auto duration = milliseconds(1000);
   const auto start = system_clock::now();
   std::this_thread::sleep_for(duration);
   const auto stop = system_clock::now();
   const auto d_correct = duration_cast<nanoseconds>(duration).count();
   const auto d_actual = duration_cast<nanoseconds>(stop - start).count();
   std::cout << "Difference is " << d_actual << ", and it should be roughly " << d_correct << "\n";
}

使用time实用程序运行它:

g++-4.7 time.cpp -pthread -std=c++11; time LD_LIBRARY_PATH=/usr/lib/gcc/i686-linux-gnu/4.7/ ./a.out
Difference is 1000193, and it should be roughly 1000000000

real    0m1.004s
user    0m0.000s
sys     0m0.000s

所以,看起来确实是ABI的问题。我的系统也和你的系统一样傻,无法使用更新版本的libstdc++。我们可以通过ldd和/或LD_DEBUG=files来确认这一点:

ldd a.out 
    linux-vdso.so.1 =>  (0x00007fff139fe000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007ff0595b7000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007ff0593a1000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007ff059183000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff058dba000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007ff058ab5000)
    /lib64/ld-linux-x86-64.so.2 (0x00007ff0598e6000)

重大发现!这绝对不是正确的libstdc++库...而且我做的任何事情都无法阻止它!

我的下一个实验是尝试链接静态libstdc++ (http://www.trilithium.com/johan/2005/06/static-libstdc/):

ln -s `g++-4.7 -print-file-name=libstdc++.a`
g++-4.7 -static-libgcc -L. time.cpp -pthread -std=c++11; time ./a.out
Difference is 1000141417, and it should be roughly 1000000000

real    0m1.003s
user    0m0.004s
sys     0m0.000s

已经全部修复了!总的来说,您是安全的。GCC 4.7本身没有问题(呵呵...),但这是一个非常恶心的问题!


1
终于有一个明智的回答!谢谢 :) - stefan
1
哎呀,太棒了!这是我的第一个! - ftwl
2
我知道并且必须说:很少能遇到一个好的第一次回答。请继续留在这个网站 :) - stefan

0
很多时候,根据编译器版本区分代码是不可避免的。 我建议不要在运行时解决4.7和4.8之间的差异(你提到的“丑陋”解决方案)。 而是在编译时解决。
#if __GNUC__ == 4 && __GNUC_MINOR__ > 7
   // your gcc 4.8 and above code here
#else
   // your gcc 4.7.x and below code here
#endif

0
尝试显式使用duration_cast(system_time::now() - start).count()。

我想我在提问时应该更加明确。我编辑了一下,以显示duration_cast并不能解决问题。 - stefan
也许你的g++4.7头文件有问题?如果库头文件错误地定义了纳秒,就会导致这个问题。尝试使用不同版本的g++4.7或者查看头文件。 - wacky6
纳秒的定义取决于ratio中的nano,对我所拥有的所有版本(4.4、4.6、4.7和4.8)都是相同的。 - stefan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接