ImageVerifierCode 换一换
格式:DOCX , 页数:17 ,大小:22.62KB ,
资源ID:6325742      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/6325742.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(C代码优化转载.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

C代码优化转载.docx

1、C代码优化转载谈到优化,很多人都会直接想到汇编。难道优化只能在汇编层次吗?当然不是,C+层次一样可以作代码优化,其中有些常常是意想不到的。在C+层次进行优化,比在汇编层次优化具有更好的移植性,应该是优化中的首选做法。1.确定浮点型变量和表达式是 float 型 为了让编译器产生更好的代码(比如说产生3DNow! 或SSE指令的代码),必须确定浮点型变量和表达式是 float 型的。要特别注意的是,以 ;F; 或 ;f; 为后缀(比如:3.14f)的浮点常量才是 float 型,否则默认是 double 型。为了避免 float 型参数自动转化为 double,请在函数声明时使用 float。2

2、.使用32位的数据类型编译器有很多种,但它们都包含的典型的32位类型是:int,signed,signed int,unsigned,unsigned int,long,signed long,long int,signed long int,unsigned long,unsigned long int。尽量使用32位的数据类型,因为它们比16位的数据甚至8位的数据更有效率。3.明智使用有符号整型变量在很多情况下,你需要考虑整型变量是有符号还是无符号类型的。比如,保存一个人的体重数据时不可能出现负数,所以不需要使用有符号类型。但是,如果是要保存温度数据,就必须使用到有符号的变量。在许多地方,

3、考虑是否使用有符号的变量是必要的。在一些情况下,有符号的运算比较快;但在一些情况下却相反。比如:整型到浮点转化时,使用大于16位的有符号整型比较快。因为x86构架中提供了从有符号整型转化到浮点型的指令,但没有提供从无符号整型转化到浮点的指令。看看编译器产生的汇编代码:不好的代码:编译前 编译后double x; mov foo + 4, 0unsigned int i; mov eax, ix = i; mov foo, eax flid qword ptr foo fstp qword ptr x上面的代码比较慢。不仅因为指令数目比较多,而且由于指令不能配对造成的FLID指令被延迟执行。最好

4、用以下代码代替: 推荐的代码:编译前 编译后double x; fild dword ptrint i; fstp qword ptr xx = i;在整数运算中计算商和余数时,使用无符号类型比较快。以下这段典型的代码是编译器产生的32位整型数除以4的代码:不好的代码 编译前 编译后int i; mov eax, ii = i / 4; cdq and edx, 3 add eax, edx sar eax, 2 mov i, eax 推荐的代码编译前 编译后unsigned int i; shr i, 2i = i / 4;总结:无符号类型用于:除法和余数,循环计数,数组下标 有符号类型用于

5、:整型到浮点的转化4.while VS. for在编程中,我们常常需要用到无限循环,常用的两种方法是while (1) 和 for (;)。这两种方法效果完全一样,但那一种更好呢?然我们看看它们编译后的代码:编译前 编译后while (1); mov eax,1 test eax,eax je foo+23h jmp foo+18h编译前 编译后 for (;); jmp foo+23h一目了然,for (;)指令少,不占用寄存器,而且没有判断跳转,比while (1)好。5.使用数组型代替指针型使用指针会使编译器很难优化它。因为缺乏有效的指针代码优化的方法,编译器总是假设指针可以访问内存的任

6、意地方,包括分配给其他变量的储存空间。所以为了编译器产生优化得更好的代码,要避免在不必要的地方使用指针。一个典型的例子是访问存放在数组中的数据。C+ 允许使用操作符 或指针来访问数组,使用数组型代码会让优化器减少产生不安全代码的可能性。比如,x0 和x2 不可能是同一个内存地址,但 *p 和 *q 可能。强烈建议使用数组型,因为这样可能会有意料之外的性能提升。 不好的代码typedef structfloat x,y,z,w; VERTEX;typedef struct float m44; MATRIX;void XForm(float* res, const float* v, const

7、 float* m, int nNumVerts)float dp;int i;const VERTEX* vv = (VERTEX *)v;for (i = 0; i ;x * *m +;dp += vv-;y * *m +;dp += vv-;z * *m +;dp += vv-;w * *m +;*res + = dp;/ 写入转换了的 xdp = vv-;x * *m +;dp += vv-;y * *m +;dp += vv-;z * *m +;dp += vv-;w * *m +;*res + = dp;/ 写入转换了的 ydp = vv-;x * *m +;dp += vv-;y

8、 * *m +;dp += vv-;z * *m +;dp += vv-;w * *m +;*res + = dp;/ 写入转换了的 zdp = vv-;x * *m +;dp += vv-;y * *m +;dp += vv-;z * *m +;dp += vv-;w * *m +;*res + = dp;/ 写入转换了的 wvv +; / 下一个矢量m -= 16; 推荐的代码typedef structfloat x,y,z,w; VERTEX;typedef structfloat m44; MATRIX;void XForm (float* res, const float* v,

9、const float* m, int nNumVerts)int i;const VERTEX* vv = (VERTEX*)v;const MATRIX* mm = (MATRIX*)m;VERTEX* rr = (VERTEX*)res;for (i = 0; i ;x = vv-;x * mm-;m00 + vv-;y * mm-;m01+ vv-;z * mm-;m02 + vv-;w * mm-;m03;rr-;y = vv-;x * mm-;m10 + vv-;y * mm-;m11+ vv-;z * mm-;m12 + vv-;w * mm-;m13;rr-;z = vv-;x

10、 * mm-;m20 + vv-;y * mm-;m21+ vv-;z * mm-;m22 + vv-;w * mm-;m23;rr-;w = vv-;x * mm-;m30 + vv-;y * mm-;m31+ vv-;z * mm-;m32 + vv-;w * mm-;m33;注意: 源代码的转化是与编译器的代码发生器相结合的。从源代码层次很难控制产生的机器码。依靠编译器和特殊的源代码,有可能指针型代码编译成的机器码比同等条件下的数组型代码运行速度更快。明智的做法是在源代码转化后检查性能是否真正提高了,再选择使用指针型还是数组型。6.充分分解小的循环要充分利用CPU的指令缓存,就要充分分解

11、小的循环。特别是当循环体本身很小的时候,分解循环可以提高性能。BTW:很多编译器并不能自动分解循环。不好的代码 推荐的代码/ 3D转化:把矢量 V 和 4x4 矩阵 M 相乘for (i = 0; i ; 4; i +)r = 0;for (j = 0; j ; 4; j +)r += Mj*Vj;r0 = M00*V0 + M10*V1 + M20*V2 + M30*V3;r1 = M01*V0 + M11*V1 + M21*V2 + M31*V3;r2 = M02*V0 + M12*V1 + M22*V2 + M32*V3;r3 = M03*V0 + M13*V1 + M23*V2 + M

12、33*v3;7.避免没有必要的读写依赖当数据保存到内存时存在读写依赖,即数据必须在正确写入后才能再次读取。虽然AMD Athlon等CPU有加速读写依赖延迟的硬件,允许在要保存的数据被写入内存前读取出来,但是,如果避免了读写依赖并把数据保存在内部寄存器中,速度会更快。在一段很长的又互相依赖的代码链中,避免读写依赖显得尤其重要。如果读写依赖发生在操作数组时,许多编译器不能自动优化代码以避免读写依赖。所以推荐程序员手动去消除读写依赖,举例来说,引进一个可以保存在寄存器中的临时变量。这样可以有很大的性能提升。下面一段代码是一个例子: 不好的代码float xVECLEN, yVECLEN, zVEC

13、LEN;.for (unsigned int k = 1; k ; VECLEN; k +)xk = xk-1 + yk;for (k = 1; k ; VECLEN; k+)xk = zk * (yk - xk-1); 推荐的代码float xVECLEN, yVECLEN, zVECLEN;.float t(x0);for (unsigned int k = 1; k ; VECLEN; k +)t = t + yk;xk = t;t = x0;for (k = 1; k ; VECLEN; k +)t = zk * (yk - t);xk = t;8.Switch 的用法Switch 可

14、能转化成多种不同算法的代码。其中最常见的是跳转表和比较链/树。推荐对case的值依照发生的可能性进行排序,把最有可能的放在第一个,当switch用比较链的方式转化时,这样可以提高性能。此外,在case中推荐使用小的连续的整数,因为在这种情况下,所有的编译器都可以把switch 转化成跳转表。 不好的代码int days_in_month, short_months, normal_months, long_months;.switch (days_in_month)case 28:case 29:short_months +;break;case 30:normal_months +;brea

15、k;case 31:long_months +;break;default:cout ; ;month has fewer than 28 or more than 31 days; ; endl;break; 推荐的代码int days_in_month, short_months, normal_months, long_months;.switch (days_in_month)case 31:long_months +;break;case 30:normal_months +;break;case 28:case 29:short_months +;break;default:cou

16、t ; ;month has fewer than 28 or more than 31 days; ; endl;break;9.所有函数都应该有原型定义一般来说,所有函数都应该有原型定义。原型定义可以传达给编译器更多的可能用于优化的信息。尽可能使用常量(const)。C+ 标准规定,如果一个const声明的对象的地址不被获取,允许编译器不对它分配储存空间。这样可以使代码更有效率,而且可以生成更好的代码。10.提升循环的性能要提升循环的性能,减少多余的常量计算非常有用(比如,不随循环变化的计算)。不好的代码(在for()中包含不变的if() 推荐的代码for( i . )if( CONSTA

17、NT0 )DoWork0( i ); / 假设这里不改变CONSTANT0的值elseDoWork1( i ); / 假设这里不改变CONSTANT0的值if( CONSTANT0 )for( i . )DoWork0( i );elsefor( i . )DoWork1( i );如果已经知道if()的值,这样可以避免重复计算。虽然不好的代码中的分支可以简单地预测,但是由于推荐的代码在进入循环前分支已经确定,就可以减少对分支预测的依赖。 把本地函数声明为静态的(static)如果一个函数在实现它的文件外未被使用的话,把它声明为静态的(static)以强制使用内部连接。否则,默认的情况下会把函

18、数定义为外部连接。这样可能会影响某些编译器的优化比如,自动内联。11.考虑动态内存分配动态内存分配(C+中的;new;)可能总是为长的基本类型(四字对齐)返回一个已经对齐的指针。但是如果不能保证对齐,使用以下代码来实现四字对齐。这段代码假设指针可以映射到 long 型。例子double* p = (double*)new BYTEsizeof(double) * number_of_doubles+7L; double* np = (double*)(long(p) + 7L) &; 8L);现在,你可以使用 np 代替 p 来访问数据。注意:释放储存空间时仍然应该用delete p。12.使

19、用显式的并行代码尽可能把长的有依赖的代码链分解成几个可以在流水线执行单元中并行执行的没有依赖的代码链。因为浮点操作有很长的潜伏期,所以不管它被映射成 x87 或 3DNow! 指令,这都很重要。很多高级语言,包括C+,并不对产生的浮点表达式重新排序,因为那是一个相当复杂的过程。需要注意的是,重排序的代码和原来的代码在代数上一致并不等价于计算结果一致,因为浮点操作缺乏精确度。在一些情况下,这些优化可能导致意料之外的结果。幸运的是,在大部分情况下,最后结果可能只有最不重要的位(即最低位)是错误的。不好的代码double a100, sum;int i;sum = 0.0f;for (i=0; i;

20、100; i+)sum += a; 推荐的代码double a100, sum1, sum2, sum3, sum4, sum;int i;sum1 = sum2 = sum3 = sum4 = 0.0;for (i = 0; i ; 0)while (*q ; (*r = a / *q)*q = (*q + *r) ; 1;*r = a - *q * *q; 推荐的代码/ 假设 q != rvoid isqrt(unsigned long a, unsigned long* q, unsigned long* r)unsigned long qq, rr;qq = a;if (a ; 0)w

21、hile (qq ; (rr = a / qq)qq = (qq + rr) ; 1;rr = a - qq * qq;*q = qq;*r = rr;16.赋值与初始化先看看以下代码:class CIntint m_i;public:CInt(int a = 0):m_i(a) cout ; ;CInt; ; endl; CInt() cout ; ;CInt; ; endl; CInt operator + (const CInt&; a) return CInt(m_i + a.GetInt(); void SetInt(const int i) m_i = i; int GetInt() const return m_i; ;

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1