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:
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#?
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 Update: I was wrong. Write your own extension methods. A rundown is available here: Avoiding garbage when working with StringBuilderStringBuilder
are ToString()
and AppendFormat()
. None of the other formatting methods (to my knowledge) generate garbage.
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With