Skip to content
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

Fix analysis time regression with Bzlmod lockfile #19970

Closed
wants to merge 1 commit into from

Conversation

fmeum
Copy link
Collaborator

@fmeum fmeum commented Oct 29, 2023

This commit fixes a regression in analysis time caused by 19c0c80. Since BazelModuleResolutionEvent and ModuleExtensionResolutionEvent are both marked as storeForReplay, they are stored in a nested set for essentially all analysis phase nodes. This resulted in frequent (i.e., per target) calls to their hashCode methods, which are not cached and delegated to the likewise uncached methods on large ImmutableMap and ImmutableTable instances.

Since there is no need for these events to be deduplicated, switching to reference equality resolves this issue.

The following analysis phase measurements for a synthetic project with a single "hub and spokes" module extension and 2,000 repos illustrate the effect:

  • without lockfile: 4.3s
  • with lockfile before this commit: 8.3s
  • with lockfile after this commit: 4.2s

Fixes #19952

@fmeum fmeum requested a review from SalmaSamy October 29, 2023 10:18
@fmeum fmeum marked this pull request as ready for review October 29, 2023 10:18
@fmeum fmeum removed the request for review from meteorcloudy October 29, 2023 10:18
@github-actions github-actions bot added awaiting-review PR is awaiting review from an assigned reviewer team-ExternalDeps External dependency handling, remote repositiories, WORKSPACE file. labels Oct 29, 2023
Copy link
Member

@meteorcloudy meteorcloudy left a comment

Choose a reason for hiding this comment

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

Thank you!!

@meteorcloudy meteorcloudy added awaiting-PR-merge PR has been approved by a reviewer and is ready to be merge internally and removed awaiting-review PR is awaiting review from an assigned reviewer labels Oct 30, 2023
@meteorcloudy
Copy link
Member

@bazel-io fork 7.0.0

@Wyverald
Copy link
Member

Wyverald commented Oct 30, 2023

Could we achieve the same result by adding @Memoized to hashCode()? That way we won't lose the benefit of using AutoValue.

Also, that would give us a convenient place to document why this is needed at all. This is important because I could certainly imagine myself later looking at this class, thinking "huh, this is a prime candidate to turn into an AutoValue", and reintroducing the performance regression.

@fmeum
Copy link
Collaborator Author

fmeum commented Oct 30, 2023

Could we achieve the same result by adding @Memoized to hashCode()? That way we won't lose the benefit of using AutoValue.

That's what I tried first, but ultimately decided against:

  • All the other event classes I could find in Bazel also use reference equality, so using value-based equality here would actually decrease consistency.
  • The Bzlmod events are fired in a way that already prevents duplicates, so the computation and storage of hash codes would still be wasted. equals could still be called and may turn out to be expensive depending on field order.
  • Without hashCode and equals, AutoValue really only gets rid of the field declarations, which are much less error-prone to maintain manually.

@fmeum
Copy link
Collaborator Author

fmeum commented Oct 30, 2023

Just saw your edit, I will add comments and think about ways to add test coverage (seems difficult though).

I could see adding comments to the classes be as effective as adding them to the @Memoized annotations as the class as where one would place @AutoValue. What do you think?

@Wyverald
Copy link
Member

I'm okay with skipping AutoValue altogether if we document it at the class level. I wouldn't worry about regression testing in this case.

Alternatively, we could also just force-override hashCode and equals to be their trivial version (using super.hashCode() and super.equals(other)).

I have a slight preference for keeping the AutoValue idiom (using X.create() etc), but it's not strong opinion at all.

This commit fixes a regression in analysis time caused by 19c0c80.
Since `BazelModuleResolutionEvent` and `ModuleExtensionResolutionEvent`
are both marked as `storeForReplay`, they are stored in a nested set for
essentially all analysis phase nodes. This resulted in frequent (i.e.,
per target) calls to their `hashCode` methods, which are not cached and
delegated to the likewise uncached methods on large `ImmutableMap` and
`ImmutableTable` instances.

Since there is no need for these events to be deduplicated, switching to
reference equality resolves this issue.

The following analysis phase measurements for a synthetic project with
a single "hub and spokes" module extension and 2,000 repos illustrate
the effect:

* without lockfile: 4.3s
* with lockfile before this commit: 8.3s
* with lockfile after this commit: 4.2s
@fmeum
Copy link
Collaborator Author

fmeum commented Oct 31, 2023

I added class comments as well as the static create methods. Is that what you meant with your last paragraph?

@copybara-service copybara-service bot closed this in 435d1c2 Nov 2, 2023
@github-actions github-actions bot removed the awaiting-PR-merge PR has been approved by a reviewer and is ready to be merge internally label Nov 2, 2023
bazel-io pushed a commit to bazel-io/bazel that referenced this pull request Nov 2, 2023
This commit fixes a regression in analysis time caused by 19c0c80. Since `BazelModuleResolutionEvent` and `ModuleExtensionResolutionEvent` are both marked as `storeForReplay`, they are stored in a nested set for essentially all analysis phase nodes. This resulted in frequent (i.e., per target) calls to their `hashCode` methods, which are not cached and delegated to the likewise uncached methods on large `ImmutableMap` and `ImmutableTable` instances.

Since there is no need for these events to be deduplicated, switching to reference equality resolves this issue.

The following analysis phase measurements for a synthetic project with a single "hub and spokes" module extension and 2,000 repos illustrate the effect:

* without lockfile: 4.3s
* with lockfile before this commit: 8.3s
* with lockfile after this commit: 4.2s

Fixes bazelbuild#19952

Closes bazelbuild#19970.

PiperOrigin-RevId: 578734010
Change-Id: I870867c5c509389632793b0d5fe5c43ddc6176f3
bazel-io pushed a commit to bazel-io/bazel that referenced this pull request Nov 2, 2023
This commit fixes a regression in analysis time caused by 19c0c80. Since `BazelModuleResolutionEvent` and `ModuleExtensionResolutionEvent` are both marked as `storeForReplay`, they are stored in a nested set for essentially all analysis phase nodes. This resulted in frequent (i.e., per target) calls to their `hashCode` methods, which are not cached and delegated to the likewise uncached methods on large `ImmutableMap` and `ImmutableTable` instances.

Since there is no need for these events to be deduplicated, switching to reference equality resolves this issue.

The following analysis phase measurements for a synthetic project with a single "hub and spokes" module extension and 2,000 repos illustrate the effect:

* without lockfile: 4.3s
* with lockfile before this commit: 8.3s
* with lockfile after this commit: 4.2s

Fixes bazelbuild#19952

Closes bazelbuild#19970.

PiperOrigin-RevId: 578734010
Change-Id: I870867c5c509389632793b0d5fe5c43ddc6176f3
@fmeum fmeum deleted the 19952-fix-regression branch November 2, 2023 08:03
iancha1992 pushed a commit that referenced this pull request Nov 2, 2023
This commit fixes a regression in analysis time caused by
19c0c80. Since
`BazelModuleResolutionEvent` and `ModuleExtensionResolutionEvent` are
both marked as `storeForReplay`, they are stored in a nested set for
essentially all analysis phase nodes. This resulted in frequent (i.e.,
per target) calls to their `hashCode` methods, which are not cached and
delegated to the likewise uncached methods on large `ImmutableMap` and
`ImmutableTable` instances.

Since there is no need for these events to be deduplicated, switching to
reference equality resolves this issue.

The following analysis phase measurements for a synthetic project with a
single "hub and spokes" module extension and 2,000 repos illustrate the
effect:

* without lockfile: 4.3s
* with lockfile before this commit: 8.3s
* with lockfile after this commit: 4.2s

Fixes #19952

Closes #19970.

Commit
435d1c2

PiperOrigin-RevId: 578734010
Change-Id: I870867c5c509389632793b0d5fe5c43ddc6176f3

Co-authored-by: Fabian Meumertzheim <fabian@meumertzhe.im>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
team-ExternalDeps External dependency handling, remote repositiories, WORKSPACE file.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Analysis performance regression when lockfile is enabled
3 participants