Android モバイルの LLM 計測結果の内訳について、一部詳細を伺いたいです

以下に示す LLM タグについて質問がございます。

・「Meshes/SkeletalMesh/Serialize」「Meshes/StaticMesh/Serialize」

- こちらは GPU で使用するジオメトリデータとは別のものかと思いますが、その認識は合っておりますでしょうか。

- その場合、どのような由来で増加するのか、また、減らすには / 増えすぎないようにするにはどのようなアプローチが良いかヒントを頂けますと幸いです。

・「Textures」

- こちらは GPU で使用するピクセルデータとは別のものかと思いますが、その認識は合っておりますでしょうか。

- その場合、どのような由来で増加するのか、また、減らすには / 増えすぎないようにするにはどのようなアプローチが良いかヒントを頂けますと幸いです。

・「Animation/ControlRig」

- こちらはジオメトリ / モーションデータとは別のものでしょうか。

- その場合、どのような由来で増加するのか、また、減らすには / 増えすぎないようにするにはどのようなアプローチが良いかヒントを頂けますと幸いです。

・「FMallocUnused」「VulkanDriverMemoryCPU」「VulkanSpareMemoryGPU」

- 各々について、どのような由来で増加するのか、また、減らすには / 増えすぎないようにするにはどのようなアプローチが良いかヒントを頂けますと幸いです。

なお、LLM タグについては以下の手順で出力された結果をもとに確認しております。

1. Android / Development ビルドでパッケージ作成し、引数 -LLMCSV を用いて実機端末にてアプリケーションを実行

2. 端末上に出力された CSV ファイルを確認

お世話になっております。

本件確認にお時間を頂いてしまい申し訳ございません​

LLMで計測されているものの多くはパッケージからメモリにロードされて展開(シリアライズ)されたものとなっていますので、

メモリが圧迫している場合は不必要なアセットがロードされていないか確認し、必要に応じてリファレンスを外したりアセット自体を軽量化して頂く形になるかと思います。

※各カテゴリに関しましては、以下記事が参考になるかと思いますのでこちらを一度ご確認いただければと思います。

ご質問頂いた各カテゴリについて

・「Meshes/SkeletalMesh/Serialize」「Meshes/StaticMesh/Serialize」

こちらは、GPU(Vulkan)用の直接的なデータではなく、以下関数等で処理されているようなLoad時シリアライズ処理にて確保されているメモリが計測されるため付随するデータ等が計測に影響します。

void USkeletalMesh::Serialize( FArchive& Ar )こちらはmemreport -fullコマンド等でメモリに載っているSkeletal MeshやStatic Meshを確認し、まずはアセット単位で不必要なものがロードされていないか確認頂くと良いかと思います。

参考リンク

UE4でのメモリ使用量の調査と対策

「Textures」

こちらもMeshと同様にTextureのデータを抑えて頂く形になるかと思います。

MemreportやListtexturesコマンドを活用し、不必要に大きなテクスチャがロードされていないか確認し、必要に応じてMipBiasなどでテクスチャサイズを制限いただければと思います。

「Animation/ControlRig」

こちらはアニメーション全般のデータが計測されます。

Obj List CLASS=AnimsequenceコマンドやObj List CLASS=ControlRigコマンドでメモリ上のアセットを確認可能となっていますので、

不必要なものが読まれていないか確認いただき、意図していないものがロードされている場合はリファレンスビューワー等から原因を探していく形になるかと思います。

※Mesh、Texture、Animationのリダクション方法に関しては以下資料にもまとめられております。

UE4の頃の資料となりますが、基本は変わっていないためこちらもご確認いただければと思います。

https://www.docswell.com/s/EpicGamesJapan/Z2MVRZ\-ue4\-95238296\#p1

・「FMallocUnused」「VulkanDriverMemoryCPU」「VulkanSpareMemoryGPU」

FMallocUnusedに関してはMallocアロケーターのうち利用されていない領域のメモリとなり、基本的には再利用される見込みのため問題となることは少ないかと思われます。

VulkanDriverMemoryCPUに関しては、GPU処理のVulkan用に管理しているCPUメモリ(FVulkanCustomMemManagerで管理)がカウントされるものとなっており、

VulkanSpareMemoryGPUに関しては、Vulkan用に確保したメモリを再利用できるようにプーリングを行い、こちらは再利用を行う想定となっているため大きくなっていたとしても問題にはならない想定です。

.\Engine\Source\Runtime\VulkanRHI\Private\VulkanMemory.cpp

bool FVulkanSubresourceAllocator::TryAllocate2(FVulkanAllocation& OutAllocation, FVulkanEvictable* AllocationOwner, uint32 InSize, uint32 InAlignment, EVulkanAllocationMetaType InMetaType, const char* File, uint32 Line)​よろしくお願いいたします。

お世話になっております。ご回答ありがとうございます。

“FMallocUnused” “VulkanSpareMemoryGPU” に関しては、再利用が想定された余剰領域という認識で間違いないでしょうか。

その場合、モバイルではメモリの消費量がシビアであるため、再利用が想定されているとしても、可能な限り減らしたいと考えております。

こちらはどのような仕様で確保されるものでしょうか。

・実行時にいずれかの設定等に応じて一括で確保される

・あるいは、実行中に大きくメモリが確保された際、再利用を想定して未開放の状態で残る

前者の場合、どういった基準で一括確保のサイズが決まるかご教示頂きたく存じます。

後者の場合、それを明示的に開放する術はあるかご教示頂きたく存じます。

どちらでもない場合、仕様の概要をご教授頂けますと幸いです。

以上、よろしくお願いいたします。

お世話になっております。ご回答ありがとうございます。

大変参考になりました。

​アセットや実装での削減を前提にしつつ、頂いた情報を考慮させて頂きます。

以上、よろしくお願いいたします。​

ご確認ありがとうございます。

ご指摘いただいた “FMallocUnused” と “VulkanSpareMemoryGPU” に関しては再利用を想定しているものとなります。

まずFMallocUnusedに関しましては、前提としてAndroidの場合MallocBinned2がデフォルトで使用されているかと思われますが、こちらは小さいメモリのアロケートコストを抑えるために、ある程度の単位(64KiB等)の単位でアロケートを行い、それぞれブロックを割り当てるような実装が行われております。

[UE4] Memory Allocationについて

https://qiita.com/EGJ-Yutaro_Sawada/items/4983d0ebfa945611d324

※細かい値は実装内部で定義されていますが、アライメントを調整しつつ無駄が少なくなるように定義を行っています。

.\Engine\Source\Runtime\Core\Private\HAL\MallocBinned2.cpp

こちらはブロックが空になったときにプール単位で解放が行わているためUnusedな領域が発生してしまいますが、基本的にはGCの解放タイミングで処理されるため、値が大きい場合はGCの実行をお試しいただければと思います。

※内部的にはFMallocBinned2::Trim(…)で解放可能となっておりますが、通常こちらはGC中のFMemory::Trim()から呼び出される想定です。

GCに関しては以下スライドや記事が参考になるかと思います

https://www.docswell.com/s/EpicGamesJapan/ZN1982-2024-12-09-104909#p22

【UE5】強制的にガベージコレクションを動作させる

https://shuntaendo.hatenablog.com/entry/2022/03/22/090220

VulkanSpareMemoryGPUに関しましては以下TrimMemory(…)が解放箇所となっておりますが、固定値で10~100フレーム利用されていない場合にFreeBlockが解放される実装となっておりました。

※RHIのRHIEndFrame_RenderThread()からReleaseFreedPages()->ReleaseFreedResources()->TrimMemory()->FreeInternal()の順に呼び出される

この設定値であるFrameThresholdFullやFrameThresholdPartialで解放までのフレーム数を短くすることで、より速い解放が可能となる見込みですが、描画用ブロックは再利用される可能性も高いため注意が必要です。

.\Engine\Source\Runtime\VulkanRHI\Private\VulkanMemory.cpp

FDeviceMemoryManager::TrimMemory(bool bFullTrim)
		//blocks are always freed after being reserved for FrameThresholdFull frames.
		const uint32 FrameThresholdFull = 100; 
		// After being held for FrameThresholdPartial frames, only LargeThresholdPartial/SmallThresholdPartial pages are kept reserved
		// SmallPageSize defines when Large/Small is used.
		const uint32 FrameThresholdPartial = 10;
		const uint32 LargeThresholdPartial = 5;
		const uint32 SmallThresholdPartial = 10;
		const uint64 SmallPageSize = (8llu << 20);

お手数おかけしますが、よろしくお願いいたします。

ご確認ありがとうございます。

本件はクローズとさせて頂きますが、また何かございましたらお気軽にご相談ください。

よろしくお願いいたします。