Edit: Please see the #7 post directly if you’re interested. Thanks.
The idea is: I at first create a Vector
v = Vector{Matrix{Float64}}(undef, 100);
Then I need to fill it with some concrete objects (here are matrices):
for i = 1:100
v[i] = rand(9999,9999)
end
These operations will use much (say 60GB) memory.
Suppose now I’ve finished using the entry matrices and now I want to reclaim my memory.
I can do
dumb = rand(3, 3)
for i = 1:100
v[i] = dumb
end
GC.gc()
Then the memory occupation will drop to the initial level (say, 60GB are freed).
The question now is that the above procedure appears not to be effective for JuMP’s direct_models (with the common GRB_ENV).
const GRB_ENV = Gurobi.Env();
const m = Vector{JuMP.Model}(undef, S); # S is some large number (of scenarios)
# fill `m` with `S` concrete large distinct models, which are all initialized by this function
function model(GRB_ENV)
m = JuMP.direct_model(Gurobi.Optimizer(GRB_ENV))
JuMP.set_attribute(m, "OutputFlag", 0)
JuMP.set_attribute(m, "Threads", 1)
JuMP.set_attribute(m, "Method", 2)
JuMP.set_attribute(m, "Crossover", 0)
m
end;
# then create a common dumb model also using the above function
# then flush the `m` with the dumb model
# I fail to see my memory reclaimed, why?
According to my observation from the htop in zsh, the occupation at three instances are illustrated below
const S = 256
const GRB_ENV = Gurobi.Env();
const m = Vector{JuMP.Model}(undef, S);
function my_initialize(GRB_ENV)
m = JuMP.direct_model(Gurobi.Optimizer(GRB_ENV))
JuMP.set_attribute(m, "OutputFlag", 0)
JuMP.set_attribute(m, "Threads", 1)
JuMP.set_attribute(m, "Method", 2)
JuMP.set_attribute(m, "Crossover", 0)
m
end
function fill_with_concrete!(m, s, Data)
model = my_initialize(GRB_ENV)
x = add_decision_and_constrs!(model, s, Data) # this operation will allocate much memory
m[s] = model
end
# Mem[873M/256G]
foreach(wait, [Threads.@spawn(fill_with_concrete!(m, s, Data)) for s=1:S])
# Mem[4.68G/256G]
dumb = my_initialize(GRB_ENV)
for s=1:S
m[s] = dumb
end
GC.gc()
# Mem[4.62G/256G]
Otherwise: this code should work. If memory isn’t being freed, you must have a reference to the model somewhere that is preventing the GC from freeing it. You can see how many models are attached to the environment using GRB_ENV.attached_models.
Try running GC.gc() multiple times. And add a return nothing to the end of perfect_consts!. You must have a reference somewhere that is preventing the models from being garbage collected.
I find that the manual GC.gc() appears to be nondeterministic, via this self-contained general example:
function f!(v)
m = v[end]
for i = eachindex(v)
v[i] = m
end
end;
# 662M here
v = map(fetch, [Threads.@spawn(rand(333,333)) for _ = 1:10000]);
# 8.91G here
f!(v)
GC.gc()
# 8.91G here
v1 = map(fetch, [Threads.@spawn(rand(333,555)) for _ = 1:10000]);
# 22.1G here
f!(v1)
GC.gc()
# 999M here