复数浮点输入FIR实现:输入输出应选交错式还是分离式实虚部?
复数FIR滤波器的存储格式选择:交错式 vs 分离式
我有两个浮点向量,分别对应复数输入的实部和虚部。我写了一段针对浮点输入的FIR计算代码,但不确定复数输入的FIR应该用交错式存储(Re,Im,Re,Im...)还是分离式存储(所有实部连续存储,所有虚部连续存储)。下面是我用x86 Intrinsic实现的代码,请告诉我输入和输出应该采用哪种格式?
void CVector::Fir(float* pInRe, float *pInIm, float* pOutRe, float *pOutIm, float* pCoeff, uint32_t N, uint32_t FilterLength) { int n, k; float* pSrcRe; float* pSrcIm; float* pCoeffSrc = pCoeff; float* pDstRe = pOutRe; float* pDstIm = pOutIm; __m128 Zero128 = _mm_set_ps1(0); __m256 VecRe, VecIm; __m256 SumRe, SumIm; uint32_t Offset; __m128 Low, High, Sum128; for (n = 0; n < N; n++) { pSrcRe = pInRe; pSrcIm = pInIm; SumRe = _mm256_set1_ps(0); SumIm = SumRe; Offset = FilterLength - 1 - n; pCoeffSrc = pCoeff + Offset; for (k = Offset; k < FilterLength; k += 8) { __m256 Coeff = _mm256_load_ps(pCoeffSrc); VecRe = _mm256_load_ps(pSrcRe); VecIm = _mm256_load_ps(pSrcIm); SumRe = _mm256_fmadd_ps(Coeff, VecRe, SumRe); SumIm = _mm256_fmadd_ps(Coeff, VecIm, SumIm); pCoeffSrc += 8; pSrcRe += 8; pSrcIm += 8; } //Accumlate 8 elements in Sum128 //extract 4 low float from Sum Low = _mm256_castps256_ps128(SumRe); //extract 4 high float from Sum High = _mm256_extractf32x4_ps(SumRe, 1); //Add Low, High into __mm128 Sum128 = _mm_add_ps(Low, High); //Pack to __mm128 with one element > 0 Sum128 = _mm_hadd_ps(Sum128, Zero128); Sum128 = _mm_hadd_ps(Sum128, Zero128); //extract float[0] from Sum128 *pDstRe = _mm_cvtss_f32(Sum128); //Accumlate 8 elements in Sum128 //extract 4 low float from Sum Low = _mm256_castps256_ps128(SumIm); //extract 4 high float from Sum High = _mm256_extractf32x4_ps(SumIm, 1); //Add Low, High into __mm128 Sum128 = _mm_add_ps(Low, High); //Pack to __mm128 with one element > 0 Sum128 = _mm_hadd_ps(Sum128, Zero128); Sum128 = _mm_hadd_ps(Sum128, Zero128); *pDstIm = _mm_cvtss_f32(Sum128); pDstRe++; pDstIm++; } }
针对你代码的直接结论
你的代码完全基于分离式存储设计:函数参数明确接收独立的实部数组(pInRe)和虚部数组(pInIm),输出也是分离的pOutRe和pOutIm,计算过程中直接加载连续的实部/虚部向量做SIMD乘加运算。所以当前代码只能适配分离式存储的输入输出。
两种存储格式的优劣分析
1. 分离式存储(当前代码采用)
- 优势:
- 内存对齐友好:连续的实部/虚部数组更容易满足AVX等SIMD指令的256位对齐要求,避免非对齐加载导致的性能损耗。
- 计算逻辑高效:复用实值FIR的优化思路,直接对实虚部分别做卷积,
fmadd指令能最大化利用CPU的浮点运算单元。 - 缓存利用率高:连续的同类型数据(全实部或全虚部)能更好地填充CPU缓存,减少缓存 miss。
- 劣势:
- 需要维护两个独立数组,内存管理稍显繁琐;若需对接交错式存储的模块,需额外做格式转换。
2. 交错式存储(Re,Im,Re,Im...)
- 优势:
- 数据结构紧凑:复数的实虚部成对存储,适合按复数单元操作的场景(如后续复数乘法、FFT),无需同时操作两个数组。
- 劣势:
- SIMD加载效率低:若要单独提取实部或虚部,必须用shuffle指令,增加指令开销;非对齐的交错数组会进一步降低加载性能。
- 缓存浪费:缓存行中同时包含实虚部数据,单独处理实部或虚部时,会加载不需要的另一半数据,降低缓存利用率。
最终建议
如果你的代码已经稳定运行,且没有强制对接交错式存储的需求,继续使用分离式存储是最优选择——你的代码已经针对这种格式做了充分的SIMD优化,性能表现会比适配交错式存储更好。
如果后续需要对接交错式存储的接口,可以在输入输出层做轻量转换:输入时从交错数组中提取实虚部分别存入分离数组,计算完成后再将结果合并成交错格式,核心计算逻辑无需改动。
内容的提问来源于stack exchange,提问作者Zvi Vered
相关产品推荐
相关产品推荐

