背景介紹
性能測(cè)試是 SDK 發(fā)版的重要依據(jù),VolcRTC 的業(yè)務(wù)方對(duì)于性能指標(biāo)都比較重視,對(duì)于 RTC 準(zhǔn)入有明確的準(zhǔn)入標(biāo)準(zhǔn)。因此我們建立了線下的性能自動(dòng)化測(cè)試系統(tǒng),測(cè)試過程中我們發(fā)現(xiàn) VolcRTC 的內(nèi)存占用較高存在較大的優(yōu)化空間。某個(gè)版本 1v1 語音通話 VolcRTC 1v1 語音通話內(nèi)存占用:
占用的資源 | Memory[MB] |
Android 高端機(jī) | 17.87 |
Android 中端機(jī) | 17.58 |
Android 低端機(jī) | 16.06 |
iOS 高端機(jī) | 6.19 |
iOS 中端機(jī) | 6.52 |
iOS 低端機(jī) | 5.73 |
為了實(shí)現(xiàn)內(nèi)存優(yōu)化,首先需要理清兩個(gè)問題:
- 哪些模塊消耗多少內(nèi)存?
- 如何優(yōu)化?
內(nèi)存組成
在回答以上兩個(gè)問題之前,我們先了解下內(nèi)存的主要組成部分有哪些。
在 Android 系統(tǒng)上,內(nèi)存主要分為:
下圖紅框部分為 VolcRTC 通話過程
- Java Heap,從 Java 代碼分配的對(duì)象;通話過程中 Java 內(nèi)存的分布曲線,主要呈鋸齒狀的周期性變化。結(jié)合 VolcRTC 的業(yè)務(wù)特點(diǎn),可以知道這部分內(nèi)存主要在 JNI 調(diào)用時(shí)分配臨時(shí)對(duì)象,累計(jì)到一定程度后由系統(tǒng)的 GC 機(jī)制回收。
- Native Heap,從 C 或 C++ 代碼分配的對(duì)象。這部分為 VolcRTC 主要內(nèi)存占用。
- Code,用于處理代碼和資源(如 dex 字節(jié)碼、經(jīng)過優(yōu)化或編譯的 dex 代碼、.so 庫(kù)和字體)的內(nèi)存。VolcRTC 庫(kù)所占用內(nèi)存,但不等于動(dòng)態(tài)庫(kù)的包大小,主要原因在于代碼段是按需分頁加載的,所以部分代碼不會(huì)被加載到內(nèi)存。VolcRTC 是一個(gè)動(dòng)態(tài)庫(kù),因此 Code 的內(nèi)存也是在通話過程中主要部分。
優(yōu)化方向
根據(jù)上文的初步分析,可以確定 VolcRTC 的內(nèi)存占用主要分布在 Native Heap 與 Code 段。因此我們明確大體的優(yōu)化方向?yàn)椋?/p>
- Native 內(nèi)存優(yōu)化
- 動(dòng)態(tài)庫(kù)包體優(yōu)化
內(nèi)存歸因分析
哪些模塊如何消耗多少內(nèi)存?
- 內(nèi)存分配堆棧信息
- 按模塊歸因
Heapprofd 實(shí)現(xiàn)原理
- hook malloc、calloc、realloc、free 等內(nèi)存分配相關(guān)的函數(shù)
- 拷貝寄存器與棧內(nèi)存,存儲(chǔ)到共享內(nèi)存,用于?;厮?/li>
- 根據(jù)堆棧信息聚類生成 Trace 文件
模塊歸因
VolcRTC 歸因規(guī)則
VolcRTC 主要分為底層媒體引擎與上層 RTC SDK 兩部分。媒體引擎的整體架構(gòu)是以流水線(Pipeline)的形式組成的,每個(gè) Pipeline 由實(shí)現(xiàn)不同功能的 Node 構(gòu)成。我們可以根據(jù)相關(guān)的命名空間進(jìn)行堆棧過濾,再根據(jù)軟件分層架構(gòu)進(jìn)行層層歸因。
純系統(tǒng)堆棧
VolcRTC 引起的系統(tǒng)堆棧內(nèi)存分配,堆棧不包含 VolcRTC 符號(hào)信息,無法按前述規(guī)則歸類,需要?dú)w類到由 VolcRTC 引起的系統(tǒng)內(nèi)存分配。
歸因示例
內(nèi)存分配堆棧特征一般為棧底為??__pthread_start(void*)?
?,棧頂為內(nèi)存存分配方法,中間為 VolcRTC 堆棧信息。根據(jù)堆棧信息,結(jié)合歸因規(guī)則然后層層向上歸因,形成一個(gè)樹狀的結(jié)構(gòu),準(zhǔn)確分析每一個(gè) Pipeline、每一個(gè) Node、每一個(gè)類型的對(duì)象所占用的內(nèi)存大小。
碰到的問題
Hook malloc 得到的內(nèi)存大小與 Native Heap 大小不一致
malloc 向內(nèi)存分配器申請(qǐng)的內(nèi)存,跟程序運(yùn)行時(shí)傳入的 size 一致。
內(nèi)存分配器向操作系統(tǒng)申請(qǐng)的內(nèi)存按頁分配,一般每頁為 4K,Native Heap 統(tǒng)計(jì)的是這部分的內(nèi)存大小。
由于內(nèi)存分配器的需要反復(fù)分配與釋放內(nèi)存,不可避免的產(chǎn)生內(nèi)存空隙也就是內(nèi)存碎片,另外內(nèi)存分配器會(huì)緩存一部分小內(nèi)存塊以提升內(nèi)存分配效率。
語音通話內(nèi)存分析
通過性能自動(dòng)化測(cè)試工具,生成分析報(bào)告?;诜治鰣?bào)告我們繪制語音通話內(nèi)存全景圖,再通過全景圖識(shí)別出內(nèi)存占用較高的幾個(gè)模塊,指引優(yōu)化方向。
內(nèi)存優(yōu)化
編譯優(yōu)化
包大小會(huì)直接影響到內(nèi)存大小,因此優(yōu)化包大小也可以有效減少內(nèi)存大小。通過打開 LTO、Oz 等編譯選項(xiàng),結(jié)合線下性能自動(dòng)化測(cè)試評(píng)估是否對(duì)性能指標(biāo)有負(fù)面影響來決定需要開啟的編譯優(yōu)化選項(xiàng)。編譯優(yōu)化后 Android 端動(dòng)態(tài)庫(kù)包體減少了 900KB,通話過程內(nèi)存優(yōu)化 850KB 左右。
按需動(dòng)態(tài)分配
VolcRTC 作為一個(gè)通用功能的 SDK,對(duì)于每個(gè)特定場(chǎng)景會(huì)有很多冗余邏輯與功能,這些邏輯與功能都存在較多的預(yù)分配內(nèi)存。對(duì)應(yīng)的優(yōu)化方案是:
- 合理代碼組件化,將不同的功能抽象成組件做到靈活組裝與按需加載,如:AI 降噪功能內(nèi)置的數(shù)據(jù)和模型會(huì)占用較大的內(nèi)存空間。
- 內(nèi)存盡量按需動(dòng)態(tài)分配。如:AEC 回聲消除在不同場(chǎng)景下有不同的算法,需要根據(jù)實(shí)際的場(chǎng)景按需分配內(nèi)存,減少過多的內(nèi)存預(yù)分配。
設(shè)置合理的緩存大小
不合理的緩存大小也會(huì)引起不必要的內(nèi)存浪費(fèi)。通過內(nèi)存的歸因分析,結(jié)合不同場(chǎng)景的業(yè)務(wù)特性,設(shè)置更加合理的緩存大小,可以減少內(nèi)存占用。例如:RTC 采用了 RTP 包重傳機(jī)制來對(duì)抗網(wǎng)絡(luò)丟包,為了實(shí)現(xiàn)重傳機(jī)制,需要緩存一定數(shù)量的包,緩存的數(shù)量需要跟進(jìn)幀長(zhǎng)、實(shí)時(shí)性要求等業(yè)務(wù)特性來設(shè)置合理的值。
合理的算法和數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)
合理的算法和數(shù)據(jù)結(jié)構(gòu)也可以有效降低內(nèi)存。在保證計(jì)算準(zhǔn)確性的前提下,通過減少數(shù)據(jù)值域范圍,使用內(nèi)存空間占用更小的數(shù)據(jù)類型來實(shí)現(xiàn)算法,比如統(tǒng)計(jì)與時(shí)間相關(guān)的數(shù)據(jù)時(shí)使用相對(duì)時(shí)間而非絕對(duì)時(shí)間、空間音頻算法通過定點(diǎn)化使用??short?
?類型代替浮點(diǎn)型數(shù)據(jù)。另外數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)時(shí)需要考慮內(nèi)存對(duì)齊問題。
優(yōu)化效果
1v1 語音通話
占用的資源 | 優(yōu)化前 Memory[MB] | 優(yōu)化后 Memory[MB] |
Android 高端機(jī) | 17.87 | 13.59 |
Android 中端機(jī) | 17.58 | 13.98 |
Android 低端機(jī) | 16.06 | 12.93 |
iOS 高端機(jī) | 6.19 | 3.87 |
iOS 中端機(jī) | 6.52 | 3.84 |
iOS 低端機(jī) | 5.73 | 3.14 |
本次內(nèi)存優(yōu)化,我們探索了 RTC 場(chǎng)景下性能歸因分析驅(qū)動(dòng)性能優(yōu)化的實(shí)踐。可以總結(jié)出以下經(jīng)驗(yàn):
- 構(gòu)造穩(wěn)定的測(cè)試用例
- 建立性能折損的數(shù)據(jù)歸因模型
- 基于歸因模型識(shí)別熱點(diǎn)性能問題,形成優(yōu)化方案
- 從 1v1 通話開始分析,然后逐步到多人、百人千人。