我的向量为1024 * 4608个元素( Original_signal ),存储在一个一维数组中。
然后,我将每1024个元素复制32次,将 Original_signal 扩展为 Expand_signal ,到1024 * 32 * 4608。
然后,我使用1024 * 32的 Com_array 用 Expand_signal 进行元素到元素的乘法,并执行After乘法数组的1024FFT。
核心代码如下:
//initialize Original_signal
MKL_Complex8 *Original_signal = new MKL_Complex8[1024*4608];
for (int i=0; i<4608; i++)
{
for (int j=0; j<1024; j++)
{
Original_signal[j+i*1024].real=rand();
Original_signal[j+i*1024].imag=rand();
}
}
//Com_array
MKL_Complex8 *Com_array= new MKL_Complex8[32*1024];
for (int i=0; i<32; i++)
{
for (int j=0; j<1024; j++)
{
Com_array[j+i*1024].real=cosf(2*pi*(i-16.0)/10.0*j^2);
Com_array[j+i*1024].imag=sinf(2*pi*(i-16.0)/10.0*j^2);
}
}
//element-to-element multiplication
MKL_Complex8 *Temp_signal= new MKL_Complex8[1024*32];
MKL_Complex8 *Expand_signal= new MKL_Complex8[1024*32*4608];
gettimeofday(&Bgn_Time, 0);
for (int i=0; i<4608; i++)
{
for (int j=0; j<32; j++)
{
memcpy(Temp_signal+j*1024, Original_signal+i*1024, 1024*sizeof(MKL_Complex8));
}
vmcMul(1024*32, Temp_signal, Com_array, Expand_signal+i*1024*32);
}
gettimeofday(&End_Time, 0);
double time_used = (double)(End_Time.tv_sec-Bgn_Time.tv_sec)*1000000+(double)(End_Time.tv_usec-Bgn_Time.tv_usec);
printf("element-to-element multiplication use time %fus\n, time_used ");
//FFT
DFTI_DESCRIPTOR_HANDLE h_FFT = 0;
DftiCreateDescriptor(&h_FFT, DFTI_SINGLE, DFTI_COMPLEX, 1, 1024);
DftiSetValue(h_FFT, DFTI_NUMBER_OF_TRANSFORMS, 32*4608);
DftiSetValue(h_FFT, DFTI_INPUT_DISTANCE, 1024);
DftiCommitDescriptor(h_FFT);
gettimeofday(&Bgn_Time, 0);
DftiComputeForward(h_FFT,Expand_signal);
gettimeofday(&End_Time, 0);
double time_used = (double)(End_Time.tv_sec-Bgn_Time.tv_sec)*1000000+(double)(End_Time.tv_usec-Bgn_Time.tv_usec);
printf("FFT use time %fus\n, time_used ");
元素到元素的乘法时间为700毫秒(除去存储器成本后),而FFT的时间为500毫秒。
FFT的复数乘法计算为N / 2log2N,元素到元素的乘法为N。
在此项目中,N = 1024。从理论上讲,FFT比元素到元素的乘法慢5倍。为什么实际上更快?
有什么方法可以加快项目进度?
(请注意 Com_array 是对称的)
答案 0 :(得分:0)
如注释中所述,FFT的时间复杂度为您提供了各种FFT长度(相对于某个常数因子)的相对度量。尝试与其他计算进行比较时,此因素变得很重要。此外,您的分析还假设性能受到浮点运算的限制,实际上,实际性能似乎受到其他因素(例如特殊情况处理(例如在此项目中,N = 1024。从理论上讲,FFT比元素到元素的乘法慢5倍。为什么实际速度更快?
NaN
,Inf
),内存和缓存访问的限制
有什么方法可以加快项目进度?
由于您的性能瓶颈围绕着复杂的逐元素矢量乘法运算,因此以下内容将重点介绍如何提高该运算的性能。
我没有MKL可以执行实际的基准测试,但是可以合理地假设vmcMul
的实现对于诸如NaN
和Inf
这样的特殊情况都相当健壮,并且在这种情况下相当优化。
如果您不需要特殊情况下的鲁棒性,请在SSE3处理器上运行,可以保证您的数组大小是2的倍数,并且它们是16字节对齐的,那么通过使用,您可能会获得一些性能提升简化的实现,例如以下内容(基于Sebastien的answer to another post):
#include <pmmintrin.h>
#include <xmmintrin.h>
// Computes and element-by-element multiplication of complex vectors "a" and "b" and
// stores the results in "c".
// Vectors "a", "b" and "c" must be:
// - vectors of even length N
// - 16-bytes aligned
// Special cases such as NaN and Inf are not handled.
//
// based on https://stackoverflow.com/questions/3211346/complex-mul-and-div-using-sse-instructions#4884057
void packed_vec_mult(int N, MKL_Complex8* a, MKL_Complex8* b, MKL_Complex8* c)
{
int M = N/2;
__m128* aptr = reinterpret_cast<__m128*>(a);
__m128* bptr = reinterpret_cast<__m128*>(b);
__m128* cptr = reinterpret_cast<__m128*>(c);
for (int i = 0; i < M; i++)
{
__m128 t0 = _mm_moveldup_ps(*aptr);
__m128 t1 = *bptr;
__m128 t2 = _mm_mul_ps(t0, t1);
__m128 t3 = _mm_shuffle_ps(t1, t1, 0xb1);
__m128 t4 = _mm_movehdup_ps(*aptr);
__m128 t5 = _mm_mul_ps(t4, t3);
*cptr = _mm_addsub_ps(t2, t5);
++aptr;
++bptr;
++cptr;
}
}
优化乘法后,仍然可以通过将Temp_signal
与{{的不同部分)直接相乘多次来摆脱memcpy
到Orignal_signal
的多余副本,从而改善您的实现{1}},如下所示:
Com_array
与将MKL_Complex8* outptr = Expand_signal;
for (int i=0; i<4608; i++)
{
for (int j=0; j<32; j++)
{
packed_vec_mult(1024, Original_signal+i*1024, Com_array+j*1024, outptr);
outptr += 1024;
}
}
替换为vmcMul
的实现相比,这最后一步将使您的性能再提高20%。
最后,由于循环在独立的块上执行操作,因此您可以通过在多个线程上启动并行计算来获得更高的吞吐量(但类似的延迟),从而使CPU始终保持繁忙状态,而不必等待数据传输往/从内存。我的测试表明,改进的程度约为2倍,但结果可能会因您的特定机器而异。