2

我使用 gprof 和报告分析了我的代码,如果不是全部,前 20 名左右的大部分内容都与向量有关

Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  ms/call  ms/call  name    
 14.71      0.05     0.05  3870399     0.00     0.00  std::vector<bool, std::allocator<bool> >::size() const
 11.76      0.09     0.04 10552897     0.00     0.00  std::_Bit_reference::_Bit_reference(unsigned long*, unsigned long)
 11.76      0.13     0.04  7890323     0.00     0.00  std::_Bit_const_iterator::_Bit_const_iterator(std::_Bit_iterator const&)
  5.88      0.15     0.02 10089215     0.00     0.00  std::_Bit_iterator::operator*() const
  5.88      0.17     0.02  6083600     0.00     0.00  std::vector<bool, std::allocator<bool> >::operator[](unsigned int)
  5.88      0.19     0.02  3912611     0.00     0.00  std::vector<bool, std::allocator<bool> >::end() const
  5.88      0.21     0.02                             std::istreambuf_iterator<char, std::char_traits<char> > std::num_get<char, std::istreambuf_iterator<char, std::char_traits<char> > >::_M_extract_int<unsigned long long>(std::istreambuf_iterator<char, std::char_traits<char> >, std::istreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, std::_Ios_Iostate&, unsigned long long&) const
  2.94      0.22     0.01  6523499     0.00     0.00  std::_Bit_reference::operator bool() const
  2.94      0.23     0.01  3940406     0.00     0.00  std::vector<bool, std::allocator<bool> >::begin() const
  2.94      0.24     0.01  2807828     0.00     0.00  std::_Bit_iterator::operator++()
  2.94      0.25     0.01   146917     0.00     0.00  std::_Bit_iterator_base::_M_incr(int)
  2.94      0.26     0.01   121706     0.00     0.00  std::__miter_base<unsigned long*, false>::__b(unsigned long*)
  2.94      0.27     0.01    46008     0.00     0.00  std::_Bvector_base<std::allocator<bool> >::~_Bvector_base()
  2.94      0.28     0.01    22596     0.00     0.00  std::_Bit_iterator std::__copy_move<false, false, std::random_access_iterator_tag>::__copy_m<std::_Bit_iterator, std::_Bit_iterator>(std::_Bit_iterator, std::_Bit_iterator, std::_Bit_iterator)
  2.94      0.29     0.01     4525     0.00     0.05  integer::operator+(integer)
  2.94      0.30     0.01     1382     0.01     0.01  void std::_Destroy<unsigned int*, unsigned int>(unsigned int*, unsigned int*, std::allocator<unsigned int>&)
  2.94      0.31     0.01                             std::string::size() const
  2.94      0.32     0.01                             std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()
  2.94      0.33     0.01                             std::locale::locale()
  2.94      0.34     0.01                             __dynamic_cast

这是一个好兆头,因为这意味着我的其余函数非常有效,或者从 vector< bool > 访问值真的很慢?

我正在用 gcc -std=c++0x 编译

4

5 回答 5

8

vector<bool>不存储bools。它基本上是一个位域。您正在为修改单个值所花费的时间付出代价。

如果运行时性能是一个问题,请考虑vector<char>deque<bool>替代。

于 2011-06-23T22:47:08.063 回答
2

因为这意味着我的其余函数非常有效,或者从向量访问值真的很慢?

由于“慢”和“高效”是相对值,这本质上是一种毫无意义的区分。最客观的解读报告的方式是:

由于 std::vector' 操作占用了最重要的时间,这应该是让代码更快的开始点

请注意,std::vector<bool>它通常比 a 慢一点,std::vector<int>因为它不存储实数bool,而是一组位掩码(即理想情况下,每个条目只需要一个位)。这样可以节省空间,但速度较慢。如果您需要它更快,请尝试改用std::vector<int>(或char,..,取决于您的需要)。

我怀疑std::vector<bool>调试版本可能会受到很大影响,所以如果你还没有这样做,请尝试一些优化标志(你总是应该进行分析)。

于 2011-06-23T22:47:35.393 回答
1

vector<bool>实际上是一个模板特化,其中每个bool值都存储为一个位。但是,不可能像使用int或仅使用 "normal"的方式直接使用单个位bool。因此,其中使用的算法vector<bool>与“正常”非常不同vector<>,为了尽可能地保留vector接口,它可能会返回代理对象,当您调用诸如operator[]. 这可能会影响 gprof 报告中的结果,具体取决于编译器的配置方式和相关代码。

于 2011-06-23T22:46:26.773 回答
1

我会说它很臭,因为你 14.71% 的时间都花在了做事上vector<bool>::size()!?!大小可能是给定的。

如果您事先知道大小,请尝试减少对 size() 的调用次数或使用固定大小的向量:bitset

阅读问题更新后进行编辑:

强制更改:(g++ --std=c++0x -g -O3这是两个错字和优化标志,重新配置!);模板类大量使用内联,这反过来又启用了无数其他优化。加速的顺序很容易 10 倍

于 2011-06-23T22:49:46.300 回答
1

它对您的程序有很多说明吗?除了vector<bool>业务之外,它基本上什么也没告诉你。

您亲眼目睹了gprof的问题

假设您知道某个函数具有较高的“自时间”,这意味着程序计数器在其中被采样了很多次,但它不是您编写或可以修改的函数。

你唯一能做的就是尽量少调用它,或者尽量少调用调用它的例程,或者尽量少调用那个例程,然后你就只能猜测它在哪里了。

gprof还尝试通过猜测例程的包含时间、调用次数以及调用图来帮助您。如果没有递归,并且您只有十几个函数,并且您没有进行任何 I/O,那么这可能会有所帮助。

有一种稍微不同的方法,体现在像Zoom这样的分析器中。不要只对程序计数器进行采样,而是对整个调用堆栈进行采样。为什么?因为负责所花费时间的代码行在那段时间都在堆栈上,只是要求引起注意。

在挂钟时间对调用堆栈进行采样并告诉您大多数时间在堆栈中找到哪些代码行的分析器是最有效的。更有效的是,如果您可以查看堆栈的各个样本,因为这还可以告诉您为什么调用这些行,而不仅仅是调用了多少,因此很容易判断您是否真的不需要它们。

于 2011-06-24T02:25:38.163 回答