# RFC: Some Ideas to Tackle #15276 - performance of captured variables in closures

**URL:** <https://discourse.julialang.org/t/rfc-some-ideas-to-tackle-15276-performance-of-captured-variables-in-closures/95260>\
**Category:** Internals & Design\
**Tags:** inference, type-stability, corebox\
**Created:** [February 27, 2023, 10:09am UTC](https://discourse.julialang.org/t/rfc-some-ideas-to-tackle-15276-performance-of-captured-variables-in-closures/95260 "2023-02-27T10:09:31Z")\
**Posts on this page:** 1\
**Showing post:** 55

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [March 8, 2023, 3:48am UTC](https://discourse.julialang.org/t/rfc-some-ideas-to-tackle-15276-performance-of-captured-variables-in-closures/95260/55 "2023-03-08T03:48:21Z")

</div>

To make Idea 1 of the OP work with `@goto`, scrap the above idea of working with the AST and instead work closer to the IR. Seems easier overall (less special cases).

**Draft Pseudo-Code of Idea 1 (with goto):**

(assumption: all AST has been lowered to flattened IR, but no `Core.Box`es have been inserted yet. There might currently be no point during lowering where this is true, but the concept could be adapted.)

With prior knowledge that a local variable `x` is captured by a closure, decide whether to box `x` by the following procedure (true for box, false for no-box):

Define `assigns(n,x,checked)` to take a line number `n`, a symbol `x`, and a set of previously explored line numbers `checked`. `assigns` operates as follows:

1. If `n ∈ checked`, return false.
2. Push `n` into `checked`.
3. While true:  
a. If expression at line `n` is a `Expr(:(=), x, ...)`, return true.  
b. If expression at line `n` is a `:method` of a closure known to capture `x`, push `n` into `checked`. Loop over every sub-expression in its CodeInfo’s `.code`. If any is a `Expr(:(=), x, ...)`, return true.  
c. If expression at line `n` is a `Core.GotoNode`, return `assigns(ex.label,x,checked)`.  
d. If expression at line `n` is a `Core.GotoIfNot`, return `assigns(n+1,x,checked)||assigns(ex.label,x,checked)`.  
e. If expression at line `n` is a `Core.ReturnNode`, or if there are no more expressions, return false.  
f. Increment `n`.

Then, initialize `checked` to an empty set; let `n` be the line at which the closure `:method` is defined; and if `assigns(n,x,checked)`, then box `x`. If multiple closures are known to capture `x`, simply call `assigns` on them without emptying `checked` to save some computations.

The basic gist is: Explore all expressions that are syntactically reachable in and after the closure’s declaration. If any is an assignment to the captured variable, then box it—but otherwise don’t. Keep a record `checked` of labels and methods that have been visited, to avoid repeated work and getting stuck in loops. As before:

> [@uniment](#):
>
> Allowing for identifiers to be rebound unboxed before the closure is declared (and only boxing if identifiers can be rebound _after_ closure declaration) should cause a good number of boxes to go away (such as [this](https://github.com/JuliaLang/julia/issues/15276#issuecomment-233107381), [this](https://discourse.julialang.org/t/spawn-large-memory-allocation-reduced-when-some-code-abstracted-out-in-a-function/88691), [this](https://discourse.julialang.org/t/strange-memory-allocations/95203), [this](https://github.com/JuliaLang/julia/issues/47539), [this](https://github.com/JuliaLang/julia/issues/45725), [this](https://github.com/JuliaLang/julia/issues/42996), [this](https://github.com/JuliaLang/julia/issues/42052), [this](https://discourse.julialang.org/t/type-unstable-function-because-same-variable-name-used-twice/58810/9), [this](https://discourse.julialang.org/t/base-generator-being-slow-because-type-inference-fails-with-isnothing/57297), [this](https://discourse.julialang.org/t/type-instability-of-nested-function/57007), [this](https://discourse.julialang.org/t/type-instability-in-closure-after-reassigning-a-variable/33656), [this](https://discourse.julialang.org/t/redefining-an-integer-makes-computing-time-blow-up/10535), and [these](https://discourse.julialang.org/t/call-fastclosures-jl-closure-in-array-comprehensions/93622/3), not to mention [the one from Performance Tips](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-captured) as well as most others that I’ve encountered), and barring any mistakes ~~[and excluding `@goto`]~~, afaict it should be a strict improvement over current behavior.

cc @simeonschaub you seem to have a pretty good grasp of closure capture boxing; do you mind if I entice you to opine?

---

_[View the full topic](https://discourse.julialang.org/t/rfc-some-ideas-to-tackle-15276-performance-of-captured-variables-in-closures/95260)._
