我需要知道如何以某种方式编写一些可并行化问题的C ++跨平台实现,以便我可以利用SIMD(SSE,SPU等)(如果可用)。我希望能够在运行时在SIMD之间切换而不是SIMD。
您如何建议我解决此问题? (当然我不想为所有可能的选项多次实现这个问题)
我可以看到这对C ++来说可能不是一件容易的事,但我相信我错过了一些东西。到目前为止我的想法看起来像这样...... 类cStream将是单个字段的数组。使用多个cStream我可以实现SoA(阵列结构)。然后使用一些函数我可以伪造我需要在整个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,则检查一次,并且循环位于主仿函数周围。
答案 0 :(得分:3)
您可能需要查看MacSTL库的来源,了解该领域的一些想法:www.pixelglow.com/macstl/
答案 1 :(得分:3)
如果有人感兴趣,这是我带来的脏代码,用于测试我在阅读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;
}
答案 2 :(得分:2)
答案 3 :(得分:2)
我见过的最令人印象深刻的SIMD扩展方法是RTFact光线跟踪框架:slides,paper。非常值得一看。研究人员与英特尔密切相关(Saarbrucken现在主持英特尔视觉计算研究所),因此您可以确保向AVX推进扩展,并且Larrabee在他们的脑海中。
英特尔的Ct“数据并行”模板库看起来也很有希望。
答案 4 :(得分:1)
请注意,给定的示例决定了在编译时要执行的内容(因为您正在使用预处理器),在这种情况下,您可以使用更复杂的技术来确定您实际要执行的内容;例如,标签发送:http://cplusplus.co.il/2010/01/03/tag-dispatching/ 按照那里显示的示例,你可以快速实现SIMD,慢速没有。
答案 5 :(得分:0)
您是否考虑使用liboil等现有解决方案?它实现lots of common SIMD operations并且可以在运行时决定是否使用SIMD /非SIMD代码(使用由初始化函数分配的函数指针)。