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

Profile gn gen and no-op engine builds. #81074

Open
chinmaygarde opened this issue Apr 23, 2021 · 11 comments
Open

Profile gn gen and no-op engine builds. #81074

chinmaygarde opened this issue Apr 23, 2021 · 11 comments
Labels
c: tech-debt Technical debt, code quality, testing, etc. engine flutter/engine repository. See also e: labels. P2 Important issues not at the top of the work list team-engine Owned by Engine team triaged-engine Triaged by Engine team

Comments

@chinmaygarde
Copy link
Member

chinmaygarde commented Apr 23, 2021

This based on an observation that no-op engine builds during development were getting steadily slower over time.

Usually, even for no-op builds, Engine developers run gn gen (invoked by ./flutter/tools/gn) followed by the ninja invocation to generate build artifacts. Just running ninja is sufficient to pick up changes to GN rules themselves but this does not pick up changes to GN args. So, most just run the two invocations in order. This workflow has been getting alarmingly slow over time.

On my eng Macbook Pro, gn gen takes ~4s today. It used to take less than 300ms earlier (say 3 years ago).

$ time ./flutter/tools/gn --runtime-mode debug
Generating GN files in: out/host_debug
Generating Xcode projects took 241ms
Done. Made 739 targets from 228 files in 1608ms
./flutter/tools/gn --runtime-mode debug  3.98s user 3.40s system 263% cpu 2.799 total

Just running ninja on the other hand seems to take 300ms which seems fine to me.

$ time ninja -C out/host_debug_unopt -j999
ninja: Entering directory `out/host_debug_unopt'
ninja: no work to do.
ninja -C out/host_debug_unopt -j999  0.29s user 0.12s system 98% cpu 0.425 total

Taking >4s for a no-op builds breaks flow. I used to be able to build on save and I can't do that anymore. Also, given the trend, this is probably going to get worse without some effort to arrest the increase. I can't tell how much of the increase is from additional targets, additional use of scripts in GN, or, just the effects of running GN on my laptop in the WFH environment vs. using the beasts in the office.

GN and Ninja have built in tools to profile the various steps and we should track what the most expensive steps are. Typically, the guidance has been to avoid Python or Dart scripts referenced in GN rules as they are typically the bottleneck. To avoid having these scripts, newer versions of GN have built-in functions that we could probably use. I am positive there are tons of low hanging fruit that will significantly improve productivity of engine developers. Separately, we don't have (or adhere to) documented best practices for authoring GN rules. Perhaps now is the time to set that up. Also, perhaps there is a way to benchmark how long no-op builds take so we can figure out trends.

@chinmaygarde chinmaygarde added engine flutter/engine repository. See also e: labels. c: tech-debt Technical debt, code quality, testing, etc. labels Apr 23, 2021
@chinmaygarde
Copy link
Member Author

Ok, that was easier than I expected. As anticipated, it was script execution. A couple of the scripts, we can get rid of and use GN built-ins. There are some that can be parallelized. Generating Xcode projects can also probably be opt-in. Adding --tracelog=<file> in gn gen generates the following trace for just that one step.

Screen Shot 2021-04-23 at 1 19 09 PM

@chinmaygarde
Copy link
Member Author

chinmaygarde commented Apr 23, 2021

The ios_sdk.py and mac_sdk.gni bottlenecks can be tackled by being smarter about when and how many times we generate symlinks for GOMA (cc @dnfield).

The bottlenecks then become script executions in Dart. Specifically, sdk_args.gni is in an import so it get executed multiple times and is slow. Perhaps that can be put in an arg. list_dart_files.py also seems to be slow and performing tasks redundantly. Is it possible to list the sources directly in GN rules like we do for all other targets? (cc @a-siva @zanderso).

Then there a are a couple scripts in Dart as well as engine that attempt to read Git revisions and such for versioning. This can also be done once upfront and stashed away in a GN variable.

Once these are tackled, I believe everything should complete in well under a second.

chinmaygarde added a commit to chinmaygarde/flutter_engine that referenced this issue Apr 23, 2021
It used to be that GN could not generate compile commands on its own. Instead,
we would use Ninja to do the same in a separate invocation after GN. This worked
but the invocation took about a second to complete. Now that GN can generate the
compile commands as it is generating the Ninja build rules, we ask it generate
the compile commands directly. The commands seem identical and IDE integrations
continue to work.

One difference now is that the compile commands are now generated in the build
directory instead of the out directory like earlier. I think this is easier to
reason about but if others think we need the old behavior, I can add a symlink
to the compile_commands.json.

This knocks 0.9 to 1 seconds off of a no-op build compared to numbers in
flutter/flutter#81074.

```
$ time ./flutter/tools/gn --unopt
Generating GN files in: out/host_debug_unopt
Generating Xcode projects took 244ms
Generating compile_commands took 104ms
Done. Made 739 targets from 228 files in 1781ms
./flutter/tools/gn --unopt  3.07s user 3.04s system 325% cpu 1.875 total

```

Related to flutter/flutter#81074
chinmaygarde added a commit to chinmaygarde/flutter_engine that referenced this issue Apr 23, 2021
`--trace-gn` to `./flutter/tools/gn` should now dump a `gn_trace.json` in the
build directory. This is not on by default because capturing and generating the
traces seems to take about a second itself. So it is opt-in.

Related to flutter/flutter#81074
@chinmaygarde chinmaygarde added the P3 Issues that are less important to the Flutter project label Apr 26, 2021
@chinmaygarde
Copy link
Member Author

cc @aam @rmacnak-google: Is there a way we can reduce the usage of script files in GN?

@chinmaygarde chinmaygarde added P2 Important issues not at the top of the work list and removed P3 Issues that are less important to the Flutter project labels Apr 26, 2021
@rmacnak-google
Copy link
Contributor

I expect all the list_dart_files.py can be removed; I believe it predates the ability of our tools to generate depfiles.

dart-bot pushed a commit to dart-lang/sdk that referenced this issue Apr 26, 2021
TEST=ci
Bug: flutter/flutter#81074
Change-Id: I6e06e21d358fd4aafd37960803269796d2e62753
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/196983
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
dart-bot pushed a commit to dart-lang/sdk that referenced this issue Apr 26, 2021
…apshots.

GN/Ninja will discover them via the depfile created alongside the snapshot.

Bug: flutter/flutter#81074
Change-Id: I6e0f07214e8ea29e6d23261c71558da06fd2223a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/196982
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
@chinmaygarde
Copy link
Member Author

xref https://dart-review.googlesource.com/c/sdk/+/196981. Thanks! I'll stash the iOS and Mac SDK details in a GN variable. I already patched compile_commands.json generation so its now not a separate step (saves 1 second). With the three fixes, I think we should be well under two seconds. I'll see if we can get rid of engine versioning from Git SHAs. We never exposed that to users so I don't think its absolutely necessary (we do use it to version caches).

@dnfield
Copy link
Contributor

dnfield commented Apr 26, 2021

I'll see if we can get rid of engine versioning from Git SHAs

We do have a test in the framework that verifies flutter_tester --help outputs the same hash that we asked to download from GCS:

flutter/dev/bots/test.dart

Lines 137 to 146 in b8833af

final String expectedVersion = File(engineVersionFile).readAsStringSync().trim();
final CommandResult result = await runCommand(flutterTester, <String>['--help'], outputMode: OutputMode.capture);
final String actualVersion = result.flattenedStderr.split('\n').firstWhere((final String line) {
return line.startsWith('Flutter Engine Version:');
});
if (!actualVersion.contains(expectedVersion)) {
print('${red}Expected "Flutter Engine Version: $expectedVersion", '
'but found "$actualVersion".');
exit(1);
}

This helps protect against issues where a bug in a builder causes it to upload the wrong git hash to a particular bucket, e.g. it thinks it's building head but it's not

dart-bot pushed a commit to dart-lang/sdk that referenced this issue Apr 26, 2021
…ation snapshots."

This reverts commit a23c31b.

Reason for revert: The bootstrapping compilations are a bit confused about whose deps they're writing

Original change's description:
> [build] Don't list Dart sources up front when creating application snapshots.
>
> GN/Ninja will discover them via the depfile created alongside the snapshot.
>
> Bug: flutter/flutter#81074
> Change-Id: I6e0f07214e8ea29e6d23261c71558da06fd2223a
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/196982
> Reviewed-by: Ben Konyi <bkonyi@google.com>
> Commit-Queue: Ryan Macnak <rmacnak@google.com>

TBR=bkonyi@google.com,rmacnak@google.com,chinmaygarde@google.com

Change-Id: I267b6bac2676a18f57291c8472fab5c2aaa60284
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: flutter/flutter#81074
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/197000
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
dart-bot pushed a commit to dart-lang/sdk that referenced this issue Apr 27, 2021
…cation snapshots."

Fix `application_snapshot`'s depfile to track the sources of the application instead of the compiler. Split compiling the compiler into a separate GN target with its own depfile.

Bug: flutter/flutter#81074
Change-Id: I0fb23ada40a6241ee3dde7f6cfebdd121b9a4224
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/197020
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
dart-bot pushed a commit to dart-lang/sdk that referenced this issue Apr 28, 2021
…ecuting scripts at GN time.

Bug: flutter/flutter#81074
Change-Id: I3fba7743f89b970dfd8d4d47b21f7d51be7a9cdb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/196981
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Chinmay Garde <chinmaygarde@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
@jason-simmons
Copy link
Member

dart-lang/sdk@8d436a2 is causing unnecessary operations to run in no-op engine builds.

If I run:

  • flutter/tools/gn --unoptimized
  • ninja -C out/host_debug_unopt
  • ninja -C out/host_debug_unopt again

the second run of ninja should do nothing. With the above change, it's rerunning the list_dart_files_as_depfile.py actions and the Dart snapshotting actions that depend on those depfiles.

Can you revert dart-lang/sdk@8d436a2?

Regarding the broader issue highlighted in this bug: in my workflow I typically only need to run gn after syncing to a new engine commit or selecting a new target configuration. Rebuilding the engine after changing source code should only require running ninja, which is generally efficient.

@chinmaygarde what scenarios require frequently rerunning gn?

@chinmaygarde
Copy link
Member Author

@jason-simmons Can you run ninja -d explain -C out/host_debug_unopt on the second run to see why ninja is doing work. AFAIK, such issues occur because of subtle difference in depfile listings. For instance, absolute vs relative paths.

dart-bot pushed a commit to dart-lang/sdk that referenced this issue May 5, 2021
…ys re-executing scripts at GN time."

This reverts commit 8d436a2.

Reason for revert: ninja is always dirty

Original change's description:
> [build] Track glob dependencies via depfiles, instead of always re-executing scripts at GN time.
>
> Bug: flutter/flutter#81074
> Change-Id: I3fba7743f89b970dfd8d4d47b21f7d51be7a9cdb
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/196981
> Commit-Queue: Ryan Macnak <rmacnak@google.com>
> Reviewed-by: Chinmay Garde <chinmaygarde@google.com>
> Reviewed-by: Ben Konyi <bkonyi@google.com>

# Not skipping CQ checks because original CL landed > 1 day ago.

Bug: flutter/flutter#81074
Change-Id: I74c9ce055ad49107ae0d21f2f3b9b74991fc81d1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/198441
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
@jason-simmons
Copy link
Member

Based on the explain output it looks like ninja expects the depfile to contain a relative path to the depfile itself.

@rmacnak-google I can get this to work as expected in the Flutter engine build by changing dart-lang/sdk@8d436a2#diff-40dd701de08faf64c714b08263634be72ff06ced2cbb199e67c5ec4f516c2d9eR32
to use os.path.relpath(depfile)

@zanderso
Copy link
Member

@jason-simmons I believe @rmacnak-google is OoO today, so if you have something working locally, sending out a CL for it is probably the fastest thing.

@jason-simmons
Copy link
Member

The depfile script was reverted in the Dart SDK: dart-lang/sdk@5c0ff97

dart-bot pushed a commit to dart-lang/sdk that referenced this issue May 12, 2021
…ays re-executing scripts at GN time."

Use a relative path for the depfile's target to match Ninja's expectation; otherwise it thinks the target is always dirty.

TEST=build twice
Bug: flutter/flutter#81074
Change-Id: I4cae7ab55f79b5206521c7090502c0769d2b5277
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/198443
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
@flutter-triage-bot flutter-triage-bot bot added team-engine Owned by Engine team triaged-engine Triaged by Engine team labels Jul 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: tech-debt Technical debt, code quality, testing, etc. engine flutter/engine repository. See also e: labels. P2 Important issues not at the top of the work list team-engine Owned by Engine team triaged-engine Triaged by Engine team
Projects
None yet
Development

No branches or pull requests

7 participants