C＃コードの最適化：Monoメモリの節約

C＃は仮想マシン（VM）環境（Mono仮想マシンまたはIL2CPP仮想マシン）で実行され、割り当てられたMonoメモリは仮想マシンによって管理および再利用されるため、C＃コードは「マネージドコード」と呼ばれ、Monoメモリも「マネージドメモリ」として知られています。「マネージドメモリ」の解放は、仮想マシンのGC（ガベージコレクション）メカニズムに依存しますが、この記事では説明していません。

C＃プログラム開発で従うべき基本原則は、不要なMonoメモリの割り当てを回避することであり、Monoメモリの割り当ては主に次の結果を引き起こします。

プログラムが使用するメモリの総量が多すぎます。メモリは限られたアセットです。ゲーム（特にモバイルゲーム）の場合、メモリ使用量は貴重なものであり、解放できないメモリが多すぎると、プログラムがクラッシュする可能性があります。
割り当ての回数が多すぎると、Monoの断片化が増加し、必要な連続メモリを割り当てることができなくなり、プログラムがクラッシュする可能性があります。
メモリ割り当てはGC（ガベージコレクション）をトリガーし、GCは高価であり、遅延を引き起こします。
## 1.当クラスのメソッドには.tagへのコールがある
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/0a51873f-110b-e1bc-da1e-d76c9d519f2e.png)



tagはシーン内のGameObjectのタグであり、クラスGameObjectのメンバーtagは属性です。属性を取得するとき、基本的にget_tag（）関数をコールし、nativeレイヤーから文字列を返します。文字列は参照型であり、この文字列が返されると、Monoメモリが割り当てられます。ただし、Unityエンジンはキャッシュによってget_tagを最適化しません。get_tagがコールされるたびに、Monoメモリが再び割り当てされます。

したがって、tagを比較する必要がある場合は、関数GameObject.CompareTag（）を使用することをお勧めします。この関数は、nativeレイヤーに実装され、マネージMonoメモリの割り当てを引き起こさないため、GCの負荷を回避できます。

## ⒉当クラスのメソッドには、texture GetPixels（）/ GetPixels32（）のコールがある
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/3814e8e4-6b22-ee5d-287c-16bd40142334.png)



Texture2DタイプのオブジェクトでGetPixels（）およびGetPixels32（）をコールすることは、通常、指定されたMipmapレイヤーのすべてのピクセル情報を取得することであり、画像上のピクセル数は非常に多いことがよくあります。

メモリ割り当てに関しては、この関数はマネージMonoにメモリを割り当てて、テクスチャデータのピクセル情報を格納しますが、エンジンはそれをキャッシュしません。したがって、頻繁にコールされる関数で使用すると、永続的なMonoメモリ割り当てが発生します。

時間の面では、大規模な並列演算が得意なGPUが画像情報を処理するのは非常に簡単ですが、ピクセルごとの情報を取得する場合、CPUは少し面倒です。また、GetPixels（）はCPUによって同期的に実装されるため、時間がかかり、コールのスレッドが同時にブロックされ、ラグが発生する可能性があります。したがって、必要な場合を除いて、GetPixels（）を使用することはお勧めしません。

## 3.当クラスのメソッドにGetComponentsInChildrenコールがある/当クラスのメソッドにGetComponentsInParentコールがある
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/459ba03f-162b-84bf-0430-5c6fa4a168a8.png)



前回の記事「C＃コードの最適化：CPU時間の節約」では、GetComponentsInChildrenとGetComponentsInParentについて簡単に説明しました。ここでは、実際のコールでは、オブジェクトのトラバーサルと結果の戻りが含まれるため、不適切に使用すると、永続的なMonoメモリの割り当てが発生することをさらに説明します。開発チームは、List型の参照をパラメータとして受け入れるバージョンを使用することをお勧めします。これにより、コールごとにMonoメモリを割り当てないようにすることができます。

## 4.当クラスのメソッドにLinq関連関数へのコールがある
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/e23e1448-f658-0582-0f85-dd408d203934.png)



Linq関連の関数は、通常、データのクエリと処理に使用されます。機能的に言​​えば、一連のデータに対してさまざまなif判定とforループ処理を実行することです。 Linqが提供するAPIを使用すると、集合体データを処理するSQLステートメントスタイルのコードを記述できます。これにより、コードの単純さと読みやすさが大幅に向上し、保守も便利になり、書き込み効率が向上しますが、これらの利点はパフォーマンスが犠牲になります。

Linqは実行中にいくつかの一時変数を生成し、デリゲート（lambda式）を使用します。デリゲートを条件付き判定方法として使用すると、時間のコストが高くなり、一定量のMonoメモリが割り当てられます。したがって、一般的なUnityゲームプロジェクトの開発では、Linq関連の関数を使用することはお勧めしません。エディター機能の開発では、LinqとReflectionを一緒に使用することがよくあります。

## 5.当クラスのメソッドに、RendererにsharedMaterialsの取得がある
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/76fbdba9-fda8-cc84-cb26-06a2b3e1e417.png)



同様に、前回の記事「C＃コードの最適化：CPU時間の節約」では、.material / materialsについて説明しました。簡単に言うと、.material（s）をコールすると、新しいシェーダーインスタンスが生成されます。sharedMaterialsは共有マテリアルであり、新しいマテリアルインスタンスは生成されません。ただし、.sharedMaterialsをコールすると、Monoメモリが割り当てられます。コールごとに、Materialのインデックスを保持する配列が割り当てられます。このアレイのメモリ使用量は比較的小さいですが、頻繁にコールすることはお勧めしません。

## 6.当クラスのメソッドにInput.touchesコールがある
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/79a0a462-1768-332a-b90f-0ba10deecf2f.png)



モバイルプロジェクトの相互作用では、タッチ操作は非常に頻繁で一般的であると言えます。タッチ操作の取得では、Input.touchesを使用して、現在のフレーム内のすべてのタッチ操作のステータスと対応するデータを取得します。しかし、.touchesの実装を見ると、コールたびにtouchesの配列がnewになり、ある程度のMonoメモリが割り当てられることがわかります。したがって、開発チームは、追加のMonoメモリの使用を防ぐために、Input.touchesを頻繁に使用しないようにする必要があります。

## 7.FindObjectsOfTypeコール

前回の記事「C＃コードの最適化：CPU時間の節約」では、FindObjectsOfTypeについて簡単に紹介しました。 CPU時間は増加する上に、Monoメモリ割り当てのかなりの部分を占めるため、パフォーマンスへの影響を軽減するために、1回のコールで結果をキャッシュすることをお勧めします。

## 8.当クラスのメソッドにTextAsset / WWW.bytesコールがある
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/046943e6-ffd0-ebc7-1eeb-09b81600b283.png)



このルールは、実際には2つの無関係なUnityAPI用です。 1つ目は、TextAssetのbytesプロパティです。 TextAssetはUnityのテキストアセットであり、txt、html、bytes、csvなどのさまざまな形式のファイルの変換をサポートしています。 bytesプロパティを取得するとき、Unityはnativeレイヤーからバイト配列（byte []）を取得し、それによって一定量のMonoメモリを割り当てます。

別のAPIは、WWWクラスのbytesメンバーを参照します。呼び出されるたびに、Monoメモリの割り当てが発生します。UnityがWWWクラスの関連するインターフェイスを放棄したため、UnityWebRequestを使用して関連機能を実現することをお勧めします。

---
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、**パフォーマンス分析**と**最適化ソリューション**及び**コンサルティングサービス**を提供している会社でございます。

今なら、UWA GOTローカルツールが**15日間に無償試用**できます！！
よければ、ぜひ！

UWA公式サイト：https://jp.uwa4d.com
UWA GOT OnlineレポートDemo：https://jp.uwa4d.com/u/got/demo.html
UWA公式ブログ：https://blog.jp.uwa4d.com