编译器优化会引入错误吗?

发布于 2024-08-30 15:09:57 字数 271 浏览 6 评论 0原文

今天我和一个朋友讨论了“编译器优化”的问题,我们争论了几个小时。

我捍卫了这样的观点:有时,编译器优化可能会引入错误或至少引入不良行为。

我的朋友完全不同意,他说“编译器是由聪明人构建的,做聪明的事情”,因此,永远不会出错。

他根本没有说服我,但我不得不承认我缺乏现实生活中的例子来加强我的观点。

谁在这里?如果是的话,您是否有任何现实生活中的例子,其中编译器优化在生成的软件中产生了错误?如果我错了,我应该停止编程并学习钓鱼吗?

Today I had a discussion with a friend of mine and we debated for a couple of hours about "compiler optimization".

I defended the point that sometimes, a compiler optimization might introduce bugs or at least, undesired behavior.

My friend totally disagreed, saying that "compilers are built by smart people and do smart things" and thus, can never go wrong.

He didn't convince me at all, but I have to admit I lack of real-life examples to strengthen my point.

Who is right here? If I am, do you have any real-life example where a compiler optimization produced a bug in the resulting software? If I'm mistaking, should I stop programming and learn fishing instead?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(22

葬シ愛 2024-09-06 15:09:57

编译器优化可能会引入错误或不良行为。这就是为什么您可以将它们关闭。

一个例子:编译器可以优化对内存位置的读/写访问,执行诸如消除重复读取或重复写入或重新排序某些操作之类的操作。如果所讨论的内存位置仅由单个线程使用并且实际上是内存,则可能没问题。但如果内存位置是硬件设备 IO 寄存器,那么重新排序或消除写入可能是完全错误的。在这种情况下,您通常必须编写代码,并知道编译器可能会“优化”它,从而知道这种简单的方法行不通。

更新:正如 Adam Robinson 在评论中指出的那样,我上面描述的场景更多的是编程错误,而不是优化器错误。但我试图说明的一点是,某些原本正确的程序与一些本来可以正常工作的优化相结合,当它们组合在一起时可能会在程序中引入错误。在某些情况下,语言规范说“您必须这样做,因为可能会发生此类优化并且您的程序将会失败”,在这种情况下,这是代码中的错误。但有时编译器具有(通常是可选的)优化功能,可能会生成不正确的代码,因为编译器过于努力地优化代码或无法检测到优化是否不合适。在这种情况下,程序员必须知道何时可以安全地打开相关优化。

另一个例子:
Linux 内核有一个错误,在测试该指针为空之前,可能会取消引用该指针。然而,在某些情况下,可以将内存映射到地址零,从而允许取消引用成功。编译器在注意到指针被取消引用后,假设它不能为 NULL,然后删除稍后的 NULL 测试以及该分支中的所有代码。 这在代码中引入了安全漏洞,因为该函数将继续使用包含攻击者提供的数据的无效指针。对于指针合法为空并且内存未映射到地址零的情况,内核仍然会像以前一样发生 OOPS。因此,在优化之前,代码包含一个错误;之后它包含两个,其中一个允许本地 root 漏洞利用。

CERT 有一个演示文稿,名为“危险的优化和Robert C. Seacord 的《因果关系损失》列出了许多在程序中引入(或暴露)错误的优化。它讨论了各种可能的优化,从“做硬件所做的事情”到“​​捕获所有可能的未定义行为”到“做任何不被禁止的事情”。

一些代码示例在积极优化的编译器掌握之前完全正常:

  • 检查溢出

    // 由于溢出测试被删除而失败
    if (ptr + len < ptr || ptr + len > max) 返回 EINVAL;
    
  • 完全使用溢出算术:

    // 编译器将其优化为无限循环
    对于 (i = 1; i > 0; i += i) ++j;
    
  • 清除敏感信息的内存:

    //编译器可以删除这些“无用的写入”
    memset(password_buffer, 0, sizeof(password_buffer));
    

