strings without GC

OK Im nearing completion of my multiyear game, so time to optimize etc

The thing that takes the most unnecessary time is string formatting
I’m using StringBuilder (I have also tried standard strings)

I’m caching results, trying to keep things to a minimum etc, but its time to bite the bullet and just fix the string formatting

I can’t understand why it generates so much garbage, esp since if the following code proposes is correct all this garbage it generates is unnecessary (I couldnt get it to compile though)

What is everyone else doing?

2 Likes

How much string work are you doing per frame that it’s a performance bottleneck? Make sure you’re only updating data, like the UI/HUD, when it actually changes. TextMeshPro’s objects also have a SetText() which is more performant than using .text.

1 Like

The page you linked explains why there’s so much garbage: StringBuilder first needs to box any value type parameters, calls ToString() on each of them, and also needs to allocate an args array, among other things.

Check the ZString library, it’s compatible with Unity:

Make sure to read their documentation thoroughly, as there are important differences to using StringBuilder and information on how to avoid allocations when passing the result to TMP.

2 Likes

How are you using it give me an example.

1 Like

@GroZZleR Its not a lot (I’m not doing a word processor :)) but its like 0.5 msec. Yes like I wrote I am caching as much as possible. I’m not using textmeshpro, I started before this was avialable, I may change over later

@Neto_Kokku thanks a lot for the link, I’ll check this out first in a small project

@koirat here are the methods that generate garbage with StringBuilder XNA/C# – Avoiding garbage when working with StringBuilder « Gavin Pugh
I’m sort of at a loss as to why they made it like this as its possible to do this without generating garbage

If you switch to TextMeshPro, you can also pretend like it’s C you’re writing and just deal with a ‘\0’-terminated char[ ] buffer.

I assume you’ve already gone through and made sure that all the text handling is actually for text on the screen, not eg. sending information from one system to another.

So the inline documentation for SetText is “This function is the same as using the text property to set the text”. Then again, the implementation of SetText and the .text setters are different. I found a bug in one of them, and the dev was all like “why are you using that one and not the other one?” when I posted about it.

But both are just setting the backing m_text to the input, so GC-wise there shouldn’t be any difference, they’re not copying anything anywhere.

OK testing results approx

hopefully the codes correct as Im adapting what Ive written

Zstring

Startup
for ( int i=0; i<N; i++ )
{
          zstr[i] = ZString.CreateStringBuilder();
}

actual = ZString.PrepareUtf16<float>("{0:0.0}");

for ( int i=0; i<N; i++ )
{
    zstr[i].Clear();
   
    zstr[i].Append( i ) // for ints
or
    zstr[i].Append( actual.Format((float)(i * 0.1f)) ); // for floats at one decimal place = 0.0
}

stringbuilder

for (int i = 0; i < N; i++)
{
    sb[i].Clear();
   
    sb[i].AppendFormat( "{0:0.0}", (float)(i * 0.1f) );
or
    sb[i].Append( i );
}

N = 1000, results from profiler (so not gonna be 100% accurate)

StringBuilder with int GC = 31kb, time = 0.5 msec
ZString with int GC 0kb, time = 0.14msec

StringBuilder with float 0.0 GC = 600kb, time = 3.83 msec
ZString with float 0.0 GC 33kb, time = 1.5msec

I’m not sure why ZString with float is still generating garbage? Am I doing it right?

I notice the parsing results are not exactly the same always for floats

Yeah this is what I do already with my text drawing I take the string and convert it into a char buffer;

I haven’t used ZString, but at a glance, it only has zero allocations in the process of the concatenation itself. The float.ToString() part can still generate garbage.