结构类型数组的性能 [英] Performance of array of struct types

查看:132
本文介绍了结构类型数组的性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

示例:

// Potentially large struct.
struct Foo
{ 
   public int A;
   public int B;
   // etc.
}

Foo[] arr = new Foo[100];

如果Foo是100字节结构,则在执行以下语句期间将在内存中复制多少字节:

If Foo is a 100 byte structure, how many bytes will be copied in memory during execution of the following statement:

int x = arr[0].A

也就是说,将arr [0]赋值给某个临时变量(Foo实例的100字节副本),然后将.A复制到变量x(4字节副本)中.

That is, is arr[0] evaluated to some temporary variable (a 100 byte copy of an instance of Foo), followed by the copying of .A into variable x (a 4 byte copy).

或者是编译器,JITer和CLR的某种组合,它们能够优化此语句,以便将A的4个字节直接复制到x中.

Or is some combination of the compiler, JITer and CLR able to optimise this statement such that the 4 bytes of A are copied directly into x.

如果执行了优化,当将项目保存在List<Foo>中或将数组作为IList<Foo>ArraySegment<Foo>传递时,它是否仍然有效?

If an optimisation is performed, does it still hold when the items are held in a List<Foo> or when an array is passed as an IList<Foo> or an ArraySegment<Foo>?

推荐答案

值类型按值复制 -从而命名.因此,我们必须考虑什么时候必须复制一个值.这归结为当特定实体引用变量时正确分析.如果它引用一个值,那么该值是从某个地方复制的.如果它引用一个变量,则它只是一个变量,可以像其他任何变量一样对待.

Value types are copied by value -- hence the name. So then we must consider at what times a copy must be made of a value. This comes down to analyzing correctly when a particular entity refers to a variable, or a value. If it refers to a value then that value was copied from somewhere. If it refers to a variable then its just a variable, and can be treated like any other variable.

假设我们有

struct Foo { public int A; public int B; }

暂时忽略这里的设计缺陷;公共字段和可变结构都是不好的代码味道.

Ignore for the moment the design flaws here; public fields are a bad code smell, as are mutable structs.

如果你说

Foo f = new Foo();

会发生什么?规范说:

  • 创建了一个新的八字节变量f.
  • 已创建一个临时的八字节存储位置temp.
  • temp填充有八个字节的零.
  • temp被复制到f.
  • A new eight byte variable f is created.
  • A temporary eight byte storage location temp is created.
  • temp is filled in with eight bytes of zeros.
  • temp is copied to f.

但这不是实际发生的情况;编译器和运行时非常聪明,可以注意到所需的工作流与工作流创建f并用零填充"之间没有明显的区别,因此发生了.这是复制删除优化.

But that is not what actually happens; the compiler and runtime are smart enough to notice that there is no observable difference between the required workflow and the workflow "create f and fill it with zeros", so that happens. This is a copy elision optimization.

执行:设计一个程序,使编译器无法进行复制删除,并且输出清楚表明,在初始化结构类型的变量时,编译器不执行复制省略.

现在,如果你说

f.A = 123;

然后对f求值以生成一个变量-而不是一个值-然后从该A求值以生成一个变量,并将四个字节写入该变量.

then f is evaluated to produce a variable -- not a value -- and then from that A is evaluated to produce a variable, and four bytes are written to that variable.

如果你说

int x = f.A;

然后将f评估为变量,将A评估为变量,并将A的值写入x.

then f is evaluated as a variable, A is evaluated as a variable, and the value of A is written to x.

如果你说

Foo[] fs = new Foo[1];

然后分配变量fs,分配数组并用零初始化,并将对数组的引用复制到fs.当你说

then variable fs is allocated, the array is allocated and initialized with zeros, and the reference to the array is copied to fs. When you say

fs[0].A = 123;

与以前相同. f[0]被评估为变量,因此A变量,因此将123复制到该变量.

Same as before. f[0] is evaluated as a variable, so A is a variable, so 123 is copied to that variable.

当你说

int x = fs[0].A;

与以前相同:我们将fs[0]评估为变量,从该变量中获取A的值,然后将其复制.

same as before: we evaluate fs[0] as a variable, fetch from that variable the value of A, and copy it.

但是如果你说

List<Foo> list = new List<Foo>();
list.Add(new Foo());
list[0].A = 123;

然后您将得到一个编译器错误,因为list[0]是一个,而不是一个变量.您无法更改.

then you will get a compiler error, because list[0] is a value, not a variable. You can't change it.

如果你说

int x = list[0].A;

然后将list[0]评估为值 -复制列表中存储的值-然后在x中复制A.因此,这里还有一个额外的副本.

then list[0] is evaluated as a value -- a copy of the value stored in the list is made -- and then a copy of A is made in x. So there is an extra copy here.

练习:编写一个程序,说明list[0]是列表中存储的值的副本.

EXERCISE: Write a program that illustrates that list[0] is a copy of the value stored in the list.

因此,您不应该(1)构造大型结构,而(2)使其不可变.结构是通过值复制的,这很昂贵,而且值不是变量,因此很难对其进行变异.

It is for this reason that you should (1) not make big structs, and (2) make them immutable. Structs get copied by value, which can be expensive, and values are not variables, so it is hard to mutate them.

是什么使数组索引器返回变量但列表索引器不返回变量?数组是否以特殊方式处理?

What makes array indexer return a variable but list indexer not? Is array treated in a special way?

是的.数组是非常特殊的类型,它们是内置于运行时的,并且从版本1开始就已经存在.

Yes. Arrays are very special types that are built deeply into the runtime and have been since version 1.

此处的主要功能是数组索引器在逻辑上产生数组中包含的变量的别名;然后可以将该别名用作变量本身.

The key feature here is that an array indexer logically produces an alias to the variable contained in the array; that alias can then be used as the variable itself.

所有其他索引器实际上是一对get/set方法,其中get返回一个值,而不是变量.

All other indexers are actually pairs of get/set methods, where the get returns a value, not a variable.

在这方面,我可以创建自己的类以使其行为与数组相同吗

Can I create my own class to behave the same as array in this regard

在C#7之前,不在C#中.您可以在IL中完成此操作,但是C#当然不知道该如何处理返回的别名.

Before C# 7, not in C#. You could do it in IL, but of course then C# wouldn't know what to do with the returned alias.

C#7增加了方法将别名返回变量的能力:ref返回.请记住,ref(和out)参数将变量作为其操作数,并导致被调用方对该变量使用别名. C#7为 locals returns 添加了执行此操作的功能.

C# 7 adds the ability for methods to return aliases to variables: ref returns. Remember, ref (and out) parameters take variables as their operands and cause the callee to have an alias to that variable. C# 7 adds the ability to do this to locals and returns as well.

这篇关于结构类型数组的性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