【发布时间】:2015-05-11 18:06:13
【问题描述】:
问题:
我正在尝试弄清楚如何编写代码(首选 C,仅当没有其他解决方案时才使用 ASM)在 50% 的情况下会导致分支预测失败。
因此,它必须是一段代码,对与分支相关的编译器优化“免疫”,并且所有硬件分支预测不应超过 50%(抛硬币)。更大的挑战是能够在多 CPU 架构上运行代码并获得相同的 50% 未命中率。
我设法在 x86 平台上编写了一个达到 47% 分支未命中率的代码。我怀疑失踪者可能有 3% 来自:
- 包含分支的程序启动开销(虽然非常小)
- 分析器开销 - 基本上,每次读取计数器都会引发一个中断,因此这可能会增加额外的可预测分支。
- 在后台运行的系统调用包含循环和可预测的分支
我编写了自己的随机数生成器,以避免调用可能具有隐藏可预测分支的 rand。如果可用,它也可以使用 rdrand。延迟对我来说并不重要。
问题:
- 我能比我的代码版本做得更好吗?更好意味着对所有 CPU 架构获得更高的分支错误预测和相同的结果。
- 此代码可以断言吗?这意味着什么?
代码:
#include <stdio.h>
#include <time.h>
#define RDRAND
#define LCG_A 1103515245
#define LCG_C 22345
#define LCG_M 2147483648
#define ULL64 unsigned long long
ULL64 generated;
ULL64 rand_lcg(ULL64 seed)
{
#ifdef RDRAND
ULL64 result = 0;
asm volatile ("rdrand %0;" : "=r" (result));
return result;
#else
return (LCG_A * seed + LCG_C) % LCG_M;
#endif
}
ULL64 rand_rec1()
{
generated = rand_lcg(generated) % 1024;
if (generated < 512)
return generated;
else return rand_rec1();
}
ULL64 rand_rec2()
{
generated = rand_lcg(generated) % 1024;
if (!(generated >= 512))
return generated;
else return rand_rec2();
}
#define BROP(num, sum) \
num = rand_lcg(generated); \
asm volatile("": : :"memory"); \
if (num % 2) \
sum += rand_rec1(); \
else \
sum -= rand_rec2();
#define BROP5(num, sum) BROP(num, sum) BROP(num, sum) BROP(num, sum) BROP(num, sum) BROP(num, sum)
#define BROP25(num, sum) BROP5(num, sum) BROP5(num, sum) BROP5(num, sum) BROP5(num, sum) BROP5(num, sum)
#define BROP100(num, sum) BROP25(num, sum) BROP25(num, sum) BROP25(num, sum) BROP25(num, sum)
int main()
{
int i = 0;
int iterations = 500000;
ULL64 num = 0;
ULL64 sum = 0;
generated = rand_lcg(0) % 54321;
for (i = 0; i < iterations; i++)
{
BROP100(num, sum);
// ... repeat the line above 10 times
}
printf("Sum = %llu\n", sum);
}
更新 v1:
按照 usr 的建议,我通过在脚本的命令行中改变 LCG_C 参数来生成各种模式。 我能够去 49.67% BP 错过。这对我的目的来说已经足够了,而且我有方法可以在各种架构上生成它。
【问题讨论】:
-
Why is processing a sorted array faster than an unsorted array? 的代码就是这样一个微型基准。除非编译器将代码替换为无分支等效代码。
-
你怎么知道你只得到了 8% 的分支失误?我很好奇您使用什么检测工具来确定这一点。
-
不确定是否相关,但
rand并不是一个好的 RNG。它可能是如此可预测,以至于分支预测器实际上能够以一致的方式预测行为。 -
内联 rand() 调用,rng 不必很好,你只需要不进出它。
-
如果你想学习一些启发性的东西,打印出你的 LCG 的前 20 个输出,全部减少模 2。
标签: c++ c performance compiler-optimization computer-architecture