C# 中具有固定大小数组的连续分层结构内存?
Posted
技术标签:
【中文标题】C# 中具有固定大小数组的连续分层结构内存?【英文标题】:Contiguous hierarchical struct memory with fixed-size arrays in C#? 【发布时间】:2018-04-11 16:54:50 【问题描述】:我有一个任务,在 C 中是微不足道的,但 C# 似乎(故意?)不可能。
在 C 语言中,我会预先分配我的模拟的整个数据模型,通过设置为单个整体层次结构的结构,包括更多结构的固定大小数组,可能包含更多数组。这在 C# 中几乎是可行的,除了一件事......
在 C# 中,我们使用 fixed
关键字在每个结构类型中指定固定大小的缓冲区(数组) - Cool。然而,这只支持原语作为固定缓冲区元素类型,在这些工作中抛出了一个主要的扳手,即拥有一个单一的、分层的和连续分配的数据模型,开始确保最佳的 CPU 缓存访问。
我可以看到的其他方法如下:
-
使用通过单独的
new
将数组分配到别处的结构(这似乎完全破坏了连续性)- 标准做法,但效率不高。
使用原始类型的固定数组(比如byte
),但是当我想改变事物时必须来回编组这些……这会很容易工作吗?可能会非常乏味。
做 (1),同时假设平台知道移动物体以获得最大的连续性。
我在 Unity 5.6 下使用 .NET 2.0。
【问题讨论】:
听起来你想要一种不同的语言,例如 C 或 C++ 【参考方案1】:请查看 C# 7.2 的 Span<T>
和 Memory<T>
功能。我想这会解决你的问题。
What is the difference between Span<T> and Memory<T> in C# 7.2?
【讨论】:
我不能使用 C# 7.2。我正在使用 .NET 2.0 AFAIK - Unity。请删除答案,因为它无关紧要?我已经更新了问题。 如果您在 Unity 中使用 .Net 标准 2.0,那么它可能是 C# 6 或 7,并且可能有用于这些功能的 nuget 包。请看forum.unity.com/threads/c-7-support.512661 我看到的另一种方法是在 C++ dll 中创建该逻辑,然后使用一些 P/Invoke 从 C# 访问。 没错,但非常乏味。我想这是方便与性能的对比。 顺便说一句:我现在太费心了,但我偶然发现了this hack to allow C# 7.2 features in Unity。【参考方案2】:无法访问Memory<T>
,最终选择了选项(2),但不需要编组,只需强制转换:在unsafe struct
中使用fixed
字节数组,并按如下方式向/从这些字节进行转换:
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
public class TestStructWithFixed : MonoBehaviour
public const int MAX = 5;
public const int SIZEOF_ELEMENT = 8;
public struct Element
public uint x;
public uint y;
//8 bytes
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct Container
public int id; //4 bytes
public unsafe fixed byte bytes[MAX * SIZEOF_ELEMENT];
public Container container;
void Start ()
Debug.Log("SizeOf container="+Marshal.SizeOf(container));
Debug.Log("SizeOf element ="+Marshal.SizeOf(new Element()));
unsafe
Element* elements;
fixed (byte* bytes = container.bytes)
elements = (Element*) bytes;
//show zeroed bytes first...
for (int i = 0; i < MAX; i++)
Debug.Log("i="+i+":"+elements[i].x);
//low order bytes of Element.x are at 0, 8, 16, 24, 32 respectively for the 5 Elements
bytes[0 * SIZEOF_ELEMENT] = 4;
bytes[4 * SIZEOF_ELEMENT] = 7;
elements[2].x = 99;
//show modified bytes as part of Element...
for (int i = 0; i < MAX; i++)
Debug.Log("i="+i+":"+elements[i].x); //shows 4, 99, 7 at [0], [2], [4] respectively
unsafe
访问速度非常快,而且没有编组或复制 - 这正是我想要的。
如果可能对所有struct
成员使用4 字节int
s 或float
s,您甚至可以更好地将fixed
缓冲区基于这种类型(uint
始终一个干净的选择) - 易于调试。
2021 年更新
今年我重新讨论了这个主题,在 Unity 5 中进行原型设计(由于编译/迭代时间快)。
坚持使用一个非常大的字节数组并在托管代码中使用它会更容易,而不是使用fixed
+ unsafe
(顺便说一下,从 C# 7.3 开始,it is no longer necessary to use the fixed
keyword every time to pin a fixed-size buffer 才能访问它) .
fixed
我们失去了类型安全性;这是互操作数据的一个自然缺点——无论是原生数据还是托管数据的互操作; CPU和GPU;或 Unity 主线程代码和用于新 Burst / Jobs 系统的代码之间。这同样适用于托管字节缓冲区。
因此,可以更轻松地接受使用无类型托管缓冲区并自己编写偏移量 + 大小。 fixed
/ unsafe
提供(一点)更多便利,但不是很多,因为您同样必须指定编译时结构字段偏移量,并在每次数据设计更改时更改这些偏移量。至少对于托管 VLA,我可以对代码中的偏移量求和,但这确实意味着这些不是编译时常量,因此会失去一些优化。
与托管 VLA(在 Unity 中)相比,以这种方式分配 fixed
缓冲区的唯一真正好处是,对于后者,GC 有可能在播放过程中将您的整个数据模型移动到其他地方,这可能会导致打嗝,尽管我还没有看到这在生产中有多严重。
托管数组are not, however, directly supported by Burst。
【讨论】:
其他对此主题感兴趣的人,另请参阅this very informative article。以上是关于C# 中具有固定大小数组的连续分层结构内存?的主要内容,如果未能解决你的问题,请参考以下文章