你将如何优化这个向量化的谐波和?

how would you optimize this vectorized sum of harmonics?

我正在使用矢量化(仅 SSE2 max 作为 SIMD)将大量谐波加在一起,每个谐波具有不同 phase/magnitude。

这是我的实际尝试:

float output = 0.0f;
simd::float_4 freqFundamentalNormalized = freq * (1.0f / sampleRate);
simd::float_4 harmonicIndex{1.0f, 2.0f, 3.0f, 4.0f};
simd::float_4 harmonicIncrement{4.0f, 4.0f, 4.0f, 4.0f};

// harmonics
const int numHarmonicsV4 = numHarmonics / 4;
const int numHarmonicsRemainder = numHarmonics - (numHarmonicsV4 * 4);

// v4
for (int i = 0; i < numHarmonicsV4; i++) {
    // signal
    simd::float_4 sineOutput4 = simd::sin(mPhases4[i] * g2PIf) * mMagnitudes4[i];

    for (int v = 0; v < 4; v++) {
        output += sineOutput4[v];
    }

    // increments
    mPhases4[i] += harmonicIndex * freqFundamentalNormalized;
    mPhases4[i] -= simd::floor(mPhases4[i]);

    harmonicIndex += harmonicIncrement;
}

// remainder
if (numHarmonicsRemainder > 0) {
    // signal
    simd::float_4 sineOutput4 = simd::sin(mPhases4[numHarmonicsV4] * g2PIf) * mMagnitudes4[numHarmonicsV4];

    for (int v = 0; v < numHarmonicsRemainder; v++) {
        output += sineOutput4[v];
    }

    // increments
    mPhases4[numHarmonicsV4] += harmonicIndex * freqFundamentalNormalized;
    mPhases4[numHarmonicsV4] -= simd::floor(mPhases4[numHarmonicsV4]);
}

但是:

  1. 我想我可以进一步优化它,也许用一些数学技巧,或者以一些增量保存
  2. 我不喜欢为 V4 重复一次“相同的代码”,为 remainder 重复一次(如果谐波数不是 %4):有没有办法把最后一个 V4 的一种“掩码”(例如)将幅度设置为 0? (因此它在同一个块中执行相同的操作,但不会求和到最终输出)。

问题的第二部分是最简单的。任何幅度为 0 的谐波都不会影响正弦输出,因此您只需将 mMagnitude 填充为 4 的倍数即可。

正如 Damien 指出的那样,sin(x) 很昂贵。但是根据 Euler,exp(x)=cos(x) + i sin(x)exp(x+dx)==exp(x)*exp(dx)。每一步只是一个复杂的乘法。

首先,确保 simd::sin 的实施速度很快。请参阅 XMVectorSin,尤其是 XMVectorSinEst in DirectXMath library 以了解如何制作快速的示例,或从那里 copy-paste,或包括库,它是 header-only。指令集可与预处理器宏切换,为获得最佳性能,它需要 SSE 4.1 和 FMA3,但仅适用于 SSE2。

如评论中所述,在循环的所有迭代完成后,您应该只进行一次水平添加。到那时,累积到 SIMD 向量中。

非常小,可能会被编译器优化,但是,您仍然不应该像现在这样访问 mPhases4。在循环体开始时将值加载到向量中,计算输出、递增、计算小数部分,并在每次迭代中仅存储一次更新值。