web-dev-qa-db-ja.com

C ++での定数とコンパイラの最適化

C++でのconst-correctnessに関するすべてのアドバイスを読みましたが、コンパイラーがコードを最適化するのに役立つため、これは(部分的に)重要です。私が今まで見たことがないのは、コンパイラがこの情報を使用してコードを最適化する方法についての良い説明であり、カーテンの後ろで何が起こっているのかを説明している良い本でさえありません。

たとえば、コンパイラは、constとして宣言されているメソッドと、そうではないがそうであるべきメソッドをどのように最適化しますか。可変変数を導入するとどうなりますか?それらはconstメソッドのこれらの最適化に影響しますか?

49
David Holm

メソッドを無視して、constオブジェクトのみを見てみましょう。コンパイラーはここで最適化を行う機会がはるかに多くなります。オブジェクトがconstとして宣言されている場合、(ISO/IEC 14882:2003 7.1.5.1(4)):

変更可能(7.1.1)と宣言されたクラスメンバーは変更できることを除いて、存続期間(3.8)中にconstオブジェクトを変更しようとすると、動作が未定義になります。

変更可能なメンバーを持つ可能性のあるオブジェクトを無視します-コンパイラーはオブジェクトが変更されないことを自由に想定できるため、大幅な最適化を行うことができます。これらの最適化には次のようなものが含まれます。

  • オブジェクトの値を直接マシン命令オペコードに組み込む
  • コンパイル時に既知の条件式でconstオブジェクトが使用されているため、到達できないコードを完全に排除
  • constオブジェクトがループの反復回数を制御している場合、ループの展開

これは、実際のオブジェクトがconstである場合にのみ適用されることに注意してください-constポインターまたは参照を介してアクセスされるオブジェクトには適用されません。これらのアクセスパスは、constではないオブジェクトにつながる可能性があるためです(const実際のオブジェクトが非constであり、オブジェクトへのアクセスパスのconstnessをキャストしている限り、ポインタ/参照)。

実際には、あらゆる種類のconstオブジェクトに対して重要な最適化を実行するコンパイラは存在しないと思います。しかし、プリミティブ型のオブジェクト(int、charなど)の場合、コンパイラーはこれらのアイテムの使用を最適化するのに非常に積極的であると思います。

35
Michael Burr

Constキーワードは、最適化のためではなく、プログラムのセマンティクスのコンパイルチェックのために主に導入されたと思います。

ハーブサッターは、 GotW#81の記事 で、const参照によってパラメーターを渡すとき、またはconstの戻り値を宣言するときに、コンパイラーが何も最適化できない理由を非常によく説明しています。その理由は、constと宣言されていても、コンパイラには参照されているオブジェクトが変更されないことを確認する方法がないためです。const_castを使用したり、他のコードが同じオブジェクトに対して非const参照を持つことができます。

しかし、ハーブサッターの記事を引用すると、

「const」と言うことが実際に何かを意味する場合が1つだけあります。それは、オブジェクトが定義された時点でconstにされる場合です。その場合、コンパイラはそのような「reallyconst」オブジェクトを読み取り専用メモリに正常に配置できることがよくあります[...]。

この記事にはもっとたくさんのことがあるので、それを読むことをお勧めします。その後、絶え間ない最適化についてよりよく理解できるようになります。

54
Luc Touraille

手を振る

基本的に、データが早期に修正されるほど、コンパイラーはデータの実際の割り当てをより多く移動できるため、パイプラインが停止することはありません。

手振りを終了

6
Paul Nathan

まあ。 const-correctnessは、最適化というよりスタイル/エラーチェックのことです。フルオンの最適化コンパイラは、変数の使用法に従い、変数が効果的にconstであるかどうかを検出できます。

それに加えて、コンパイラはあなたがそれに真実を伝えることに依存することはできません-あなたはそれが知らないライブラリ関数の中にconstをキャストしている可能性があります。

つまり、const-correctnessは目指す価値のあるものですが、適切な最適化コンパイラーを想定して、コンパイラーがそれ自体について理解できないことは何もコンパイラーに伝えません。

5
Mike F

Constとして宣言されている関数は最適化されません。

call constとして宣言されている関数を最適化できます。

void someType::somefunc();

void MyFunc()
{
    someType A(4);   // 
    Fling(A.m_val);
    A.someFunc();
    Flong(A.m_val);
}

