# Automatically fusing together several for-loops

**URL:** <https://discourse.julialang.org/t/automatically-fusing-together-several-for-loops/46097>\
**Category:** General Usage\
**Created:** [September 5, 2020, 10:49am UTC](https://discourse.julialang.org/t/automatically-fusing-together-several-for-loops/46097 "2020-09-05T10:49:03Z")\
**Posts on this page:** 1\
**Showing post:** 18

<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:** [September 6, 2020, 5:50pm UTC](https://discourse.julialang.org/t/automatically-fusing-together-several-for-loops/46097/18 "2020-09-06T17:50:46Z")

</div>

> [@Adam\_Haber](#):
>
> The length of `fs` can vary between a couple of functions, to 100-200 at most. All functions have the same signature, and the same type of their return value (see below). What would be the pros/cons of using a tuple instead of an array of functions?

If it is 100-200 functions, I don’t think using a tuple makes sense. I’d use a tuple if there are only “handful” of elements (let’s say \< 16, as that’s the [_heuristics_ `Base` uses](https://github.com/JuliaLang/julia/blob/5cba88c5bbb06c4e6c75f3373fe2e214c8d951dd/base/tuple.jl#L183-L185); but the compiler can handle more).

Note that there is no way to express “signature” (input types and output type) in Julia’s type system. That’s why you need the `ccall` hack to get some decent performance.

This is why I asked if you have closures or some callable objects. That is to say, do you have 100 functions with completely different implementations? Or, are they actually some parameterized functions? If they are closures generated by the same function, their type is identical:

```julia
julia> create_adder(value) = (x) -> x + value;

julia> typeof(create_adder(1)) === typeof(create_adder(2))
true

```

So, you can put them in a vector without invoking run-time dispatch:

```julia
julia> callfirst(fs, x) = first(fs)(x);

julia> @code_warntype callfirst([create_adder(1), create_adder(2)], 0)
Variables
  #self#::Core.Const(callfirst, false)
  fs::Vector{var"#1#2"{Int64}}
  x::Int64

Body::Int64
1 ─ %1 = Base.getindex(fs, 1)::var"#1#2"{Int64}
│ %2 = (%1)(x)::Int64
└── return %2

```

Even if not all function types are identical, I’d imagine there are only handful of function types. If that’s the case, you can use `Iterators.flatten` to group closures/functions by their type, e.g., `Iterators.flatten(([create_adder(1), create_adder(2)], [create_adder(1im)]))`.

Unfortunately, Julia’s native `for` loop is not powerful enough to completely optimize a complex iterator like `Iterators.flatten`. You’d need to use `Base.foldl` or some external packages like [FLoops.jl](https://github.com/JuliaFolds/FLoops.jl) (ref [[RFC/ANN] FLoops.jl: fast generic for loops (foldl for humans™)](https://discourse.julialang.org/t/rfc-ann-floops-jl-fast-generic-for-loops-foldl-for-humans/37876)) to eliminate dynamic dispatches.

> [@Adam\_Haber](#):
>
> `subgraphcounts` iterates over the edges of the graph; for each existing edge, it calls `change_scores` , accumulates the result, and then removes the edge. This can theoretically be fully parallelised (by calling `change_scores` separately for each edge, and within each call, delete all the edges before it).

Yes, I think it’d be a better way to parallelize the computation. FWIW, if your graph object already defines `Base.iterate` (and, preferably, `Base.length`), you can simply add [`SplittablesBase.halve`](https://github.com/JuliaFolds/SplittablesBase.jl) to support parallel computations via [Transducers.jl](https://github.com/JuliaFolds/Transducers.jl), aforementioned [FLoops.jl](https://github.com/JuliaFolds/FLoops.jl), [ThreadsX.jl](https://github.com/tkf/ThreadsX.jl), etc.

* * *

Regarding ThreadPools.jl… I hope I don’t sound like trivializing @tro3’s hard work but I think it’s important to understand that the primary motivation for ThreadPools.jl is to _“undo”_ the design of composable multi-threading in Julia (see [Announcing composable multi-threaded parallelism in Julia](https://julialang.org/blog/2019/07/multithreading/)). IIUC, ThreadPools.jl exists for separating out latency-critical code (executed in the primary thread) from throughput-oriented code (executed in non-primary threads). This is _very_ useful if you are writing, e.g., GUI application but it is not desirable to use it in a library or throughput-oriented user code. ThreadPools.jl is a very clever and useful workaround for the current state of multi-threading in Julia. However, I think it’s a good idea to avoid using it if you mainly care about the “overall speed” (i.e., throughput) of your computation and composability with the rest of the ecosystem.

---

_[View the full topic](https://discourse.julialang.org/t/automatically-fusing-together-several-for-loops/46097)._
