Performance problem to apply a set of functions to one data

Hello,

When we have a variable number of functions fs = [f_1, f_2, ..., f_3] and we want to apply these functions to only one value of x. I’m struggling with performance problems. See:

julia> f1(x) = x^2
julia> f2(x) = x^2 + x

julia> calc(x::Float64, fs::Vector{Function}) =
          Float64[ f(x) for f in fs ];

julia> calc(4.0, [f1, f2])
2-element Array{Float64,1}:
 16.0
 20.0

The answer is right, but there is a performance issue:

julia> @code_warntype calc(4.0, [f1, f2])
Variables
...
  f::Function **RED: HERE**

Body::Array{Float64,1}
4 ┄ %19 = @_6::Tuple{Function,Int64}::Tuple{Function,Int64} **RED: HERE**
│   %22 = (f)(x)::Any                                       **RED: HERE**
...

I think it’s because Function is an abstract type. So I tried use a Tuple instead of Vector, but the problem persists. The code:

julia> calc(x::Float64, fs::Tuple{Vararg{Function}}) =
                  Float64[ f(x) for f in fs ]

julia> @code_warntype calc(4.0, (f1, f2))
  f::Union{typeof(f1), typeof(f2)} **RED: HERE**
4 ┄ %19 = @_6::Union{Tuple{typeof(f1),Int64}, 
Tuple{typeof(f2),Int64}}::Union{Tuple{typeof(f1),Int64}, 
Tuple{typeof(f2),Int64}} **HERE**
│   %22 = (f)(x)::Any **AND HERE**

How can I solve this problem? Thanks.

I think you’re managing to prevent specialisation. calc(x, fs) = [f(x) for f in fs] gives type-stable results with a tuple of functions.

You can define it with calc(x::Number, fs::Tuple) = ... if you’d like to remember your intentions, but there’s no need for more specific annotations.

Firstly you don’t need to annotate calc with type. Also you can achieve that without passing in multiple functions. Just use broadcasting with |>.

Any way this will work

f1(x) = x^2
f2(x) = x^2 + x
calc(x, fs) = Ref(x) .|> fs
@code_warntype calc(4.0, (f1, f2))

but I suggest doing it this way

Ref(4.0) .|> (f1, f2)

The issue is Float64[] which is interacting weird with the functions fs.