5

私のプログラム コードには、1 バイトまたは 2 から約 16 までのさまざまなかなり小さなオブジェクトがあります。たとえば、Vector2 (2 * T)、Vector3 (3 * T)、Vector4 (4 * T)、ColourI32 (4)、LightValue16 (2 )、タイル (2) など (括弧内のバイト サイズ)。

プロファイリング(サンプルベース)を行っていたため、予想よりも遅い関数が発生しました。

//4 bits per channel natural light and artificial RGB
class LightValue16
{
...
    explicit LightValue16(uint16_t value);
    LightValue16(const LightValueF &);
    LightValue16(int r, int g, int b, int natural);

    int natural()const;
    void natural(int v);
    int artificialRed()const;
    ...
    uint16_t data;
};
...
LightValue16 World::getLight(const Vector3I &pos)
{ ... }

この関数はいくつかの計算を行い、いくつかの配列を介して値を検索します。世界の人口の多い部分の上にいくつかのデフォルト値があります。コンテンツはきれいにインライン化されており、逆アセンブルを見ると、約 100 の命令が含まれているように見えます。ただし、1 つのことが際立っていました。すべてのリターン サイトで、次のように実装されていました。

mov eax, dword pyt [ebp + 8]
mov cx, word ptr[ecx + edx * 2] ; or say mov ecx, Fh
mov word ptr [eax], cx
pop ebp
ret 10h

x64 の場合、ほとんど同じことがわかりました。私は GCC ビルドをチェックしませんでしたが、ほとんど同じことをしていると思います。

少し実験して、uint16_t 戻り型を使用して見つけました。実際には、World::getLight 関数がインライン化され (ほぼ同じコア 80 命令のように見え、条件/ループが異なるチートはありません)、調査していた外部関数の合計 CPU 使用率が 16.87 から上昇しました。 % to 14.04% ケースバイケースでそれを行うことはできますが (強制的にインラインのものを試すことに加えて)、最初からこのようなパフォーマンスの問題を回避する実用的な方法はありますか? おそらく、コード全体で数 % 速くなるでしょうか?

私が今考えることができる最善の方法は、そのような場合 (< 4 またはおそらく 8 バイト オブジェクト) でプリミティブ型を使用し、現在のメンバーのものをすべて非メンバー関数に移動することです。名前空間だけで、C で行われるようになります。 .

これについて考えると、「t foo(float x、float y、float z)」よりも「t foo(const Vector3F &p)」のようなものを使用するコストもあると思いますか? もしそうなら、 const& を広範囲に使用するプログラムで、合計すると大きな違いになるでしょうか?

4

2 に答える 2

2

Itanium C++ ABIを見てください。お使いのコンピューターには間違いなく Itanium プロセッサが搭載されていませんが、gcc は Itanium ABI と非常によく似た x86 および x86-64 ABI をモデル化しています。リンクされたセクションには、

ただし、戻り値の型に自明でないコピー コンストラクタまたはデストラクタがある場合、[呼び出し元が提供するメモリへの戻りが発生する]

自明ではないコピー コンストラクターまたはデストラクタの意味を調べるには、集合体と POD とは何か、それらがどのように/なぜ特別なのかを調べてください。、そしてクラスが「自明にコピー可能」になるためのルールをのぞきます。あなたの場合、問題は定義したコピー コンストラクターです。まったく必要ありません。コンパイラは、必要に応じてdataメンバーを割り当てるだけのコピー コンストラクターを合成します。コピー コンストラクターが必要であり、C++11 を使用していることを明示的に示したい場合は、それをデフォルトの functionとして書き留めることもできます。

LigthValue16(const LightValue16 & other) = default;
于 2014-02-09T14:27:15.503 に答える
0

この質問へのコメントでは、分析した関数をコンパイラがclass LightValue16単純なものとして処理できるかどうかについて、すでに多くの議論が行われています。uint16_t

クラスに特別な魔法 (仮想関数など) が含まれておらず、クラス全体が分析された関数から見える場合、コンパイラは `uint16_t 型を使用する場合と同じように 100% 効率的なコードを生成できます。

問題は「できる」ことです。すべての適切なコンパイラは通常、100% 高速なコードを生成しますが、最適化が適用されないか、少なくとも結果のコードが異なる状況が散発的に発生します。ヒューリスティックのパラメーターが変更された可能性があります (たとえば、クラスのために最適化ステップのコードが少し残っているためインラインは適用されません)、または一部の最適化パスがこの段階で単純な数値型を本当に必要としているだけかもしれません。これはコンパイラの本当のバグでさえありません。たとえば、上記のクラスに「template < bool NotUsed>」を追加すると、コンパイラ内の最適化手順が変更される可能性がありますが、意味的にはプログラムは変更されません。

したがって、100% 確実にしたい場合は、int または double のみを直接使用してください。しかし、90% の場合は 100% 速くなり、10% の場合のみパフォーマンスの 90% になります。これは、すべてのユースケースの 99% (ただし 100% ではない) で問題ないはずです。

于 2013-08-16T18:12:14.750 に答える