Filip Hracek (From Flutter Team)

The great thing about Flutter is that it runs very fast by default! But does that mean you don’t need to worry about performance at all?

The answer is no. It is entirely possible to write a very slow Flutter application. On the other hand, however, you can take full advantage of this framework to make your app not only fast and efficient, but also use less CPU time and power.

That’s what we want! There is a statistically significant difference between the two versions of the app on a meaningful metric

There are some general guidelines for performance optimization in Flutter:

  • When updating your status, have as little influence as possible.
  • Update status only when necessary.
  • Do not perform computationally intensive tasks in the Build method, ideally outside of the Main ISOLATE.

It may be hard to believe that for most performance tuning questions, the answer all leads to the question “What does it depend on?” . Are specific optimizations for specific widgets worth the maintenance costs? Is special treatment justified in particular circumstances?

The only useful answers to these questions are testing and measurement. Quantify the performance impact of each choice and make decisions based on that data.

The good news is that Flutter provides excellent performance analysis tools, such as the Dart DevTools that includes Flutter Inspector (currently in preview release), Or the Flutter Inspector in Android Studio (with the Flutter plugin installed). You can use Flutter Driver to manipulate applications and store performance information in Profile mode.

The bad news is that today’s phones are just too smart. 😂

Problems with Managers (Governors)

System-level daemons need to adjust the speed of CPU and GPU units based on the current load, but iOS and Android managers find it difficult to quantify the performance of Flutter applications. Overall this is a good thing, as it ensures smooth performance while using as little power as possible.

The downside, however, is that you can dramatically speed up your app by making it more efficient.

In the example below, you can see how looping through an application with meaningless print statements causes the manager to switch to a higher gear, making the application run faster and more predictable.

Manager’s problem: By default, you can’t trust the numbers. In the box diagram above, we have individual runs on the X-axis (marked with the exact time they started) and build times on the Y-axis. As you can see, it actually shortens (not increases) build times when we introduce completely unnecessary print statements.

In this experiment (above), worse code actually led to faster build times, faster rasterization times, and higher frame rates. If objectively worse code leads to better performance, then you can’t follow these metrics.

The above is just one example to explain why mobile application performance benchmarks are not intuitive and testing is difficult.

Next, I will share some examples of Flutter on Google I/O App, Developer Quest.

Basic Suggestions

  • Do not measure performance in DEBUG mode. The performance can be measured only in profile mode.
  • Test on a real machine, not on an Android or iOS emulator. Although simulator software is very suitable for development, their performance is very different from that of the real machine. Flutter is not allowed to run in profile mode on emulators because it does not make sense. The performance data collected in this way is not the actual performance.
  • The ideal situation is to use the same physical device. Keep it as your own performance tester and never use it for anything else.
  • Learn the Flutter performance analysis tool

CPU/GPU manager

As we just said, modern operating systems adjust the frequency of each CPU and GPU based on load and some other heuristics. (Touching the screen, for example, often causes An Android phone to put its priority in a higher gear.)

On Android you can just turn off these managers, which we call “scale locking.”

  • Write a script to scale-lock your test machine performance. You can find inspiration in Skia’s Recipe, or consult the Unix CPU API.
  • Unless you’re running a large benchmark like Skia, you probably want something lighter and less generic in general. Take a look at some of the shell scripts in Developer Quest. For example, the following one sets the CPU manager to userspace (the only one that does not automatically adjust the CPU frequency).
#! /usr/bin/env bash
GOV="userspace"
echo "Setting CPU governor to: ${GOV}"
adb shell "echo ${GOV} > /sys/devices/system/cpu/cpu${CPU_NO}/cpufreq/scaling_governor"
ACTUAL_GOV=`adb shell "cat /sys/devices/system/cpu/cpu${CPU_NO}/cpufreq/scaling_governor"`
echo "- result: ${ACTUAL_GOV}"
Copy the code
  • The goal now is not to simulate real performance (devices without user scale-Lock), but to capture performance metrics that are comparable at run time.
  • Finally, you need to test and run a shell script on the device. That’s the only way it works, the performance data up to that point is lying to you.

Flutter Driver

