繁体   English   中英

GCC:程序不适用于编译选项-O3

[英]GCC: program doesn't work with compilation option -O3

当我用优化(选项-O1,-O2,-O3等)编译它时,我正在编写一个不起作用的C ++程序(我遇到了分段错误),但是当我编译它时它工作得很好优化。

我的代码中是否有错误的可能性? 或者我应该假设这是GCC中的错误?

我的GCC版本是3.4.6。

这种问题有没有已知的解决方法?

我的程序的优化和未优化版本之间的速度差异很大,所以我真的需要使用优化。


这是我原来的算子。 没有优化级别的工作正常,并且在任何优化级别都会引发分段错误:

struct distanceToPointSort{
    indexedDocument* point ;
    distanceToPointSort(indexedDocument* p): point(p) {}
    bool operator() (indexedDocument* p1,indexedDocument* p2){
        return distance(point,p1) < distance(point,p2) ;
    }
} ;

并且这个可以完美地适用于任何级别的优化:

struct distanceToPointSort{
    indexedDocument* point ;
    distanceToPointSort(indexedDocument* p): point(p) {}
    bool operator() (indexedDocument* p1,indexedDocument* p2){

        float d1=distance(point,p1) ;
        float d2=distance(point,p2) ;

        std::cout << "" ;  //without this line, I get a segmentation fault anyways

        return d1 < d2 ;
    }
} ;

不幸的是,这个问题难以重现,因为它发生了一些特定的值。 我在对超过一千个向量中的一个进行排序时得到了分段错误,所以它实际上取决于每个向量具有的特定值组合。

现在你发布了代码片段并找到了一个工作的解决方法(@Windows程序员的答案),我可以说你正在寻找的也许是-ffloat-store

-ffloat店

不要将浮点变量存储在寄存器中,并禁止其他可能会改变浮点值是从寄存器或存储器获取的选项。

这个选项可以防止诸如68000之类的机器上出现不希望的过度精度,其中浮动寄存器(68881)比双重应该具有更高的精度。 同样适用于x86架构。 对于大多数程序来说,多余的精度只会很好,但有些程序依赖于IEEE浮点的精确定义。 在修改它们以将所有相关的中间计算存储到变量中之后,对这些程序使用-ffloat-store。

资料来源: http//gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Optimize-Options.html

我会先假设你的代码错了。
虽然很难分辨。

您的代码是否编译为0警告?

 g++ -Wall -Wextra -pedantic -ansi

这里有一些似乎有用的代码,直到你点击-O3 ...

#include <stdio.h>

int main()
{
    int i = 0, j = 1, k = 2;
    printf("%d %d %d\n", *(&j-1), *(&j), *(&j+1));
    return 0;
}

没有优化,我得到“2 1 0”; 有了优化,我得到了“40 1 2293680”。 为什么? 因为我和k得到了优化!

但我正在取j的地址并走出分配给j的内存区域。 标准不允许这样做。 您的问题很可能是由与标准的类似偏差引起的。

我觉得valgrind在这些时候经常会有所帮助。

编辑:一些评论者认为标准允许任意指针算术。 它不是。 请记住,某些体系结构具有有趣的寻址方案,对齐可能很重要,如果溢出某些寄存器,则可能会出现问题!

关于向指针添加/从指针中减去一个整数的[草稿]标准的字样(强调添加):

“如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不会产生溢出; 否则,行为是未定义的。

看到&j甚至没有指向数组对象,&j-1和&j + 1几乎不能指向同一个数组对象的一部分。 因此,简单地评估&j + 1(更不用说解除引用它)是未定义的行为。

在x86上,我们可以非常自信地将一个添加到指针是相当安全的,只需将我们带到下一个内存位置即可。 在上面的代码中,当我们对内存包含的内容做出假设时会出现问题,当然标准不会接近。

错误在您的代码中。 根据C标准,你可能正在做一些调用未定义行为的东西,它恰好在没有优化的情况下工作,但是当GCC对执行其优化做出某些假设时,代码会在这些假设不正确时中断。 确保使用-Wall选项进行编译, -Wextra也可能是一个好主意,看看是否有任何警告。 您也可以尝试-ansi-pedantic ,但这些可能会导致误报。

作为一个实验,试着看看这是否会迫使编译器一致地舍入所有内容。

volatile float d1=distance(point,p1) ;
volatile float d2=distance(point,p2) ;
return d1 < d2 ;

您可能遇到了别名问题(或者可能是其他一百万个问题)。 查找-fstrict-aliasing选项。

没有更多信息,这种问题无法正确回答。

它很少是编译器的错误,但编译器确实存在错误,并且它们通常表现在不同的优化级别(例如,如果优化传递中存在错误)。

通常在报告编程问题时: 提供一个最小的代码示例来演示问题 ,这样人们就可以将代码保存到文件中,编译并运行它。 尽可能简单地重现您的问题。

另外,尝试不同版本的GCC(编译自己的GCC非常容易,特别是在Linux上)。 如果可能,请尝试使用其他编译器。 英特尔C有一个或多或少兼容GCC的编译器(我认为可以免费用于非商业用途)。 这将有助于查明问题。

它几乎( 几乎 )从不编译器。

首先,确保您使用-Wall编译无警告。

如果这没有给你一个“尤里卡”时刻,请将调试器附加到可执行文件的最低优化版本,该版本崩溃并查看它正在做什么以及它在哪里。

5将为您解决此问题。

几天前遇到同样的问题,在我的情况下,它是别名。 与其他编译器相比,GCC做的不同,但没有错。 GCC已成为有些人可能称之为C ++标准的规则律师,并且它们的实现是正确的,但你必须在C ++中真正正确,否则它将过度优化某些事情,这很痛苦。 但你得到速度,所以不能抱怨。

在阅读一些评论后,我希望能在这里获得一些评价,但在控制台游戏编程领域,相当常见的是,较高的优化级别有时会在奇怪的边缘情况下生成错误的代码。 但是,很可能边缘情况可以通过对代码的细微更改来修复。

好吧......这是我遇到过的最奇怪的问题之一。
我不认为我有足够的证据表明它是一个GCC错误,但老实说......它看起来真的像一个。

这是我原来的算子。 没有优化级别的工作正常,并且在任何优化级别都会引发分段错误:

struct distanceToPointSort{
    indexedDocument* point ;
    distanceToPointSort(indexedDocument* p): point(p) {}
    bool operator() (indexedDocument* p1,indexedDocument* p2){
        return distance(point,p1) < distance(point,p2) ;
    }
} ;

并且这个可以完美地适用于任何级别的优化:

struct distanceToPointSort{
    indexedDocument* point ;
    distanceToPointSort(indexedDocument* p): point(p) {}
    bool operator() (indexedDocument* p1,indexedDocument* p2){

        float d1=distance(point,p1) ;
        float d2=distance(point,p2) ;

        std::cout << "" ;  //without this line, I get a segmentation fault anyways

        return d1 < d2 ;
    }
} ;

不幸的是,这个问题难以重现,因为它发生了一些特定的值。 我在对超过一千个向量中的一个进行排序时得到了分段错误,所以它实际上取决于每个向量具有的特定值组合。

真正的答案隐藏在此线程中所有注释的某处。 首先:它不是编译器中的错误。

问题与浮点精度有关。 distanceToPointSort应该是一个函数,对于参数(a,b)和(b,a)都应该永远不会返回true,但这正是编译器决定对某些数据路径使用更高精度时可能发生的情况。 问题尤其可能出现,但绝不限于x86而没有-mfpmath=sse 如果比较器以这种方式运行,则sort函数可能会变得混乱,并且分段错误也就不足为奇了。

我认为-ffloat-store是最好的解决方案(已经由CesarB提出)。

哇,我没想到答案如此狡猾,而且这么多......

使用std :: sort()对std ::指针向量进行排序时发生错误

我提供严格弱序的仿函数。

但我知道我提供的仿函数是正确的,因为我已经使用了很多它并且工作正常。

另外,错误不能是向量中的一些无效指针,因为当我对向量进行排序时会发生错误。 如果我迭代遍历向量而不首先应用std :: sort,程序运行正常。

我只是用GDB来试图找出发生了什么。 当std :: sort调用我的仿函数时发生错误。 通常std :: sort将无效指针传递给我的仿函数。 (当然这只发生在优化版本,任何级别的优化-O,-O2,-O3)

正如其他人所指出的那样,可能是严格的混淆。 把它转到o3然后再试一次。 我的猜测是你在你的函子中做了一些指针技巧(快速浮点数作为int比较?低2位的对象类型?),它们在内联模板函数中失败。 警告无助于抓住这种情况。 “如果编译器可以检测到所有严格的别名问题,它也可以避免它们”只是更改不相关的代码行可能会使问题出现或消失,因为它会改变寄存器分配。

正如更新的问题将显示;),问题存在于std::vector<T*> 向量的一个常见错误是reserve()应该调整大小()d。 因此,您将在数组边界外编写。 优化器可能会丢弃这些写入。

发布距离代码! 它可能会做一些指针魔术,请参阅我以前的帖子。 执行中间分配只是通过更改寄存器分配来隐藏代码中的错误。 更有说服力的是输出变化的东西!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM