我最初的想法是给出一个优雅的代码示例,以展示指令缓存限制的影响。我使用模板元编程编写了以下代码,它创建了大量相同的函数。
volatile int checksum;
void (*funcs[MAX_FUNCS])(void);
template <unsigned t>
__attribute__ ((noinline)) static void work(void) { ++checksum; }
template <unsigned t>
static void create(void) { funcs[t - 1] = &work<t - 1>; create<t - 1>(); }
template <> void create<0>(void) { }
int main()
{
create<MAX_FUNCS>();
for (unsigned range = 1; range <= MAX_FUNCS; range *= 2)
{
checksum = 0;
for (unsigned i = 0; i < WORKLOAD; ++i)
{
funcs[i % range]();
}
}
return 0;
}
外部循环使用跳转表来改变要调用的不同函数的数量。WORKLOAD
对于每个循环通过,然后测量调用函数所花费的时间。现在结果如何?下图显示了与使用范围相关的每个函数调用的平均运行时间。蓝线显示在 Core i7 机器上测量的数据。红线所示的比较测量是在 Pentium 4 机器上进行的。然而,当谈到解释这些台词时,我似乎有点挣扎......
分段常数红色曲线的唯一跳跃恰好发生在范围内所有函数的总内存消耗超过测试机器上一个缓存级别的容量的地方,该机器没有专用的指令缓存。然而,对于非常小的范围(在这种情况下低于 4),运行时间仍然会随着函数的数量而增加。这可能与分支预测效率有关,但由于在这种情况下每个函数调用都会减少为无条件跳转,所以我不确定是否应该有任何分支惩罚。
蓝色曲线的行为完全不同。运行时间对于小范围是恒定的,然后以对数方式增加。然而,对于更大的范围,曲线似乎再次接近恒定的渐近线。如何准确地解释两条曲线的质量差异?
我目前正在使用 GCC MinGW Win32 x86 v.4.8.1g++ -std=c++11 -ftemplate-depth=65536
没有编译器优化。
任何帮助,将不胜感激。我也对如何改进实验本身的任何想法感兴趣。提前致谢!