【问题标题】:Performance of creating struct values创建结构值的性能
【发布时间】:2012-03-04 10:18:19
【问题描述】:

我目前正在阅读Oreilly的Learning XNA 4.0,我注意到作者很容易创建一个结构值,例如Vector2Rectangle多次而不考虑性能。

例如,SquareCollision() 方法检查两个游戏实体之间的碰撞,每次调用时都会创建两个 Rectangles,而不是简单地让每个游戏实体拥有自己的 Rectangle 字段并比较字段不再创建任何结构值。

我在很多地方多次看到这种模式,这让我想知道:
在 C# 中创建结构值可以忽略不计吗?
真的如此吗?属于微优化部分?

也许因为我的经验主要是 Java,ValueTypes 的概念对我来说很陌生,但似乎多次从结构重新创建一个值(并调用它的构造函数)似乎是对资源的巨大浪费。

【问题讨论】:

  • 我认为这有点难以描述,因为真正重要的是对整体性能的影响。即使重新创建相同的值比使用字段慢 10,000 倍,如果它仅对性能造成 0.0001% 的影响,它仍然没有多大意义。 (对不起,随意的数字,只是想确保我被理解。)我试图理解的是它实际上是多么糟糕(如果有的话)以及我是否应该尽量避免这样做。
  • 您可以通过显示代码使问题更具体一些。防止误解。
  • 我一直想知道Point p; p.X=1; p.Y=2p = new Point(1,2) 的区别。也许对于编译器来说最终没有区别。

标签: c# struct xna


【解决方案1】:

在堆栈上创建一个用于短暂使用的结构非常便宜,并且与仅为所有这些值设置局部变量没有太大区别,而是通过组件化。但是,作为对象的一部分,它将是每个对象相同数据的额外副本。

这两种方法都有效,具体取决于场景。然而,在堆栈上,没有真正的资源成本。作为对象的一部分,您需要确保没有不必要地复制数据,因为这会产生实际成本。

如果它是对象的属性,则需要复制很多次。

如果它是对象上的字段,是的:它可以就地使用而无需复制,但是:在大多数情况下,这相对较小。

与往常一样,您需要进行概要分析才能知道这是否重要。

【讨论】:

  • 在我看来,唯一的出路是使用 protected / public fields 来确保始终使用相同的结构,而不是重复它不必要的。或者也许是一个使用ref关键字通过引用接收结构的方法?
  • @Acidic 如果您直接使用结构(不分配给变量等),则不需要副本;但是是的 - 通过“ref”获取结构的方法在这种情况下相当普遍。我自己在一些疯狂优化的地方使用了这种方法。
【解决方案2】:

基本上,在你的(比如)Mesh 对象中拥有该结构的东西对你没有好处。因为在向它发出请求的那一刻,结构的值将被复制。考虑示例:

public class Mesh 
{
  public Vector3 MeshCogVector {get;set};
}

在你拥有的代码中的某处

public void CollisionDetection
{
    for(int i=0;....)
    {
       Mesh curmesh = listofmashes[i];
       Vector3 vec3 = curmesh.MeshCogVector; //value copied

    }
}

因此,在您的条件下以这种方式使用,您基本上不会获得任何好处 + 您会增加您的 Mesh 实例大小。

而不是在实际方法中使用/构造 Vector3 实例,在您真正需要它的那一刻,您可以快速进行堆栈分配和快速垃圾回收。

如果您对结构的性能不满意,可以考虑使用non safe 数据数组。但这是您应该衡量的东西,以便了解这是否适合您。

【讨论】:

  • 如果我只是使用公共字段而不是公共属性,这不会解决吗?据我了解,结构属性会在 getter 调用上创建结构的新副本。
  • @Acidic:不,它不会改变任何东西。这不是关于访问方式属性/字段/函数返回参数,而是关于C# value types。你可以尝试,如果你想在类中保存一个Vector,用ref关键字传递它像CollisionDetection这样的函数。在这种情况下,值不会被复制,而是通过引用传递。但这又是你必须衡量的东西。如果Vector 类不大,我会去你已经看到的解决方案,所以在需要的时候分配。
  • 我讨厌问这样的问题,但什么是Vector2 可能不大,但 Vector4Rectangle 呢?我猜 this 只能通过分析来回答......
  • 最好的答案可能是这个Why 16 byte is recommended size。在这种情况下,Vector 只有 3 个浮点数 xyz 仍然是“可行的”
  • @Acidic:结构类型的属性通过返回整个结构来操作,即使读取结构的代码只是读取一个字段。 struct 类型的公共字段没有这个问题。请注意,一些古老的 C# 编译器将允许写入返回的结构属性的字段(由于该属性的返回值只是一个临时值,因此没有任何效果)。当时,防止该问题的最佳方法是让结构禁止以任何方式写入字段,但在构造函数中除外。鉴于现代编译器不再允许对临时文件进行愚蠢的写入,...
【解决方案3】:

结构中存在的所有属性都作为连续内存分配存在于内存中,这意味着所有属性并排存在,因此访问每个属性,结构使 CPU 变得简单。遵循核心 OPP 概念会降低性能,例如 在课堂上,如果你有 var Power,如果你在引用这个值,那么 CPU 会引用该类中存在的所有属性位置(因为所有数据都存在于堆上的随机位置,所以 CPU 要做的工作更多)调用作为行缓存。

解决方案

[System.Serializeable]
public Struct SomeclassData(){  

Vector3 vec;
float value;
// some numeric data or vector data  }


public class SomeClass : Monobehaviour{
SomeclassData data;

void Start(){
 data = new SomeClassData();
}

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-01
    • 2023-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多