大话处理器之编写高效的代码总结

2011.11.15,阅读《大话处理器》第6章《编写高效的代码—时间就是生命》,主要内容笔记如下。

Profile在美剧《犯罪心理》中的意思是剖析犯罪分子的心理。在软件开发中,就叫软件性能剖析。性能剖析工具分析每个函数(有的工具能分析到每个循环)的执行时间。

常用的性能剖析软件有:IBM的Rational Quantify、Intel的VTune、AMD的CodeAnalyst,DSP的软件集成开发环境中也自带有这种工具。

(一)  减少指令数;

1.      使用更快的算法—算法,程序设计的灵魂;(高斯的故事)

2.      选用合适的指令—合适的人做合适的事;(主要是处理器的专用汇编指令)

3.      降低数据精度—比特也要省着用;(单精度浮点数绝对值函数fabsf()比双精度fabs()快)

4.      减少函数调用—不要老打断我;

a)      将小函数直接写成语句;

b)      将小函数写成宏;

c)       将函数声明为内联函数;

5.      空间交换时间;(Fibonacci数列某项值的计算、Google百度搜索引擎算法)

6.      减少过保护—打不破的部门墙;(函数入参检查、保护可适当去掉)

(二)  减少处理器不擅长的操作—不要逼我做我不喜欢的事情;

1.      少用乘法;(可转化为移位运算)

2.      少用除法、求余;

3.      在精度允许的条件下,将浮点数定点化;

《反恐精英》中扔了烟雾弹之后的半透明烟雾效果,使用图像的alpha混合:将两张图像进行半透明的混合来实现,混合后新图像的每个像素的颜色值为:

Pixel_C =(int)(Pixel_A * alpha + Pixel_B * (1 – alpha));

alpha为透明度,是介于0和1之间的小数,随着之间的推移,烟雾图像的alpha值越来越小。当alpha为0时,烟雾效果完全消失。

         由于浮点运算耗时,可以将alpha定点化为0~32之间的一个整数,0表示完全显示B图像,32表示完全显示A图像,中间分为32个等级,代码变为

Pixel_C =(Pixel_A * alpha + Pixel_B * (32 – alpha) + 16) >> 5);

由于全部都是定点语句,执行时间大幅减少。

4.      尽量减少分支;(if和switch跳转语句会打乱流水线执行,影响效率)

5.      将最有可能进入的分支放在if中,而不是else中;

(三)  优化内存访问

1.      少使用数组,少使用指针;(用局部变量代替)

2.      少使用全局变量;(用局部变量代替)

3.      一次多访问一些数据;

4.      数据对齐访问;对2字节变量,它的起始地址应该为2的整数倍;对于4字节变量,它的起始地址应该为4的整数倍;对于8字节变量亦是如此;

5.      大数据结构时的Cacheline对齐;(Intel处理器的Cache line大多为64 byte,在对大数据结构分配内存时,起始地址最好为64 byte的整数倍,这样Cachemiss的次数最少。)

6.      程序、数据访问符合Cache的时间、空间局部性;(两重for循环处理二维数组例子;程序组织、存储符合Cache局部性原则)

7.      多线程编程时,避免falsesharing;

8.      自己管理内存的动态分配;

9.      隐藏数据搬移时间;(根据处理器支持,用DMA将SRAM中数据搬移到处理器、Cache预取机制)

(四)  充分利用编译期进行优化—编译器:我才是优化第一高手;

1.      编译器提供了分级优化选项;

2.      简化表达式;(消除重复计算)

3.      提取公共代码;(把两个分支中的公共代码提到外面)

4.      循环展开、软件流水;(比如可以使用编译期的预编译指令)

5.      自动向量化;(使用关键字restrict)

6.      高效的数据组织;(编译器对变量、函数分配存储空间,减少Cache miss)

7.      指令并行化;

(五)  利用多核加速程序—人多力量大;

1.      并行计算;(分工:任务划分、数据划分、数据流划分;Amdahl’s law,阿姆达尔定律:串行与并行分开;多线程编程)

2.      OpenMP;(并行编程框架,适合共享内存系统、多核处理器)

 

 

1.      减少重复计算

for (i =0; i < strlen(pswd); i++)

{

    if (isNum(pswd[i]))

    {

        j++;

    }

}

...

 

2.  减少分支

for (i =0; i < 100; i++)

{

    if (i % 2 == 0)

    {

        a[i] = x;

    }

    else

    {

        a[i] = y;

    }

}

修改后:

for (i =0; i < 100; i+=2)

{

    a[i] = x;

    a[i+1] = y;

}

 

3.  程序、数据访问符合Cache的时间、空间局部性:

for (j< 0; j < 500; j++)

{

    for (i < 0; i < 500; i++)

    {

        sum += a[i][j];

    }

}

优化后:

for (i< 0; i < 500; i++)

{

    for (j < 0; j < 500; j++)

    {

        sum += a[i][j];

    }

}

 

4.  自动向量化:

void add(float *restrict a, float *restrict b, float *restrict c)

{

    int i;

    for (i = 0; i < 4; i++)

    {

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

    }

}

retrict关键字告诉编译期a、b、c三个指针指向的空间是没有交叠的,这样编译器可对它们做合适的优化。编译器能使用SIMD指令来优化。

/* VC 6.0帮助文档 */

i = 100;

while (i< 0)

{

    i += x + y;

    *p = i;

}

如果使用Assume No aliasing编译选项,编译期能将x+y放到循环外面;否则不会,因为指针p可能指向x或y;

 

5.  指令并行化:

x = a +b;

y = x +c;

z = y +d;

优化为:

x = a +b;

y = c +d;

z = x +y;



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值