【问题标题】:Timing of std::find() in standard containers标准容器中 std::find() 的计时
【发布时间】:2021-11-03 00:05:43
【问题描述】:

我正在尝试为std::unordered_setstd::setstd::vector 计时std::find() 函数。但结果对我来说很奇怪。

unordered_set 中查找元素比在vector 中查找元素需要更多时间。

理论上,搜索的时间复杂度应该是O(1) 对应unordered_setO(n) 对应vector

那么,在unordered_set 中查找元素是否比vector 更快?我是不是做错了什么?

这是我的代码:

#include <iostream>
#include <vector>
#include <set>
#include <unordered_set>
#include <algorithm>
#include <random>
#include <numeric>
#include <chrono>

using namespace std;

int main(int argc, char** argv)
{
  int size = 10000;
  vector<int> items(size);
  iota(items.begin(), items.end(), 0);
  auto rng = default_random_engine {};
  shuffle(items.begin(), items.end(), rng);
  
  chrono::steady_clock::time_point begin, end;  
  unordered_set<int> unsetInt;
  set<int> setInt;
  vector<int> vecInt;

  // add element
  for(auto it = items.begin(); it != items.end(); it++)
  {
    unsetInt.insert(*it);
    setInt.insert(*it);
    vecInt.push_back(*it);
  }
  
  // lookup time for unordered_set<int>
  begin = chrono::steady_clock::now();
  for(int i = 0; i < size; i++)
  {
    find(unsetInt.begin(), unsetInt.end(), i);
  }
  end = chrono::steady_clock::now();
  cout << "lookup time for unordered_set<int>: " << 
  chrono::duration_cast<chrono::milliseconds>(end-begin).count() << "ms" << endl;
  
  // lookup time for set<int>
  begin = chrono::steady_clock::now();
  for(int i = 0; i < size; i++)
  {
    find(setInt.begin(), setInt.end(), i);
  }
  end = chrono::steady_clock::now();
  cout << "lookup time for set<int>: " << 
  chrono::duration_cast<chrono::milliseconds>(end-begin).count() << "ms" << endl;
  
  // lookup time for vector<int>
  begin = chrono::steady_clock::now();
  for(int i = 0; i < size; i++)
  {
    find(vecInt.begin(), vecInt.end(), i);
  }
  end = chrono::steady_clock::now();
  cout << "lookup time for vector<int>: " << 
  chrono::duration_cast<chrono::milliseconds>(end-begin).count() << "ms" << endl;

  return 0;
}

这是我的结果:

lookup time for unordered_set<int>: 2293ms
lookup time for set<int>: 3080ms
lookup time for vector<int>: 1311ms

【问题讨论】:

  • 这些 Big-O 语句总是以“有一个n_0 所以所有n &gt; n_0 ...”开头。您的n 可能远低于该限制。至少,您需要增加n 值的趋势,而不仅仅是您假设“它会足够大”的趋势。

标签: c++ stl big-o


【解决方案1】:

我是不是做错了什么?

你做到了。

std::find 通过遍历所有元素来执行搜索。它对std::[unordered_]set和其他容器没有任何特殊的了解。

要在集合中正确搜索,您需要使用其自己的.find() 成员函数。

【讨论】:

    【解决方案2】:

    作为HolyBlackCat(正确)答案的后续,这里有一些更正的代码来显示差异:

    #include <iostream>
    #include <vector>
    #include <set>
    #include <unordered_set>
    #include <algorithm>
    #include <random>
    #include <numeric>
    #include <chrono>
    
    using namespace std;
    
    int main(int argc, char** argv)
    {
      int size = 10000;
      vector<int> items(size);
      iota(items.begin(), items.end(), 0);
      auto rng = default_random_engine {};
      shuffle(items.begin(), items.end(), rng);
    
      using clock = std::chrono::high_resolution_clock;
    
      clock::time_point begin, end;
      unordered_set<int> unsetInt;
      set<int> setInt;
      vector<int> vecInt;
    
      // add element
      for(auto it = items.begin(); it != items.end(); it++)
      {
        unsetInt.insert(*it);
        setInt.insert(*it);
        vecInt.push_back(*it);
      }
    
      int suma = 0, sumb = 0, sumc = 0;
      // lookup time for unordered_set<int>
      begin = clock::now();
      for(int i = 0; i < size; i++)
      {
            suma += *unsetInt.find(i);
        // find(unsetInt.begin(), unsetInt.end(), i);
      }
      end = clock::now();
      cout << "lookup time for unordered_set<int>: " <<
      chrono::duration_cast<chrono::microseconds>(end-begin).count() << "us" << endl;
    
      // lookup time for set<int>
      begin = clock::now();
      for(int i = 0; i < size; i++)
      {
        // find(setInt.begin(), setInt.end(), i);
        sumb += *setInt.find(i);
      }
      end = clock::now();
      cout << "lookup time for set<int>: " <<
      chrono::duration_cast<chrono::microseconds>(end-begin).count() << "us" << endl;
    
      // lookup time for vector<int>
      begin = clock::now();
      for(int i = 0; i < size; i++)
      {
        sumc += *find(vecInt.begin(), vecInt.end(), i);
      }
      end = clock::now();
      cout << "lookup time for vector<int>: " <<
          chrono::duration_cast<chrono::milliseconds>(end-begin).count() << "ms" << endl;
    
      cout << "\nIgnore:" << suma << "," << sumb << "," << sumc << '\n';
    
      return 0;
    }
    

    我将其更改为显示我们的时间而不是毫秒,因为结果是这样的:

    lookup time for unordered_set<int>: 216us
    lookup time for set<int>: 889us
    lookup time for vector<int>: 21916us
    
    Ignore:49995000,49995000,49995000
    

    我添加了代码来汇总搜索结果,因为没有它,编译器“注意到”循环没有可观察到的行为,并简单地将它们完全优化掉(它们都在 0 毫秒内运行)。我猜你是在关闭优化的情况下进行测试,但在关闭优化的情况下测量代码速度通常不是一件有用的事情。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-30
      • 1970-01-01
      • 1970-01-01
      • 2011-06-05
      • 2012-09-21
      相关资源
      最近更新 更多