ここでフリングを呼び出すには、評価されたA.m_valをCPUレジスタにロードする必要がありました。 someFunc()がconstでない場合、Flong()を呼び出す前に値を再ロードする必要があります。 someFuncがconstの場合、まだレジスターにある値でFlongを呼び出すことができます。

3
James Curran

methodsをconstとして使用する主な理由は、constの正確さのためであり、メソッド自体のコンパイルの最適化の可能性のためではありません。

変数がconstの場合、(理論的には)最適化することができます。ただし、コンパイラがスコープを確認できるのはスコープだけです。結局のところ、コンパイラは、他の場所でconst_castを使用してそれらを変更できるようにする必要があります。

3
Andrew Stein

これらはすべて真の答えですが、答えと質問は、コンパイラの最適化が実際に重要であるという1つのことを前提としているようです。

コンパイラの最適化が問題となるコードは1種類だけです。つまり、

  • タイトな内側のループ、
  • サードパーティのライブラリとは対照的に、コンパイルするコードでは、
  • 関数やメソッドの呼び出しを含まない(非表示のものも含む)、
  • プログラムカウンタがその時間のかなりの部分を費やしている場所

他の99%のコードがN次の程度に最適化されている場合、プログラムカウンターが実際に時間を費やしているコード(サンプリングによって見つけることができる)でのみ問題となるため、コードに大きな違いはありません。

2
Mike Dunlavey

オプティマイザが実際に多くの在庫をconst宣言に入れると、私は驚かれます。不変性を捨ててしまうコードはたくさんあります。状態がいつ変化するかを想定するためにプログラマー宣言に依存したのは、非常に無謀なオプティマイザーです。

1
Rob Walker

const-correctnessはドキュメントとしても役立ちます。関数またはパラメーターがconstとしてリストされている場合は、コードの下から値が変化することを心配する必要はありません(チームの他の誰かがいたずらでない限り)。ライブラリに組み込まれていなかったとしても、実際に価値があるかどうかはわかりません。

1
David Thornley

constが直接最適化である最も明白な点は、引数を関数に渡すことです。関数がデータを変更しないようにすることが重要な場合が多いため、関数シグネチャの実際の選択肢は次のとおりです。

void f(Type dont_modify); // or
void f(Type const& dont_modify);

もちろん、ここでの本当の魔法は、オブジェクトの(高価な)コピーを作成するのではなく、参照を渡すことです。ただし、参照がconstとしてマークされていない場合、これはこの関数のセマンティクスを弱め、(エラー追跡を困難にするなど)悪影響を及ぼします。したがって、constはここで最適化を有効にします。

/ EDIT:実際、優れたコンパイラーは関数の制御フローを分析し、引数を変更しないと判断し、それ自体を最適化(コピーではなく参照を渡す)することができます。 constは、コンパイラのヘルプにすぎません。ただし、C++にはかなり複雑なセマンティクスがあり、そのような制御フロー分析は大きな関数にとって非常に高価になる可能性があるため、おそらくコンパイラーに依存すべきではありません。誰かが私をバックアップ/私が間違っていることを証明するためのデータを持っていますか?

/ EDIT2:はい、カスタムコピーコンストラクターが登場するとすぐに、コンパイラーは残念ながらこの状況でそれらの呼び出しを省略することが許可されていないため、さらにトリッキーになります。

0
Konrad Rudolph

このコード、

class Test
{
public:
  Test (int value) : m_value (value)
  {
  }

  void SetValue (int value) const
  {
    const_cast <Test&>(*this).MySetValue (value);
  }

  int Value () const
  {
    return m_value;
  }

private:
  void MySetValue (int value)
  {
    m_value = value;
  }

  int
    m_value;
};

void modify (const Test &test, int value) 
{
  test.SetValue (value);
}

void main ()
{
  const Test
    test (100);

  cout << test.Value () << endl;
  modify (test, 50);
  cout << test.Value () << endl;
}

出力:

100
50

これは、const宣言されたオブジェクトがconstメンバー関数で変更されたことを意味します。 C++言語にconst_cast(および可変キーワード)が存在するということは、constキーワードがコンパイラーが最適化されたコードを生成するのを助けることができないことを意味します。以前の投稿で指摘したように、予期しない結果が生じることさえあります。

原則として:

const!=最適化

実際、これは正当なC++修飾子です。

volatile const
0
Skizz