这里的问题是,几十年来,编译器在优化方面不太积极,因此一代又一代的 C 程序员学习并理解诸如固定大小的二进制补码加法之类的东西以及如何它溢出了。然后编译器开发人员修改了C语言标准,尽管硬件没有改变,但微妙的规则发生了变化。 C 语言规范是开发人员和编译器之间的合同,但协议的条款可能会随着时间的推移而发生变化,并不是每个人都理解每个细节,或者都认为这些细节是明智的。

这就是为什么大多数编译器提供标志来关闭(或打开)优化。您编写的程序是否考虑到整数可能会溢出?那么您应该关闭溢出优化,因为它们可能会引入错误。您的程序是否严格避免别名指针?然后,您可以打开假设指针永远不会出现别名的优化。您的程序是否尝试清除内存以避免泄漏信息?哦,在这种情况下你就不走运了:你要么需要关闭死代码删除,要么你需要提前知道你的编译器将消除你的“死”代码,并使用一些工作- 围绕它。

Compiler optimizations can introduce bugs or undesirable behaviour. That's why you can turn them off.

One example: a compiler can optimize the read/write access to a memory location, doing things like eliminating duplicate reads or duplicate writes, or re-ordering certain operations. If the memory location in question is only used by a single thread and is actually memory, that may be ok. But if the memory location is a hardware device IO register, then re-ordering or eliminating writes may be completely wrong. In this situation you normally have to write code knowing that the compiler might "optimize" it, and thus knowing that the naive approach doesn't work.

Update: As Adam Robinson pointed out in a comment, the scenario I describe above is more of a programming error than an optimizer error. But the point I was trying to illustrate is that some programs, which are otherwise correct, combined with some optimizations, which otherwise work properly, can introduce bugs in the program when they are combined together. In some cases the language specification says "You must do things this way because these kinds of optimizations may occur and your program will fail", in which case it's a bug in the code. But sometimes a compiler has a (usually optional) optimization feature that can generate incorrect code because the compiler is trying too hard to optimize the code or can't detect that the optimization is inappropriate. In this case the programmer must know when it is safe to turn on the optimization in question.

Another example:
The linux kernel had a bug where a potentially NULL pointer was being dereferenced before a test for that pointer being null. However, in some cases it was possible to map memory to address zero, thus allowing the dereferencing to succeed. The compiler, upon noticing that the pointer was dereferenced, assumed that it couldn't be NULL, then removed the NULL test later and all the code in that branch. This introduced a security vulnerability into the code, as the function would proceed to use an invalid pointer containing attacker-supplied data. For cases where the pointer was legitimately null and the memory wasn't mapped to address zero, the kernel would still OOPS as before. So prior to optimization the code contained one bug; after it contained two, and one of them allowed a local root exploit.

CERT has a presentation called "Dangerous Optimizations and the Loss of Causality" by Robert C. Seacord which lists a lot of optimizations that introduce (or expose) bugs in programs. It discusses the various kinds of optimizations that are possible, from "doing what the hardware does" to "trap all possible undefined behaviour" to "do anything that's not disallowed".

Some examples of code that's perfectly fine until an aggressively-optimizing compiler gets its hands on it:

  • Checking for overflow

    // fails because the overflow test gets removed
    if (ptr + len < ptr || ptr + len > max) return EINVAL;
    
  • Using overflow artithmetic at all:

    // The compiler optimizes this to an infinite loop
    for (i = 1; i > 0; i += i) ++j;
    
  • Clearing memory of sensitive information:

    // the compiler can remove these "useless writes"
    memset(password_buffer, 0, sizeof(password_buffer));
    

The problem here is that compilers have, for decades, been less aggressive in optimization, and so generations of C programmers learn and understand things like fixed-size twos complement addition and how it overflows. Then the C language standard is amended by compiler developers, and the subtle rules change, despite the hardware not changing. The C language spec is a contract between the developers and compilers, but the terms of the agreement are subject to change over time and not everyone understands every detail, or agrees that the details are even sensible.

