Author Topic: StringBuilder Extensions  (Read 4890 times)

Jasper Flick

  • Global Moderator
  • Jr. Member
  • *****
  • Posts: 85
StringBuilder Extensions
« on: February 18, 2016, 08:43:07 AM »
Since version 0.1.54 beta 1, TextMesh Pro has a SetText method with a StringBuilder parameter.

The System.Text.StringBuilder object is useful when you create a lot of dynamic text. Using it will prevent the creation and subsequent garbage collection of many temporary string objects.

However, you will notice that appending ints or floats to a StringBuilder will still create temporary strings. This happens because the StringBuilder.Append method invokes ToString on those numbers, appending the result.

I have written a collection of extension methods for StringBuilder that append ints and floats without any allocations. You can control the amount of digits and decimals, use group separators, and adjust which symbols to use for them.

Here is an example component that uses a StringBuilder to display the unfiltered frame rate. It displays two decimals with three leading digits.

Code: [Select]
using UnityEngine;
using System.Text;
using TMPro;

public class StringBuilderExample : MonoBehaviour {

TMP_Text text;

StringBuilder builder;

void Awake () {
text = GetComponent<TMP_Text>();
}

void Update () {
if (builder == null) {
builder = new StringBuilder();
}
builder.Length = 0;
builder.Append("fps ");
builder.AppendFloat(1f / Time.deltaTime, 2, 3);
text.SetText(builder);
}
}

[ Guests cannot view attachments ]

[ Guests cannot view attachments ]
« Last Edit: February 18, 2016, 08:46:42 AM by Jasper Flick »

somadevs

  • Newbie
  • *
  • Posts: 36
Re: StringBuilder Extensions
« Reply #1 on: May 05, 2016, 08:57:31 AM »
Very nice! I didn't even realize there was a SetText() - combined with these extensions it should really cut down on throwaway strings and putting trash on the heap... :)

PS. Thanks for your Catlike Coding tutorials! They are awesome  8)

fippydarkpaw

  • Newbie
  • *
  • Posts: 2
Re: StringBuilder Extensions
« Reply #2 on: December 20, 2016, 04:26:26 PM »
Jasper,

Thanks for the extension, but I have a question.

I'm still getting allocations using your extensions. Should I be able to generate Text Mesh Pro text, and change it frame by frame, with zero allocations?

I have a 35 char string with a couple of ints, as a test, and it's generating 100B under GC Alloc in the Unity profiler. Without your extension, I get 160B, so it seems there is some savings, but I assumed you could get close to zero here. If I end up with half a screen full of stats (text and numbers) I get 1K or over, per frame of allocations. Is this correct?

Thanks.

Stephan B.

  • Administrator
  • Hero Member
  • *****
  • Posts: 5687
Re: StringBuilder Extensions
« Reply #3 on: December 20, 2016, 05:16:20 PM »
Jasper,

Thanks for the extension, but I have a question.

I'm still getting allocations using your extensions. Should I be able to generate Text Mesh Pro text, and change it frame by frame, with zero allocations?

I have a 35 char string with a couple of ints, as a test, and it's generating 100B under GC Alloc in the Unity profiler. Without your extension, I get 160B, so it seems there is some savings, but I assumed you could get close to zero here. If I end up with half a screen full of stats (text and numbers) I get 1K or over, per frame of allocations. Is this correct?

Thanks.

When changing text dynamically, there should be zero allocations. When setting the text in your script using SetText() there should be no allocations either on the calling side. If you are getting allocations, I would have to see your script as you might be creating new string builder objects on your end instead of re-using them.

Look at Example 07a and script included with TextMeshPro. This script uses SetText() and changes the text each frame without any allocations.

Can you provide an example of the script you are using which generates allocations?

fippydarkpaw

  • Newbie
  • *
  • Posts: 2
Re: StringBuilder Extensions
« Reply #4 on: December 20, 2016, 08:55:57 PM »
Stephan, thanks for the reply. I may be misunderstanding the profiler (new to it).

Running Example 07a with the script, TMP_ExampleScript_01, I see the following in the profiler: 180B under GC Alloc (see attached image). I see this every frame. Does't that mean it's being allocated every frame?

I see the same in my small test using SetText and StringBuilder. Here is a code snippet of the setup.

Code: [Select]
using UnityEngine;
using TMPro;
using System.Text;

public class Stats : MonoBehaviour
{
private TMP_Text textmeshPro;
private StringBuilder sb = new StringBuilder();

private void Start()
{
textmeshPro = statsLeft.GetComponentInChildren<TMP_Text>();
}

private void Update()
{
sb.Length = 0;

sb.AppendInt(fpsInt);  // just an int defined elsewhere
sb.Append(" fps (");
sb.AppendInt(chunkUpdates);  // just an int defined elsewhere
sb.Append(" chunk updates)\n\n");

textmeshPro.SetText(sb);
        }
}

I get about 130B under GC Alloc when I profile the above. And when I do a full screen of text with lots of stats/text, it's 1.2K or so. Note that I'm using Jasper's extension from his post above for AppendInt or AppendFloat etc.

My goal is just to have a stat screen that has no allocations per frame.

Hoping it's just me misunderstanding how to read the profiler. On Unity 5.5.0p2 if that matters.
« Last Edit: December 20, 2016, 09:10:03 PM by fippydarkpaw »

Stephan B.

  • Administrator
  • Hero Member
  • *****
  • Posts: 5687
Re: StringBuilder Extensions
« Reply #5 on: December 20, 2016, 09:29:55 PM »
Stephan, thanks for the reply. I may be misunderstanding the profiler (new to it).

Running Example 07a with the script, TMP_ExampleScript_01, I see the following in the profiler: 180B under GC Alloc (see attached image). I see this every frame. Does't that mean it's being allocated every frame?

When running in the editor, there are allocations in order to show the text input in the Text Input Box in the inspector. These are editor only allocations when using SetText(). In a build, these allocations are gone.

Jashan Chittesh

  • Beta User
  • Newbie
  • *
  • Posts: 5
    • narayana games
Re: StringBuilder Extensions
« Reply #6 on: April 03, 2017, 03:05:46 AM »
I have written a collection of extension methods for StringBuilder that append ints and floats without any allocations. You can control the amount of digits and decimals, use group separators, and adjust which symbols to use for them.

Very awesome, thanks for sharing! In my project, I applied two changes to this:

  • Returning the input StringBuilder to make consistent with other StringBuilder methods, so we can do: sb.Append("Text ").AppendInt(number).Append(" more text");
  • Put into System.Text namespace, so it doesn't make StringBuilder show up when I don't use System.Text

Also, one limitation I found with this was for localization because using this will bake the order of how things are said into code, which doesn't work with many languages that have different grammar (and therefore different order of certain words / numbers that will be added).

Doing some research to find out if this was already solved by someone, I found: XNA/C# A garbage-free StringBuilder Format() method. From a quick glance, one thing that this is still missing is "#" which would give us the grouping from your extension. But I believe it shouldn't be too hard to combine the two ;-)
Score Flash with Unity UI (uGUI) support
Scrolling Combat Text on Steroids:
Easy GUI for Scores, PowerUps, Achievements, Tutorials

TextMesh Pro Addon