嫉妒是一个人对另一个人发自内心的认可(GCC strict aliasing – 嫉妒就是承认自己不如别人)
事情是这样的 。我们对tair(淘宝的分布式Key/Value系统)动了一次大手术 ,更换了网络框架 ,经过长时间的测试/调试 ,终于完全通过了回归测试 。但要打包发布的时候 ,却发现服务器可以正常启动 ,但却完全无法接受请求 。调试无果 ,对比打包前后程序的差异 ,仅在于是否使用-O2选项对程序进行编译优化 。
无头苍蝇一样 ,Google搜索“gcc optimization problems ” ,找到StackOverflow上面的这个帖子 ,“抱着试试看的心态 ” ,在编译选项中加入-fno-strict-aliasing ,bingo!
-fno-strict-aliasing这个选项是做什么的?aliasing又是什么?C和C++的标准给出了说明: Strict aliasing is an assumption, made by the C (or C++) compiler, that dereferencing pointers to objects of different types will never refer to the same memory location (i.e. alias eachother.)即是说,在strict aliasing规则下 ,C/C++编译器认为 ,“不同类型 ”的指针(准确说是lvalue)一定不会引用同一个内存区域(即aliasing) 。在这个规则的前提下,编译器就可以进行相应的优化 。看下面这个函数:
编译并运行:
一切正常 ,不是吗?但如果把函数foo的参数类型改作double* ,运行结果“可能 ”会是:
在加-O2选项的情况下程序编译该程序 ,输出竟然是1 ,难道*ptr=3没有被执行吗?不是的 ,*ptr=3确实是执行了的 ,全局变量n在函数返回时也确实已经是3了(你可以在fprintf之后打印出n值做验证) ,但是foo函数中的语句return n却被优化成了return 1 。为什么呢?因为后者比前者稍了一次内存访问 。编译器为什么做这样的优化 ,为什么在ptr为int*时不做此优化?
这就涉及到strict aliasing的具体规则了 。首先定义一下alias:两个不同的变量引用了同一个对象(内存区域) ,那么就称这两个变量互为alias 。下面是C99中可以互为alias的所有情况 ,除此之外的其他情况下 ,如果编译时指定-fstrict-aliasing(-O2及以上优化时自动指定) ,那么就执行strict aliasing: a type compatible with the effective type of the object, a qualified version of a type compatible with the effective type of the object, a type that is the signed or unsigned type corresponding to the effective type of the object, a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object, an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), a character type.大致是这样的:两个类型兼容的变量可以互为alias,即使使用了signed/unsigned和const/volatile等修饰符;一个类型可以与另一个包含与该类型兼容的成员的struct/union类型的变量互为alias;char类型可以与任意类型互为alias 。C++中可以互为alias的还可以是父类与子类 。
可以使用-fno-strict-aliasing来取消strict aliasing规则 ,同时也就放弃了这个规则带来的优化空间 ,放弃了一定的性能提升。如果你也遇到了文章开头我遇到的问题,而且担心-fno-strict-aliasing的性能损失 ,那就只能找出违反规则的代码 ,调整该代码 ,或者仅仅取消该代码的strict aliasing 。
基本就是这样了 ,最后总结一下 。GCC的aliasing与优化紧密相关 ,在指定-O2及以上优化级别时自动打开-fstrict-aliasing ,执行strict aliasing规则以优化编译程序。如果你的程序不遵守该规则(比如上面foo函数中出现double*ptr和n同时应用n的情况) ,就极有可能受到惩罚 。GCC中与strict aliasing相关的选项除了-fstrict-aliasing/-fno-strict-aliasing ,还有warning选项-Wstrict-aliasing=n ,这个选项在你违反stict aliasing时给出警告 ,其中n为检查的力度 ,一般设为2 。
最后 ,如果想深入了解strict aliasing,可以参考这篇Understanding Strict Aliasing 。另外 ,GCC的官方文档中有和优化选项相关的描述 ,其中也提到了strict aliasing 。
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!