Search Unity

strings without GC

Discussion in 'Scripting' started by zedz, Oct 1, 2021.

  1. zedz

    zedz

    Joined:
    Aug 31, 2013
    Posts:
    253
    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)

    https://github.com/MikePopoloski/StringFormatter

    What is everyone else doing?
     
    rqhnaruto likes this.
  2. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    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
    .
     
    zedz likes this.
  3. Neto_Kokku

    Neto_Kokku

    Joined:
    Feb 15, 2018
    Posts:
    1,751
    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:
    https://github.com/Cysharp/ZString

    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.
     
    ilmario and zedz like this.
  4. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,073
    How are you using it give me an example.
     
    zedz likes this.
  5. zedz

    zedz

    Joined:
    Aug 31, 2013
    Posts:
    253
    @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 https://www.gavpugh.com/2010/04/01/xnac-avoiding-garbage-when-working-with-stringbuilder/
    I'm sort of at a loss as to why they made it like this as its possible to do this without generating garbage
     
  6. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    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.
     
  7. zedz

    zedz

    Joined:
    Aug 31, 2013
    Posts:
    253
    OK testing results approx

    hopefully the codes correct as Im adapting what Ive written

    Zstring
    Code (CSharp):
    1.  
    2. Startup
    3. for ( int i=0; i<N; i++ )
    4. {
    5.           zstr[i] = ZString.CreateStringBuilder();
    6. }
    7.  
    8. actual = ZString.PrepareUtf16<float>("{0:0.0}");
    9.  
    10. for ( int i=0; i<N; i++ )
    11. {
    12.     zstr[i].Clear();
    13.    
    14.     zstr[i].Append( i ) // for ints
    15. or
    16.     zstr[i].Append( actual.Format((float)(i * 0.1f)) ); // for floats at one decimal place = 0.0
    17. }
    stringbuilder
    Code (CSharp):
    1. for (int i = 0; i < N; i++)
    2. {
    3.     sb[i].Clear();
    4.    
    5.     sb[i].AppendFormat( "{0:0.0}", (float)(i * 0.1f) );
    6. or
    7.     sb[i].Append( i );
    8. }
    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
     
  8. zedz

    zedz

    Joined:
    Aug 31, 2013
    Posts:
    253
    Yeah this is what I do already with my text drawing I take the string and convert it into a char [] buffer;
     
  9. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    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.