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

C++ recompilation is too sensitive to preprocessor-only changes #18246

Open
jmmv opened this issue Apr 27, 2023 · 5 comments
Open

C++ recompilation is too sensitive to preprocessor-only changes #18246

jmmv opened this issue Apr 27, 2023 · 5 comments
Labels
help wanted Someone outside the Bazel team could own this P3 We're not considering working on this, but happy to review a PR. (No assignee) team-Rules-CPP Issues for C++ rules type: feature request

Comments

@jmmv
Copy link
Contributor

jmmv commented Apr 27, 2023

Description of the feature request:

Whenever there is a preprocessor-only change to the build graph (say a new -I flag added to the compiles), Bazel will invalidate all C++ actions and recompile all of them. This can be very costly.

ccache has logic to mitigate this problem by splitting a single C++ compile into its preprocessing step and its compilation step. These two steps are then cached separately, so that if the preprocessing of a file does not result in a different output, the recompilation is skipped. For an individual action, the performance impact may be small, but the key here is that this can have cascading effects on the build graph.

It'd be nice if the C++ rules in Bazel offered similar functionality and ran the preprocessor and the compiler as separately-cacheable actions. The C++ compiler is going to run the two stages separately anyway, so Bazel could do it too.

As a workaround, it's possible to use ccache in combination with Bazel to obtain the desired behavior, but this sounds like a hack. It'd be great to evaluate whether Bazel wants to do this on its own or not, and if not, recommend how to set up ccache in a way that works well with dynamic execution.

What underlying problem are you trying to solve with this feature?

Faster build performance when there are innocuous changes deep in the dependency chain that have minimal impact on the majority of the build graph. This is common when doing a git rebase for example.

Which operating system are you running Bazel on?

N/A

What is the output of bazel info release?

bazel-6.1.1

If bazel info release returns development version or (@non-git), tell us how you built Bazel.

No response

What's the output of git remote get-url origin; git rev-parse master; git rev-parse HEAD ?

No response

Have you found anything relevant by searching the web?

No response

Any other information, logs, or outputs that you want to share?

No response

@oquenchil
Copy link
Contributor

Hello Julio,

This sounds like a heavy change to the current model. If the community wants to explore this and produce experiments changing the current code we'd be happy to evaluate it.

(say a new -I flag added to the compiles),

Added where? Would a local_includes attribute in cc_library solve the problem? local_* meaning that it wouldn't get propagated downstream and cause re-compiles in the reverse transitive deps.

@oquenchil oquenchil added P3 We're not considering working on this, but happy to review a PR. (No assignee) help wanted Someone outside the Bazel team could own this and removed untriaged labels May 5, 2023
@sfc-gh-sgiesecke
Copy link

Would a local_includes attribute in cc_library solve the problem?

No, we're talking about include paths that need to be propagated because some headers of a library use it, but not all of them. (It can be argued that such a library should be split up, which indeed would ideally be done, but it's not done easily and that's not something the build system should impose).

The description above mentions ccache. However, we found that with CMake and its Ninja generator, the ccache compiler wrapper isn't even invoked if the include paths for a translation unit change and the translation unit is known not to be affected by the include path change. That's obviously even more performant than having ccache run the preprocessor again to find that its outputs didn't change.

There may be other compiler options that are known not to affect the output files (e.g. -fdiagnostics-color).

@jmmv
Copy link
Contributor Author

jmmv commented Jul 14, 2023

-fdiagnostics-color is an interesting one because even though this flag does not change the compiler outputs, it changes the content of the console output. And the console output is just one more output of an action that influences caching (if the compiler prints warnings, rerunning the same action has to output the same warnings from the cache), so changing this option does require rerunning actions.

@fmeum
Copy link
Collaborator

fmeum commented Jul 14, 2023

@sfc-gh-sgiesecke Could you share examples of CMake builds that are able to avoid this situation? I would be interested in understanding better how it is able to do this.

@sfc-gh-sgiesecke
Copy link

@sfc-gh-sgiesecke Could you share examples of CMake builds that are able to avoid this situation? I would be interested in understanding better how it is able to do this.

Well, that's the default behaviour when you use CMake's Ninja generator. I can try to sketch something that demonstrates this behaviour. We have an internal build benchmark setup that does this, I need to figure out if I can share that (and it needs to be generalized so that it works on some minimal project).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Someone outside the Bazel team could own this P3 We're not considering working on this, but happy to review a PR. (No assignee) team-Rules-CPP Issues for C++ rules type: feature request
Projects
None yet
Development

No branches or pull requests

6 participants