web-dev-qa-db-ja.com

なぜこの未使用の変数は最適化されていないのですか?

GodboltのCompilerExplorerをいじりました。特定の最適化がいかに優れているかを見たかった。私の最小作業例は次のとおりです。

#include <vector>

int foo() {
    std::vector<int> v {1, 2, 3, 4, 5};
    return v[4];
}

生成されたアセンブラー(clang 5.0.0、-O2 -std = c ++ 14による):

foo(): # @foo()
  Push rax
  mov edi, 20
  call operator new(unsigned long)
  mov rdi, rax
  call operator delete(void*)
  mov eax, 5
  pop rcx
  ret

ご覧のとおり、clangは答えを知っていますが、戻る前に多くのことを行います。 「operator new/delete」のため、ベクターでさえ作成されているように思えます。

ここで何が起こるのか、なぜ戻って来ないのか、誰にも説明できますか

GCCによって生成されたコード(ここではコピーされません)は、ベクトルを明示的に構築しているようです。 GCCが結果を推測できないことを誰もが知っていますか?

38
Max Görner

std::vector<T>は、動的割り当てを含むかなり複雑なクラスです。 clang++ヒープの割り当てを省略できる場合があります 、これはかなりトリッキーな最適化であり、それに依存するべきではありません。例:

int foo() {
    int* p = new int{5};
    return *p;
}
foo():                                # @foo()
        mov     eax, 5
        ret

例として、std::array<T>(動的に割り当てない)完全にインライン化されたコードを生成する

#include <array>

int foo() {
    std::array v{1, 2, 3, 4, 5};
    return v[4];
}
foo():                                # @foo()
        mov     eax, 5
        ret

Marc Glisse が他の回答のコメントで指摘されているように、これは規格が [expr.new]#1 で述べていることです:

実装では、置換可能なグローバル割り当て関数([new.delete.single]、[new.delete.array])の呼び出しを省略することができます。その場合、ストレージは代わりに実装によって提供されるか、別の新しい式の割り当てを拡張することによって提供されます。実装は、割り当てが拡張されなかった場合に以下が当てはまる場合、new-expression e1の割り当てを拡張してnew-expression e2のストレージを提供できます。[...]

29
Vittorio Romeo

コメントにあるように、operator newは置き換えることができます。これはどの翻訳単位でも発生する可能性があります。したがって、置き換えられないケースに合わせてプログラムを最適化するには、プログラム全体の分析が必要です。そして、それがisに置き換えられた場合、もちろんそれを呼び出さなければなりません。

デフォルトのoperator newはライブラリですI/O呼び出しは指定されていません。ライブラリI/O呼び出しは監視可能であり、したがって最適化することもできないため、これは重要です。

7
MSalters

N3664 の[expr.new]への変更は、1つの回答と1つのコメントで引用されており、new-expressionsが置換可能なグローバル割り当てを呼び出さないようにします。関数。ただし、vectorは、new-expressionではなく、std::allocator<T>::allocateを直接呼び出す::operator newを使用してメモリを割り当てます。そのため、特別な許可は適用されず、通常、コンパイラは::operator newへのそのような直接呼び出しを排除できません。

ただし、std::allocator<T>::allocateの仕様には this があるため、すべての希望が失われるわけではありません。

備考:ストレージは​::​operator newを呼び出すことで取得されますが、この関数が呼び出されるタイミングや頻度は指定されていません。

この許可を活用して、libc ++のstd::allocator特別なclangビルトインを使用 は、コンパイラーに省略が許可されていることを示します。 -stdlib=libc++を使用すると、 clangでコードがコンパイルされます

foo():                                # @foo()
        mov     eax, 5
        ret
4
T.C.