• notice
  • Congratulations on the launch of the Sought Tech site

How to optimize Ninja for Android to speed up compilation

This article mainly introduces the relevant knowledge of "how to optimize Ninja in Android system to speed up compilation". The editor will show you the operation process through actual cases. The operation method is simple, fast and practical. I hope this article "How to optimize Ninja in Android system to speed up compilation" The article can help you solve the problem.

background

The compilation of Android system module code is too time-consuming. Even a few lines of code modification can make a compilation server with sufficient performance work for more than ten minutes (module single compilation), just to compile some megabytes. size jar and dex.

What is explored here is the single compilation of modules after the system has completed a reorganization, that is, commands such as m, mm, and mmm.

In addition, some operations that do not update the content of the source code, compilation configuration and other files, such as touch, git operations, etc., will be recognized as differences by the Android system compilation tools, so that the compilation configuration will be regenerated during compilation, recompile and A series of serious time-consuming operations such as no updated source code, regenerating intermediate files without differences, etc.

This article introduces several stages in the compilation process , as well as the time-consuming points/time-consuming reasons of these stages , and finally gives a ninja-based accelerated compilation method covering certain application scenarios (actually cutting out redundant compilations) Work).

surroundings

Compile server hardware and Android information:

  • Ubuntu 18.04.4 LTS

  • Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz (28 cores, 56 hyperthreads)

  • MemTotal: 65856428 kB (62.8GiB)

  • AOSP Android 10.0

  • Only modify the boolean initialization value inside a Java file (true to false)

  • Without modifying any other content, including source code, mk, bp, use the m single-editing module (after cleaning, use the comparison ninja for single-editing)

  • use time

  • The entire system has been reorganized once before

  • Do not modify any compilation configuration files such as Android.mk when compiling

The reason for making a case where the amount of code modification is minimal is to analyze the compilation performance bottleneck. The smaller the amount of code modification, the more obvious the bottleneck and the more conducive to analysis.

Critical compilation stages and time-consuming analysis

Due to Makefileits complex structure, difficult to debug, and difficult to extend, Android decided to replace it. Android was introduced in 7.0 Soong, which brought the compiled architecture from Android Makefileinto ninjathe era.

SoongContains two modules, one of which Katiis responsible for parsing Makefileand conversion .ninja, and the second module Ninjais .ninjacompiled based on the generated one.

Katiis right GNU Makeclone and switch the compile backend implementation to ninja. KatiIt does not compile itself, only the generated .ninjafiles are provided Ninjafor compilation.

Makefile/Android.mk -> Kati -> Ninja Android.bp -> Blueprint -> Soong -> Ninja

So before the compilation is performed (ie, Ninjawhen it actually starts), there are some build .ninjasteps. The key compilation stages are as follows:

Soong's bootstrap (Bootstrap), compiles Soong itself

Compiling the system code for the first time is time-consuming, one of the reasons is that Soong has to compile itself completely

Traverse the source tree and collect all build configuration files (Makefile/Android.mk/Android.bp)

  • Traversal and verification are very time-consuming, and no matter how powerfully configured machines are, they will be limited by single-thread efficiency and disk IO efficiency

  • Due to the dependencies and introductions between modules of the Android system, Soong (Kati) has to confirm whether the paths other than the target module need to be recompiled even if it is a single-compiled module.

Verify the legitimacy, validity, timeliness of the compilation configuration file, whether it should be added to the compilation, and generate .ninja

  • .ninja does not need to be regenerated if nothing has changed

  • The final generated .ninja file is very large (in my case, more than 1GB), and has obvious IO performance efficiency problems. Obviously, it is also very low in query efficiency.

The last step is to actually execute the compilation and call ninja to enter multi-threaded compilation

  • Because Android has added a lot of code compilation work, such as API permission control check, API list generation and other work (for example, generating system API protection list, instrumentation work, etc.), the compilation process is not actually fully invested in compilation.

  • The compilation process is interspersed with "pan-packaging work", such as generating odex, art, and res resource packaging. Although different "pan packs" can be performed in parallel by multiple threads, each pack itself can only be performed by a single thread

The following will analyze the performance of these four key stages based on the single compilation of modules (because the frequency of new compilation scenarios of the development environment system is low and will not be considered).

Phase 1: Soong bootstrap

In the case that the system has been reorganized once, Soong has already completed the compilation, so the proportion of its warm-up process to the entire compilation time will be relatively small.

