This article documents the common practice of binary rearrangement
The method of collecting function calls in this paper is Clang piling
Clang piling can be done:
Obj -c’s anonymous function Block
Swift code method
Custom C functions/system C functions
Obj-c method of our/system
Because when Clang compiles our code, generating IR,
There is an AST abstract syntax tree for handling call dependencies
The example in this article is a demo of MJRefresh for the OBJ-C project
The background is briefly
(Lots of information online)
The operating system has a Page fault. A page fault occurs for about 4 ms, and the user does not perceive it
When the app was started, a large number of page faults occurred, which was easily perceived by users
Feel the default compile order of the startup methods
Use the link map
- Look at the symbols below
This is the default link order, which is independent of the call order
Address Size File Name
0x100005B5C 0x0000008C [ 1] -[MJRefreshBackFooter willMoveToSuperview:]
0x100005BE8 0x00000340 [ 1] -[MJRefreshBackFooter scrollViewContentOffsetDidChange:]
.
- Access to the link map
Set within the Xcode
In the folder. Find it
The key to binary rearrangement is to sort out the stack of function calls that are started
Answer in the interview, arrange manually, that GG
Because calls branch and they repeat
There is some complexity
Clang piling is used in this paper
In other C Flags, enter
-fsanitize-coverage=func,trace-pc-guard
After setting,
#include
Two C functions fire
void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop)
with
void __sanitizer_cov_trace_pc_guard(uint32_t *guard)
Clang inserts the second method into the method call we care about
I get the address of the last call
Set the pile, generally only for performance optimization
Usually development and production, generally close the effect of pile insertion
Next, get the symbol
Import the # import < DLFCN. H >
Through the DLADDR method
Note:
- 1. Avoid loops
-fsanitize-coverage=func
Clang peg, by default, every time the cycle, also collected
=func, has the effect of inserting pile, ignoring circular jump
- 2. Thread safety
Atomic queues can be used
Import the # import < libkern/OSAtomic. H >
static OSQueueHead symbolList = OS_ATOMIC_QUEUE_INIT;
-
- Remove duplicate
Remove the collected duplicate symbols
- 4. Symbol processing
Obj-c class method, prefixed with +[
An instance method of Obj -c, prefixed with -[
C method, manually prefix, _
- 5. Performance optimization
In piling, do as little as possible
In the piling method, only addresses are collected
After symbol collection is completed, unified parsing processing is performed
- 6, details
When is the symbol collection finished
Binary rearrangement, do is start optimization
After rendering the first screen,
Click on the screen, you can calculate
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
If you choose a list, you can do it
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
See the Github repo below for the detailed method implementation
order.file
Such a long
+[UIViewController(Example) load]
_main
+[MJNavigationController initialize]
Copy the code
// ...
Copy the code
Take out and write into the real machine sandboxorder.file
This article is obtained by Xcode
- 1. Click the device
- 2. Select the application
- 3. Download app sandbox information
- 4. Fetch the file written to the sandbox
order.file
The setting of the
- General, and engineering XcodeProj level, relatively simple
- Write to the compile configuration file
Check the result
Time statistics before main
You can use environment variables
DYLD_PRINT_STATISTICS
= 1
After the main function, the first screen controller- (void)viewWillAppear:(BOOL)animated
Time statistics of
The main file
CFAbsoluteTime StartTime; int main(int argc, char * argv[]) { StartTime = CFAbsoluteTimeGetCurrent(); @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); }}Copy the code
In the first screen controller
extern CFAbsoluteTime StartTime; - (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear: animated]; CFAbsoluteTime doneTime = CFAbsoluteTimeGetCurrent(); NSLog(@"AppLanuch-- main after using -- \n% fsec \n",(donetime-startTime)); }Copy the code
Preliminary conclusions: There may be a slight improvement
Before the rearrangement
Total pre-main time: 521-08-25 15:49:26.472878+0800 MJRefreshExample[8439:116264] AppLanuch-- test milliseconds (521-08-25 15:49:26.472878+0800 MJRefreshExample[8439:116264 0.551359 secondsCopy the code
After the rearrangement
Total pre-main time: 227.41 milliseconds (100.0) 2021-08-25 15:47:342.945831 +0800 MJRefreshExample[8382:114265] AppLanuch-- milliseconds (100.0) 2021-08-25 15:47:342.945831 +0800 MJRefreshExample[8382:114265] AppLanuch-- 0.406833 secondsCopy the code
Small projects, not obvious
Every run is different
Step: Xcode Product clean
Delete app on real machine
After deleting, run a few other programs, refresh the hardware, and then run the program test