производительность Xbox получил - PullRequest
2 голосов
/ 14 июня 2011

Справочная информация: У меня есть игра, которую я в основном тестировал на ноутбуке (с низкой спецификацией). Работает нормально. При тестировании на xbox есть один метод, который, по-видимому, серьезно влияет на производительность / fps при его вызове. На ПК вы не заметите никаких замедленных / пропущенных кадров.

Я профилировал на xbox, и в среднем я получал GC примерно 1-2 раза в секунду, занимая 20-40 мс при запуске игры.

Я не заметил изменений в скорости или продолжительности GC, когда мой медленный метод работает.

Затем я попытался выполнить профилирование на ПК, чтобы определить, что в методе заняло больше всего времени. Оказывается, он делал List<T>.Contains(), поэтому я создал свой собственный класс, который имел List<T> и HashSet<T> для внутреннего использования, поэтому я мог использовать HashSet<T> для Contains().

.

Сейчас я достиг точки, когда я не могу придумать, что еще нужно настроить, не меняя алгоритм, и я думаю, алгоритм настолько прост, насколько может быть.

Я знаю, что не могу профилировать, чтобы получить времена / проценты метода на xbox, так что я немного растерялся, что делать дальше.

Я включил приведенный ниже код, который используется для имитации проточной воды в системе на основе плитки. Он проходит один раз за плитку воды, пытаясь переместить ее вниз, возможно, через другие плитки воды (вообще говоря).

Вопрос: Я хочу знать, делаю ли я что-нибудь явно неправильно (то есть сильно ли это повлияет на производительность xbox) здесь. Звонит ли Func медленно? Я получаю бокс где-нибудь с моими Point объектами? и т.д.

Извинения за огромное количество кода! Сами плитки приходят из пула объектов, чтобы минимизировать GC. Метод InternalThink() - вот что вызывает все проблемы.

public abstract class TileFlowing : TileMovable
{
    private FastList<Point> TeleportSwapLocations = new FastList<Point>();

    private FastList<Point> PossibleMoveLocations = new FastList<Point>();

    private FastQueue<Point> PositionsToCheck = new FastQueue<Point>();

    private FastList<Point> PositionsChecked = new FastList<Point>();

    private static Comparison<Point> _PossibleMoveComparer;

    public bool Static = false;

    protected abstract Func<Point, int> PossibleMoveLocationOrdering { get; }

    protected abstract Func<Point, Point, bool, bool> MoveSidewaysFunc { get; }

    protected abstract int MaxUnitsWithin { get; }

    protected abstract int Weight { get; }

    public int UnitsWithin;

    public int AvailableToFlowThrough = 0;

    protected virtual bool RecurseTilesUp { get { return true; } }
    protected virtual bool RecurseTilesDown { get { return true; } }
    protected virtual bool RecurseTilesLeft { get { return true; } }
    protected virtual bool RecurseTilesRight { get { return true; } }

    public TileFlowing()
        : base()
    {

    }

    public override void LoadContent(Components.TileGridManagement.GameGrid Owner)
    {
        base.LoadContent(Owner);

        _PossibleMoveComparer = (Point P1, Point P2) =>
        {
            int Result = PossibleMoveLocationOrdering(P1) -
                            PossibleMoveLocationOrdering(P2);
            if (Result == 0)
                Result = (IsSameType(P1) ? (_Owner[P1] as TileFlowing).UnitsWithin : 0) -
                            (IsSameType(P2) ? (_Owner[P2] as TileFlowing).UnitsWithin : 0);

            return Result;
        };
    }

    public override void ResetProperties()
    {
        base.ResetProperties();

        Static = false;
        UnitsWithin = MaxUnitsWithin;
        AvailableToFlowThrough = 0;
    }

    public override void CopyProperties(Tile SourceTile)
    {
        base.CopyProperties(SourceTile);

        Static = (SourceTile as TileFlowing).Static;
        UnitsWithin = (SourceTile as TileFlowing).UnitsWithin;
        AvailableToFlowThrough = (SourceTile as TileFlowing).AvailableToFlowThrough;
    }

    public override void Think()
    {
        base.Think();

        InternalThink(false, false);
    }

    public override void FactoryThink()
    {
        base.FactoryThink();

        InternalThink(true, false);
    }

    public void FlowThink(bool CalledFromFactoryThink)
    {
        InternalThink(CalledFromFactoryThink, true);
    }

    private bool IsSameType(Point Position)
    {
        return IsSameType(Position.X, Position.Y);
    }

    private bool IsSameType(int X, int Y)
    {
        return _Owner[X, Y] != null && _Owner[X, Y].GetType() == GetType();
    }

    private bool IsDifferentFlowingTile(Point Position)
    {
        return IsDifferentFlowingTile(Position.X, Position.Y);
    }

    private bool IsDifferentFlowingTile(int X, int Y)
    {
        return !IsSameType(X, Y) && _Owner[X, Y] is TileFlowing;
    }

    protected void CheckPosition(Point PositionToCheck, Point TilePosition, bool CalledFromFactoryThink, bool CalledFromFlowThink,
                                 ref FastList<Point> PossibleMoveLocations, ref FastList<Point> TeleportSwapLocations, ref FastQueue<Point> PositionsToCheck,
                                 Func<Point, Point, bool, bool> ClearCheckFunc)
    {
        if (IsSameType(PositionToCheck))
        {
            if (!PositionsToCheck.Contains(PositionToCheck))
                PositionsToCheck.Enqueue(PositionToCheck);
        }
        else if (_Owner[PositionToCheck] is TileFlowing && (ClearCheckFunc == null || ClearCheckFunc(PositionToCheck, TilePosition, CalledFromFactoryThink)))
        {
            // If we weigh more than the other tile, or we're called from the factory think (are under pressure)
            if ((_Owner[PositionToCheck] as TileFlowing).Weight < Weight || CalledFromFactoryThink)
            {
                if (!(_Owner[PositionToCheck] as TileFlowing).Static || !CalledFromFlowThink)
                    PossibleMoveLocations.Add(PositionToCheck);
            }
        }
        else if (_Owner.IsClear(PositionToCheck) && (ClearCheckFunc == null || ClearCheckFunc(PositionToCheck, TilePosition, CalledFromFactoryThink)))
        {
            PossibleMoveLocations.Add(PositionToCheck);
        }
    }

    private int PossibleMoveLocationsComparer(Point P1, Point P2)
    {
        return (PossibleMoveLocationOrdering(P1) - PossibleMoveLocationOrdering(P2)) * 1000 +
               ((IsSameType(P1) ? (_Owner[P1] as TileFlowing).UnitsWithin : 0) - (IsSameType(P2) ? (_Owner[P2] as TileFlowing).UnitsWithin : 0)) * 100;
    }

    protected void InternalThink(bool CalledFromFactoryThink, bool CalledFromFlowThink)
    {
        AvailableToFlowThrough = 0;

        TeleportSwapLocations.Clear();

        PossibleMoveLocations.Clear();

        PositionsToCheck.Clear();

        PositionsChecked.Clear();

        PositionsToCheck.Enqueue(Position);

        while (PositionsToCheck.Count != 0)
        {
            Point PositionToCheck = PositionsToCheck.Dequeue();

            if (!PositionsChecked.Contains(PositionToCheck) &&
                ((_Owner[PositionToCheck] as TileFlowing).AvailableToFlowThrough < MaxUnitsWithin || CalledFromFactoryThink))
            {
                if (((_Owner[PositionToCheck] as TileFlowing).Static && !CalledFromFactoryThink))
                    continue;

                if (PositionToCheck != Position)
                {
                    (_Owner[PositionToCheck] as TileFlowing).AvailableToFlowThrough++;
                }

                PositionsChecked.Add(PositionToCheck);

                if ((_Owner[PositionToCheck] as TileFlowing).UnitsWithin < MaxUnitsWithin && PositionToCheck != Position)
                {
                    PossibleMoveLocations.Add(PositionToCheck);

                    if (CalledFromFactoryThink && (_Owner[PositionToCheck] as TileFlowing).UnitsWithin + UnitsWithin <= MaxUnitsWithin)
                        continue;
                }

                // Check below
                Point PosBelow = new Point(PositionToCheck.X + TileDirection.Down.X, PositionToCheck.Y + TileDirection.Down.Y);

                CheckPosition(PosBelow, Position, CalledFromFactoryThink, CalledFromFlowThink, ref PossibleMoveLocations, ref TeleportSwapLocations, ref PositionsToCheck, null);

                // Check one horizontal direction
                Point RandHDir = Randomiser.GetHDirection();

                Point RandHPos = new Point(RandHDir.X + PositionToCheck.X, RandHDir.Y + PositionToCheck.Y);

                CheckPosition(RandHPos, Position, CalledFromFactoryThink, CalledFromFlowThink, ref PossibleMoveLocations, ref TeleportSwapLocations, ref PositionsToCheck, MoveSidewaysFunc);

                // Check the other horizontal direction
                Point OtherHDir = new Point(-RandHDir.X, RandHDir.Y);

                Point OtherHPos = new Point(OtherHDir.X + PositionToCheck.X, OtherHDir.Y + PositionToCheck.Y);

                CheckPosition(OtherHPos, Position, CalledFromFactoryThink, CalledFromFlowThink, ref PossibleMoveLocations, ref TeleportSwapLocations, ref PositionsToCheck, MoveSidewaysFunc);

                // Check above if appropriate
                Point AbovePos = new Point(PositionToCheck.X + TileDirection.Up.X, PositionToCheck.Y + TileDirection.Up.Y);

                if (TileDirection.Below(AbovePos, Position) || CalledFromFactoryThink)
                {
                    CheckPosition(AbovePos, Position, CalledFromFactoryThink, CalledFromFlowThink, ref PossibleMoveLocations, ref TeleportSwapLocations, ref PositionsToCheck, null);
                }
            }
        }

        PossibleMoveLocations.Sort(_PossibleMoveComparer);

        bool Moved = false;

        if (PossibleMoveLocations.Count != 0)
        {
            if (CalledFromFactoryThink)
            {
                while (UnitsWithin != 0 && PossibleMoveLocations.Count != 0)
                {
                    int OldUnitsWithin = UnitsWithin;

                    Moved = IterateTeleport(CalledFromFactoryThink, ref PossibleMoveLocations, (P) => !IsDifferentFlowingTile(P), Moved);

                    if (UnitsWithin == OldUnitsWithin)
                    {
                        Moved = IterateTeleport(CalledFromFactoryThink, ref PossibleMoveLocations, (P) => IsDifferentFlowingTile(P), Moved);
                    }

                    PossibleMoveLocations.RemoveAll(P => IsSameType(P) && (_Owner[P] as TileFlowing).UnitsWithin == MaxUnitsWithin);
                }
            }
            else
            {
                Moved = Moved || Teleport(PossibleMoveLocations[0]);
            }

            // If we did move and not because we were forced to then mark all mercury tiles above and left or right as not static.
            if (!CalledFromFactoryThink)
            {
                _Owner.RecurseTiles(Position,
                                    (P) => RecurseTilesUp && IsSameType(P.X + TileDirection.Up.X, P.Y + TileDirection.Up.Y),
                                    (P) => RecurseTilesDown && IsSameType(P.X + TileDirection.Down.X, P.Y + TileDirection.Down.Y),
                                    (P) => RecurseTilesLeft && IsSameType(P.X + TileDirection.Left.X, P.Y + TileDirection.Left.Y),
                                    (P) => RecurseTilesRight && IsSameType(P.X + TileDirection.Right.X, P.Y + TileDirection.Right.Y),
                                    (P) =>
                                    {
                                        if (IsSameType(P))
                                            (_Owner[P] as TileFlowing).Static = false;
                                    });
            }
        }
        else
        {
            // Mark this tile as static if we didn't move, no water moved through this tile and we have all the units we can take.
            Static = (AvailableToFlowThrough == 0) && (UnitsWithin == MaxUnitsWithin); // TODO: 9 Fix flowing tiles becoming static and getting stuck
        }

        if (!Moved)
        {
            // If we haven't moved
            if (TeleportSwapLocations.Count != 0)
            {
                Moved = TeleportSwap(TeleportSwapLocations[0]);
            }

            if(!Moved)
            {
                // If we didn't move, undo checked tiles
                foreach (var CheckedPosition in PositionsChecked)
                {
                    (_Owner[CheckedPosition] as TileFlowing).AvailableToFlowThrough--;
                }
            }
        }
    }

    private bool IterateTeleport(bool CalledFromFactoryThink, ref FastList<Point> SortedPossibleMoveLocations, Func<Point, bool> PossibleMoveLocationsFilter, bool Moved)
    {
        foreach (var PossibleMoveLocation in SortedPossibleMoveLocations)
        {
            if (PossibleMoveLocationsFilter(PossibleMoveLocation))
            {
                if (IsDifferentFlowingTile(PossibleMoveLocation))
                {
                    bool OldStatic = Static;

                    Static = true;
                    (_Owner[PossibleMoveLocation] as TileFlowing).FlowThink(CalledFromFactoryThink);
                    Static = OldStatic;
                }

                bool TeleportResult = Teleport(PossibleMoveLocation);
                Moved = Moved || TeleportResult;

                if (TeleportResult)
                    break;
            }
        }
        return Moved;
    }

    protected bool TeleportSwap(Point NewPosition)
    {
        TileFlowing OurNewTile = (TileFlowing)Tile.GetNewTileFromStore(this);
        OurNewTile.CopyProperties(this);
        OurNewTile.Position = NewPosition;

        TileFlowing ReplacedTile = (TileFlowing)Tile.GetNewTileFromStore(_Owner[NewPosition]);
        ReplacedTile.CopyProperties(_Owner[NewPosition]);
        ReplacedTile.Position = Position;

        _Owner.ClearTile(NewPosition);
        _Owner.AddTileToGrid(OurNewTile);

        _Owner.ClearTile(Position);
        _Owner.AddTileToGrid(ReplacedTile);

        UnitsWithin = 0;

        return true;
    }

    protected bool Teleport(Point NewPosition)
    {
        if (IsDifferentFlowingTile(NewPosition))
        {
            return TeleportSwap(NewPosition);
        }
        else
        {

            TileFlowing NewTile;

            bool RemovedAllUnits = false;

            int NewPositionUnits = IsSameType(NewPosition) ? (_Owner[NewPosition] as TileFlowing).UnitsWithin : 0;

            int UnitsToRemove = Math.Min(UnitsWithin,
                                         Math.Max(1,
                                                  Math.Min(Math.Abs(UnitsWithin - NewPositionUnits) / 2,
                                                           MaxUnitsWithin - NewPositionUnits)));

            UnitsWithin -= UnitsToRemove;

            if (IsSameType(NewPosition))
            {
                (_Owner[NewPosition] as TileFlowing).UnitsWithin += UnitsToRemove;
            }
            else
            {
                NewTile = (TileFlowing)Tile.GetNewTileFromStore(this);

                NewTile.Position = NewPosition;
                NewTile.UnitsWithin = UnitsToRemove;

                _Owner.AddTileToGrid(NewTile);
            }

            if (UnitsWithin == 0)
            {
                _Owner.ClearTile(Position);
                RemovedAllUnits = true;
            }

            return RemovedAllUnits;
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 14 июня 2011

Здесь просто дикое предположение, но ...

Этот повторный поиск и приведение в цикле while выглядит для меня немного странно: _Owner[PositionToCheck] as TileFlowing.Я хотел бы вытащить его как переменную и посмотреть, что произойдет.

0 голосов
/ 14 июня 2011

Вы можете использовать Stopwatch для базового профилирования, я думаю.И вы могли бы рассмотреть установку миллисекунды или итерационного «предела» для метода Think, чтобы он никогда не занимал более 5 мс / 200 итераций или что-либо еще работающее.

...