preface

Recently, we have been improving the quality of App, and the optimization of startup time is a very important one. This paper will give an in-depth understanding of the startup time.

The body of the

What is startup time?

The startup time can be understood as the time from the user clicking the Icon of the App to the time when the user sees the real picture of the App and can interact with it. This period can also be divided into two parts: the time when the iOS system starts the App and the time when the App initializes the in-app logic and interface.

I. App generation

Before exploring how iOS handles App launches, we need to understand how the next App is created:

1. Compilation: If we open an Xcode project, we will see several. H /.m components; When we compile, the compiler will compile each.m file separately to get the corresponding.o file;

2. Link: Multiple. O files generated by compilation are linked with static library and dynamic library to obtain an executable file, also called Mach-O file;Some information in Mach-O is stripped by lines, such as debug symbols, line numbers, etc. For debugging purposes, this information is put into a DSYM file;

3. Signature & Package & upload: Package the clipped Mach-O and resource files (storyboard, asset) into. App files, sign them, and upload them to AppStore background.

Ii. How does iOS start the App

The WWDC video explains how to start an App, starting with dyLD2 before iOS 13:

1. Parse the header of the Mach-O file, find LC_LOAD_DYLINKER, locate the path to DYLD, and load DYLD into memory;

2. Analyze the dependency of the dynamic library, such as this part of the dependency in our project;

3. Put the dynamic library MMAP into the memory separately. An App will rely on many dynamic libraries during running.

4, symbol search location, the following picture is our project depends on glkit. framework, but open the framework folder, you will find only header file and a TBD file; TBD is short for text-based Stub Library, which provides symbols for xcode linking. When the App is actually running, it also needs to load the dynamic library for real links; (See the previous article for dynamic linking.)

5. Symbol binding and redirection. As with static linking, symbols need to be converted to runtime memory addresses. Dynamic library symbols need to be run to determine the specific position of all symbols; Another factor affecting iOS is that ASLR(Process address space layout randomization) also requires offsets at runtime;

6, static initialization, including our common +load method, as well as other static initialization methods;

How to optimize DYLD3? After iOS 13, dyLD3 provided by the system made a cache of the head of the parsing Mach-O file, the parsing dynamic library dependencies, and the result of symbol search and location, and wrote it to disk. At startup, the cache is read and validated, followed by dynamic library loading, symbol binding and redirection, and static initialization.

The cache is stored in the sandbox’s TMP /com.apple.dyld directory (TMP cannot be wiped entirely) and is recreated when the phone’s system is upgraded or the App is updated.

Iii. How to analyze these times during development

During development, you can set DYLD_PRINT_STATISTICS to 1 in the environment variable;

When you start up, you can see that the console has typed the exact time.

Total pre-main time: 622.64 milliseconds (100.0%) Dylib loading time: 33.89 milliseconds (5.4%) rebase/binding time: 279.52 milliseconds (44.8%) ObjC Setup Time: 270.59 milliseconds (43.4%) Initializer time: 38.63 milliseconds (6.2%) vs intializers: 7.08 milliseconds (1.1%) libMainThreadChecker. Dylib: 19.92 milliseconds (3.2%).Copy the code

Also, you can set DYLD_PRINT_LIBRARIES to 1 to print out which dynamic libraries are loaded.

dyld: loaded: /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 12.2. Simruntime/Contents/Resources/RuntimeRoot/usr/lib/libMobileGestaltExtensions dylib dyld: the loaded: /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 12.2. Simruntime/Contents/Resources/RuntimeRoot/usr/lib/libobjc - trampolines. Dylib dyld: the loaded: /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 12.2. Simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/FontServices framework/libTrueTypeScaler .dylib dyld: loaded: /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 12.2. Simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Accelerate framework/Frameworks/vImage framewor k/Libraries/libCGInterfaces.dylib ...Copy the code

Instrucment also has tools for analyzing these times, such as the most commonly used Time Profiler, and the more complex System Trace.

Time Profiler collects running Time statistics based on sampling, which is approximately once every millisecond. You can select Recording Options for High Frequency to increase the sampling Frequency. Time profilers are relatively simple to use and provide direct feedback on bottlenecks.

System Trace can analyze locks, thread states, memory changes, and System calls In more detail. For example, the distribution of Zero Fill, File Backed Page In, Page Cache Hit, and Copy On Write is shown In the following figure.

File Backed Page In is a Page fault. This Page is triggered if the memory fails to have a Page. The operating system allocates physical memory and copies the Page to the physical memory.

PageCache Hit If the operating system has a corresponding Cache in the PageCache, a PageCache Hit is triggered. (The resources)

Copy On Write Memory pages in the operating system are shared. If some pages are read-only, they can always be shared. However, if you Write to a writable shared memory page and need to Copy it before attempting to Write, this process is called Copy On Write.

The value of the memory page Zero Fill is 0. After the page is read, it needs to Fill the page with 0.

Iv. How to make statistics of online users’ startup time

The most practical way is to load statistics: If you use CocoaPod to manage the integration library, you can create A Pod library starting with A (CocoaPod is in alphabetical order), so that the +load method of the Pod library is the first to be executed. __attribute__ sets function, variable, and type attributes. You can set a constructor attribute so that the function is automatically executed before main().

static void __attribute__((constructor)) _mainConstructor() {
    NSLog(@"main constructor");
}
Copy the code

DidFinishLaunchingWithOptions to get: the APPDelegate didFinishLaunchingWithOptions method directly at the beginning of the dot; Dot didFinishLaunchingWithOptions end: the APPDelegate didFinishLaunchingWithOptions method directly at the end of the dot; Dot RootViewControllerDidAppear: viewDidAppear: method at the beginning of the dot;

conclusion

Learn more about priming so that you can better analyze problems and design good solutions.

Finally, two tools were introduced: MachOView and Hopper Disassembler. The former is open source and free (search and download directly), while the latter is paid software (you can also try it in 30 minutes).

The appendix

Mmap WWDC2017-App Startup Time: Past, Present, and Future