This is why most compilers offer flags to turn off (or turn on) optimizations. Is your program written with the understanding that integers might overflow? Then you should turn off overflow optimizations, because they can introduce bugs. Does your program strictly avoid aliasing pointers? Then you can turn on the optimizations that assume pointers are never aliased. Does your program try to clear memory to avoid leaking information? Oh, in that case you're out of luck: you either need to turn off dead-code-removal or you need to know, ahead of time, that your compiler is going to eliminate your "dead" code, and use some work-around for it.

翻身的咸鱼 2024-09-06 15:09:57

当错误通过禁用优化而消失时,大多数时候它仍然是你的错

我负责一个商业应用程序,主要用 C++ 编写 - 从 VC5 开始,早期移植到 VC6,现在成功移植到VC2008。在过去 10 年里,它增长到了超过 100 万行。

那时我可以确认启用积极优化时发生的单个代码生成错误。

那么我为什么要抱怨呢?因为同时出现了几十个bug让我对编译器产生了怀疑——但事实证明是我对C++标准的理解不够。该标准为编译器可能使用或不使用的优化留出了空间。

多年来,在不同的论坛上,我看到许多帖子指责编译器,最终证明是原始代码中的错误。毫无疑问,其中许多隐藏的错误需要详细了解标准中使用的概念,但仍然是源代码错误。

为什么我回复这么晚:在确认这实际上是编译器的错误之前,请停止指责编译器。

When a bug goes away by disabling optimizations, most of the time it's still your fault

I am responsible for a commercial app, written mostly in C++ - started with VC5, ported to VC6 early, now successfully ported to VC2008. It grew to over 1 Million lines in the last 10 years.

In that time I could confirm a single code generation bug thast occured when agressive optimizations where enabled.

So why am I complaining? Because in the same time, there were dozens of bugs that made me doubt the compiler - but it turned out to be my insufficient understanding of the C++ standard. The standard makes room for optimizations the compiler may or may not make use of.

Over the years on different forums, I've seen many posts blaming the compiler, ultimately turning out to be bugs in the original code. No doubt many of them obscure bugs that need a detailed understanding of concepts used in the standard, but source code bugs nonetheless.

Why I reply so late: stop blaming the compiler before you have confirmed it's actually the compiler's fault.

国际总奸 2024-09-06 15:09:57

编译器(和运行时)优化肯定会引入不期望的行为 - 但它至少应该只有在您依赖于未指定的行为(或者确实对良好做出错误的假设)时才会发生 -指定的行为)。

除此之外,编译器当然也可能存在错误。其中一些可能与优化有关,其影响可能非常微妙 - 事实上它们很可能,因为明显的错误更有可能得到修复。

假设您将 JIT 作为编译器,我已经在 .NET JIT 和 Hotspot JVM 的发布版本中看到了错误(不幸的是,目前我没有详细信息),这些错误在特别奇怪的情况下是可重现的。我不知道它们是否是由于特定的优化所致。

Compiler (and runtime) optimization can certainly introduce undesired behaviour - but it at least should only happen if you're relying on unspecified behaviour (or indeed making incorrect assumptions about well-specified behaviour).

Now beyond that, of course compilers can have bugs in them. Some of those may be around optimisations, and the implications could be very subtle - indeed they're likely to be, as obvious bugs are more likely to be fixed.

Assuming you include JITs as compilers, I've seen bugs in released versions of both the .NET JIT and the Hotspot JVM (I don't have details at the moment, unfortunately) which were reproducible in particularly odd situations. Whether they were due to particular optimisations or not, I don't know.

无敌元气妹 2024-09-06 15:09:57