Under "Environment", modify a line of Framework code to trigger the diff to compile. And use the following command to compile.

time  m  services framework -j57

Compilation actually takes 22m37s:

build completed successfully (22:37 (mm:ss)) ####
real 22m37.504s
user 110m25.656s
sys 12m28.056s

The corresponding staged time is as shown in the figure below.

  • It can be seen that the proportion of preheating time including the Soong bootstrap process is very low, the time is about 11.6s, the total time is about 1357s, and the proportion of preheating time is 0.8%.

How to optimize Ninja to speed up compilation in Android system

  • Kati and ninja, that is, the second and third steps of the above-mentioned compilation key process, account for nearly 60% (820 seconds, 13 minutes and a half) and about 35% (521 seconds, 8 minutes and a half) of the time respectively, and the total than nearly 95% of the time.

Note: This time-consuming is the time-consuming test after only minor modifications to the Java code. If you modify the compilation configuration file such as Android.mk, it will take more time.

Summary: It seems that the single compilation of modules after a reorganization, including the Soong bootstrap, the execution of the compilation preparation script, and the vendorsetup script, takes a very low proportion of time, which can completely eliminate the possibility of performance bottlenecks.

Stage 2: Kati traversal, mk collection and ninja generation

As can be seen from the above figure Kati, it takes a large proportion of the time. Its task is to traverse the source tree, collect all the compilation configuration files, and parse and convert them after verification and screening .ninja.

From a performance point of view, its main features are as follows:

  • It has to traverse the source tree and collect all mk files (In my case, there are 983 mk files)

  • Parsing the mk file (In my case, framework/base/Android.mk took ~6800ms)

  • Generate and write the corresponding .ninja

  • single thread

The intuitive display is as follows, it is a single-threaded, IO speed-sensitive, CPU-insensitive process:

How to optimize Ninja to speed up compilation in Android system

Kati processes files serially, and the CPU utilization is very low at this time, and the pressure on IO is not high.

Summary: It can be determined that its performance bottleneck comes from the IO speed. Simply allocating more CPU resources to the compilation instance will not help to improve the speed of Kati.

Stage 3: Ninja Compilation

SoongClone a copy GNU Makeand transform it into Kati. Even if we did not modify any mk files, Katiit would still take several minutes to tens of minutes of work in the front, just to generate a file that can be recognized by the generation tool of NinjaOR . .ninjaNext is the call to Ninjaactually start the compilation job.

From a performance point of view, its main features are as follows:

  • According to the target target and dependencies, read the previously generated .ninja configuration and compile it

  • Relatively independent, not coupled with the previous components, such as blueprint, kati, etc., as long as .ninjathe target and build rule can be found in the file, the compilation can be completed

  • Multithreading

The intuitive display is as follows, and Ninjathe corresponding number of threads will be started for compilation according to the incoming parallel task number parameter. NinjaThe compilation phase actually starts multithreading. However, it is impossible to do multi-threaded compilation all the time, because some stages such as some compilation targets (such as generating an API document), pan-packaging stages, etc. cannot be executed in parallel by multi-threading.

How to optimize Ninja to speed up compilation in Android system

It can be seen that the CPU utilization should be able to increase significantly at this time. However, only a few threads are enabled in the time-consuming stage, and more threads are used in the later stages and the final stage where the graph is very detailed (the time proportion is small).

Among them, the reason why some stages (several records with a longer proportion of time in the figure) failed to run full of resources is that these compilation targets themselves do not support parallelism, and the targets specified by this compilation command have all been "arranged" and do not need to be mobilized More resources to start work on other compilation targets. When compiling the whole system, it can run full.

The last stage (the last few columns in the figure with very detailed records) runs all the thread resources, but the running time is very short. This is because only one line of code is modified to trigger compilation during the compilation analysis in this case. Because the compilation workload is very small, these columns are very thin.

Summary: We see that the Ninjacompilation starts faster, which indicates that it is not sensitive to the reading and parsing Ninjaof the file. .ninjaNo significant time-consuming points were seen throughout the process. And the last compilation amount is very small, indicating Ninjathat incremental compilation can be ensured, and no compilation is not updated.

Compile optimization

This section completes the topic - Android system compilation optimization: Use Ninja to speed up compilation.

