【问题标题】:A code works slowly because of compiler optimizations由于编译器优化,代码运行缓慢
【发布时间】:2015-09-10 09:48:36
【问题描述】:

我有 3 个独立的全局函数,我想测试它的速度。我正在使用此代码:

        // case 1
        {
            chrono::duration<double, milli> totalTime;
            for (uint32_t i{ 0 }; i < REPEATS; ++i)
            {
                auto start = chrono::steady_clock::now();
                func1(); // "normal" c++ code
                auto end = chrono::steady_clock::now();

                auto diff = end - start;
                cout << chrono::duration <double, milli>(diff).count() << " ms" << endl;
            }
        }

        // case 2
        {
            chrono::duration<double, milli> totalTime;
            for (uint32_t i{ 0 }; i < REPEATS; ++i)
            {
                auto start = chrono::steady_clock::now();
                func2(); // multithreaded c++ code
                auto end = chrono::steady_clock::now();

                auto diff = end - start;
                cout << chrono::duration <double, milli>(diff).count() << " ms" << endl;
            }
        }

        // case 3
        {
            chrono::duration<double, milli> totalTime;
            for (uint32_t i{ 0 }; i < REPEATS; ++i)
            {
                auto start = chrono::steady_clock::now();
                func3(); // SIMD c++ code
                auto end = chrono::steady_clock::now();

                auto diff = end - start;
                cout << chrono::duration <double, milli>(diff).count() << " ms" << endl;
            }
        }

这个func1()func2()func3()是不会改变程序状态的全局函数(我没有任何全局变量)。

输出结果取决于运行案例。如果我运行 case 1 和 case 2 我分别有 100ms10ms。如果我运行 case 1 和 case 3 我有 100ms130ms。如果我运行案例 123 我有 130ms10ms120 毫秒。第一个案例在 30% 时变慢,第三个案例变得更快!如果我单独运行案例,我有 100ms10ms130ms。我试图关闭优化 - 代码变得(惊喜,惊喜)慢得多,但至少结果是相同的,不取决于案例顺序。所以我得出一个结论,编译器做了一些特别的事情。是真的吗?

我使用的是 Win7 和 VS 2013。

【问题讨论】:

  • 为什么不提供我们调用的函数? :o 如果不知道函数,谁会知道编译器可以进行哪些优化?
  • 如果第一次调用需要从内存或磁盘加载公共数据,然后为连续调用缓存或缓冲,则顺序很重要。
  • “所以我得出一个结论,编译器做了一些特别的事情。是真的吗?” 是的,他们这样做了!没有您提供的更多信息,这就是我能提供的所有信息。
  • 缓存未命中也很重要。我建议您通过多次循环这些方法并计算每个函数需要多少的平均值来预热缓存。单一衡量指标与任何绩效报告都不相关。
  • @dau_sama 如果您仔细观察,您会发现我在循环中使用了REPEAT

标签: c++ performance optimization


【解决方案1】:

可能会发生一些事情:

  1. 您的测试已被内核抢占。你无能为力来防止这种情况发生。多次运行测试以确保结果一致。
  2. 编译器可以内联您的函数并优化内联代码。
  3. 函数之间存在交互。我猜最简单的事情是内存分配(即 func1() 被优化为请求一个足以供整个程序使用的内存块,或者其中一个函数将一些内存块带入缓存)。

所以建议:

  1. 在基准测试之前首先运行每个函数以摆脱一些 记忆工件。
  2. 运行基准测试几次,看看效果如何 这些值会发生波动以消除一些操作系统工件。
  3. 在每次运行时打乱函数的顺序,或将其作为 参数,以确保消除排序工件。
  4. 不要在循环中执行 cout,因为它会与操作系统交互,并且可能会弄乱您的缓存,甚至会抢占您的进程。将结果写入某个向量并在最后输出所有内容。

可能还有其他因素会影响您的性能(即磁盘、网络、其他进程、内存和 CPU 负载),因此请对这些值持保留态度。

【讨论】:

    【解决方案2】:

    编译器优化各不相同。编译器可以做很多事情——优化之一(至少在 GNU GCC 中)是积极的循环展开——这可能会创建更快的代码,但您必须意识到这会导致缓存未命中,从而有效地减慢您的代码速度。也就是说,如果我们只考虑编译器的优化。

    现在您有三种不同的情况,如果分别运行会给出不同的输出。这可能会受到对齐问题的影响——如果你的代码正确对齐,它会更快,如果不是,额外的填充可能会减慢它——我在 C# 中看到过类似的事情,但我找不到这个立即线程。

    最后可能发生的事情是您运行的测试太少而无法确定 - 10k 测试是不错的设置,您可以开始比较速度输出。一次性测试可能会受到操作系统的影响,因此请记住这一点。

    哦,因为微软在编写编译器方面非常出色,there are bugs in certain versions。我不认为微软的 C++ 编译器的世界 - 有很多黑客和解决方法,它不像其他流行的编译器那样最新 - 但这只是我的看法。所以另一种选择是编译器只是出现故障。另请参阅 thisthis beautiful typedef

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多