SIMD かどうか - クロスプラットフォーム
-
22-09-2019 - |
質問
利用可能な場合は SIMD (SSE、SPU など) を利用できるように、いくつかの並列化可能な問題の C++ クロスプラットフォーム実装を記述する方法のアイデアが必要です。また、実行時に SIMD と非 SIMD を切り替えられるようにしたいと考えています。
この問題にどのように取り組むとよいでしょうか?(もちろん、考えられるすべてのオプションに対して問題を複数回実装する必要はありません)
C++ ではこれがそれほど簡単な作業ではないことはわかりますが、何かが欠けていると思います。これまでのところ、私のアイデアは次のようになります...クラス cStream は単一フィールドの配列になります。複数の cStream を使用すると、SoA (Structure of Arrays) を実現できます。次に、いくつかのファンクターを使用して、cStream 全体で実行する必要がある Lambda 関数を偽装できます。
// just for example I'm not expecting this code to compile
cStream a; // something like float[1024]
cStream b;
cStream c;
void Foo()
{
for_each(
AssignSIMD(c, MulSIMD(AddSIMD(a, b), a)));
}
ここで、for_each は、ストリームの現在のポインターをインクリメントするだけでなく、ファンクターの本体を SIMD ありまたは SIMD なしでインライン化する役割を果たします。
そのようなもの:
// just for example I'm not expecting this code to compile
for_each(functor<T> f)
{
#ifdef USE_SIMD
if (simdEnabled)
real_for_each(f<true>()); // true means use SIMD
else
#endif
real_for_each(f<false>());
}
SIMD が有効かどうかが 1 回チェックされ、ループがメインファンクターの周りにあることに注意してください。
解決 2
これは、私はポールが掲載さというライブラリについて読んでいる間、私が来たことを、新しいアイデアをテストするために付属して汚いコードです。
おかげでポール!
// This is just a conceptual test
// I haven't profile the code and I haven't verified if the result is correct
#include <xmmintrin.h>
// This class is doing all the math
template <bool SIMD>
class cStreamF32
{
private:
void* m_data;
void* m_dataEnd;
__m128* m_current128;
float* m_current32;
public:
cStreamF32(int size)
{
if (SIMD)
m_data = _mm_malloc(sizeof(float) * size, 16);
else
m_data = new float[size];
}
~cStreamF32()
{
if (SIMD)
_mm_free(m_data);
else
delete[] (float*)m_data;
}
inline void Begin()
{
if (SIMD)
m_current128 = (__m128*)m_data;
else
m_current32 = (float*)m_data;
}
inline bool Next()
{
if (SIMD)
{
m_current128++;
return m_current128 < m_dataEnd;
}
else
{
m_current32++;
return m_current32 < m_dataEnd;
}
}
inline void operator=(const __m128 x)
{
*m_current128 = x;
}
inline void operator=(const float x)
{
*m_current32 = x;
}
inline __m128 operator+(const cStreamF32<true>& x)
{
return _mm_add_ss(*m_current128, *x.m_current128);
}
inline float operator+(const cStreamF32<false>& x)
{
return *m_current32 + *x.m_current32;
}
inline __m128 operator+(const __m128 x)
{
return _mm_add_ss(*m_current128, x);
}
inline float operator+(const float x)
{
return *m_current32 + x;
}
inline __m128 operator*(const cStreamF32<true>& x)
{
return _mm_mul_ss(*m_current128, *x.m_current128);
}
inline float operator*(const cStreamF32<false>& x)
{
return *m_current32 * *x.m_current32;
}
inline __m128 operator*(const __m128 x)
{
return _mm_mul_ss(*m_current128, x);
}
inline float operator*(const float x)
{
return *m_current32 * x;
}
};
// Executes both functors
template<class T1, class T2>
void Execute(T1& functor1, T2& functor2)
{
functor1.Begin();
do
{
functor1.Exec();
}
while (functor1.Next());
functor2.Begin();
do
{
functor2.Exec();
}
while (functor2.Next());
}
// This is the implementation of the problem
template <bool SIMD>
class cTestFunctor
{
private:
cStreamF32<SIMD> a;
cStreamF32<SIMD> b;
cStreamF32<SIMD> c;
public:
cTestFunctor() : a(1024), b(1024), c(1024) { }
inline void Exec()
{
c = a + b * a;
}
inline void Begin()
{
a.Begin();
b.Begin();
c.Begin();
}
inline bool Next()
{
a.Next();
b.Next();
return c.Next();
}
};
int main (int argc, char * const argv[])
{
cTestFunctor<true> functor1;
cTestFunctor<false> functor2;
Execute(functor1, functor2);
return 0;
}
他のヒント
あなたがこの分野でいくつかのアイデアのためのMacSTLライブラリのソースを見たいと思うかもしれません: www.pixelglow .COM / macstl / の
お知らせ与えられた例では、(あなたがプリプロセッサを使用しているので)、コンパイル時に実行するかを決定することを、この場合には、あなたが実際に実行したいかを決めるために、より複雑な技術を使用することができます。例えば、タグ派遣: http://cplusplus.co.il/2010 / 01/03 /タグ-派遣/ に そこに示された例に続いて、あなたはせずに、低速に高速な実装は、SIMDと一緒に持って、そして可能性があります。
あなたは liboil のように、既存のソリューションを使用して考えたことはありますか?それを実装共通SIMD演算の多くはとSIMD /非SIMDコードを使用するかどうかを実行時に決定することができます(初期化関数によって割り当てられた関数ポインタを使用して)。