[英]g++ Optimization : O2 flag fixes a broken code where O3 breaks it again
這段代碼用於匹配 NFA 中的字符串,我認為它需要O(N^2)
內存,當字符串大小為20,000
時可預見地中斷,然后使用-O2
編譯代碼,然后再次中斷-O3
。 編譯是在啟用-std=c++14
下完成的。 在我看來,問題是堆棧溢出。
輸入字符串是"ab"
重復10,000
次,最后加上一個'c'
。下圖包含我試圖匹配的 NFA。
具體來說,我的問題是-
1) 這背后的-O2
優化是什么,(我認為這是令人印象深刻的)修復?
2)什么-O3
優化再次打破了它?
struct State
{
map<char,vector<State*> > transitions;
bool accepting = false;
};
bool match(State* state,string inp){
if(inp=="") return state->accepting;
for(auto s:state->transitions[inp[0]])
if(match(s,inp.substr(1))) return true;
for(auto s:state->transitions['|']) //e-transitions
if(match(s,inp)) return true;
return false;
}
在 gcc 文檔中,據說 O3 具有 O2 的所有優化,以及更多優化。 我無法“獲得”其中的一些額外內容或它們與此問題的相關性。而且我想強調,對於我在類似問題中看到的內容,我不是在尋找解決此問題的具體方法。
正如您已經發現的那樣:問題在於遞歸的堆棧使用。 確實,對於-O2
和-O3
都不會執行 TLO(理論上只有最后一次 recur-call 可能對您的情況沒有幫助)。
但是,根據優化級別,您的函數在堆棧上需要不同的空間量。 不能保證-O3
版本會更快並且需要更少的堆棧空間。
當我們查看程序集時,我們可以看到以下內容:
-O3
通過subq $88, %rsp
保留 88 個字節subq $88, %rsp
堆棧上的占用空間更大,因為除了通常的函數序言之外,還將寄存器r12-r15
壓入堆棧。
-O2
除了壓入堆棧的寄存器外,僅保留 56 個字節。
如果沒有優化,堆棧上的占用空間是最大的:所有內容都需要在兩行原始代碼之間存儲/加載到/從堆棧中加載,以獲得可預測的調試行為,以便我們可以更改調試器中的值。
這可以解釋您的觀察結果:如果沒有優化,堆棧很快就會滿了。 -O2
優化減輕了它(但沒有修復它),因此可以處理 20000 的遞歸深度 - 它可能會崩潰 30000。 -O3
優化具有更大的堆棧占用空間,並且對於較小的輸入已經失敗。
這個問題的正確解決方案現在很明顯:應該使用深度優先搜索或廣度優先搜索的迭代版本。
代碼中的另一個問題 - substr
的使用導致不必要的內存復制/使用。 只需將迭代器傳遞給字符串中的第一個字符,並為遞歸調用增加它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.