getter中的'new'关键字>性能打击?

时间:2012-03-06 12:30:28

标签: c# performance xna new-operator getter

我有以下代码:

public class Character
{
    public Vector2 WorldPixelPosition
    {
        get { return Movement.Position; }
    }
    public Vector2 WorldPosition
    {
        get { return new Vector2(Movement.Position.X / Tile.Width, Movement.Position.Y / Tile.Height); }
    }
    public Vector2 LevelPosition
    {
        get { return new Vector2(WorldPosition.X % Level.Width, WorldPosition.Y % Level.Height); }
    }
}

现在我的代码中的其他地方,我在循环到Character.LevelPosition中进行了大约2500次调用。 这意味着每个更新周期,正在制作5000个“新”Vector2,而在我的笔记本电脑上,它确实会降低帧速率。

我已经通过创建

暂时修复了它
var levelPosition = Character.LevelPosition;

在我开始循环之前,但是每次遇到类似的情况时,我都会感觉到它的丑陋代码。也许它是 - 可行的方式,但我想确定。

有更好或普遍接受的方法吗?

我正在使用XNA-Framework,它使用Vector2

3 个答案:

答案 0 :(得分:5)

据我所知,你应该避免在XNA中从堆中分配大量对象,因为这会导致性能不佳。但由于Vector2struct,我们在这里没有在堆上分配任何东西,所以这不应该是问题。

现在,如果你像在游戏中那样在性能关键型应用程序中进行紧密循环,那么你将始终不得不考虑性能,而不会出现这种情况。

如果我们查看LevelPosition的代码,您可以将WorldPosition的getter调用两次,可能还需要更多的getter。 WorldPosition的getter可能会调用其他几个getter。 (如果没有源代码,很难说究竟发生了什么,因为getter调用和字段访问看起来完全一样。)

调用getter,实际上只是对特殊方法的调用,通常非常快,如果编译器决定使用内联,则可以更快。但所有的调用都加在一起,特别是如果你在一个循环中调用它们。

解决方案是某种缓存。一种选择是使LevelPosition成为一个字段并设计一个系统以在必要时更新它。这可能有效,但如果你需要更频繁地更新它,它实际上也可能会损害性能。

正如您所发现的,另一种解决方案是将结果缓存在局部变量中。如果你知道这是正确的,即在执行循环期间属性的值不会改变,那就太棒了!您解决了性能问题,只需使用一行代码即可让任何程序员轻松理解。你还想要什么?

让我重申一下。您找到了解决性能问题的解决方案:

  1. 工作原理
  2. 易于实施
  3. 很容易理解
  4. 我认为这样的解决方案很难被击败。

答案 1 :(得分:4)

在循环中创建许多对象可能是一项昂贵的操作(*)。也许如果有助于提前创建Vector2(例如当坐标发生变化时)以及将来只更改坐标。

示例:

public class Character
{
    private Vector2 m_worldPosition = new Vector2(0, 0);
    private Vector2 m_levelPosition = new Vector2(0, 0);

    ....

    public Vector2 WorldPosition
    {
        get
        {
            m_worldPosition.X = ...;
            m_worldPosition.Y = ...;
            return m_worldPosition;
        }
    }

    public Vector2 LevelPosition
    {
        get
        {
            m_levelPosition.X = ...;
            m_levelPosition.Y = ...;
            return m_levelPosition;
        }
    }
}

修改
LevelPosition属性也应该这样做。请参阅修改后的源代码。

<强>(*)
Tim Schmelter向我指出this question详细讨论了实例化对象的影响。我重写了我的第一句话,即对象创建总是昂贵。虽然创建对象并不总是一项昂贵的操作,但在某些情况下仍可能会降低性能。

答案 2 :(得分:2)

您可以创建一个私有字段来存储值,而不是每次都计算它。您可以创建一个方法来更新私有字段,并以某种方式订阅Movement.Position更改。这样,当位置改变时,该值仅计算一次。