性能優(yōu)化 - 啟動優(yōu)化 (冷啟動)

啟動階段: Main 之前 ,Main之后

查看啟動時間

增加環(huán)境變量 Edit schmene -> Arguments -> Environment Variables -> DYLD_PRINT_STATISTICS

         dylib loading time: 348.01 milliseconds (26.0%)  // 動態(tài)庫加載時間
        rebase/binding time: 222.60 milliseconds (16.6%)  // rebase:修正偏移指針ASLR  binding:fishHook 里面的符號綁定       減少OC類 
            ObjC setup time:  44.97 milliseconds (3.3%)   // runtime類注冊
           initializer time: 721.15 milliseconds (53.9%)  // Load方法加載 (優(yōu)化 懶加載 可以放在initializer)
           slowest intializers :
             libSystem.B.dylib :   4.55 milliseconds (0.3%)
          libglInterpose.dylib : 435.84 milliseconds (32.6%) //調(diào)試相關(guān)
             marsbridgenetwork :  46.28 milliseconds (3.4%)
                        WeChat : 259.34 milliseconds (19.4%)  //主程序

Main階段 優(yōu)化常規(guī)方式

  1. 懶加載
  2. 發(fā)揮CPU價值 (多線程進(jìn)行初始化)
  3. 啟動頁少用XIB和Strotboard

Main 函數(shù)之后

main函數(shù) 到 第一個界面 使用工具類BLStopwatch 查看 在Application 打點(diǎn)開始 在第一個界面 打點(diǎn)結(jié)束

打點(diǎn)信息可以看到哪個方法比較耗時 就優(yōu)化哪個函數(shù)

Main函數(shù)之前

需要只是點(diǎn) 虛擬內(nèi)存 、 物理內(nèi)存 fishHook 、 Clang Hook所有方法、二進(jìn)制重排

二進(jìn)制重排 原理

15873095584658.jpg

Page Fault:當(dāng)進(jìn)程訪問一個虛擬內(nèi)存Page而對應(yīng)的物理內(nèi)存卻不存在時,會觸發(fā)一次缺頁中斷(Page Fault),需要操作系統(tǒng)將其調(diào)入主存后再進(jìn)行訪問

Page Fault 為什么會出現(xiàn):一個可執(zhí)行文件可能很大,放在磁盤上,由局部性原理一次只將其中一部分讀進(jìn)內(nèi)存

Instruments 調(diào)試工具 System Trace 查看Main函數(shù)之前耗時

File Backed Page In(Page Fault) 耗時

重排核心問題

  1. 重排效果怎么樣 - 獲取啟動階段的page fault次數(shù)
  2. 重排成功了沒 - 拿到當(dāng)前二進(jìn)制的函數(shù)布局
  3. 如何重排 - 讓鏈接器按照指定順序生成Mach-O
  4. 重排的內(nèi)容 - 獲取啟動時候用到的函數(shù)

1、重排效果怎么樣 - 獲取啟動階段的page fault次數(shù)

Instruments 調(diào)試工具 System Trace 查看Main函數(shù)之前耗時

File Backed Page In(Page Fault) 耗時

2、重排成功了沒 - 獲取當(dāng)前二進(jìn)制的函數(shù)布局

查看 函數(shù)執(zhí)行順序
Build Setting -> link Map File 設(shè)置為YES
查看 文件
/Intermediates.noindex/TraceDemo.build/Debug-iphonesimulator/TraceDemo.build/TraceDemo-LinkMap-normal-x86_64.txt

3、如何重排 - 讓鏈接器按照指定順序生成Mach-O

生成 .Order 文件 放根目錄 里面放著函數(shù)執(zhí)行順序
Build Setting -> Order File 設(shè)置.Order 文件

4、重排的內(nèi)容 - 獲取啟動時候用到的函數(shù)

使用 clang 生成 order文件

插莊 clang HOOK所有方法

http://clang.llvm.org/docs/SanitizerCoverage.html 官網(wǎng)

OC 配置 
build Setting -> other c Flags  -fsanitize-coverage=func,trace-pc-guard
Swift 配置
build Setting ->  other  swift flags 加入以下兩個配置
-sanitize-coverage=func 
-sanitize=undefined

需要實(shí)現(xiàn) 一下函數(shù)
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("INIT: %p %p\n", start, stop);
  for (uint32_t *x = start; x < stop; x++)
    *x = ++N;  // Guards should start from 1.
}

// This callback is inserted by the compiler on every edge in the
// control flow (some optimizations apply).
// Typically, the compiler will emit the code like this:
//    if(*guard)
//      __sanitizer_cov_trace_pc_guard(guard);
// But for large functions it will emit a simple call:
//    __sanitizer_cov_trace_pc_guard(guard);
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
  if (!*guard) return;  // Duplicate the guard check.
  // If you set *guard to 0 this code will not be called again for this edge.
  // Now you can get the PC and do whatever you want:
  //   store it somewhere or symbolize it and print right away.
  // The values of `*guard` are as you set them in
  // __sanitizer_cov_trace_pc_guard_init and so you can make them consecutive
  // and use them to dereference an array or a bit vector.
  void *PC = __builtin_return_address(0);
  char PcDescr[1024];
  // This function is a part of the sanitizer run-time.
  // To use it, link with AddressSanitizer or other sanitizer.
  __sanitizer_symbolize_pc(PC, "%p %F %L", PcDescr, sizeof(PcDescr));
  printf("guard: %p %x PC %s\n", guard, *guard, PcDescr);
}

每個函數(shù)、方法 、load、initialize、block 都會先調(diào)用__sanitizer_cov_trace_pc_guard 函數(shù)

利用 clang 生成 .order文件

使用 clang 獲取 執(zhí)行過的函數(shù) 、 方法 之后生成.oder文件

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

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    NSMutableArray <NSString *> * symbolNames = [NSMutableArray array];
    
    while (YES) {
        SYNode * node = OSAtomicDequeue(&symbolList, offsetof(SYNode, 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];
        [symbolNames addObject:symbolName];
    }
    //取反
    NSEnumerator * emt = [symbolNames reverseObjectEnumerator];
    //去重
    NSMutableArray<NSString *> *funcs = [NSMutableArray arrayWithCapacity:symbolNames.count];
    NSString * name;
    while (name = [emt nextObject]) {
        if (![funcs containsObject:name]) {
            [funcs addObject:name];
        }
    }
    //干掉自己!
    [funcs removeObject:[NSString stringWithFormat:@"%s",__FUNCTION__]];
    //將數(shù)組變成字符串
    NSString * funcStr = [funcs  componentsJoinedByString:@"\n"];
    
    NSString * filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"hank.order"];
    NSData * fileContents = [funcStr dataUsingEncoding:NSUTF8StringEncoding];
    [[NSFileManager defaultManager] createFileAtPath:filePath contents:fileContents attributes:nil];
    NSLog(@"%@",funcStr);
}

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("INIT: %p %p\n", start, stop);
  for (uint32_t *x = start; x < stop; x++)
    *x = ++N;  // Guards should start from 1.
}

//原子隊(duì)列
static  OSQueueHead symbolList = OS_ATOMIC_QUEUE_INIT;
//定義符號結(jié)構(gòu)體
typedef struct {
    void *pc;
    void *next;
}SYNode;

void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
//    if (!*guard) return;  // Duplicate the guard check.
    /*  精確定位 哪里開始 到哪里結(jié)束!  在這里面做判斷寫條件!*/
    
    void *PC = __builtin_return_address(0);
    SYNode *node = malloc(sizeof(SYNode));
    *node = (SYNode){PC,NULL};
    //進(jìn)入
    OSAtomicEnqueue(&symbolList, node, offsetof(SYNode, next));
 //   Dl_info info;
//   dladdr(node->pc, &info);
//    printf("fname:%s \nfbase:%p \nsname:%s \nsaddr:%p\n",
//           info.dli_fname,
//           info.dli_fbase,
//           info.dli_sname,
//           info.dli_saddr);
//
}

注意 : 生成.order文件之后需要刪除clang相關(guān)代碼

參考: 字節(jié)跳動技術(shù)團(tuán)隊(duì)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容