[性能优化 ] 化绝对为相对
1. 核心思路
站在巨人的肩膀, 不香吗? 何必重新计算? 重复的计算是阻碍计算机性能发挥的一大杀手. 假设给我一个数列, 通项和递推关系, 我一定会选择后者, 花费计算可能更少, 效率更高.
2. 乘2 vs 左移?
众所周知, 遇到2的指数, 通过左右移可以快速计算.
但是请注意!
假设我想要计算 (a-b)/2
的平方, 例如 a=14
, b=29
. 我们知道答案应该是 49
(以计算机整数计算)
int a = 14;
int b = 29;
int err = ((a-b)>>1) * ((a-b)>>1);
int crt = ((b-a)>>1) * ((b-a)>>1);
printf("err=%d, crt=%d\n",err, crt);
运行后发现
在进行左右移运算时, 请务必检查目标数的正负符号, 否则符号位将会被一起移动, 导致结果大相径庭. 具体可百度二进制有无符号数 及 补码运算 进行了解.
3. 绝对指针 vs 迭代指针 ?
循环迭代是编写程序的家常便饭. 尤其是在迭代访问数组内存.
绝对指针是指每次都计算出该指针的偏移量指向, 不移动迭代器, 而是通过基地址计算而来的, 例如
for( int y=0; y<img_h; y++ ){
for( int x=0; img_w; x++ ){
size_t index = (ys+y)*(info_MainScreen.width)+(xs+x);
info_MainScreen.pBuffer[ index ].R = xxx;
info_MainScreen.pBuffer[ index ].G = xxx;
info_MainScreen.pBuffer[ index ].B = xxx;
}
}
index
就是访问的索引偏移量. 每次计算index
都需要耗费乘法运算, 相当累赘.
不妨…
Image_t* pIter = info_MainScreen.pBuffer + (ys)*(info_MainScreen.width)+(xs);
for( int y=0; y<src->height&&y<dst->height; y++ ){
for( int x=0; x<src->width; x++,pIter++ ){
pIter->R = xxx;
pIter->G = xxx;
pIter->B = xxx;
}
pIter -= src->width;
pIter += dst->width;
}
这样的迭代仅使用了++
运算符. 很高效.
二维数组请格外注意迭代的内存对齐宽度
当然, 使用绝对地址index
有助于我们DEBUG, 方便查看索取的索引是否正确. 各有利弊.
4. 公式代入计算 vs 前后做差计算 ?
在求一个函数的离散值时, 也许可以通过通项获得其递推关系, 利用递推节省算力.
以下 A
采用直接代入函数求值, B
则采用函数的递推进行累加.
const ys = 70, ye = 80;
int A = 0;
for( int y=ys; y<=ye; y++ ){
A = (y-((ye+ys)/2))*(y-((ye+ys)/2));
printf("A=%d\n", A);
}
int B = ((ye-ys)/2)*((ye-ys)/2);
for( int y=ys; y<=ye; y++ ){
printf("B=%d\n", B);
B += 2*(y+1)-(ye+ys) -1;
}
两次的运行结果是相同的, 然而A使用了乘法, 而B只是用了累加.
当然, 你可以将 /2
改成>>1
, 就更高效了哦. 注意事项见上文.
推导证明, 日后有空再写~