今天看了Peter Van Der Linden大师的关于cache优化的一段讲解,其中的程序作了改动展示如下:
// cache.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
# define SMARTCOPY memcpy(destination, source, 65536)
# define DUMBCOPY for(i = 0; i < 65536; i++) destination[i] = source[i]
int _tmain(int argc, _TCHAR* argv[])
{
clock_t begin, end;
double cost;
begin = clock();//计时函数,头文件time.h,精确度ms,好用。
char source[65536], destination[65536];
int i,j;
for(j = 0; j < 1000; j++)
DUMBCOPY;
//SMARTCOPY;
end = clock();
cost = (double)(end - begin) / CLOCKS_PER_SEC;
printf("%lf seconds\n", cost);
//system("pause");
begin = clock();
for(j = 0; j < 1000; j++)
//DUMBCOPY;
SMARTCOPY;
end = clock();
cost = (double)(end - begin) / CLOCKS_PER_SEC;
printf("%lf seconds\n", cost);
system("pause");
return 0;
}
原书的解释如下;
编译并记录上面程序的运行时间,采用两种方式.第一种就是上面的程序,第二种就是用DUMBCOPY宏替换上面程序中的SMARTCOPY宏.我是在SPARCstation2上运行这个程序的,使用笨拷贝(dumpcopy)的程序的性能有显著的下降.
之所以出现性能下降,是因为source和destination的大小正好都是Cache容量的整数倍.SS2上的Cache行并不是按顺序填充的---它使用了一种特别的算法,填充于同一Cache行的主存地址恰好都是该Cache行大小的整数倍.这是由于对标签存储的优化所引起的--在这种设计方法中,只有地址的高位才被放入标签中.这样一来,source和destination便不可能同时出现在Cache中,于是导致了性能的显著下降.
在这个source和destination都使用同一Cache行的特殊情况下,会导致每次对内存的引用都无法命中Cache,使CPU的利用率大大降低,因为它不得不等待常规的内存操作完成.
库函数memcpy()经过特别优化以提高性能.它把先读取一个Cache行再对它进行写入这个循环分解开来,这就避免了上述问题.
这个解释我不是很理解,因为即使memcpy()这个函数即使不是copy 的64k它的运行速度依然会比for循环快的多。 当然这里不是争论这个的问题。
这里memcopy()把先读取一个cache行再对它进行写入.
memcpy在汇编上是使用stosb指令,而tosb指令是cpu内部指令,执行stosb指令时系统可以保证用户不需要作任何中间工作,系统完全有理由先把缓存填充满然后再写进目标地址中。而for循环是N多指令的结合,指令上memcpy就省了好多。而且由于rep stdsb指令是CPU内置指令组合,所以CPU懂得如何去高效地加载数据。
for循环每次都是复制1个字节,小于缓存容量,所以缓存不能够充分利用(印象中好象是32个字节),因为用户过程的介入,所以指令一旦写成CPU是无法优化的(区别与编译期优化)。