【发布时间】:2021-03-09 10:45:26
【问题描述】:
我复制了 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 循环并在有和没有缓存线的情况下再次测量了几次,但我也没有注意到性能有任何变化。
更多信息:
- 使用 CentOS8 64bit 最新内核
- 使用 GCC 9.2.1 20191120
- 使用
-O3 -Wall -pthread -lanl -Wno-missing-braces -Wmissing-field-initializers编译,编译期间没有错误/警告 - 事情没有被优化掉(检查 asm 输出)
我很确定我不知道某事/我做错了什么。
完整代码here.
【问题讨论】:
-
您的更改几乎没有提高性能。你确定他们做到了?
-
仅仅因为您指定了最小对齐方式,这并不意味着对象必须 未对齐。您的
main中有许多“优化”,但是binary_search()和rand()这么快以至于微优化很重要?你应该看看生成的程序集。 -
这就是重点。为什么他们根本没有改变任何东西?我的意思是,我做了一些事情来提高和降低性能(增加对齐和减少对齐,缓存和不缓存)。也许我所做的改变真的无关紧要,这就是原因。
-
另外,您的代码已损坏。
10000000不适合 16 位,因此循环只会结束,因为uint_fast16_t大于uint16_t。 -
@FranciszekBalcerak 除了你是谁,你不可能记得一年后你重新访问代码时潜入代码中的所有微妙的、硬编码的废话:)