结合其他帖子:

  1. 编译器的代码中偶尔会出现错误,就像大多数软件一样。 “聪明人”的说法与此完全无关,因为美国宇航局卫星和其他由聪明人构建的应用程序也存在错误。进行优化的代码与不进行优化的代码是不同的,因此,如果错误恰好出现在优化器中,那么您的优化代码确实可能包含错误,而未优化的代码则不会。

    编译器

  2. 正如 Shiny 和 New 先生所指出的那样,对于并​​发和/或计时问题不了解的代码有可能在没有优化的情况下令人满意地运行,但在优化时会失败,因为这可能会改变执行的计时。你可以将这样的问题归咎于源代码,但如果它只在优化时才会出现,有些人可能会归咎于优化。

To combine the other posts:

  1. Compilers do occasionally have bugs in their code, like most software. The "smart people" argument is completely irrelevant to this, as NASA satellites and other apps built by smart people also have bugs. The coding that does optimization is different coding from that which doesn't, so if the bug happens to be in the optimizer then indeed your optimized code may contain errors while your non-optimized code will not.

  2. As Mr. Shiny and New pointed out, it's possible for code that is naive with regard to concurrency and/or timing issues to run satisfactorily without optimization yet fail with optimization as this may change the timing of execution. You could blame such a problem on the source code, but if it will only manifest when optimized, some people might blame optimization.

涙—继续流 2024-09-06 15:09:57

仅举一个例子:几天前,有人发现 gcc 4.5 具有以下选项-foptimize-sibling-calls(由 -O2 暗示)生成一个在启动时出现段错误的 Emacs 可执行文件。

此后显然已修复

Just one example: a few days ago, someone discovered that gcc 4.5 with the option -foptimize-sibling-calls (which is implied by -O2) produces an Emacs executable that segfaults on startup.

This has apparently been fixed since.

无人接听 2024-09-06 15:09:57

我从未听说过或使用过其指令无法改变程序行为的编译器。一般来说,这是一件好事,但它确实需要您阅读手册。

我最近遇到了一个编译器指令“删除”了一个错误的情况。当然,这个错误确实仍然存在,但我有一个临时的解决方法,直到我正确修复程序为止。

I've never heard of or used a compiler whose directives could not alter the behaviour of a program. Generally this is a good thing, but it does require you to read the manual.

AND I had a recent situation where a compiler directive 'removed' a bug. Of course, the bug is really still there but I have a temporary workaround until I fix the program properly.

荆棘i 2024-09-06 15:09:57

是的。一个很好的例子是双重检查锁定模式。在 C++ 中,无法安全地实现双重检查锁定,因为编译器可以以在单线程系统中有意义但在多线程系统中无效的方式重新排序指令。完整的讨论可以在 http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf< /a>

Yes. A good example is the double-checked locking pattern. In C++ there is no way to safely implement double-checked locking because the compiler can re-order instructions in ways that make sense in a single-threaded system but not in a multi-threaded one. A full discussion can be found at http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf

白日梦 2024-09-06 15:09:57

有可能吗?不在主要产品中,但它肯定是可能的。编译器优化生成代码;无论代码来自何处(您编写它或由某些东西生成它),它都可能包含错误。

Is it likely? Not in a major product, but it's certainly possible. Compiler optimizations are generated code; no matter where code comes from (you write it or something generates it), it can contain errors.

太傻旳人生 2024-09-06 15:09:57

我在使用较新的编译器构建旧代码时遇到过几次这种情况。旧代码可以工作,但在某些情况下依赖于未定义的行为,例如不正确定义/强制转换运算符重载。它可以在 VS2003 或 VS2005 调试版本中工作,但在发布中会崩溃。

打开生成的程序集,很明显编译器刚刚删除了相关函数 80% 的功能。重写代码以不使用未定义的行为将其清除。

更明显的例子:VS2008 vs GCC

声明:

Function foo( const type & tp ); 

调用:

foo( foo2() );

其中 foo2() 返回类 type 的对象;

在 GCC 中往往会崩溃,因为在这种情况下对象没有在堆栈上分配,但 VS 做了一些优化来解决这个问题,并且它可能会起作用。

I encountered this a few times with a newer compiler building old code. The old code would work but relied on undefined behavior in some cases, like improperly defined / cast operator overload. It would work in VS2003 or VS2005 debug build, but in release it would crash.

