Skip to content

Performance disparity in expression operations #85

@jd-lara

Description

@jd-lara

I made a test of the performance impact of having Parameters in an affine expression for a different number of expressions in the vector product operation.

The test compares 3 possible cases.

  1. Expressions with parameters ParameterJuMP.ParametrizedGenericAffExpr{Float64, JuMP.VariableRef}
  2. Expresions without parameters. JuMP.GenericAffExpr{Float64, JuMP.VariableRef}
  3. Mix of expressions using JuMP.GenericAffExpr{Float64, JuMP.VariableRef} and JuMP.GenericAffExpr{Float64, JuMP.VariableRef}

Here is some reproducible code:

using JuMP
using ParameterJuMP
using Random

function model_with_params(size_1, size_2)
    m = Model()
    ParameterJuMP.enable_parameters(m)

    set_names_1 = [randstring() for _ in 1:size_1]
    set_names_2 = [randstring() for _ in 1:size_1]
    time = 1:size_2

    z = @variable(m, [v in set_names_1, t in time])
    x = @variable(m, [v in set_names_2, t in time])

    param_array = Matrix(undef, size_1, size_2)
    for i in 1:size_1, t in 1:size_2
        param_array[i, t] = JuMP.@variable(m, variable_type = ParameterJuMP.Param())
        ParameterJuMP.set_value(param_array[i, t], rand())
    end

    expression_container = JuMP.Containers.DenseAxisArray{ParameterJuMP.ParametrizedGenericAffExpr{Float64, JuMP.VariableRef}}(undef, 1:size_1, 1:size_2)

    for i in 1:size_1, t in 1:size_2
        expression_container[i, t] = z[set_names_1[i], t] + 5.0*x[set_names_2[i], t] + param_array[i, t] + rand()
    end

    col = rand(size_1,)
    _, build_time, build_bytes_alloc, build_sec_in_gc =
            @timed @constraint(m, sum(col[i]*expression_container[i, 1] for i in 1:size_1) == 0.0)
    return build_time, build_bytes_alloc, build_sec_in_gc
end

function model_without_params(size_1, size_2)
    m2 = Model()

    set_names_1 = [randstring() for _ in 1:size_1]
    set_names_2 = [randstring() for _ in 1:size_1]
    time = 1:size_2

    z = @variable(m2, [v in set_names_1, t in time])
    x = @variable(m2, [v in set_names_2, t in time])

    expression_container = JuMP.Containers.DenseAxisArray{JuMP.GenericAffExpr{Float64, JuMP.VariableRef}}(undef, 1:size_1, 1:size_2)

    for i in 1:size_1, t in 1:size_2
        expression_container[i, t] = z[set_names_1[i], t] + 5.0*x[set_names_2[i], t] + rand()
    end

    col = rand(size_1,)
    _, build_time, build_bytes_alloc, build_sec_in_gc =
            @timed @constraint(m2, sum(col[i]*expression_container[i, 1] for i in 1:size_1) == 0.0)
    return build_time, build_bytes_alloc, build_sec_in_gc
end

function model_with_mixed_params(size_1, size_2)
    m = Model()
    ParameterJuMP.enable_parameters(m)

    set_names_1 = [randstring() for _ in 1:size_1]
    set_names_2 = [randstring() for _ in 1:size_1]
    time = 1:size_2

    z = @variable(m, [v in set_names_1, t in time])
    x = @variable(m, [v in set_names_2, t in time])

    param_array = Matrix(undef, size_1, size_2)
    for i in 1:size_1, t in 1:size_2
        param_array[i, t] = JuMP.@variable(m, variable_type = ParameterJuMP.Param())
        ParameterJuMP.set_value(param_array[i, t], rand())
    end

    expression_container = JuMP.Containers.DenseAxisArray{ParameterJuMP.ParametrizedGenericAffExpr{Float64, JuMP.VariableRef}}(undef, 1:size_1, 1:size_2)

    param_ixs = shuffle(1:size_1)[1:Int(size_1/2)]
    for i in 1:size_1, t in 1:size_2
        if i in param_ixs
            expression_container[i, t] = z[set_names_1[i], t] + 5.0*x[set_names_2[i], t] + param_array[i, t] + rand()
        else
            expression_container[i, t] = z[set_names_1[i], t] + 5.0*x[set_names_2[i], t] + rand()
        end
    end

    col = rand(size_1,)
    _, build_time, build_bytes_alloc, build_sec_in_gc =
            @timed @constraint(m, sum(col[i]*expression_container[i, 1] for i in 1:size_1) == 0.0)
    return build_time, build_bytes_alloc, build_sec_in_gc
end


model_with_params(10, 48)
model_without_params(10, 48)
model_with_mixed_params(10, 48)

results = Dict()
results_params = Dict()
results_mixed = Dict()
for i in [100, 1000, 5000, 10_000, 50_000, 100_000, 500_000]
    println(i)
    @show results[i] = model_without_params(i, 48)
    @show results_params[i] = model_with_params(i, 48)
    @show results_mixed = model_with_mixed_params(i, 48)
end

Results:

100
results[i] = model_without_params(i, 48) = (0.00015558, 29488, 0.0)
results_params[i] = model_with_params(i, 48) = (0.000182823, 76048, 0.0)
results_mixed = model_with_mixed_params(i, 48) = (0.000172758, 67904, 0.0)
1000
results[i] = model_without_params(i, 48) = (0.000626027, 197072, 0.0)
results_params[i] = model_with_params(i, 48) = (0.001597523, 560976, 0.0)
results_mixed = model_with_mixed_params(i, 48) = (0.001377474, 462640, 0.0)
5000
results[i] = model_without_params(i, 48) = (0.00356614, 1241216, 0.0)
results_params[i] = model_with_params(i, 48) = (0.008178023, 3541104, 0.0)
results_mixed = model_with_mixed_params(i, 48) = (0.006022408, 2940048, 0.0)
10000
results[i] = model_without_params(i, 48) = (0.006917794, 2477760, 0.0)
results_params[i] = model_with_params(i, 48) = (0.019982686, 7071424, 0.0)
results_mixed = model_with_mixed_params(i, 48) = (0.014490844, 6131968, 0.0)
50000
results[i] = model_without_params(i, 48) = (0.048876917, 10544256, 0.0)
results_params[i] = model_with_params(i, 48) = (0.234299942, 31453584, 0.11880938)
results_mixed = model_with_mixed_params(i, 48) = (0.322152996, 25348384, 0.203175162)
100000
results[i] = model_without_params(i, 48) = (0.106661881, 18983120, 0.0)
results_params[i] = model_with_params(i, 48) = (7.070552948, 54502512, 6.839998649)
results_mixed = model_with_mixed_params(i, 48) = (0.384903389, 48584096, 0.173239921)

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