簡體   English   中英

g++ 優化:O2 標志修復了 O3 再次破壞的損壞代碼

[英]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 的所有優化,以及更多優化。 我無法“獲得”其中的一些額外內容或它們與此問題的相關性。而且我想強調,對於我在類似問題中看到的內容,我不是在尋找解決此問題的具體方法。

測試 NFA

正如您已經發現的那樣:問題在於遞歸的堆棧使用。 確實,對於-O2-O3都不會執行 TLO(理論上只有最后一次 recur-call 可能對您的情況沒有幫助)。

但是,根據優化級別,您的函數在堆棧上需要不同的空間量。 不能保證-O3版本會更快並且需要更少的堆棧空間。

當我們查看程序集時,我們可以看到以下內容:

  1. -O3通過subq $88, %rsp保留 88 個字節subq $88, %rsp堆棧上的占用空間更大,因為除了通常的函數序言之外,還將寄存器r12-r15壓入堆棧。

  2. -O2除了壓入堆棧的寄存器外,僅保留 56 個字節。

  3. 如果沒有優化,堆棧上的占用空間是最大的:所有內容都需要在兩行原始代碼之間存儲/加載到/從堆棧中加載,以獲得可預測的調試行為,以便我們可以更改調試器中的值。

這可以解釋您的觀察結果:如果沒有優化,堆棧很快就會滿了。 -O2優化減輕了它(但沒有修復它),因此可以處理 20000 的遞歸深度 - 它可能會崩潰 30000。 -O3優化具有更大的堆棧占用空間,並且對於較小的輸入已經失敗。

這個問題的正確解決方案現在很明顯:應該使用深度優先搜索或廣度優先搜索的迭代版本。

代碼中的另一個問題 - substr的使用導致不必要的內存復制/使用。 只需將迭代器傳遞給字符串中的第一個字符,並為遞歸調用增加它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM