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

在我們那台只有 2GB RAM、CPU 效能孱弱的嵌入式門禁機上跑人臉辨識,記憶體就像每個月的薪水一樣:永遠不夠用。

開機進入系統,Android 10 的 System Server、SystemUI、各種背景服務一跑起來,直接吃掉將近 1.2GB;剩下的 800MB 還要分給 Camera HAL(雙鏡頭 RGB + ToF)、ION 緩衝區、演算法模型(50MB 權重 + 推理區)。

只要系統記憶體稍微緊繃,LMKD 就會提著大刀在背景四處巡邏,隨時準備把你的主程式當場槍斃。

為了讓 AI 模型能在這台破板子上安穩運行,我們遵循的第一原則始終是:「先求能活著運行,再求效能最佳化」

而在 Linux 核心的世界裡,自古流傳著三大「記憶體擴張黑魔法」:KSM(記憶體合併)傳統 SWAP(硬碟交換)、以及 ZRAM(記憶體壓縮交換)

這篇就來聊聊我們在 2GB 嵌入式設備上,如何踩平這些內核坑洞,找到最極致的平衡點。


0. 硬核前置知識:為什麼 DMA / ION 緩衝區碰都不能碰?

在討論任何記憶體優化之前,必須先建立一個鐵一般的物理認知:

很多人以為開了 ZRAM 或 KSM,相機影像和 AI 模型的記憶體就能跟著縮小。這在 Linux 核心層面完全是不可能的。

  • DMA / ION 的物理本質:相機 ISP 吐出的 YUV 畫面、ToF 深度圖,以及 GPU/NPU 推理所存取的緩衝區,都是透過 ION / DMA-BUF 機制向系統申請的。
  • 驅動層釘死(Pinned Pages):這些緩衝區為了讓硬體晶片(ISP、GPU、NPU)以最高頻寬直接存取,是由驅動層直接鎖定在實體記憶體中的不可移動頁面(Non-LRU 頁面)。
  • 核心無法壓縮:Linux 核心的記憶體回收機制(Swap/ZRAM)只能作用於用戶空間的匿名頁面(Anonymous Pages),它根本沒有機制、也不可能把硬體驅動釘死的 DMA-BUF 丟進 Swap 壓縮。

這代表了一件極度殘酷的事實:相機與 AI 佔用的實體 RAM 是雷打不動的「硬成本」。

我們能優化的對象,從來就不是 AI 或相機本身,而是旁邊那些討人厭的「Android 系統背景服務(System Services)」——我們的任務是把系統服務壓到最小,把寶貴乾淨的實體 RAM 騰出來給 AI 揮霍。


1. Kernel Samepage Merging(KSM)——看似美好的賠本毒藥

KSM 的原理聽起來非常夢幻:核心背景會有個叫 ksmd 的守護行程,定期掃描系統記憶體,只要發現有兩塊 4KB 頁面的內容一模一樣,就把它們合併成同一個頁面,並標記為 Copy-on-Write(寫入時複製),藉此憑空生出記憶體。

聽起來很神對吧?我們實測之後差點沒吐血:

  • 記憶體省了個寂寞:在我們這台門禁機上,KSM 掃了半天,總共只省下了不到 20MB 的記憶體(佔比不足 1%)。因為相機緩衝區不能碰、AI 模型不能碰,系統服務裡真正完全相同的匿名頁面少得可憐。
  • CPU 算力被嚴重偷吃ksmd 為了不斷比對記憶體頁面的 Hash,在我們這顆 1.4GHz 的 4 核心弱 CPU 上,白白吃掉了 5%~10% 的 CPU 使用率

在原本就已經捉襟見肘的 CPU 上,拿 10% 的寶貴算力去換微不足道的 20MB 記憶體,完全是賠本生意。

結論:果斷關閉 KSM,核心編譯時直接拿掉,連一行掃描都不給它跑。


2. 傳統 Swap(eMMC / Flash)——門禁機的慢性自殺

當 RAM 不足時,傳統 Linux 最標準的做法就是切一塊硬碟空間當 Swap,把冷資料換出到硬碟上。

但在 7×24 小時無人看管的嵌入式門禁機上,絕對禁止使用 eMMC / Flash 作為 Swap 分割區

  • Flash 壽命被迅速磨穿(TBW 限制):eMMC 的寫入次數是有限的。門禁機長期運作下,頻繁的 Swap 寫入會在短短幾個月內把板載 eMMC 顆粒寫爛,整台機器直接變磚(無法開機)。
  • 災難級的 I/O 延遲:eMMC 的讀寫延遲動輒幾十毫秒(ms),而 RAM 是奈秒(ns)級。一旦關鍵執行緒踩到 eMMC Swap In,畫面會當場定格卡死,相機直接掉幀。

結論:Android 原廠從未開放 eMMC Swap 是絕對正確的,千萬不要自己自作聰明去掛載 Flash Swap。


3. ZRAM(記憶體壓縮)——弱 CPU 設備上的「克制藝術」

排除掉前面兩條死路,唯一可行的正道就是 ZRAM(壓縮 Swap)

ZRAM 的概念非常巧妙:它在 RAM 內部劃出一塊虛擬區域,當記憶體告急時,核心把較少使用的資料透過 CPU 快速壓縮後塞進這塊區域。需要用時再解壓縮還原,完全不經過慢速的 eMMC。

但使用 ZRAM,必須有極度清醒的「克制思維」

壓縮演算法抉擇:為什麼非 LZ4 不可?

  • ZSTD / GZIP:壓縮率較高(可達 3~4 倍),但解壓縮極度消耗 CPU。在 1.4GHz 弱 CPU 上,解壓縮會直接引發嚴重的 CPU 尖峰。
  • LZ4:壓縮比稍低(約 2.5~3 倍),但解壓縮速度極快、延遲極低。對於 30fps 即時人臉辨識來說,LZ4 是唯一及格的選擇。

容量設定抉擇:為什麼只設 256MB,而不是手機常見的 1GB?

一般手機因為 CPU 效能強悍,動輒開 1GB~2GB 的 ZRAM。但在我們這台 4 核心低階門禁機上,ZRAM 的本質是「用 CPU 算力換記憶體空間」

如果 ZRAM 開得太大,當大量資料頻繁換入換出時,解壓縮的 CPU 開銷會直接搶走 AI 推理和相機 Pipeline 的算力

我們經過壓測發現:

  • 系統中常駐的 System Server、Settings、Launcher 等背景服務,冷資料大約佔用 1.1GB。
  • 透過 LZ4 壓縮後,大約需要 250MB 左右的空間
  • 因此,我們把 ZRAM 精確鎖定在 256MB(268435456 bytes)

這達成了極致的平衡:用最小的 CPU 代價,剛好把系統背景服務壓縮收容進去,擠出約 150~200MB 的乾淨實體 RAM 給相機與 AI,同時保證 CPU 完全不被拖垮。


4. 關鍵配置與實戰程式碼

要讓這套配置在 Android 10 完美落地,需要修改內核配置與系統掛載腳本:

內核設定(Kernel defconfig)

在自編的 Linux 內核中啟用 ZRAM,並果斷關閉 KSM:

# 啟用 Swap 與 ZRAM 核心支援
CONFIG_SWAP=y
CONFIG_ZRAM=y
CONFIG_ZSMALLOC=y

# 啟用現代 Memory Cgroup(注意:舊版 CONFIG_CGROUP_MEM_RES_CTLR 已廢棄)
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y

# 關鍵決策:果斷關閉 KSM,省下 10% CPU 算力
# CONFIG_KSM is not set

fstab 掛載設定

在設備的 fstab 檔案中加入 ZRAM 掛載(注意:參數逗號間嚴禁空格,否則 fs_mgr 會解析失敗導致無法掛載):

# /vendor/etc/fstab.qcom (緊湊無空格)
/dev/block/zram0 none swap defaults zramsize=268435456

init.rc 系統啟動調優

在系統開機腳本中掛載 Swap,並下達最關鍵的性能優化參數:

# 1. 啟用所有 Swap 分割區
swapon_all /vendor/etc/fstab.qcom

# 2. 靈魂參數:關閉 Swap 預讀(page-cluster 設為 0)
# 傳統硬碟需要一次預讀 8 頁 (32KB) 減少尋道;ZRAM 尋道時間為 0,設 0 每次只解壓觸發的 4KB,省下巨大 CPU 算力!
write /proc/sys/vm/page-cluster 0

# 3. 設定換出積極度(設為 60,避免過於頻繁觸發壓縮)
write /proc/sys/vm/swappiness 60

結語

在這台 2GB 的破裝置上調教記憶體,我們學會的最重要心法不是「大刀闊斧」,而是「精準克制」

  1. 認清現實:DMA / ION 是硬成本,誰也動不了。
  2. 拒絕誘惑:KSM 雖好,但弱 CPU 根本無福消受,果斷關閉。
  3. 拒絕玩火:eMMC 壽命有限,傳統 Swap 碰都別碰。
  4. 精準施策256MB ZRAM + LZ4 + page-cluster=0,以最小的 CPU 代價換取系統服務的壓縮空間,把純淨的實體記憶體留給 30fps 人臉辨識戰場。

參考資料 (References)

  1. Android Low RAM Configuration Guide: Google 官方低記憶體設備調優指南.
    https://source.android.com/docs/core/perf/low-ram
  2. Linux Kernel ZRAM Documentation: Official Linux ZRAM module architecture & tuning.
    https://www.kernel.org/doc/Documentation/blockdev/zram.txt
  3. Linux Kernel Memory Cgroup (memcg): Control Group Memory Resource Controller.
    https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt

發佈留言

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