Android 爛裝置想跑人臉辨識3 – 系統優化:ZRAM, SWAP, KSM 擴張最大值

上一篇把高通 BSP 預載的無用系統服務一陣亂砍,開機常駐從 1,300MB 降到 780MB,好不容易搶回 520MB 實體記憶體。

但現場的小學數學題依舊殘酷得讓人想笑:

  1,900 MB (可用實體 RAM,已扣除基頻保留區)
-   780 MB (剪裁後的 Android 10 系統常駐)
-   950 MB (RGB 1080p + ToF 雙相機 ION/DMA 緩衝區)
------------------------------------------------------
    170 MB (留給人臉辨識主程式的全部空氣)

170MB 看似大於零,但在工業級即時辨識系統面前,這點空間比免洗紙杯還薄。人臉特徵庫載入吃 45MB、模型推論權重吃 60MB、幾百個人臉追蹤歷史結構體再吃個 30MB,記憶體水位瞬間推到懸崖邊緣。只要演算法在瞬時並行時多要兩塊臨時緩衝區,Android 的底層屠夫 LMKD 就會提著大刀衝出來把你的主程式當場處決。

硬體焊死在 PCB 板上,老闆不可能加錢換 3GB RAM。

這時候,很多工程師腦子裡會浮現出 Linux 核心的三大擴張黑魔法:KSM(記憶體合併)、傳統 Flash SWAP(硬碟交換)、以及 ZRAM(記憶體壓縮交換)。

在 4 核心弱 CPU 的工控板子上,亂開核心功能不叫最佳化,叫自尋死路。這篇就來記錄我們在 QM215 上摸爬滾打,把這三大黑魔法翻個底朝天的現場真相。


1. 物理鐵律:為什麼 DMA / ION 緩衝區碰都不能碰?

在動核心參數前,先建立一個不可動搖的物理底線:

很多新人以為開了 ZRAM 或 SWAP,相機畫面和 AI 模型的記憶體就能跟著被壓縮收納。在 Linux 核心層,這純屬幻想。

  • 硬體 DMA 的本質:相機 ISP 吐出的 YUV420 影像、ToF 感測器的 RAW12 深度圖,都是直接透過 ION / DMA-BUF 機制向底層申請的連續實體頁面(Contiguous Physical Pages)。
  • 驅動層釘死(Pinned Pages):為了讓 ISP 與 GPU 能以最高頻寬直接寫入,驅動層會把這些頁面鎖死在實體記憶體中,標記為不可移動、不可分頁的 Non-LRU 頁面。
  • 核心無法介入壓縮:Linux 核心的 Swap/ZRAM 機制只能處理使用者空間的匿名頁面(Anonymous Pages)。核心根本沒有權限把硬體驅動釘死的 DMA-BUF 抓去壓縮。

這宣告了一條死線:雙相機與 AI 推理佔用的 950MB 實體 RAM 是物理硬成本,雷打不動。

我們能動手術的目標,永遠只有旁邊那些 Android 系統背景服務的匿名頁面。我們的戰略很純粹:用極微小的 CPU 代價,把系統服務的冷資料壓縮打包,騰出純淨的實體 RAM 留給 30fps 人臉辨識戰場。


2. KSM(Kernel Samepage Merging):直接進焚化爐

核心有另一個看似甜美的技術叫 KSM。它透過背景行程掃描匿名記憶體,把內容完全相同的記憶體頁合併成一個唯讀頁面(COW)。

在板載僅 2GB RAM(被相機 Carveout 與系統吃掉後,可用實體空間不足 1GB)的門禁機上,KSM 是徹底的災難:

  1. CPU 偷竊者:ksmd 背景執行緒每秒都在瘋狂雜湊與比對記憶體頁,在 Cortex-A53 四核晶片上,它隨隨便便就能吃掉 15%~25% 的單核算力。
  2. Copy-On-Write 延遲地雷:一旦被合併的頁面被寫入,系統立刻引發 Page Fault 並配置新頁面。在 30fps 即時管線旁邊發生這種中斷,相機影格直接掉給你看。

決策:核心編譯直接拔除 CONFIG_KSM,連一行背景掃描代碼都不准跑。


3. SWAP:在 eMMC 上挖坑自焚的愚蠢行為

也有人問:「既然 RAM 不夠,在 Android 底下的 eMMC 分割區掛一個 1GB 的 SWAP 檔不就解決了?」

如果你嫌硬體壽命太長,可以這麼做:

  • eMMC 寫入放大與閃存死亡:門禁機用的是廉價 TLC/QLC eMMC,頻繁的 SWAP 換頁會在三到六個月內徹底燒穿快閃記憶體區塊。
  • I/O 阻塞致死:當系統發生 Page Fault 去 eMMC 讀取交換資料時,I/O Wait 會瞬間飆破 80%。UI 介面完全凍結,人臉辨識逾時失敗。

底線:非揮發性儲存裝置上嚴禁開啟任何磁碟 SWAP。


4. ZRAM:弱 CPU 裝置上的克制藝術

既然磁碟不能動,唯一的解法就是 ZRAM。

ZRAM 是一塊核心虛擬出的 RAM 區塊。當系統匿名頁面不活躍時,核心會把資料「壓縮」後放進這塊記憶體,對外偽裝成 SWAP 裝置。用 CPU 運算時間來換取實體記憶體空間。

但在弱 CPU 裝置上玩 ZRAM,關鍵在於克制:

壓縮演算法:非 LZ4 莫屬

  • LZO:壓縮比普通,CPU 負載偏高。
  • ZSTD:壓縮比極高,但在弱 CPU 上解壓縮開銷過大,容易拖垮主執行緒。
  • LZ4:壓縮比約 2.6:1,但解壓縮速度極快、指令開銷極低。對於維持 30fps 的系統,LZ4 是唯一及格的演算法。

大小配置:256MB 的黃金平衡

ZRAM 不是越大越好。在扣除相機與系統硬體保留區後、實體可用 RAM 不足 1GB 的機器上:

  • 設 512MB:壓縮後需要佔用約 200MB 的實體記憶體作為儲存池,實體可用 RAM 反而縮水,引發頻繁換頁。
  • 最佳配比:256MB。它能為系統吃下約 600MB 的冷資料,而壓縮後僅佔用約 100MB 實體 RAM。
[ZRAM 核心關鍵設定]
/vendor/etc/fstab.qcom:
/dev/block/zram0 none swap defaults zramsize=268435456

/init.qcom.rc:
# 靈魂參數:徹底關閉 Swap 預讀(RAM 沒有機械尋找時間,預讀只會增加無效解壓縮負擔)
write /proc/sys/vm/page-cluster 0
# 設定換出積極度(設為 60,平衡回收與 CPU 負載)
write /proc/sys/vm/swappiness 60

5. 實測戰果與邊界

實作上線後,透過 adb shell dumpsys meminfo 抓取真實數據:

  • ZRAM:256MB 實體空間吞進了約 660MB 系統冷頁面。
  • Free RAM:全機可用實體記憶體安全維持在 360MB 以上。
  • CPU 開銷:背景閒置 CPU 佔用低於 2%,A53 四核心全速待命處理神經網路推論。

硬體邊界認清了,舞台才算真正清乾淨。

下一篇,我們直面 Android 最嗜血的看門狗:LMKD(Low Memory Killer Daemon)。看看在記憶體見底的最後關頭,如何透過底層 PSI 探測與 AOSP 殺手邏輯,保證人臉辨識主程式永遠免死。

感謝觀看,收工。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *