C++ 重构内联 Double 以获得更快的代码 PolyBelp

Posted

技术标签:

【中文标题】C++ 重构内联 Double 以获得更快的代码 PolyBelp【英文标题】:C++ Refactor an inline Double for faster code PolyBelp 【发布时间】:2021-07-02 16:29:06 【问题描述】:

拥有来自 PolyBlep 振荡器的内联双代码,用于制作合成器。我想知道是否可以使用内部替换或仅重构代码使其更高效,以便编译器在编译时可以自动应用内部。任何其他不使用向量的方法只是简单的重构都可以,只要提高一些速度就可以了,因为它有点费力,谢谢!

inline double blep(double t, double dt) 
 if (t < dt) 
     return -square_number(t / dt - 1);
 
 else if (t > 1 - dt) 
     return square_number((t - 1) / dt + 1);
 
 else 
     return 0;
 

它使用了大量的减法和除法,但也有一些逻辑想知道是否有一种方法可以加快速度以更好地节省 CPU?

这是在 Visual Studio 2019 c++17 中使用 C++ 类型代码。任何建议将不胜感激!

源码https://github.com/martinfinke/PolyBLEP

编辑:t 和 dt 是相位位置 (t) 和频率/音高 (dt) 的可变非静态输入值

【问题讨论】:

你必须改变调用者才能做很多事情,例如使用内在函数对不同的 t 值一次执行 2 或 4 (AVX) bleps。和/或让调用者传递dt 的倒数,这样你就可以乘而不是除。 【参考方案1】:

可能最简单和最有效的优化是用乘法代替除法(假设dt 不变)。但是你也可以避开所有的分支。

请注意,您的函数围绕t=0.5(假设dt&lt;0.5)是点对称的,即f(0.5-x) = -f(0.5+x),并且square 函数内的表达式可以重写为(abs(t-0.5)-0.5)/dt+1)(因为square(-x)=square(x))。

现在如果且只有两个分支都失败了

--> dt<t && t<1-dt
--> dt-0.5 < t-0.5 < 0.5-dt
--> abs(t-0.5) < 0.5-dt
--> abs(t-0.5) - 0.5 < -dt
--> (abs(t-0.5) - 0.5)/dt < -1
--> (abs(t-0.5) - 0.5)/dt + 1 < 0

也就是说,我们可以改写max((abs(t-0.5) - 0.5)/dt + 1, 0)(当然,平方0仍然是0)并总结:

bleb(t,dt) = sign(t-0.5)*square(max(0,(abs(t-0.5)-0.5)/dt+1))

或使用 C++:

double s = t-0.5;
return std::copysign(square_number(std::max(0.0, (std::abs(s)-0.5)*(1.0/dt)+1.0)), s);

1/dt 的计算当然应该被排除在外(你的编译器可能可以做到这一点),copysignabs 应该编译成一些简单的位旋转操作(检查生成的程序集是否你的编译器)。

所有操作都可以毫无问题地矢量化,但您可能需要重构周围的代码才能做到这一点。

【讨论】:

感谢 chtz 的回复! t 和 dt 将是一个变量,并且会以音频速率速度变化。实例变量 * t 示例:phase = osc.t;振荡器的当前相位 [0.0..1.0)。 * dt 示例:freq = osc.dt * srate;振荡器频率,以秒/样本为单位。 我尝试重构为内在的 AVX2 和 SSE2,但我现在对 NEON 不熟悉,我需要为 NEON 找到像 Agnor Fog 这样的库。 ARM 提出了挑战,那里似乎有 2 个选项,但它们的工作方式与 Agnor Fog 库不同,后者的代码重写相当简单。 可能有第三种解决方案,似乎我可以编写常规 C++ 代码,编译器将根据编译的 OS/CPU 对 NEON 和 AVX2 等应用内在提升。我看过几个展开循环的例子,这对我来说也有点陌生,但从长远来看,这些似乎最简单。没有库,也没有多组代码希望这可能是一个解决方案。 只要dt 不会因每次调用您的函数而改变,就值得考虑除法。是的,如果你的代码足够简单,可以使用-O3(和-march=native)进行矢量化和编译,gcc 和 clang 会自动对其进行矢量化(检查编译器生成的程序集)。 dt 将代表音高,因此如果您更改音符,音高不会在每次通话时都发生变化,而是会在音高发生变化时发生变化。

以上是关于C++ 重构内联 Double 以获得更快的代码 PolyBelp的主要内容,如果未能解决你的问题,请参考以下文章

学习重构-目录

重构 C++ 代码以使用前向声明

简单的数学运算在 double 上比在 float 数据类型上更快? [复制]

重构改善既有代码设计--重构手法02:Inline Method (内联函数)& 03: Inline Temp(内联临时变量)

何时可以/将在 C++ 中内联函数?可以强制内联行为吗?

关于C++内联函数