«новое» ключевое слово в getter> производительность снизилась? - PullRequest
8 голосов
/ 06 марта 2012

У меня есть следующий код:

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); }
    }
}

Теперь где-то еще в моем коде я делаю около 2500 вызовов в цикле в Character.LevelPosition. Это означает, что за цикл обновления производится 5000 «новых» векторов Vector2, а на моем ноутбуке это действительно снижает частоту кадров.

Я временно исправил это, создав

var levelPosition = Character.LevelPosition;

до того, как я инициирую цикл, но я чувствую, что его уродливый код делает это каждый раз, когда я сталкиваюсь с подобной ситуацией. Может быть, это путь, но я хочу убедиться.

Есть ли лучший или общепринятый способ сделать это?

Я использую XNA-Framework, который использует Vector2.

Ответы [ 3 ]

5 голосов
/ 06 марта 2012

Из того, что я понимаю, вам следует избегать выделения большого количества объектов из кучи в XNA, потому что это приводит к плохой производительности. Но поскольку Vector2 - это struct, мы не размещаем здесь ничего в куче, так что здесь проблем не должно быть.

Теперь, если у вас есть замкнутый контур, как у вас, в приложении, критичном к производительности, например, в игре, вам всегда придется думать о производительности, нет никакого обхода.

Если мы посмотрим на код для LevelPosition, вы дважды вызываете геттер для WorldPosition и, возможно, еще несколько геттеров. Получатель для WorldPosition, вероятно, вызывает несколько других получателей. (Трудно сказать, что именно происходит без источника, потому что вызов геттера и доступ к полю выглядят совершенно одинаково.)

Вызов getter, который на самом деле является просто вызовом специального метода, обычно довольно быстрый и может быть даже быстрее, если компилятор решит использовать встраивание. Но все вызовы складываются вместе, особенно если вы вызываете их в цикле.

Решением для этого является своего рода кэширование. Одним из вариантов было бы сделать LevelPosition полем и разработать систему для его обновления при необходимости. Это может сработать, но может также снизить производительность, если вам нужно обновлять его чаще, чем вы читаете.

Другое решение, как вы обнаружили, заключается в кэшировании результата в локальной переменной. Если вы знаете, что это правильно, то есть, что значение свойства не изменится во время выполнения цикла, то это здорово! Вы решили свою проблему с производительностью и сделали это с помощью всего одной строки кода, которая легко понятна любому программисту. Что еще ты хочешь?

Позвольте мне повторить это. Вы нашли решение проблемы с производительностью, которая:

  1. работает
  2. просто реализовать
  3. легко понять

Я думаю, что такое решение будет очень трудно победить.

4 голосов
/ 06 марта 2012

Создание многих объектов в цикле может быть дорогой операцией (*).Может быть, если это поможет создать 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.См. Модифицированный исходный код.

(*)
Тим Шмельтер указал мне на этот вопрос с подробным обсуждением влияния создания объектов.Я перефразировал мое первоначальное предложение, что создание объекта стоит всегда дорого.Хотя создание объектов не всегда является дорогостоящей операцией, в некоторых случаях оно все же может снижать производительность.

2 голосов
/ 06 марта 2012

Вы можете создать приватное поле для хранения значения, а не вычислять его каждый раз.Вы можете создать метод для обновления приватных полей и подписки на изменения Movement.Position.Таким образом, значение будет вычислено только один раз при изменении положения.

...