我需要一些想法如何编写一些可并行问题的 C++ 跨平台实现,以便我可以利用 SIMD(SSE、SPU 等)(如果可用)。我希望能够在运行时在 SIMD 和非 SIMD 之间切换。
您建议我如何解决这个问题?(当然我不想针对所有可能的选项多次实现该问题)
我可以看到这对于 C++ 来说可能不是一件容易的任务,但我相信我错过了一些东西。到目前为止我的想法看起来像这样......
类 cStream 将是单个字段的数组。使用多个 cStream,我可以实现 SoA(数组结构)。然后使用一些 Functor,我可以伪造需要在整个 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 已启用,则会检查一次,并且循环围绕主函子。
如果有人感兴趣,这是我用来测试我在阅读 Paul 发布的库时提出的新想法的脏代码。
谢谢保罗!
// 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;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)