【问题标题】:C++ STL optimization warning: problem with the code or something more sinister?C++ STL 优化警告:代码有问题还是更险恶?
【发布时间】:2011-01-29 03:08:04
【问题描述】:

我有一个正在处理的程序,我正在从使用数组切换到向量,但是我遇到了一个问题。我已将其简化为:

#include <vector>

class A {
public:
A(void);
~A(void);

private:
std::vector< std::vector<int> > a;
};

A::A(void) : a() {}

A::~A(void) {}

这会从 g++ 发出以下警告(标志:-O2 -Wunsafe-loop-optimizations,Ubuntu 10.04 x86_64 上的版本 4.4.3 (Ubuntu 4.4.3-4ubuntu5)):

/usr/include/c++/4.4/bits/stl_construct.h:在析构函数“A::~A()”中: /usr/include/c++/4.4/bits/stl_construct.h:92:警告:无法优化循环,循环计数器可能溢出

那么,Leo,什么给了?向量类不应该跟踪它有多少元素吗?那么,如果那是被引用的“计数器”,它怎么会溢出呢? (编辑:这个问题或多或少在下面得到了回答,但是,对我来说,只留下了推论:为什么要警告库中的循环无法优化?为什么我应该,最终用户,会担心吗?)

编辑:

这是被投诉的相关代码(但是,正如我在 cmets 中所说,我不明白为什么应该考虑我,因为我不需要担心库的实现):

/**
 * Destroy the object pointed to by a pointer type.
 */
template<typename _Tp>
  inline void
  _Destroy(_Tp* __pointer)
  { __pointer->~_Tp(); }

template<bool>
  struct _Destroy_aux
  {
    template<typename _ForwardIterator>
      static void
      __destroy(_ForwardIterator __first, _ForwardIterator __last)
    {
      for (; __first != __last; ++__first) // <-- this is line 92
        std::_Destroy(&*__first);
    }
  };

更新:

好的,我期待两个答案之一:我在 my 代码中做了一些微妙的异常(但不一定是错误的),或者该消息非常字面上告诉我循环在库中指出无法优化。感激地放心,它不是前者,是从它自己的库中报告警告预期来自 gcc 的行为,还是应该报告? (似乎不值得再提出一个问题来问这个问题,所以请原谅这个新问题。)

更新 2:

好的,感谢所有信息人员:那么这是编译器中的一个错误。仅供参考,我从 GNU(版本 4.5.2)编译了一个不受干扰的编译器和 c++ 库,它不显示这种行为(库代码是相同的),所以我想这样的警告不应该出现。再次感谢大家。

【问题讨论】:

  • : a() 在构造函数中的作用是什么?如果删除它有什么变化吗?
  • 好吧,我想它可能会让学究远离! (除非我弄错了,我很可能是......)不,当我删除它时仍然是同样的警告。
  • 你真的需要使用-Wunsafe-loop-optimizations吗?旧的 --funroll-loops 对您来说还不够好吗?为什么没有-O3?
  • @Zorawar: 如果您在这里复制/bits/stl_construct.h 中的内容并标识92 行(注意,仅复制所涉及的函数就足够了,无需粘贴整个标题)。这是我的一个小障碍:我不是千里眼。
  • @Matthieu:嗯,这就是我真正要问的问题:gcc 是在抱怨我的代码还是 GNU 的代码?我不必粘贴 bits/stl_construct.h 中的内容,因为我永远不必担心里面有什么。不过,我会添加它以防万一我很愚蠢。

标签: c++ optimization gcc stl vector


【解决方案1】:

这只是意味着编译器无法证明它不会溢出。由于您启用了 -O2,因此您要求对循环进行某些优化。只有在可以证明这些条件的情况下,才能进行其中的一些优化。您要求的编译器警告告诉您某些循环无法优化(我猜这可能是向量底层数组的内联破坏循环。

我认为你可以忽略这个警告。但是我确实想知道您为什么首先在编译选项中要求此警告?

【讨论】:

  • 我要求只是迂腐:无论如何我都会忽略这个警告,因为我的理解是,如果它不能优化怎么办?但是,像这种基本情况,gcc不应该冷静一下吗?
  • 如果您不要求它显示此警告,它确实会冷静下来;)
  • 但我想要一个只有冷酷无情的程序才能给予的保证,该死的!那么,这只是gcc抱怨自己的库的一个例子吗? (或者,也许,库哀叹其编译器的迂腐。)
  • @Zorawar:你读过 GCC 给你的警告吗?它不是“抱怨自己的图书馆”。它告诉你“我无法做到你所要求的”。您要求它优化循环,然后要求它在发现任何无法优化的循环时警告您。然后它找到了一个它无法优化的循环,就像你要求它那样,它警告你它发现了一个它无法优化的循环。
  • @jalf:我想我读过它。但是告诉我它不能优化我不应该担心的循环又有什么意义呢?
【解决方案2】:

我想我终于明白了编译器的暗示。

循环不是在整数上执行的,所以我认为编译器试图告诉的是它实际上无法计算循环将执行多少次。

您使用的警告实际上应该通常与-funsafe-loop-optimizations 结合在一起,来自gcc optimize options page:

-funsafe-loop-optimizations 如果给定,循环优化器将假定循环索引不会溢出,并且具有非平凡退出条件的循环不是无限的。即使循环优化器本身无法证明这些假设是有效的,这也可以实现更广泛的循环优化。使用 -Wunsafe-loop-optimizations`,编译器会在发现这种循环时发出警告。

我必须承认我认为编译器总是被允许假设一个循环最终会终止,因为推断它需要解决在一般情况下无法解决的停止问题。

但是这里,如果你看函数,不可能证明循环不会溢出,原因很简单:first 到last 的范围可能是不正确的。

  • 可能是first &gt; last(注意:不能比较前向迭代器)
  • 可能是对齐*关闭,指针上 (T*) ++ 等效于 += sizeof(T),并且因为使用 != 而不是 &lt;,first 可能会跳过last 如果是这样的话

当然,这在实践中永远不会发生,因为库编写者已确保他们向方法传递了正确的参数,但编译器无法推断出它,因此会发出警告。

我仍然感到惊讶,我原以为编译器编写者会故意忽略他们自己的库发出的警告,因为他们不得不使用那些轻微的手,以免我们不这样做。

(*) 这里不是内存对齐,我只是在暗示一个事实,一个错误校准的 T* 实际上可能指向一个 T 并且一次推进 sizeof(T) 永远不会真正让它永远稳定下来回到T 边界...

【讨论】:

  • 是的,编译器会告诉用户关于库的任何事情,这很奇怪,因为这不是用户关心的问题:我为什么要关心编译器是否无法优化 那个循环?我担心 my 代码中可能有一些微妙的不好(尽管不一定是错误的),但似乎没有。感谢您的意见。
【解决方案3】:

你的代码是无辜的。

警告必须是 gcc 编译器(至少是您当前拥有的版本)或它们使用的 stl 实现的错误。您应该不会收到关于正确代码的警告。

【讨论】:

    猜你喜欢
    • 2012-01-18
    • 2021-12-11
    • 1970-01-01
    • 1970-01-01
    • 2011-01-17
    • 1970-01-01
    • 2017-08-29
    • 1970-01-01
    • 2012-03-26
    相关资源
    最近更新 更多