Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

Consider following example. I have a title screen for a game which displays top scores list, and some other text. The Update method looks like (simplified):

// remove all text
textList.Clear();

// add scores
foreach(var sc in scores)
{
    textList.Add(new GameText(string.Format("{0}: {1}", sc.Name, sc.Score));
}

If I understand all correctly, then each frame 10 new strings are created, polluting memory at high rate. Currently I fight it with creating a cache:

// this is a class level variable, outside of Update method of course
List<GameText> scoreCache = new List<GameText>();

// remove all text
textList.Clear();

// add scores to cache if needed
if(scoreCache.Count == 0)
{
    foreach(var sc in scores)
    {
        scoreCache.Add(new GameText(string.Format("{0}: {1}", sc.Name, sc.Score));
    }
}

// get fast copy from cache 
// no new strings created until score cache is invalidated
textList.AddRange(scoreCache);

I wonder, if there are some other reliable solutions and patterns to this common, as I see it, problem?

Update. As @JasonH correctly mentioned, there is no need to update list each time at all, so now scoreCache is initialized once in a LoadContent-like method which is called once before main Game loop starts.

Update 2. Another similar problem was with a debug text layer output which displays FPS, Total game time, etc. on top of all screens. Despite using StringBuilder, intermediate strings were required for creation of final debug text for each frame. I've decided to solve it this way, since I don't actually need per frame response, I update this only each second:

private void PrepareDebugText(GameTime gameTime)
{
    this.refreshAccumulator += gameTime.ElapsedGameTime.TotalMilliseconds;

    // if required time is not elapsed yet
    if (this.refreshAccumulator < RefreshIntervalMs)
    {
        // do nothing
        return;
    }

    // reset accumulator
    this.refreshAccumulator = 0;

    // create debug text below
    // ...
}

Applying this two approaches gives following:

  • reduced garbage collection frequency by 2 (from 60-70 seconds to 130-140 seconds)
  • reduced peak heap allocation from 3,4 Mb to 1,5 Mb
  • reduced short life objects to less than 1% from more than 50%.

Now I'm satisfied.

Update 3. @GeorgeDuckett found an interesting approach example how we can further reduce string pollution then output is integer number.

I also would like to clarify why strings are so specific in XNA (.NET). Despite being a reference type, on each assign (i.e. =) operation new string is created instead of assigning reference. This can produce not very obvious amounts of garbage strings.

share|improve this question
5  
Do you really have a problem with this? When you are displaying the top scores list you are in a non critical section of your game... the garbage collector will work without causing problems... you should worry when you have a real problem.... not now –  Blau Apr 18 '12 at 8:24
    
Personally I think caching to memory, as you have done in the second example, is probably more suited. However, is there a reason why you are clearing and re-adding the collection each Update cycle? Wouldn't it be more appropriate to just invoke this process once when the high score is displayed? –  Jason H Apr 18 '12 at 9:33
    
@Blau: In his example it's probably ok, but in the more general case it would be useful to see other's approaches. –  George Duckett Apr 18 '12 at 9:38
    
@Blau you are right, but this doesn't make this situation normal. Usually I start to worry then I see potential problem. This is one. This also usually indicate that I'm doing something not in the right way. –  Petr Abdulin Apr 18 '12 at 10:21
    
@JasonH you are absolutely right, just realized that not long before you comment. High scores are now read once at LoadContent-like method. –  Petr Abdulin Apr 18 '12 at 10:23
show 8 more comments

2 Answers

up vote 1 down vote accepted

So here is general tips:

  1. Don't create static (i.e. which doesn't change) strings in Update, create then in Initialize or LoadContent.

  2. If string is changed occasionally then cache it, and invalidate cache on text change.

  3. Finally, try to reduce string update rate if applicable. For example if you displaying game time with seconds precision, there is no need to update it more frequently then once in a second (this reduces string creation rate by your FPS value).

  4. Suggested by @GeorgeDuckett use StringBuilder optimized routines for displaying integer numbers. My implementation can be seen here.

share|improve this answer
add comment

Generally, what you want to do is create an object pool. It is like your buffer, but managed.

In practice, you would create the pool with a set number of objects; instead of throwing the object out when you are done, you set it to "inactive" and give it back to the pool. When you need to create a new object, just ask the pool for one. It will give you an inactive one and set it's state to active for you.

Keep in mind that you want to carefully tune your pool to contain just enough objects. Pretend you give it 10,000 strings. The GC is going to have to iterate over all 10,000 to see if any are not referenced.* Being that they all will be, it will have been wasted GC time. The best thing is to tune it to just what you need.

See a good implementation of a generic pool here, and you can look at his implementation of an interpolator on how to use it. Let me know if you have any questions.

* There are exceptions. I think if you use an array to hold them then it is a bit different. Don't quote me on that though

share|improve this answer
    
Note that since strings are immutable (in C#) an object pool containing them would need to be specialised (if useful at all). Typically you'd put mutable objects in the pool - objects that could have properties reset / changed / re-initialised. –  George Duckett Apr 19 '12 at 7:03
    
My initial cache example is similar to Pool, except it's much more basic pattern. The problem is (with Debug layer output) is that I don't know content of the string, thus no reason to put them in Pool. –  Petr Abdulin Apr 19 '12 at 7:52
    
@george duckett, yes great point. Thank you for pointing that out. –  Joe Apr 19 '12 at 14:35
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.