和vs版本关系_栈局部变量优化探究,意外发现了 vs 的一个 bug ?
緣起
我在《棧又溢出了》一文中記錄了一個奇怪的棧溢出問題。雖然解決了,但是總感覺哪里不太合理。我想,vs 一定有一個合理的設置。一起折騰起來吧!
查找工程設置
本以為能找到某個編譯選項對局部變量占用內存的行為進行控制。看遍了工程設置也沒發現相關的設置項。release 版會不會有什么不同呢?畢竟,release 版會做一些優化,于是抱著試試看的心態編譯了 release 版。您猜怎么著,居然沒崩潰!
反匯編
趕緊查看下相關的反匯編,果然和預想的一樣,函數局部變量占用的棧空間不再是 debug 版中的 0x12C0DC,而是 0x064008。換算成十進制大概是 409608。
這說明三個局部變量被優化成了一個!release 優化果然給力!但是具體是哪一項優化導致的呢?該怎么排查呢?debug 版和 release 版結果不一樣,最簡單粗暴的方法就是找不同!
對比設置
把 debug 版的配置保存到 debug.txt,release 版的配置保存到 release.txt。然后用 beyond compare 比較兩個文件的內容。
為了讓各位小伙伴兒更清晰的對比 debug 和 release 的區別,我特意調整了先后順序,把不同的選項放到了后面!最重要的不同在最后一行。
debug 版為 /ZI /Gm /Od /RTC1 /MDd,release 版為 /Zi /GL /Gy /Gm- /O2 /Oi /MD。
不知道大家熟悉哪幾個,我比較熟悉 /Od (禁用優化,debug 版的默認配置)和 /O2 (使速度最大化,release 版的默認配置)。
先把 /Od改成 /O2試試,沒想到提示錯誤 error D8016: “/ZI”和“/O2”命令行選項不兼容。那就把 /ZI改成 /Zi。說明:/Zi 和 /ZI 都是用來配置生成調試符號的,與當前調查的問題基本沒什么關系。使用 /ZI 生成的符號文件可以支持編輯并繼續運行,低版本的 vs 對編輯并繼續運行的功能支持的并不好,一般 /Zi 就好。
改好后,繼續編譯。沒想到又遇到了如下錯誤:error D8016: “/O2”和“/RTC1”命令行選項不兼容。改成默認值,和 release 一樣的設置。
再次編譯,這次終于編譯通過了。再次運行 debug版的程序,不報棧溢出了。趕緊查看反匯編確認,傳給 _chkstk的大小不再是之前的 0x12C0DC了,而是 0x064004,轉換成十進制是 409604。如果把 release 版的 /O2 改成 /Od,release 版會不會也報棧溢出呢?
讓 release 版也報棧溢出
說干就干,改好配置后編譯,運行。果然,棧溢出了。
至此,基本上找到了關鍵的編譯選項—— /O2。但這就完了么?
/O2
在 google 中輸入 /O2 msdn ,回車。第一條搜索結果就是 /O2 的官方文檔[1]。看看 /O2 都做了哪些優化。
原來,/O2相當于設置了這么多選項,到底是哪一個在起作用呢?一共才 7個,不多,挨個試!恢復 debug版的優化選項為 /Od。然后添加額外選項。先試 /Og。沒想到運氣這么好,一下就猜中了。這里就不放運行結果圖了。
再查看下/Og 的官方文檔[2]。這里截取部分關鍵描述。
如何快速得到編譯選項?
可以在工程上 右鍵,屬性,配置屬性,c/c++,命令行 來快速找到所有編譯選項。如下圖:
vs2013 的 bug?
為了更好的理解棧局部變量優化策略,我準備了很多測試用例,其中有一個用例結果出乎意料!我把代碼和結果貼出來。我想這應該是 vs2013 的一個 bug。
#include?"stdafx.h"struct?BigData
{
????char?data[400?*?1024];
};
struct?BigData1?:?public?BigData
{
????int?data1;
};
struct?BigData2?:?public?BigData1
{
????int?data2;
????BigData1?bigData;
};
void?Use(BigData*?pData)?{?printf("%c",?pData->data[0]);?}
void?CorrpuptStackEasyly(int?argc){
????auto?size?=?sizeof(BigData2);
????printf("size?is?%d",?(int)size);
????if?(argc?==?1)
????{
????????BigData?data;
????????Use(&data);
????}
????else?if?(argc?==?2)
????{
????????BigData1?data;
????????Use(&data);
????}
????else
????{
????????BigData2?data;
????????Use(&data);
????}
}
int?_tmain(int?argc,?_TCHAR*?argv[])
{
????CorrpuptStackEasyly(argc);
????return?0;
}
BigData2 的大小應該是 819212,但是傳遞給 _chkstk 的值卻是 1228824。看下圖就知道我沒瞎說。
這真的是 vs2013 的 bug 嗎?同樣的代碼在高版本的 vs 中會是什么結果呢?vs2019 太大了,我的 c 盤已經爆紅了,不想裝了。偶然發現了一個超級寶藏網址,可以查看源代碼被不同編譯器編譯后的匯編代碼,真是寶藏啊。
寶藏網址
這個寶藏網址是 https://gcc.godbolt.org/。上面的代碼的編譯結果如下圖:
符合預期!看來這的確是 vs2013?的一個?bug了!
總結
- debug 和 release 之所以不同,是因為設置了不同的選項。
- 可以在所有選項下搜索對應的設置選項來快速定位某個設置。
- /Og 可以消除不必要的局部變量,從而避免上文中的棧溢出問題。
References:
[1]/O2 的官方文檔: https://docs.microsoft.com/en-us/cpp/build/reference/o1-o2-minimize-size-maximize-speed?view=msvc-160
[2]/Og 的官方文檔: https://docs.microsoft.com/en-us/cpp/build/reference/og-global-optimizations?view=msvc-160
感謝你的分享,點贊和在看
總結
以上是生活随笔為你收集整理的和vs版本关系_栈局部变量优化探究,意外发现了 vs 的一个 bug ?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数学建模论文分析--2015A高教社杯-
- 下一篇: Unity3D加载资源的四种方式