この記事は、記事C ++の最適化/コードの最適化/操作の高速化の無料翻訳です。 オリジナルはリンクで見つけることができます。 最初の部分はここにあります 。
パート2
前置または後置演算子
前置演算子は後置演算子よりも望ましいです。 プリミティブ型を使用する場合、プレフィックスとポストフィックスの算術演算は同じパフォーマンスになる可能性があります。 ただし、オブジェクトの場合、後置演算子はオブジェクトに初期状態(操作の結果として返される必要がある)を維持するために独自のコピーを作成させ、操作の副作用を引き起こすことができます。 次の例を考えてみましょう。
class IntegerIncreaser { int m_Value; public: IntegerIncreaser operator++ (int) { IntegerIncreaser tmp (*this); ++m_Value; return tmp; }; IntegerIncreaser operator++ () { ++m_Value; return *this; }; };
後置演算子は、結果が実際に使用されているかどうかに関係なく、増加(または減少)する値の変更されていないバージョンを返す必要があるため、ほとんどの場合、コピーを作成します。 STLイテレータ(たとえば)は、プレフィックス演算子を使用して変更するとより効率的です。
組み込み関数
コンパイラーがオプションを使用してプログラム全体を最適化し、コンパイラーが関数を埋め込むことができない場合、一部の関数をinline
関数としてヘッダーファイルに転送する、つまりインラインで宣言することは理にかなっています。
マニュアル(たとえば、gccコンパイラ「 5.34インライン関数はマクロと同じくらい速い」)を信じている場合、 inline
関数はユーティリティ呼び出しの排除により通常よりも速く実行されます(ただし、すべての関数ではないことに注意してください)動作が速くなり、 inline
として宣言された一部の関数はプログラム全体の速度を低下させる可能性があります。
定数整数除算
整数(正またはゼロに等しい)を定数で除算する場合、整数を符号なしに変換します。
たとえば、sが符号付き整数で、uが符号なし整数で、Cが定数整数(正または負)の式である場合、s / C操作はu / Cより遅く、s%Cはuより遅い%C。これは、Cが2のべき乗のときに最も顕著になりますが、それでも、除算するときは符号を考慮する必要があります。
ちなみに、 unsigned
signed
からunsigned
への変換は、同じビットの単なる別の解釈であるため、コストはかかりません。 したがって、sが将来的に正またはゼロとして使用される符号付き整数である場合、次の式を使用して除算を高速化できます:( unsigned
)s / Cおよび( unsigned
)s%C.
構造体フィールドの代わりに複数の配列を使用する
集合オブジェクトの1つの配列を並列処理する代わりに、同じ長さの2つ以上の配列を処理します。 たとえば、次のコードの代わりに:
const int n = 10000; struct { double a, b, c; } s[n]; for (int i = 0; i < n; ++i) { s[i].a = s[i].b + s[i].c; }
次のコードの方が高速です。
const int n = 10000; double a[n], b[n], c[n]; for (int i = 0; i < n; ++i) { a[i] = b[i] + c[i]; }
この再配置を使用すると、「a」、「b」、「c」を配列処理命令で処理できます。これはスカラー命令よりも大幅に高速です。 一部のアーキテクチャでは、この最適化によりゼロまたは悪影響が生じる場合があります。
さらに良いことに、インターリーブ配列:
const int n = 10000; double interleaved[n * 3]; for (int i = 0; i < n; ++i) { const size_t idx = i * 3; interleaved[idx] = interleaved[idx + 1] + interleaved[idx + 2]; }
PS:時期尚早に最適化するのではなく、各ケースをテストする必要があることに注意してください。