Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/142.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 将大复数向量乘以标量C++;_C++_Parallel Processing_Sse_Simd_Complex Numbers - Fatal编程技术网

C++ 将大复数向量乘以标量C++;

C++ 将大复数向量乘以标量C++;,c++,parallel-processing,sse,simd,complex-numbers,C++,Parallel Processing,Sse,Simd,Complex Numbers,我目前正在尝试最有效地将复数数组(内存对齐方式与std::complex相同,但目前使用我们自己的ADT)与与复数数组大小相同的标量值数组进行就地乘法 该算法已经并行化,即调用对象将工作分解为线程。这个计算是在上亿个阵列上完成的,因此,它可能需要一些时间才能完成。CUDA不是这个产品的解决方案,尽管我希望它是。我确实可以使用boost,因此有可能使用BLAS/uBLAS 然而,我认为SIMD可能会产生更好的结果,但我对如何处理复数还不够熟悉。我现在的代码如下(请记住,这是分块成线程的,对应于目标

我目前正在尝试最有效地将复数数组(内存对齐方式与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];
    }
}
我已经尝试手动展开循环,因为我的最终循环计数将始终是2的幂,但编译器已经为我这样做了(我已经展开了32次)。我尝试了对标量的const-float引用——我想我会保存一次访问权——结果证明这与编译器已经在做的相同。我尝试过STL和transform,这两种方法的效果非常接近,但更糟糕。我还尝试强制转换到std::complex,并允许它在乘法中使用scalar*complex的重载运算符,但这最终产生了相同的结果


有人有什么想法吗?非常感谢您花时间考虑这个问题!目标平台是Windows。我正在使用VisualStudio2008。产品也不能包含GPL代码!非常感谢。

您有权使用英特尔的集成性能原件吗?
它们有许多函数可以处理类似这样的情况,并且性能相当不错。您可能在解决特定问题方面取得了一些成功,但如果您的编译器已经在优化代码方面做得很好,我也不会感到惊讶。

您最好的选择是使用优化的BLAS,它将利用目标平台上可用的任何功能。

您可以很容易地使用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);
    }
}

<> P>一个问题是,在函数中,编译器很难理解标量指针并没有指向复杂数组的中间(<代码>标量< /COD>理论上可以指向复杂的或真实的部分)。 这实际上强制了评估的顺序

我看到的另一个问题是,这里的计算非常简单,其他因素会影响原始速度,因此,如果您真的关心性能,我认为唯一的解决方案是实现几个变体,并在运行时在用户机器上测试它们,以发现什么是最快的

我考虑的是使用不同的展开大小,也可以使用<代码>标量< /COD>和<代码>值< /代码>(内存访问模式对缓存效果有很大影响)。 对于不需要的序列化问题,一个选项是查看为以下内容生成的代码

float r0 = values[i].real, i0 = values[i].imag, s0 = scalar[i];
float r1 = values[i+1].real, i1 = values[i+1].imag, s1 = scalar[i+1];
float r2 = values[i+2].real, i2 = values[i+2].imag, s2 = scalar[i+2];
values[i].real = r0*s0; values[i].imag = i0*s0;
values[i+1].real = r1*s1; values[i+1].imag = i1*s1;
values[i+2].real = r2*s2; values[i+2].imag = i2*s2;

因为在这里,优化器在理论上有更多的自由。

另一种产品在我们公司有MKL/TBB/IPP。我也为该产品编程,但这一个没有这些许可证。所以,我很想把它作为解决方案,但由于资金的原因,我们不能用它来解决这个问题:/所以,非常熟悉这些产品,但却不能使用它们,这很糟糕。我还担心编译器已经在为这种方法做我能想到的一切了。我希望IPP是一种选择,但遗憾的是它不是。@Keck:你不再需要IPP等人的特殊许可证了。只要您拥有ICC编译器的许可证,那么这就包括与IPP等链接的二进制文件的分发。@Paul R所以,您认为如果我,开发人员,通过我的公司拥有许可证,我们可以在其他产品上使用它?有些“负责人”告诉我情况并非如此。@Keck:几年前,当他们开始将IPP、MKL等与ICC编译器的专业版捆绑在一起时,许可证发生了变化。只要您拥有ICC本身的有效许可证,那么您生成的任何可交付成果都不会有许可问题,即使它们链接到IPP或其他英特尔库<代码>IANAL当然,所以请仔细检查英特尔许可证,也许可以与您的法律部门联系,但我对此有99.9%的把握。IPP没有复数/标量乘法。我也倾向于尝试。我不熟悉其他产品上使用的超出MKL范围的BLAS。所以,我希望boost中的那个比我现在做的更有效率。LGPL、BOOST许可证的事情是可能的,但除了“没有钱”的问题外,我们还有很强的可跟踪性要求。如果您使用Intel ICC编译器(您只需将其插入Visual Studio),那么它应该能够向量化这样一个简单的循环。它们是16字节对齐的。我在之前的一次优化中对我们正在处理的所有数据都这样做了。我要去试试这个。非常感谢您的回复!让我知道它是如何进行的-我想我可能可以使vk的加载更有效,这样我们就可以获得更高的性能。话虽如此,与加载和存储相关的操作太少,您可能已经受到内存带宽的限制。。。同样的平均时间,编译器可能已经在这样做了?虽然我让它为MSVC 2008编译,但我可能弄糟了一些事情。@Keck:ICC只有500美元左右-如果性能很重要,那么这是比让程序员手工编写优化代码更快、更容易、更可靠的价格。@Keck:我想vk的加载效率可能更高
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);
    }
}
float r0 = values[i].real, i0 = values[i].imag, s0 = scalar[i];
float r1 = values[i+1].real, i1 = values[i+1].imag, s1 = scalar[i+1];
float r2 = values[i+2].real, i2 = values[i+2].imag, s2 = scalar[i+2];
values[i].real = r0*s0; values[i].imag = i0*s0;
values[i+1].real = r1*s1; values[i+1].imag = i1*s1;
values[i+2].real = r2*s2; values[i+2].imag = i2*s2;