Skip to content

Add a LRU cache to limit indexing memory usage. - #2079

Merged
voodoos merged 28 commits into
ocaml:mainfrom
voodoos:lru_cache_2
Jul 16, 2026
Merged

Add a LRU cache to limit indexing memory usage.#2079
voodoos merged 28 commits into
ocaml:mainfrom
voodoos:lru_cache_2

Conversation

@voodoos

@voodoos voodoos commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator
  • Change the way small values are handled by storing them along their parent.
  • Make filenames relative to the current working directory of the indexer.
  • Disable related-uids compression

This work is based on #2050

voodoos added a commit to voodoos/merlin that referenced this pull request Jun 15, 2026
@voodoos

voodoos commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator Author

cc @art-w if you want to have a quick 🍔 look ?

Comment thread src/index-format/granular_marshal.ml Outdated
Comment thread src/index-format/granular_marshal.ml Outdated
Comment thread src/index-format/granular_marshal.ml Outdated
}
let resolve_filename store ~filename =
if Filename.is_relative filename then
Filename.concat (Filename.dirname store.filename) filename

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: I believe filename could contain ../../ (introduced in relativize below), which could mess up relativize when we write later? (I guess the result wouldn't be wrong but would have ../../path/../otherpath)

Comment thread src/analysis/occurrences.ml Outdated
Comment on lines +93 to +94
could lead to memory leaks if the user of the cache keeps a reference
to the cell. *)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some TODOs here with nice ideas :) (but not essentials)

Comment thread src/index-format/granular_marshal.ml Outdated
Comment thread src/index-format/granular_marshal.ml Outdated
Comment thread src/index-format/granular_marshal.ml Outdated
@voodoos

voodoos commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks a lot for the review @art-w. I think I addressed all the important comments. These are probably worth a re-review 🙂

@voodoos

voodoos commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator Author

I ran two benchmarks with a LRU large enough to store the entire date.

One that builds Merlin's @ocaml-index: in this build, there are no aggregations, only things built In_memory and written to the disk.

Command Mean [s] Min [s] Max [s]
Before pointing indexes 10.570 ± 0.175 10.366 10.935
With pointing indexes 10.533 ± 0.392 10.151 11.528
With pointing + LRU 10.951 ± 0.347 10.596 11.619

One that aggregates all of Merlin indexes:

Command Mean [ms] Min [ms] Max [ms]
Before pointing indexes 768.5 ± 43.1 707.9 829.1
With pointing indexes 229.4 ± 10.3 217.2 250.3
With pointing + LRU 354.9 ± 19.2 340.4 406.1

So we loose a bit of the improvements that pointing indexes brought, but we are still much faster at aggregating than before this work.

The most likely explanation for the slowdown is the move to a functional union-find, which is a necessary evil.

I think these results are quite good overall, with an aggregation that is twice as fast as before.

@voodoos

voodoos commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator Author

And index sizes:

Version total index size
Before pointing indexes 25M
With pointing indexes 25M
With pointing + LRU 27M

These are the index built by dune @ocaml-index so no aggregates which expect that pointing indexes has no impact.

And the size of the aggregate of all these indexes:

Version total index size
Before pointing indexes 25M
With pointing indexes 4.9M
With pointing + LRU 4.1M

@art-w art-w left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! It's looking good, I only have some minor questions/suggestions

Comment thread src/index-format/union_find.ml
Comment thread src/ocaml-index/bin/ocaml_index.ml Outdated
Comment thread src/index-format/index_format.ml Outdated
Comment thread src/index-format/index_format.ml Outdated
index.related_uids
index.related_uids;
Uid_map.schema type_ufstore iter
(fun _iter _uid _content -> ())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need to schema iter on the Uid_set.t stored in _content?

@voodoos voodoos Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the Uid_set is not granular: module Uid_set = Shape.Uid.Set.
Should we make it granular ? I did that in d8aa258

It required removing some fast-paths in Union_find.union that were relying on polymorphic comparison. If it turns out to be important we can add an equal function to granular set.

Uid_map.fold
(fun uid content store ->
match content with
| Root _ -> ensure store uid

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If ensure terminates early because Uid_map.mem uid s1 and there are no Link to that Root in s2, then I believe we need to compute the Uid_set.union of s2 with s1? (otherwise store will only have the s1 set?)

(or is it the case that if we only have a root-with-no-link then the uid set is the singleton {uid} which is guaranteed to be in the s1 set anyway?)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't even have "roots with no link" since we always register at least a pair of uids during the indexing...

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in db3ab43

Comment thread src/index-format/granular_marshal.ml Outdated
Comment thread src/index-format/granular_marshal.ml Outdated
Comment thread src/index-format/granular_marshal.ml
Comment thread src/index-format/granular_marshal.ml Outdated
Comment thread src/index-format/granular_marshal.ml Outdated
@voodoos

voodoos commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

My changes following the latest review do not have any significant performance impact.

@voodoos

voodoos commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator Author

Ok, I think I addressed all your remaining comments @art-w !

@art-w art-w left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, great work!

Lucccyo and others added 17 commits July 16, 2026 07:41
- Change the way small values are handled by storing them along their parent.
- Make filenames relative to the current working directory of the indexer.
- Disable related-uids compression

Suggested-by: ArthurW <arthur@tarides.com>
Co-authored-by: Lucccyo <cha.git@mailo.fr>
Co-authored-by: Tim ats <tim.arnouts@protonmail.com>
Co-authored-by: ArthurW <arthur@tarides.com>
Before we could rely on deduplicate to flag links worth caching, but now we have both the LRU which could trigger multiple reads to the same loc and the smalls pointing to parents (which are potentially not marked as Serialized_reused)

Suggested-by: ArthurW <arthur@tarides.com>
@voodoos

voodoos commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator Author

Thank you for the thorough review @art-w. Merging now.

@voodoos
voodoos merged commit 69de75b into ocaml:main Jul 16, 2026
7 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants