web-dev-qa-db-ja.com

Cでの戻り値の最適化とコピーの省略

Cの値で構造体を渡して返すことができる であることを知らない人もいます。私の質問は、Cで構造体を返すときに不要なコピーを作成するコンパイラーについてです。GCCなどのCコンパイラーは 戻り値の最適化(RVO) 最適化を使用しますか、これはC++のみの概念ですか?私がRVOとコピー省略について読んだすべてはC++に関するものです。

例を考えてみましょう。私は現在Cで double-doubleデータ型 を実装しています(単体テストが簡単なので、フロートとフロートを最初から実装しています)。次のコードを検討してください。

typedef struct {
    float hi;
    float lo;
} doublefloat;

doublefloat quick_two_sum(float a, float b) {
    float s = a + b;
    float e = b - (s - a);
    return (doublefloat){s, e};
}

コンパイラーは、返されたdoublefloat値の一時コピーを作成しますか、それとも一時コピーを省略できますか?

Cの名前付き戻り値の最適化(NRVO)についてはどうですか?別の機能があります

doublefloat df64_add(doublefloat a, doublefloat b) {
    doublefloat s, t;
    s = two_sum(a.hi, b.hi);
    t = two_sum(a.lo, b.lo);
    s.lo += t.hi;
    s = quick_two_sum(s.hi, s.lo);
    s.lo += t.lo;
    s = quick_two_sum(s.hi, s.lo);
    return s;
}

この場合、名前付き構造体を返します。この場合の一時的なコピーは省略できますか?

これはCの一般的な質問であり、ここで使用したコード例は例にすぎないことを述べておく必要があります(これを最適化すると、組み込み関数でSIMDを使用します)。コンパイラの動作を確認するためにアセンブリの出力を確認できることは承知していますが、それでも興味深い質問だと思います。

56
Z boson

RVO/NRVOは、Cの「as-if」ルールの下で明確に許可されています。

C++では、コンストラクタ、デストラクタ、または代入演算子をオーバーロードしてそれらの副作用(たとえば、これらの操作のいずれかが発生したときに何かを出力する)をオーバーロードしているため、観察可能な副作用を得ることができますが、Cではこれらの演算子をオーバーロードする機能があり、組み込みの演算子には目に見える副作用はありません。

それらをオーバーロードしないと、コピーの省略による目に見える副作用は発生しないため、コンパイラーがそれを停止することはありません。

41
Jerry Coffin

C++で多く取り上げられている理由は、C++ではRVOに副作用があるためです(つまり、一時オブジェクトのデストラクタも、結果のオブジェクトのコピーコンストラクタや代入演算子も呼び出されません)。

Cでは、起こり得る副作用はなく、潜在的なパフォーマンスの向上のみです。そのような最適化が一部のコンパイラで実行できなかった理由はわかりません。少なくとも、規格ではそれを禁止するものは何もありません。

とにかく、最適化はコンパイラーと最適化レベルに依存するので、使用するコンパイラーが明確に定義されていて変更が予想されない場合(まだよくあることです)を除いて、重要なコードパスについては最適化に賭けません。

34
Arkanosis