According to the summary of the previous analysis, the performance bottleneck can be summarized:

  • Kati traversal and generation are too slow, limited by IO rate

  • Kati throughput is too low, single thread

  • Reparse Makefile with or without update

The idea of using Ninjacompilation optimization is that in most scenarios, Kati's work can be abandoned and only Ninja's work can be performed to save more than 60% of the time . Its core idea is also a constraint, that is, abandoning unnecessary Kati compilation work without affecting the correctness of compilation.

  • Use Ninjadirect .ninjafile-based compilation to improve time-consuming:

Combined with the previous analysis, it is easy to think that if the mk file is not updated and does not need to regenerate a long list of final compilation targets (ie. ninja) before the target is built, then makethe Soong bootstrap, Kati, etc. It is redundant and redundant--this property Soong and Kati can't recognize by themselves, they will work again and again.

Since it is redundant to regenerate .ninja, directly instructing the compilation system to compile according to the specified .ninja will obviously save a lot of work and time-consuming. The ninja command is the key:

Use the ninja that comes with the source code:

./prebuilts/build-tools/linux-x86/bin/ninja  --version 1.8 .2 .git

Comparing the compilation of the commands listed at the top make, here is the same target compiled with ninja:

 time  ./prebuilts/build-tools/linux-x86/bin/ninja  
 -j  57  -v  -f  out/combined-full_xxxxxx.ninja  services  framework

After ninja recognizes the CPU platform by itself, -j58 is used by default. Here, in order to compare the m command above, use -j57 to compile

The -f parameter specifies the .ninja file. It is the build configuration file, generated by Kati in Android. This filename is modified by replacing with 'x'

The compilation result, compared to the above m, has a three-fold improvement:

real 7m57.835s
user 97m12.564s
sys 8m31.756s

The compilation time is 8.5 minutes, only one-third of make . As we can see, when it can be ensured that the compilation configuration is not updated and the changes only exist in the source code scope, using Ninja to compile directly, skipping Kati can achieve a significant improvement .

Use ninja directly:

./prebuilts/build-tools/linux-x86/bin/ninja  
-j $MAKE_JOBS -v -f out/combined-*.ninja <targets...>

Comparison summary

Here is a compilation demo of another project. The characteristics of this demo are that the code itself is relatively simple, the compilation configuration is relatively simple, and the overall compilation work is less. Most of the time spent compiling through make comes from the consumption of tools such as soong and make. The proportion of ninja time-consuming to actually execute compilation is extremely low . Since ninja itself skips soong, this useless tedious time-consuming can be skipped. As you can see below, ninja takes only 10 seconds to compile iperf. If this time is given to soong to compile, the preheating will not be enough.

$ -> f_ninja_msf iperf
Run ninja with out/combined-full_xxxxxx.ninja to build iperf.
====== ====== ======
Ninja: ./prebuilts/build-tools/linux-x86 /bin/[email protected] .git
Ninja: build with out/combined-full_xxxxxx.ninja
Ninja: build targets iperf
Ninja: j72 ======
====== ====== time /usr /bin/time ./prebuilts/build-tools/linux-x86/bin/ninja -j 72 -f out/combined-full_xxxxxx.ninja iperf
[ 24 / 24 ] Install: out/target/product/xxxxxx/system /bin /iperf
 53.62 user 11.09 system 0 :
 10.17 elapsed 636% CPU (0avgtext+0avgdata 5696772 maxresident)
 4793472 inputs+ 5992 outputs ( 4713 major+ 897026 minor) pagefaults 0swaps
real 0m1 0.174 s
user 0m53.624s
sys 0m11.096s

The horror time-consuming of soong compilation is given below:

$ -> rm out /target/product/xxxxxx/system/bin/iperf
$ -> time m iperf -j72
... [100% 993/993] Install: out/target/product/xxxxxx/system/bin/iperf # ### build completed successfully (14:45 (mm:ss)) ####
real 14m45.164s
user 23m40.616s
sys 11m46.248s

As we can see, one of m and ninja is 10+ minutes, the other is 10+ seconds, the ratio is 88.5 times.

The content of "How to optimize Ninja to speed up the compilation of Android system" is introduced here, thank you for reading.


Tags

Technical otaku

Sought technology together

Related Topic

1 Comments

author

order atorvastatin 80mg online & lt;a href="https://lipiws.top/"& gt;order lipitor online& lt;/a& gt; order lipitor 40mg online

Kqbdzs

2024-03-08

Leave a Reply

+