Skip to content

Commit d9169ab

Browse files
Wyveraldcopybara-github
authored andcommitted
Attempt to make main repo mapping inverse more efficient
During `bazel query`, `Label#getDisplayForm(mainRepoMapping)` might be called many many times. We can optimize for that case without sacrificing too much memory by computing a reverse mapping for the main repo mapping only. Fixes #20613. Closes #20617. PiperOrigin-RevId: 592607904 Change-Id: Iaaa709a51fe39556f03408080c1fe5e73689b761
1 parent 405d1a3 commit d9169ab

3 files changed

Lines changed: 80 additions & 22 deletions

File tree

src/main/java/com/google/devtools/build/lib/cmdline/RepositoryMapping.java

Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,53 +14,77 @@
1414

1515
package com.google.devtools.build.lib.cmdline;
1616

17-
import com.google.auto.value.AutoValue;
17+
import com.google.common.base.Objects;
1818
import com.google.common.base.Preconditions;
1919
import com.google.common.collect.ImmutableMap;
20+
import com.google.common.collect.Maps;
2021
import java.util.HashMap;
2122
import java.util.Map;
2223
import java.util.Map.Entry;
2324
import java.util.Optional;
2425
import javax.annotation.Nullable;
2526

2627
/**
27-
* A class to distinguish repository mappings for repos from WORKSPACE and Bzlmod.
28+
* Stores the mapping from apparent repo name to canonical repo name, from the viewpoint of an
29+
* "owner repo".
2830
*
2931
* <p>For repositories from the WORKSPACE file, if the requested repo doesn't exist in the mapping,
3032
* we fallback to the requested name. For repositories from Bzlmod, we return null to let the caller
31-
* decide what to do. This class won't be needed if one day we don't define external repositories in
32-
* the WORKSPACE file since {@code fallback} would always be false.
33+
* decide what to do.
3334
*/
34-
@AutoValue
35-
public abstract class RepositoryMapping {
36-
37-
// Always fallback to the requested name
35+
public class RepositoryMapping {
36+
/* A repo mapping that always falls back to using the apparent name as the canonical name. */
3837
public static final RepositoryMapping ALWAYS_FALLBACK = createAllowingFallback(ImmutableMap.of());
3938

39+
private final ImmutableMap<String, RepositoryName> entries;
40+
@Nullable private final RepositoryName ownerRepo;
41+
42+
private RepositoryMapping(
43+
Map<String, RepositoryName> entries, @Nullable RepositoryName ownerRepo) {
44+
this.entries = ImmutableMap.copyOf(entries);
45+
this.ownerRepo = ownerRepo;
46+
}
47+
4048
/** Returns all the entries in this repo mapping. */
41-
public abstract ImmutableMap<String, RepositoryName> entries();
49+
public final ImmutableMap<String, RepositoryName> entries() {
50+
return entries;
51+
}
4252

4353
/**
4454
* The owner repo of this repository mapping. It is for providing useful debug information when
4555
* repository mapping fails due to enforcing strict dependency, therefore it's only recorded when
46-
* we don't fallback to the requested repo name.
56+
* we don't fall back to the requested repo name.
4757
*/
4858
@Nullable
49-
abstract RepositoryName ownerRepo();
59+
public final RepositoryName ownerRepo() {
60+
return ownerRepo;
61+
}
62+
63+
@Override
64+
public boolean equals(Object o) {
65+
if (this == o) {
66+
return true;
67+
}
68+
if (!(o instanceof RepositoryMapping)) {
69+
return false;
70+
}
71+
RepositoryMapping that = (RepositoryMapping) o;
72+
return Objects.equal(entries, that.entries) && Objects.equal(ownerRepo, that.ownerRepo);
73+
}
74+
75+
@Override
76+
public int hashCode() {
77+
return Objects.hashCode(entries, ownerRepo);
78+
}
5079

5180
public static RepositoryMapping create(
5281
Map<String, RepositoryName> entries, RepositoryName ownerRepo) {
53-
return createInternal(
82+
return new RepositoryMapping(
5483
Preconditions.checkNotNull(entries), Preconditions.checkNotNull(ownerRepo));
5584
}
5685

5786
public static RepositoryMapping createAllowingFallback(Map<String, RepositoryName> entries) {
58-
return createInternal(Preconditions.checkNotNull(entries), null);
59-
}
60-
61-
private static RepositoryMapping createInternal(
62-
Map<String, RepositoryName> entries, RepositoryName ownerRepo) {
63-
return new AutoValue_RepositoryMapping(ImmutableMap.copyOf(entries), ownerRepo);
87+
return new RepositoryMapping(Preconditions.checkNotNull(entries), null);
6488
}
6589

6690
/**
@@ -70,7 +94,7 @@ private static RepositoryMapping createInternal(
7094
public RepositoryMapping withAdditionalMappings(Map<String, RepositoryName> additionalMappings) {
7195
HashMap<String, RepositoryName> allMappings = new HashMap<>(additionalMappings);
7296
allMappings.putAll(entries());
73-
return createInternal(allMappings, ownerRepo());
97+
return new RepositoryMapping(allMappings, ownerRepo());
7498
}
7599

76100
/**
@@ -134,6 +158,26 @@ public RepositoryMapping composeWith(RepositoryMapping other) {
134158
RepositoryName mappedName = other.get(entry.getValue().getName());
135159
entries.put(entry.getKey(), mappedName.isVisible() ? mappedName : entry.getValue());
136160
}
137-
return createInternal(entries, null);
161+
return new RepositoryMapping(entries, null);
162+
}
163+
164+
/**
165+
* Returns a new {@link RepositoryMapping} instance with identical contents, except that the
166+
* inverse mapping is cached, causing {@link #getInverse} to be much more efficient. This is
167+
* particularly important for the main repo mapping, as it's often used to generate display-form
168+
* labels ({@link Label#getDisplayForm}).
169+
*/
170+
public RepositoryMapping withCachedInverseMap() {
171+
var inverse = Maps.<RepositoryName, String>newHashMapWithExpectedSize(entries.size());
172+
for (Map.Entry<String, RepositoryName> entry : entries.entrySet()) {
173+
inverse.putIfAbsent(entry.getValue(), entry.getKey());
174+
}
175+
var inverseCopy = ImmutableMap.copyOf(inverse);
176+
return new RepositoryMapping(entries, ownerRepo) {
177+
@Override
178+
public Optional<String> getInverse(RepositoryName postMappingName) {
179+
return Optional.ofNullable(inverseCopy.get(postMappingName));
180+
}
181+
};
138182
}
139183
}

src/main/java/com/google/devtools/build/lib/skyframe/RepositoryMappingFunction.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,17 @@ public SkyValue compute(SkyKey skyKey, Environment env)
128128
.withAdditionalMappings(
129129
ImmutableMap.of(
130130
externalPackageValue.getPackage().getWorkspaceName(), RepositoryName.MAIN))
131-
.withAdditionalMappings(additionalMappings);
131+
.withAdditionalMappings(additionalMappings)
132+
.withCachedInverseMap();
132133
}
133134

134135
// Try and see if this is a repo generated from a Bazel module.
135136
Optional<RepositoryMappingValue> mappingValue =
136137
computeForBazelModuleRepo(repositoryName, bazelDepGraphValue);
137138
if (mappingValue.isPresent()) {
138-
return mappingValue.get();
139+
return repositoryName.isMain()
140+
? mappingValue.get().withCachedInverseMap()
141+
: mappingValue.get();
139142
}
140143

141144
// Now try and see if this is a repo generated from a module extension.

src/main/java/com/google/devtools/build/lib/skyframe/RepositoryMappingValue.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,17 @@ public final RepositoryMappingValue withAdditionalMappings(Map<String, Repositor
107107
getAssociatedModuleVersion());
108108
}
109109

110+
/**
111+
* Replaces the inner {@link #getRepositoryMapping() repository mapping} with one returned by
112+
* calling its {@link RepositoryMapping#withCachedInverseMap} method.
113+
*/
114+
public final RepositoryMappingValue withCachedInverseMap() {
115+
return new AutoValue_RepositoryMappingValue(
116+
getRepositoryMapping().withCachedInverseMap(),
117+
getAssociatedModuleName(),
118+
getAssociatedModuleVersion());
119+
}
120+
110121
/** Returns the {@link Key} for {@link RepositoryMappingValue}s. */
111122
public static Key key(RepositoryName repositoryName) {
112123
return RepositoryMappingValue.Key.create(

0 commit comments

Comments
 (0)