在现代 CPU 上,二分查找比线性查找快多少?

Posted

技术标签:

【中文标题】在现代 CPU 上,二分查找比线性查找快多少?【英文标题】:At which n does binary search become faster than linear search on a modern CPU? 【发布时间】:2010-11-19 12:38:19 【问题描述】:

由于分支预测的奇妙之处,二分查找可能比通过整数数组的线性查找要慢。在一个典型的桌面处理器上,该数组必须有多大才能更好地使用二分搜索?假设该结构将用于许多查找。

【问题讨论】:

这将取决于对相关数据进行比较的成本 OP 确实非常清楚明确地指出,他说的是一个 整数 数组——您还担心其他哪些变体?! 【参考方案1】:

我已经尝试了一些 C++ 基准测试,但我很惊讶 - 线性搜索似乎在多达几十个项目中占优势,而且我还没有发现二进制搜索更适合这些大小的情况。也许 gcc 的 STL 没有调好?但是然后——你会用什么来实现这两种搜索?-) 所以这是我的代码,所以每个人都可以看到我是否做了一些愚蠢的事情,会严重扭曲时间......:

#include <vector>
#include <algorithm>
#include <iostream>
#include <stdlib.h>

int data[] = 98, 50, 54, 43, 39, 91, 17, 85, 42, 84, 23, 7, 70, 72, 74, 65, 66, 47, 20, 27, 61, 62, 22, 75, 24, 6, 2, 68, 45, 77, 82, 29, 59, 97, 95, 94, 40, 80, 86, 9, 78, 69, 15, 51, 14, 36, 76, 18, 48, 73, 79, 25, 11, 38, 71, 1, 57, 3, 26, 37, 19, 67, 35, 87, 60, 34, 5, 88, 52, 96, 31, 30, 81, 4, 92, 21, 33, 44, 63, 83, 56, 0, 12, 8, 93, 49, 41, 58, 89, 10, 28, 55, 46, 13, 64, 53, 32, 16, 90
             ;

int tosearch[] = 53, 5, 40, 71, 37, 14, 52, 28, 25, 11, 23, 13, 70, 81, 77, 10, 17, 26, 56, 15, 94, 42, 18, 39, 50, 78, 93, 19, 87, 43, 63, 67, 79, 4, 64, 6, 38, 45, 91, 86, 20, 30, 58, 68, 33, 12, 97, 95, 9, 89, 32, 72, 74, 1, 2, 34, 62, 57, 29, 21, 49, 69, 0, 31, 3, 27, 60, 59, 24, 41, 80, 7, 51, 8, 47, 54, 90, 36, 76, 22, 44, 84, 48, 73, 65, 96, 83, 66, 61, 16, 88, 92, 98, 85, 75, 82, 55, 35, 46
                 ;

bool binsearch(int i, std::vector<int>::const_iterator begin,
                      std::vector<int>::const_iterator end) 
  return std::binary_search(begin, end, i);


bool linsearch(int i, std::vector<int>::const_iterator begin,
                      std::vector<int>::const_iterator end) 
  return std::find(begin, end, i) != end;


int main(int argc, char *argv[])

  int n = 6;
  if (argc < 2) 
    std::cerr << "need at least 1 arg (l or b!)" << std::endl;
    return 1;
  
  char algo = argv[1][0];
  if (algo != 'b' && algo != 'l') 
    std::cerr << "algo must be l or b, not '" << algo << "'" << std::endl;
    return 1;
  
  if (argc > 2) 
    n = atoi(argv[2]);
  
  std::vector<int> vv;
  for (int i=0; i<n; ++i) 
    if(data[i]==-1) break;
    vv.push_back(data[i]);
  
  if (algo=='b') 
    std::sort(vv.begin(), vv.end());
  
  bool (*search)(int i, std::vector<int>::const_iterator begin,
                        std::vector<int>::const_iterator end);
  if (algo=='b') search = binsearch;
  else search = linsearch;
  int nf = 0;
  int ns = 0;
  for(int k=0; k<10000; ++k) 
    for (int j=0; tosearch[j] >= 0; ++j) 
      ++ns;
      if (search(tosearch[j], vv.begin(), vv.end()))
        ++nf;
    
  
  std::cout << nf <<'/'<< ns << std::endl;

  return 0;

以及我在核心二重奏上的一些时间安排:

AmAir:stko aleax$ time ./a.out b 93
1910000/2030000

real    0m0.230s
user    0m0.224s
sys 0m0.005s

AmAir:stko aleax$ time ./a.out l 93
1910000/2030000

real    0m0.169s
user    0m0.164s
sys 0m0.005s

无论如何,它们是相当可重复的......

OP 说:Alex,我编辑了你的程序,只用 1..n 填充数组,而不运行 std::sort,并进行大约 1000 万次(模整数除法)搜索。在 Pentium 4 上,二进制搜索在 n=150 处开始脱离线性搜索。对图表颜色表示抱歉。

binary vs linear search http://spreadsheets.google.com/pub?key=tzWXX9Qmmu3_COpTYkTqsOA&oid=1&output=image

【讨论】:

这是 -O -- -O3 使线性搜索更差一点,大约 178 毫秒,而二分搜索更好一点,大约 222 毫秒。【参考方案2】:

我认为分支预测并不重要,因为线性搜索也有分支。据我所知,没有 SIMD 可以为您进行线性搜索。

话虽如此,一个有用的模型是假设二分查找的每一步都有乘数成本 C。

C 对数2 n = n

所以要在没有实际基准测试的情况下对此进行推理,您可以猜测 C,然后将 n 舍入到下一个整数。例如,如果您猜测 C=3,那么在 n=11 时使用二分查找会更快。

【讨论】:

@joeforker,那么二分查找在 117 个元素处会更快。 似乎对 +1 感到羞耻,因为您的代表是一个如此整洁的数字 (10,000) @Rich Seller,我今天刷爆了。明天格林威治标准时间上午 12 点重置代表计数器时,请给我投票 @Unknown 秘密是线性搜索中的分支将被正确预测,直到找到该项目。 分支的数量并不重要。重要的是分支被采用的概率。使用线性搜索,它总是以相同的方式(总是采用或始终不采用)直到匹配发生。使用二分搜索,每一步都有 50% 的概率被错误预测,因为相同的分支不断被采用或不被采用,这是不可预测的。【参考方案3】:

数量不多 - 但如果不进行基准测试就很难准确地说出来。

就我个人而言,我更喜欢二分搜索,因为在两年的时间里,当其他人将你的小数组的大小翻了两番时,你的性能并没有损失太多。除非我非常明确地知道它现在是一个瓶颈,当然我需要它尽可能快。

话虽如此,请记住还有哈希表;您可以就它们与二分搜索提出类似的问题。

【讨论】:

是的,哈希表在达到数千个元素之前非常慢。 OP的问题非常重要。

以上是关于在现代 CPU 上,二分查找比线性查找快多少?的主要内容,如果未能解决你的问题,请参考以下文章

快速排序和二分查找

为啥我的线性搜索比我在 Python3 中的二分搜索运行得更快?

Java查找算法:二分查找

二分查找会更快吗?Python中的二分查找与线性查找性能测试

二分查找(折半查找)

为什么二分查找比较快?