Xingfuli C-side iOS compilation optimization practice - 40% optimization time consumption

Xingfuli C-side iOS compilation optimization practice - 40% optimization time consumption

Author: Xu Binbin

background

After a long period of business iteration, the incremental compilation of C-side engineering has seriously deteriorated. Before December 2021, the average incremental compilation of C-side was more than 3 minutes, which seriously affected the R&D efficiency and urgently needed optimization! After optimization, the incremental compilation time was reduced to about 2 minutes.

analyze

Xingfuli app compilation process

Main time-consuming analysis

  • Full compilation: Pod compilation takes most of the time, up to hundreds of seconds, and CI packaging takes 20 to 30 minutes.
  • Incremental compilation: link and resource processing take up most of the time (this part takes 130s before C-side project optimization).

plan

LLVM compilation optimization

LLVM compilation process

The compilation of .m files from .o files goes through the following stages:

  • Preprocessing: remove comments, replace macro definitions, add line numbers and file identifiers
  • Lexical analysis: splitting the code into tokens
  • Syntax analysis: verify whether the syntax is correct and generate semantic nodes
  • Generate AST: combine all nodes to generate an abstract syntax tree
  • Static analysis: static code detection through syntax tree
  • Generate LLVM IR: CodeGen translates the syntax tree from top to bottom into IR code
  • Generate assembly: Convert IR code to assembly code
  • Generate object file: The assembler converts the assembly code into machine code

As you can see, a lot of work is done in the compilation process from source files to target files. If a new line of code is added to a source file, all R&D colleagues have to follow these steps again when building, which increases a lot of repetitive work and time.

dolphin distributed compilation cache

The Byte App Infra team hooks LLVM Clang. For basic compilation commands (such as oc files), they can be hashed into a unique key based on content and dependencies. After we compile the new .m, we store the corresponding .o and key on the local hard disk and remote server. When other R&D colleagues compile, they only need to download the .o file, which can greatly improve the compilation efficiency. After Xingfuli CI was connected to Dolphin, the time consumed for packaging and compilation was reduced from 600s to 240s.

Resource Optimization

Main project asset compilation

The main project resources will be compiled into Assets.car every time you compile. There are many images in the project stored under the resources of the main project. Each compilation will take more than 30 seconds at this step. Therefore, most of the main project image resources are migrated to the pod library, which can reduce the compilation time of the main project resources to within 5 seconds.

copy pods resource

Our project uses resources to reference resources. This step is to copy all pod library resources and compile and merge them into the main project's Assets.car, which takes about 40 seconds. There are two directions for optimization:

  1. If it is changed to resource_bundles, each pod will have its own bundle and its own Assets.car, and there is no need to compile it every time. The time consumption of incremental compilation will be reduced to 0, but the cost of project transformation is huge, so it can be regarded as a long-term goal.
  2. If we don't need to care about the UI interface, for example, when doing tracking, we can write a script to skip this step during compilation, which can be achieved in the short term.

Link Optimization

ld64

ld64 working principle reference: https://mp.weixin.qq.com/s/tSj6JVEg7plJQm7aDHLyMw

The static linker ld64 is responsible for analyzing the .o, .a, and .dylib files output by the compiler and other modules, and assembling executable files after parsing, redirecting, and aggregating symbols. The main workflow of ld64 is as follows:

zd

Zld is an optimized linker developed based on ld64. It increases the number of concurrency and uses more efficient data structures to optimize the link process. Of course, we can also participate in optimizing zld. For example, a big guy from Feishu optimized linear search through map search, reduced the time complexity of the algorithm, and optimized the time consumption of symbol resolution.

Linear Search

Map Search

Access zld data comparison

ld64 data:

zld data:

in conclusion

Data comparison:

Before optimization: 3.79m

After optimization: 1.91m

Practical tips

Resource copy

When the project is pod installed, a resource copy script code will be generated in pods-target-resources. This script will be run during compilation. If you want to skip resource copying, just add exit 0 to the first line of resources.

zld debugging

zld source code: https://github.com/michaeleisel/zld

Use zld to compile the project, view the compilation log, and obtain the link command code:

Delete the brackets and the contents inside, add a -v after the clang command to display the link parameters, then execute the script to generate link parameters, copy and delete the contents before -demangle, and save them to juzi.txt:

 -demangle -lto_library /Applications/Xcode.app/Content......

Open the zld project, adjust the compilation mode to release (debug runs too slowly, release runs fast but cannot be debugged with breakpoints), and copy the parameters of juzi.txt to arguments, then you can directly debug the link process of the project.

Analysis of zld time

Copy the release executable file generated by the zld project to the desktop.

Open the time profiler of instruments in Xcode and select the zld executable file on the desktop.

Replace \s- in the juzi.txt parameter with \\n-, copy it to the arguments in the figure above, and then run and analyze.

 -demangle \
-lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib \
-dynamic \
...

As shown in the figure, getUserVisibleName() takes a long time. Let's check the zld source code:

After breakpoint or log test, I found that this method can never find the substring of ".llvm." (only as a demo test), so I tried to change it to the following code:

Compile again to generate a new executable file, and test again with instruments to get the following data:

Todo

  1. Change resources to resource_bundles to reduce the resource copying time to zero.
  2. As Swift is used more and more in projects, you can use Dolphin to compile and cache Swift.
  3. Explore the industry trends of lld and further optimize link speed.

<<:  Do you know what threats your mobile devices face?

>>:  iOS 15.6 official version battery life test is out, it consumes more power

Recommend

Case Study: 12 Gamification Strategies for User Growth

This article uses a case study to introduce how t...

A complete set of live streaming script templates

First put the gains of reading this article in fr...

To deal with Omicron, don’t choose the wrong mask!

The epidemic situation caused by the Omicron vari...

TikTok Advertising: Account Opening Tips

Preliminary preparation As a cross-border indepen...

How does traffic turn into sales? You must solve these 5 problems!

Suppose there is a chopping board that costs 1,50...

"Dolphins can smile", the biggest lie in the aquarium

During the National Day holiday, the aquarium is ...

Make a stylish headphone Beats new Solo2 review

Apple's $3 billion acquisition of Beats has be...

Copying the routine of Tik Tok's popular content, why is it not popular yet?

What are the usage scenarios of short videos that...

You have an April Fool's Day marketing strategy package, please check it!

April Fools' Day is here again! Are advertise...

Information flow advertising creative optimization guide!

The core of information flow advertising lies in ...