Tenho um tipo de dado cujo tempo de vida é indefinido. Portanto, é uma classe. No entanto, ele é criado com muita frequência, o que faz com que ele aloque memória constantemente. Ele também tem bastante peso, o que o torna problemático.
O que pode ser feito para reduzir o número de alocações necessárias para esse tipo de dado, otimizar suas alocações e desalocações e reduzir a pressão do GC? (Criar uma estrutura não funcionaria, pois exigiria cópia constante de dados, com a impossibilidade de sempre ser capaz de liberar dados do array interno via try-finally).
[Serializable]
public class DamageData
{
[Serializable]
public class DamageDataInternal
{
public float[] values;
public float totalDamage;
public int cachedHashCode;
public bool isHashCodeDirty;
}
private DamageDataInternal _internalData;
public static readonly DamageType[] DamageTypes;
public int Hash => GetHashCode();
public bool IsInit => _internalData != null;
static DamageData()
{
DamageTypes = (DamageType[])Enum.GetValues(typeof(DamageType));
}
~DamageData()
{
DamageDataInternalPool.Shared.Release(_internalData);
_internalData = null;
}
public DamageData()
{
_internalData = DamageDataInternalPool.Shared.Get();
_internalData.totalDamage = 0f;
_internalData.isHashCodeDirty = true;
_internalData.cachedHashCode = 0;
}
}
using UnityEngine.Pool;
public class DamageDataInternalPool
{
private readonly ObjectPool<DamageData.DamageDataInternal> _pool;
public static DamageDataInternalPool Shared { get; } = new();
public DamageDataInternalPool()
{
_pool = new ObjectPool<DamageData.DamageDataInternal>(
createFunc: () => new DamageData.DamageDataInternal(),
actionOnGet: obj =>
{
obj.totalDamage = default;
obj.cachedHashCode = default;
obj.isHashCodeDirty = default;
obj.values = ArrayPool<float>.Shared.Rent(DamageData.DamageTypes.Length);
for (var i = 0; i < DamageData.DamageTypes.Length; i++)
{
obj.values[i] = 0f;
}
},
actionOnRelease: obj =>
{
ArrayPool<float>.Shared.Return(obj.values);
},
actionOnDestroy: null,
defaultCapacity: 10,
maxSize: 10000
);
}
public DamageData.DamageDataInternal Get()
{
return _pool.Get();
}
public void Release(DamageData.DamageDataInternal obj)
{
_pool.Release(obj);
}
}
Tentei encapsular seus dados internos em uma classe separada, instâncias das quais obtenho via object-pooling, para que a classe principal pesasse apenas SIZE_OF_LINK + SOME_META_DATA_SIZE. No entanto, isso não parece ter mudado a situação em nada e ainda há muita alocação.