1. 优化前 缺页中断耗时 (第一次安装)

如果有, 请先删除 -fsanitize-coverage=func,trace-pc-guard 配置.

删除app, 清除Xcode缓存 ;

Xcode菜单栏 > Product > Profile (快捷键command+I) , 等待 running 完成;

出现 Instruments 界面, 选择System Trace ;

启动项目, 到第一个界面出现后停止, 等待分析,

748-145

搜索Main Thread, 选择 Virtu Memory, 查看缺页中断File Backed Page in 次数与时间;

2. 获取启动时加载的所有函数符号

删除app, 在 Xcode Build Settings > Apple Clang - Custom Compiler Flags > Other C Flags

添加 -fsanitize-coverage=func,trace-pc-guard ,

在启动后会出现的第一个界面中添加如下代码:

#import <dlfcn.h>
#import <libkern/OSAtomic.h>

//原子队列
static OSQueueHead symboList = OS_ATOMIC_QUEUE_INIT;
//定义符号结构体
typedef struct {
    void * pc;
    void * next;
} SymbolNode;

void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
    static uint64_t N;  // Counter for the guards.
    if (start == stop || *start) return;  // Initialize only once.
    printf("[clang] INIT: %p %p\n", start, stop);
    for (uint32_t *x = start; x < stop; x++)
        *x = ++N;  // Guards should start from 1.
}

void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
    //if (!*guard) return;  // Duplicate the guard check.
    
    void *PC = __builtin_return_address(0);
    
    SymbolNode * node = malloc(sizeof(SymbolNode));
    *node = (SymbolNode){PC,NULL};
    
    //入队
    // offsetof 用在这里是为了入队添加下一个节点找到 前一个节点next指针的位置
    OSAtomicEnqueue(&symboList, node, offsetof(SymbolNode, next));
}

void tmpOrderFile() {
    NSMutableArray<NSString *> * symbolNames = [NSMutableArray array];
    while (true) {
        //offsetof 就是针对某个结构体找到某个属性相对这个结构体的偏移量
        SymbolNode * node = OSAtomicDequeue(&symboList, offsetof(SymbolNode, next));
        if (node == NULL) break;
        Dl_info info;
        dladdr(node->pc, &info);
        
        NSString * name = @(info.dli_sname);
        
        // 添加 _
        BOOL isObjc = [name hasPrefix:@"+["] || [name hasPrefix:@"-["];
        NSString * symbolName = isObjc ? name : [@"_" stringByAppendingString:name];
        
        //去重
        if (![symbolNames containsObject:symbolName]) {
            [symbolNames addObject:symbolName];
        }
    }
    
    //干掉自己
    NSString * thisFunc = [NSString stringWithFormat:@"_%s",__FUNCTION__];
    if ([symbolNames containsObject:thisFunc]) {
        [symbolNames removeObject:thisFunc];
    }

    //取反
    NSMutableArray * symbolAry = [NSMutableArray arrayWithArray:[[symbolNames reverseObjectEnumerator] allObjects]];
    NSLog(@"[clang] %@",symbolAry);
    
    //将结果写入到文件
    NSString * funcString = [symbolAry componentsJoinedByString:@"\n"];
    NSString * filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"lb.order"];
    NSData * fileContents = [funcString dataUsingEncoding:NSUTF8StringEncoding];
    BOOL result = [[NSFileManager defaultManager] createFileAtPath:filePath contents:fileContents attributes:nil];
    if (result) {
        NSLog(@"[clang] %@",filePath);
    }else{
        NSLog(@"[clang] 文件写入出错");
    }
}

然后在viewDidAppear中调用tmpOrderFile(), (或者在其他事件方法里调用也可以).

在第一个界面出现后 (或者其他事件方法触发并完成后), 下载文件.

iShot2021-03-28 18.28.06

下载的文件右击"显示包内容", 找到 tmp 文件下的 lb.order 文件, 里面就是启动加载的所有项目符号了.

3. 重排启动符号

将 ld.order 文件拷贝到项目目录下;

在 Build Setting 中搜索 order file, 写入 ld.order 文件路径:

order-file

重新执行步骤1, 可查看优化后的效果.

查看符号重排顺序

Build Setting 中搜索 link map, 修改 Write Link Map File 为YES, 运行后到 Products同级目录 Intermediates.noindex 下 找到 ..._ios.build 目录下一个 txt 文件, 搜索 # Symbols:, 就是项目编译的符号顺序, 可以与 ld.order 顺序对比一下, 相同则说明有效果.

iShot2021-03-28 19.49.35

4. 优化后 缺页中断耗时

502-98

这个项目规划良好,所以优化空间没有很大.

缺页中断速度提升了 32.44%;总体速度加快46.34ms,提升29.32%。


Jerod
47 声望4 粉丝