2016-07-02 45 views
1

我想這主要是關於打破C/C++優化器。C/C++標籤數量(goto)如何影響性能?

l_line1: 
    do_stuff(a, b); 
l_line2: 
    do_other_stuff(n, m, o); 
l_line3: 
    do_more_stuff(x); 

是否有性能差異,如果標籤是一個if塊內,如果這是不正確的運行?

if (debug_active) { 
    l_line1: 
    nextline = debugme(d1, d2, d3); 
    goto nextline_check; 
} 
do_stuff(a, b); 

if (debug_active) { 
    l_line2: 
    nextline = debugme(d1, d2, d3); 
    goto nextline_check; 
} 
do_other_stuff(n, m, o); 

if (debug_active) { 
    l_line3: 
    nextline = debugme(d1, d2, d3); 
    goto nextline_check; 
} 
do_more_stuff(x); 
return; 

nextline_check: 
    switch (nextline) { 
    case 1: goto l_line1; 
    case 2: goto l_line2; 
    case 3: goto l_line3; 
    default: DEB_ASSERT("Wrong line"); 
    } 

背景: 這些線插入用於源級調試器。 所有這些goto的原因是因爲代碼生成(「原始」代碼 - 從另一種語言翻譯到C/C++ - 這是第一個沒有標籤的樣本,第二個是或多或少的調試選項產生什麼)。 如果您希望能夠在調試過程中跳過行或者甚至更改下一條語句(兩者都將在函數debugme期間由「用戶」設置),那麼您需要在每個語句之前標記標籤,並且goto這些標籤取決於返回(在這種情況簡化爲返回碼)。

一般想法,讓一個可移植的代碼有更好的表現會不錯,太(非便攜的建議將是很好也知道, - 至少對於GCC和MSC)。

+3

請告訴我你爲什麼使用'goto'? –

+3

好神,那個代碼基本上是「Goto認爲有害」的* manifest *。 –

+3

@NicolBolas你是什麼意思?他說他正在生成這個代碼。此外,沒有冒犯,但我認爲這個「Goto認爲有害」的東西是有害的;) – dercz

回答

1

我假設「在運行中從不真實」你承認變量debug_active可以在運行時取值爲真或假 - 儘管在任何給定的運行中只有一個這樣的值 - 換句話說,它不是'編譯時宏。考慮到這一點:

這不是if語句的標籤輸入或輸出,會損害優化器優化代碼的能力。事實上,它所依賴的控制流在第二個版本中被debugme的調用完全銷燬,返回一個int,然後在switch,nextline_check處使用該int來動態選擇要執行的下一個代碼塊。任何依賴於通過了解控制流而計算出的數據流的優化都將在那裏失敗。

第一個版本更好,但不是太多,因爲標籤在控制流量無法確定時也會混淆許多/所有優化,在很多情況下很可能會這樣。除非有一個隱藏在某個你沒有顯示的地方 - 在這種情況下,表面上和第二個版本相同。

(實際上是一個蘋果對蘋果的比較,我們就需要看到在第一種情況下調度代碼,就像你在第二種情況下提供的。)

在任何情況下的「表演」這個生成的代碼無論你做什麼都會非常糟糕(與未優化的未優化原始源相比)。你唯一的問題是:它是否足夠糟糕地摧毀你的調試器概念,讓用戶感到沮喪而不是調試器提供的好處讓他們感到滿意? (PS只是一個簡單的示例,說明優化器在第二個版本中無法做到的事情:它不能假定任何特定的值在語句開始時在給定的寄存器中(即,原來出現在源代碼中的一個聲明)作爲它在前面的聲明中留下的結果。)

1

我懷疑標籤的數量有任何實際的性能影響,因爲沒有成本,如果沒有跳轉到他們 - 他們只是可能的(符號)目的地和導致自己沒有代碼,甚至不使它的最終的目標文件。即使有東西跳到他們身上,他們也很容易被跳轉站點上的常量替代。但是,您應該能夠通過檢查生成的asm來輕鬆驗證或反駁,您應該看到所有標籤都消失了。

+0

上下文是由優化編譯器處理的代碼中的標籤。標籤在這種情況下確實會有性能影響。 – davidbak

+0

我在優化器上工作了大約1年。它所做的第一件事是將代碼分解成「基本塊」。基本塊以標籤開始並以跳轉結束,並且不在塊內。最終會有一個基本模塊圖。如果它看到某些形狀的圖形,它會放棄。添加標籤和gotos是導致奇怪形狀圖形的唯一方法。 –

2

標籤和在C和C++ goto很像彙編標籤和分支機構,而這大概是什麼將被編譯器生成。查看示例程序的生成代碼以查看編譯器的功能。如果這就是如何處理標籤和goto,那麼「性能」就像在彙編器中使用相同。

還應該注意的是,編譯器會在機器碼,循環和條件以及函數調用中產生跳躍的批次,所有這些都會導致跳轉,並且沒有太大的性能損失。然而,編譯器知道底層CPU的細節比你做的,這樣可以產生更好的跳躍,這不垃圾的指令高速緩存和高達你無條件跳轉會做停頓的指令流水線,因此,使用跳躍確實可能原因雖然它很少會被注意到,除非您使用標籤和goto而不是循環或條件語句。

也就是說,會導致任何可能的性能問題,標籤本身只是編譯器中的佔位符,因此它可以爲機器代碼生成正確的跳轉偏移量。一旦程序通過編譯器,標籤就不再存在於目標文件或生成的代碼中。

最後一點反對使用goto通常的建議。雖然它可能使在幾個感的情況下,大多數程序員不會來的情況下goto是有道理的。簡單地不使用它,通常會有比其他語言結構更適合,或者至少更優化,至少更易維護的結構。

+0

這就是問題的答案:「編譯後他們沒有任何性能影響。」謝謝。儘管如此:您如何看待這個特定示例中的'goto'(一個允許您設置下一條指令的源代碼級調試器)可以被刪除? –

+1

這些語句對於彙編語言編程(以及因此而生成的機器代碼)是正確的,但不通過優化C編譯器(或C++)運行(生成)C代碼的上下文中。在這種情況下,_labels_可以產生性能差異,因爲它們是程序可以跳轉到的潛在位置,因此它們會影響現有的控制流,即使跳轉到運行時也不會發生(只要跳轉到它們源代碼使得goto不能被優化器證明是死碼)。 – davidbak

1

這是我該怎麼做的。沒有必要。

if (! debug_active) { 
    // fast path when not debugging 
    do_stuff(a, b); 
    do_other_stuff(n, m, o); 
    do_more_stuff(x); 
} else { 
    nextline = debugme(d1, d2, d3); 
    while (true) { 
     if(nextline == 1) { 
      do_stuff(a, b); 
      nextline = debugme(d1, d2, d3); 
      if(nextline < 2) continue; 
     } 

    if(nextline == 2) { 
     do_other_stuff(n, m, o); 
     nextline = debugme(d1, d2, d3); 
     if(nextline < 3) continue; 
    } 
    if(nextline == 3) { 
     do_more_stuff(x); 
     nextline = debugme(d1, d2, d3); 
     if(nextline < 4) continue; 
    } 
    break; 
    } // while 
} // if 

return; 
+0

這是「唯一可能的選項沒有轉到」davidbak在評論中指出 - 因爲這是產生這將是一個可能的選擇,但你會做同樣的,如果你不只有3但12000 nextlines?順便說一句:'!debug_active'的重複代碼是一個好主意! –

+0

啊,davidbak的評論確實匹配。對於12,000個語句,一個開關(沒有休息)會更好。讓編譯器爲你製作跳轉表。複製代碼是時空交易。 –