-
Notifications
You must be signed in to change notification settings - Fork 10
Closed
Labels
Milestone
Description
TL;DR: using dot
or sum
to construct a SAF leads to significant performance degradation compared to manually constructing the SAF from a loop comprehension. The "manual" version below does not use linear algebra functions, the non-manual one does, and hence calls MutableArithmetics.
Setting:
[87dc4568] HiGHS v0.3.1
[b8f27783] MathOptInterface v0.10.7
MWE:
using BenchmarkTools, SparseArrays
import MathOptInterface
const MOI = MathOptInterface
using HiGHS
using LinearAlgebra
using Random
function build_moi_model(n, optimizer = HiGHS.Optimizer())
MOI.set(optimizer, MOI.Silent(), true)
MOI.set(optimizer, MOI.ObjectiveSense(), MOI.MIN_SENSE)
(x, _) = MOI.add_constrained_variables(optimizer, fill(MOI.Interval(0.0, 1.0), n * n))
xmat = reshape(x, n, n)
for idx in 1:n
# column constraint
MOI.add_constraint(
optimizer,
sum(1.0 * xmat[:, idx]),
MOI.EqualTo(1.0),
)
# row constraint
MOI.add_constraint(
optimizer,
sum(1.0 * xmat[idx, :]),
MOI.EqualTo(1.0),
)
end
return optimizer
end
function moi_lmo_manual(direction, optimizer)
n = size(direction, 1)
variables = reshape(MOI.get(optimizer, MOI.ListOfVariableIndices()), n, n)
obj = MOI.ScalarAffineFunction(
[MOI.ScalarAffineTerm(direction[i], variables[i]) for i in eachindex(direction)],
0.0,
)
MOI.set(optimizer, MOI.ObjectiveFunction{typeof(obj)}(), obj)
MOI.optimize!(optimizer)
v = spzeros(n, n)
@inbounds for i in 1:n
for j in 1:n
vij = MOI.get(optimizer, MOI.VariablePrimal(), variables[i,j])
if vij ≈ 1
v[i,j] = vij
end
end
end
return v
end
function moi_lmo(direction, optimizer)
n = size(direction, 1)
variables = reshape(MOI.get(optimizer, MOI.ListOfVariableIndices()), n, n)
obj = dot(direction, variables)
MOI.set(optimizer, MOI.ObjectiveFunction{typeof(obj)}(), obj)
MOI.optimize!(optimizer)
v = spzeros(n, n)
@inbounds for i in 1:n
for j in 1:n
vij = MOI.get(optimizer, MOI.VariablePrimal(), variables[i,j])
if vij ≈ 1
v[i,j] = vij
end
end
end
return v
end
function f(optimizer, manual)
direction = zeros(n, n)
if manual
moi_lmo_manual(randn!(direction), optimizer)
else
moi_lmo(randn!(direction), optimizer)
end
end
julia> @btime f($optimizer, true)
15.907 ms (27 allocations: 552.14 KiB)
julia> @btime f($optimizer, false)
24.955 ms (20034 allocations: 1.33 MiB)
The left 2/3 is on MA:
packages/MutableArithmetics/UtY4H/src/linear_algebra.jl:427, operate [inlined]
packages/MutableArithmetics/UtY4H/src/reduce.jl:34, MethodInstance for MutableArithmetics.fused_map_reduce(::typeof(MutableArithmetics.add_dot), ::Matrix{Float64}, ::Matrix{MathOptInterface.VariableIndex})
The split above is spent roughly 50-50 on these two:
/home/mbesancon/.julia/packages/MutableArithmetics/UtY4H/src/interface.jl:496, buffered_operate_fallback!! [inlined]
/home/mbesancon/.julia/packages/MutableArithmetics/UtY4H/src/interface.jl:188, mutability [inlined]
All the the time in the manual example is spent within the HiGHS lib call