No Garbage Text and Numbers.

Ever watch your gc slowly climb when all your doing is looking at the framerate and think what is causing that?
Its not mono-game, its c# itself !

Numbers simply sent to StringBuilder will create garbage even xna / monogame’s charctersource struct, (hack around) doesn’t stop it .

This is a FrameRate and gc counter example that uses a class i wrote that bypasses the garbage allocations meaning you don’t get a bunch of collections each second just for text output.

Edit: fixed a bug that was causing a precision error.

I fixed a decimal placement bug shown in the picture below as well as a error in the algorithm. Anyways the point is it works gc wise.
It’s not perfect minor floating point errors are inherent to it, and im sure there are still some edge case bugs, but if you want to see when you are actually getting allocations other then from text in real time, its useful.

The framerate example class has simple bool options as well in this case to turn on and off stuff.

On the left is regular string builder, on the right is my rigged up wrapper, you can see their are no allocations from the FrameRate output itself.

well I can’t get it to run, since one of the libraries has an ArgumentNullException in mscorlib.dll, but the source is not found for said library, which is … mscorlib.dll itself.

I got to that point by disabling myCode etc., beforehand it would just show me that the code is on a breakpoint, but said breakpoint has no source.

I’ve googled this problem and can’t seem to fix it, if someone knows, I’d appreciate that.

But I did look at the code

Can you explain how this one works different to the default stringbuilder and why it’s preferable? I have no clue about this stuff actually, I guess it would be nice if the stringbuilder was smart about stuff that is repeated often and don’t throw the old things away?

(edit it was because i published it before i zipped it which actually screwed it up somehow)

Well its just 3 class file and a monogame created spritefont.
Its basically just all straight c# code other then the spritefont.

You can just make a new test project and add them to it.
use the ‘add existing’ class files button in vs

Rename the namespace in each file to your projects namespace.

Game1 (just a basic game1)
FrameRate (just a usage example)
MgGcStringBuilder (the actual class)

Create a monogame spritefont named MgGenFont.

that should be it.

but the class itself is basically just like stringbuilder, it uses it, differs from it, and can be swaped with it…

Thanks for sharing this, I’ve been looking for a way to draw a TimeSpan as a string without generating garbage past couple of days.

Had a quick look at the code, FrameRate class is a bit over complicated at a glance to see how you’ve built the strings but I’ll take a proper look in a bit and see how it goes :slight_smile:

Sooooo, I couldnt figure out if there was a better way to format the string the way I wanted it to display but this did the trick.

Can confirm it generates zero garbage from the profiler! :smiley:

//timeString = stageTime.ToString(@"mm\:ss\.fff"); // Old string generates garbage.

// New String, code isn't pretty but it works with 0 garbage
    noGarbageTime.Length = 0;
    if (stageTime.Minutes < 10)
    {
        noGarbageTime.Append("0");
        noGarbageTime.Append(stageTime.Minutes);
    }
    else
    {
        noGarbageTime.Append(stageTime.Minutes);
    }

    noGarbageTime.Append(":");

    if (stageTime.Seconds < 10)
    {
        noGarbageTime.Append("0");
        noGarbageTime.Append(stageTime.Seconds);
    }
    else
    {
        noGarbageTime.Append(stageTime.Seconds);
    }

    noGarbageTime.Append(".");

    if (stageTime.Milliseconds < 10)
    {
        noGarbageTime.Append("0");
        noGarbageTime.Append("0");
        noGarbageTime.Append(stageTime.Milliseconds);
    }
    else if (stageTime.Milliseconds < 100)
    {
         noGarbageTime.Append("0");
        noGarbageTime.Append(stageTime.Milliseconds);
    }
    else if (stageTime.Milliseconds < 1000)
    {
         noGarbageTime.Append(stageTime.Milliseconds);
    }

Thanks again for sharing, great work! Please do let me know if there is a better way of formatting the Time Span so it displays as “00:00.000”

I made a couple minor fixes to the class, to fix a math error which seemed to be due to a bug, that i left in from a larger test project. I just re-posted the 3 class files instead of the whole project.

FrameRate class is a bit over complicated at a glance to see how you’ve built the strings

Well i really did it like that so you could change the bool in the example and see the difference between a regular string builders output and this versions but i maybe put to many examples in there.

I couldnt figure out if there was a better way to format the string the way I wanted it to display but this did the trick.

