在Unreal中MallocBinned總共有3個版本:MallocBinned、MallocBinne2、MallocBinned3。這類的memory pool機制,主要的設計原則就是希望盡量的將大小相同的物件放在一塊連續的記憶體上,然後根據物件的大小,一個蘿蔔一個坑,在分配的時候再從對應的Pool中拿出空間出來使用。通常我們只會對小物件做這件事,過大的記憶體物件需求通常會直接呼OS的allocator。
例如FMallocBinned所定義的小物件是以下幾個size:16, 32, 48, 64…到32768,也就是說至多到32KB。
看了一下android的memory pool機制,預設arm64是用MallocBinned2跑,而要不要換到MallocBinned3取決於各別專案會不會用爆目標最低機型的記憶體,會的話,由於MallocBinned3有Virtual Memory,因此可以突破記憶體上限。
另外IOS預設是使用MallocBinned,因為只有他有實作Apple系統內建的的nano allocator。系統的nano allocator預設會保留512MB的pool給small object用,看起來有點像是MallocBinned2在做的事;相對於MallocBinned的Pool,它是在該size被使用到的時候再去跟OS要一大塊記憶體回來。
關於nano allocator,詳細可以看Engine\Source\Runtime\Core\Private\Apple\ApplePlatformMemory.cpp中NanoMallocInit 的註解。以下是Copy出來的註解:
iOS reserves 512MB of address space for 'nano' allocations (allocations <= 256 bytes)
Nano malloc has buckets for sizes 16, 32, 48....256
The number of buckets and their sizes are fixed and do not grow
We'll walk through the buckets and ask the VM about the backing regions
We may have to check several sizes because we can hit a case where all the buckets
for a specific size are full - which means malloc will put that allocation into
the MALLOC_TINY region instead.
The OS always tags the nano VM region with user_tag == VM_MEMORY_MALLOC_NANO (which is 11)
Being apple this is subject to change at any time and may be different in debug modes, etc.
We'll fall back to the UE allocators if we can't find the nano region.
We want to detect this as early as possible, before any of the memory system is initialized.
可以想見能在OS層級做掉的機制,當然會比FMallocBinned2引擎自己實作的還要高效,因此也不難理解iOS會選擇使用FMallocBinned來搭配。
IOS要換到MallocBinned3是不可能的,因為根本就沒有實作這條路,我有試著硬開出來,但會直接crash,微稍深入研究了一下Apple官方文檔,裡面有提到,雖然Mac相關VirtualMemory的功能都是正常的,但是我們只能page-in但不能page-out到硬碟上,不排除未來官方未來會在iOS上支援swap memory,可以觀察每年WWDC看看有沒有提到相關的議題,但現況就是iOS不支援MallocBinned3。另外有發現蘋果在發表iPadOS16的時候,有提到支援Virtual memory swap這個feature,這表示現況我們是能在iPad上使用MacllocBinned3的(待測)。
只是為什麼iOS不支援MallocBinned3而Android支援?
我想是為了延長手機的生命週期吧。
由於MallocBinned3是依賴於VirtualMemory的技術,他會需要大量進行PageOut/PageIn的動作,但由於手機系統大多是使用FlashMemory,其在設計上有寫入次數的限制,寫入次數越多,其跟記憶體之間的資料交換會越不穩定。
因此iOS只支援PageIn而不支援PageOut的根本原因在此,為了維持系統的穩定度,減少硬碟的寫入次數,因此Apple禁止了PageOut這條路;由於不支援PageOut,因此我們就沒辦法使用MallocBinned3。
這個限制其實在Android系統上也是存在,只是該限制只存在於boot disk上,其他另外的secondary storage,例如SD card則沒有這種限制,所以Android還是可以正常的使用MallocBinned3。
Leave a Reply