2

UPDATE II:受け入れられた答えとコメントで提案されたアイデアを説明する2つの例を書いた。最初の1つは明示的な型付き操作を実行します。math_cmp.cc1つの汎用カーネルを作成し、それを別のISAにマップする

// math_cmp.cc 
#include <iostream> 
#include <xmmintrin.h> 

using namespace std; 

int main() 
{ 
    float a, b; 
    cin >> a >> b; 

    float res = (a + b) * (a - b); 

    cout << res << endl; 

    __m128 a_vec, b_vec, res_vec; 
    a_vec = _mm_set1_ps(a); 
    b_vec = _mm_set1_ps(b); 
    res_vec = _mm_mul_ps(_mm_add_ps(a_vec, b_vec), 
      _mm_sub_ps(a_vec, b_vec)); 

    float *res_ptr; 
    res_ptr = (float *) &res_vec; 
    for (int i = 0; i < 4; ++i) 
    cout << "RES[" << i << "]: " << res_ptr[i] << ' '; 
    cout << endl; 

    return 0; 
} 

第二ファイルmath_traits.ccテンプレート+はを形質行います。 -O3でコンパイルしたときに生成されたアセンブリは、math_cmp.ccのアセンブリとほぼ同じです。

// math_traits.cc 
#include <iostream> 
#include <xmmintrin.h> 

using namespace std; 

template <typename T> 
class MathOps 
{ 
}; 

template <typename T> 
T kernel (T a, T b) 
{ 
    T res = MathOps<T>::mul(MathOps<T>::add(a, b), MathOps<T>::sub(a, b)); 

    return res; 
} 

template <> 
class MathOps <float> 
{ 
public: 
    static float add (float a, float b) 
    { 
    return a + b; 
    } 

    static float sub (float a, float b) 
    { 
    return a - b; 
    } 

    static float mul (float a, float b) 
    { 
    return a * b; 
    } 
}; 

template <> 
class MathOps <__m128> 
{ 
public: 
    static __m128 add (__m128 a, __m128 b) 
    { 
    return a + b; 
    } 

    static __m128 sub (__m128 a, __m128 b) 
    { 
    return a - b; 
    } 

    static __m128 mul (__m128 a, __m128 b) 
    { 
    return a * b; 
    } 
}; 

int main() 
{ 
    float a, b; 
    cin >> a >> b; 
    cout << kernel<float>(a, b) << endl; 

    __m128 a_vec, b_vec, res_vec; 
    a_vec = _mm_set1_ps(a); 
    b_vec = _mm_set1_ps(b); 
    res_vec = kernel<__m128>(a_vec, b_vec); 

    float *res_ptr; 
    res_ptr = (float *) &res_vec; 
    for (int i = 0; i < 4; ++i) 
    cout << "RES[" << i << "]: " << res_ptr[i] << ' '; 
    cout << endl; 

    return 0; 
} 

I UPDATE:私は質問のようにまとめることができると思います。は「現代のC++のアプローチは、多型関数のようなマクロと等価であることはありますか?」


抽象操作で1つのカーネルを書き、自動的にISA固有のコードを生成するためにC++でプログラミングすることが可能になるのだろうか。例えば、一般的なカーネルが使用できます

RET_TYPE kernel(IN_TYPE a, IN_TYPE b) 
{ 
    RET_TYPE res = ADD(a, b); 
    return res; 
} 

とカーネルが両方にスカラーバージョン変換することができます。

float kernel(float a, float b) 
{ 
    float res = a + b; 
    return res; 
} 

とベクトル化バージョン:では

__m128 kernel(__m128 a, __m128 b) 
{ 
    __m128 res = _mm_add_ps(a, b); 
    return res; 
} 

を実際には、一般的なカーネルははるかに複雑になります。型の汎用性は、テンプレートパラメータによって処理できます。しかし、命令の一般性は私をつかまえました。

通常、この種の問題は、あなたには、いくつかの中間表現(IR)でプログラムを書くコード生成介してアドレスされ、その後、様々なターゲット言語へのIR表現を翻訳します。

しかし、私は純粋で現代的な C++の中でそれをしなければならない、つまりCマクロを意味しない。 汎用プログラミングテンプレートメタプログラミングまたはOOPで巧妙に悪用して達成できるかどうか疑問です。あなたはいくつかのポインタを持っている場合は助けてください!

+0

C++でテンプレートベースの並列プログラミングの既存のライブラリを調査しましたか?あなたの最後の段落は、これが宿題であるかのように見えます。 – JAB

+0

@JAB質問はC++の宿題の通常の範囲を超えていると思います。しかし私は間違っている可能性があります。最後の段落の制約は、既存のプロジェクト設計によるものです。 –

+0

@JAB。アイデアを説明するための例を追加しました。それが意味をなさないかどうか私に教えてください。ありがとうございました。 –

答えて

2

一般的には、テンプレートや型特性で達成可能です:あなたはさまざまな状況で異なる同じ種類を扱いたいかもしれ際

template <typename T> 
T kernel(T a, T b) 
{ 
    return MathTraits<T>::add (a, b); 
} 
template <typename T> 
class MathTraits 
{ 
} 
// specialization for float 
template <> 
class MathTraits <float> 
{ 
    float add(float a, float b) 
    { 
      return a+b; 
    } 
} 
// specialization of MathTraits for __m128 etc. 

しかし、このアプローチは失敗することがあります。しかし、それはオーバーロードの一般的な限界です...

実際に直接的に関数を特殊化することは可能ですが、説明されている方法がより明確で再利用可能であるため、より一般的です。

+0

ありがとうございます@Zbynek Vyskovsky - kvr000。これはうまくいくようです。しかし、私は、ラッパークラス 'MathTraits'の使用と関数呼び出しオーバーヘッドのためにパフォーマンスが低下すると思います。特殊なバージョンの 'add'を強制的にインライン展開することができますが、それはあまり助けにならないでしょう。 –

+0

私はこの質問を次のようにまとめることができると思います:** "多相関数のようなマクロに相当する最新のC++アプローチがありますか?" ** –

+1

@ K.G。 「多相関数のようなマクロに相当する現代的なC++のアプローチはありますか?」はい、テンプレートとタイプ形質の使用(本当の多型は、あなたがやろうとしていることに応じてテンプレート引数を取り除くのが難しいかもしれませんが)。パフォーマンスは確かに最適化されていないコードでのラッパークラスの使用/関数呼び出しオーバーヘッドのために悪化しますが、現代のコンパイラのほとんどは、最適化されたビルドを使用すると、そのような短いフラグメントを自動的にインライン展開します。 – JAB

関連する問題