iOS 性能探究

一克胳、什么是 App性能?性能指標是什么圈匆?

一般來講漠另,性能問題雖然不會導致 App不可用,但會影響用戶體驗臭脓,用戶只關心產(chǎn)品體驗問題,如果說性能問題不斷累計達到臨界點腹忽,就會爆發(fā)出一些不可估計的問題来累,要么任性地卡頓運行砚作,要么就低調地崩潰,最后導致用戶用著不開心嘹锁,開發(fā)者也比較煩惱葫录,如果是這樣的話,你在這家公司的可能就呆不長時間了领猾。
性能指標:主要集中在 CPU 使用率米同、FPS 的幀率和內存這三個方面

二、為什么需要考慮 App性能呢摔竿?

做任何一件事情面粮,我們都必須追求完美,做開發(fā)也是一樣的继低,我們都需要站在用戶角度和公司利益出發(fā)去考慮問題熬苍,去工作,去打造一款在市場上能站穩(wěn)腳的 App袁翁。一款好的 App柴底,不僅僅是要抓住了用戶需求,還需要完美的體驗粱胜,視覺的沖擊柄驻,得讓用戶用起來舒服,有種愛不釋手的感覺焙压,“阿哈”的感覺鸿脓。(“阿哈”解釋于《黑客增量》一書中)
把自己往大牛的路上逼,我要當大牛

三冗恨、如何對 App做性能監(jiān)測答憔?

為了主動、高效地發(fā)現(xiàn)性能問題掀抹,避免 App 質量進入無人監(jiān)管的失控狀態(tài)虐拓,我們需要對App 的性能進行監(jiān)控,目前傲武,對 App 的性能監(jiān)控蓉驹,主要是分為線下和線上兩個方面。

線下性能監(jiān)控:Instruments

