第一章 引入基础
基本思路 优化循环,降低计算量
- 降低循环层数 – x86上有明显效果, arm上未见明显效果
- 去除if语句 --有明显效果
1.x86上优化
//原代码
int i = 0, j = 0;
int P[25] = {0};
int y_add = queryYAdd(fmt);
uint8_t *myGrad = new uint8_t[w_algn*h*2];
uint8_t *myRawY = nullptr;
uint16_t *raw_addr = (uint16_t *)y_addr;
smd_sub = 0;
if(y_addr == nullptr)
{
return BAD_VALUE;
}
if( fmt == BAYER_RAW)
{
myRawY = new uint8_t[w_algn*h*2];
for (i = start_Dpoint.x; i < start_Dpoint.x + x_width - 1; i++)
{
for ( j = start_Dpoint.y; j < start_Dpoint.y + y_height - 1; j++)
{
if(1){
int idx = (j * w_algn + i);
//int idx_buttom = idx + w_algn;
//int idx_right = idx + 1;
int value_idx= raw_addr[idx] >>4;
int value_idx_buttom= raw_addr[idx + w_algn] >>4;
int value_idx_right = raw_addr[idx + 1] >>4;
int diff1 = abs(value_idx - value_idx_buttom);
int diff2 = abs(value_idx - value_idx_right);
if( value_idx < 10 || value_idx_buttom < 10 || value_idx_right < 10)
{
continue;
}
if( diff1/5 == 25 || diff2/5 == 25 ){
raw_addr[idx] = 0;
}
#endif
smd_sub += diff1 * diff2;
P[int(diff1/10)]++;
P[int(diff2/10)]++;
myGrad[idx] = (int)(diff1 * diff2)/(255.0)
//Rwa Y
myRawY[idx] = raw_addr[idx] >>4<<2;
}
}
}
printf("smd_sub00 %d\n", (int)smd_sub);
for(int k =0 ; k< 25; k++)
{
printf("%d ", P[k]);
}
printf("\n");
1000次 原始数据 27s
第一轮:()
1.去掉core函数中的alloc ->19s
2.去掉统计,去掉if ->16s
3.去掉 中间值 diff1*diff2 ->13s
4.去掉循环嵌套,两层变一层 ->7s
5.加if 屏蔽瑕疵点 ->10s
6.两个abs去掉一个->无影响
7.去掉if -->8.4
8.减为一句->7.18s
9.去掉for中乘法->7.08
10.将smd 结果值double修改为int ->6.7s
经过第一轮,core中只有一个for循环,只有一行代码
//优化后
if( fmt == BAYER_RAW)
{
int max_size = w_algn * (y_height-1);
for (i = 0; i < max_size; i++)
{
int_smd_sub += abs((raw_addr[i] - raw_addr[i + w_algn]) * (raw_addr[i] - raw_addr[i + 1]));
}
}
第二轮:
1.未进行
第三轮:
1.未进行
2. arm上的优化
384*384 raw 300次 原始数据0.32018s , 32位程序
camera打开时候原始数据是7-8ms
在x86的基础上
第一轮:
1.改为128位一次 ->0.2962s
2.去掉 volatile ->0.1806031s
3.去掉 register->0.185144s
.去掉 extern c 变差 ->0.186s
4.改为4位 ->0.07065s (32位程序最佳)
改为32位 ->0.05836s(64位程序最佳)
改为48位(qcom8953最佳)
5.另开循环,增加去行尾部,瑕疵点 ->0.071409s
6.去掉log ->0.065346s
7.去掉for中的两个++ ->0.062144s
总结:
优化的原则. 降低循环层数>降低循环次数>=降低单次循环的运算量
1.尽量使用单次循环
2.去除循环内if语句
3.减少循环内中间变量数量
4.循环内不使用double类型。
5.循环内一次处理多位数据,降低循环次数
细节:
1.指针访问不应该大于8 (pointer+8 ) --不要出现(pointer+n ) ,n>=8
2.for(;不应有计算;)
3.不应该有log,android设备一次log的时间为20us
x86架构:
1.使用register变量
2.减少循环次数,增加单次计算量,能指数减少处理时间
第二章 Arm汇编及NEON优化
需要学习一下知识:
- Arm架构32和64位寄存器
- Arm汇编指令集 Arm官网
- ddd
- 熟知每一条NEON指令集,具体能做什么–这个是关键
总结:
没有减少指令数量很难优化
1.经典指令
byte5[1] & 0x3) << 8 会被解释成 UBFX R8, R4, #8, #2
UBFX
UBFX Rd(目标), Rn(源), #lsb(起始位0-31), #width(提取宽度)
实例: 图像 大小为 w * h * 5/4的图像,转为w*h图像
原始存储:
R1低8 | R2低6+R1高2 | R3低4+R2高4 | R4低2+R3高6 | R4高8 |
---|
转为
R1高8 | R2高8 | R3高8 | R4高8 |
---|
核心思路及代码:
由于没有5个通道的指令,五个一组的数据不太好处理。
每次加载 8*8*5 Bytes,用neon处理器处理为 8*8*4
uint8x8x4_t byte_8x4 = vld4_u8(lineBuf);//加载8*4个Byte
uint8x8_t byte5 = vld1_u8(lineBuf+32); //加载8*1个Byte
...8*5的数据重新映射到4通道上,体力活
//流处理器,一次性处理 8*4个Byte像素
//vshr_n_u8 右移动取高位
//vshr_n_u8 第二个操作数左移取低位并和第一个操作数按位或。
byte_dest_8x4_final.val[0] = vsli_n_u8( vshr_n_u8( byte_dest_8x4.val[0], 2), byte_dest_8x4.val[1], 6);
byte_dest_8x4_final.val[1] = vsli_n_u8( vshr_n_u8( byte_dest_8x4.val[1], 4), byte_dest_8x4.val[2], 4);
byte_dest_8x4_final.val[2] = vsli_n_u8( vshr_n_u8( byte_dest_8x4.val[2], 6), byte_dest_8x4.val[3], 2);
lineBuf+=40;
//asm ("PLD [%0, #4095]"::"r" (lineBuf));
vst4_u8( pu2DestBuf, byte_dest_8x4_final); // vst4_u8 一次写入8*4个数据到addr
优化结果:2000*1000的数据,优化前C代码8000us,neon优化后4200ms左右。
理论上,处理时间可以降低为180us 6 (6个流处理指令 单次循环时间),需要汇编级的优化。暂时没有实现。