Linux性能学习(1.2):CPU_如何提高CPU缓存命中率

在上一篇文章中简单介绍了CPU缓存的相关知识,并且引申出了一个概念—“CPU缓存命中”,那么如何让我们编写的程序尽可能的被CPU缓存命中,从而尽可能的提高运行效率?

1 数组访问测试

代码测试如下:


#include <stdio.h>
#include <sys/time.h>

#define Len 1024

void main()
{
	float time_use = 0;
	struct timeval start;
	struct timeval end;
	int s32Array[Len][Len];
	int i = 0;
	int j = 0;
	
	gettimeofday(&start,NULL); 
	#if 0  //方法1
	for (i = 0; i < Len; i++)
	{
		for(j = 0; j < Len; j++)
		{
			s32Array[i][j] = 0;
		}
	}
	#else //方法2
	for (j = 0; j < Len; j++)
	{
		for(i = 0; i < Len; i++)
		{
			s32Array[i][j] = 0;
		}
	}
	#endif
	gettimeofday(&end,NULL);
	time_use=(end.tv_sec-start.tv_sec)*1000000+(end.tv_usec-start.tv_usec);
	printf("time_use is %f\n",time_use);
}

编译上面的程序,进行测试,使用方法1中的方式:


# perf stat -e cache-references,cache-misses,instructions,cycles,L1-dcache-load-misses,L1-dcache-loads ./test
time_use is 5095.000000

 Performance counter stats for './test':

   <not supported>      cache-references                                            
   <not supported>      cache-misses                                                
   <not supported>      instructions                                                
   <not supported>      cycles                                                      
   <not supported>      L1-dcache-load-misses                                       
   <not supported>      L1-dcache-loads                                             

       0.007139561 seconds time elapsed
       

使用方法2中的方式:


# perf stat -e cache-references,cache-misses,instructions,cycles,L1-dcache-load-misses,L1-dcache-loads ./test
time_use is 22464.000000

 Performance counter stats for './test':

   <not supported>      cache-references                                            
   <not supported>      cache-misses                                                
   <not supported>      instructions                                                
   <not supported>      cycles                                                      
   <not supported>      L1-dcache-load-misses                                       
   <not supported>      L1-dcache-loads                                             

       0.025807438 seconds time elapsed
       

PS:因为是在虚拟中测试,所以没法测试出缓存命中率等信息。

通过上面的测试,可以得出,使用方法1时间大约是5ms左右,而使用方法2则大约是22ms左右。

为啥有这么大的差距,因为数组在内存中是连续的,并且按照前面说的cache line,CPU加载数组时候会一次加载一块的数据进缓存中。因此按照方法1的方式,CPU在加载[0][0]时候,会一次性的将[0][1]、[0][2]等后续内存中的数组加载进来,则进行[0][1]运算的时候,不需要从内存中读取[0][1]了,所以运行时间就会大大减小;按照方法2的方式,CPU在加载[0][0]时候,会一次性的将[0][1]、[0][2]等后续内存中的数组加载进来,但是下一个计算的不是[0][1],而是[1][0],没有在缓存中,则需要重新从内存中读取,运行时间就会减慢。

但是,如果数组很小,即一次可以将全部的数据加载进缓存,那么其实两种方法时间上没有很明显的差异,如果数组很大,则会看到明显的差异。

因此在进行数据操作的时候,我们可以尽可能的按照内存布局顺序来进行操作,可以大大提高性能。

2 其它规则

基于以上的结论,我们在定义数据结构的时候,如果按照cache line的大小进行对齐,就可能减少读写内存的次数,提高性能,即空间换取时间。

函数循环体内的代码尽量精简,因为指令是放在指令缓存中的,大小是有限的,如果对某段代码需要多次读取,超过指令缓存大小,那么就无法发挥优势。

对于if/switch等语句,有规律的数据(比如递增、递减)能够让CPU的分支预测器发挥作用,从而提高效率。

如果有多个核,则可以尽量将进程绑定到一个核上,这样避免进程在核A中运行,因为任务调度要在核B中运行,则需要将数据重新缓存到B的缓存中。

猜你喜欢

转载自blog.csdn.net/u011003120/article/details/128284710