A small change in my code resulted in a big slow down. The simple question is why?
Changeing this:
func next(osc: var GSinOsc){.inline.} =
if osc.n > 0:
let d0 = osc.multiplier * osc.current - osc.previous
osc.previous = osc.current
osc.current = d0
else:
inc osc.n
to this:
func next(osc: var GSinOsc){.inline.} =
if osc.n > 0:
osc.current = osc.multiplier * osc.current - osc.previous
osc.previous = osc.current
else:
inc osc.n
The timings:
(seconds: 0, nanosecond: 517771800)
(seconds: 5, nanosecond: 181525300)
The full code:
#nim.cfg
#-d:release
#-d:danger
#-d:samplerate=44100
import std/[math]
const samplerate {.intdefine.} = 44100
type
GSinOsc* = object
multiplier: float
current*: float
previous: float
n: int
func initGsinOsc(frequency: float, phase: float = 0.0): GSinOsc =
let
phaseIncrement = frequency * Tau / samplerate.float
GSinosc(
multiplier: 2 * cos(phaseIncrement),
current: sin(phase),
previous: sin(phase - phaseIncrement),
n: 0
)
func next(osc: var GSinOsc){.inline.} =
if osc.n > 0:
let d0 = osc.multiplier * osc.current - osc.previous
osc.previous = osc.current
osc.current = d0
else:
inc osc.n
#sloooowwww!
#func next(osc: var GSinOsc){.inline.} =
# if osc.n > 0:
# osc.current = osc.multiplier * osc.current - osc.previous
# osc.previous = osc.current
# else:
# inc osc.n
when isMainModule:
import std/[monotimes]
func mse(s1, s2: seq[float]):float =
var mseSum = 0.0
for i in 0..<s1.len:
let diff = s1[i] - s2[i]
mseSum += float(diff * diff)
mseSum / float(s1.len)
let
frequency = 440.0
duration = 4440 #seconds, is 74 min, is cd length.
ticks = duration * samplerate
var
sOut = newSeq[float](ticks)
gOut = newSeq[float](ticks)
let t0 = getMonoTime()
let phaseIncrement = frequency * Tau / samplerate.float
for tick in 0..<ticks:
sOut[tick] = sin(tick.float * phaseIncrement)
let t1 = getMonoTime()
var gsinOsc = initGsinOsc(frequency)
for tick in 0..<ticks:
gsinOsc.next()
gOut[tick] = gsinOsc.current
let t2 = getMonoTime()
let mserror = mse(sOut, gOut)
echo "Goertel vs sin() oscillator:"
echo "time sin() : ", t1 - t0
echo "time Goertzel : ", t2 - t1
echo "mean squared error : ", mserror
echo "RMSD : ", pow(mserror, 0.5)
#Goertel vs sin() oscillator:
#time sin() : (seconds: 5, nanosecond: 938688100)
#time Goertzel : (seconds: 0, nanosecond: 512263100)
#mean squared error : 2.108412096000499e-15
#RMSD : 4.591744870961908e-08
These two sub functions do not generate the same result by the way.
swap 2 lines will..
func next(osc: var GSinOsc){.inline.} =
if osc.n > 0:
let tmp = osc.previous
osc.previous = osc.current
osc.current = osc.multiplier * osc.current - tmp
else:
inc osc.n
applied both your suggestions, with the same result.
Thanks
#sloooowwww!
func next(osc: var GSinOsc){.inline.} =
if osc.n > 0:
osc.current = osc.multiplier * osc.current - osc.previous
osc.previous = osc.current
else:
inc osc.n
doAssert classify(osc.current) != fcSubnormal
In this function, osc.current is just decreasing to zero. And it become sub normal float value. Sub normal float value is slow.
When I put following code in initGsinOsc to make denormal number to zero, the slower version of proc runs as fast as faster one.
{.emit:"""/*INCLUDESECTION*/
#include <immintrin.h>""".}
func initGsinOsc(frequency: float, phase: float = 0.0): GSinOsc =
{.emit:"""
_mm_setcsr(_mm_getcsr() | (_MM_FLUSH_ZERO_ON | _MM_DENORMALS_ZERO_ON));
""".}