0.6: how do you broadcast `call` over a vector of functions?

Ah, right, this is of course the idiomatic way to broadcast pipelining.

julia> 2.0 .|> fs
2-element Array{Float64,1}:
 2.0
 4.0
4 Likes

That’s great. The call version benchmarks at ~ 20 μs on my machine. The pipeline syntax is as fast as ~750 ns. @andyferris tuple version benchmarks as ~ 35 ns.

using BenchmarkTools

 julia> f=[x->x,x->x^2]
2-element Array{Function,1}:
 #1
 #2

julia>  call(f, x...) = f(x...)
call (generic function with 1 method)

julia> test1() = call.(f, 2.)
test1 (generic function with 1 method)

julia> @benchmark test1()
BenchmarkTools.Trial: 
  memory estimate:  1.67 KiB
  allocs estimate:  46
  --------------
  minimum time:     16.092 μs (0.00% GC)
  median time:      17.721 μs (0.00% GC)
  mean time:        19.897 μs (1.60% GC)
  maximum time:     3.304 ms (96.32% GC)
  --------------
  samples:          10000
  evals/sample:     1
  time tolerance:   5.00%
  memory tolerance: 1.00%


julia> test2() = 2. .|> f
test2 (generic function with 1 method)

julia> @benchmark test2()
BenchmarkTools.Trial: 
  memory estimate:  64 bytes
  allocs estimate:  3
  --------------
  minimum time:     705.667 ns (0.00% GC)
  median time:      732.167 ns (0.00% GC)
  mean time:        783.709 ns (0.97% GC)
  maximum time:     21.167 μs (93.98% GC)
  --------------
  samples:          10000
  evals/sample:     138
  time tolerance:   5.00%
  memory tolerance: 1.00%

julia> f=(x->x,x->x^2)
(#12, #13)

julia> apply(fs::Tuple, x) = _apply(x, (), fs...)
apply (generic function with 1 method)

julia> _apply(x, out::Tuple) = out
_apply (generic function with 1 method)

julia> @inline _apply(x, out::Tuple, f, fs...) = _apply(x, (out..., f(x)), fs...)
_apply (generic function with 2 methods)

julia> test3() = apply(f, 2.)
test3 (generic function with 1 method)

julia> @benchmark test3()
BenchmarkTools.Trial: 
  memory estimate:  32 bytes
  allocs estimate:  1
  --------------
  minimum time:     28.073 ns (0.00% GC)
  median time:      29.532 ns (0.00% GC)
  mean time:        34.651 ns (9.05% GC)
  maximum time:     3.345 μs (96.15% GC)
  --------------
  samples:          10000
  evals/sample:     995
  time tolerance:   5.00%
  memory tolerance: 1.00%

@andyferris, would you know a way to force Julia/LLVM to inline the functions of the tuple in the resulting apply function? I tried adding some @inlines and meta inlines, but didn’t succeed according to @code_native.

Did you add @inline to apply, _apply and the elements of f?

Sometimes you also need to wrap the outer apply in a function to get all the magic of inlining the splat and everything (I didn’t think that was necessary here…)

Otherwise, I’m not sure.

Well, turns out it was the power function I tried. Higher than third powers don’t get inlined.

Haha yes. I think there is a new approach for “literal” exponents in v0.6 that might help here.

It was 0.6 current master of yesterday. But in general it works fine. So probably one only needs to keep in mind to ‘@inline’ all functions down the line.