【问题标题】:g++ -O optimizationsg++ -O 优化
【发布时间】:2011-12-27 07:42:31
【问题描述】:

我有以下代码:

#include <iostream>
int main()
{
        int n = 100;
        long a = 0;
        int *x = new int[n];

        for(int i = 0; i < n; ++i)
                for(int j = 0; j < n; ++j)
                        for(int k = 0; k < n; ++k)
                                for(int l = 0; l < n; ++l)
                                {   

                                        a += x[(j + k + l) % 100];
                                }   
        std::cout << a << std::endl;
        delete[] x;
        return 0;
}

如果我在没有优化的情况下编译 g++ test.cc 然后运行时间 ./a.out 它将显示 0.7s。但是,当我用 -O 编译它时,时间减少了 2 倍。

使用的编译器

g++ (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2

我的问题

如何重写代码,以便在没有 -O 的情况下编译时可以获得相同的时间(或接近它)?换句话说,如何手动优化嵌套循环

我为什么要问

我有一个类似的代码,如果我使用 -O 优化,它的运行速度大约快 4 倍。

PS:我查看了编译器的手册,但是那里的标志太多了,无法测试哪一个真正有所作为。

【问题讨论】:

  • -O2 执行“不以空间换速度的所有优化”,因此编译器很可能优化了这些循环中的大部分,用编译时数学(即常量)替换它们。就我个人而言,我无法在脑海中做到这一点,但编译器肯定可以弄清楚如何在更少的 OP 中多次添加相同的数组元素。 GCC 特别擅长做很棒的编译时东西。所以它只会归结为一个或两个嵌套循环......这就是它更快的原因。话虽如此,使用-save-temps 编译并查看.s 文件。那你肯定知道。
  • 你为什么要问?如果您想要更多优化,请告诉编译器。 -O2 隐含了一系列优化,-O 却没有。如果您希望两者都生成相似的代码,您需要编写不会从 -O2 添加的任何优化中受益的代码。我不知道那些是什么,但我猜其中有很多通用的。手动添加它们或牺牲一只山羊来阻止它们应用到你的代码中不会帮助你自己。
  • @Alessandro Pezzato:数组的所有元素都使用该类型的默认构造函数进行初始化。
  • 所以...编译器能够优化您的类似代码。听起来一切都按预期工作。就像 delnan 说的,如果你想要它快,告诉编译器进行优化。问自己如何做是没有意义的。编译器会为你做各种各样的事情。让它!
  • QtLearner:算了。你不可能达到你想要的。如果你真的想知道为什么,你必须用 -S 编译并查看编译器生成的机器码。

标签: c++ g++ compiler-optimization


【解决方案1】:

编译器使用 -O 优化的大部分内容都低于 C++。例如,所有变量都存在于内存中,包括您的循环变量。因此,如果没有优化,编译器很可能在内循环的每次迭代中首先读取循环变量以将其与 0 进行比较,在循环内部再次加载它以将其用作索引,然后在循环结束时将再次读取该值,将其递增,然后将其写回。通过优化,它会注意到循环体中的循环变量没有改变,因此不需要每次都从内存中重新读取。此外,它还会注意到变量的地址永远不会被占用,因此没有其他代码会访问它,因此也可以省略将其写入内存。也就是说,循环变量将只存在于内存中。仅此优化一项就可以在函数执行期间节省 3 亿次内存读取和 1 亿次内存写入。但由于处理器寄存器和内存读取/写入等内容并未在语言级别公开,因此无法在语言级别对其进行优化。

此外,手动优化编译器优化的东西是没有意义的。最好把时间花在优化编译器无法优化的东西上。

【讨论】:

    【解决方案2】:

    这段代码有未定义的行为,所以实际上优化器可以为所欲为..

    a += x[j + k + l % 100];
    

    应该是:

    a += x[(j + k + l) % 100];
    

    如果你解决了这个问题,我仍然不明白你为什么要优化实际上什么都不做的东西......

    我个人会将其优化为:)

    std::cout << 0 << std::endl;
    

    注意:删除i 的循环并执行std::cout &lt;&lt; a * n &lt;&lt; std::endl;

    【讨论】:

    • 我想优化它,因为我有一个更复杂的嵌套循环程序,而且速度不够快。当我使用 -O2 时,它比没有优化时快 4 倍左右。
    • 另外,只需删除最外层的循环并将 a 乘以 n 即可得到最终结果。另见en.wikipedia.org/wiki/Loop_optimization
    • 这是一个作业,其他人如果发现它可能会复制我的代码,因为截止日期还没有过去。无论如何都是类似的,它只是在嵌套部分中包含更多变量和很多条件。
    • @QtLearner:我无能为力,因为您所要求的没有意义。阅读 cmets。
    • @QtLearner:取决于实际的循环...因为这不是您要优化的代码,我能帮上什么忙吗? ://
    【解决方案3】:

    您可以观察到您的循环形成了一组系数coeff[i],因此单个循环求和a[i] * coeff[i] 产生与嵌套循环相同的总数。如果我们假设您的索引是(i + j + k) % 100,那么所有i 的索引都是coeff[i] = n * n * n,因此您的整个程序将简化为

    long coeff = n * n * n;
    for (int i = 0; i < n; ++n)
        a += x[i] * coeff;
    

    我确信它的运行时间远远少于 0.7 秒。

    【讨论】:

      【解决方案4】:

      您使用的是多项式时间算法 (n ** 4),因此按道理它会很慢,尤其是对于较大的 n。也许复杂度较低的算法会对您有所帮助?

      如果您想优化您的代码,您可以要求编译器为您优化,或者直接用汇编语言编写并完成。不要试图猜测 C++ 编译器。

      【讨论】:

      • 我已经尝试对实际问题进行所有优化,并从 O(n^6) 减少到 O(n^4)。现在我唯一的选择是尝试优化代码。
      • @QtLearner:那么根据这个定义,不可能优化代码以提高速度,因为代码只是一堆词汇字符。我们通常以代码实现和算法的关系来指代代码和算法。为了优化代码,你优化了你已经完成的算法。您应该做的是使用分析器来真正查看哪些操作使用的时间最多。
      【解决方案5】:

      如何重写代码以便在没有 -O 的情况下编译时可以获得相同的时间(或接近它)?也就是说,如何手动优化嵌套循环?

      你在汇编中编写它们。祝你好运,顺便说一句。

      编译器中优化开关的目的是让你说,“编译器,我希望你尝试生成快速汇编。”默认情况下,编译器不进行优化,因为这些优化可能会抑制结果代码的可调试性。所以你必须专门要求他们。

      一般来说,编译器为优化代码所做的事情不是您可以手动完成的事情。有些可以(如循环展开),但有些不能。

      【讨论】:

        猜你喜欢
        • 2012-09-05
        • 2012-02-12
        • 1970-01-01
        • 1970-01-01
        • 2011-07-11
        • 1970-01-01
        • 1970-01-01
        • 2018-11-01
        • 2013-01-07
        相关资源
        最近更新 更多