连载:编写高效代码(12) 优化内存访问——别让包袱拖垮了你

         从理论上看,每条运算指令的执行时间都很短,大多数指令一个Cycle就能完成,很多时候还能一个Cycle执行多条指令,可是实际上,执行指令只是处理器要做的很少一部分工作,处理器还要从存储器中取指令,从存储器中将数据导入到寄存器中,等算完后,再将结果存入到存储器中。

        处理器运算的速度像兔子赛跑一样快,但是存储器的访问速度像乌龟走路一样慢,而且越是远离内核的存储器,访问速度越慢。

        下面这个表是在几个x86处理器中,内核访问各级数据Cache和内存所需要的 Cycle数: 

处理器

Level 1 data Cache

Level 2 data Cache

内存

P3

3

8

140

P4

2

19

350

PM(奔腾M)

3

10

80

Core2(酷睿)

3

14

185

Opteron(皓龙)

3

13

100

 

        从这张表可以看出,从L1中访问数据速度还较快,但是仍然要慢于运算的速度,从L2中访问数据速度还能将就,从内存中访问数据就无法忍受了。我们应该尽量减少内存的访问,要访问,也要尽量避免Cache miss。

少使用数组,少使用指针

         由于大块数据会被放在存储器中,简单局部变量才会被放在寄存器中,因此应该尽量少用数组、指针,多用简单局部变量。

下面这段程序,需要4次内存访问:

 c = a[i] * b[i];

d = a[i] + b[i];

 如果改成如下的形式,就只有两次内存访问了:

 x = a[i];

y = b[i];

c = x * y;

d = x + y;

 

少用全局变量

        全局变量因为要被多个模块使用,不会被放到寄存器中,局部变量才能被放在寄存器中,应尽量避免使用全局变量。下面这段程序:

 int  x;

int fun_a ()

{

   int y, z;

   y = x;

   z = x + 1;

   …

}

 

最好改为:

 

int  x;

int fun_a ()

{

   int y, z, temp;

   temp = x;

   y = temp;

   z = temp + 1;

   …

}

 

 一次多访问一些数据

        我们通常会有这样的生活常识,要去很远的地方,就会多带一些东西,要去近一点的地方,就会少带一些东西。既然数据访问速度较慢,我们就一次多访问些数据。处理器将这些为我们考虑到了,通常都提供了较大的数据带宽。

         以C64 DSP为例,通常一条指令,一次对两个32bit的数据做处理,而它却1次可以访问两个64bit数据。

        在DSP中,SIMD指令和普通指令共用寄存器,有些数据虽然不能用SIMD指令处理,我们也可以一次将内存中的多个数据搬入到寄存器中,用简单指令分别处理,当要存储时,将分散的寄存器组合在一个连续的位置,再将数据输出到内存中。由于操作寄存器远比操作存储器快,虽然多了些数据的拆分和组合的操作,代价还是值得的。

你可能感兴趣的:(软件开发)