C++ g++;循环的优化中断

C++ g++;循环的优化中断,c++,for-loop,optimization,nested-loops,C++,For Loop,Optimization,Nested Loops,几天前,我遇到了我认为是g++5.3中的一个bug,它涉及到在更高的-OX优化级别上嵌套for循环。(专门为-O2和-O3体验过)。问题是,如果有两个嵌套的for循环,它们有一些内部和来跟踪总迭代次数,一旦这个和超过其最大值,它就会阻止外部循环终止。我所能复制的最小代码集是: int main(){ int sum = 0; // Value of 100 million. (2047483648 less than int32 max.)

几天前,我遇到了我认为是g++5.3中的一个bug,它涉及到在更高的
-OX
优化级别上嵌套for循环。(专门为
-O2
-O3
体验过)。问题是,如果有两个嵌套的for循环,它们有一些内部和来跟踪总迭代次数,一旦这个和超过其最大值,它就会阻止外部循环终止。我所能复制的最小代码集是:

int main(){
    int sum = 0;
    //                 Value of 100 million. (2047483648 less than int32 max.)
    int maxInner = 100000000;

    int maxOuter = 30;

    // 100million * 30 = 3 billion. (Larger than int32 max)

    for(int i = 0; i < maxOuter; ++i)
    {
        for(int j = 0; j < maxInner; ++j)
        {
            ++sum;
        }
        std::cout<<"i = "<<i<<" sum = "<<sum<<std::endl;
    }
}
但是,当使用
g++-O2-o run.me main.cpp
编译时,外部循环无法终止。(这仅在
maxInner*maxOuter>2^31
时发生)当总和不断溢出时,它不应以任何方式影响其他变量。我也在Ideone.com上测试了这个,测试用例如下:

因此,我的问题是双重的

  • 总和的值如何以某种方式影响系统?没有基于其值的决策,它仅用于计数器和
    std::cout
    语句。
  • 在不同的优化级别上,什么可能导致显著不同的结果?
  • <> P>提前感谢您抽出时间阅读并考虑我的问题。


    注意:此问题与现有问题不同,例如:因为该问题的问题是sentinal变量溢出。但是,这个问题中的前哨变量
    i
    j
    都不会超过100m的值,更不用说2^31了。

    您的代码调用未定义的行为,因为整数和溢出。你说“这不应该以任何方式影响其他变量”。错。一旦你有了未定义的行为,所有的可能性都会消失。任何事情都有可能发生

    gcc以假设不存在未定义行为的优化而闻名,如果出现未定义行为,我们可以说一些有趣的事情


    解决方法:不要这样做

    这是一个对正确代码完全有效的优化。你的代码不正确

    GCC认为,达到循环退出条件
    i>=maxOuter
    的唯一方法是,在计算
    sum
    的早期循环迭代中,如果您有符号整数溢出。编译器假定不存在有符号整数溢出,因为标准C中不允许有符号整数溢出。因此,
    i
    可以优化为
    true

    这由
    -faggressive循环优化
    标志控制。通过向命令行参数添加
    -fno积极的循环优化
    ,您应该能够获得预期的行为。但最好是确保你的代码是有效的。使用无符号整数类型可获得有保证的有效环绕行为。

    正如@hvd指出的,问题在于无效代码,而不是编译器

    在程序执行期间,
    sum
    值溢出
    int
    范围。由于
    int
    默认情况下是
    signed
    的,并且
    signed
    值的溢出在C中会导致未定义的行为*,因此编译器可以自由地执行任何操作。正如有人在某处指出的,龙可能会从你的鼻子里飞出来。结果只是没有定义

    造成差异的原因是测试终端条件。当编译器优化您的循环时,它意识到它可以优化掉内部循环,使其

    int sum = 0;
    for(int i = 0; i < maxOuter; i++) {
        sum += maxInner;
        std::cout<<"i = "<<i<<" sum = "<<sum<<std::endl;
    }
    
    这看起来像是观察到的行为

    *:根据第6.5节表达式:

    如果在表达式求值期间出现异常情况(即,如果结果未在数学上定义或不在其类型的可表示值范围内),则行为未定义

    **:根据附录H,H.2.2:

    C的无符号整数类型在LIA中为“模”−1表示溢出或越界结果会自动换行


    我对这个话题做了一些研究。我用
    gcc
    g++
    (Manjaro上的5.3.0版)编译了上面的代码,得到了一些非常有趣的东西

    描述 为了使用
    gcc
    (即C编译器)成功编译它,我已经替换了

    #include <iostream>
    ...
    std::cout<<"i = "<<i<<" sum = "<<sum<<std::endl;
    
    main.cpp:

    #ifdef ORIG
    #include <iostream>
    #else
    #include <stdio.h>
    #endif
    
    int main(){
        int sum = 0;
        //                 Value of 100 million. (2047483648 less than int32 max.)
        int maxInner = 100000000;
    
        int maxOuter = 30;
    
        // 100million * 30 = 3 billion. (Larger than int32 max)
    
        for(int i = 0; i < maxOuter; ++i)
        {
            for(int j = 0; j < maxInner; ++j)
            {
                ++sum;
            }
    #ifdef ORIG
            std::cout<<"i = "<<i<<" sum = "<<sum<<std::endl;
    #else
            printf("i = %d sum = %d\n", i, sum);
    #endif
        }
    }
    
    #ifdef ORIG
    #包括
    #否则
    #包括
    #恩迪夫
    int main(){
    整数和=0;
    //价值1亿(2047483648小于int32最大值)
    int maxInner=100000000;
    int maxOuter=30;
    //1亿*30=30亿(大于int32最大值)
    对于(int i=0;istd::coutWhen你写的时候,你的程序可以做任何事情。人们总是通过打开优化来发现“编译器错误”。我还没有看到他们的代码没有被破坏的情况。你很幸运,只得到了一个无限循环——龙可能会在我们说话的时候从你鼻子里出来。你不能“包含”未定义的行为。即使在时间上-如果你在代码中的某个点执行了一些非法操作,甚至在你接近该操作之前发生的一切都是未定义的。@Chris,这就是为什么它被称为未定义的行为。对我们人类来说,似乎合乎逻辑的行为对优化器来说并不重要,这是一个相当复杂的野兽。可能是d这也许是一篇有趣的评论,但它没有回答OP提出的两个明确的问题,也没有试图回答。鉴于OP出于某种原因决定接受你的帖子作为正确答案,它不太可能发生任何事情,但作为提醒,通常像这样的帖子会被删除。Putti不过,在一篇也能回答问题的帖子中,把你的测试作为额外的评论是完全合适的。@hvd谢谢你的通知。我意识到
    
    int i = 0;
    for(int sum = 0;/* nothing here evaluates to true */; sum += maxInner) {
        i++;
        std::cout<<"i = "<<i<<" sum = "<<sum<<std::endl;
    }
    
    #include <iostream>
    ...
    std::cout<<"i = "<<i<<" sum = "<<sum<<std::endl;
    
    #include <stdio.h>
    ...
    printf("i = %d sum = %d\n", i, sum);
    
    $ gcc -x c -Wall -Wextra -O2 -DORIG=1 -o gcc_opt_orig  main.cpp
    
    #ifdef ORIG
    #include <iostream>
    #else
    #include <stdio.h>
    #endif
    
    int main(){
        int sum = 0;
        //                 Value of 100 million. (2047483648 less than int32 max.)
        int maxInner = 100000000;
    
        int maxOuter = 30;
    
        // 100million * 30 = 3 billion. (Larger than int32 max)
    
        for(int i = 0; i < maxOuter; ++i)
        {
            for(int j = 0; j < maxInner; ++j)
            {
                ++sum;
            }
    #ifdef ORIG
            std::cout<<"i = "<<i<<" sum = "<<sum<<std::endl;
    #else
            printf("i = %d sum = %d\n", i, sum);
    #endif
        }
    }