Opening up the assembly generated it was clear that the compiler had just removed 80% of the functionality of the function in question. Rewriting the code to not use undefined behavior cleared it up.

More obvious example: VS2008 vs GCC

Declared:

Function foo( const type & tp ); 

Called:

foo( foo2() );

where foo2() returns an object of class type;

Tends to crash in GCC because the object isn't allocated on the stack in this case, but VS does some optimization to get around this and it will probably work.

月下伊人醉 2024-09-06 15:09:57

别名可能会导致某些优化出现问题,这就是编译器可以选择禁用这些优化的原因。来自维基百科

为了以可预测的方式启用此类优化,C 编程语言的 ISO 标准(包括其较新的 C99 版本)指定不同类型的指针引用同一内存位置是非法的(有一些例外)。这条规则被称为“严格别名”,可以显着提高性能[需要引用],但众所周知会破坏一些其他有效的代码。一些软件项目故意违反 C99 标准的这一部分。例如,Python 2.x 这样做是为了实现引用计数,[1] 并且需要更改 Python 3 中的基本对象结构才能实现此优化。 Linux 内核这样做是因为严格的别名会导致内联代码优化出现问题。[2]在这种情况下,当使用 gcc 编译时,会调用选项 -fno-strict-aliasing 以防止可能产生不正确代码的不需要的或无效的优化。

Aliasing can cause problems with certain optimizations, which is why compilers have an option to disable those optimizations. From Wikipedia:

To enable such optimizations in a predictable manner, the ISO standard for the C programming language (including its newer C99 edition) specifies that it is illegal (with some exceptions) for pointers of different types to reference the same memory location. This rule, known as "strict aliasing", allows impressive increases in performance[citation needed], but has been known to break some otherwise valid code. Several software projects intentionally violate this portion of the C99 standard. For example, Python 2.x did so to implement reference counting,[1] and required changes to the basic object structs in Python 3 to enable this optimisation. The Linux kernel does this because strict aliasing causes problems with optimization of inlined code.[2] In such cases, when compiled with gcc, the option -fno-strict-aliasing is invoked to prevent unwanted or invalid optimizations that could produce incorrect code.

一指流沙 2024-09-06 15:09:57

是的,编译器优化可能很危险。通常,硬实时软件项目正是出于这个原因而禁止优化。无论如何,你知道有没有没有错误的软件吗?

激进的优化可能会缓存变量,甚至对变量做出奇怪的假设。问题不仅在于代码的稳定性,而且它们还可以欺骗您的调试器。我曾多次看到调试器无法表示内存内容,因为某些优化在微寄存器中保留了变量值。

您的代码也可能发生同样的情况。优化将变量放入寄存器中,并且在完成之前不要写入该变量。现在想象一下,如果您的代码有指向堆栈中变量的指针并且有多个线程,情况会有多么不同

Yes, compiler optimizations can be dangerous. Usually hard real-time software projects forbids optimizations for this very reason. Anyway, do you know of any software with no bugs?

Aggressive optimizations may cache or even do strange assumptions with your variables. The problem is not only with the stability of your code, but also they can fool your debugger. I have seen several times a debugger failing to represent the memory contents because some optimizations retained a variable value within the registers of the micro

The very same thing can happen to your code. The optimization puts a variable into a register and do not write to the variable until it has finished. Now imagine how different things can be if your code has pointers to variables in your stack and it has several threads

和影子一齐双人舞 2024-09-06 15:09:57

当然,理论上这是可能的。但是,如果您不相信这些工具能够完成其应有的任务,那么为什么要使用它们呢?但马上,任何从立场上争论的人

“编译器是由聪明人构建的
并做聪明的事情”,因此,可以
永远不会出错。

正在做出愚蠢的论点。

因此,除非您有理由相信编译器正在这样做,否则为什么要对此做出姿态呢?

