Skip to content

Gurobi is slow with MOI, likely due to high overhead of model updates #184

Closed
@evanfields

Description

@evanfields

Here's code for a small working example. The problem is a network flow problem over a complete graph: n points are generated in the unit square, corresponding to vertices of the graph. Vectors x and y of length n and sum 1 correspond to vertex-wise supply and demand. Script:

using JuMP, GLPK, Gurobi, Random

# build a network flow JuMP model
# n (dense) constraints, n^2 variables
function build_jumpmodel(x, y, dm, optimizer)
    n = length(x)
	@assert n == length(y)

	m = JuMP.Model(with_optimizer(optimizer))
	@variable(m, f[1:n, 1:n] >= 0)
	@objective(m, Min, sum(dm[i,j] * f[i,j] for i in 1:n, j in 1:n))


	# correct net change at each voxel to transform x to y
	for i in 1:n
		@constraint(m,
            sum(f[j,i] for j in 1:n)
            - sum(f[i,j] for j in 1:n)
            == y[i] - x[i]
        )
	end
    
    return m
end

function random_network_flow(n, optimizer)
    Random.seed!(123)
    # n random vertices in the unit square
    # random weight vectors, each summing to 1
    vert_locs = rand(2,n)
    dm = zeros(n,n)
    for i in 1:n, j in 1:n
        dm[i,j] = (vert_locs[:,i] - vert_locs[:,j]).^2 |> sum |> sqrt
    end
    x = rand(n); x ./= sum(x)
    y = rand(n); y ./= sum(y)
    
    build_time = @elapsed m = build_jumpmodel(x, y, dm, optimizer)
    @show build_time
    
    solve_time = @elapsed JuMP.optimize!(m)
    println()
    @show solve_time
    
    return JuMP.objective_value(m)
end

Example output:

julia> @profile random_network_flow(150, Gurobi.Optimizer)
Academic license - for non-commercial use only
build_time = 0.288749231

Warning: excessive time spent in model updates.
Consider calling update less frequently.

Optimize a model with 150 rows, 22500 columns and 44700 nonzeros
Coefficient statistics:
  Matrix range     [1e+00, 1e+00]
  Objective range  [8e-03, 1e+00]
  Bounds range     [0e+00, 0e+00]
  RHS range        [4e-05, 1e-02]

Concurrent LP optimizer: primal simplex, dual simplex, and barrier
Showing barrier log only...

Presolve removed 1 rows and 150 columns
Presolve time: 0.02s
Presolved: 149 rows, 22350 columns, 44402 nonzeros

Ordering time: 0.00s

Barrier statistics:
 AA' NZ     : 1.103e+04
 Factor NZ  : 1.118e+04 (roughly 9 MBytes of memory)
 Factor Ops : 1.114e+06 (less than 1 second per iteration)
 Threads    : 4

Barrier performed 0 iterations in 0.04 seconds
Barrier solve interrupted - model solved by another algorithm


Solved with primal simplex
Solved in 1445 iterations and 0.04 seconds
Optimal objective  3.122653030e-02

solve_time = 2.96464323
0.031226530301749666

# for context, using GLPK
julia> random_network_flow(150, GLPK.Optimizer)
build_time = 0.112245802

solve_time = 0.216538412
0.031226530301749662

Notice that the overall solve time with Gurobi is order of magnitude worse than with GLPK, but of the ~3 seconds needed for JuMP.optimize! with Gurobi, only ~.04 seconds are spent in Gurobi! Gurobi also warns that excessive time is spent in model updates.

Basic profiling seems a little unreliable here, or I am bad at reading it - reported line numbers and functions don't always agree. That said, looks like lots of time is spent in update_model! of grb_model.jl.

Edit: forgot to mention - this is with Gurobi.jl v0.5.8, LQOI v0.6.0, Gurobi the solver 8.1.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions