# \[ANN\] FunctionWranglers.jl - Fast, inlined execution of arrays of functions

**URL:** <https://discourse.julialang.org/t/ann-functionwranglers-jl-fast-inlined-execution-of-arrays-of-functions/48770>\
**Category:** Package Announcements\
**Tags:** package, announcement\
**Created:** [October 21, 2020, 7:02pm UTC](https://discourse.julialang.org/t/ann-functionwranglers-jl-fast-inlined-execution-of-arrays-of-functions/48770 "2020-10-21T19:02:32Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![tisztamo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tisztamo/32/16200_2.png) [@tisztamo](https://discourse.julialang.org/u/tisztamo)\
**Post date:** [October 21, 2020, 7:02pm UTC](https://discourse.julialang.org/t/ann-functionwranglers-jl-fast-inlined-execution-of-arrays-of-functions/48770/1 "2020-10-21T19:02:32Z")

</div>

The need for this functionality come up recently in this topic:

> [@Automatically fusing together several for-loops](https://discourse.julialang.org/t/automatically-fusing-together-several-for-loops/46097):
>
> I’m trying to speed up an algorithm that computes various graph statistics. The relevant part of the code is: struct erg{G \<: AbstractArray{Bool},F \<: Function} \<: AbstractGraph{Int} m::G fs::Vector{F} trans::G realnodecov::Union{Dict{String,Array{Float64,1}},Nothing} catnodecov::Union{Dict{String,Array{Any,1}},Nothing} edgecov::Union{Array{Array{Float64,2}},Nothing} indegree::Vector{Int64} outdegree::Vector{Int64} n\_funcs::Int kstars\_precomputed::Array{F…

Given an array of small functions (array length up to 200), each of them had to be computed for a large set of inputs. The problem was that dynamic dispatch took too long compared to the actual calculation.

[FunctionWranglers.jl](https://github.com/tisztamo/FunctionWranglers.jl) started as a [solution](https://discourse.julialang.org/t/automatically-fusing-together-several-for-loops/46097/16) of this issue. It uses template (meta)programming to unroll loops that call several functions, and to allow static dispatch/inlining on dinamically provided functions.

The following operations are implemented as of now:

- `smap!` maps a single set of arguments using all the functions into a preallocated array.
- `sfindfirst` looks for the first function which returns `true` for the given arguments, and returns its index.

- `sreduce` transforms a single value using the composite of the functions, and also allows you to provide extra “context” arguments to the functions.

The `smap!` function can be used to solve the above issue:

```julia
  | | |_| | | | (_| | | Version 1.5.2 (2020-09-23)
 _/ |\ __'_|_|_|\__'_| | Official https://julialang.org/ release
|__/ 

julia> using FunctionWranglers

julia> create_adder(value) = (x) -> x + value
create_adder (generic function with 1 method)

julia> adders = Function[create_adder(i) for i = 1:5]
5-element Array{Function,1}:
 #3 (generic function with 1 method)
 #3 (generic function with 1 method)
 #3 (generic function with 1 method)
 #3 (generic function with 1 method)
 #3 (generic function with 1 method)

julia> w = FunctionWrangler(adders)
FunctionWrangler with 5 items: #3, #3, #3, #3, #3, 

julia> result = zeros(Float64, length(adders))
5-element Array{Float64,1}:
 0.0
 0.0
 0.0
 0.0
 0.0

julia> smap!(result, w, 10.0)

julia> result
5-element Array{Float64,1}:
 11.0
 12.0
 13.0
 14.0
 15.0

julia> @btime smap!($result, $w, d) setup = (d = rand())
  3.934 ns (0 allocations: 0 bytes)

```

`sfindfirst` mimics DOM event handling, `sreduce` is mostly a robust \circ. They can be used e.g. for inlining user provided callbacks.

Note that the `s` prefix stays for “switched”, as the role of the functions and the data is reversed compared to the normal use (or for “static”, as the value of package is that it allows static dispatch).

A bit more info can be found in the [readme](https://github.com/tisztamo/FunctionWranglers.jl).

I hope you find this helpful!

---

<div class="post-metadata">

**Author:** ![oschulz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oschulz/32/2998_2.png) [@oschulz](https://discourse.julialang.org/u/oschulz)\
**Post date:** [October 21, 2020, 8:14pm UTC](https://discourse.julialang.org/t/ann-functionwranglers-jl-fast-inlined-execution-of-arrays-of-functions/48770/2 "2020-10-21T20:14:33Z")

</div>

Could [GitHub - yuyichao/FunctionWrappers.jl](https://github.com/yuyichao/FunctionWrappers.jl) be useful here?

---

<div class="post-metadata">

**Author:** ![tisztamo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tisztamo/32/16200_2.png) [@tisztamo](https://discourse.julialang.org/u/tisztamo)\
**Post date:** [October 22, 2020, 5:36am UTC](https://discourse.julialang.org/t/ann-functionwranglers-jl-fast-inlined-execution-of-arrays-of-functions/48770/3 "2020-10-22T05:36:39Z")

</div>

Yes, it could, and it is. In the motivating example, FunctionWrappers improved performance with 20-30%. FunctionWranglers added _another_ 30-40% over it.

FunctionWrappers is lower-level API that gives you type-stability for a single call. It does not unroll your loops, so it may be better when the list of functions changes often. And it has a ~15ns overhead on every call, which is smaller than a dynamic dispatch, but still a lot compared to a simple calculation.

---

<div class="post-metadata">

**Author:** ![oschulz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oschulz/32/2998_2.png) [@oschulz](https://discourse.julialang.org/u/oschulz)\
**Post date:** [October 22, 2020, 6:35am UTC](https://discourse.julialang.org/t/ann-functionwranglers-jl-fast-inlined-execution-of-arrays-of-functions/48770/4 "2020-10-22T06:35:55Z")

</div>

Oh, very interesting, thanks @tisztamo!
