Share v3 tick maps via Arc to cut per-solve copies#4566
Conversation
The UniswapV3 `liquidity_net` tick map was deep-copied multiple times per `/solve`, dominating driver heap churn: - `append_events` does `Arc::make_mut` on each event-affected `PoolInfo`, cloning the whole map even for Swap events (the common case) that only touch scalar state and never read the map. - `to_domain` rebuilt a fresh `BTreeMap` for every relevant pool on every request when converting the source pool into the driver domain type. Wrap `liquidity_net` in `Arc<BTreeMap<i32, i128>>` end-to-end (source `PoolState` and domain `Pool`). Now: - Swap events share the map (no copy); Mint/Burn copy-on-write only when they actually mutate ticks. - `to_domain` clones the `Arc` instead of rebuilding the map. The map is write-once / read-once in the domain (built in `to_domain`, read only when serializing the solver request), so the previously distinct `Tick`/`LiquidityNet`-keyed domain map is now the shared source map; the unused `LiquidityNet` newtype is removed. Wire format is unchanged (serde_with serializes the `Arc` transparently; `encode_decode_pool_info` still passes) and the event arithmetic is unchanged (append_events tests pass).
4b334ee to
f928120
Compare
|
Claude finished @jmg-duarte's task in 2m 26s —— View job PR Review: Share v3 tick maps via Arc
LGTM. This is a clean, well-scoped optimization. I traced the full data flow and found no blocking issues. Correctness verified:
Minor (non-blocking) observation: the domain I couldn't run |
There was a problem hiding this comment.
Code Review
This pull request optimizes Uniswap V3 pool data structures by wrapping the liquidity_net tick map in an Arc (Arc<BTreeMap<i32, i128>>) across domain, boundary, and DTO layers. This avoids expensive deep-copying of the tick map during solver requests, utilizing copy-on-write via Arc::make_mut when updating liquidity net values during mint or burn events. No critical issues were found, and no additional feedback is provided.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
Any idea why the overall memory consumption is higher on the second run? |
IIRC, the green run had quite a bit of time running before, and the allocator might've add more aggressive clean up settings (lower decay) but there's one more trick to improve the situation (needs testing, but I'll open the PR ASAP) |
Description
Share the Uniswap V3
liquidity_nettick map viaArcinstead of deep-copyingit on every
/solve(once into the driver domain pool, once per solver request).The optimization relies on forcing the scalar types over wrappers due to Rust not being able to "see through" cross crate boundaries (driver/solver-dto), with the scalars it can do so and properly avoid a full clone.
Changes
PoolState.liquidity_net→Arc<BTreeMap<i32, i128>>, mutated copy-on-writevia
Arc::make_muton Mint/Burn.Pool.liquidity_net→Arc<BTreeMap<i32, i128>>; removed thenow-unused
LiquidityNetnewtype.solvers-dtoConcentratedLiquidityPool.liquidity_net:HashMap→Arc<BTreeMap<i32, i128>>.How to test
Tested in staging, spikes are orders I placed, one can see that the size of the spike reduces considerably