-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Remove import-path guessing from ProtoCompileActionBuilder #10939
Conversation
11087ad
to
2a94760
Compare
I seem to remember that my concern with this exact change was RAM use (that's why we use the otherwise-not-very-reasonable "null as import path" pattern). Let me run the numbers. |
src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCompileActionBuilder.java
Show resolved
Hide resolved
Let me actually change this patch to always use null if |
How would using null if |
Let's go a step back and see what we want to achieve here. The goal of this is to get the import paths of all protos without guessing it. I see two possible options here:
The first option would be the cleanest, but it seems like that's unfeasible because of memory consumption. [1] possibly implemented as |
Yeah, that |
3a14067
to
cfbd0c6
Compare
Instead of trying to guess the correct import-path of a proto file, we save their associated source root in ProtoInfo and compute the import path by taking the proto files's exec path relative to that source root.
cfbd0c6
to
7ab5bd3
Compare
Modified the change to use |
Good job! I ran our internal benchmark and this results in net improvement of memory use: 0.1%. This significantly exceeds my expectations of any such change having an unacceptable amount of memory overhead. Let me review the pull request in more detail. |
private final String outputDirectory; | ||
private final NestedSet<String> directProtoSourceRoots; | ||
private final boolean siblingRepositoryLayout; | ||
static final class ExpandProtosUnderSingleSourceRootToImportPathsArgsFn |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this unused now (or I just can't find the references to this?)
protoInfo.getStrictImportableProtoSourceRoots(), | ||
protoInfo.getTransitiveProtoSources(), | ||
siblingRepositoryLayout); | ||
areDepsStrict ? protoInfo.getImportableProtos() : null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mind removing the siblingRepositoryLayout
variable from line 266? (it's unused now as far as I can tell)
private final Artifact directDescriptorSet; | ||
private final NestedSet<Artifact> transitiveDescriptorSets; | ||
private final NestedSet<Pair<PathFragment, ImmutableList<Artifact>>> importableProtos; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: add back the strict
prefix, i.e. call this strictImportableProtos
@@ -682,72 +617,52 @@ private static String guessProtoPathUnderRoot( | |||
} | |||
}; | |||
|
|||
private static String computeImportPath(PathFragment protoSourceRoot, Artifact proto) { | |||
PathFragment importPath = proto.getExecPath().relativeTo(protoSourceRoot); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: why not inline the importPath
variable?
|
||
@AutoCodec | ||
@AutoCodec.VisibleForSerialization | ||
static final class ExpandToImportPathsArgsFn |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
opt: you can make these lambdas instead of inner classes it you want
NestedSet<Pair<PathFragment, ImmutableList<Artifact>>> exportedProtos) { | ||
NestedSetBuilder<Pair<PathFragment, ImmutableList<Artifact>>> protos = | ||
NestedSetBuilder.stableOrder(); | ||
protos.addTransitive(exportedProtos); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is inconsistent with how transitiveProtoSources
(the NestedSet<Artifact>
) is computed: that is only local sources + deps and this one is local sources + deps + exports. Make this consistent (or else explain in a comment why the discrepancy exists)
this.sourceRoot = sourceRoot; | ||
this.importPathSourcePair = importPathSourcePair; | ||
} | ||
|
||
public ImmutableList<Artifact> getSources() { | ||
return sources; | ||
return ImmutableList.copyOf(Iterables.transform(importPathSourcePair, p -> p.first)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit, opt: you can replace p -> p.first
with Pair::getFirst
(I'm not sure if it's an improvement)
for (ProtoInfo info : ruleContext.getPrerequisites("exports", TARGET, ProtoInfo.PROVIDER)) { | ||
protos.addTransitive(info.getExportedProtos()); | ||
} | ||
if (library.getSources().isEmpty()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears that computeExportedProtos
at HEAD does not special-case rules without sources and this one does. Why is that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually it does, but it's not so obvious:
https://source.bazel.build/bazel/+/master:src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCommon.java;l=67?q=ProtoCommon
(Tested here: https://source.bazel.build/bazel/+/master:src/test/java/com/google/devtools/build/lib/rules/proto/BazelProtoLibraryTest.java;l=53?q=BazelProtoLibraryTest)
I see a bunch of failures in our internal test battery (unfortunately, I am too pressed for time to investigate myself). There are two kinds:
My guess is that the latter is related to buggy import part computation (I can fix it when I import it if you have no idea) and the former is related to the export set computation you changed (I'll take a look, but for now, I'd like to know why you changed it in the first place) I was somewhat surprised, but then I realized that we don't have external tests for exports, so no surprise you broke it with this change :( Once I know why you changed the logic, I'll take a look myself. |
Argh, turns out I misunderstood For the second kind of failure you're seeing, I see two possible explanations:
|
I have a test case for the second failure (add this to
This is because if you have both source and generated The trick is to fix this in such a way that one doesn't need to create virtual proto source roots for every I think what would work is to have multiple |
Re, the first, it appears to be the case. The simplest test case appears to be to test whether Although you should probably not change any behavior in this change, I couldn't resist and tried to grok how the It appears that the proto compile action has the strict importable protos of the rules in its
And the strict importable protos for dependents are:
So it appears that sources of |
Is [1] #9215 |
Yes, that seems to be what those methods do. I'll refrain from updating the PR for this until we've figured out the other issue. |
Yep, I agree on both counts :) What's the "other issue"? The I'll run the benchmark as soon as you update this change. What's the plan? As long as you don't create virtual proto source roots for every |
Yes, the Can you check what's the status-quo on creating virtual source roots for generated protos? Unless I'm missing something really obvious here, Bazel already creates virtual source roots for all targets that have at least one generated source. We're still putting the original source in the |
What you are missing is the very sad existence of the The second, more hairy one is that of implicit outputs: if you write @c-mita is working on removing this (in fact, I was hoping he'd be done with it last week), and then we can hopefully make Blaze and Bazel consistent. |
...and as to the fix, you can either wait until @c-mita is done, although that will take at least a week until the submits the change and it percolates through our release process, but more like two, or use the "two WDYT? |
@c-mita is done with the removal of implicit outputs! (well, he submitted the change a week ago, but we had to wait for our internal release to make sure that it doesn't break anything). Your path is free! |
Found another neat solution: Instead of using |
@lberki ptal |
e0e9fec
to
c47521c
Compare
pinging this again |
@lberki Can we merge this? |
(rebased to fix conflicts) |
Thanks for rebasing! I wanted to look at it today, but it's not a trivial change so I'll need to find a chunk of time in which to do it :( I do want to merge this, though, if at all possible. |
Update: I started reviewing this change, had to resolve conflicts when importing it, then found a bug in javac (!) while fixing an internal test case. Let's see if I will have enough time and brain to continue the review today. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also ran some memory benchmarks and there is even a slight improvement!
I'll need to take a closer look, but if you don't mind, I'll wait until you shuffe ProtoInfo back so that it's easier to see what's going on.
src/main/java/com/google/devtools/build/lib/rules/proto/ProtoInfo.java
Outdated
Show resolved
Hide resolved
protoInfo.getExportedProtoSourcesImportPaths(); | ||
if (protosInExports.isEmpty()) { | ||
NestedSet<ProtoSource> publicImportSources = protoInfo.getPublicImportSources(); | ||
if (publicImportSources.isEmpty()) { | ||
// This line is necessary to trigger the check. | ||
result.add("--allowed_public_imports="); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A test case in our internal test battery fails. I can't export it at the moment, but the BUILD file it uses is as follows:
proto_library(
name = 'myProto',
srcs = ['myProto.proto'],
)
then verifies that its proto compile action has --allowed_public_imports=
. This fails, and instead, it has --allowed_public_imports test/myProto.proto
. I think I know why (see the other comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I think you're right about the culprit of the failure which would allow public imports of srcs
while they aren't allowed with the current behavior.
I had to update BazelProtoLibraryTest#testExportedStrippedImportPrefixes
though because it checked that the direct proto source root was part of the exported source roots (but we don't rely on a set of proto source roots anymore for computing public imports, so it's not a behavioral change). You may have to do the same for internal tests :(.
src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCommon.java
Outdated
Show resolved
Hide resolved
src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCompileActionBuilder.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the delay, took me some time to get this stuff back into memory. Tried my best to shuffle everything back. PTAL
I also ran some memory benchmarks and there is even a slight improvement!
I'm curious how much of an improvement this is. Any chance you can share the numbers?
protoInfo.getExportedProtoSourcesImportPaths(); | ||
if (protosInExports.isEmpty()) { | ||
NestedSet<ProtoSource> publicImportSources = protoInfo.getPublicImportSources(); | ||
if (publicImportSources.isEmpty()) { | ||
// This line is necessary to trigger the check. | ||
result.add("--allowed_public_imports="); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I think you're right about the culprit of the failure which would allow public imports of srcs
while they aren't allowed with the current behavior.
I had to update BazelProtoLibraryTest#testExportedStrippedImportPrefixes
though because it checked that the direct proto source root was part of the exported source roots (but we don't rely on a set of proto source roots anymore for computing public imports, so it's not a behavioral change). You may have to do the same for internal tests :(.
src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCompileActionBuilder.java
Outdated
Show resolved
Hide resolved
It was a ~0.1% improvement, if my memory serves well, so ntohing very significant, but it was still a pleasant surprise. I tried to import this, then WDYT? |
Sure, merged them into |
Wow, eight months already! I fixed some linter errors and I could nitpick a bit more, but instead let's submit this and see if it sticks. It should, but it's an intricate enough problem that I'm not sure. |
Submitted. Let's see whether this stands the test of our internal source tree! (we'll know tomorrow evening with reasonable certainty in Europe if all goes well, if not, a day or two after that) Thanks for your work here, I really appreciate this :) |
The information is already part of another field. Cleanup after bazelbuild#12431 and bazelbuild#10939
The information is already part of another field. Cleanup after bazelbuild#12431 and bazelbuild#10939
Instead of trying to guess the correct source root and import-path of a proto file,
we save the real source root together with the .proto source when creating the
ProtoInfo provider and re-use that information for codegen actions.