【问题标题】:C++: Performance of std::vector vs std::list [duplicate]C++:std::vector 与 std::list 的性能 [重复]
【发布时间】:2018-06-06 23:23:37
【问题描述】:

我有以下代码来分析各种 N 的 std::vector 性能与 std::list 的性能。

void vectorPerf(size_t n)
{
   std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
   std::vector<size_t> cache;
   for (size_t i = 0; i < n; i++) {
      cache.push_back(i);
   }
   std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now();

   auto duration = std::chrono::duration_cast<std::chrono::microseconds>(t2-t1).count();

   std::cout << duration << " us." << "\n";

   return;
}

void listPerf(size_t n)
{
   std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
   std::list<size_t> cache;
   for (size_t i = 0; i < n; i++) {
      cache.push_back(i);
   }
   std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now();

   auto duration = std::chrono::duration_cast<std::chrono::microseconds>(t2-t1).count();

   std::cout << duration << " us." << "\n";

   return;
}

int main(int argc, char** argv)
{

   if (argc != 2) {
      return -1;
   }
   std::stringstream ss(argv[1]);
   size_t n;
   ss >> n;
   vectorPerf(n);
   std::cout << "\n";
   listPerf(n);
   std::cout << "\n";
}

对于同一组 N 的多次运行,我得到的结果与以下数字的顺序一致:

N       std::vector  std::list
1000    46           116
2000    85           217
5000    196          575
10000   420          1215
100000  3188         10497
1000000 34489        114330

我想知道为什么 std::list 的性能总是比 std::vector 差。对于 std::vector,我预计性能会被摊销 O(N),因为支撑 std::vector 的内部对象需要不时调整大小。由于我所做的只是将一个元素插入到列表的末尾,因此我希望 std::list 为 O(1)。所以这表明 std::list 会比 std::vector 做得更好,但事实恰恰相反。

如果有人能解释为什么会发生这种情况,我将不胜感激。我在 2015 MBP 上使用 g++ 在 MacOS 10.12.6 上编译。

谢谢。

编辑:我现在明白 std::vector::push_back() 是摊销 O(1)。但是,我不清楚的是为什么 std::list::push_back() 的性能始终比 std::vector::push_back() 差?

【问题讨论】:

  • 谢谢。链接表明 std::vector::push_back() 摊销 O(1)。这仍然无法向我解释为什么 std::list::push_back() 比 std::list::push_back() 差这么多
  • 因为每次插入列表都需要分配内存,而添加到向量通常不需要。
  • 拥有不同容器的原因之一是因为不同的操作没有相同的性能。因此,根据使用情况(和数据的大小),必须选择合适的容器。简单的规则是在大多数情况下使用std::vector
  • 重复push_back()s 的影响是代码最终写入连续的内存地址。这就是向量的全部意义所在。现代 CPU 非常非常擅长按顺序访问连续的内存地址。使用 std::list,每个元素最终都会被分配到随机的内存块中,因此性能配置文件更差。
  • 这里还有一些基准测试:baptiste-wicht.com/posts/2012/12/…

标签: c++ performance c++11 stdvector stdlist


【解决方案1】:

插入到末尾的向量是摊销常数(即摊销 O(1))而不是摊销 O(n),其中列表始终为 O(n),在这种情况下向量容量增长速度快于其实际大小,它分配额外的空间,然后在执行过程中填充,因此不需要为每个 push_back 分配空间。

【讨论】:

  • 来自 cppref.com:“std::list 是一个容器,它支持从容器中的任何位置以恒定时间插入和删除元素。”这是否与您的“列表始终为 O(n)”声明相矛盾?
猜你喜欢
  • 2014-05-23
  • 2010-09-19
  • 2012-06-30
  • 1970-01-01
  • 1970-01-01
  • 2017-03-22
  • 2012-05-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多