Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mutable strings in WP7 C#/XNA?

I'm working on a Windows Phone 7 XNA game. It's a port of a game written in C++, and as such, I'm trying to do as little rewriting of gameplay code as possible.

Garbage is a huge problem on WP7, because the collector is nongenerational and slow, so a collection (which is triggered every 1MB) takes about 10ms per MB of allocations. I fully intend to be using the maximum 90MB available, so we're looking at a ~900ms stall every MB of allocation.

I've been able to rework things so that we don't have garbage generated per-frame, except for a few cases of strings.

It seems that StringBuilder.ToString() generates garbage, and the method described here doesn't work on WP7.

The two things I need to do are:

  • Format minutes/seconds/hundreths as mm:ss.hh for display to the screen. Apparently I can do that with a StringBuilder (using extension methods that don't create garbage from boxing the ints) and display the StringBuilder directly with SpriteBatch.
  • Split a string of the form "foo.bar.baz.qux" into an array on '.', i.e. {"foo", "bar", "baz", "qux"} and copy one element at a time into an array of strings. This is for setting the hierarchical state of game actors. It's also pretty much directly ported from the original game, and quite a bit depends on it working this way. I'd really like to avoid rewriting it.

Short of converting a lot of code to use char[] instead of string, is there any way to have truly garbage-free mutable strings in C#?

like image 980
jlongstreet Avatar asked Sep 08 '11 18:09

jlongstreet


1 Answers

Why do you need to convert a StringBuilder to a String in the first place? As you've noted, you can pass StringBuilder directly into XNA's drawing methods. You can also use StringBuilder to retrieve substrings:

substringBuilder.Length = 0;
for (int i = start; i <= end; i++)
    substringBuilder.Append(originalBuilder[i]);

The only things you want to avoid with StringBuilder are ToString() and AppendFormat(). None of the other formatting methods (to my knowledge) generate garbage. Update: I was wrong. Write your own extension methods. A rundown is available here: Avoiding garbage when working with StringBuilder

It's relatively easy to write a method that will split a string into substrings based on a given delimiter. Just remember that String.Split() is evil for two reasons -- first, because it allocates new strings; and second, because it allocates a new array. The immutability of strings is only half of your problem.

Consider the following extension methods for StringBuilder. I haven't thoroughly tested this code, but it should give you a general idea. Note that it expects you to pass in a pre-allocated array, which it will then populate.

public static void Substring(this StringBuilder source, Int32 start, Int32 count, StringBuilder output)
{
    output.Length = 0;
    for (int i = start; i < start + count; i++)
    {
        output.Append(source[i]);
    }
}

public static int Split(this StringBuilder source, Char delimiter, StringBuilder[] output)
{
    var substringCount = 0;
    var substringStart = 0;
    for (int i = 0; i < source.Length; i++)
    {
        if (source[i] == delimiter)
        {
            source.Substring(substringStart, i - substringStart, output[substringCount]);
            substringCount++;
            substringStart = i + 1;
        }
    }
    if (substringStart < source.Length - 1)
    {
        source.Substring(substringStart, source.Length - substringStart, output[substringCount]);
        substringCount++;
    }
    return substringCount;
}
like image 129
Cole Campbell Avatar answered Nov 03 '22 00:11

Cole Campbell