编译器是否允许删除像 Intel C++ 这样的无限循环?编译器用-O2吗?

发布于 2024-09-15 17:19:57 字数 781 浏览 7 评论 0原文

以下测试代码在 VS 中无论是调试还是发布都能正确运行,在 GCC 中也能正确运行。对于带调试的 ICC,它也能正确执行,但在启用优化时则无法正确执行 (-O2)。

#include <cstdio>

class tClassA{
public:
  int m_first, m_last;

  tClassA() : m_first(0), m_last(0) {}
  ~tClassA() {}

  bool isEmpty() const {return (m_first == m_last);}
  void updateFirst() {m_first = m_first + 1;}
  void updateLast() {m_last = m_last + 1;}
  void doSomething() {printf("should not reach here\r\n");}
};

int main() {
  tClassA q;
  while(true) {
    while(q.isEmpty()) ;
    q.doSomething();
  }
  return 1;
}

它应该停止在 while(q.isEmpty()) 处。然而,当在 ICC(发布)下启用 -O2 时,它开始无限地“doSomething”。

由于这是单线程程序并且 isEmpty() 应该被评估为true,我找不到 ICC 应该以这种方式运行的原因?我错过了什么吗?

The following testing code does correctly in VS either with debug or release, and also in GCC. It also does correctly for ICC with debug, but not when optimization enabled (-O2).

#include <cstdio>

class tClassA{
public:
  int m_first, m_last;

  tClassA() : m_first(0), m_last(0) {}
  ~tClassA() {}

  bool isEmpty() const {return (m_first == m_last);}
  void updateFirst() {m_first = m_first + 1;}
  void updateLast() {m_last = m_last + 1;}
  void doSomething() {printf("should not reach here\r\n");}
};

int main() {
  tClassA q;
  while(true) {
    while(q.isEmpty()) ;
    q.doSomething();
  }
  return 1;
}

It is supposed to stop at while(q.isEmpty()). When -O2 enabled under ICC (release), however, it starts to "doSomething" infinitely.

Since this is single-threaded program and isEmpty() should be evaluated as true, I can find no reason the ICC should behave in this way? Do I miss anything?

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

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

发布评论

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

评论(7

溺渁∝ 2024-09-22 17:19:57

由于 while (q.isEmpty()) ; 循环不包含可能导致外部可见副作用的语句,因此整个循环正在被优化以使其不存在。出于同样的原因:

for (int i = 0; i < 10; i++)
    ;

只要 i 不是 易失性(存储到 易失性 对象是程序的“外部可见”效果)。

在C语言中,是否允许通过这种方式优化掉无限循环实际上是一个争论的焦点(我不知道C++的情况如何)。据我所知,在这个问题上一直没有达成共识——聪明而有知识的人都站在了一边。

Because the while (q.isEmpty()) ; loop contains no statements that can ever cause an externally-visible side-effect, the entire loop is being optimised out of existence. It's the same reason that:

for (int i = 0; i < 10; i++)
    ;

could be optimised out of existence, as long as i was not volatile (stores to volatile objects are part of the "externally visible" effects of a program).

In the C language, it is actually an outstanding bone of contention as to whether an infinite loop is allowed to be optimised away in this manner (I do not know what the situation is with C++). As far as I know, a consensus has never been reached on this issue - smart and knowledgeable people have taken both sides.

忆依然 2024-09-22 17:19:57

这听起来确实像一个错误。这是一个(非常疯狂的)猜测,关于什么推理可能会导致它......

内联后,它看到:

while (q.m_first == q.m_last) /* do nothing */ ;
do_something();

并且 的任何序列不重复执行任何操作; do some 可以简单地翻译为“做某事”。如果重复的部分是无限的(如本例所示),则该值会下降。但也许他们不会在故意无限循环的示例上测试其编译;-)。

It sure sounds like a bug. Here's a (pretty wild) guess about what reasoning might have lead to it...

After inlining, it sees:

while (q.m_first == q.m_last) /* do nothing */ ;
do_something();

and any sequence of do nothing repeatedly ; do something can be translated to simply "do something". This falls down if the repeated part is endless (as in this case). But perhaps they don't test their compiling on examples that intentionally have endless looping ;-).

淡水深流 2024-09-22 17:19:57

您构建和运行的实际代码是否有可能在 while(q.isEmpty()) 之后缺少分号?这肯定会导致下一行被无限调用。

Any chance the actual code that you built and ran was missing the semicolon after while(q.isEmpty()) ? That would certainly result in the next line being called infinitely.

二智少女 2024-09-22 17:19:57

顺便说一句,这个版本的 icc 可以满足您的需求。也就是说,它从不调用doSomething()

[9:41am][wlynch@computer /tmp] icc --version
icc (ICC) 11.0 20081105

As a slight aside, this version of icc does what you want. That is, it never calls doSomething().

[9:41am][wlynch@computer /tmp] icc --version
icc (ICC) 11.0 20081105
初见 2024-09-22 17:19:57

C++ 标准允许删除没有副作用的循环,即使它们不会终止:

普遍认为
允许转变很重要
潜在的非终止循环
(例如,通过合并两个循环
迭代相同的潜在可能性
无限集,或者通过消除
无副作用循环),即使这样
可能没有其他合理的理由
第一个循环永远不会的情况
终止。
http://www.open-std。 org/jtc1/sc22/wg21/docs/papers/2007/n2429.htm

请参阅此处的讨论:
http://blog.regehr.org/archives/161

The C++ standard allows loops without side-effects to be removed, even if they don't terminate:

It is generally felt that it is
important to allow the transformation
of potentially non-terminating loops
(e.g. by merging two loops that
iterate over the same potentially
infinite set, or by eliminating a
side-effect-free loop), even when that
may not otherwise be justified in the
case in which the first loop never
terminates.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2429.htm

See the discussion here:
http://blog.regehr.org/archives/161

一页 2024-09-22 17:19:57

最好的选择是将生成的二进制步骤放入其中并反汇编 main 函数并查看生成的程序集。并不是说您将能够看到错误,但您可以看到某些内容是否被优化。

Your best bet is to take the resulting binary step into it and dis-assemble the main function and see what assembly was generated. Not saying you will be able to see a bug, but you can see if something was optimized out.

烟酉 2024-09-22 17:19:57

我认为这可能是你的 gcc 版本。我在 4.4.2 下编译了你的程序,它的工作原理与应有的完全一样。

I think it may have been your version of gcc. I compiled your prog under 4.4.2 and it worked exactly as it should have.

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