Skip to content

Commit

Permalink
[docs] add a section on Function barriers to design patterns tutorial (
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored Feb 5, 2025
1 parent 5d28e63 commit 54c1239
Showing 1 changed file with 60 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,48 @@ end

solve_knapsack_5(data, BinaryKnapsackConfig())

# ## Function barriers

# Querying a variable like `x = model[:x]` is not type stable. The lack of type
# stability means that Julia cannot statically prove what the type of `x` will
# be within a function. As a consequence, the functions
# `add_knapsack_constraints` and `add_knapsack_objective` are slower than they
# would be if Julia could prove that `x` was a [`Containers.DenseAxisArray`](@ref).

# One solution to the problem of type stability is to use a function barrier:

function add_knapsack_constraints(
model::Model,
data::KnapsackData,
config::AbstractConfiguration,
)
return add_knapsack_constraints_inner(model, data, config, model[:x])
end

function add_knapsack_constraints_inner(
model::Model,
data::KnapsackData,
::AbstractConfiguration,
x,
)
@constraint(
model,
capacity_constraint,
sum(v.weight * x[k] for (k, v) in data.objects) <= data.capacity,
)
return
end

# Now, `add_knapsack_constraints_inner` is faster because Julia can compile a
# specific version that depends on the type of `x` passed as an input argument.

# The downside of function barriers is that they require more code. The upside is
# faster code and reduced memory allocations. In general, you should add a
# function barrier if you have a benchmark showing that the difference is
# meaningful for your code base. Function barriers are more likely to be useful
# if the `_inner` function does a lot of computational work, for example, it
# adds thousands of constraints or has a summation over thousands of items.

# ## Remove solver dependence, add error checks

# Compared to where we started, our knapsack model is now significantly
Expand Down Expand Up @@ -492,11 +534,19 @@ function _add_knapsack_variables(
end

function _add_knapsack_constraints(
model::JuMP.Model,
data::_KnapsackData,
config::_AbstractConfiguration,
)
return _add_knapsack_constraints_inner(model, data, config, model[:x])
end

function _add_knapsack_constraints_inner(
model::JuMP.Model,
data::_KnapsackData,
::_AbstractConfiguration,
x,
)
x = model[:x]
JuMP.@constraint(
model,
capacity_constraint,
Expand All @@ -506,11 +556,19 @@ function _add_knapsack_constraints(
end

function _add_knapsack_objective(
model::JuMP.Model,
data::_KnapsackData,
config::_AbstractConfiguration,
)
return _add_knapsack_objective_inner(model, data, config, model[:x])
end

function _add_knapsack_objective_inner(
model::JuMP.Model,
data::_KnapsackData,
::_AbstractConfiguration,
x,
)
x = model[:x]
JuMP.@objective(model, Max, sum(v.profit * x[k] for (k, v) in data.objects))
return
end
Expand Down

0 comments on commit 54c1239

Please sign in to comment.