Instruments是由蘋果官方力推的唯一一款線下性能監(jiān)控揪利,性能分析工具态兴,集成于xcode,功能非常強大疟位,比如說Leaks專門用于監(jiān)控內存泄露問題的瞻润,Network專門用于檢查網(wǎng)絡情況的,Time Profiler對程序命令執(zhí)行時間的采樣來分析頁面卡頓問題的,等等......

  • Instruments 可以實現(xiàn)下面功能:
    1.分析一個或多個進程的行為绍撞。
    2.記錄一系列用戶的動作并響應它們正勒,可靠的再現(xiàn)這些事件并收集多次運行的數(shù)據(jù)。
    3.創(chuàng)建你自己自定義的 DTrace instruments 來分析系統(tǒng)和應用程序的行為傻铣。
    4.保存用戶界面記錄和instruments的配置為模板章贞,并從Xcode里面訪問。
    5.追查代碼中難以重現(xiàn)的問題非洲。
    6.對你的程序進行性能分析鸭限。
    7.自動化測試你的代碼。
    8.對你程序進行壓力測試两踏。
    9.進行一般的系統(tǒng)級故障診斷败京。
    10.對你的代碼如何工作有更深入的了解。

  • 啟動Instruments:


    image.png

    啟動后的界面:


    啟動后的界面.png
  • Instruments創(chuàng)建跟蹤文檔:
    1.Blank:創(chuàng)建一個空的模板缆瓣,你可以自定義的添加各種工具喧枷。
    2.Activity Monitor:可以只用這個模板,研究系統(tǒng)工作負載和虛擬內存大小的關系
    3.Allocations:將Allocations和VM跟蹤器加到跟蹤文檔中弓坞,使用該工具可以監(jiān)視內存和對象的內存分配方式和情況隧甚。
    4.CocoaLayout:是一種Cocoa布局工具 ,可以應用于iOS模擬器和Cocoa桌面應用渡冻,但是不能和連接的iOS設備一起使用戚扳。觀察NSLayoutConstraint對象的改變,幫助我們判斷什么時間什么地點的constraint是否合理
    5.Core Animation:將CoreAnimation加入到跟蹤文檔中族吻,可以測量ios設備上每秒的CoreAnimation幀數(shù)帽借,這可以幫助你理解內容是如何渲染到屏幕上的
    6.CoreData:將CoreData數(shù)據(jù)提取、緩存缺失和存儲加入到跟蹤文檔中超歌,使用這個工具可以檢測應用程序中數(shù)據(jù)的存儲交互砍艾。
    7.Counters:收集使用時間或基于事件的抽樣方法的性能監(jiān)控計數(shù)器(PMC)事件。
    8.Energy Log:耗電量監(jiān)控巍举,將Energy Diagnostics, CPU Activity, Display Brightness, Sleep/Wake, Bluetooth, WiFi, and GPS instruments加入到跟蹤文檔中進行檢測脆荷。
    9.File Activity:將File Activity, Reads/Writes, File Attributes, and Directory I/O instruments 加入到跟蹤文檔中,只用這個模板可以讓你檢查系統(tǒng)文件的使用情況懊悯,可以檢查文件的打開蜓谋、關閉、讀和寫操作炭分,同時也可以檢測文件系統(tǒng)本身的改變桃焕,包括權限和所有權發(fā)生的改變。
    10.Leaks:將the Allocations and Leaks instruments加入到模板中捧毛,使用這個模板可以幫助你檢測內存的泄漏观堂。
    11.Metal System Trace:它是是apple 2014年在ios平臺上推出的高效底層的3D圖形API让网,它通過減少驅動層的API調用CPU的消耗提高渲染效率。
    12.Network:用鏈接工具分析你的程序如何使用TCP/IP和UDP/IP鏈接师痕。
    13.OpenGL ES Analysis:將OpenGL ES Analyzer and OpenGL ES Driver加入到模板中寂祥,這個模塊測量分析OpenGL ES活動正確性檢測以及表現(xiàn)問題,提供解決建議七兜。
    14.System Trace:系統(tǒng)跟蹤,通過顯示當前被調度線程提供綜合的系統(tǒng)表現(xiàn)福扬,顯示從用戶到系統(tǒng)的轉換代碼通過兩個系統(tǒng)調用或內存操作腕铸。
    15.System Usage:這個模板記錄關于文件讀寫,sockets铛碑,I/O系統(tǒng)活動狠裹, 輸入輸出。
    16.Time Profile:執(zhí)行對系統(tǒng)的CPU上運行的進程低負載時間為基礎采樣汽烦。
    17.Zombies:測量一般的內存使用涛菠,專注于檢測過度釋放的野指針對象,也提供對象分配統(tǒng)計撇吞,以及主動分配的內存地址歷史俗冻。

  • 跟蹤文檔舉例:如Leaks


    Leaks

    Leaks圖中數(shù)字說明說明:
    1.開始錄制/暫停
    2.call tree的官方解釋:Separate By Thread(線程分離,只有這樣才能在調用路徑中能夠清晰看到占用CPU最大的線程.)
    3.Invert Call Tree 的意思是翻轉調用樹,直接定位到內存泄漏的那個方法牍颈,如果不勾選會顯示最外層的函數(shù)迄薄,我們還要手動展開≈笏辏可以點選看下效果
    Hide System Libraries 的意思很明顯了讥蔽。就是隱藏系統(tǒng)類庫,避免一些莫名其妙的画机,我們無法改動的信息迷惑我們冶伞。
    4.刷新間隔時間
    5.雙擊第三行定位到具體泄漏的函數(shù),泄漏的字節(jié)數(shù)都標出來了步氏,如此強大响禽,定位到泄漏代碼,加以更改后就可以了
    如下圖舉例:


    image.png
  • 功能簡介:


    Instruments功能簡介圖

其中常用的有以下幾種工具:

Allocations戳护,
Leaks金抡,
Zombies,
Core Animation腌且,
Automation梗肝,
Time Profiler,
Cocoa Layout铺董,
Energy Diagnostics巫击,
Network
  • 自定義Instruments
    除了對各種性能問題進行監(jiān)控外禀晓,最新版本的 Instruments 10 還有以下兩大優(yōu)勢:
    1.Instruments 基于 os_signpost 架構,可以支持所有平臺坝锰。
    2.Instruments 由于標準界面(Standard UI)和分析核心(Analysis Core)技術粹懒,使得我們可以非常方便地進行自定義性能監(jiān)測工具的開發(fā)。當你想要給 Instruments 內置的工具換個交互界面顷级,或者新創(chuàng)建一個工具的時候凫乖,都可以通過自定義工具這個功能來實現(xiàn)。

開發(fā)一款自定義 Instruments 工具弓颈,主要包括以下這幾個步驟:
a.在 Xcode 中帽芽,點擊 File > New > Project;
b.在彈出的 Project 模板選擇界面翔冀,將其設置為 macOS导街;
c.選擇 Instruments Package,點擊后即可開始自定義工具的開發(fā)了
點擊查看詳情

線上性能監(jiān)控:CPU 使用率纤子、FPS 的幀率和內存

需要考慮兩個原則:
a.監(jiān)控代碼不要侵入到業(yè)務代碼中搬瑰。
b.采用性能消耗最小的監(jiān)控方案。

CPU 使用率的線上監(jiān)控方法