The Flutter Driver can automatically execute applications. You can read the performance analysis article on Flutter. Dev to learn how to use flutter to analyze applications.

  • Do not manually manipulate your application during performance testing. Always use the Flutter Driver to ensure that your comparisons are meaningful.
  • Write your code for Flutter Driver so that it can execute what you really want to test. If you’re doing regular application performance testing, try walking through the entire application and doing what users would do.
  • If your application is fortuitous (random network events, etc.) use mock data and make sure they are as similar as possible.
  • You can also use Timeline if you wantstartSync()Methods andfinishSync()Method to add custom timeline events. This is useful when you want to test the performance of a particular method, for example. willstartSync()Placed at the beginning of it and used at the end of the methodfinishSync().
  • Multiple tests are required for each version of the application. I did it 100 times in Developer Quest. When you measure something as complicated as the 99th percentile, you have to run it a lot more. For POSIX based systems, you just run the following:
for i in{1.. 100};do flutter drive --target=test_driver/perf.dart --profile; done
Copy the code

Timeline

The timeline is the raw data output when you run profile mode. Flutter dumps this information to a JSON file that can be loaded by Chrome :// Tracing.

  • Learn how to start the full Timeline in Chrome’s Tracing Timeline. All you need to do is open Chromechrome://tracingThen click “Load” and select the JSON file. You can find it in this articleBrief instructionFor more information about it. (there are alsoFlutter Timeline tooling, but it is still in the Tech Preview phase. Since the Developer Quest project was started before the Flutter timeline tooling was ready, I have not used that yet.
  • Use the WSAD key inchrome://tracingMove in timeline, and use 1234 to change operation mode.
  • When setting up the performance test for the first time, consider whether to run the full Android Systrace using the Flutter Driver. This gives you a deeper understanding of what is actually happening in the device, including CPU scaling information. However, don’t measure your application with Systrace fully on, as it can make things very slow and more unpredictable.
  • How do I use the Flutter Driver to run the full Android Systrace?First of all,, “you have to go through/path/to/your/android/sdk/platform-tools/systrace/systrace.py --atrace-categories=gfx,input,view,webview,wm,am,sm,audio,video,camera,hal,app,res,dalvik,rs,bionic,power,pm,ss,database ,network,adb,pdx,sched,irq,freq,idle,disk,load,workq,memreclaim,regulators,binder_driver,binder_lockStart Android Systrace.thenthroughflutter run test_driver/perf.dart --profile --trace-systraceRun the app.The lastThrough theFlutter drive - driver = test_driver/perf_test. Dart - use existing - app = http://127.0.0.1:NNNNN/Start the Flutter Driver (where NNNN isFlutter runRun on the port for you).

To measure the

It’s best to look at as many indicators as possible, but I find some more useful than others.

  • Build times and rasterization times are only useful in really rigorous performance tests (the default metric provided is TimelineSummary), which don’t involve much more than the UI.

  • Don’t put the TimelineSummary. The frameCount as calculating frames per second (FPS) method. The Flutter profile tool does not give you true frame rate information. TimelineSummary provides the countFrames() method, but it only counts how many completed frames have been built. A well-optimized application can limit unnecessary rebuilds to far fewer frames per second than an unoptimized application that often rebuilds.

  • The most useful data I personally got was by measuring the total CPU time spent running the Dart code. This counts the build method and externally executed code. Assuming you are running profile tests on scale-locked devices, the total CPU time is a good estimate of whether your application will consume more or less power.

  • The easiest way to find out the total CPU time spent running the Dart code is to look at timelineMessageLoop:FlushTasksScope of events. In the productionDeveloper QuestWhen I wrote oneThe Dart toolsTo extract them.
  • To detect Jank (skipped frames), look for extreme cases. For example, for the specific case of Developer Quest and the device we were testing, it was helpful to look at 95% of build time. (90% of build times are similar, even when comparing code with very wide efficiency levels, and 99% is often too messy. But your situation may be different.)

  • As I mentioned before, run it hundreds of times for each version of your app, and then use averages or percentiles that are slightly off. Even better, use a box chart!

conclusion

When this is all set up, you can confidently compare submissions and experiments. Below, you can see the answer to a very common dilemma: “Is this optimization worth the maintenance overhead?”

I think in this case the answer is yes. With just a few lines of code, each automated test we applied reduced CPU time by an average of 12%.

The main point of this article, however, is that different measurements can reflect very different things. Trying to extrapolate performance measurements too broadly may be intuitive, but it is wrong.

In other words: “What does it depend on”. We should embrace it.

The above is all the content of the translation. I think this article has great guiding significance for us to build efficient Flutter application, so I can translate it and share it with you.

Please point out any mistranslations.