Skip to content

Too many MethodInstances #2829

Closed
Closed

Description

@odow

Check this out:

Image
import MathOptInterface as MOI

function get_method_instances(x::Core.MethodInstance, ret::Vector{Any})
    push!(ret, x)
    return ret
end

function get_method_instances(x::Core.SimpleVector, ret::Vector{Any})
    for instance in x
        if instance === nothing
            break
        end
        push!(ret, instance)
    end
    return ret
end

function get_method_instances(mod::Module, ret::Vector{Any} = Any[])
    for name in names(mod; all = true)
        f = getfield(mod, name)
        if f === mod
            continue
        elseif f isa Module
            get_method_instances(f, ret)
        elseif  !(f isa Function)
            continue
        end
        for m in methods(f)
            get_method_instances(m.specializations, ret)
        end
    end
    return ret
end
before = get_method_instances(MOI)
include("test/runtests.jl")
after = get_method_instances(MOI)
function get_methods(mod::Module, ret::Vector{Any} = Any[])
    for name in names(mod; all = true)
        f = getfield(mod, name)
        if f === mod
            continue
        elseif f isa Module
            get_methods(f, ret)
        elseif  !(f isa Function)
            continue
        end
        for m in methods(f)
            push!(
                ret,
                m => get_method_instances(m.specializations, Any[])
            )
        end
    end
    return ret
end
ret = get_methods(MOI);
sort!(ret, by = x -> length(x[2]));
import Plots
data = filter!(>(0), length.(last.(ret)))
plt = Plots.plot(
    data,
    1:length(data);
    xscale = :log10,
    color = "#cb3c33",
    width = 3,
    legend = false,
    xlabel = "Number of MethodInstances",
    ylabel = "Method",
    title = "Cumulative distribution of |MethodInstances|",
    dpi = 300,
)
Plots.savefig(plt, "/tmp/method_instances.png")

Running the tests creates 379,000 method instances:

julia> length(before)
3269

julia> length(after)
381680

There are 17,515 different specialisations of supports_constraint:

julia> counts = [a => length(b) for (a, b) in ret];

julia> counts[end]
supports_constraint(::Type{<:MathOptInterface.Bridges.Constraint.AbstractBridge}, ::Type{<:MathOptInterface.AbstractFunction}, ::Type{<:MathOptInterface.AbstractSet}) @ MathOptInterface.Bridges.Constraint ~/git/jump-dev/MathOptInterface/src/Bridges/Constraint/bridge.jl:38 => 17515

Even 100 methods in, there are 766 different method instances for add_constraints:

julia> counts[end - 99]
add_constraints(model::MathOptInterface.ModelLike, funcs, sets) @ MathOptInterface ~/git/jump-dev/MathOptInterface/src/constraints.jl:269 => 766

I wonder if we should @nospecialize a few of the larger ones.

Activity

  1. blegat commented on Sep 3, 2025

    @blegat
    Member

    When bridges use supports_constraint then the cost of supports_constraint is not the bottleneck but when you add many small constraints and the bridges aren't used, we should be careful

  2. odow commented on Sep 3, 2025

    @odow
    MemberAuthor

    There’s a trade off here. Runtime performance against compile time and precompile size. The question is really: is a small decrease in runtime performance worth 100k fewer method instances in test.

    I’ll make some benchmarks before doing anything.

  3. odow commented on Sep 3, 2025

    @odow
    MemberAuthor

    I started looking to make a pretty plot for my MOI talk.

  4. odow commented on Oct 6, 2025

    @odow
    MemberAuthor

    Closed for now by #2830, even though I might come back to it at a later date.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions