编译器必须假设不同的指针可能会指向存储器中的同一个位置。这造成了一个主要的妨碍优化的因素。
例如:
void twiddle1(int *xp, int *yp)
{
*xp += *yp;
*xp += *yp;
}
void twiddle2(int *xp, int *yp)
{
*xp = 2 * (*yp);
}
看起来twiddle1与twiddle2实现的是同样的功能,并且twiddle2应该是twiddle1的优化版,因为twiddle2只需要访问一次xp和一次yp,而twiddle1却用了两次。
但是,考虑下面一种情况:
int t;
twiddle1(&t, &t);
twiddle2(&t, &t);
它们得到的结果是一样的吗,不!这也就是编译器在做优化的时候必须要考虑到的问题,编译器并不能完全了解编程者的意图,所以只能对当前的代码保守的优化。
同样的例子还有:
int counter = 0;
int f(int x)
{
return (counter+x);
}
int f1(int x)
{
return f(x)+f(x);
}
int f2(int x)
{
return 2*f(x);
}
如何表示程序的性能—CPE/
CPE: cycles per element, 每元素的同期数。
如何理解,比如一个数组int array[50],它在函数f()中被用于计算,最后f()所用去的cpu clock为100, 那么函数f()的CPE为100/50=2.0;
为什么不采用每次循环的周期数而是采用每元素的同期数呢,因为可能会出现循环展开的情况。
如何消除循环的低效率
Ø 减少不必要的函数调用:
如for (i = 0; i < vec_length(v) i++)中的vec_length完全可以放在循环体的外面。
Ø 消除不必要的存储器引用
如
for (i = 0; i < length; i++)
{
*dest = *dest + data[i];
}
可以先用局部变量进行运算,最后才赋值到dest中去。
Ø 作循环展开,让软件流水
注意,让软件能流水的前提条件是在循环体中不能有判断语句,一个if也不行。所以要想让代码执行得更快,就要尽量想办法把判断语句搬移到循环体外面去。
减少浮点运算所带来的开销
一般来说,处理器作定点运算的速度会比作浮点运算的速度快(专用的浮点处理器例外)。如在DM642上,作定点运算的速度是作浮点运算速度的10倍。所以在处理浮点数之间,先将其转化为定点后运算再赋值将会获得很大的性能提升。
是否应该将数组转化为指针代码
根据经验,数组代码将会比指针代码更可取,我们已经看到过的编译器,它们对数组代码应用非常高级的优化,而对指针代码只应用最小限度的优化。并且数组代码具有更好的可读性。
合理利用CACHE
对于DM642而言,L2 Cache的大小是可以设定的,但如果Cache全开,将会用尽所有的L2 SRAM。这样就不能使用如DMA two buffer的技术了。但如果一点都不开,那对程序的运行速度是有非常大的影响的。