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 Makefile
its 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 Makefile
into ninja
the era.
Soong
Contains two modules, one of which Kati
is responsible for parsing Makefile
and conversion .ninja
, and the second module Ninja
is .ninja
compiled based on the generated one.
Kati
is right GNU Make
clone and switch the compile backend implementation to ninja. Kati
It does not compile itself, only the generated .ninja
files are provided Ninja
for compilation.
Makefile/Android.mk -> Kati -> Ninja Android.bp -> Blueprint -> Soong -> Ninja
So before the compilation is performed (ie, Ninja
when it actually starts), there are some build .ninja
steps. 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%
.
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:
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
Soong
Clone a copy GNU Make
and transform it into Kati
. Even if we did not modify any mk files, Kati
it 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 Ninja
OR . .ninja
Next is the call to Ninja
actually 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
.ninja
the target and build rule can be found in the file, the compilation can be completedMultithreading
The intuitive display is as follows, and Ninja
the corresponding number of threads will be started for compilation according to the incoming parallel task number parameter. Ninja
The 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.
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 Ninja
compilation starts faster, which indicates that it is not sensitive to the reading and parsing Ninja
of the file. .ninja
No significant time-consuming points were seen throughout the process. And the last compilation amount is very small, indicating Ninja
that 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 Ninja
compilation 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
Ninja
direct.ninja
file-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 make
the 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.
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