You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

复数浮点输入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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 12:50:20