They way you did it or some other inventive manner of doing that. Is probably required for formatting numerical things to a specific manner of display, If one wants to not create garbage.

The AppendTrim function itself limits the decimal places to 3 that’s about all the custom formatting i added. Which was just done as a example to show how you could alter the outputted text mathematically, by simply breaking the while loop after a specific number of iterations.

I suppose since this is somewhat of a hack around the root of the problem which unfortunately is in dot net. Its pretty much create your own formatting at least for numerical display

Can you explain how this one works different to the default stringbuilder

This basically wraps a StringBuilder and intercepts the numerical calls and does them instead that’s about the jist of it, well there is a little more.
It additionally keeps a couple extra static swaping string builders around in case a big capacity change occurs in one of them so it doesn’t cause garbage to escape in that manner. The operators allow you to use it with a regular StringBuilder or swap one into or out of it if need be or for convenience.

and why it’s preferable?

I tried quite a few different peoples solutions, some that dug very deep. All of them show the same problem, most all of them missed the root cause. Which was that numbers being converted to strings even with stringbuilder, simply create temporary garbage no matter what.
That garbage can and will be marked and swept by the gc when enough garbage that is markable builds up. It then at some point causes a GC collection to occur that for the user this appears as a stutter in a game, which it is.
Your entire app must wait for the GC to finish taking out the trash. While not a big deal for say a calculator on your desktop. For a game going at 60fps or more its very noticeable.

For the game programmer it makes it hard to differentiate in real time at a glance when something else that he is doing creates a GC problem when your very informational numerical displayed output is also causing collections.

To think of it simply, its like a temporary object going out of scope, each time you turn a number into a character via c#.

Even though it is said that char and numerics are primitive value types when it comes to conversion. We can see in the framerate test given (by switching on the regular stringbuilder and running with no fixed timestep ) that this is not what you see what you see is garbage and collections.

Some where deep down below StringBuilder and String, a temp character object is created and forgotten for each numerical value changed to a character. This is c# wide not limited to monogame.

The reason this works is we never actually ask for a conversion at all, we mathematically calculate the numerical character ourselves and only allow a character to be created. Until we let our stringbuilder go out of scope or call new on it. We wont be allowing the gc to mark it for sweeping.
Unfortunately StringBuilder and Strings, underlying ToString conversion itself, needs to actually be re-written internally within dot net, for a proper fix to the problem. Because this does work im like 90% sure this is actually a unrealized bug within .net.

I spent a good amount of time attempting to make this class have all the functionality of a regular string e.g. MyStringBuilder = “some variable” + 10.5 + newline; Which yielded unfavorable results.
While this was possible, it wasn’t ‘properly’ possible due to the rules of operator overloading being a little to loose and not strict enough to allow it to work without generating garbage, and not really worth it even with a regular string builder, because of the extra stuff involved.

I have modified the appendAt so it will work if the index + length of new string is longer than the given stringbuilder

public void AppendAt(int index, StringBuilder s)
        {
            int len = this.StringBuilder.Length;
            int reqcapacity = (index + s.Length + 1) - this.StringBuilder.Capacity;
            if (reqcapacity > 0)
                this.StringBuilder.Capacity += reqcapacity;

            int initialLength = StringBuilder.Length;
            //If we append near the end we can run out of space in the for loop. Make sure we are large enough
            if (StringBuilder.Length < index + s.Length)
            {
                StringBuilder.Length = index + s.Length;
            }

            //If our appendAt is outside the scope we need to add spaces until then
            if (index > initialLength-1)
            {
                for (int j = initialLength - 1; j < index; j++)
                {
                    StringBuilder[j] = space;
                }
            }

            for (int i = 0; i < s.Length; i++)
            {
                this.StringBuilder[i + index] = (char)(s[i]);
            }
        }

Thanks.

Take care to when altering some of those that use the “static sb” though while they may look noob or unnecessary. They maybe in there to keep the reference to the characters alive, during a copy operation or prevent a de-allocation of a entire string builder itself via a collect at a undesirable time.
Though its been a bit of time since i worked on it. I tend to get a working version then stash it away then use it a few times before i get it to were im satisfied its fairly dependable, but that isn’t really the case here.

Ya sorry its still pretty raw / rough though. You will probably need to tweek it for a bit as you use it and you may find bugs as well. I would keep a separate test class though for changes made, to test it at very high iteration speed.

