我目前正在尝试最有效地将复数数组(内存对齐方式与std::complex相同,但目前使用我们自己的ADT)与与复数数组大小相同的标量值数组进行就地乘法。
算法已经并行化了,即调用对象将工作拆分成线程。此计算是在数组上完成的,以百万为单位-因此,它可能需要一些时间才能完成。CUDA不是这个产品的解决方案,尽管我希望它是。我确实可以访问boost,因此有一些使用BLAS/uBLAS的潜力。
然而,我在想,SIMD可能会产生更好的结果,但我对如何处理复数还不够熟悉。我现在拥有的代码如下(请记住,这段代码被分成与目标机器上的内核数量相对应的线程)。目标机器也是未知的。因此,通用的方法可能是最好的。
void cmult_scalar_inplace(fcomplex *values, const int start, const int end, const float *scalar)
{
for (register int idx = start; idx < end; ++idx)
{
values[idx].real *= scalar[idx];
values[idx].imag *= scalar[idx];
}
}fcomplex的定义如下:
struct fcomplex
{
float real;
float imag;
};我尝试过手动展开循环,因为我的最终循环计数将始终是2的幂,但编译器已经为我做了这件事(我已经展开到32次)。我尝试了一个对标量的常量浮点引用--我想我会保存一次访问--事实证明这与编译器已经在做的事情是相等的。我已经尝试了STL和transform,这两个游戏的结果很接近,但仍然更糟。我还尝试了强制转换为std::complex,并允许它使用用于标量* complex的重载运算符进行乘法运算,但这最终产生了相同的结果。
那么,有谁有什么想法吗?非常感谢您花时间考虑这一点!目标平台为Windows。我使用的是Visual Studio 2008。产品不能同时包含GPL代码!非常感谢。
发布于 2011-07-29 03:42:53
使用SSE可以很容易地做到这一点,例如
void cmult_scalar_inplace(fcomplex *values, const int start, const int end, const float *scalar)
{
for (int idx = start; idx < end; idx += 2)
{
__m128 vc = _mm_load_ps((float *)&values[idx]);
__m128 vk = _mm_set_ps(scalar[idx + 1], scalar[idx + 1], scalar[idx], scalar[idx]);
vc = _mm_mul_ps(vc, vk);
_mm_store_ps((float *)&values[idx], vc);
}
}请注意,values和scalar需要16字节对齐。
或者,您可以直接使用英特尔ICC编译器,让它为您完成繁重的工作。
更新
这是一个改进的版本,它以2的因子展开循环,并使用单个加载指令来获得4个标量值,然后将其解压缩为两个向量:
void cmult_scalar_inplace(fcomplex *values, const int start, const int end, const float *scalar)
{
for (int idx = start; idx < end; idx += 4)
{
__m128 vc0 = _mm_load_ps((float *)&values[idx]);
__m128 vc1 = _mm_load_ps((float *)&values[idx + 2]);
__m128 vk = _mm_load_ps(&scalar[idx]);
__m128 vk0 = _mm_shuffle_ps(vk, vk, 0x50);
__m128 vk1 = _mm_shuffle_ps(vk, vk, 0xfa);
vc0 = _mm_mul_ps(vc0, vk0);
vc1 = _mm_mul_ps(vc1, vk1);
_mm_store_ps((float *)&values[idx], vc0);
_mm_store_ps((float *)&values[idx + 2], vc1);
}
}https://stackoverflow.com/questions/6864053
复制相似问题