master_me icon indicating copy to clipboard operation
master_me copied to clipboard

Optiimzation - avoid log() calls in inner loop

Open x42 opened this issue 3 years ago • 8 comments

https://github.com/trummerschlunk/soundsgood/blob/e8eb9ba756971c22c5da8ca5a1f887f9f342faac/soundsgood.dsp#L348

The value is for display only. With a custom GUI it's preferable to have the GUI call it. Currently FAUST compiles this into the inner loop and calls log10() for each sample!

Since it's for display only, accuracy is not required you might want to investigate a fast_log implementation https://www.flipcode.com/archives/Fast_log_Function.shtml

x42 avatar Aug 13 '22 21:08 x42

It is best to have the values on the dsp side be linear and let the GUI deal with the calculations for log scale.

falkTX avatar Aug 16 '22 05:08 falkTX

Does this helps ?

sletz avatar Aug 16 '22 06:08 sletz

I dont think so. we want to avoid some calculations altogether, the issue is not optimization but one of simply not doing them. The GUI is the one that needs to display values on screen, the values it receives do not need to be in log scale, it is quite fine to do the conversion on the GUI side.

falkTX avatar Aug 16 '22 06:08 falkTX

Then this C++ conversion code might help.

sletz avatar Aug 16 '22 06:08 sletz

I don't know how to make use of the LogValueConverter. But I just remembered that FAUST can use external C functions via Foreign Expressions. That way, some of the normal log10 calls could be replaced with the approximation @x42 suggested above.

jkbd avatar Aug 30 '22 19:08 jkbd

This Taylor-approximation looked interesting to me. But further investigation showed, that this cannot solve our problem.

This Python code

import numpy as np
import matplotlib.pyplot as plt

eps = np.finfo(float).eps
x = np.linspace(eps, 2.0, num=10**4)

plt.plot(x, 20*np.log(x)/np.log(10), label="factor2decibel")

def ln_approx_5(x):
    """
    The Taylor approximation is calculated with SageMath (https://www.sagemath.org/):
    ```
    sage: ln(x).taylor(x, 0.0707945784384138, 5).horner(x)
    ```
    where `0.0707945784384138` is the x value at -23dB.
    """
    return ((((112468.2650380697*x - 49763.396319187115)*x + 9394.609770881507)*x - 997.6311574844394)*x + 70.6268772311377)*x - 4.931306190276485

plt.plot(x, 20*ln_approx_5(x)/np.log(10), label="degree 5")

def ln_approx_11(x):
    return ((((((((((406075992864.511*x - 347850542618.52094)*x + 136810736245.84268)*x - 32688422094.510403)*x + 5289515570.280675)*x - 611632740.7376956)*x + 51960338.44758817)*x - 3284384.1570663475)*x + 155011.06121954476)*x - 5486.971366164414)*x + 155.3791299085029)*x - 5.667850201820498

plt.plot(x, 20*ln_approx_11(x)/np.log(10), label="degree 11")

def ln_approx_42(x):
    return ((((((((((((((((((((((((((((((((((((((((x*(-(4.75062455944967e+46)*x + 1.4469896798574425e+47) - 2.1524999999999812e+47)*x + 2.0839019499307262e+47)*x - 1.476260185786845e+47)*x + 8.157524221420126e+46)*x - 3.6602274100007795e+46)*x + 1.3707144342764619e+46)*x - 4.370329389860752e+45)*x + 1.2042469514201538e+45)*x - 2.9013054712441056e+44)*x + 6.167924296304309e+43)*x - 1.1656278666150943e+43)*x + 1.9699766602062316e+42)*x - 2.992064900743801e+41)*x + 4.100454989478739e+40)*x - 5.087052816328745e+39)*x + 5.72827702369729e+38)*x - 5.867056670588852e+37)*x + 5.474712652474278e+36)*x - 4.659768327420024e+35)*x + 3.620521220644458e+34)*x - 2.568958037099986e+33)*x + 1.6646984097251124e+32)*x - 9.848248980054393e+30)*x + 5.3151447931161086e+29)*x - 2.614084440040622e+28)*x + 1.1697809757872537e+27)*x - 4.753363261576625e+25)*x + 1.7495071687140665e+24)*x - 5.814333388836693e+22)*x + 1.738233210776675e+21)*x - 4.653111241729626e+19)*x + 1.1091415784016607e+18)*x - 2.3383154588928156e+16)*x + 432431169745875.56)*x - 6944793221868.8955)*x + 95673154083.40453)*x - 1114003390.0013206)*x + 10785012.016971951)*x - 85896.0426594101)*x + 593.2657687415557)*x - 6.974715663591488

plt.plot(x, 20*ln_approx_42(x)/np.log(10), label="degree 42", alpha=0.5)

plt.ylim(-96, 6)
# plt.xscale('log')
plt.xlabel('Factor')
plt.ylabel('Decibel')
plt.legend()
plt.show()

produces the image grafik where you can see, the approximation is only close to factor2decibel within a narrow interval and with higher degrees numerical problems seem to arise.

jkbd avatar Aug 30 '22 21:08 jkbd

Use case of converters here https://github.com/grame-cncm/faust/blob/master-dev/architecture/faust/gui/APIUI.h

sletz avatar Aug 31 '22 06:08 sletz

Reading the produced C++ code of the final product now, the log here is really just the tip of the iceberg and would just be a micro optimization.

x42 avatar Sep 02 '22 11:09 x42