目的
一時的な配列を受け渡しするのに、普通の配列やListを使うとGCゴミが発生します。
例えばゲームマネージャーからアクティブなプレイヤーを取得するのを考えます。
public List<Player> GetActivePlayers()
{
List<Player> list = new(); //将来的にごみになる
foreach(var player in players){
if(player.isActive)list.Add(player);
}
return list;
}
stack上で処理が完結すれば、ゴミが出ないのではないかというのがこの試行錯誤の原点です。
0. stackallocで確保したSpanを、なんやかんやして出力する(ダメ)
普通にエラーになります
public Span<int> CreateData()
{
return stackalloc int[]{0,1,2,3};// エラー
}
1. 構造体を示した一時的なSpanを受け渡す(ダメ)
構造体をSpanとして扱えるようなレイアウトで定義して、Spanを出力する。
構造体が関数を抜けた瞬間解放されるので、Spanにアクセスできるけどメモリリークになります。
using System;
using System.Runtime.InteropServices;
class Program
{
public struct A
{
int n0, n1, n2, n3, n4;
public Span<int> AsSpan() => MemoryMarshal.CreateSpan(ref n0, 5);
}
public static Span<int> Create()
{
A a = new();
var span = a.AsSpan();
span[0] = 0;
span[1] = 1;
span[2] = 2;
span[3] = 3;
span[4] = 4;
Display(span);// 0 1 2 3 4
return span;
}
public static void Display(Span<int> span)
{
foreach(ref var num in span) Console.WriteLine(num);
}
static void Main()
{
Display(Create()); // 無茶苦茶な値になる
}
}
2. 配列っぽい構造体を受け渡す
- で出力するのをSpanではなく配列構造体そのものにします。
しかし、構造体のコピーの仕様により、配列構造体の大きさによってコピーのコストが発生します。
3. UnityのNativeArray(Allocator.Temp)的な処理をする
UnityのNativeArrayはGCが発生しない方法で、いい感じに管理されてるみたいです。
で、Allocator.Tempを指定した場合は、解放処理が一括で行われて処理負荷が全然ないようです。
これとc#のArrayPoolの内容を斜め読みした知識をミックスして、独自の一時配列を作ってみようと思います。