It's theoretically possible, sure. But if you don't trust the tools to do what they are supposed to do, why use them? But right away, anyone arguing from the position of

"compilers are built by smart people
and do smart things" and thus, can
never go wrong.

is making a foolish argument.

So, until you have reason to believe that a compiler is doing so, why posture about it?

提笔书几行 2024-09-06 15:09:57

我当然同意这样说是愚蠢的,因为编译器是由“聪明人”编写的,因此它们是绝对正确的。聪明人还设计了兴登堡大桥和塔科马海峡大桥。即使编译器编写者确实是最聪明的程序员之一,但编译器也是最复杂的程序之一。当然他们也有缺陷。

另一方面,经验告诉我们,商业编译器的可靠性非常高。我已经多次有人告诉我,程序无法运行的原因一定是因为编译器中的错误,因为他已经非常仔细地检查了它,并且他确信它是 100% 正确的......然后我们发现实际上是程序有错误,而不是编译器有错误。我试图回想一下我个人遇到过的一些事情,我确信这是编译器中的一个错误,但我只能回忆起一个例子。

所以总的来说:相信你的编译器。但他们有错吗?当然。

I certainly agree that it's silly to say the because compilers are written by "smart people" that they are therefore infallible. Smart people designed the Hindenberg and the Tacoma Narrows Bridge, too. Even if it's true that compiler-writers are among the smartest programmers out there, it's also true that compilers are among the most complex programs out there. Of course they have bugs.

On the other hand, experience tells us that the reliability of commercial compilers is very high. I've had many many times that someone told me that the reason why is program doesn't work MUST be because of a bug in the compiler because he has checked it very carefully and he is sure that it is 100% correct ... and then we find that in fact the program has an error and not the compiler. I'm trying to think of times that I've personally run across something that I was truly sure was an error in the compiler, and I can only recall one example.

So in general: Trust your compiler. But are they ever wrong? Sure.

活泼老夫 2024-09-06 15:09:57

它有可能发生。它甚至影响了 Linux

It can happen. It has even affected Linux.

删除→记忆 2024-09-06 15:09:57

我记得,早期的 Delphi 1 有一个错误,即 Min 和 Max 的结果颠倒了。仅当浮点值在 dll 中使用时,某些浮点值还存在一个不明显的错误。诚然,已经十多年了,我的记忆可能有点模糊了。

As I recall, early Delphi 1 had a bug where the results of Min and Max were reversed. There was also an obscure bug with some floating point values only when the floating point value was used within a dll. Admittedly, it has been more than a decade, so my memory may be a bit fuzzy.

赏烟花じ飞满天 2024-09-06 15:09:57

我在 .NET 3.5 中遇到了一个问题,如果您使用优化进行构建,将另一个变量添加到一个方法,该方法的命名类似于同一范围内相同类型的现有变量,那么两个变量(新变量或旧变量)之一将不会在运行时有效,并且对无效变量的所有引用都将替换为对另一个变量的引用。

因此,例如,如果我有 MyCustomClass 类型的 abcd 和 MyCustomClass 类型的 abdc,并且设置 abcd.a=5 和 abdc.a=7,那么这两个变量都将具有属性 a=7。要解决此问题,应删除这两个变量,编译程序(希望没有错误),然后重新添加它们。

我想我在使用 .NET 4.0 和 C# 做 Silverlight 应用程序时也遇到过几次这个问题。在我上一份工作中,我们经常在 C++ 中遇到这个问题。可能是因为编译需要 15 分钟,所以我们只会构建我们需要的库,但有时优化后的代码与之前的构建完全相同,即使添加了新代码并且没有报告构建错误。

是的,代码优化器是由聪明人构建的。它们也非常复杂,因此出现错误是很常见的。我建议全面测试大型产品的任何优化版本。通常,有限用途产品不值得完整发布,但仍应对其进行总体测试,以确保它们正确执行常见任务。

I have had a problem in .NET 3.5 if you build with optimization, add another variable to a method which is named similarly to an existing variable of the same type in the same scope then one of the two (new or old variable) will not be valid at runtime and all references to the invalid variable are replaced with references to the other.

So, for example, if I have abcd of MyCustomClass type and I have abdc of MyCustomClass type and I set abcd.a=5 and abdc.a=7 then both variables will have property a=7. To fix the issue both variables should be removed, the program compiled (hopefully without errors) then they should be re-added.

I think I have run into this problem a few times with .NET 4.0 and C# when doing Silverlight applications also. At my last job we ran into the problem quite often in C++. It might have been because the compilations took 15 minutes so we would only build the libraries we needed, but sometimes the optimized code was exactly the same as the previous build even though new code had been added and no build errors had been reported.

Yes, code optimizers are built by smart people. They are also very complicated so having bugs is common. I suggest fully testing any optimized release of a large product. Usually limited use products are not worth a full release, but they should still be generally tested to make sure they perform their common tasks correctly.

友欢 2024-09-06 15:09:57

编译器优化可以揭示(或激活)代码中休眠(或隐藏)的错误。您的 C++ 代码中可能存在您不知道的错误,只是您没有看到它。在这种情况下,它是一个隐藏或休眠的错误,因为代码的该分支没有执行[足够的次数]。

代码中出现错误的可能性比编译器代码中出现错误的可能性大得多(数千倍):因为编译器经过了广泛的测试。通过 TDD 加上几乎所有自发布以来使用过它们的人!)。因此,几乎不可能一个错误是由您发现的,而不是被其他人使用数十万次才发现的。

休眠错误或隐藏错误只是尚未向程序员揭示的错误。能够声称他们的 C++ 代码没有(隐藏的)错误的人非常罕见。它需要 C++ 知识(很少有人能做到这一点)和对代码的广泛测试。这不仅与程序员有关,还与代码本身(开发风格)有关。容易出现错误取决于代码的特征(测试的严格程度)或/和程序员的特征(测试的纪律程度以及对 C++ 和编程的了解程度)。

安全性+并发错误:如果我们将并发性和安全性视为错误,情况会更糟。但毕竟,这些“都是”错误。编写在并发性和安全性方面首先没有错误的代码几乎是不可能的。这就是为什么代码中总是存在错误,这些错误可能会在编译器优化中被发现(或被遗忘)。

Compiler optimization can reveal (or activate) dormant (or hidden) bugs in your code. There may be a bug in your C++ code that you don't know of, that you just don't see it. In that case, it is a hidden or dormant bug, because that branch of the code is not executed [enough number of times].

The likelihood of a bug in your code is much bigger (thousands of times more) than a bug in the compiler's code: Because the compilers are tested extensively. By TDD plus practically by all people who have use them since their release!). So it is virtually unlikely that a bug is discovered by you and not discovered by literally hundreds of thousands of times it is used by other people.

A dormant bug or hidden bug is just a bug that is not revealed itself to the programmer yet. People who can claim that their C++ code does not have (hidden) bugs are very rare. It requires C++ knowledge (very few can claim for that) and extensive testing of the code. It is not just about the programmer, but about the code itself (the style of development). Being bug-prone is in the character of the code (how rigorously it is tested) or/and the programmer (how disciplined is in test and how well knows C++ and programming).

Security+Concurrency bugs: This is even worse if we include concurrency and security as bugs. But after all, these 'are' bugs. Writing a code that is in the first place bug-free in terms of concurrency and security is almost impossible. That's why there is always already a bug in the code, which can be revealed (or forgotten) in compiler optimization.

趁微风不噪 2024-09-06 15:09:57

如果您编译的程序具有良好的测试套件,则可以启用更多、更积极的优化。然后就可以运行该套件并更加确定程序运行正确。此外,您还可以准备自己的测试,与您计划在生产中执行的测试紧密匹配。

确实,任何大型程序都可能有(并且可能确实有)一些错误,这些错误与您使用哪些开关来编译它无关。

