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

加入VIP,免费下载
 

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

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

下载须知

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

版权提示 | 免责声明

本文(使用gcc和glibc来优化程序转载共20页文档Word文档格式.docx)为本站会员(b****1)主动上传,冰点文库仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰点文库(发送邮件至service@bingdoc.com或直接QQ联系客服),我们立即给予删除!

使用gcc和glibc来优化程序转载共20页文档Word文档格式.docx

1、if(sizeof(int)=sizeof(long int)|(type=0)sizeof运算总是在编译时进行,因此增加的条件表达式总是在编译时计算.如果long int和int确实相同,那么这个函数就可以被编译器优化.进一步优化,利用limits.h中定义的宏#include limits.h long int add(long int a,void*ptr,int type)#if LONG_MAX!=INT_MAX if(type=0)else#endif return a+*(long int*)ptr;这样,即便在long int不同于int的平台上,该函数也被优化了2.2节省函数

2、调用(Saving Function Calls)很多函数很短小,相对函数执行的时间,函数调用的代价不可忽视.例如标准库中的字符串函数和数学函数.解决办法有两个:使用宏代替函数,或者用inline函数.一般而言,inline函数和宏一样快,但是更安全.但是如果用到alloca和_builtin_constant_p的时候,可能要考虑用优先使用宏了但是,如果函数被声明为extern,inline并不总是有效了.另外,当gcc的编译优化选项没有打开时,gcc不会展开inline函数.如果inline函数是static的,那么编译器总是会展开该函数,不考虑是否真的值得.尤其是当使用-Os(optim

3、ize for space)选项时,static inline函数是否值得使用就是个问题了.编写正确而又安全的宏并不容易.要注意a)正确使用括号括起参数,例如#define mult(a,b)(a*b)/错误#define mult(a,b)(a)*(b)b)宏定义中的大括号引入新的block,这有时侯会导致问题.#define scale(result,a,b,c)int c_=(c);*(result)=(a)*c_+(b)*c_;下面的代码编译会出现问题:if(.)scale(r,a,b,c);/多余的分号导致编译错误else else正确的写法应该是:dowhile(0)c)如果参数是

4、表达式并且在宏定义中出现多次,尽量避免重复计算.这也是上面例子中要引入变量c_的原因.但这会限制变量c_的类型.d)宏缺乏返回值2.3编译器内部函数(Compiler Intrinsics)绝大部分C编译器都知道内部函数(Intrinsic functions).它们是特殊的inline函数,由编译器提供使用.这些函数用外部实现来代替.gcc2.96的内部函数有*_builtin_alloca:动态分配栈上内存dynamiclly allocate memory on the stack*_builtin_ffs:find first bit set*_builtin_abs,_builtin

5、_labs:absolute value of an integer*_builtin_fabs,_builtin_fabsf,_builtin_fabsl absolute value of floating-point vlaue*_builtin_memcpy copy memory region*_builtin_memset set memory region to give value*_builtin_memcmp compare memory region*_builtin_strcmp*_builtin_strcpy*_builtin_strlen*_builtin_sqrt

6、,_builtin_sqrtf,_builtin_sqrtl*_builtin_sin,_builtin_sinf,_builtin_sinl*_builtin_cos,_builtin_cosf,_builtin_cosl*_builtin_div,_builtin_ldiv integer division with rest*_builtin_fmod,_builtin_frem module and remainder of floating-point value不能保证所有内部函数在所有平台上都定义了.关于intrinsic function,有一个很有用的特性:如果参数在编译时是

7、常数,那么可以在编译时计算其值.例如strlen(foo bar)有可能在编译时就计算好.2.4 _builtin_constant_p _builtin_constant_p并不属于intrinsic function,它是一个类似于sizeof的操作符._builtin_constant_p接收一个参数,如果该参数在运行时是固定不变的(constant at runtime),那么就返回非0值,表示这是一个常量.例如,前面的add函数可以在进一步优化:#define add(a,ptr,type)(_extension_(_buildtin_constant_p(type)?(a)+(ty

8、pe)=0*(int*)(ptr):*(long int*)(ptr):add(a,ptr,type)如果第三个参数为constant,那么这个宏将改变add函数的行为;否则就调用真正的add函数.这样尽量在编译时计算,从而提高了效率.2.5 type-generic macro有时侯我们希望宏对不同的参数数据类型,能正确处理不同数据类型并表现相同的行为,可以借助_typeof_例如前面的scale#define tgscale(result,a,b,c)_externsion_ _typeof_(a)+(b)+(c)c_=(c);这里,c_自动拥有返回值类型,而不是前面固定写的int类型._

9、typeof_(o)定义了与o相同的类型._typeof_的另外一个用途:被ISO C9x用于tgmath中,从而实现一些对任意数据类型(包括复数)都适用的数学函数.错误示例:#define sin(val)(sizeof(_real_(val)sizeof(double)?(sizeof(_real_(val)=sizeof(val)?sinl(val):csinl(val)(sizeof(_real_(val)=sizeof(double)?sin(val):csin(val)sinf(val):csinf(val)上面这个宏的意思是:如果val是虚数(即sizeof(_real_(val

10、)!=sizeof(val),那么对val调用csinl,csin和csinf如果val是实数,且比double精度高,即sizeof(_real_(val)sizeof(double),那么对val调用sinl,就long double,否则调用sin或者sinf.sinl:相当于sin(long double)sin:相当于sin(double)sinf:相当于sin(float)csin:对应的复数sin函数但是这个宏是有错误的,由于整个宏是一个表达式,表达式是有静态的类型的,能代表该表达式的数据类型必须有足够的精度来表示各种值,所以这个表达式的最终数据类型就是comple long d

11、ouble,这并不是我们期望的.正确的实现方法是:(_typeof_(val)_tgmres;if(sizeof(_real_(val)sizeof(double)if(sizeof(_real_(val)=sizeof(val)_tgmres=sinl(val);else_tgmres=csinl(val);else if(sizeof(_real_(val)=sizeof(double)_tgmres=sin(val);_tgmres=csin(val);_tgmres=sinf(val);_tgmres=csinf(val);_tgmres;)上面对_tgmres赋值的6个分支中,真正会

12、执行的那个分支是不存在精度损失的;其他分支都会作为deadcode被编译器优化掉3.help the compilerGNU C编译器提供一些扩展来更清晰的描述程序,从而帮助编译器生成代码.3.1不返回的函数(Functions of No Return)大项目一般都至少有一个用于严重错误处理的函数,这个函数体面的结束应用程序.这个函数一般情况下不会被编译器优化,因为编译器不知道它不返回.void fatal(.)_attribute_(_noreturn_);void fatal(.)/print some message exit(1);/application codeif(d=0)fa

13、tal(.);else a=b/d;函数fatal保证不会返回,exit函数也不返回.因此可以在函数原型上加上_attribute_(_noreturn_).如果没有noreturn的标记,gcc会把上面的代码翻译成下面的形式(伪代码):1)compare dwith zero 2)if not zero jump to 5)3)call fatal 4)jump to 6)5)compute b/d and assign to a6).如果有noreturn标记,gcc可以优化代码,省略4).对应的源代码为a=b/d;3.2常值函数(constant value functions)有些函数

14、的值仅仅取决于传入的参数,这种函数没有副作用,我们称之为pure function.对于相同的参数,这种函数有相同的返回值.举例说明:htons函数要么返回参数(如果是big-endian计算机),要么交换字节顺序(如果计算机是little-endian).这个函数没有副作用,是一个pure function.那么下面的代码可以被优化:short int server=.while(1)struct sockaddr_in s_in;memset(&s_in,0,sizeof s_in);s_in.sin_port=htons(serv);优化后的结果为:serv=htons(serv);s_

15、in.sin_port=serv;从而减少循环中执行的代码,节省CPU.但是编译器并无法知道函数是否是pure function.我们必须给pure function显著的标记:extern uint16_t htons(uint16_t _x)_attribute_(_const_);_const_可以用来标记pure function.3.3 Different Calling Conventions每种平台都支持特定的calling conventions以便由不同语言和编译器写的程序/库能够一起工作.但是,有时侯在某些平台上,编译器支持一种更高效的calling convention.

16、在项目内部使用这种calling convention不会影响系统的其他部分.尤其是在Intel ia32平台上,编译器支持多种不同于标准Unix x86的calling convention,这有时侯会大大提高程序速度.GNU C编译器手册有更详细解释.本节只讨论x86平台.改变函数的calling convention的两个办法:1)命令行选项(command line option):这种方法不安全,所有函数(包括exported function)都受到影响2)对单个函数设置function attribute.3.3.1 _stdcall_一般情况下,函数参数是通过栈来传递的,因此需

17、要在某个位置调整栈指针.ia32 unix平台上标准的calling convention是让调用方(caller)调整栈指针;因此可以延迟调整操作,一次同时调整多个函数的栈指针.如果函数被标记为_stdcall_,这意味这个函数自己调整栈指针.在ia32平台上,这不算是坏注意,因为ia32体系结构提供一个指令,能同时从函数调用返回并调整栈指针.示例:int _attribute_(_stdcall_)add(int a,int b)return a+b;int foo(int a)return add(a,42);int bar(void)return foo(100);上面的代码翻译成汇编

