MySQL編譯選項-fno-strict-aliasing隨手記
最近釋出的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 <stdio.h>
#include <stdint.h>
int main()
{
int a = 0x12345678;
uint16_t* const sp = (uint16_t*)&a;
uint16_t hi = sp[0];
uint16_t lo = sp[1];
sp[1] = hi;
sp[0] = lo;
printf("%x
", a);
return 0;
}
函式的功能很簡單,對一個數字的高低位進行交換
gcc版本
$gcc --version
gcc (GCC) 4.4.6 20110731 (Red Hat 4.4.6-3)
執行strict aliasing (O2及以上預設開啟)
$gcc -O2 x.c
$./a.out
12345678
非strict aliasing (顯式指定-fno-strict-aliasing)
$gcc -O2 -fno-strict-aliasing x.c
$./a.out
56781234
不同的gcc flag,兩次的執行結果居然完全不相同,只有第二次才實現了預期功能。因為預設情況下不報warning,我們把告警開啟看看:
$gcc -O2 -Wstrict-aliasing x.c
x.c: In function ‘main’:
x.c:13: warning: dereferencing pointer ‘sp’ does break strict-aliasing rules
x.c:9: warning: dereferencing pointer ‘sp’ does break strict-aliasing rules
x.c:8: note: initialized from here
x.c:10: warning: dereferencing pointer ‘({anonymous})’ does break strict-aliasing rules
x.c:10: note: initialized from here
x.c:12: warning: dereferencing pointer ‘({anonymous})’ does break strict-aliasing rules
x.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 main
Dump of assembler code for function main:
0x00000000004004d0 <+0>: sub $0x18,%rsp
0x00000000004004d4 <+4>: mov $0x4005f8,%edi
0x00000000004004d9 <+9>: xor %eax,%eax
0x00000000004004db <+11>: movw $0x5678,0xe(%rsp)
0x00000000004004e2 <+18>: movw $0x1234,0xc(%rsp)
0x00000000004004e9 <+25>: mov 0xc(%rsp),%esi
0x00000000004004ed <+29>: callq 0x4003b8 <printf@plt>
0x00000000004004f2 <+34>: xor %eax,%eax
0x00000000004004f4 <+36>: add $0x18,%rsp
0x00000000004004f8 <+40>: retq
End of assembler dump.
錯誤的 (gcc -O2 -fstrict-aliasing x.c)
(gdb) disassemble main
Dump of assembler code for function main:
0x00000000004004d0 <+0>: sub $0x18,%rsp
0x00000000004004d4 <+4>: mov $0x12345678,%esi
0x00000000004004d9 <+9>: mov $0x4005f8,%edi
0x00000000004004de <+14>: xor %eax,%eax
0x00000000004004e0 <+16>: movw $0x5678,0xe(%rsp)
0x00000000004004e7 <+23>: movw $0x1234,0xc(%rsp)
0x00000000004004ee <+30>: callq 0x4003b8 <printf@plt>
0x00000000004004f3 <+35>: xor %eax,%eax
0x00000000004004f5 <+37>: add $0x18,%rsp
0x00000000004004f9 <+41>: retq
End of assembler dump.
但是如果我換成高版本的gcc,例如4.8版本,兩種編譯方式都沒有問題,甚至加上-Wstrict-aliasing連報警都沒有。只有加上-Wstrict-aliasing=1才報warning
$/opt/rh/devtoolset-2/root/usr/bin/gcc --version
gcc (GCC) 4.8.2 20140120 (Red Hat 4.8.2-15)
$/opt/rh/devtoolset-2/root/usr/bin/gcc -O2 -fno-strict-aliasing x.c
$./a.out
56781234
/opt/rh/devtoolset-2/root/usr/bin/gcc -O2 -fstrict-aliasing x.c
$./a.out
56781234
$/opt/rh/devtoolset-2/root/usr/bin/gcc -O2 -fstrict-aliasing -Wstrict-aliasing=1 x.c
x.c: In function ‘main’:
x.c:9:2: warning: dereferencing type-punned pointer might break strict-aliasing rules [-Wstrict-aliasing]
uint16_t* const sp = (uint16_t*)&a;
網上搜了一下,Stackoverflow上有一些類似的問題 1, 2。 我理解這應該是gcc編譯器的高版本對型別轉換規則的識別可能做的更加好,細節不太瞭解,如有看到這篇文章的朋友,求幫忙修正 ?
!!!無論如何, 如果你需要開啟strict aliasing, 一定要開啟Wstrict-aliasing,消除程式碼warning。 同時在程式碼上也要儘量減少這種不同型別的轉換。
在MySQL移除-fno-strict-aliasing後, 也看到了一些擔憂,因為mysql的codebase畢竟已經相當古老了, 而當前並沒有一些靜態或動態的分析工具能夠找到所有違反strict aliasing規則的地方。可能存在潛在的風險。
相關文章
- PHP編譯選項PHP編譯
- GCC編譯選項GC編譯
- GCC 編譯選項GC編譯
- vc 編譯連線選項編譯
- cmake中新增 -g編譯選項編譯
- -debug(C# 編譯器選項)C#編譯
- typescript 3.2 新編譯選項strictBindCallApplyTypeScript編譯APP
- FFmpeg音視訊編譯配置選項編譯
- 越獄手記:手動編譯安裝 Electra編譯
- centos手動編譯安裝apache、php、mysqlCentOS編譯ApachePHPMySql
- 核心編譯選單中相關選項的意義(轉)編譯
- C# 編譯器選項(Visual Studio配置)C#編譯
- Git.Framework 框架隨手記--ORM編輯刪除GitFramework框架ORM
- js隨手記-1JS
- MySQL-5.6.29原始碼編譯安裝記錄MySql原始碼編譯
- gcc常用的編譯選項對程式碼的影響(轉)GC編譯
- 編譯安裝mysql編譯MySql
- MySQL隨機選取資源MySql隨機
- Kaldi學習手記(一):Kaldi的編譯安裝編譯
- MySQL5.7.17原始碼編譯安裝時的注意事項MySql原始碼編譯
- Bitbucket / Sourcetree 隨手筆記筆記
- Hive隨手記——建庫Hive
- 自己動手編譯OpenJDK編譯JDK
- ubuntu手動編譯lampUbuntu編譯LAMP
- MySQL如何選擇隨機記錄?有好幾種方式呢!MySql隨機
- GCC的-g選項應該在編譯階段起作用(轉)GC編譯
- Mysql 5.7.17 編譯安裝MySql編譯
- Qt編譯MySQL驅動QT編譯MySql
- kafka文件: 配置選項翻譯Kafka
- 管理 MySQL Shell 配置選項MySql
- MySQL5.5原始碼編譯新增編譯備註資訊~MySql原始碼編譯
- ios layoutSubviews呼叫隨手筆記iOSView筆記
- Knockout.js隨手記(5)JS
- Knockout.js隨手記(4)JS
- Knockout.js隨手記(6)JS
- Knockout.js隨手記(7)JS
- Knockout.js隨手記(8)JS
- Knockout.js隨手記(1)JS