是否可以在.NET中使用不安全的代码来实现高效的固定大小数组?

时间:2022-01-12 21:42:30

Is there a good way of implementing a fixed-size array in .NET that does not require unsafe code?

有没有一种在.NET中实现固定大小数组的好方法,不需要不安全的代码?

My goal is to create a value type which represents a fixed-size array which can be embedded (included as a member) in other types - i.e. I'm specifically hoping to avoid creating an array as a seperate object to the type which declares it.

我的目标是创建一个表示固定大小数组的值类型,该数组可以嵌入(作为成员包含在其他类型中) - 即我特别希望避免创建一个数组作为声明它的类型的单独对象。

I realize that .NET's implementation of arrays is superb and supported at CLR/CIL level - and don't really want to debate whether or not to just use arrays... the exploration here is to whether or not a safe, fixed-size, value type implementation is possible with almost as good efficiency.

我意识到.NET的数组实现非常出色并且在CLR / CIL级别得到支持 - 并且真的不想讨论是否只使用数组......这里的探索是否是一个安全的,固定大小的,价值类型的实现是可能的,效率几乎一样好。

2 个解决方案

#1


5  

The objective is to be able to have a fixed-size value-type array which can be used as a private member on other types. Consider a custom dictionary or linked-list implementation... the number of heap allocations can be reduced if each bucket/node is flattened to contain it's own, fixed-size array.

目标是能够使用固定大小的值类型数组,该数组可以用作其他类型的私有成员。考虑自定义字典或链表实现......如果每个存储桶/节点被展平以包含它自己的固定大小数组,则可以减少堆分配的数量。

Making your array a value type does not necessarily mean it's going to be stored on the stack. In fact, if it's a value type embedded within a reference type, it's most likely going to be stored on the heap with the reference type, and not on the stack.

使数组成为值类型并不一定意味着它将存储在堆栈中。实际上,如果它是嵌入在引用类型中的值类型,它很可能将使用引用类型存储在堆上,而不是存储在堆栈中。

So making it a value type will not reduce heap allocations at all.

因此,使其成为值类型根本不会减少堆分配。

More info here: http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx

更多信息:http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx

#2


2  

After some research using reflector, it turns out that the following represents an acceptable (performance-wise) solution, since C# compiles a switch statement against integers into a CIL switch statement, which is implemented as a jump-list... that is - the getter executes in about 11 CIL instructions, which it fine.

在使用反射器进行一些研究之后,事实证明以下代表了一个可接受的(性能方面)解决方案,因为C#将针对整数的switch语句编译成CIL switch语句,该语句实现为跳转列表......即 - getter在大约11个CIL指令中执行,没问题。

 public struct EmbeddedArray<T>
    {
        private T _element0;
        private T _element1;
        private T _element2;

        public int Length { get { return 3; } }


        public T this[int index]
        {
            get
            {
                switch (index)
                {
                    case 0:
                        return _element0;
                    case 1:
                        return _element1;
                    case 2:
                        return _element2;
                }
                throw new ArgumentOutOfRangeException("index");

            }
        }
    }

Please see Hans' comment below. It turns out that this is not as performant as I'd hoped... once the CIL is compiled to native machine code, the measured performance is far off what a .NET array will yield.

请参阅下面的汉斯评论。事实证明,这并不像我希望的那样高效......一旦将CIL编译为本机机器代码,测得的性能远远不如.NET数组所产生的那样。

#1


5  

The objective is to be able to have a fixed-size value-type array which can be used as a private member on other types. Consider a custom dictionary or linked-list implementation... the number of heap allocations can be reduced if each bucket/node is flattened to contain it's own, fixed-size array.

目标是能够使用固定大小的值类型数组,该数组可以用作其他类型的私有成员。考虑自定义字典或链表实现......如果每个存储桶/节点被展平以包含它自己的固定大小数组,则可以减少堆分配的数量。

Making your array a value type does not necessarily mean it's going to be stored on the stack. In fact, if it's a value type embedded within a reference type, it's most likely going to be stored on the heap with the reference type, and not on the stack.

使数组成为值类型并不一定意味着它将存储在堆栈中。实际上,如果它是嵌入在引用类型中的值类型,它很可能将使用引用类型存储在堆上,而不是存储在堆栈中。

So making it a value type will not reduce heap allocations at all.

因此,使其成为值类型根本不会减少堆分配。

More info here: http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx

更多信息:http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx

#2


2  

After some research using reflector, it turns out that the following represents an acceptable (performance-wise) solution, since C# compiles a switch statement against integers into a CIL switch statement, which is implemented as a jump-list... that is - the getter executes in about 11 CIL instructions, which it fine.

在使用反射器进行一些研究之后,事实证明以下代表了一个可接受的(性能方面)解决方案,因为C#将针对整数的switch语句编译成CIL switch语句,该语句实现为跳转列表......即 - getter在大约11个CIL指令中执行,没问题。

 public struct EmbeddedArray<T>
    {
        private T _element0;
        private T _element1;
        private T _element2;

        public int Length { get { return 3; } }


        public T this[int index]
        {
            get
            {
                switch (index)
                {
                    case 0:
                        return _element0;
                    case 1:
                        return _element1;
                    case 2:
                        return _element2;
                }
                throw new ArgumentOutOfRangeException("index");

            }
        }
    }

Please see Hans' comment below. It turns out that this is not as performant as I'd hoped... once the CIL is compiled to native machine code, the measured performance is far off what a .NET array will yield.

请参阅下面的汉斯评论。事实证明,这并不像我希望的那样高效......一旦将CIL编译为本机机器代码,测得的性能远远不如.NET数组所产生的那样。