18、大致如下:8 add:9 0000 8B 442408 movl 8(%esp),%eax 10 0004 03442404 addl 4(%esp),%eax 11 0008 C20800 ret17 foo:18 0010 6A2A pushl 19 0012 FF 742408 pushl 8(%esp)20 0016 E8E5FFFF call add 20 FF 21 001b C3 ret27 bar:28 0020 6A64 pushl0 29 0022 E8E9FFFF call foo 29 FF 30 0027 83C404 addl,%esp 31 002a C3 ret

19、从上面的例子可以看出,add函数被标记为_stdcall_,foo函数在调用add后直接返回,不需要调整栈指针,因为add函数已经调整来指针(ret指令完成返回和调整指针操作);而bar函数调用foo函数,调用结束后必须调整栈指针.由此可见,使用_stdcall_是有好处的;但是,现代编译器都已经很智能,能作到一次性为多个函数调用调整栈指针,从而使得生成的代码更少速度更快.此外,以后的发展可能会出现更快的调用方式,所以使用_stdcall_必须非常谨慎.3.3.2 _regparm_ _regparm_只能在ia32平台上使用,它能指明有多少个(最多3个)整数和指针参数是通过寄存器来传递的,而

20、不是通过栈传递.当函数体比较短小,而且参数立刻就能使用时,这种方式效果很显著.假设有下面的例子:int _attribute_(_regparm_(3)return a+b;经过编译优化后,生成的代码时9 0000 01D0 addl%edx,%eax 10 0002 C3 ret这个代码比起3.3.1中add的代码更高效.用寄存器传参数总是很快.3.4 Sibling Calls经常有这样的代码:一个函数最后结束时是在调用另外一个函数.这种情况下生成的伪代码如下:/this is in function f1 ncall function f2 n+1 execute code of f2

21、n+2 get return address from call in f1 n+3 jump back into function f1 n+4 optionally adjust stack pinter from call to f2 n+5 get return address from call to f1 n+6 jump back to caller of f1经过优化,f1在调用f2结束后可以直接返回.3.5使用goto goto有时侯提高效率4.了解库(Knowing the Libraries)4.1 strcpy vs.memcpy strcpy:两个参数src和dest

22、,逐个byte拷贝memcpy:三个参数,src,dest和size,按word拷贝strncpy:3个参数:src,dest和length退出条件:遇到NUL字符或达到拷贝长度逐个检查byte是否为NUL追加NUL字符非gcc内部函数3个参数达到拷贝长度按word检查长度不必追加NUL字符gcc内部函数,特殊优化类似的,mem*和对应的str*函数都存在差别.mem*函数参数多些,一般情况下这不是问题,可以通过寄存器传参数;但是当函数被inline的时候,寄存器可能不够,生成的代码可能稍微复杂一些.建议如下:*尽量别使用strncpy,而使用strcpy*如果要拷贝的字符串很短,用strcp

23、y*如果字符串可能很长,用memcpy 4.2 strcat和strncat关于字符串操作的一个金口玉言(gold rule)是:绝对不要使用strcat和strncat.要使用这两个函数,必须知道长度,并准备足够的空间.定型代码如下:char*buf=.;size_t bufmax=.;if(strlen(buf)+strlen(s)+1 bufmax)buf=(char*)realloc(buf,(bufmax*=2);strcat(buf,s);上面的代码中,已经调用了strlen,strcat中会重复执行strlen操作,因此更高效的作法是:size_t slen;size_t buf

24、len;slen=strlen(s)+1;buflen=strlen(buf);if(buflen+slen bufmax)memcpy(buf+buflen,s,slen);4.3内存分配malloc和calloc:分配堆内存.alloca分配栈内存.malloc的实现:从内核申请内存,可能会调用sbrk系统调用;在某些系统上如果申请的内存很多,可能会调用mmap来分配内存.malloc的内部实现会用相关的数据结构来管理好申请内存,以便释放或者重新申请.因此调用malloc的代价并不低.alloca的实现相对简单得多,起码编译器能直接把它作为inline来编译,alloca只是简单修改一下栈指针就可以了.而且,调用alloca后不需要调用free函数来释放内存.free函数的代价也是不小的.但是,alloca申请的内存只能用在当前函数中,而且alloca不适合用来申请大量内存,很多平台系统出于安全考虑对栈的大小有限制.malloc的实现和内核相关,能更好的处理大内存申请.alloca总是成功的,因为它只是执行修改栈指针操作而已.因此alloca非常适合在函数内部申请局部使用的内存,不比检查申请释放成功,也不必调用free来释放内存,不仅提高性能还简化来代码.示例如下:int tmpcopy(const int*a,int a)

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2