【问题标题】:Performance difference in empty array initialization空数组初始化的性能差异
【发布时间】:2015-07-16 07:02:24
【问题描述】:

最近,我一直在进行性能/内存优化,并且一直坚持使用泛型方法初始化空数组的空数组初始化:

泛型空数组类的代码实现:

    public static class EmptyArray<T>
    {
        public static readonly T[] Instance;

        static EmptyArray()
        {
            Instance = new T[0];
        }
    }

因此,无论何时创建任何类型的空数组,它都被称为:

var emptyStringArray = EmptyArray<string>.Instance;

这种空数组声明已经在代码库的许多地方完成。我很困惑使用时性能会有什么不同:

var emptyStringArray = new string[0];

我已经问过上面的代码作者,他回复了我:

基本上,所有空数组都是只读的,并且等于一 另一个,这意味着您可以使用相同的实例(这将是 在运行时根据需要延迟创建)......这应该减少 分配总数,减少内存使用和GC压力,以及 应该会有所改善

不过,我无法理解 EmptyArray Instance 如何提高数组声明的性能。

使用以下方法的代码是否有任何性能差异:

第一种方法:

var emptyStrArr = EmptyArray<string>.Instance;
var emptyFooArr = EmptyArray<Foo>.Instance;
var emptyBarArr = EmptyArray<Bar>.Instance;

第二种方法:

var emptyStrArr = new string[0];
var emptyFooArr = new Foo[0];
var emptyBarArr = new Bar[0];

【问题讨论】:

标签: c# .net arrays performance clr


【解决方案1】:

在第一个代码中,static 构造函数只执行一次。所以你只是创建一个数组并一直使用它。在第二个代码中,您每次都创建一个数组实例。这就是区别。

改变构造函数可以看得更清楚:

static EmptyArray()
{
   Instance = new T[0];
   Console.WriteLine("Array of "+ typeof(T) + " is created.");
}

var s = EmptyArray<string>.Instance;
s = EmptyArray<string>.Instance;
s = EmptyArray<string>.Instance;

var i = EmptyArray<int>.Instance;
i = EmptyArray<int>.Instance;

// output:
// Array of System.String is created.
// Array of System.Int32 is created.

【讨论】:

  • 尽管如果没有静态构造函数,这可能会变得更加更加高效......只需使用public static readonly T[] Instance = new T[0];。对时间的保证较少,但效率会稍高一些。 (我自己可能会使用一个属性,但这不应该影响事情。)
  • 不仅可以创建空字符串数组,还可以创建其他类型的空数组。如果像这样创建不同的数组会怎样: var emptyStrArr = EmptyArray.Instance; var emptyFooArr = EmptyArray.Instance; var emptyBarArr = EmptyArray.Instance;
  • @JonSkeet :你能详细说明“更少的时间保证”吗?
  • @akash88 第一次将为每种类型创建一个实例。那么如果您使用相同的类型,您将获得先前创建的实例而不是新的实例
  • @Selman22 :这是否意味着 var emptyStrArr = EmptyArray.Instance; var emptyFooArr = EmptyArray.Instance; var emptyBarArr = EmptyArray.Instance;等同于 var emptyStrArr = new string[0]; var emptyFooArr = new Foo[0]; var emptyBarArr = new Bar[0];
【解决方案2】:

这个:

var emptyStringArray = new string[0];

每次调用时都会创建一个空字符串数组的新实例,并带有所有相关的内存分配开销,而这个:

public static class EmptyArray<T>
    {
        public static readonly T[] Instance;

        static EmptyArray()
        {
            Instance = new T[0];
        }
    }

无论您调用多少次Instance 字段,都只创建一个空字符串数组的实例。

【讨论】:

  • 这不是意味着 var emptyStrArr = EmptyArray.Instance; var emptyFooArr = EmptyArray.Instance; var emptyBarArr = EmptyArray.Instance;等同于 var emptyStrArr = new string[0]; var emptyFooArr = new Foo[0]; var emptyBarArr = new Bar[0];
【解决方案3】:

只是为了说明这如何提高性能和内存,你可以试试这个

using System;

namespace ConsoleApplication11
{
    class Program
    {
        static void Main(string[] args)
        {
            var str = EmptyArray<string>.Instance;
            var intTest = EmptyArray<int>.Instance;
            var intTest1 = EmptyArray<int>.Instance;  
            var str1 = EmptyArray<string>.Instance;
            Console.WriteLine(str.GetType());
            Console.WriteLine(intTest.GetType());
            if (ReferenceEquals(str,str1))
            {
                Console.WriteLine("References are equals");
            }
            if (ReferenceEquals(intTest,intTest1))             
            {
                Console.WriteLine("References are equals");
            }

        }
    }

    public static class EmptyArray<T>
    {
        public static readonly T[] Instance;

        static EmptyArray()
        {
            Instance =  new T[0];
        }
    }
}

您可以看到即使对于值类型也不需要其他分配,并且考虑到对象的分配和销毁会消耗时间和内存,您可以通过这样做来增强您的代码

【讨论】:

    猜你喜欢
    • 2022-01-09
    • 2021-11-20
    • 2015-02-15
    • 2023-01-10
    • 1970-01-01
    • 1970-01-01
    • 2015-11-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多