我一直想知道如何更好地利用cpu缓存(已知会受益于参考的位置) – 两个循环遍历相同的数学数组,每个循环都有不同的循环体,或者具有一个“两个”结合成一体的循环,从而完成相同的总体结果,但本身呢?
在我看来,有两个循环将引入更少的缓存未命中和驱逐,因为循环中使用的更多指令和数据适合缓存.我对吗?
假设:
f和g的成本与完成包含每个的整个循环的成本相比可以忽略不计
> f和g本身使用大部分缓存,因此缓存将被一个被另外调用的高速缓存(这将是单循环体版本的情况)
> Intel Core Duo cpu
> C语言源代码
> gcc编译器,没有开关
迭代的集合是数学集合,而不是存储器中数字的容器,如矢量或列表.参见下面的例子.
请不要回答“过早优化是邪恶”的角色:-)
我倡导的双循环版本的一个例子:
int j = 0,k = 0; for(int i = 0; i < 1000000; i++) { j += f(i); } for(int i = 0; i < 1000000; i++) { k += g(i); }
解决方法
我可以看到三个变量(即使在一个看似简单的代码块):
> f()和g()做什么?其中一个可以使所有指令高速缓存行无效(有效地将另一个引出)?也可以在L2指令缓存中发生(不太可能)?然后只保留其中一个可能是有益的.注意:逆并不意味着“有一个循环”,因为:
> do f()和g()对大量数据进行操作,根据我?那么,知道他们是否在同一组数据上运行会很好,再次,您必须考虑是否通过两个不同的组来操作,通过缓存未命中进行操作.
>如果f()和g()确实是第一个状态的原语,并且我假定代码大小以及运行时间和代码复杂性,缓存局部性问题不会出现在像这样的一小段代码中 – 最大的问题是,如果有其他进程安排了实际的工作要做,并使所有缓存无效,直到你的进程转向运行.
最后的想法:考虑到像上面这样的过程在你的系统中可能是一个罕见的事情(而且我很自由地使用“罕见”),你可以考虑将这两个函数内联起来,让编译器展开循环.这是因为对于指令缓存,故障回到L2并不大,而在该循环中包含i,j,k的单个缓存行将被无效的概率看起来并不可怕.但是,如果不是这样,一些更多的细节将是有用的.