-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reuse memory for ELBO calculation #75
base: main
Are you sure you want to change the base?
Conversation
Codecov Report
@@ Coverage Diff @@
## main #75 +/- ##
===========================================
- Coverage 92.89% 78.43% -14.47%
===========================================
Files 13 13
Lines 521 524 +3
===========================================
- Hits 484 411 -73
- Misses 37 113 +76
Continue to review full report at Codecov.
|
Thanks @cscherrer for the analysis and PR! I had considered reusing The main reason I considered reusing |
EE = Core.Compiler.return_type( | ||
elbo_and_samples, Tuple{typeof(rng),typeof(logp),eltype(dists),Int} | ||
elbo_and_samples!, Tuple{typeof(rng),typeof(u),typeof(logp),eltype(dists),Int} | ||
) | ||
estimates = similar(dists, EE) | ||
isempty(estimates) && return 0, estimates | ||
Folds.map!(estimates, dists, executor) do dist |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since u
is being overwritten here, I think we cannot use Transducers, or different threads could be writing to it at the same time. This will probably just need to be made a simple map
end | ||
_, iteration_opt = _findmax(estimates |> Transducers.Map(est -> est.value)) | ||
return iteration_opt, estimates | ||
end | ||
|
||
function elbo_and_samples(rng, logp, dist, ndraws) | ||
ϕ, logqϕ = rand_and_logpdf(rng, dist, ndraws) | ||
function elbo_and_samples!(rng, u, logp, dist, ndraws) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
draws
will need to be removed from ELBOEstimate
as well.
Thanks @sethaxen , memory overhead is a great point. One possibility for getting to the best of both worlds is to have a callback that can optionally copy the draws at each step. You could even copy conditionally, like thinning in MCMC or based on some predicate. There's a risk here of over-engineering, solving problems that don't exist, or at least don't exist yet. But then it's also useful to get to an API that can scale. |
Keeping in mind #15, the goal is ultimately to allow the user to provide an object that configures an optimization procedure over the trace of distributions, with the default being |
I noticed this line:
This allocates a new
u
on each iteration, which will have some overhead. This PR removes that, instead allocatingu
once inmaximize_elbo
and reusing it.Some caveats:
That said, here's the test case:
The result is
Note that there are 22 ELBO evaluations.
Now for benchmarking.
BEFORE
AFTER
Now 3359 - 3338 = 21, because we've allocated once instead of 22 times.
It's very minor in this case, but maybe much larger problems could require many more ELBO evaluations. Then again, in those cases the non-allocation overhead will also be much higher.