XNA Creators Club Online
Page 1 of 2 (29 items) 1 2 Next >
Sort Posts: Previous Next

Avoiding int.ToString() GC garbage

Last post 1/26/2009 9:05 AM by gamefool. 28 replies.
  • 8/27/2008 6:54 AM

    Avoiding int.ToString() GC garbage

     

    I am currently reducing the number of managed bytes allocated to stop GC glitching. By commenting out most of my code I have narrowed a problem. Every time I need an int in string form ie.

    displayText = "Lives Used: " + game.LivesUsed.ToString(); 

    it allocates 7KB of managed memory every second (in 240 objects). This is trivial for just one but if I'm doing this in 30 different places then I'm going to get hit with a GC collect every 5 seconds or so *just* from one tiny section of the over all game...

    Does anyone know a good way around this? I hope I'm not going to have to store a text version of every int I want to display and update it every time the int changes!

    Kick back and chat with fellow Creator's Club members on XNA Chat!

    Check out one of this year's Dream Build Play finalists: Creed Arena. Fight your opponents in a stadium full of 100,000 spectators! Coming soon to XBOX Live Indie Games.
  • 8/27/2008 7:05 AM In reply to

    Re: Avoiding int.ToString() GC garbage

    One way is to only update the displayText in the set of LivesUsed property for eg.

    public int LivesUsed

    {

    get{return _livesUsed;}

    set{

    _displayText="Lives Used: "+value.ToString();

    _livesUsed=value;

    }

    That way its only ever set when the property changes instead of 60 times per second. this should save you a lot of garbage

    Also for added benefit, add "Lives Used: " to a resource file.


  • 8/27/2008 10:27 AM In reply to

    Re: Avoiding int.ToString() GC garbage

    conkerjo:

    set{

    _displayText="Lives Used: "+value.ToString();

    _livesUsed=value;

    }

    I'm hoping to avoid that approach because most of them aren't really set() as propertys like that...

    Kick back and chat with fellow Creator's Club members on XNA Chat!

    Check out one of this year's Dream Build Play finalists: Creed Arena. Fight your opponents in a stadium full of 100,000 spectators! Coming soon to XBOX Live Indie Games.
  • 8/27/2008 10:35 AM In reply to

    Re: Avoiding int.ToString() GC garbage

    Have you tried using a StringBuilder for the string concatenation? This will not create additional garbage when adding two strings.
  • 8/27/2008 1:32 PM In reply to

    Re: Avoiding int.ToString() GC garbage

    I'm not sure that's true Melvil ... in fact, I'm pretty sure it's quite the opposite ... when you only have a trivial concatenation to do such as two or three strings, it's better to just use the + method. StringBuilder is useful if you have to build up a large string from an unknown number of iterations, such as when building an html table in asp.net.

    The suggestion to only update the displaytext when the value actually changes is the best one ... it ensures that you only allocate when necessary (you can't avoid allocating a new string when you have to change it ... it's a value type). You probably want to refactor your code so that it works with the set technique :-)
    Joel Martinez - XNA MVP
    Blog: http://codecube.net
    Play Videos on an XNA Texture: Scurvy Media
    XNA Unit Testing: Scurvy Test
  • 8/27/2008 2:27 PM In reply to

    Re: Avoiding int.ToString() GC garbage

    Joel Martinez:
    I'm not sure that's true Melvil ... in fact, I'm pretty sure it's quite the opposite ... when you only have a trivial concatenation to do such as two or three strings, it's better to just use the + method.

    This of course totally depends on the use case. While it might be overhead for this simple case, it might be faster when he later plans to concatenate more strings. In such cases I always try both methods, see which produce less garbage and go with the better one. Of course, combined with the property method that only updates the string when necessary you can reduce the garbage creation to a minimum, that's why I'd also suggest this solution.

  • 8/27/2008 9:17 PM In reply to

    Re: Avoiding int.ToString() GC garbage

    Joel Martinez:
    The suggestion to only update the displaytext when the value actually changes is the best one ... it ensures that you only allocate when necessary (you can't avoid allocating a new string when you have to change it ... it's a value type).

    But the fact that it's a value type means that it shouldn't be putting anything on to the GC heap right? That was my understanding anyways...

    Kick back and chat with fellow Creator's Club members on XNA Chat!

    Check out one of this year's Dream Build Play finalists: Creed Arena. Fight your opponents in a stadium full of 100,000 spectators! Coming soon to XBOX Live Indie Games.
  • 8/27/2008 9:20 PM In reply to

    Re: Avoiding int.ToString() GC garbage

    reedake2:

    Joel Martinez:
    The suggestion to only update the displaytext when the value actually changes is the best one ... it ensures that you only allocate when necessary (you can't avoid allocating a new string when you have to change it ... it's a value type).

    But the fact that it's a value type means that it shouldn't be putting anything on to the GC heap right? That was my understanding anyways...

    An int is a value type, but a string is not. In this small example:

    string text = "Lives: " + lives.ToString();

    You actually are allocating three strings, two garbage and one stored in text (which likely goes out of scope at the end of the method and becomes garbage itself). It's always best to only update strings when the data changes. This is a prime reason why you should use properties for things like these. Then you could easily implement the suggestion above and be done. If you don't have it set up that way now, I would change it to set up that way. This tiny operation is allocating 180 strings per second, tons of wasted garbage that is sure to ruin your day on the Xbox 360 if you do it with other variables as well.

  • 8/27/2008 9:22 PM In reply to

    Re: Avoiding int.ToString() GC garbage

    Strings are reference types. They are a primitive type, and they are immutable (which in many ways makes them behave similar to what people are used to with value types, and means you cannot create new ones without heap allocations) but they are allocated on the heap, not the stack.
    XNA Framework Developer - blog - homepage
  • 8/28/2008 3:21 AM In reply to

    Re: Avoiding int.ToString() GC garbage

    Nick Gravelyn:
    This is a prime reason why you should use properties for things like these. Then you could easily implement the suggestion above and be done.

    Yeah, it's one of those new things to me that I've not yet got into a habbit of doing. Do you generally have *all* of your public variables as properties? Because that would be a very tedious process for some of my classes...

    Kick back and chat with fellow Creator's Club members on XNA Chat!

    Check out one of this year's Dream Build Play finalists: Creed Arena. Fight your opponents in a stadium full of 100,000 spectators! Coming soon to XBOX Live Indie Games.
  • 8/28/2008 3:33 AM In reply to

    Re: Avoiding int.ToString() GC garbage


    reedake2:

    Nick Gravelyn:
    This is a prime reason why you should use properties for things like these. Then you could easily implement the suggestion above and be done.

    Yeah, it's one of those new things to me that I've not yet got into a habbit of doing. Do you generally have *all* of your public variables as properties? Because that would be a very tedious process for some of my classes...

    I don't do it for all values, no. In fact the large majority of my variables are just public fields. I generally make a property when either A) I need to do some sort of data constraint on the input values (such as making sure a value is clamped between two values, or something similar) or B) I will be displaying the data as text. Otherwise I generally stick with public fields.

    I also take into account the data type. For things like ints, floats, and strings, there's not much difference between using a public field and property. However for a Vector2 or other struct, there is because you can no longer change a single component on the property since the value returned is actually a copy of the original data. So for those I usually use public fields so I can change a single component of the vector more easily.
  • 8/28/2008 7:35 PM In reply to
    • (69)
    • premium membership
    • Posts 68

    Re: Avoiding int.ToString() GC garbage

    Well, if you're absolutely set on getting rid of those allocations you can write your own string formatting routines that format text directly into a char[ ] and then write your own text rendering system and give it a DrawString( char[ ] textChars, int charsToDraw ) method so you can draw straight out of that array without having to call ToString all over the place. Then you just make one big text buffer at load time and keep reusing it. It's a fair bit of work to go to to get rid of a few allocations, but as you said even a few ToStrings per frame can add up.

    That's what I've done in my project. Come to think of it I should probably clean up the code and release it or something...

  • 8/29/2008 10:41 PM In reply to

    Re: Avoiding int.ToString() GC garbage

    nop:
    Come to think of it I should probably clean up the code and release it or something...

    It'd be great to have a look at! Because as you say it would be a lot of work. I think maybe making the ints properties would be the path of least resistance at the moment.

    Kick back and chat with fellow Creator's Club members on XNA Chat!

    Check out one of this year's Dream Build Play finalists: Creed Arena. Fight your opponents in a stadium full of 100,000 spectators! Coming soon to XBOX Live Indie Games.
  • 8/30/2008 12:00 AM In reply to

    Re: Avoiding int.ToString() GC garbage

    Strings are garbage. There's not much you can do about it. I do one of three things:

    1) update the strings only when the data changes and the new string is needed, and live with the little garbage.

    2) keep a cache/hash of number -> string. This is useful if the range of numbers is small (say, 0 .. 100).

    3) use an array of char instead, and draw the strings as textured billboards (a la spritefont, but with a char[/length instead of a string)

    Jon Watte, Direct3D MVP
    Tweets, occasionally
    kW X-port 3ds Max .X exporter
    kW Animation source code
  • 8/30/2008 10:06 AM In reply to

    Re: Avoiding int.ToString() GC garbage

    Very well put Jon. I'd advocate options 1 and 2 where possible, but performance issues lead me down the dark, winding road of option 3.

    The only way I've managed to avoid horrendous garbage issues when drawing large amounts of text (or small amounts of regularly-changing text; or a moderate amount of text which needs to word wrap in any sort of resizeable "window") is to discard the string data type as a means of holding displayed text, and implement my own font rendering based on efficient text buffers rather along the lines of MemoryStream (though cutting out that particular middle man). On the face of it it might seems ludicrous and backward to do so, but the performance gain has been more than quite noticable. It's a last resort, but it works.

    --formerly Genstein
  • 8/31/2008 4:32 AM In reply to

    Re: Avoiding int.ToString() GC garbage

    sigh, sometime I miss the days of and free() and delete() lol.
    Kick back and chat with fellow Creator's Club members on XNA Chat!

    Check out one of this year's Dream Build Play finalists: Creed Arena. Fight your opponents in a stadium full of 100,000 spectators! Coming soon to XBOX Live Indie Games.
  • 9/5/2008 8:25 PM In reply to

    Re: Avoiding int.ToString() GC garbage

    Strings are only immutable if you stay in safe code ;-) Its not too hard to work out how the string is laid out and modify it without creating garbage. Of course then you end up implementing your own number formating code but thats much easier than writing your own font rendering IMO

    Another idea I got from Frank's talk at GameFest is to use a struct that overlays a byte[ with a string. Than you can modify the byte[ and pass the now modified string into the .Draw method. You don't even need unsafe code to do that. I did a proof of concept but I didn't do any serious coding.

     

     

    Playtest Kissy Poo - a game for 4 year olds on Xbox and windows
    The ZBuffer
    News and information for XNA
      Follow The Zman on twitter, Email me
        Please read the forum FAQs - Bug/Feature reporting
          Don't forget to mark good answers and good playtest feedback when you see it!!!
  • 9/5/2008 9:08 PM In reply to
    • (69)
    • premium membership
    • Posts 68

    Re: Avoiding int.ToString() GC garbage

    The ZMan:
    Strings are only immutable if you stay in safe code ;-)
     

    Been there. Done that. Couldn't figure out how to set the length... The approach works fairly well, though I ended up formatting into a char[ ], and then unsafely copying into a preallocated string once the final length was known. I was basically at the point of setting up a caching system for all the different-length strings I was using when I decided to just bite the bullet and write my own font renderer (and sneak in a few extra features as well).

    The ZMan:
    Of course then you end up implementing your own number formating code but thats much easier than writing your own font rendering IMO

    You end up doing both if you go the custom font route. Of course if you do the custom font rendering engine you can do things like rendering a font at multiple sizes and picking the closest one at runtime based on screen resolution and desired vertical height, or inserting formatting codes (just color changes in my case) into strings rather than breaking them up ahead of time and drawing them with multiple Measure/Draw calls (which can get ugly if you switch colors mid-word in oddly kerned fonts).

  • 9/5/2008 9:40 PM In reply to

    Re: Avoiding int.ToString() GC garbage

    nop:

    The ZMan:
    Strings are only immutable if you stay in safe code ;-)
     

    Been there. Done that. Couldn't figure out how to set the length... The approach works fairly well, though I ended up formatting into a char[ ], and then unsafely copying into a preallocated string once the final length was known. I was basically at the point of setting up a caching system for all the different-length strings I was using when I decided to just bite the bullet and write my own font renderer (and sneak in a few extra features as well).

    Off the top of my head the 1st 2 (4?) bytes are the length... but note that only enough memory is there for the initial string. So you need to make a nice long string to 'reserve' the memory. Then you can just fill it with any shorter string and reset the length.

    I should tidy up the struct approach - worked pretty well in my PoC...

    You are right about handling all the other stuff though. Do you use the standard font XNB files or did you have to write your own exporter too.

    Playtest Kissy Poo - a game for 4 year olds on Xbox and windows
    The ZBuffer
    News and information for XNA
      Follow The Zman on twitter, Email me
        Please read the forum FAQs - Bug/Feature reporting
          Don't forget to mark good answers and good playtest feedback when you see it!!!
  • 9/5/2008 10:03 PM In reply to

    Re: Avoiding int.ToString() GC garbage

    In 3.0, you can pass a StringBuilder directly to SpriteBatch.DrawString.

    You have to be careful to format into a StringBuilder without causing allocations, but this is possible if you tickle it just right, and easier than implementing your own system entirely from scratch.


    XNA Framework Developer - blog - homepage
  • 9/5/2008 10:22 PM In reply to
    • (69)
    • premium membership
    • Posts 68

    Re: Avoiding int.ToString() GC garbage

    The ZMan:
    Do you use the standard font XNB files or did you have to write your own exporter too.

    I rolled my own. Simple enough task really. Render character into bitmap. Trim away blank space. Pack characters into Alpha8 texture. I used interop to get at the kerning data, and stopped at calling GetCharABCWidthsFloat. Still not sure if I should go all the way and use GetKerningPairs. It hasn't really seemed necessary yet.

    Most of the work was on the runtime side. When I create a font I pick a set of point sizes to render it at, and at runtime I specify font size as a fraction of the screen height and it picks the closest matching set of glyphs based on screen resolution and the available font sizes. This has been pretty useful on the XBOX because I can do things like scalling my font sizes in code by some global amount and the layout/render just works without having to have a _pc and _xbox (the same as _pc, but bigger) set of fonts. The final glyph layout stuff is dead simple but the actual line layout (word wrap, center text in rectangle, clip/don't clip, etc) is a loop of pure insanity, written with raw char pointers (since I wanted to be able to use it with either char[ ] or string data) and probably has a few bugs in it.

  • 9/6/2008 1:47 AM In reply to

    Re: Avoiding int.ToString() GC garbage

    That's a step forward, and perhaps enough. However, I wonder why there's no "DrawString(char[ ], int length, ...)" as that would be the easiest fall-back for many possible cases. If it were me, that's the method I would have implemented first, and then put the strings on top of that...
    Jon Watte, Direct3D MVP
    Tweets, occasionally
    kW X-port 3ds Max .X exporter
    kW Animation source code
  • 9/6/2008 5:54 AM In reply to

    Re: Avoiding int.ToString() GC garbage

    As suggested earlier, you could use a dictionary of int->string.
    This technique is called memoization: http://en.wikipedia.org/wiki/Memoization
    Dictionary healthMemos = new Dictionary();
    string GetHealthString(int health)
    {
    string s;
    if (!healthMemos.TryGetValue(health, out s))
    {
    s = "Health: " + health;
    healthMemos[health] = s;
    }
    return s;
    }
    Brandon Bloom
    Software Design Engineer
    XNA Platform and Tools
  • 9/6/2008 3:16 PM In reply to

    Re: Avoiding int.ToString() GC garbage

    You can get nasty and unexpected side effects by making strings mutable via pointer manipulation or reflection. Beyond the uncertainty in your own code caused by strings being mutable, string interning may cause you to get unexpected results, and mutable strings are unreliable as keys in a Dictionary<> or similar.

    The additional of a StringBuilder overload to DrawString() etc. is great, but still not quite far enough if you're doing a lot of text manipulation. You can't use string.Format() or StringBuilder.AppendFormat() without generating garbage. You can't append individual integers, floats etc. via StringBuilder.Append() or .Insert() without generating garbage. If you want allocation-free string formatting you have to write it yourself, but even appending individual characters to a StringBuilder isn't guaranteed to happen without allocation. Moreover, that's one function call per character, and if you're dealing with large volumes of text at high frame rates, the whole thing can be rather unperformant.

    Much as it's unpleasant and extreme, custom text output, formatting and word wrap based on custom memory buffers does avoid these problems. You don't break string itself, and you can get back to strings at any point. However, I still wouldn't advocate it except in extreme cases, as it's something of a pain to create.

    --formerly Genstein
  • 9/6/2008 4:18 PM In reply to

    Re: Avoiding int.ToString() GC garbage

    Ego:

    You can get nasty and unexpected side effects by making strings mutable via pointer manipulation or reflection. Beyond the uncertainty in your own code caused by strings being mutable, string interning may cause you to get unexpected results, and mutable strings are unreliable as keys in a Dictionary<> or similar.

    Absolutly, my intention was to hide the whole thing inside another class so the implementation was totally hidden and the mutable strings themselves were never exposed. Its not perfect but it would cut down on the major issues....

    Playtest Kissy Poo - a game for 4 year olds on Xbox and windows
    The ZBuffer
    News and information for XNA
      Follow The Zman on twitter, Email me
        Please read the forum FAQs - Bug/Feature reporting
          Don't forget to mark good answers and good playtest feedback when you see it!!!
Page 1 of 2 (29 items) 1 2 Next > Previous Next