This is actually the only test case for practical use i made for it so far. Too boot its a newer class not based on anything i really fully hashed out previously, and its trimmed way down from the original which was a huge / sloppy project for pure experimenting.

Just don’t overdo it on adding operator overloading it’s a waste of time to try to make it work like a string “a” + “b”, it starts to become circularly futile either by performance or function.

i added this into all the constructors as well, i dunno why i didn’t before. I hope i didn’t have a reason not to lol i honestly can’t remember if i purposely didn’t put them in or not.

if (sb == null) { sb = new StringBuilder(); }
if (last == null) { last = new StringBuilder(); }

The below returns a live reference however this is not a new copy but a actual reference.
Meaning if its saved and acted upon, it can alter the original which can i suppose cause confusion or even possibly muck something up, if the user doesn’t get whats going on.

public static implicit operator StringBuilder(MgStringBuilder msb)
{
return msb.sb;
}

but it lets you do the below

public void Draw(SpriteBatch spriteBatch, GameTime gameTime, float x, float y)
{
        if (DisplayFrameRate)
        {
           // instead of mgsb_perf_msg.StringBuilder as a parameter
           //
           spriteBatch.DrawString(currentFont, mgsb_perf_msg, new Vector2(x, y), textColor);
        }
        frames+=1;
}

So i left a lot of the operators out that would make it easier to work with it. You can add them back in at your discretion to make it easier to use the class itself.

Thanks

I actually did that part because I wanted my debug text (all 3 lines) to be in one string instead of 5 or 6.

The problem seems to be then that the frametime for example is inconsistent, and so the length of the string is, too.

So the FPS are unreadable if the previous string is jittering around between 2 values in my case.

Originally I added another drawString behind it all together with new position etc.

But if I just fix the starting character index I can work around that. I just need a monospace font and voila.

Ya i suppose you could write all kinds of your own line data formatting’s using the index’s.

I actually have another class i was working on a while back. That is basically just a rewritten spritebatch im planning to rewrite it in some way eventually again. To control drawstring output better. In fact i have a couple but they all are more a jumbled mess of tests then organized classes.

The idea though was to simply be able to get end vector returned from were the last DrawString ends at maximum or save it each draw and limit the size of the draw area. So you could do text outputs like that to were they didn’t get that jitter basically like appending with some limiting. It works so far its just a mess though of test methods and test classes.

Well i guess i cant seem to edit the original post for this for some odd reason. So ill just post this here.

This is newer version of the no garbage class itself.

(you can alternately just copy paste it, as its straight c# code.)

It adds a number of fixes.

The primary fix in this version is.

A fix to high order precision errors on floats and doubles that was creating a annoying precision loss.
The old version was actually compounding that error. So i basically shift all the decimals into the integer range now.

Additionally.

Calls can be chained like stringbuilder does like so.
myMessege.Append( “\n the number is " ).Append( 5 ).Append(”\n");

Trailing zeros are cut.

Some additional things will be added later these will probably be in the form of nicetys, formating or optimizations ect, I think most of the major bugs are out whittled out of it at this point.

Sorry kosmos append wasn’t in this one. I never use AppendAt and forgot it wasn’t in this one.

You can just paste it in.

public void AppendAt(int index, StringBuilder s)
{
    int len = this.StringBuilder.Length;
    int reqcapacity = (index + s.Length + 1) - this.StringBuilder.Capacity;
    if (reqcapacity > 0)
        this.StringBuilder.Capacity += reqcapacity;

    int initialLength = StringBuilder.Length;
    //If we append near the end we can run out of space in the for loop. Make sure we are large enough
    if (StringBuilder.Length < index + s.Length)
    {
        StringBuilder.Length = index + s.Length;
    }

    //If our appendAt is outside the scope we need to add spaces until then
    if (index > initialLength - 1)
    {
        for (int j = initialLength - 1; j < index; j++)
        {
            StringBuilder[j] = ' ';
        }
    }

    for (int i = 0; i < s.Length; i++)
    {
        this.StringBuilder[i + index] = (char)(s[i]);
    }
}
1 Like

Since this post is still getting views and i realized it actually poped up on a search about garbage in c# im linking it to were i keep the class file updated on github.

if you have any recommendations for adding formatting without creating garbage that work let me know that’s its one shortcoming.