Function definition inside let-block impacting performance?

These issues are loosely related, but I don’t see them stemming from the same problem or having the same solution.

  1. Closure poor performance with shared captures:
    Boxing (heap allocation) is necessary for values that could get reassigned during the closure’s lifetime, since the semantic is that a captured variable that changes value must reflect this everywhere that it can be seen. Most of the performance loss we see is actually due to type instability from not type-parameterizing the box, which happens because lowering doesn’t have access to function return types.
    Solutions: type-annotate boxed captures or use the let block trick, and either rewrite lowering in Julia so it accesses return types or have lowering emit Julia code which does so.
  2. Conditional function definition weirdness:
    Two versions of local foo() defined in two branches of a conditional could easily become instances of two structs (e.g. var"#foo#3" and var"#foo#4") declared at global scope, and the local variable foo would simply be assigned to one or the other instance. However, Julia’s multiple dispatch paradigm affords us the ability to declare additional methods of foo, and if those additional methods were declared in yet other conditionals … you see the issue.
    Solution: resolve the local const issue, and then treat local functions as local constants (and allow conditionally-added methods only if they are added within the same branch as the initial function declaration).
  3. Mandatory boxing of local function self-calls:
    As I’ve shown, having the function capture itself at all is totally unnecessary if the function’s local identifier is never reassigned (i.e. almost always). It happens as a consequence of the rule that when a local function captures a local variable, if that variable has any syntactical assignment after the function’s instantiation, then it should be boxed (this is a good rule). However, even though the first assignment of the function’s local identifier technically happens after the object’s instantiation, it’s not assigned to any value that isn’t known to the function.
    Solution: carve out an exemption for the first assignment of the identifier which represents the local function itself.
1 Like