5

コンパイラの最適化に適したコーディングスタイルはどれですか?具体的には、1)直ちに捨てられる一時的な値の数を最小限に抑えること、2)自動ベクトル化、すなわち算術演算のためのSIMD命令を生成することに興味がある。変更可能なベクトルと不変のベクトル演算の最適化

私はこの構造体があるとします。この構造体の

#define FOR_EACH for (int i = 0; i < N; ++i) 

template<typename T, unsigned N> 
struct Vector { 
    void scale(T scalar) { 
     FOR_EACH v[i] *= scalar; 
    } 

    void add(const Vector<T, N>& other) { 
     FOR_EACH v[i] += other.v[i]; 
    } 

    void mul(const Vector<T, N>& other) { 
     FOR_EACH v[i] *= other.v[i]; 
    } 

    T v[N]; 
}; 

使用例:

Vector<int, 3> v1 = ...; 
Vector<int, 3> v2 = ...; 
v1.scale(10); 
v1.add(v2); 
v1.mul(v2); 

これは変更可能なアプローチです。

代替不変のアプローチは、次のようになります。

template<typename T, unsigned N> 
struct Vector { 
    Vector(const Vector<T, N>& other) { 
     memcpy(v, other.v, sizeof(v)); 
    } 

    Vector<T, N> operator+(const Vector<T, N>& other) const { 
     Vector<T, N> result(*this); 
     FOR_EACH result.v[i] += other.v[i]; 
     return result; 
    } 

    Vector<T, N> operator*(T scalar) const { 
     Vector<T, N> result(*this); 
     FOR_EACH result.v[i] *= scalar; 
     return result; 
    } 

    Vector<T, N> operator*(const Vector<T, N>& other) const { 
     Vector<T, N> result(*this); 
     FOR_EACH result.v[i] *= other.v[i]; 
     return result; 
    } 

    T v[N]; 
}; 

使用例:今すぐ

Vector<int, 3> v1 = ...; 
Vector<int, 3> v2 = ...; 
auto result = (v1 * 10 + v2) * v2; 

を、私はこの問題のAPIの設計に関係していませんよ。この点に関して、両方の解決策が実行可能であると仮定する。

また、サンプルコードのintの代わりに、floatまたはdoubleでもかまいません。

私にとって興味深いのは、これは現代のC++コンパイラによってどのデザインがより簡単に分析できるのでしょうか?私は特に単一のコンパイラを対象としていません。コンパイラの経験があり、最適化の方法を知っていれば、あなたの経験を共有してください。

  • 第2バージョンでは、多くの一時的な値が生成されます。コンパイラーは最終的にすべての演算子呼び出しをインライン化し、その中に保持されているすべての算術式を見ることができるでしょうか? (私はインライン化せずに、コンパイラなしで副作用の可能性があるため一時的なものを排除できると仮定しています)

  • 最初のバージョンでは一時的な数は最小限に抑えられますが、コンパイラは、操作の数を最小限に抑え、並列化(CPU命令レベルで)を可能にする方法で、インテントを引き出し、オペレーションの順序を変更できますか?

  • 現代のコンパイラが上記のループをベクトル化するのはどれくらい難しいですか?

+0

直接インデックスは、それが簡単にコンパイラがそれらをベクトル化できるようになります。インデックスが複雑なアルゴリズムで間接的に適用されると、コンパイラが失敗することがあります。 –

答えて

0

私が理解する限り、最初の例は、ターゲットアーキテクチャでサポートがある限り、ベクトル化が容易です。これは、連続する反復で要素間にデータ依存関係がないためです。

連続した繰り返しで要素間にデータ依存関係があるループがある場合、ソフトウェアパイプライニングで削除することもできます。ソフトウェアパイプライニングはベクトル化に役立ちます。

一部のアーキテクチャでは、浮動小数点演算ユニットが限られているため、浮動小数点演算はベクトル化が容易ではありません。

第2の例では、インライン展開によって除去することができる一時変数があります。

便利なリンク:要素の

http://software.intel.com/en-us/articles/vectorization-writing-cc-code-in-vector-format

http://ieeexplore.ieee.org/xpl/login.jsp?tp=&arnumber=1540953&url=http%3A%2F%2Fieeexplore.ieee.org%2Fxpls%2Fabs_all.jsp%3Farnumber%3D1540953

+0

リンクをありがとう。私はまだ質問の他の部分、すなわち一時的なものを最適化することに興味があります。私はちょうど確かに異なるコンパイラの出力を比較する必要がありますね。 –

+0

ここに私の応答をチェックするhttp://stackoverflow.com/a/17476463/811335、それは助けるかもしれない。 –

関連する問題