【发布时间】:2019-10-25 21:48:32
【问题描述】:
我正在从事一个性能至关重要的物理模拟项目,我认为一个瓶颈是我的内存管理。目前,我有包含固定数量的刚体、粒子和力的缓冲对象。在模拟开始之前,所有物理实体都在其缓冲区中初始化。当需要一个实体时,选择第一个不活动的实体,否则,最旧的,当不再需要时,将其移到最后,因此活动实体都放在前面。
这是我正在使用的数据结构。
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# 中管理内存的方法似乎有很多,我很难确定在这种情况下哪种方法适合我。
现在,问题归结为三个部分:
- 可以这样做吗?
- 如果是这样,管理内存的最佳方法是什么?
- 那么,正确的实现应该是什么样的?
【问题讨论】:
-
考虑查看 c# 的 Span
。这是 c# 中高性能的数组的线程安全包装器。 youtube.com/watch?v=NVWQRbqcXJ4 -
在我的情况下,我应该使用 Memory
吗?模拟是长时间运行的,所以它会占用一个以上的堆栈帧。如果我理解正确的话,Span 是为了在堆栈上分配空间,当执行离开上下文时释放。 -
您可以在静态空间中分配它,这样它就不会超出范围并被丢弃。
-
是否有理由使用 Span
而不是 Memory ? -
array 布局在连续内存中。 contents 可能不是:由于您正在修改
BufferElement,我假设它是一个类,此时数组将包含指针,而不是元素本身。我不清楚结构是否会直接放入数组中,但如果是这样,它很可能会受到与直接复制优化类似的约束。仅当您(按比例)对顺序元素执行足够量的工作时,顺序布局才有用 - 如果将元素直接放置到数组中,这会容易得多。
标签: c# memory-management .net-core