App 作為進程運行起來后會有多個線程控硼,每個線程對 CPU 的使用率不同泽论。各個線程對 CPU 使用率的總和,就是當前 App 對 CPU 的使用率卡乾。明白了這一點以后佩厚,我們也就摸清楚了對 CPU 使用率進行線上監(jiān)控的思路。

在 iOS 系統(tǒng)中说订,你可以在 usr/include/mach/thread_info.h 里看到線程基本信息的結構體抄瓦,其中的 cpu_usage 就是 CPU 使用率。結構體的完整代碼如下所示:

struct thread_basic_info {
    time_value_t user_time;  // 用戶運行時長
    time_value_t system_time;  // 系統(tǒng)運行時長
    integer_t  cpu_usage;  // CPU 使用率
    policy_t policy; // 調度策略
    integer_t  run_state;  // 運行狀態(tài)
    integer_t  flags;  // 各種標記
    integer_t  suspend_count; // 暫停線程的計數(shù)
    integer_t  sleep_time; // 休眠的時間
};

因為每個線程都會有這個 thread_basic_info 結構體陶冷,所以接下來的事情就好辦了钙姊,你只需要定時(比如,將定時間隔設置為 2s)去遍歷每個線程埂伦,累加每個線程的 cpu_usage 字段的值煞额,就能夠得到當前 App 所在進程的 CPU 使用率了。實現(xiàn)代碼如下:

+ (integer_t)cpuUsage {
    thread_act_array_t threads; //int 組成的數(shù)組比如 thread[1] = 5635
    mach_msg_type_number_t threadCount = 0; //mach_msg_type_number_t 是 int 類型
    const task_t thisTask = mach_task_self();
    // 根據(jù)當前 task 獲取所有線程
    kern_return_t kr = task_threads(thisTask, &threads, &threadCount);
    if (kr != KERN_SUCCESS) {
        return 0;
    }
    integer_t cpuUsage = 0;
    // 遍歷所有線程
    for (int i = 0; i < threadCount; i++) {
        thread_info_data_t threadInfo;
        thread_basic_info_t threadBaseInfo;
        mach_msg_type_number_t threadInfoCount = THREAD_INFO_MAX;
        if (thread_info((thread_act_t)threads[i], THREAD_BASIC_INFO, (thread_info_t)threadInfo, &threadInfoCount) == KERN_SUCCESS) {
            // 獲取 CPU 使用率
            threadBaseInfo = (thread_basic_info_t)threadInfo;
            if (!(threadBaseInfo->flags & TH_FLAGS_IDLE)) {
                cpuUsage += threadBaseInfo->cpu_usage;
            }
        }
    }
    assert(vm_deallocate(mach_task_self(), (vm_address_t)threads, threadCount * sizeof(thread_t)) == KERN_SUCCESS);
    return cpuUsage;
}

在上面這段代碼中沾谜,task_threads 方法能夠取到當前進程中的線程總數(shù) threadCount 和所有線程的數(shù)組 threads膊毁。
接下來,我們就可以通過遍歷這個數(shù)組來獲取單個線程的基本信息基跑。其中婚温,線程基本信息的結構體是 thread_basic_info_t,這個結構體里就包含了我們需要的 CPU 使用率的字段 cpu_usage媳否。然后栅螟,我們累加這個字段就能夠獲取到當前的整體 CPU 使用率荆秦。
到此,我們就實現(xiàn)了對 CPU 使用率的線上監(jiān)控力图。接下來步绸,我們再看看對 FPS 的線上監(jiān)控方法吧。

FPS 線上監(jiān)控方法

FPS 是指圖像連續(xù)在顯示設備上出現(xiàn)的頻率吃媒。FPS 低瓤介,表示 App 不夠流暢,還需要進行優(yōu)化赘那。
但是惑朦,和前面對 CPU 使用率和內存使用量的監(jiān)控不同,iOS 系統(tǒng)中沒有一個專門的結構體漓概,用來記錄與 FPS 相關的數(shù)據(jù)。但是病梢,對 FPS 的監(jiān)控也可以比較簡單的實現(xiàn):通過注冊 CADisplayLink 得到屏幕的同步刷新率胃珍,記錄每次刷新時間,然后就可以得到 FPS蜓陌。具體的實現(xiàn)代碼如下:

- (void)start {
    self.dLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(fpsCount:)];
    [self.dLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
// 方法執(zhí)行幀率和屏幕刷新率保持一致
- (void)fpsCount:(CADisplayLink *)displayLink {
    if (lastTimeStamp == 0) {
        lastTimeStamp = self.dLink.timestamp;
    } else {
        total++;
        // 開始渲染時間與上次渲染時間差值
        NSTimeInterval useTime = self.dLink.timestamp - lastTimeStamp;
        if (useTime < 1) return;
        lastTimeStamp = self.dLink.timestamp;
        // fps 計算
        fps = total / useTime;
        total = 0;
    }
}
內存使用量的線上監(jiān)控方法

通常情況下觅彰,我們在獲取 iOS 應用內存使用量時,都是使用 task_basic_info 里的 resident_size 字段信息钮热。但是填抬,我們發(fā)現(xiàn)這樣獲得的內存使用量和 Instruments 里看到的相差很大。后來隧期,在 2018 WWDC Session 416 iOS Memory Deep Dive中飒责,蘋果公司介紹說 phys_footprint 才是實際使用的物理內存。

內存信息存在 task_info.h (完整路徑 usr/include/mach/task.info.h)文件的 task_vm_info 結構體中仆潮,其中 phys_footprint 就是物理內存的使用宏蛉,而不是駐留內存 resident_size。結構體里和內存相關的代碼如下:

struct task_vm_info {
    mach_vm_size_t virtual_size;  // 虛擬內存大小
    integer_t region_count;  // 內存區(qū)域的數(shù)量
    integer_t page_size;
    mach_vm_size_t resident_size; // 駐留內存大小
    mach_vm_size_t resident_size_peak; // 駐留內存峰值
    /* added for rev1 */
    mach_vm_size_t phys_footprint;  // 物理內存
}

OK性置,類似于對 CPU 使用率的監(jiān)控拾并,我們只要從這個結構體里取出 phys_footprint 字段的值,就能夠監(jiān)控到實際物理內存的使用情況了鹏浅。具體實現(xiàn)代碼如下:

uint64_t memoryUsage() {
    task_vm_info_data_t vmInfo;
    mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
    kern_return_t result = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
    if (result != KERN_SUCCESS){return 0;}
    return vmInfo.phys_footprint;
}

從以上三個線上性能監(jiān)控方案可以看出嗅义,它們的代碼和業(yè)務邏輯是完全解耦的,監(jiān)控時基本都是直接獲取系統(tǒng)本身提供的數(shù)據(jù)隐砸,沒有額外的計算量之碗,因此對 App 本身的性能影響也非常小,滿足了我們要考慮的兩個原則季希。

說明:
本文內從不做任何商業(yè)用途继控,僅僅本人學習筆記記錄械馆,方便以后回顧

版權參考文章:
極客空間
簡書

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市武通,隨后出現(xiàn)的幾起案子霹崎,更是在濱河造成了極大的恐慌,老刑警劉巖冶忱,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尾菇,死亡現(xiàn)場離奇詭異,居然都是意外死亡囚枪,警方通過查閱死者的電腦和手機派诬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來链沼,“玉大人默赂,你說我怎么就攤上這事±ㄉ祝” “怎么了缆八?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長疾捍。 經(jīng)常有香客問我奈辰,道長,這世上最難降的妖魔是什么乱豆? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任奖恰,我火速辦了婚禮,結果婚禮上宛裕,老公的妹妹穿的比我還像新娘瑟啃。我一直安慰自己,他們只是感情好揩尸,可當我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布翰守。 她就那樣靜靜地躺著,像睡著了一般疲酌。 火紅的嫁衣襯著肌膚如雪蜡峰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天朗恳,我揣著相機與錄音湿颅,去河邊找鬼。 笑死粥诫,一個胖子當著我的面吹牛油航,可吹牛的內容都是我干的。 我是一名探鬼主播怀浆,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼谊囚,長吁一口氣:“原來是場噩夢啊……” “哼怕享!你這毒婦竟也來了?” 一聲冷哼從身側響起镰踏,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤函筋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后奠伪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跌帐,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年绊率,在試婚紗的時候發(fā)現(xiàn)自己被綠了谨敛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡滤否,死狀恐怖脸狸,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情藐俺,我是刑警寧澤炊甲,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站紊搪,受9級特大地震影響,放射性物質發(fā)生泄漏全景。R本人自食惡果不足惜耀石,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望爸黄。 院中可真熱鬧滞伟,春花似錦、人聲如沸炕贵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽称开。三九已至亩钟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鳖轰,已是汗流浹背清酥。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蕴侣,地道東北人焰轻。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像昆雀,于是被迫代替她去往敵國和親辱志。 傳聞我的和親對象是個殘疾皇子蝠筑,可洞房花燭夜當晚...
    茶點故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內容