簡體   English   中英

C#中具有固定大小數組的連續分層結構內存?

[英]Contiguous hierarchical struct memory with fixed-size arrays in C#?

我有一個任務,在 C 中是微不足道的,但 C# 似乎(故意?)不可能。

在 CI 中,將通過設置為單個整體層次結構的結構預先分配我的模擬的整個數據模型,包括更多結構的固定大小數組,可能包含更多數組。 這在 C# 中幾乎是可行的,除了一件事......

在 C# 中,我們有fixed關鍵字來指定每個結構類型中固定大小的緩沖區(數組) - Cool。 然而,這支持原語作為固定緩沖區元素類型,在這些工作中拋出了一個主要的扳手,即擁有一個單一的、分層的和連續分配的數據模型,開始確保最佳的 CPU 緩存訪問。

我可以看到的其他方法如下:

  1. 使用通過單獨的new (這似乎完全破壞連續性)在別處分配數組的結構 - 標准做法但效率不高。
  2. 使用原始類型的固定數組(比如byte ),但是當我想改變事物時必須來回編組這些......這甚至會很容易工作嗎? 可能會很乏味。
  3. 做 (1) ,同時假設平台知道為了最大的連續性而移動事物。

我在 Unity 5.6 下使用 .NET 2.0。

請查看 C# 7.2 的Span<T>Memory<T>特性。 我認為這會解決你的問題。

C# 7.2 中的 Span<T> 和 Memory<T> 有什么區別?

無法訪問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 開始,不再需要每次都使用fixed關鍵字來固定一個固定的-size 緩沖區以訪問它)。

使用fixed我們失去了類型安全性; 這是互操作數據的一個自然缺點——本地和托管之間是否互操作; CPU和GPU; 或者在 Unity 主線程代碼和用於新 Burst / Jobs 系統的代碼之間。 這同樣適用於托管字節緩沖區。

因此,可以更容易地接受使用無類型的托管緩沖區並自己編寫偏移量 + 大小。 fixed / unsafe提供(一點)更多的便利,但不是太多,因為您同樣必須指定編譯時結構字段偏移量並在每次數據設計更改時更改這些偏移量。 至少對於托管 VLA,我可以對代碼中的偏移量求和,但這確實意味着這些不是編譯時常量,因此會失去一些優化。

與托管 VLA(在 Unity 中)相比,以這種方式分配fixed緩沖區的唯一真正好處是,對於后者,GC 有可能在中途將您的整個數據模型移動到其他地方,這可能會導致打嗝,雖然我還沒有看到這在生產中有多嚴重。

但是, Burst 不直接支持托管陣列。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM