Repository navigation
Improve hot reload performance - #28152
Conversation
|
cc @aam |
|
cc @zanderso for review |
| stayResident: stayResident, | ||
| ipv6: ipv6); | ||
| ipv6: ipv6) { | ||
| watcher = DirectoryWatcher(projectRootPath ?? fs.currentDirectory.path); |
There was a problem hiding this comment.
I thought we talked about how this will regress dependency analysis: files outside of current directory won't be watched, unused files inside of current directory will be reported to the compiler.
The right solution is to have incremental compiler report files that comprise the application(similar to how list of files produced for .dep-file used for gradle), watch those.
There was a problem hiding this comment.
For most projects the current directory contains the only sources that will be changed. For instance, on a pub based workflow to update sources I need to kill the runner and run pub get too.
How much execution time are we willing to trade to make dependency tracking more accurate? The changes here represent ~0.5 seconds across all hot reloads on windows for example. Would requiring a hot restart to update other directories be a reasonable compromise?
There was a problem hiding this comment.
Querying the compiler for the list of sources it used should be instantaneous - it already has this information because it just compiled all of them.
There was a problem hiding this comment.
Understood. I think we both agree that the analyzer based approach is flawed - tomorrow lets discuss how we can proceed on incorporating the incremental compiler reports.
| } | ||
| assert(reassembleViews.isNotEmpty); | ||
| printTrace('Evicting dirty assets'); | ||
| await _evictDirtyAssets(); |
There was a problem hiding this comment.
Why is this getting deleted? Does flutter engine no longer need to be told to refresh assets when they are changed?
There was a problem hiding this comment.
As far as I can tell we only support updating assets on hot restart. These calls are fired on every hot reload regardless of the changed files and seem to serve no purpose
There was a problem hiding this comment.
I know that you can use hot reload when you (just) update assets. Well, at least it used to work in the past fine.
It's good idea to skip sending this request if no assets were changed though.
There was a problem hiding this comment.
Its possible we regressed or removed this feature in the past, or I might be missing something. I will do some archaeology tomorrow to confirm
There was a problem hiding this comment.
I looked into this some more, we support reloading assets but only in the case where the content of the asset itself has changed - we don't support adding/removing/renaming them.
I've added back the evict call, but we can still skip scanning the bundle since additional/removed assets wont show up until a restart anyway
There was a problem hiding this comment.
Supposedly we need to scan assets to figure out which ones were changed, which ones need to be transferred to the device.
| @@ -0,0 +1,105 @@ | |||
| // Copyright 2019 The Chromium Authors. All rights reserved. | |||
|
|
||
| try { | ||
| // Will return immediately if pubspec.yaml is up-to-date. | ||
| await pubGet( |
There was a problem hiding this comment.
What will happen now if user changes pubspec.yaml in application they hot reload?
There was a problem hiding this comment.
We currently run a pub get when starting via flutter run or attach. It's not possible to actually update dependencies via pub and hot reload, because this changes .packages.
There was a problem hiding this comment.
If changing .packages doesn't work that I would imagine is also a bug. Once that is fixed(not sure where the culprit is), without this pub get step, changes to pubspec.yaml have no chance of being picked up though.
| await _scanPackages(fileFilter); | ||
| } | ||
| if (bundle != null) { | ||
| if (bundle != null && fullRestart) { |
There was a problem hiding this comment.
What is the reason for doing this for fullRestart only? Don't we need to scan assets for changes when doing hot reload as well? I think you mentioned there might be a limitation currently that we don't pick up newly added assets on hot reload, but isn't that just a bug? All pieces seems to be in place to have that functionality available.
There was a problem hiding this comment.
We need to update both the assets and the asset manifest. Right now we have the capability to update the assets but the manifest doesn't update correctly. Its possible this is an oversight or a bug.
Currently the asset _entries remains populated, so hot reload of assets still works.
There was a problem hiding this comment.
I'm going to add this back for now, because I've found its possible to hot reload assets in the hello_world example. Its not possible in flutter_gallery where I originally tried, and the differences there are number of assets and the usage of package assets.
|
We should still be able to update existing packages though, will update the PR. EDIT: same issue as adding a new package |
| return false; | ||
| } else { | ||
| throw 'Could not parse additional details for dependency $name; line was: "$line"'; | ||
| // We're an empty target. |
There was a problem hiding this comment.
is there a way to confirm that the line describes empty target and keep else { throw for futher unexpected cases?
There was a problem hiding this comment.
Removed this code, instead I should use the regular yaml parser - all I need are the the list of dependencies.
| if (content is DevFSFileContent) { | ||
| filesUris.add(uri); | ||
| invalidatedFiles.add(content.file.uri.toString()); | ||
| syncedBytes -= content.size; |
There was a problem hiding this comment.
How syncedBytes is going to be updated with this code gone?
There was a problem hiding this comment.
I added syncedBytes which I accidentally removed from the dill sync. All other entires are assets and are handled above
| ? fs.path.relative(pathToReload, from: projectRootPath) | ||
| : pathToReload, | ||
| ); | ||
| if (!dirtyEntries.containsKey(entryUri)) { |
There was a problem hiding this comment.
Why was this if removed, I'm curious?
There was a problem hiding this comment.
I'm not sure how this could ever be false - this entryUri is for the dill file and at this point we have only added assets to the dirtyEntries.
Reduces devfs operations, diff of packages, and computation of dart dependencies. Instead of traversing packages, we also include information from pubspec to remove transitive dependencies, dev_dependencies, and any cached packages from changing.
We also log a warning when updating a package version or adding a new package, since in its current state this may cause a hot reload crash.
This moves the best case hot reload times for an emulator/simulator/desktop closer to 250 ms.
In cases where a large number of libraries are invalidated (50+), times are now bottle-necked by frontend server instead of filesystem operations.
Linux Emulator w/Change
Linux Emulator at HEAD
Windows Emulator with change
Windows Emulator at HEAD
Mac/Pixel 2 w/ change
Mac/Pixel 2 at HEAD
Improved trace for hot reload command:

We still have some additional overhead on windows from file stat commands: I think these could be removed as well.
Trace on Windows
Below is a trace captured from a hot reload (the 6th reload) on an Android emulator running on Windows. I added Timeline events for _computeDartDependencies as well as the file stat operations in the devfs
And here is the top of the CPU profile:
The timeline events were added as follows:
flutter_tools/lib/src/run_hot.dart
flutter_tools/lib/src/devfs.dart
Fixes #28150
Fixes #27446