整数二进制搜索的极端优化

Cha*_*les 9 algorithm optimization micro-optimization

我正在编写一个程序,需要在紧密的循环中进行大量的二进制搜索 - 至少10 15次.这些以及少量的按位操作将占程序运行时的75%以上,因此使它们快速非常重要.(现在实现它占用了95%以上的时间,但是使用了一个非常不同的实现[不是搜索],我正在替换它.)

要搜索的数组(当然,它不需要实现为数组)非常小.在我目前的情况下,它由41个64位整数组成,但用于优化其他大小的数组的技术将是有用的.(我之前遇到过类似的问题.)

我可以提前分析数据,以确定最有可能的范围以及匹配的频率.收集这些信息并不容易,但我应该在一天结束时收到这些信息.

我的代码可能在C中使用内联汇编; 它将使用最新版本的gcc进行编译.欢迎任何语言的回复; 如果您愿意(例如)FORTRAN,我可以翻译.

那么:我如何有效地实现这种搜索?

澄清:我实际上是使用搜索来测试成员资格,而不是使用数组中的位置.丢弃该信息的解决方案是可以接受的.


最终代码:

long ispow3_tiny(ulong n)
{
    static ulong pow3table[] = {
#ifdef LONG_IS_64BIT
        12157665459056928801, 0, 4052555153018976267, 1350851717672992089, 0, 450283905890997363, 150094635296999121, 0, 50031545098999707, 0, 16677181699666569, 5559060566555523, 0, 1853020188851841, 617673396283947, 0, 205891132094649, 0, 68630377364883, 22876792454961, 0, 7625597484987, 2541865828329, 0, 847288609443, 282429536481, 0, 94143178827, 0, 31381059609, 10460353203, 0,
#endif
        3486784401, 1162261467, 0, 387420489, 0, 129140163, 43046721, 0, 14348907, 4782969, 0, 1594323, 531441, 0, 177147, 0, 59049, 19683, 0, 6561, 2187, 0, 729, 0, 243, 81, 0, 27, 9, 0, 3, 1
    };

    return n == pow3table[__builtin_clzl(n)];
}
Run Code Online (Sandbox Code Playgroud)

Fog*_*ird 15

既然你的价值是三的权力,我认为我们可以大大优化.让我们看看二进制数字:

Columns are P, I, B:
P = Power (3 ^ P)
I = Index of MSB (Most Significant Bit)
B = Binary Value

00 00 0000000000000000000000000000000000000000000000000000000000000001
01 01 0000000000000000000000000000000000000000000000000000000000000011
02 03 0000000000000000000000000000000000000000000000000000000000001001
03 04 0000000000000000000000000000000000000000000000000000000000011011
04 06 0000000000000000000000000000000000000000000000000000000001010001
05 07 0000000000000000000000000000000000000000000000000000000011110011
06 09 0000000000000000000000000000000000000000000000000000001011011001
07 11 0000000000000000000000000000000000000000000000000000100010001011
08 12 0000000000000000000000000000000000000000000000000001100110100001
09 14 0000000000000000000000000000000000000000000000000100110011100011
10 15 0000000000000000000000000000000000000000000000001110011010101001
11 17 0000000000000000000000000000000000000000000000101011001111111011
12 19 0000000000000000000000000000000000000000000010000001101111110001
13 20 0000000000000000000000000000000000000000000110000101001111010011
14 22 0000000000000000000000000000000000000000010010001111101101111001
15 23 0000000000000000000000000000000000000000110110101111001001101011
16 25 0000000000000000000000000000000000000010100100001101011101000001
17 26 0000000000000000000000000000000000000111101100101000010111000011
18 28 0000000000000000000000000000000000010111000101111001000101001001
19 30 0000000000000000000000000000000001000101010001101011001111011011
20 31 0000000000000000000000000000000011001111110101000001101110010001
21 33 0000000000000000000000000000001001101111011111000101001010110011
22 34 0000000000000000000000000000011101001110011101001111100000011001
23 36 0000000000000000000000000001010111101011010111101110100001001011
24 38 0000000000000000000000000100000111000010000111001011100011100001
25 39 0000000000000000000000001100010101000110010101100010101010100011
26 41 0000000000000000000000100100111111010011000000100111111111101001
27 42 0000000000000000000001101110111101111001000001110111111110111011
28 44 0000000000000000000101001100111001101011000101100111111100110001
29 45 0000000000000000001111100110101101000001010000110111110110010011
30 47 0000000000000000101110110100000111000011110010100111100010111001
31 49 0000000000000010001100011100010101001011010111110110101000101011
32 50 0000000000000110100101010100111111100010000111100011111010000001
33 52 0000000000010011101111111110111110100110010110101011101110000011
34 53 0000000000111011001111111100111011110011000100000011001010001001
35 55 0000000010110001101111110110110011011001001100001001011110011011
36 57 0000001000010101001111100100011010001011100100011100011011010001
37 58 0000011000111111101110101101001110100010101101010101010001110011
38 60 0001001010111111001100000111101011101000000111111111110101011001
39 61 0011100000111101100100010111000010111000010111111111100000001011
40 63 1010100010111000101101000101001000101001000111111110100000100001
Run Code Online (Sandbox Code Playgroud)

观察结果是所有值都具有唯一的MSB.

使用x86位扫描指令,我们可以快速确定MSB.

http://www.arl.wustl.edu/~lockwood/class/cs306/books/artofasm/Chapter_6/CH06-4.html#HEADING4-67

使用MSB作为64项表的索引.将表中的值与要检查的相等值进行比较.如果它们不相等,则测试失败.

编辑:j_random_hacker指出最低的8位也都是唯一的.您可能希望实现每个版本并查看哪个版本最快.

  • 更简单:所有这些值在最右边的8位上都是唯一的,所以只需屏蔽其余值并查找256个元素的表; 如果整个64位值与查找值一致,那就是匹配.我记得多年前那些x86指令,如`BSR`(大概是`__builtin_clzl()`变成了)实际上比手动循环要慢,如果仍然如此,那就是dunno,但每个CPU都存在`AND`,我肯定它至少和那些拥有它们的CPU上的位扫描指令一样快! (3认同)
  • @j_random_hacker - 刮擦 - 在第32项首先重复7位.不确定这是否是模式破坏,或者我在某处弄乱了我的数字.但LSB始终为1,因此您仍然可以获得128个表.位2也总是0,但可能不值得利用它. (3认同)

flo*_*olo 12

不是你的问题的答案,而是一个改进的建议:如果你必须做那么多的查询,它可能值得首先计算41个值的完美哈希函数,然后用它来获取索引.