*From*: Stefan Westerfeld <notifications github com>*To*: tim-janik/beast <beast noreply github com>*Cc*: Subscribed <subscribed noreply github com>*Subject*: Re: [tim-janik/beast] BSE: bsemathsignal: add approximations: Bse::fast_log2 and Bse::fast_exp2 (#124)*Date*: Wed, 11 Sep 2019 02:57:38 -0700

Inserting T=float casts makes the function perform better (at least here). It avoids conversions between single precision and double precision values (i.e. cvtsd2ss) which would otherwise be used. So this version

```
static inline float G_GNUC_CONST
fast_log2ff (float value)
{
union {
float f;
int i;
} float_u;
float_u.f = value;
// compute log_2 using float exponent
const int log_2 = ((float_u.i >> 23) & 255) - 128;
// replace float exponent
float_u.i &= ~(255 << 23);
float_u.i += BSE_FLOAT_BIAS << 23;
typedef float T;
T u, x = float_u.f;
// lolremez --long-double -d 6 -r 1:2 "log(x)/log(2)+1-0.00000184568668708"
u = T (-2.5691088815846393966e-2l);
u = u * x + T (2.7514877034856806734e-1l);
u = u * x + T (-1.2669182593669424748l);
u = u * x + T (3.2865287704176774059l);
u = u * x + T (-5.3419892025067624343l);
u = u * x + T (6.1129631283200211528l);
x = u * x + T (-2.040042118396715321l);
return x + log_2;
}
```

is faster, because all operations are on floats. This costs a bit of precision but the float version (`fast_log2ff`

) is faster than using double (`fast_log2fd`

) or long double (`fast_log2fl`

).

```
$ g++ -std=c++17 -Wall -g -O3 -o l2 l2.cc `pkg-config --cflags --libs spectmorph glib-2.0 bse`
$ l2
log2f: 5.369997 ns/call
fast_log2fl: 7.890487 ns/call
fast_log2fd: 4.662395 ns/call
fast_log2ff: 3.652096 ns/call
prec: fast_log2ff: 4.493532e-06
prec: fast_log2fd: 3.721012e-06
prec: fast_log2fl: 3.691373e-06
$ clang++ -std=c++17 -g -O3 -o l2 l2.cc `pkg-config --cflags --libs spectmorph glib-2.0 bse`
$ l2
log2f: 5.323792 ns/call
fast_log2fl: 7.597113 ns/call
fast_log2fd: 5.201006 ns/call
fast_log2ff: 4.071403 ns/call
prec: fast_log2ff: 4.493532e-06
prec: fast_log2fd: 3.721012e-06
prec: fast_log2fl: 3.691373e-06
```

On the other stuff I mostly agree. If you have use cases in mind (for key tracking or filter frequency modulation it doesn't matter) that need integers k exp2 (k) to be 2^k and you think you want to pay for it with one add-mul, ok. I think relative error is the most important goal here, though. For instance if the key tracking algorithm returns 222 instead of 220, from a muscians point it is as bad as returning 888 instead of 880. Both sound equally wrong, and both have the same relative error (not absolute error).

Applying corrections for fast_log2 (2^k) to yield k for integer k sounds ok to me. Note that it doesn't fix fast_log2 (7.999999) to be 3, as you patched only the case where the input is equal to or slightly greater than 2^k, not the case where it is slightly smaller.

```
fast_log2fl (7.999999) = 2.999996; log2f (7.999999) = 3.000000
fast_log2fl (8.000000) = 3.000000; log2f (8.000000) = 3.000000
fast_log2fl (8.000001) = 3.000000; log2f (8.000001) = 3.000000
```

This could be fixed by adjusting the linear coeffcient of the remez polynomial, but this would make our worst case error larger, and I think as the result is so close to the perfect value it is probably not worth it.

As for whether to approximate at all on AMD64: my impression from the benchmarks is that in many cases using one of the approximations would yield sufficient quality faster that exp2f or log2f. On AMD64 especially when using T=float internally.

However, the gain is not dramatic, and maybe we're trying to optimize something with approximations that is not really a performance problem. For instance the LadderFilter (the place where this started) typically only needs one log2 value per note-on. Only portamento would affect this negatively which we do not support at the moment. What I'm trying to say here is: if we use log2f/exp2f and one day we run perf on beast and see than 10% of the CPU usage is spent in exp2f, we could still deal with it at that point in time.

—

You are receiving this because you are subscribed to this thread.

Reply to this email directly, view it on GitHub, or mute the thread.

**References**:

[Date Prev][Date Next] [Thread Prev][Thread Next] [Thread Index] [Date Index] [Author Index]