Using ReactiveBasics, I am using Signals to model time series events (extension of discussion here). I want to build a signal that gives the median value of the last 5 inputs to another signal. I have developed 2 approaches, and I suspect both are much slower than they need to be. How can I improve?
Approach 1:
ff = Signal(0.0)
macro medWin(sig,win = 5)
arr = []
quote
flatmap($(esc(sig))) do iv
unshift!($arr, iv)
length($arr) > $(esc(win)) ? pop!($arr) : false
median($arr) |> Signal
end
end
end
function testmacro()
@time begin
f = Signal(0.0)
g = @medWin(f)
for i=1:10^5
push!(f, i)
end
f,g
end
end
testmacro()
Output:
1.243613 seconds (898.99 k allocations: 32.028 MB, 0.70% gc time)
I was concerned that using an array within the global scope to hold values would be expensive, so I tried to find another approach that would use the functions in ReactiveSignal more directly:
function lastX(u::Signal, n ::Int)
sigs = Array{Signal}(1,n+1)
sigs[1] = u
for i=1:n
sigs[i+1] = previous(sigs[i])
end
return zip(sigs[2:end]...)
end
function testfunction()
@time begin
f = Signal(0.0)
g = lastX(f, 5)
h = flatmap(g) do tup
v = collect(tup)
return median(v) |> Signal
end
for i=1:10^5
push!(f, i)
end
f,h
end
end
Output:
1.755510 seconds (13.00 M allocations: 633.236 MB, 16.01% gc time)
I suspect the answer lies in the ReactiveBasics implementations of previous
, subscribe!
, and flatmap
; would have to look closer. Any insight into why this uses so much memory would be helpful to me learning.
Is my first approach as good as it gets? Or are there more improvements that can be achieved?