Skip to content

Don't allocate a VisitorState when a memoized lookup hits the cache - #6082

Merged
copybara-service[bot] merged 1 commit into
masterfrom
test_973864464
Aug 31, 2026
Merged

Don't allocate a VisitorState when a memoized lookup hits the cache#6082
copybara-service[bot] merged 1 commit into
masterfrom
test_973864464

Conversation

@copybara-service

Copy link
Copy Markdown
Contributor

Don't allocate a VisitorState when a memoized lookup hits the cache

Why

VisitorState.Cache.get replaced the caller's state with a pathless copy before it looked in the cache:

public synchronized T get(VisitorState state) {
  state = state.withNoPathForMemoization();

  T value = cache.get();
  if (value == null) {
    value = impl.get(state);
    ...

The copy keeps a memoized supplier from reading a TreePath that belongs to one compilation unit while its result is cached for the whole compilation, so impl.get is the only caller that needs it. The cache answers most reads without calling impl, and those reads allocated a VisitorState and dropped it.

The scanner reaches memoized suppliers through TypePredicates, Suppliers.typeFromString, Matchers, and MethodMatchers, once per matcher per AST node, so the reads are frequent: compiling the 948 sources of error_prone_core with Error Prone enabled runs Cache.get 25448504 times and answers 17019874 of those from the cache.

What

compute now makes the pathless copy, and it runs only when the value has to be computed. withNoPathForMemoization returns this when the path is already null, so a memoized supplier that reads another memoized value allocates nothing either.

impl still receives a state whose getPath() throws, and the provenance bookkeeping reads sharedState, which the copy shares with the original.

How to verify

mvn -pl check_api,core test

Allocation was measured with an in-process javac compiling the 948 sources of error_prone_core with Error Prone enabled on JDK 24, reading ThreadMXBean.getThreadAllocatedBytes for the compiling thread. Steady state over six compilations in one JVM: 8.251 GiB before, 7.744 GiB after. The 17019874 cached reads at 32 bytes per VisitorState come to 0.507 GiB, which is the whole difference.

Fixes #6078

FUTURE_COPYBARA_INTEGRATE_REVIEW=#6078 from vlsi:vs/memoize-no-alloc-on-hit e91d4c0

## Why

`VisitorState.Cache.get` replaced the caller's state with a pathless copy before it looked in the cache:

```java
public synchronized T get(VisitorState state) {
  state = state.withNoPathForMemoization();

  T value = cache.get();
  if (value == null) {
    value = impl.get(state);
    ...
```

The copy keeps a memoized supplier from reading a `TreePath` that belongs to one compilation unit while its result is cached for the whole compilation, so `impl.get` is the only caller that needs it. The cache answers most reads without calling `impl`, and those reads allocated a `VisitorState` and dropped it.

The scanner reaches memoized suppliers through `TypePredicates`, `Suppliers.typeFromString`, `Matchers`, and `MethodMatchers`, once per matcher per AST node, so the reads are frequent: compiling the 948 sources of `error_prone_core` with Error Prone enabled runs `Cache.get` 25448504 times and answers 17019874 of those from the cache.

## What

`compute` now makes the pathless copy, and it runs only when the value has to be computed. `withNoPathForMemoization` returns `this` when the path is already null, so a memoized supplier that reads another memoized value allocates nothing either.

`impl` still receives a state whose `getPath()` throws, and the `provenance` bookkeeping reads `sharedState`, which the copy shares with the original.

## How to verify

```bash
mvn -pl check_api,core test
```

Allocation was measured with an in-process `javac` compiling the 948 sources of `error_prone_core` with Error Prone enabled on JDK 24, reading `ThreadMXBean.getThreadAllocatedBytes` for the compiling thread. Steady state over six compilations in one JVM: 8.251 GiB before, 7.744 GiB after. The 17019874 cached reads at 32 bytes per `VisitorState` come to 0.507 GiB, which is the whole difference.

Fixes #6078

COPYBARA_INTEGRATE_REVIEW=#6078 from vlsi:vs/memoize-no-alloc-on-hit e91d4c0
PiperOrigin-RevId: 973878054
@copybara-service
copybara-service Bot merged commit 9fefe41 into master Aug 31, 2026
1 check passed
@copybara-service
copybara-service Bot deleted the test_973864464 branch August 31, 2026 14:49
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.

1 participant