问题:
我正在尝试编写一段代码(首选C语言,如果没有其他解决方案,也可以使用汇编语言),使得分支预测在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 未命中率。这已经足够满足我的需求,并且我有办法在不同的架构上实现这一目标。
rand
不是一个好的随机数生成器。它可能会变得非常可预测,以至于分支预测器实际上能够以一致的方式预测其行为。 - Stefano Sanfilippo