background
As mobile application development becomes more and more streamlined, more and more applications appear on the market. However, developing a mobile application is not a simple process and takes a lot of time, especially if you want an extensible mobile application that can run across Apple, Android, and Windows.
However, poor performance may greatly harm the user experience. The user does not want to see the startup screen longer than 10 seconds at any time. If the waiting time is over\they may feel overwhelmed, abandon shopping, reduce their stay time or uninstall the application completely.
With the popularization of development platforms, we need the right tools and methods to meet the ever-increasing demand.
Xamarin is such a framework. on Android, iOS and Windows platforms.
So, we will Xamarin.Android application, just like using Java development in Android Studio, we can use c# to test the performance to optimize the startup time.
Total test start time
Therefore especially to test the program at the start time of different devices, here Use to work surely has to be League of Friends + launched U-APM . It can be seen from the figure that there is still room for optimization in the startup time of the application.
On Android, the ActivityManager
system process will display an "initial display time" log message to better understand the overall startup time. Use 06181365a36485 in the command adb logcat
quickly view the logs of the Android device. Or use the Android debug log in Visual Studio
On Windows, run the following powershell
:
> adb logcat -d | Select-String Displayed
Output:
ActivityTaskManager: Displayed com.lgq.wood.expiramentation/.MainActivity:
The above log message was captured when debugging the application Visual Studio
Starting/connecting the debugger will incur some additional overhead, and it lacks Debug
compilation-time optimization.
If we simply switch to the Release
configuration and deploy and run the application again:
ActivityTaskManager: Displayed com.lgq.wood.expiramentation/.MainActivity:
If we test the application on a Pixel 3 XL device:
ActivityTaskManager: Displayed com.lgq.wood.expiramentation/.MainActivity:
Because our ultimate goal is to improve the performance of mobile applications, the first step should be to actually test the specific location of the stuttering function. If you make changes to the code blindly, you may end up with very big differences from our guessed results. If some complex performance improvements are made, the maintainability of the code base will even be damaged. This process should be: test, make changes, test again, and repeat the above steps.
The stuck position measured by U-APM
com.lgq.wood.expiramentation.apache.http.impl.exec.readRawTextFile
Diagnose the problem
Well, the previous application is slow due to readRawTextFile. what should I do now?
First, we need to have a systematic understanding of the following components
Android ART
Android runtime (ART) is a managed runtime used by apps and system services on Android. ART used as an Dalvik
file to be executed during operation. 06181365a36678 executable file (.dex file-executable file for D alvik EX), which is a compact format Dalvik
ART introduces ahead of time (AOT) compilation by compiling the entire application into native code when installing the application. This leads to faster application execution and improved memory allocation. And garbage collection mechanism, more accurate analysis and so on.
To achieve this, ART uses dex2oat
to create an ELF
(executable and link format). The disadvantage is that it takes extra time to compile. In addition, the application uses a large amount of disk memory to store the compiled code.
AOT
Mono
provides AOT function during operation. Mono
will precompiled assemblies to minimize JIT
time and reduce memory usage. Mono
ELF .so
files on platforms that support it (such as Android). Then it stores a pre-compiled image next to the original assembly.
which is
Mono.Android.dll → libaot-Mono.Android.dll.so
Then, these files can be used by Mono
, and the overhead of JIT
Start tracking
Mono introduces a feature that allows the built-in AOT analyzer to be used on the application to generate AOT configuration files. The analyzer performs memory analysis, execution time analysis, and even sampling analysis based on statistics. This will generate an AOT configuration file, which can be used to optimize the application when using the AOT function of Mono with the configuration file.
Start tracking can be used in Visual Studio 2019 version 16.2 or Visual Studio for Mac 2019 version 8.2.
You can start using boot tracking by editing the .csproj
file of the Android project and adding the following attributes to Release <PropertyGroup>
<PropertyGroup Condition = " '$(Configuration)|$(Platform)' == 'Release|Any
You can also set it in the Android options of the item settings. The activation of Mono's AOT compiler will configure the startup tracking of the file by default, and speed up the startup time of the Android application during deployment.
Actual analysis
We need to actually analyze our code and need to improve. Switch back to the Debug
configuration and enable the Mono analyzer by running the following command:
$ adb shell setprop debug.mono.profile log:calls
adb shell
shell
command on an Android device or emulator. setprop
sets Android system properties, similar to environment variables on other platforms.
Then just force quit and restart the application. The next time it starts, Mono will save a file in the local directory of the Android application. profile.mlpd
Note that there is a problem with this. The file can only be accessed by the application itself, so we must use a command to locate the file: run-as
$ adb shell run-as com.lgq.wood.expiramentation ls -l files/.__override__
-rw-rw-r-- 1 u0_a411 u0_a411 515303 2020-07-27 09:29 profile.mlpd
To get the files from the device, I used a known writable /sdcard/Download/
$ adb shell run-as com.lgq.wood.expiramentation cp files/.__override__/prof
After copying the files, you can use adb pull to get the files to your desktop computer: profile.mlpd
$ adb pull /sdcard/Download/profile.mlpd
/sdcard/Download/profile.mlpd: 1 file pulled, 0 skipped. 162.7 MB/s (515303
profile.mlpd
is a binary file
Windows users need to install Mono system for Linux to run.
With the series of codes above, some interesting numbers will appear.
solution
Through the previous call, we can find that the following functions may take a considerable amount of time:
You can also see the memory allocation, for example:
Please note that if you need to see which method these allocations came from, you can pass it to. --tracesmprof-report
We have made various attempts and have all received certain results. But what we most unexpectedly did was the following simple change. We tried to string
directly from stream
instead of creating it with the response content, and then use the new System.Text.Json
library to perform a more efficient JSON
parsing:
// At the top
using System.Text.Json;
//...
async Task<Response> GetSlides()
{
var response = await httpClient.GetAsync("https://httpbin.org/json");
response.EnsureSuccessStatusCode();
using (var stream = await response.Content.ReadAsStreamAsync())
{
return await JsonSerializer.DeserializeAsync<Response>(stream);
}
}
Looking at the difference in method call, we can see an obvious time optimization:
This point is consistent with the bottleneck function we tested U-APM readRawTextFile
function. We tried the following methods, which alleviated the startup problem to some extent but benefited It is not as U-APM . Listed here for reference only:
- We can cache the results of web requests
- We can load the previous call result from the file on the disk, which is valid for 24 hours.
- Since the calls are not dependent on each other, we can make asynchronous calls at the same time
- On the server side, we can make a new API call and return all the called data in one request
Optimizing performance is difficult, and there are many directions. Regarding the slow positioning part of the code, after the change, you may find that this part has no effect at all. The best way to affect the code is to test, test, and then test again. Test again after changing. To improve performance through testing, it is often possible to prepare for problems in advance. It also tends to improve the core performance bottleneck more intensively, thereby bringing about an overall improvement of the side surface.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。