Skip to content

Conversation

vtjnash
Copy link
Member

@vtjnash vtjnash commented Apr 15, 2025

Most of the complexity here is because of the need to provide legacy support to the experimental external/overlay MethodTable, otherwise this would likely just be a simple renaming of Table->Cache, which would then have simply been unnecessary and made this PR very tiny. Oh well.

Fixes #57560

The remaining question is what to call this global singleton object for introspection. I currently use Core.GlobalMethods. the placeholder name Core._, but someone may object to that.

The remaining failing test (in core) is due to the better optimized cache structure here being better specialized and optimized now, but not yet reimplementing the fallback we had before for that one specific case. The good news is that there is no longer any reason to limit this to that one specific case anymore, since we could now fix all similar cases too and make it generally applicable to them. The bad news is that we might need every supertype to contain a list of all of its possible subtypes so that we can efficiently walk to all of their caches, if we want to restore that case to functional.

TODO:

@StefanKarpinski
Copy link
Member

The bad news is that we might need every supertype to contain a list of all of its possible subtypes so that we can efficiently walk to all of their caches, if we want to restore that case to functional.

This doesn't seem that awful. Would obviously speed up subtypes too, which is currently very slow.

@JeffBezanson
Copy link
Member

Wow, did not expect this to get implemented so fast! Benchmarking this will be interesting (by which I mean, I hope it's boring :) ).

Agree that keeping lists of subtypes seems ok. Though I'm sure someone out there is constructing arbitrarily-long chains of subtypes in a loop 😂

but not yet reimplementing the fallback we had before for that one specific case

I do think we'll need this; I'm pretty sure people use this feature. If it helps, we can shard the global method table based on some properties of the signature; no reason the single method table actually has to be a single MethodTable object.

@vtjnash vtjnash force-pushed the jn/GlobalMethodTable branch 2 times, most recently from 6467c56 to 5f6efef Compare April 23, 2025 20:22
@vtjnash vtjnash force-pushed the jn/GlobalMethodTable branch from 5f6efef to c4d3ce8 Compare April 24, 2025 20:42
@KristofferC
Copy link
Member

KristofferC commented Apr 25, 2025

I currently use the placeholder name Core._, but someone may object to that.

17455884561771762396181280348029

_ is not allowed to be used as an rvalue so it feels weird to use it. Just call it METHOD_TABLE or something? Why make it so obscure?

vtjnash added a commit that referenced this pull request Apr 25, 2025
Ensure a total split of constructors and non-constructors, so they do
not end up seaching the opposing table. Instead cache all kind lookups
as keyed by typename(Type). Not really needed on its own, but it avoids
a minor performance regression in
#58131.
KristofferC pushed a commit that referenced this pull request Apr 29, 2025
Ensure a total split of constructors and non-constructors, so they do
not end up seaching the opposing table. Instead cache all kind lookups
as keyed by typename(Type). Not really needed on its own, but it avoids
a minor performance regression in
#58131.

(cherry picked from commit d992554)
LebedevRI pushed a commit to LebedevRI/julia that referenced this pull request May 2, 2025
Ensure a total split of constructors and non-constructors, so they do
not end up seaching the opposing table. Instead cache all kind lookups
as keyed by typename(Type). Not really needed on its own, but it avoids
a minor performance regression in
JuliaLang#58131.
@vtjnash vtjnash force-pushed the jn/GlobalMethodTable branch 2 times, most recently from 0e51bdf to 898e1c1 Compare May 14, 2025 14:13
Most of the complexity here is because of the need to provide legacy
support to the experimental external/overlay MethodTable, otherwise this
would just be a simple renaming of Table->Cache, which would then have
simply been unnecessary and made this PR very tiny. Oh well.
vtjnash added a commit to vtjnash/Aqua.jl that referenced this pull request May 27, 2025
vtjnash added a commit to vtjnash/Aqua.jl that referenced this pull request May 27, 2025
@vtjnash vtjnash force-pushed the jn/GlobalMethodTable branch from 898e1c1 to 98a18ab Compare May 27, 2025 17:55
@lgoettgens
Copy link
Contributor

JuliaTesting/Aqua.jl#334 mentions that this is supposed to be part of julia 1.12. I find that a bit surprising, given that it is already pretty late in the release cycle and this PR doesn't have a backport label. Could someone clarify that for me? Thanks

@vtjnash vtjnash added the backport 1.12 Change should be backported to release-1.12 label May 28, 2025
@vtjnash vtjnash marked this pull request as ready for review May 28, 2025 14:38
@StefanKarpinski
Copy link
Member

This PR helps compile time regressions, which everyone has been very unhappy about. That's why it's going into the release even though it's so late in the process.

@vtjnash vtjnash merged commit 1735d8f into master May 28, 2025
9 of 12 checks passed
@vtjnash vtjnash deleted the jn/GlobalMethodTable branch May 28, 2025 15:24
@vtjnash
Copy link
Member Author

vtjnash commented May 28, 2025

It is more of an internal correctness fix for the bindings work, when brings along some slight new features (or possibility of new features), than a performance fix (which should be about neutral with it)

DilumAluthgeBot pushed a commit to DilumAluthgeBot/julia that referenced this pull request May 28, 2025
Instead of hiding the fragments of the method table in each TypeName, make one
variable `Core.GlobalMethods` with access to all methods. The need to split
them early for performance apparently had been long past since kwargs and
constructors were already using a single table and cache anyways. Some new
concepts introduced here:

 - A single Method can now be added to multiple functions. So instead of using
   eval in a for loop, we could define it just once (see example below).
 - Several fields (`max_args`, `name`, and `backedges`) were moved from
   MethodTable to their TypeName.
 - TypeName currently has a (user-modifiable) field called `singletonname`. If
   set to something other than `name`, it may be used for pretty printing of a
   singleton object using its "canonical" (unmangled) name, particularly for
   `function`.
 - `Core.Builtin` method table entries are even more normal now, with valid
   `sig` fields, and special logic to specifically prevent adding methods which
   would become ambiguous with them (as that would violate the tfuncs we have
   for them).
 - `Core.GlobalMethods` is a `Base.Experimental.@MethodTable GlobalMethods`.
 - Each `MethodTable` contains a separate `MethodCache` object for managing
   fast dispatch lookups. We may want to use this for the `Method` field
   containing the `invokes` list so that lookups there get more of the same
   optimizations as global calls.
 - Methods could be put into any number of different MethodTables (or none).
   The `Method.primary_world` field is intended to reflect whether it is
   currently put into the GlobalMethods table, and what world to use in the
   GlobalMethods table for running its generator, and otherwise is meaningless.
 - The lock for TypeName backedges is a single global lock now, in
   `Core.GlobalMethods.mc`.
 - The `backedges` in TypeName are stored on the "top-most" typename in the
   hierarchy, to enable efficient lookup (although we might want to consider
   replacing this entirely with a TypeMap). The "top-most" typename is the
   typename of the type closest to Any, after union-splitting, which doesn't
   have an intersection with Builtin (so Function and Any by implication
   continue to not require scanning for missing backedges since it is not
   permitted to add a Method applicable to all functions).
 - Support for having backedges from experimental method tables was removed
   since it was unsound and had been already replaced with staticdata.jl
   several months ago.
 - Documentation lookup for `IncludeInto` is fixed (previously attached only to
   `Main.include` instead of all `include` functions).

Example: given this existing code in base/operators:

    for op in (:+, :*, :&, :|, :xor, :min, :max, :kron)
        @eval begin
            ($op)(a, b, c, xs...) = (@inline; afoldl($op, ($op)(($op)(a,b),c), xs...))
        end
    end

It could now instead be equivalently written as:

    let ops = Union{typeof(+), typeof(*), typeof(&), typeof(|), typeof(xor), typeof(min), typeof(max), typeof(kron)}
        (op::ops)(a, b, c, xs...) = (@inline; afoldl(op, (op)((op)(a,b),c), xs...))
    end

Fixes JuliaLang#57560
vtjnash added a commit that referenced this pull request May 29, 2025
Missed updates from early designs in #58131.
vtjnash added a commit that referenced this pull request May 29, 2025
Missed updates from early designs in #58131.

Fix #58557
vtjnash added a commit that referenced this pull request May 29, 2025
Missed updates from early designs in #58131.

Fix #58557
vtjnash added a commit that referenced this pull request May 29, 2025
Missed updates from early designs in #58131.

Fix #58557
vtjnash added a commit that referenced this pull request May 29, 2025
Missed updates from early designs in #58131.

Fix #58557
vtjnash added a commit that referenced this pull request May 29, 2025
Missed updates from early designs in #58131.

Fix #58557
vtjnash added a commit that referenced this pull request May 29, 2025
Missed updates from early designs in #58131.

Fix #58557
@serenity4
Copy link
Member

Is there a way to access the relevant method table from a MethodList? I understand that we didn't have any API guarantee for being able to do so, but this change broke a few packages, and I'm wondering what the fixes should be.

@vtjnash
Copy link
Member Author

vtjnash commented May 29, 2025

There is only one MethodTable, but a MethodList is not associated with any particular table

maleadt added a commit to JuliaGPU/GPUCompiler.jl that referenced this pull request May 31, 2025
maleadt added a commit to JuliaGPU/GPUCompiler.jl that referenced this pull request Jun 2, 2025
@KristofferC KristofferC mentioned this pull request Jun 2, 2025
58 tasks
@KristofferC
Copy link
Member

Needs a manual backport to 1.12

vtjnash added a commit that referenced this pull request Jun 5, 2025
Instead of hiding the fragments of the method table in each TypeName, make one
variable `Core.GlobalMethods` with access to all methods. The need to split
them early for performance apparently had been long past since kwargs and
constructors were already using a single table and cache anyways. Some new
concepts introduced here:

 - A single Method can now be added to multiple functions. So instead of using
   eval in a for loop, we could define it just once (see example below).
 - Several fields (`max_args`, `name`, and `backedges`) were moved from
   MethodTable to their TypeName.
 - TypeName currently has a (user-modifiable) field called `singletonname`. If
   set to something other than `name`, it may be used for pretty printing of a
   singleton object using its "canonical" (unmangled) name, particularly for
   `function`.
 - `Core.Builtin` method table entries are even more normal now, with valid
   `sig` fields, and special logic to specifically prevent adding methods which
   would become ambiguous with them (as that would violate the tfuncs we have
   for them).
 - `Core.GlobalMethods` is a `Base.Experimental.@MethodTable GlobalMethods`.
 - Each `MethodTable` contains a separate `MethodCache` object for managing
   fast dispatch lookups. We may want to use this for the `Method` field
   containing the `invokes` list so that lookups there get more of the same
   optimizations as global calls.
 - Methods could be put into any number of different MethodTables (or none).
   The `Method.primary_world` field is intended to reflect whether it is
   currently put into the GlobalMethods table, and what world to use in the
   GlobalMethods table for running its generator, and otherwise is meaningless.
 - The lock for TypeName backedges is a single global lock now, in
   `Core.GlobalMethods.mc`.
 - The `backedges` in TypeName are stored on the "top-most" typename in the
   hierarchy, to enable efficient lookup (although we might want to consider
   replacing this entirely with a TypeMap). The "top-most" typename is the
   typename of the type closest to Any, after union-splitting, which doesn't
   have an intersection with Builtin (so Function and Any by implication
   continue to not require scanning for missing backedges since it is not
   permitted to add a Method applicable to all functions).
 - Support for having backedges from experimental method tables was removed
   since it was unsound and had been already replaced with staticdata.jl
   several months ago.
 - Documentation lookup for `IncludeInto` is fixed (previously attached only to
   `Main.include` instead of all `include` functions).

Example: given this existing code in base/operators:

    for op in (:+, :*, :&, :|, :xor, :min, :max, :kron)
        @eval begin
            ($op)(a, b, c, xs...) = (@inline; afoldl($op, ($op)(($op)(a,b),c), xs...))
        end
    end

It could now instead be equivalently written as:

    let ops = Union{typeof(+), typeof(*), typeof(&), typeof(|), typeof(xor), typeof(min), typeof(max), typeof(kron)}
        (op::ops)(a, b, c, xs...) = (@inline; afoldl(op, (op)((op)(a,b),c), xs...))
    end

Fixes #57560

(cherry picked from commit 1735d8f)
vtjnash added a commit that referenced this pull request Jun 5, 2025
Missed updates from early designs in #58131.

Fix #58557

(cherry picked from commit 8ce50a0)
@KristofferC KristofferC mentioned this pull request Jun 6, 2025
60 tasks
@KristofferC KristofferC removed the backport 1.12 Change should be backported to release-1.12 label Jul 9, 2025
@nsajko nsajko added the types and dispatch Types, subtyping and method dispatch label Sep 11, 2025
nsajko added a commit to nsajko/julia that referenced this pull request Sep 12, 2025
Needs manual backport to v1.12 NEWS.

xref:

* issue JuliaLang#54620

* PR JuliaLang#58131
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
types and dispatch Types, subtyping and method dispatch
Projects
None yet
Development

Successfully merging this pull request may close these issues.

methodtable total searches need to consider all binding partitions
7 participants