C ++コンパイラがこの条件付きブール割り当てを無条件割り当てとして最適化しないのはなぜですか?


117

次の関数について考えてみましょう。

void func(bool& flag)
{
    if(!flag) flag=true;
}

flagに有効なブール値がある場合、これは次のように無条件にを設定することtrueと同じであるように思えます。

void func(bool& flag)
{
    flag=true;
}

しかし、gccもclangもこのように最適化していません—どちらも-O3最適化レベルで次のものを生成します。

_Z4funcRb:
.LFB0:
    .cfi_startproc
    cmp BYTE PTR [rdi], 0
    jne .L1
    mov BYTE PTR [rdi], 1
.L1:
    rep ret

私の質問は、コードが特別な場合に最適化することflagができないということvolatileですか、それとも参照が参照されていないので、そのような最適化が望ましくない理由があるのですか?それを読んだ時点で未定義の動作なしにflag何らかの方法で非true-または- false値が存在する可能性があることが唯一の理由のようですが、これが可能かどうかはわかりません。


8
それが「最適化」であるという証拠はありますか?
David Schwartz

1
@ 200_success機能していないマークアップを含むコード行をタイトルとして置くのは良いことだとは思いません。より具体的なタイトル必要な場合は問題ありません英語の文を選択し、その中のコードは避けてください(たとえば、コンパイラーが条件付き書き込みを無条件書き込みに最適化して、同等であることを証明できるようにしないでください)。また、バックティックはレンダリングされないため、コードを使用する場合でもタイトルで使用しないでください。
Bakuriu 2016年

2
@Ruslanは、関数自体に対してこの最適化を行っていないようですが、コードをインライン化できる場合は、インライン化されたバージョンに対してそうするようです。多くの場合、コンパイル時定数1が使用されるだけです。 godbolt.org/g/swe0tc
エヴァン・テラン

回答:


102

これは、キャッシュの一貫性を考慮して、プログラムのパフォーマンスに悪影響を及ぼす可能性があります。が呼び出されるflagたびに書き込むとfunc()、含まれているキャッシュラインがダーティになります。これは、書き込まれる値が、書き込み前の宛先アドレスで見つかったビットと正確に一致するという事実に関係なく発生します。


編集

hvdは、このような最適化を妨げる別の理由を提供しています。これは、提案された最適化に対してより説得力のある議論です。なぜなら、それは未定義の動作を引き起こす可能性があるためです。

もう少し考察した後、無条件の書き込みを導入することから、変換が特定のコンテキストに対して安全であることを証明できない限り、コンパイラを強く禁止する必要があるもう1つの例を提案できます。このコードを考えてみましょう:

const bool foo = true;

int main()
{
    func(const_cast<bool&>(foo));
}

これで無条件書き込みを行うと、func()未定義の動作が確実にトリガーされます(読み取り専用メモリへの書き込みは、書き込みの影響が何もない場合でもプログラムを終了します)。


7
また、ブランチを取り除くため、パフォーマンスにプラスの影響を与える可能性があります。したがって、この特定のケースは、非常に具体的なシステムを考慮せずに議論する意味がないと思います。
2016年

3
@Yakkの動作の定義は、ターゲットプラットフォームの影響を受けません。プログラムを終了させると言うのは正しくありませんが、UB自体が鼻の悪魔を含む広範囲の結果をもたらす可能性があります。
John Dvorak

16
@Yakkこれは、「読み取り専用メモリ」の意味によって異なります。いいえ、それはROMチップにはありませんが、書き込みアクセスが有効になっていないページにロードされたセクションに含まれることがよくあります。たとえば、SIGSEGV信号やSTATUS_ACCESS_VIOLATIONに書き込もうとすると、例外が発生します。
Random832

5
「これは間違いなく未定義の動作を引き起こします」。いいえ。未定義の動作は抽象マシンのプロパティです。UBが存在するかどうかを決定するのは、コードが言うことです。コンパイラーはそれを引き起こしません(ただし、バグが多いとコンパイラーがプログラムを正しく動作させない場合があります)。
Eric M Schmidt

7
無条件の書き込みではなく、未定義の動作のソースであるデータ変更する可能性のconstある関数に渡すのは、キャスティングアウェイです。ドクター、私がこれを行うと痛いです...
スペンサー

48

パフォーマンスに関するレオンの答えは別として:

と仮定しflagますtrue。2つのスレッドが常に呼び出しているとしfunc(flag)ます。その場合、記述されている関数はに何も格納しないflagため、これはスレッドセーフである必要があります。2つのスレッドは同じメモリにアクセスしますが、読み取るだけです。無条件にに設定flagするtrueと、2つの異なるスレッドが同じメモリに書き込むことになります。これは安全ではありません。書き込まれているデータがすでに存在するデータと同一であっても安全ではありません。


9
私が考えるこれが適用した結果です[intro.races]/21
Griwes、2016年

10
とても興味深い。だから私はこれを次のように読みます:コンパイラーは、抽象マシンが持っていないような書き込み操作を「最適化」すること決してできません。
Martin Ba

3
@MartinBa主にそうです。しかし、たとえば他のスレッドがその特定の変数にアクセスできない可能性があることを証明できるなどの理由で、コンパイラがそれが問題ではないことを証明できる場合、問題はありません。

13
これは、コンパイラがターゲットにしているシステムが安全でない場合にのみ危険です。私0x01は、すでに0x01「安全でない」動作を引き起こしているバイトに書き込むシステムで開発したことはありません。wordまたはdwordのメモリアクセスを備えたシステムでは、そうなります。ただし、オプティマイザはこれを認識している必要があります。最新のPCまたは電話OSでは問題は発生しません。したがって、これは正当な理由ではありません。
Yakk-Adam Nevraumont

4
@Yakk実際、もっと考えてみると、これは結局のところ、一般的なプロセッサであっても正しいと思います。CPUがメモリに直接書き込むことができるのは正しいと思いますがflag、コピーオンライトページにいるとします。これで、CPUレベルでは動作が定義される可能性がありますが(ページフォールト、OSに処理を任せる)、OSレベルではまだ定義されていない可能性がありますよね?

13

ここではC ++の動作はわかりませんが、Cではメモリが変更される可能性があります。メモリに1以外のゼロ以外の値が含まれている場合、チェックで変更されないままですが、チェックで1に変更されるためです。

しかし、私はC ++に堪能ではないので、この状況が可能かどうかさえわかりません。


これはまだ真実_Boolでしょうか?
Ruslan

5
Cでは、メモリにABIがそのタイプに対して有効であるとは言わない値が含まれている場合、それはトラップ表現であり、トラップ表現の読み取りは未定義の動作です。C ++では、これは、初期化されていないオブジェクトを読み取るときにのみ発生し、UBである初期化されていないオブジェクトを読み取っています。しかし、ゼロ以外の値がタイプbool/に有効で_Boolあり、を意味することを示すABIを見つけた場合true、その特定のABIでは、おそらく正しいでしょう。

1
@Ruslan Itanium ABIを使用するコンパイラでは、ARMプロセッサでは、C _BoolとC ++ boolは同じ型か、同じ規則に従う互換型です。MSVCの場合、サイズと配置は同じですが、同じルールを使用するかどうかについて公式な声明はありません。
ジャスティン時間-2016年

1
@JustinTime:C <stdbool.h>にはtypedef _Bool bool; Andはい、x86では(少なくともSystem V ABIでは)bool/ _Boolは、バイトの上位ビットがクリアされた0または1である必要があります。この説明は妥当だとは思いません。
Peter Cordes、2016年

1
@JustinTime:確かに、System V ABIのすべてのx86フレーバーでこのセマンティクスが確実に同じであることを指摘しておかなければなりません。これがこの質問に関するものです。(funcWindowsがRDXを使用する一方で、最初の引数がRDIで渡されたため、わかります)。
Peter Cordes
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.