# Module parameters and run-time efficiency

**URL:** <https://discourse.julialang.org/t/module-parameters-and-run-time-efficiency/119505>\
**Category:** Performance\
**Tags:** traits, modules\
**Created:** [September 17, 2024, 2:25pm UTC](https://discourse.julialang.org/t/module-parameters-and-run-time-efficiency/119505 "2024-09-17T14:25:29Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![jhidding](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jhidding/32/212015_2.png) [@jhidding](https://discourse.julialang.org/u/jhidding)\
**Post date:** [September 17, 2024, 2:25pm UTC](https://discourse.julialang.org/t/module-parameters-and-run-time-efficiency/119505/1 "2024-09-17T14:25:29Z")

</div>

Hi, I want to perform dispatch based on functions of the same name within a module. There’s two ways to go about it: having a function `f(m::Module,...)` or defining a trait type and saying `f(::Type{Mod{M}}, ...) where M`.

```julia
module A
   foo(bar) = bar + bar
end

module B
  foo(bar) = bar * bar
end

foo_twice(M::Module, bar) = M.foo(M.foo(bar))

```

or

```julia
struct Mod{M} end

foo_twice(::Type{Mod{M}}, bar) where M = M.foo(M.foo(bar))

```

I found that the second is much much faster than the first. But I’m wondering why. I know that modules could be considered mutable in a way, and that would mean that `M.foo` falls under mutable global lookup somehow? Still, I can’t shake the feeling that these implementations should be equivalent. Am I missing something?

Cheers, Johan

---

<div class="post-metadata">

**Author:** ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)\
**Post date:** [September 17, 2024, 2:39pm UTC](https://discourse.julialang.org/t/module-parameters-and-run-time-efficiency/119505/2 "2024-09-17T14:39:04Z")

</div>

Welcome!

I have never attempted something like this before, and there’s almost certainly a better way to do what you’re doing.

But the issue is probably that `Module` is a type (although I didn’t even know that until now). When compiling this function, Julia does not specialize on the _instance_ of the `Module` but rather on the type `Module` itself. So the exact same compiled code runs whether your module is `A`, `B`, `Base`, `LinearAlgebra`, etc.

Because the function does not specialize on the _specific_ module you pass, `M.foo` must be a run-time lookup. This runs much slower than doing this work at compile time.

Your pattern of `Type{Mod{M}}` forces the instance of the module into the type domain, in which case it is specialized. Although I would have simply used `::Val{M}` rather than `Type{Mod{M}}` to do that.

Perhaps you can provide a little more info on what you’re trying to accomplish? People here can probably suggest a better way to do it. My first impulse is that you should have a single function with multiple methods or pass the function directly, rather than pass modules and do lookups within them.

---

<div class="post-metadata">

**Author:** ![jhidding](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jhidding/32/212015_2.png) [@jhidding](https://discourse.julialang.org/u/jhidding)\
**Post date:** [September 17, 2024, 2:55pm UTC](https://discourse.julialang.org/t/module-parameters-and-run-time-efficiency/119505/3 "2024-09-17T14:55:45Z")

</div>

Thanks, this is more or less what I suspected. I’m working on a larger post on exactly why I’m doing it this way. Short answer: it’s not one function I’m worried about, but a group of related structs and functions that I don’t all want to give trait arguments. Grouping them into a module makes my code more readable.

The funny thing is, I can define

```julia
@inline foo_twice(M::Module, bar) = foo_twice(Mod{M}, bar)

```

regaining a simpler interface with the performance of the trait implementation (as far as I can tell).

---

<div class="post-metadata">

**Author:** ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)\
**Post date:** [September 17, 2024, 3:17pm UTC](https://discourse.julialang.org/t/module-parameters-and-run-time-efficiency/119505/4 "2024-09-17T15:17:57Z")

</div>

Regarding your `@inline` workaround. This will work _sometimes_, because inlining makes constant propagation more potent. But it’s only a hint and may not work in all cases. If the module is not a known constant at the call site, you can still end up in a similar situation to the original (except with the dynamic dispatch happening within the `::Module` method rather than the `::Type{Mod}` method). The [function barrier](https://docs.julialang.org/en/v1/manual/performance-tips/#kernel-functions) this introduces may result in different performance characteristics (often an improvement).

A more idiomatic pattern similar to what you’re doing might be this (I use slightly different patterns for `A` and `B`, you can choose which you prefer):

```julia
module ABCore
export foo
function foo end # function stub we'll add to later
end

module A
using ..ABCore # may need to fiddle with the number of `.` depending on your context
export AA
struct AAType end # singleton type
AA = AAType() # construct an instance
ABCore.foo(::AAType, bar) = bar + bar # add a method
end

module B
using ..ABCore # may need to fiddle with the number of `.` depending on your context
export BB
struct BB end # singleton type
# here I use a slightly different pattern where BB is a type, which will change how this is called
ABCore.foo(::BB, bar) = bar * bar # add a method
end

```

```julia-repl
julia> using .ABCore, .A, .B

julia> foo(AA, 3) # AA is an instance, so call like this
6

julia> foo(BB(), 3) # BB is a type, so use () to construct an instance
9

julia> methods(foo) # all methods of the same function now
# 2 methods for generic function "foo" from Main.ABCore:
 [1] foo(::BB, bar)
     @ Main.B REPL[3]:9
 [2] foo(::Main.A.AAType, bar)
     @ Main.A REPL[2]:10

```

In this pattern, rather than passing around `Module`s, you use the singleton type provided by each module to control which method of `foo` is called. There is only a single `foo` function, but different modules create different methods to implement it.
