8

I have the following code:

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

Now somewhere else in my code, I make about 2500 calls in a loop to Character.LevelPosition. This means that per update-cycle, 5000 'new' Vector2s are being made, and on my laptop, it really drops the framerate.

I have temporarily fixed it by creating

var levelPosition = Character.LevelPosition;

before I initiate the loop, but I kinda feel its ugly code to do this everytime I come across a similar situation. Maybe it -is- the way to go, but I want to make sure.

Is there a better or commonly accepted way to do this?

I'm using the XNA-Framework, which uses Vector2's.

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
Taelia
  • 591
  • 3
  • 20
  • Is `Vector2` a `class`? There's not much code here but it looks like it could (should?) be a `struct`, in which case it should perform way better. In any case, the fix you mention is a totally valid optimization since you know for a fact what exactly is slowing you down. – Jon Mar 06 '12 at 12:32
  • Oh, I'm sorry. I'm working in the XNA-Framework, I should've mentioned. – Taelia Mar 06 '12 at 12:33
  • Do you really need to do that 2500 times? Can't it be cut down to for example the visible area? – CodeCaster Mar 06 '12 at 12:33
  • Actually, I already cut it to the visible area. I'm drawing tiles from a tile-based game, offset by the players position. – Taelia Mar 06 '12 at 12:35
  • 3
    If the return value of the property doesn't change, and performance tests show that it's faster to call it once and put this value into a local var, then by all means do so! – Mr Lister Mar 06 '12 at 12:37
  • This would be a good time for you to read and listen to [Understanding XNA Framework Performance](http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=16477). – Andrew Russell Mar 07 '12 at 03:58

3 Answers3

5

From what I understand, you should avoid allocating lots of objects from the heap in XNA, because that causes bad performance. But since Vector2 is a struct, we're not allocating anything on the heap here, so that shouldn't be the problem here.

Now, if you have tight loop, like you do, in a performance-critical application, like a game, you will always have to think about performance, there is no going around that.

If we look at the code for LevelPosition, you call the getter for WorldPosition twice and probably some more getters. The getter for WorldPosition probably calls few other getters. (It's hard to say what exactly is going on without having the source, because getter call and field access look exactly the same.)

Call to a getter, which is actually just a call to a special method, is usually pretty fast and can be even faster if the compiler decides to use inlining. But all the calls add up together, especially if you call them in a loop.

The solution for this is some sort of caching. One option would be to make LevelPosition a field and devise a system to update it when necessary. This could work, but it could also actually hurt performance if you need to update it more often than you read it.

Another solution is, as you discovered, to cache the result in a local variable. If you know that this is correct, i.e. that the value of the property won't change during the execution of the loop, then that's awesome! You solved your performance problem and you did it with only a single line of code that's easily understandable to any programmer. What more do you want?

Let me restate that. You found a solution to your performance problem that:

  1. works
  2. is simple to implement
  3. is easy to understand

I think such solution wold be very hard to beat.

svick
  • 236,525
  • 50
  • 385
  • 514
  • Alright, this is also a very clear answer. I will play around with these solutions some more and figure out what works best for me. Thank you too! – Taelia Mar 06 '12 at 13:42
4

Creating many objects in a loop may be an expensive operation (*). Maybe if would help to create the Vector2 in advance (for example when the coordinates change) and in the future just change the coordinates.

Example:

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

EDIT
The same should be done for the LevelPosition property as well. See modified source code.

(*)
Tim Schmelter pointed me to this question with a detailed discussion about the impact of instantiating objects. I have rephrased my initial sentence that object creation is always expensive. While creating objects is not always an expensive operation, it may still slow down performance in certain cases.

Community
  • 1
  • 1
Thorsten Dittmar
  • 55,956
  • 8
  • 91
  • 139
  • I think this is a great idea. Actually it might have increased its performance over my original solution. Thank you! – Taelia Mar 06 '12 at 12:42
  • I agree you should be able to store the values, and only update them when necessary. – cgatian Mar 06 '12 at 12:43
  • 1
    When talking about speed issues, object creation is always a good starting point for speeding things up. Glad I could help. – Thorsten Dittmar Mar 06 '12 at 12:47
  • @ThorstenDittmar: Your premise that creating objects is always expensive is too categorical. http://stackoverflow.com/questions/2105011/is-it-expensive-to-create-objects-in-net It sounds as if you'd recommend to avoid objects wherever possible, then you're using the wrong language. – Tim Schmelter Mar 06 '12 at 12:52
  • I don't think this will help anything. `Vector2` is a `struct`, so creating it is very cheap. The performance hit here probably comes from the calculations and copying, which you did not improve at all. – svick Mar 06 '12 at 12:59
  • @Tim Schmelter: Not at all! I'm in no way against objects or object orientation! I just suggested that in the OP's situation it might be much faster instantiating only once and the modifying instead of instantiating every time - and as he said it worked. In the link you provided, please refer to Jon Skeet's answer, especially the following: "I strongly suspect the performance hit will be insignificant, but if you are using this in a tight loop, it may not be." The OP is creating 5000 objects in a tight loop and it *was* lowering performance significantly. – Thorsten Dittmar Mar 06 '12 at 13:04
  • @svick: Well, why would the OP then say that it actually improved the performance a lot? – Thorsten Dittmar Mar 06 '12 at 13:05
  • 1
    @ThorstenDittmar: It was only an assumtion ("might have..."). – Tim Schmelter Mar 06 '12 at 13:10
  • @Tim: Well, in the end the OP is happy with this solution and it is still proper class design - so we shouldn't be nitpicking. I agree with you that instantiating is *not always* expensive, but it seem that in this case it was. – Thorsten Dittmar Mar 06 '12 at 13:14
2

You can make a private field to store the value and not compute it each time. You can make a method to update the private fields and subscribe for the Movement.Position changes in some way. This way the value will be computed only once when position changes.

Stilgar
  • 22,354
  • 14
  • 64
  • 101
  • Subscribing might work, but isn't that a whole lot of overhead everytime I come across a similar situation? – Taelia Mar 06 '12 at 12:38
  • performance overhead or coding overhead? I doubt it is performance overhead since you need to update the values anyway. In coding... it certainly is. Maybe you can use some generic base class to ease the situation. – Stilgar Mar 06 '12 at 12:56