You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on Jun 14, 2020. It is now read-only.
Summary: Constructing bounded variables from JuMP using LQOI is prohibitively slow due to constraint conflict checks in LQOI and excessive model updates induced by single variable additions and updates. Adapting the implementation of the JuMP.variable macro together with an implementation for addconstraints! in LQOI, seems to recover the speed of MPB. What are the plans to address this?
Disclaimer: All test are on julia 0.6.
using BenchmarkTools
using.Profile
using JuMP
import JuMPMPB # release-0.18 branch renamed to be able to use in parallelusing Gurobi
using MathOptInterface; const MOI=MathOptInterface
functionrun_moi_direct(n)
m = JuMP.direct_model(Gurobi.Optimizer(LogToConsole=false))
@variable m x[i=1:n] <= i
JuMP.optimize(m)
endfunctionrun_moi_caching(n)
m = JuMP.Model(with_optimizer(Gurobi.Optimizer, LogToConsole=false))
@variable m x[i=1:n] <= i
JuMP.optimize(m)
endfunctionrun_mpb(n)
m = JuMPMPB.Model(solver=GurobiSolver(LogToConsole=false))
JuMPMPB.@variable m x[i=1:n] <= i
JuMPMPB.solve(m)
end
The old MathProgBase based JuMP:
run_mpb(1)
@btimerun_mpb(10^4)
# 17.638 ms (29775 allocations: 3.93 MiB)
Direct mode as well as caching mode of MOI based JuMP take about 40 times as
long (in the case of an additional lowerbound this becomes 80 times)
run_moi_direct(1) @btimerun_moi_direct(10^4)
# 683.307 ms (533812 allocations: 24.35 MiB)
The profile shows that 4/5 of the time is spent in LQOI's __check_for_conflicting__ function when setting the upperbound. It seems there should be a way to bypass that check (we're
talking about setting a variable bound) or do it once for the bunch of
variables.
The second big chunk of time (nearly 9/10) is actually spent on updating the
lower level gurobi model twice for each variable, i.e. the separate creation and
updating of the upperbound.
If one neglects the storage bit, the @variable JuMP macro is expanded to
for i =1:n
(JuMP.addvariable)(m, (JuMP.buildvariable)(_error, (JuMP.VariableInfo)(false, NaN, true, i, false, NaN, false, NaN, false, false)), (JuMP.string)("x", "[", i, "]"))
end
or
functionrun_moi_single(n)
m = JuMP.direct_model(Gurobi.Optimizer(LogToConsole=false))
for i =1:n
index = MOI.addvariable!(m.moibackend)
MOI.addconstraint!(m.moibackend, MOI.SingleVariable(index), MOI.LessThan(convert(Float64,i)))
# Plus setting the name, but let's neglect that for nowendendrun_moi_single(1)
@btimerun_moi_single(10^4)
# 107.721 ms (286960 allocations: 16.08 MiB)
One could imagine to have JuMP expand it instead to
functionrun_moi_vector(n)
m = JuMP.direct_model(Gurobi.Optimizer(LogToConsole=false))
index = MOI.addvariables!(m.moibackend, n)
MOI.addconstraints!(m.moibackend, MOI.VectorOfVariables(index), MOI.LessThan.(convert.(Float64,1:n)))
# Plus setting the name, but let's neglect that for nowend
Thanks for this pretty impressive write-up. I don't have time to address all your points, but I'll layout my initial thoughts and come back to this in the weekend.
To date, the development of JuMP/MOI/LQOI/the solver wrappers has focused on functionality. It is likely that at the release of JuMP 0.19 will be slower than JuMP 0.18. However, once the API's settle down, we can start to optimize speed. The 40x+ slow-down is a deal-breaker for my work and @joaquimg's so this will definitely be addressed at some point.
That said, there are a few fixes that can make improvements:
In Gurobi.jl, we should move all of the update_model! lines (e.g., like this) from the end of the set functions to the start of the get functions. Presumably calling update_model! on a model with no changes is a no-op in Gurobi. However, if this has some overhead we could store a flag indicating whether we needed to update the model before letting the user query some part of it.
JuMP should definitely use the MOI.addvariables!(model, n) call. However, this is likely to be a little difficult in some cases, for example, if the conditional syntax is used. I am not aware of any current efforts to adapt the JuMP macros to this. As I said above, the current focus has just been on getting something working. (cc @mlubin.)
LQOI should definitely implement MOI.addconstraints for variable bounds. PR's welcome :)
I want to remove the VectorOfVariables-in-Set functionality in favour of bridges (see Add bridges #34).
__check_for_conflicting__ was added to prevent incorrect behaviour. It wasn't written with performance in mind. PR's to improve this would be great!
Summary: Constructing bounded variables from JuMP using LQOI is prohibitively slow due to constraint conflict checks in LQOI and excessive model updates induced by single variable additions and updates. Adapting the implementation of the JuMP.variable macro together with an implementation for addconstraints! in LQOI, seems to recover the speed of MPB. What are the plans to address this?
Disclaimer: All test are on julia 0.6.
The old MathProgBase based JuMP:
Direct mode as well as caching mode of MOI based JuMP take about 40 times as
long (in the case of an additional lowerbound this becomes 80 times)
The profile shows that 4/5 of the time is spent in LQOI's
__check_for_conflicting__
function when setting the upperbound. It seems there should be a way to bypass that check (we'retalking about setting a variable bound) or do it once for the bunch of
variables.
The second big chunk of time (nearly 9/10) is actually spent on updating the
lower level gurobi model twice for each variable, i.e. the separate creation and
updating of the upperbound.
If one neglects the storage bit, the
@variable
JuMP macro is expanded toor
One could imagine to have JuMP expand it instead to
as proposed in jump-dev/MathOptInterface.jl#63 (comment) .
And provide an LQOI implementation for the
addconstraints!(::SolverInstance,::Vector{F},::Vector{S})
as the POC in coroa@d085429 .
Then one could recover the MPB speed:
Is this the plan? Are there other plans? Does
LinQuadOptInterface.jl/src/constraints/vectorofvariables.jl
Line 18 in 4f75448
already provide this and I just don't see how?
Are the JuMP macros being adapted?
Sorry, this has become somewhat lengthy, it started exploratorily and turned
into something interesting on the way.
The text was updated successfully, but these errors were encountered: