我正在一个物理仿真项目中,性能至关重要,我认为内存管理是一个瓶颈。目前,我有包含固定数量的刚体,粒子和力的缓冲对象。在开始模拟之前,所有物理实体都在其缓冲区中初始化。当需要一个实体时,选择第一个不活动的实体,否则选择最旧的实体,并且当不再需要它时,将其移到末尾,因此所有活动实体都被放在前面。
这是我正在使用的数据结构。
public sealed class Buffer<TValue> : IEnumerable<BufferElement<TValue>> where TValue : new()
{
public Buffer(int capacity)
{
Count = 0;
Capacity = capacity;
Elements = new BufferElement<TValue>[capacity];
for (var index = 0; index < Elements.Length; index++)
{
Elements[index] = new BufferElement<TValue>();
}
}
public int Count { get; private set; }
public int Capacity { get; private set; }
private int ActiveCount { get; set; }
private BufferElement<TValue>[] Elements { get; }
public BufferElement<TValue> Activate()
{
if (Count == ActiveCount) Count = 0;
var bufferElement = Elements[Count++];
if (!bufferElement.Active)
{
bufferElement.Active = true;
ActiveCount++;
}
return bufferElement;
}
public void Deactivate(BufferElement element)
{
if (!element.Active) return;
element.Active = false;
var lhs = element.Index;
var rhs = --ActiveCount;
Elements[lhs] = Elements[rhs];
Elements[rhs] = element;
Elements[lhs].Index = lhs;
Elements[rhs].Index = rhs;
}
}
阅读了.NET Core如何处理数组后,可能有两件事。第一种是每次访问数组中的元素时,它都会执行安全检查,第二种是GC可以将数组复制到新的内存地址。
我希望所有包含物理实体的缓冲区不进行任何安全检查,并尽可能固定在连续内存中。我相信这应该是可能的,因为每个缓冲区的大小都是固定的,并且元素(刚体,粒子,力)的大小也都是固定的。
[C#中似乎有很多管理内存的方法,而且我很难确定在这种情况下哪种方法对我合适。
现在,问题归结为三个部分:
首先,是的,可以做到。第二,best确实取决于,因为存在许多折衷,并且这很快成为opinionated帖子。第三,最简便的选择是使用fixed
关键字在unsafe
上下文中分配数组(此处没有对数组进行安全检查,也不允许您使用C样式的指针)并固定其地址,以便它不会尽管使用Span
或Memory
可能会更容易,但是如果正确使用fixed
和unsafe
的低级方法可以产生更好的性能。看看this,它是官方文档,上面有完整的示例。最后一个技巧是尝试切换到struct
而不是如果可能,它们没有内存开销,并且具有更高的内存密度,从而产生了更好的缓存访问时间,因为还有更多的缓存可以使用。