大部分的 App 開發者總覺得 new 一個物件是免費的,確實不需要去考慮 new 一個物件後會怎麼樣,直到他們被派去寫爛裝置。
當你在一台只有 2GB RAM、CPU 1.3GHz 的破板子上,被要求同時跑滿 RGB、紅外線(IR)與 TOF 深度相機三路高頻影像流時…
整理一下有哪些方法.除了大規模的調整android 參數,有些小細節可以高效的幫助 gc 效能
可能有機會可以保命,當然是希望不要再寫這種爛裝置
可回收的 Pool
在 Android 處理相機串流,相機每秒吐出來的影像資料跟山一樣大。
如果你每一張圖都去 new 一個新空間(就像使用免洗餐具),用完就丟給系統回收,系統光是「跟在後面撿垃圾 (GC)」就會卡到懷疑人生:上一秒開門很順,下一秒撿個垃圾就卡了 800ms。最後系統會直接因為垃圾堆滿 (OOM) 而閃退。
解法:發放鐵碗 (RecycleableBuffer)
我們徹底禁止使用免洗餐具,手刻了記憶體池。 只發給程式幾個固定的鐵碗。裝完影像、AI 算完之後,把鐵碗洗乾淨(
.recycle()),繼續裝下一頓。不產垃圾,系統就永遠不會因為撿垃圾而卡頓。
小小的例外,送進 tflite or 其他 模型,希望是盡可能乾淨的
雖然我們提倡不用免洗餐具,但如果你看程式碼,會發現對於相機傳來的 RGB 畫面,我們居然偷偷複製了一份(bitmap.copy),硬生生吃了一次效能虧。
打臉了吧?為什麼在這裡破功?
解法:物理隔離
因為相機硬體是個無情的機器。如果你不把畫面趕快拷貝出來,當 AI 慢慢吞吞算到一半時,相機底層隨時會把「下一幀」的畫面,直接倒進同一個碗裡蓋掉。
你的神經網路就會吃到一張:**「上半截是前一秒的人、下半截是後一秒的背景」**的撕裂破圖。 為了解決這個問題,我們認輸。我們老老實實花效能把畫面拷貝出來,去換取「送進 AI 嘴裡的肉絕對乾淨」。
最粗暴的安全背壓,就像「把佔位的客人踹出去」
相機一秒塞給你 30 張圖,但你那可悲的 CPU 一秒只能消化 6 張。
如果你讓多出來的圖在記憶體裡面慢慢排隊,不用 10 秒鐘,排隊的人潮就會把整台機器撐爆。
解法:無情的 Drop-Oldest
我們不依賴任何優雅的框架,直接在門口只放 3 張椅子(
FD_QUEUE_MAX_SIZE = 3)。 如果椅子坐滿了,AI 又還沒算完怎麼辦?我們的程式會直接走過去,把坐最久的那個人拖出來踹出去(呼叫 recycle 丟棄舊幀),讓最新進來的客人坐!
這在手機 App 叫「卡頓掉幀」,在工業機台叫「極限生存」。寧可流失客人,也絕對不讓店被擠爆。而且 AI 絕對不能去算「過期的舊畫面」,否則大門打開時,人早就走過去了。
漏斗式淘汰
如果你連路邊飛過去的蒼蠅、或是走過去的背影,都要無腦啟動最頂級的 AI 去算他長得像誰,這台破機器的 CPU 會 24 小時瘋狂滿載。然後過熱、降頻,最後電池膨脹炸裂。
解法:漏斗式淘汰
我們在 Pipeline 門口設了三道惡霸警衛,純粹為了擋下無效運算:
- 第一關(臉部偵測): 沒看到人臉?滾。
- 第二關(模糊檢查): 臉是糊的、人在跑?滾。
- 第三關(活體防偽): 確定是個站定不動的立體活人,才准進大廳享受最昂貴的冷氣(跑 CNN 神經網路)。
90% 的時間大門口是沒人的。這套惡霸機制讓 CPU 大部分時間都在睡覺,極大地延長了設備壽命。