0

我复制了 glibc 的二分搜索算法实现,然后对其进行了一些修改以满足我的需要。我决定测试它和我学到的关于 GCC 的其他东西(属性和内置)。代码如下所示:

int main() {
  uint_fast16_t a[61] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61 };
  uint64_t t1 = Time(0);
  for(register uint_fast16_t i = 0; i < 10000000; ++i) {
    binary_search(rand() % 62, a, 61);
  }
  printf("%ld\n", Time(0) - t1);
  return 0;
}

现在,这个程序运行得很好。当我添加更多代码行时,问题就开始了,例如:

uint_fast16_t a[61] __attribute__ ((aligned (64) )) = /* ... */

在这种情况下,我希望代码更快,但经过多次测试(数十次测试)后性能没有改变。我还测试了 8 和 1 对齐的程序 - 没有变化。我什至预计 gcc 会抛出错误/警告,因为使用的对齐小于类型大小(在我的情况下 64 位机器,uint_fast16_t 是 8 个字节),但没有。然后是另一个更改,即添加缓存(在 GCC 9 中引入)。我在 for 循环之前添加了以下代码:

caches(a, uint_fast16_t, uint_fast16_t, 61, 0, 3);
// where "caches" is:
#define caches(x, type, data_type, size, rw, l) ({ \
  for(type Q2W0 = 0; Q2W0 < size; Q2W0 += 64 / sizeof(data_type)) { \
    __builtin_prefetch(x + Q2W0, rw, l); \
  } \
})

性能也没有变化。我发现我的 CPU 可能会在第一次之后自动缓存数组,binary_search所以我消除了for循环并在有和没有缓存线的情况下再次测量了几次,但我也没有注意到性能有任何变化。
更多信息:

  1. 使用 CentOS8 64bit 最新内核
  2. 使用 GCC 9.2.1 20191120
  3. 编译-O3 -Wall -pthread -lanl -Wno-missing-braces -Wmissing-field-initializers,编译期间没有错误/警告
  4. 事情没有优化(检查 asm 输出)

我很确定我不知道某事/我做错了什么。

此处提供完整代码。

4

1 回答 1

1
  • register uint_fast16_t是过早的优化,让编译器决定将哪些变量放在寄存器中。视为register一个基本过时的关键字。

  • 如评论中所述,uint_fast16_t i = 0; i < 10000000要么是错误,要么是不好的做法。你也许应该做这样的事情:

    const uint_fast16_t MAX = 10000000; 
    ... i < MAX
    

    在这种情况下,如果值不合适,您应该在初始化时得到编译器错误。或者,使用静态断言检查值。

    更好的是,size_t在这种情况下使用 for 循环迭代器。

  • __attribute__ ((aligned (64) ))“在这种情况下,我希望代码更快”

    为什么?是什么让你认为数组一开始就错位了?编译器不会仅仅为了变量而错位。特别是当数组成员被声明为时uint_fastnn- 使用的全部uint_fast16_t目的实际上是为了获得正确的对齐。

    在这种情况下,数组导致 x86/64 的 gcc 和 clang 都吐出一堆.quad汇编指令,从而产生完美对齐的数据。

  • 关于缓存命令,我对它们的工作原理知之甚少,无法评论它们。然而,在这种情况下,您可能已经拥有理想的数据缓存性能 - 阵列应该在数据缓存中。

    至于指令缓存,它在二分搜索期间不太可能做得很好,它本质上带有大量的分支。在某些情况下,出于这个原因,蛮力线性搜索可能会胜过二分搜索。基准测试并查看。(并且当蛮力被证明比二分搜索快得多时,请确保用一个大 O 打击你的老计算机科学算法老师。)

  • rand() % 62可能是也可能不是瓶颈。rand 函数和模数都可能意味着大量开销,具体取决于系统。

于 2020-11-26T15:11:55.897 回答