21

goto最新のC++コンパイラで使用することのパフォーマンス上の利点または欠点は何ですか?

私はC++コードジェネレーターを書いていますが、を使用するgotoと書きやすくなります。結果のC++ファイルには誰も触れないので、すべての「gotoisbad」を取得しないでください。利点として、一時変数の使用を節約できます。

純粋にコンパイラの最適化の観点から、gotoがコンパイラのオプティマイザに与える結果はどうでしょうか。一時/フラグを使用する場合と比較して、コードが速くなるか、遅くなるか、または一般的にパフォーマンスに変化がありませんか。

4

4 に答える 4

22

影響を受けるコンパイラの部分は、フローグラフで機能します。特定のフローグラフを作成するために使用する構文は、厳密に移植可能なコードを記述している限り、通常は関係ありません。実際のステートメントの代わりにwhileループのようなものを作成すると、同じフローグラフは生成されません。ループの構文を使用したかのように。ただし、移植性のないコードを使用すると、最新のコンパイラでは、ループに注釈を追加して、ループが取得されるかどうかを予測できます。コンパイラーによっては、を使用してその追加情報を複製できる場合とできない場合があります(ただし、ループの注釈があるほとんどの場合、ステートメントの注釈もあるため、またはgotowhilewhilegotoiflikely takenlikely not takenifgoto通常、対応するループの同様の注釈と同じ効果があります)。

gotoただし、の値に応じて、条件付きでループの途中に直接ジャンプするなど、通常のフロー制御ステートメント(ループ、スイッチなど)では生成できないsを含むフローグラフを生成することは可能です。グローバル。このような場合、既約のフローグラフを作成する可能性があります。作成すると、コンパイラがコードを最適化する機能が制限されることがよくあります。

言い換えると、(たとえば)通常の、、、などで記述されたコードを取得しforwhileすべての場合switchに使用するように変換したgotoが、同じ構造を保持している場合、ほとんどすべての合理的に最新のコンパイラは、おそらく本質的に同じコードを生成できます。仕方。ただし、gotosを使用して、数十年前に調べなければならなかったFORTRANのように、スパゲッティの混乱を生成する場合、コンパイラーはおそらくそれをあまり使用できません。

于 2012-04-30T15:34:40.310 に答える
8

アセンブリレベルでループがどのように表現されていると思いますか?

ラベルへのジャンプ命令の使用..。

多くのコンパイラは、中間表現でも実際にジャンプを使用します。

int loop(int* i) {
  int result = 0;
  while(*i) {
    result += *i;
  }
  return result;
}

int jump(int* i) {
  int result = 0;
  while (true) {
    if (not *i) { goto end; }
    result += *i;
  }

end:
  return result;
}

LLVMでの歩留まり:

define i32 @_Z4loopPi(i32* nocapture %i) nounwind uwtable readonly {
  %1 = load i32* %i, align 4, !tbaa !0
  %2 = icmp eq i32 %1, 0
  br i1 %2, label %3, label %.lr.ph..lr.ph.split_crit_edge

.lr.ph..lr.ph.split_crit_edge:                    ; preds = %.lr.ph..lr.ph.split_crit_edge, %0
  br label %.lr.ph..lr.ph.split_crit_edge

; <label>:3                                       ; preds = %0
  ret i32 0
}

define i32 @_Z4jumpPi(i32* nocapture %i) nounwind uwtable readonly {
  %1 = load i32* %i, align 4, !tbaa !0
  %2 = icmp eq i32 %1, 0
  br i1 %2, label %3, label %.lr.ph..lr.ph.split_crit_edge

.lr.ph..lr.ph.split_crit_edge:                    ; preds = %.lr.ph..lr.ph.split_crit_edge, %0
  br label %.lr.ph..lr.ph.split_crit_edge

; <label>:3                                       ; preds = %0
  ret i32 0
}

分岐命令(条件付きジャンプ)brはどこにありますか。

すべての最適化はこの構造で実行されます。だから、gotoオプティマイザーのパンとバターです。

于 2012-04-30T15:25:03.120 に答える
4

純粋にコンパイラの最適化の観点から、gotoがコンパイラのオプティマイザに与える結果はどうでしょうか。一時/フラグを使用する場合と比較して、コードが速くなるか、遅くなるか、または一般的にパフォーマンスに変化がありませんか。

なんで気にするの?主な関心事は、コードジェネレーターに正しいコードを作成させることです。効率は正確さよりもはるかに重要ではありません。あなたの質問は、「gotoを使用すると、生成されたコードが正しくなる可能性が高くなるのか、それとも低くなるのか」ということです。

lex/yaccまたはflex/bisonによって生成されたコードを見てください。そのコードはgotoでいっぱいです。それには正当な理由があります。lexとyaccは有限状態マシンを実装しています。マシンは状態遷移で別の状態に移行するため、gotoは間違いなくそのような遷移の最も自然なツールです。

多くの場合、ステートメントのwhile周りのループを使用して、これらのgotoを削除する簡単な方法があります。switchこれは構造化コードです。Douglas Jones(Jones DW、有限状態マシンをコーディングする方法(ではない)、SIGPLAN Not。23、8(1988年8月)、19-22)によると、これはFSMをエンコードするための最悪の方法です。彼は、gotoベースのスキームの方が優れていると主張しています。

彼はまた、グラフ理論手法を使用してFSMを制御フロー図に変換するというさらに優れたアプローチがあると主張しています。それは必ずしも簡単ではありません。NP困難な問題です。そのため、多くのFSM、特に自動生成されたFSMが、スイッチの周りのループとして実装されているか、gotoを介して実装された状態遷移を使用して実装されています。

于 2012-04-30T16:46:27.440 に答える
1

私はデビッド・ハメンの答えに心から同意しますが、付け加える点は1つだけです。

人々がコンパイラについて教えられるとき、彼らはコンパイラが行うことができるすべての素晴らしい最適化について教えられます。

これの実際の値はユーザーが誰であるかに依存することを彼らは教えられていません。

作成(または生成)およびコンパイルしているコードに含まれる関数呼び出しが非常に少なく、それ自体が他のプログラムの時間の大部分を消費する可能性がある場合は、はい、コンパイラの最適化が重要です。

生成されるコードに関数呼び出しが含まれている場合、またはその他の理由でプログラムカウンターが生成されたコードにその時間のごく一部を費やしている場合は、心配する価値はありません。なんで?そのコードが非常に積極的に最適化されて時間がかからなかったとしても、それはほんのわずかな部分しか節約できず、おそらくコンパイラが修正できないはるかに大きなパフォーマンスの問題があり、それはあなたの回避を喜んでします注意。

于 2012-04-30T18:09:09.873 に答える