# Multi-thread Variable Creation

**URL:** <https://discourse.julialang.org/t/multi-thread-variable-creation/93300>\
**Category:** Optimization (Mathematical)\
**Tags:** performance\
**Created:** [January 21, 2023, 9:32am UTC](https://discourse.julialang.org/t/multi-thread-variable-creation/93300 "2023-01-21T09:32:07Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![A\_M](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/a_m/32/46038_2.png) [@A\_M](https://discourse.julialang.org/u/A_M)\
**Post date:** [January 21, 2023, 9:32am UTC](https://discourse.julialang.org/t/multi-thread-variable-creation/93300/1 "2023-01-21T09:32:07Z")

</div>

I have a large problem; even model creation will take a long time, and I was thinking of using a thread-safe approach to speed up the process. Here is the code:

```julia
using Random
using JuMP
using HiGHS
using ThreadSafeDicts

struct Limit
    mn::Float64
    mx::Float64
end

I = 1:24
J = [randstring(12) for i in 1:60]
K = ["sample_"*string(i) for i in 1:100_000]
L = Dict()
X = ThreadSafeDict()
Y = Dict()

limits = Dict()
for i in I, j in J
    limits[i, j] = Limit(rand(), rand())
    L[i,j] = sort([rand(), rand(), rand()])
    Threads.@threads for k in K
        X[i, j, k] = rand()
    end
end

model = Model(HiGHS.Optimizer)

BO = Dict()
ABO = Dict()
for i in I, j in J
    mn = limits[i, j].mn
    mx = limits[i, j].mx
    m = max(mn, mx)
    l = L[i,j]

    BO[i, j, 1] = @variable(model, lower_bound = mn, upper_bound = 0.0)
    BO[i, j, 4] = @variable(model, lower_bound = 0.0, upper_bound = mx)
    for seg in 2:3
        BO[i, j, seg] = @variable(model, lower_bound = mn, upper_bound = mx)
    end

    ABO[i, j] = @variable(model, lower_bound = 0.0, upper_bound = mx)

    for k in K
        x = X[i, j, k]
        idx = 3
        while idx ≥ 1 && l[idx] > x
            idx -= 1
        end
        Y[i, j, k] = BO[i, j, idx+1]
    end
end

```

Overall performance is unacceptable compared to AMPL; even if I modified the code to use Channel, that would not help (`julia> Threads.nthreads() 30`)

```julia
using Random
using JuMP
using HiGHS
using ThreadSafeDicts

struct Limit
    mn::Float64
    mx::Float64
end

I = 1:24
J = [randstring(12) for i in 1:60]
K = ["sample_"*string(i) for i in 1:100_000]
L = Dict()
X = ThreadSafeDict()
Y = Dict()

limits = Dict()
for i in I, j in J
    limits[i, j] = Limit(rand(), rand())
    L[i,j] = sort([rand(), rand(), rand()])
    Threads.@threads for k in K
        X[i, j, k] = rand()
    end
end

model = Model(HiGHS.Optimizer)

BO = Dict()
ABO = Dict()
for i in I, j in J
    mn = limits[i, j].mn
    mx = limits[i, j].mx
    m = max(mn, mx)
    l = L[i,j]

    BO[i, j, 1] = @variable(model, lower_bound = mn, upper_bound = 0.0)
    BO[i, j, 4] = @variable(model, lower_bound = 0.0, upper_bound = mx)
    for seg in 2:3
        BO[i, j, seg] = @variable(model, lower_bound = mn, upper_bound = mx)
    end

    ABO[i, j] = @variable(model, lower_bound = 0.0, upper_bound = mx)

    for k in K
        x = X[i, j, k]
        idx = 3
        while idx ≥ 1 && l[idx] > x
            idx -= 1
        end
        Y[i, j, k] = BO[i, j, idx+1]
    end
end

```

I would appreciate any suggestions.

---

<div class="post-metadata">

**Author:** ![mtanneau](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mtanneau/32/17787_2.png) [@mtanneau](https://discourse.julialang.org/u/mtanneau)\
**Post date:** [January 21, 2023, 5:47pm UTC](https://discourse.julialang.org/t/multi-thread-variable-creation/93300/2 "2023-01-21T17:47:13Z")

</div>

What is the difference between the first and second code snippet?

There are a number of performance improvements you can implement; I would recommend you take a look at [Performance Tips · The Julia Language](https://docs.julialang.org/en/v1/manual/performance-tips/) for a more complete picture.

- Put your code in a functions
- Use arrays instead of dictionaries. It’s a LOT faster.  
For instance, running the first loop that populates `X` takes forever (I killed it after ~30s), but the equivalent array `X = rand(length(I), length(J), length(K)) takes less than a second.
- Avoid untyped containers like `Dict()`: the compiler doesn’t know the type of keys & values, and cannot generate efficient code.
- When building the JuMP model, create all variables at once instead of one by one.

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [January 22, 2023, 12:02am UTC](https://discourse.julialang.org/t/multi-thread-variable-creation/93300/3 "2023-01-22T00:02:19Z")

</div>

In general, you should be able to build very large problems without JuMP becoming the bottleneck. If you’re finding it slow, you can likely change how you’re modeling the problem without resorting to multithreading. (And, more importantly, JuMP doesn’t support multithreaded variable creation.)

For example, it seems like the bulk of your model could be:

```julia
using JuMP, HiGHS
I = 24
J = 60
MN = rand(I, J)
MX = 1 .+ rand(I, J)
model = Model(HiGHS.Optimizer)
@variable(model, (k == 4 ? 0 : MN[i, j]) <= BO[i=1:I, j=1:J, k=1:4] <= (k == 1 ? 0 : MX[i, j]))
@variable(model, 0 <= ABO[i=1:I, j=1:J] <= MX[i, j])

```

I don’t know that the `K` loop is doing. Why do you need to create `Y`?

---

<div class="post-metadata">

**Author:** ![A\_M](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/a_m/32/46038_2.png) [@A\_M](https://discourse.julialang.org/u/A_M)\
**Post date:** [January 22, 2023, 2:41am UTC](https://discourse.julialang.org/t/multi-thread-variable-creation/93300/4 "2023-01-22T02:41:17Z")

</div>

Thank @mtanneau, for your recommendation.

- I did use a function and typed containers; I didn’t copy them to make them simpler to understand.
- I used the incremental method in SparseVariables - Efficient sparse modeling with JuMP | Lars Hellemo | JuliaCon 2022; that is why I used Dict and incremental variable creation.

---

<div class="post-metadata">

**Author:** ![A\_M](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/a_m/32/46038_2.png) [@A\_M](https://discourse.julialang.org/u/A_M)\
**Post date:** [January 22, 2023, 2:45am UTC](https://discourse.julialang.org/t/multi-thread-variable-creation/93300/5 "2023-01-22T02:45:10Z")

</div>

Thank you @odow

As I mentioned, I tried to mimic `SparseVariables - Efficient sparse modeling with JuMP | Lars Hellemo | JuliaCon 2022,` but I will do as you said.

I created `Y`, which is `RefVarable`, to represent an equality constraint without creating a constraint.

---

<div class="post-metadata">

**Author:** ![A\_M](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/a_m/32/46038_2.png) [@A\_M](https://discourse.julialang.org/u/A_M)\
**Post date:** [May 22, 2023, 7:04pm UTC](https://discourse.julialang.org/t/multi-thread-variable-creation/93300/6 "2023-05-22T19:04:12Z")

</div>

@odow

I tried something like this, and it is very slow. Any recommendation? Thanks

```julia
@variable(model, (0 ≤ p[s=1:100000, i=1:2000] ≤10)

```

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [May 22, 2023, 7:14pm UTC](https://discourse.julialang.org/t/multi-thread-variable-creation/93300/7 "2023-05-22T19:14:16Z")

</div>

The model would have 2e8 variables. That is a very very large problem, and even if it can build you likely can’t find a solution to it in reasonable time.

Why such a large problem?

---

<div class="post-metadata">

**Author:** ![A\_M](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/a_m/32/46038_2.png) [@A\_M](https://discourse.julialang.org/u/A_M)\
**Post date:** [May 22, 2023, 7:20pm UTC](https://discourse.julialang.org/t/multi-thread-variable-creation/93300/8 "2023-05-22T19:20:29Z")

</div>

That is the problem size that I need to solve, I could solve it, and yes, it takes a long time to solve. I compared AMPL vs. JuMP again, and AMPL looks faster (like five times).

JuMP took like 40 minutes, using AMD EPYC 7B13 (112 threads).

That would be great if JuMP could utilize multi-threading in problem creation.

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [May 22, 2023, 7:27pm UTC](https://discourse.julialang.org/t/multi-thread-variable-creation/93300/9 "2023-05-22T19:27:43Z")

</div>

What solver? Did you use direct mode? Did you disable string names?

See [Performance tips · JuMP](https://jump.dev/JuMP.jl/stable/tutorials/getting_started/performance_tips/#Disable-string-names)
