MySQL编译选项 -fno-strict-aliasing随手记

阅读原文请点击

摘要:最近发布的MySQL8.0.2版本中,将gcc的编译选项从--fno-strict-aliasing移除,也就是说打开strict aliasing, 根据worklog #10344 的描述,在单线程的性能测试中,有最多%4的性能提升,还是相当可观的。

最近发布的MySQL8.0.2版本中,将gcc的编译选项从--fno-strict-aliasing移除,也就是说打开strict aliasing, 根据worklog #10344的描述,在单线程的性能测试中,有最多%4的性能提升,还是相当可观的。这个flag在我们内部编译版本中也是一直打开的,但一直不知甚解。本文是网上搜索文档和自己试验的小结。

首先strict aliasing是个什么鬼? --fno-strict-aliasing对应的是--f-strict-aliasing,GCC文档的解释如下:

Allow the compiler to assume the strictest aliasing rules applicable to the language being compiled. For C (and C++), this activates optimizations based on the type of expressions. In particular, an object of one type is assumed never to reside at the same address as an object of a different type, unless the types are almost the same. For example, an unsigned int can alias an int, but not a void* or a double. A character type may alias any other type.

Stackoverflow上关于strict aliasing规则的问题

当使用strict aliasing时, 编译器会认为在不同类型之间的转换不会发生,因此执行更激进的编译优化,例如reorder执行顺序。

Strcit aliasing只能隐式的开启或者显式的禁止掉。在-o2或更高的编译级别上会被隐式开启。

这里举个简单的例子,参考网络上这篇文章

$cat x.c#include#includeintmain(){inta =0x12345678;uint16_t*constsp = (uint16_t*)&a;uint16_thi = sp[0];uint16_tlo = sp[1];        sp[1] = hi;        sp[0] = lo;printf("%x\n", a);return0;}

函数的功能很简单,对一个数字的高低位进行交换

gcc版本

$gcc --versiongcc (GCC)4.4.620110731(Red Hat4.4.6-3)

执行strict aliasing (O2及以上默认打开)

$gcc-O2  x.c$./a.out12345678

非strict aliasing (显式指定-fno-strict-aliasing)

$gcc-O2 -fno-strict-aliasing x.c$./a.out56781234

不同的gcc flag,两次的执行结果居然完全不相同,只有第二次才实现了预期功能。因为默认情况下不报warning,我们把告警打开看看:

$gcc -O2 -Wstrict-aliasing x.cx.c:In function ‘main’:x.c:13:warning:dereferencing pointer ‘sp’ doesbreakstrict-aliasing rulesx.c:9:warning:dereferencing pointer ‘sp’ doesbreakstrict-aliasing rulesx.c:8:note:initialized from herex.c:10:warning:dereferencing pointer ‘({anonymous})’ doesbreakstrict-aliasing rulesx.c:10:note:initialized from herex.c:12:warning:dereferencing pointer ‘({anonymous})’ doesbreakstrict-aliasing rulesx.c:12:note:initialized from her

果然在使用strict aliasing时,因为破坏了严格aliasing的规则大量报警,因此如果我们要使用strict aliasing,一定要打开报警,并重视每个warning。

回到刚才的问题,为什么strict aliasing会输出最原始的数据,而不是修改后的数据呢 ? 看起来就好像后面的修改全部被忽略掉了一样。 我们来看看编译后的代码。可以看到两个汇编代码完全不相同。编译器认为代码里不可能出现不规范的类型转换,所以在错误的案例里,a的未被修改的值被直接抛给了printf函数

正确的 (gcc -O2 -fno-strict-aliasing x.c)

(gdb) disassemble mainDump of assembler code for function main:0x00000000004004d0<+0>:sub$0x18,%rsp0x00000000004004d4<+4>:mov$0x4005f8,%edi0x00000000004004d9<+9>:xor%eax,%eax0x00000000004004db<+11>:    movw$0x5678,0xe(%rsp)0x00000000004004e2<+18>:    movw$0x1234,0xc(%rsp)0x00000000004004e9<+25>:mov0xc(%rsp),%esi0x00000000004004ed<+29>:    callq0x4003b80x00000000004004f2<+34>:xor%eax,%eax0x00000000004004f4<+36>:add$0x18,%rsp0x00000000004004f8<+40>:    retqEnd of assembler dump.

错误的 (gcc -O2 -fstrict-aliasing x.c)

(gdb) disassemble mainDumpofassembler codeforfunctionmain:0x00000000004004d0<+0>:    sub    $0x18,%rsp0x00000000004004d4<+4>:    mov    $0x12345678,%esi0x00000000004004d9<+9>:    mov    $0x4005f8,%edi0x00000000004004de<+14>:    xor%eax,%eax0x00000000004004e0<+16>:    movw  $0x5678,0xe(%rsp)0x00000000004004e7<+23>:    movw  $0x1234,0xc(%rsp)0x00000000004004ee<+30>:    callq0x4003b80x00000000004004f3<+35>:    xor%eax,%eax0x00000000004004f5<+37>:    add    $0x18,%rsp0x00000000004004f9<+41>:    retqEndofassembler dump.

但是如果我换成高版本的gcc,例如4.8版本,两种编译方式都没有问题,甚至加上-Wstrict-aliasing连报警都没有。只有加上-Wstrict-aliasing=1才报warning

$/opt/rh/devtoolset-2/root/usr/bin/gcc--versiongcc(GCC)4.8.220140120(Red Hat4.8.2-15)$/opt/rh/devtoolset-2/root/usr/bin/gcc-O2 -fno-strict-aliasingx.c$./a.out56781234/opt/rh/devtoolset-2/root/usr/bin/gcc-O2 -fstrict-aliasing x.c$./a.out56781234$/opt/rh/devtoolset-2/root/usr/bin/gcc-O2 -fstrict-aliasing -Wstrict-aliasing=1x.cx.c: Infunction‘main’:x.c:9:2: warning: dereferencing type-punned pointer mightbreakstrict-aliasingrules [-Wstrict-aliasing]  uint16_t* constsp= (uint16_t*)&a;

网上搜了一下,Stackoverflow上有一些类似的问题1,2。 我理解这应该是gcc编译器的高版本对类型转换规则的识别可能做的更加好,细节不太了解,如有看到这篇文章的朋友,求帮忙修正 :)

!!!无论如何, 如果你需要打开strict aliasing, 一定要打开Wstrict-aliasing,消除代码warning。 同时在代码上也要尽量减少这种不同类型的转换。

在MySQL移除-fno-strict-aliasing后, 也看到了一些担忧,因为mysql的codebase毕竟已经相当古老了, 而当前并没有一些静态或动态的分析工具能够找到所有违反strict aliasing规则的地方。可能存在潜在的风险。

阅读原文请点击

你可能感兴趣的:(MySQL编译选项 -fno-strict-aliasing随手记)