您的位置:首页 > 编程语言

代码优化-有效使用内存

2017-08-04 00:00 232 查看
“代码优化-有效使用内存”
这本书某些思想和方法可以借鉴,但具体的方法跟处理器架构有关系,不可同一而论,书中
(原书及翻译版)也存在一些bug。
一、内存优化
1.展开循环
展开循环可以减小分支(循环次数),一般通过重复循环内的指令来实现
exp:
for(a = 0; a < 666; a++)
x+=p[a];
for(a = 0; a < 664; a+=4)
{
// This shows the approximate number of
// loop iterations to the nearest multiple of four.

x+=p[a]; // The body
x+=p[a + 1]; // of the loop
x+=p[a + 2]; // is duplicated
x+=p[a + 3]; // four times.
}
x+=p[a]; // The two remaining iterations
x+=p[a + 1]; // are added to the end.

如果循环次数是一个变量,则可以改写成
for(a = 0; a < (N & ~3); a += 4)
{
4次...
}
for(a = (N & ~3); a < N; a++)

2.消除数据相关性
如果请求的RAM单元存在地址-数据相关性,那么CPU就不能并行地处理它们,
而在得到地址之前必须等待(地址值的读取和运算)。
; /*-----------------------------------------------------------------------
; * Loop for reading dependent data
; * (nonoptimized version)
; -----------------------------------------------------------------------*/
for (a=0; a < BLOCK_SIZE; a += 32)
// The loop is unrolled to speed up the processing.

{
x = *(int *)((int)p1 + a + 0);
// The cell is read.

a += x;
// The address of the next cell is calculated using the value of
// the previous cell. Therefore, the processor cannot send
// the next request to the chipset until it receives this cell.
// The code proceeds in a similar manner...
y = *(int *)((int)p1 + a + 4);
a += y;

x = *(int *)((int)p1 + a + 8);
a += x;

y = *(int *)((int)p1 + a + 12);
a += y;

x = *(int *)((int)p1 + a + 16);
a += x;

y = *(int *)((int)p1 + a + 20);
a += y;

x = *(int *)((int)p1 + a + 24);
a += x;

y = *(int *)((int)p1 + a + 28);
a += y;
}

; /*-----------------------------------------------------------------------
; * Loop for reading independent data
; * (optimized version)
; -----------------------------------------------------------------------*/
for (a=0; a<BLOCK_SIZE; a += 32)
{
x += *(int *)((int)p1 + a + 0);
y += *(int *)((int)p1 + a + 4);
x += *(int *)((int)p1 + a + 8);
y += *(int *)((int)p1 + a + 12);
x += *(int *)((int)p1 + a + 16);
y += *(int *)((int)p1 + a + 20);
x += *(int *)((int)p1 + a + 24);
y += *(int *)((int)p1 + a + 28);
// The processor could send the next request to the chipset
// without waiting for the previous request to be completed,
// because the cell address is not related to the data being processed.
}

注:书中的代码似乎有问题:循环变量a在循环
3ff0
中有修改,忌!
而优化版本中循环中并未修改a.

在MIPS里,对数据相关同样需注意。避免潜在的数据相关。
for(i=0;i<100;i++)
{
b[i]=a[0]+a[1];//读写内存同时进行,编译器会认为内存有修改,a[0]会重复读取
b[i+1]=a[0]+a[2];
...
}
可以利用临时变量,采取读内存,运算,写内存的分步操作
同时这样可以发挥寄存器的作用
for(i=0;i<100;i++)
{
int tmp, b1, b2;
tmp=a[0];
b1=tmp+a[1];
b2=tmp+a[2];
b[i]=b1;
b[i+1]=b2;
}

3.数据并行处理
线性读取无关数据(按字节顺序读取)并不能确保对该内容进行并行处理。应该按32字节的增量(cache line的大小?)读取。并且可以与预取内存结合起来。即
(1)以增量32访问内存
(2)循环访问每个32字节里面的内容。

4.优化引用数据结构
内存中的数据应尽可能紧密地放在一起。
(1) 字节对齐,减小数据结构大小
(2)减小引用数组的大小(用小的数据类型)
(3)注意页面内存大小(32K)及高速缓存大小(4K)的影响
核心代码or数据尽可能地在此范围之内

5.内存访问与计算的结合
在加载内存的同时,进行计算,以尽可能地提高并行处理。

6.读写操作的结合
一般来说,不要集中的读内存或者集中的写内存。

7.只有在必要时才访问内存
该问题与具体情况的算法及数据处理流程有关系。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: