You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We are building a Monte Carlo simulation engine for quantitative finance. The core computation is: for each simulated path, evolve a set of coupled stochastic differential equations (interest rate + foreign exchange + other models) across ~600 discretization time steps, then evaluate a portfolio of derivative instruments at ~75 observation dates along each path. We need reverse-mode first-order adjoints and second-order (Hessian) sensitivities with respect to ~500–1500 market inputs.
At full production scale each path involves O(10M–100M) floating point operations across the full time loop, with strong path dependence (collateral, exotic payoffs) that makes sub-path checkpointing impractical.
The current implementation is fully working and produces correct derivatives — the issue is exclusively the linear RSS growth at runtime, not correctness or compilation.
What we implemented
A CRTP-based simulation kernel: Workflow<BindMarketTask, BindModelsTask, SimulateTask, PriceTask> templated on a model container using std::variant + std::visit. The market inputs are bound to pre-allocated model objects (no dynamic allocation inside the differentiated region) — BindMarketTask and BindModelsTask simply write the flat double* inputs into pre-existing structures.
Per-path differentiation: one __enzyme_autodiff call per Monte Carlo path
The functor takes a flat double* array of market inputs and random draws, returns a flat array of portfolio values
Compiled with Clang-18 + Enzyme plugin, -O1, all vectorization disabled as required
The problem
RSS grows ~70 KB per __enzyme_autodiff call (measured with 1 trade, 100 paths, 612 timesteps, 3 models). malloc_trim(0) does not reclaim it — confirmed true leak, not glibc fragmentation. At production scale (10,000 paths, 1000+ trades) this becomes prohibitive.
Mitigations attempted:
Reducing tape scope to a single path (not the full path batch) — leak reduced proportionally but persists
Constructing/destroying context objects inside the loop — no effect
Separate shadow workspace — no effect
The hypothesis:
The combination of std::variant + std::visit + deep CRTP template inlining produces IR complex enough that Enzyme's generated free() calls for the augmented forward cache either don't trigger or trigger incorrectly.
This appears related to #573 (forward split mode cache never freed, open since 2022) and the broader pattern in #1308 (type analysis failures with complex C++ types) and #2369 (STL internals with no augmented forward pass). We also note #278 reports that -mllvm -enzyme-cache-never=1 had no effect on loop caching, which matches our experience. This is also the first Monte Carlo / quantitative finance use case we are aware of in the tracker — we wanted to flag it as a workload class.
Is there a recommended strategy for controlling augmented cache size in kernels with strong path time-dependence (precluding time step level checkpointing)? For example, checkpointing at a coarser granularity such as instrument evaluation boundaries?
Are there compiler flags or Enzyme attributes that help with cache deallocation in heavily templated code?
Is full support for complex C++ (variant, template-heavy kernels) on the roadmap?
Happy to provide a minimal reproducer if useful. This is a real production workload and we would like to find a path back to Enzyme — the performance case for differentiating plain double over operator-overloading AD is compelling for us.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Context
We are building a Monte Carlo simulation engine for quantitative finance. The core computation is: for each simulated path, evolve a set of coupled stochastic differential equations (interest rate + foreign exchange + other models) across ~600 discretization time steps, then evaluate a portfolio of derivative instruments at ~75 observation dates along each path. We need reverse-mode first-order adjoints and second-order (Hessian) sensitivities with respect to ~500–1500 market inputs.
At full production scale each path involves O(10M–100M) floating point operations across the full time loop, with strong path dependence (collateral, exotic payoffs) that makes sub-path checkpointing impractical.
The current implementation is fully working and produces correct derivatives — the issue is exclusively the linear RSS growth at runtime, not correctness or compilation.
What we implemented
The problem
RSS grows ~70 KB per __enzyme_autodiff call (measured with 1 trade, 100 paths, 612 timesteps, 3 models). malloc_trim(0) does not reclaim it — confirmed true leak, not glibc fragmentation. At production scale (10,000 paths, 1000+ trades) this becomes prohibitive.
Mitigations attempted:
The hypothesis:
The combination of std::variant + std::visit + deep CRTP template inlining produces IR complex enough that Enzyme's generated free() calls for the augmented forward cache either don't trigger or trigger incorrectly.
This appears related to #573 (forward split mode cache never freed, open since 2022) and the broader pattern in #1308 (type analysis failures with complex C++ types) and #2369 (STL internals with no augmented forward pass). We also note #278 reports that -mllvm -enzyme-cache-never=1 had no effect on loop caching, which matches our experience. This is also the first Monte Carlo / quantitative finance use case we are aware of in the tracker — we wanted to flag it as a workload class.
Questions
Happy to provide a minimal reproducer if useful. This is a real production workload and we would like to find a path back to Enzyme — the performance case for differentiating plain double over operator-overloading AD is compelling for us.
All reactions