【问题标题】:Objects created at the same time - unwanted compiler optimization?同时创建的对象 - 不需要的编译器优化?
【发布时间】:2015-03-30 15:35:28
【问题描述】:

我有一个奇怪的问题:

for (size_t i=0; i<20; i++)
{
   // pre is a vector<UserType>
   pre.push_back(UserType()); // In UserType constructor, record std::chrono::steady_clock::now()
}

给出以下对象

(gdb) print pre $1 = std::vector of length 20, capacity 32 = {{timePoint_ = {__d = {__r = 1427724945979761000}}},{ 时间点_ = {__d = {__r = 1427724945979761000}}},{时间点_ = {__d = { __r = 1427724945979761000}}},{timePoint_ = {__d = {__r = 1427724945979761000}}},{ 时间点_ = {__d = {__r = 1427724945979761000}}},{时间点_ = {__d = { __r = 1427724945979761000}}},{timePoint_ = {__d = {__r = 1427724945979761000}}},{ 时间点_ = {__d = {__r = 1427724945979761000}}},{时间点_ = {__d = { __r = 1427724945979761000}}},{timePoint_ = {__d = {__r = 1427724945979761000}}},{ 时间点_ = {__d = {__r = 1427724945979761000}}},{时间点_ = {__d = { __r = 1427724945979761000}}},{timePoint_ = {__d = {__r = 1427724945979761000}}},{ 时间点_ = {__d = {__r = 1427724945979761000}}},{时间点_ = {__d = { __r = 1427724945979761000}}},{timePoint_ = {__d = {__r = 1427724945979761000}}},{ 时间点_ = {__d = {__r = 1427724945979761000}}},{时间点_ = {__d = { __r = 1427724945979761000}}},{timePoint_ = {__d = {__r = 1427724945979761000}}},{ timePoint_ = {__d = {__r = 1427724945979761000}}}}

1、理论上,20 个 UserType 对象中的每一个都应该有不同且唯一的 time_since_epoch().count(),但在 gdb 输出中,它们都是相同的。

2,我在这里尝试了相同的代码:http://melpon.org/wandbox,每个对象都有一个唯一的时间戳。所以我正在观察不同的行为。

3、一些分析:pre.push_back(UserType())中的UserType();是一个右值;然后编译器将值复制(通过复制构造函数)右值到向量 pre 中的左值对象中。编译器是否有可能看到常量循环号 20 和右值对象指令,因此决定“同时”创建 20 个对象作为优化?即使是这种情况,编译器也不太可能同时进行构造——不存在同时进行的事情——只有可以忽略的微小差异。我不认为编译器可以在 stable_clock 的 1 个刻度内完成 20 个对象构造。

4,这是我的 Makefile 中的相关编译标志 - 请注意,我没有要求编译器进行优化:-g -Wall -std=gnu++0x

5,这段代码(20个对象构造的循环)在google测试文件中;我的编译器是 cygwin 上的 g++ 4.8.3。

我的问题是:

这是怎么回事?具体来说,为什么我看到构建 20 个对象的相同时间戳?

非常感谢。

/************************根据请求,UserType实现********************* /

class UserType
{
    public:
    UserType()
    {
        timePoint_ = std::chrono::steady_clock::now();
    }

    bool operator==(const UserType other) const
    {
        return timePoint_ == other.timePoint_;
    }

    friend std::ostream& operator<<(std::ostream& out, UserType type);

    protected:
    std::chrono::time_point<std::chrono::steady_clock> timePoint_;
};



std::ostream& operator<<(std::ostream& out, UserType type)
{
    out << type.timePoint_.time_since_epoch().count() << std::endl;
    return out;
}

【问题讨论】:

  • 显示UserType 的代码,您可能没有正确使用std::chrono::steady_clock::now()
  • @b4hand 我是。我曾经在 cygwin 上使用 google 测试观察到奇怪的行为(进入函数调用时此指针设置为 null)(这是一个完全不相关的情况),因此为了消除 google 测试问题,我还尝试将类移动到 main.cc 和,我观察到同样的事情:gdb 中所有 20 个对象的滴答计数相同。这是一件非常奇怪的事情......!
  • “我不认为编译器可以在 stable_clock 的 1 个时钟周期内完成 20 个对象构造。” ---我的这个假设是非常错误的。此外,这种说法本身是可笑的错误。编译器生成汇编代码,链接器生成最终的二进制文件。二进制文件负责构建,而不是编译器。但是当我说“编译器”进行构造时,您就会明白我的意思。

标签: c++11 timestamp compiler-optimization chrono g++4.8


【解决方案1】:

std::chrono::steady_clock::now() 不保证任何特定的分辨率。它只保证单调性。因此,连续调用可以返回相同的时间。事实上,为了满足单调性要求,可以牺牲分辨率。

如果您想要最高分辨率的时钟,您必须使用std::chrono::high_resolution_clock

std::chrono::steady_clock::period 将使用std::ratio 告诉您滴答声的长度(以秒为单位)。

【讨论】:

  • 在 20 个构造之前,我还尝试了 std::this_thread::sleep_for(std::chrono::milliseconds(1000)) - 我仍然看到相同的滴答计数。真不觉得分辨率能超过1秒。
  • 为什么您认为分辨率不能达到 1 秒?该标准没有这样的要求。
  • 1,从实用的角度讲,没有编译器供应商会愚蠢到提供分辨率大于 1 秒的 stead_clock 实现。在电子世界里,1秒对人类来说就像1个世纪。 2,我倾向于不使用高分辨率时钟,因为不能保证它是稳定的——这是一个实际问题。 3,为了让生活更轻松,我只是尝试了高分辨率时钟,它仍然给我所有 20 个构造的相同滴答计数。
  • 不过还是感谢您的意见。我很确定时钟不是这里的原因。
  • 当我告诉你如何验证实际分辨率时,为什么要猜测你的想法?
【解决方案2】:

我想我已经找到了问题所在:我将 20 次构造函数调用扩展到 2000 次,并开始观察构造的不同时间戳。话虽如此,在 1 纳秒内进行了数百次构造函数调用。这太棒了。

【讨论】:

    猜你喜欢
    • 2014-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-10
    • 1970-01-01
    • 2021-10-14
    相关资源
    最近更新 更多