# Custom stateful transducer performance

**URL:** <https://discourse.julialang.org/t/custom-stateful-transducer-performance/34065>\
**Category:** Performance\
**Tags:** performance\
**Created:** [February 1, 2020, 7:55pm UTC](https://discourse.julialang.org/t/custom-stateful-transducer-performance/34065 "2020-02-01T19:55:01Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jakub\_Wronowski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakub_wronowski/32/204030_2.png) [@Jakub\_Wronowski](https://discourse.julialang.org/u/Jakub_Wronowski)\
**Post date:** [February 1, 2020, 7:55pm UTC](https://discourse.julialang.org/t/custom-stateful-transducer-performance/34065/1 "2020-02-01T19:55:01Z")

</div>

Hi, I am trying to implement a custom stateful transducer, but I am not happy with performance I got.

```julia
using Transducers
using Transducers: Transducer, R_, next, inner, xform
using Transducers: start, complete, wrap, unwrap, wrapping
using DataStructures

struct MovingMaximum{T<:Real} <: Transducer
    windowlength::Int64
end

IndexValuePair{T} = NamedTuple{(:index, :value),Tuple{Int64,T}}

function Transducers.start(rf::R_{MovingMaximum{T}}, result) where T<:Real
    buffer = CircularBuffer{IndexValuePair{T}}(xform(rf).windowlength)
    private_state = (buffer, 1)
    return wrap(rf, private_state, start(inner(rf), result))
end

function Transducers.next(rf::R_{MovingMaximum{T}}, result, input) where T<:Real
    wrapping(rf, result) do (buffer, k), iresult
        while !isempty(buffer) && last(buffer).value < input
            pop!(buffer)
        end
        if !isempty(buffer) && first(buffer).index == k
            popfirst!(buffer)
        end
        push!(buffer, (index=k, value=input))
        iresult = next(inner(rf), iresult, first(buffer).value)
        return (buffer, ifelse(k == xform(rf).windowlength, 1, k+1)), iresult
    end
end

function Transducers.complete(rf::R_{MovingMaximum{T}}, result) where T<:Real
    _private_state, inner_result = unwrap(rf, result)
    return complete(inner(rf), inner_result)
end

```

For reference this simpler approach with state kept in closures is more performant:

```julia
using Transducers
using DataStructures
IndexValuePair{T} = NamedTuple{(:index, :value),Tuple{Int64,T}}
function movingmaximum(::Type{T}, window::Int) where {T<:Real}
    buffer = CircularBuffer{IndexValuePair{T}}(window)
    k::Int64 = 1
    return ScanEmit(nothing) do _, x
        while !isempty(buffer) && last(buffer).value < x
            pop!(buffer)
        end
        
        if !isempty(buffer) && first(buffer).index == k
            popfirst!(buffer)
        end
        push!(buffer, (index=k, value=x))

        value = first(buffer).value
        k = ifelse(k == window, 1, k+1)
        return value, nothing
    end
end

```

```julia
x = rand(10000000);
K = 100;

using BenchmarkTools
@btime scan_emit = collect(movingmaximum(Float64, $K), $x);
@btime custom_transducer = collect(MovingMaximum{Float64}($K), $x);

julia> @btime scan_emit = collect(movingmaximum(Float64, $K), $x);
  372.746 ms (34 allocations: 129.00 MiB)

julia> @btime custom_transducer = collect(MovingMaximum{Float64}($K), $x);
  582.371 ms (20000035 allocations: 675.35 MiB)

```

I followed [this tutorial](https://tkf.github.io/Transducers.jl/dev/examples/transducers/#Stateful-transducer-1). Any ideas if I did something wrong, or is it by design? I ran it on Julia 1.5

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [February 2, 2020, 4:04am UTC](https://discourse.julialang.org/t/custom-stateful-transducer-performance/34065/2 "2020-02-02T04:04:59Z")

</div>

You’d need `@inline` for `Transducers.next(rf::R_{MovingMaximum{T}}, result, input)`. It’s a bit faster than the other version on my laptop if I do that. Stateful transducers typically need to use compiler at its full capability (and sometimes compiler just gives up).

```julia
julia> @btime collect(MovingMaximum{Float64}($K), $x);
  661.515 ms (20000035 allocations: 675.35 MiB)

julia> # put @inline

julia> @btime collect(MovingMaximum{Float64}($K), $x);
  301.025 ms (31 allocations: 129.00 MiB)

julia> @btime collect(movingmaximum(Float64, $K), $x);
  378.188 ms (34 allocations: 129.00 MiB)

```

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [February 2, 2020, 8:44am UTC](https://discourse.julialang.org/t/custom-stateful-transducer-performance/34065/3 "2020-02-02T08:44:17Z")

</div>

> [@Jakub\_Wronowski](#):
>
> Julia 1.5

btw do you mean `v1.0.5`?

---

<div class="post-metadata">

**Author:** ![Jakub\_Wronowski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakub_wronowski/32/204030_2.png) [@Jakub\_Wronowski](https://discourse.julialang.org/u/Jakub_Wronowski)\
**Post date:** [February 2, 2020, 9:20am UTC](https://discourse.julialang.org/t/custom-stateful-transducer-performance/34065/4 "2020-02-02T09:20:39Z")

</div>

> [@tkf](#):
>
> You’d need `@inline`

Thanks! It speeded up from 582 ms to 276 ms, I think it is really fast now, I expected such timings writing this transducer.

> [@jling](#):
>
> btw do you mean `v1.0.5` ?

Nope, I mean 1.5 - nightly build:

> **[Julia Downloads (nightly binaries)](https://julialang.org/downloads/nightlies/)**
>
> The official website for the Julia Language. Julia is a language that is fast, dynamic, easy to use, and open source. Click here to learn more.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [February 2, 2020, 3:07pm UTC](https://discourse.julialang.org/t/custom-stateful-transducer-performance/34065/5 "2020-02-02T15:07:22Z")

</div>

Latest development branch doesn’t always mean best performance

---

<div class="post-metadata">

**Author:** ![Jakub\_Wronowski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakub_wronowski/32/204030_2.png) [@Jakub\_Wronowski](https://discourse.julialang.org/u/Jakub_Wronowski)\
**Post date:** [February 2, 2020, 3:21pm UTC](https://discourse.julialang.org/t/custom-stateful-transducer-performance/34065/6 "2020-02-02T15:21:10Z")

</div>

Sure, but by this I meant that I checked it on 1.3, 1.4 and 1.5, sorry for not being precise. Adding @inline solves my problem everywhere.