More, and more aggressive optimizations could be enabled if the program you compile has a good testing suite. Then it is possible to run that suite and be somewhat more sure the program operates correctly. Also, you can prepare your own tests that match closely that do you plan to do in production.

It is also true that any large program may have (and probably indeed has) some bugs independently on which switches do you use to compile it.

南城追梦 2024-09-06 15:09:57

我从事一个大型工程应用程序的工作,我们时不时地会看到客户报告的仅发布崩溃和其他问题。我们的代码有 37 个文件(超出了
6000),我们在文件顶部有这个,关闭优化来修复此类崩溃:(

#pragma optimize( "", off)

我们使用 Microsoft Visual C++ 本机,2015,但这对于几乎任何编译器都是如此,除了 Intel Fortran 2016 update 2,其中我们还没有关闭任何优化。)

如果您搜索 Microsoft Visual Studio 反馈站点,您也可以在那里找到一些优化错误。我们偶尔会记录一些我们的问题(如果您可以用一小部分代码轻松地重现它并且您愿意花时间)并且它们确实得到修复,但遗憾的是其他问题又被引入。 smiles

编译器是人们编写的程序,任何大型程序都会有错误,相信我。编译器优化选项肯定存在错误,并且打开优化肯定会在程序中引入错误。

I work on a large engineering application, and every now and then we see release only crashes and other problems reported by clients. Our code has 37 files (out of around
6000) where we have this at the top of the file, to turn off optimization to fix such crashes:

#pragma optimize( "", off)

(We use Microsoft Visual C++ native, 2015, but it is true for just about any compiler, except maybe Intel Fortran 2016 update 2 where we have not yet turned of any optimizations.)

If you search through the Microsoft Visual Studio feedback site you can find some optimization bugs there as well. We occasionally log some of ours (if you can reproduce it easily enough with a small section of code and you are willing to take the time) and they do get fixed, but sadly others get introduced again. smiles

Compilers are programs written by people, and any big program has bugs, trust me on that. The compiler optimization options most certainly has bugs and turning on optimization can certainly introduce bugs in your program.

过期情话 2024-09-06 15:09:57

您可以想象对程序执行的任何操作都会引入错误。

Everything that you can possibly imagine doing with or to a program will introduce bugs.

第七度阳光i 2024-09-06 15:09:57

由于详尽的测试以及实际 C++ 代码的相对简单(C++ 有不到 100 个关键字/运算符),编译器错误相对较少。糟糕的编程风格通常是他们遇到的唯一问题。通常编译器会崩溃或产生内部编译器错误。这条规则的唯一例外是 GCC。 GCC,尤其是旧版本,在 O3 甚至其他 O 级别中启用了许多实验性优化。 GCC 还针对如此多的后端,因此在中间表示中留下了更多的错误空间。

Because of exhaustive testing and the relative simplicity of actual C++ code (C++ has under 100 keywords / operators) compiler bugs are relatively rare. Bad programming style often is the only thing encounters them. And usually the compiler will crash or produce an internal compiler error instead. The only exception to this rule is GCC. GCC, especially older versions, had a lot of experimental optimizations enabled in O3 and sometimes even the other O levels. GCC also targets so many backends that this leaves more room for bugs in their intermediate representation.

单挑你×的.吻 2024-09-06 15:09:57

昨天我在 .net 4 上遇到了一个问题,看起来像......

double x=0.4;
if(x<0.5) { below5(); } else { above5(); }

并且它会调用 above5(); 但如果我实际上在某个地方使用 x ,它会调用below5();

double x=0.4;
if(x<0.5) { below5(); } else { System.Console.Write(x); above5(); }

代码不完全相同,但相似。

I had a problem with .net 4 yesterday with something that looks like...

double x=0.4;
if(x<0.5) { below5(); } else { above5(); }

And it would call above5(); But if I actually use x somewhere, it would call below5();

double x=0.4;
if(x<0.5) { below5(); } else { System.Console.Write(x); above5(); }

Not the exact same code but similar.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文