xbox性能陷阱

时间:2011-06-14 07:27:49

标签: c# performance xna xna-4.0 xbox

背景:我有一款游戏,我一直主要测试(低规格)笔记本电脑。它运行正常。在xbox上进行测试时,有一种方法在调用时会严重影响性能/ fps。在PC上你不会注意到任何减速/跳过帧。

我在xbox上进行过分析,平均而言,我每次获得GC约1-2次,在游戏运行时需要20-40ms。

当我的慢速方法运行时,我注意到GC速率或持续时间没有变化。

接下来,我尝试在PC上进行性能分析,以确定方法中花费的时间最多。事实证明它正在进行List<T>.Contains(),所以我创建了自己的类,内部有List<T>HashSet<T>,因此我可以在HashSet<T>内部使用Contains() }。

我现在已经达到了这一点,在没有改变算法的情况下,我无法真正想到要调整的其他内容,并且我认为算法就像它将要获得的一样简单。< / p>

我知道我无法对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 :(得分:0)

我认为您可以使用Stopwatch进行一些基本的分析。你可以考虑设置一个毫秒或迭代限制&#39;在思考方法上 - 所以它不会超过5毫秒/ 200次迭代或其他任何工作。

答案 1 :(得分:0)

这里只是猜测,但是......

while循环中重复查找和转换对我来说有点狡猾:_Owner[PositionToCheck] as TileFlowing。我很想把它作为变量拉出来,看看会发生什么。