Hello, well i want to display some Raycast Values with SpriteText. Now the normal way is: Code (csharp): RaycastHit hit; if (Physics.Raycast (SpaceShip.transform.position, -Vector3.up,out hit)) { distogroundText.Text = ("Ground " + hit.distance.ToString("000") + " ft") ; this seams to produce some overhead garbage ... suck's... So i want to use the StringBuilder Class ... If i do this: Code (csharp): RaycastHit hit; if (Physics.Raycast (SpaceShip.transform.position, -Vector3.up,out hit)) { StringBuilder sb = new StringBuilder("Ground "); sb.AppendFormat("{0:F2} ", hit.distance); distogroundText.Text = sb.ToString() ; there is no difference in Profiler... hmmm did i made something wrong ? thank you for taking the time!
you should initialize the string builder variable with a initial size. otherwise it will start containing ground and need to reallocate memory same as string does. but i think you definitely waste your time at a wrong point. this calculation is usually done once per frame and the effort is not worth the runtime. you only optimize when there is a real bootleneck (which has been proven by profiler). so when you do heavy string operations in a loop you can use string builder but for gui + debug messages its not worth it imo.
You may also want to look at String.Format http://msdn.microsoft.com/en-us/library/system.string.format.aspx It's not quite as efficient as string builder, but a lot easier to deal with IMO.
The code looks fine, so I think the problem is your { } after the if statement. You havn't copied in all your code, so its hard to be sure. If your'e getting garbage, it probably means the hit is null.
Hello, well many thxx for the reply ! i was on the wrong way ! Someone told me to use StringBuilder because of Performance but it only makes sense if you have real Strings no int's or float's.... So i found out if i Display in my GUI a RayCast with SpriteText it takes over 8% and 123 GC Alloc... in Profiler. If i sent the Value to a HealthBar or similar... it takes 0.2 % and NO GC ! That means to Display Text and update it is very heavy .... nothing change here... i thought there make some Optimizations in Unity 3.5 EZ GUI but it is not true... If i open my Old Project with Unity3D 3.x.x i get the same and in older Version too.... So Conclusion is : Do not use Dynamic Text ... it still kill's a lot of Performance.... damn or did i miss something ? thank you
I know this post is old but I wanted to point out the problem with the OP's use of StringBuilder. Calling 'new' is always going to allocate memory and seeing as they are creating a new StringBuilder for every ray cast hit, memory is going to be allocated. The correct way to use StringBuilder is Code (CSharp): StringBuilder sb; void Start() { sb = new StringBuilder(32, 32);//initialize to a usable value so you only use what you need no more no less } void SomeFunction(string input) { sb.Append(input); } The version of StringBuilder being used with Unity is however still going to generate garbage with certain calls. - AppendFormat() will generate gabrage as it calls ToString() on any object passed in. Latest versions of Mono and .NET still have this issue(to my knowledge). - ToString() will generate garbage. Latest versions of Mono and .NET apparently don't on StringBuilder(have not tested) There are probably more functions but these are the two I needed to use. There are some workarounds you can do extending the StringBuilder to include garbage free string use. There is no Clear() function in this version of Unity/Mono, and I have yet to get it to clear the StringBuilder properly to make it of any use(still trying at it). I have read somewhere that Unity 5 will update to a newer version of Mono and will have better GC and other much needed fixes.
Clear is added in a later .Net Version (4.0 iirc) so I made an Extension Method with the same name and once mono is updated you can simply remove the extension code and your client-code should require no change. Code (csharp): public static class ExtensionMethods_String { public static void Clear ( this StringBuilder instance ) {// #Hack this shall reset the stringbuilder without releasing the memory, .net 4.0 adds a clear method which is not available here, maybe when unity's mono is updated, at least it can be made compatible by removing this method source: http://www.mindfiresolutions.com/A-better-way-to-Clear-a-StringBuilder-951.php instance.Length = 0; } } Thanks for the hint. I'll look into it. First post with some usefull contribution. Rarely seen these days here. Kudos for that.
Thanks for the kudos . I think community means that we work together towards the same common goal, forums are communities; and if we don't help each other how will we ever get better? Your extension method works if the strings are going to be the same size or larger every time you clear StringBuilder. If the old string is larger than the new, you will get the new string plus what is left over from the old string. Setting the length to 0 only sets the cursor to 0. The rest of the old data is still in there. I use this method to clear Code (CSharp): public static void Clear(this StringBuilder sb) { sb.Length = 0;//set to zero to put cursor at the beginning. //blank out the entire capacity of the StringBuilder for (int i = 0; i < sb.Capacity; ++i) { sb.Append(" "); } sb.Length = 0;//Set cursor back at the beginning. } Now I just need to get the formatting to work correctly.
While your method is good if you NEED the content to be cleared everytime you call clear, it's inefficient in comparison to just zeroing the length, which yes only sets the cursor point, but that's all you need to do if you don't need to clear the data. The data will be overwritten when you assign/append new values. Hope this helps!
Yes, but the forum is flooded by leechers who don't want to contribute only take out what they need. thats why i find it important to note that you seem to be one of the rare exceptions to that general observation. afaik setting length to zero is also what the built-in net4.0 clear method does. i have only read this and not veryfied but it seems a little overkill to me to overwrite the data twice. i could only imagine that usefull for security/obscurity purposes but not for the general ingame usecase. Code (csharp): public static void Clear(this StringBuilder sb) { sb.Length = 0;//set to zero to put cursor at the beginning. //blank out the entire capacity of the StringBuilder for (int i = 0; i < sb.Capacity; ++i) { sb.Append(" "); } sb.Length = 0;//Set cursor back at the beginning. } in your method you overwrite the whole capacity what is a waste when you only use a fraction of that. if you want to keep this "overwrite" feature at least cache the length and only clear until that: Code (csharp): public static void Clear(this StringBuilder sb) { int oldlength = sb.Length; sb.Length = 0;//set to zero to put cursor at the beginning. //blank out the entire capacity of the StringBuilder for (int i = 0; i < oldlength; ++i) { sb.Append(" "); } sb.Length = 0;//Set cursor back at the beginning. } but as mentioned i find this unneccessary.
For how long the average string is the clear method is negligible. In practical use setting length to 0 and leaving it at that is sufficient as most numbers being displayed only get bigger, like a score or a dollar value. Even those may need clearing, and if your worried about performance then caching the length and only clearing that is more efficient. How often are you going to be clearing out the score or dollar amount? Pretty sure your not going to be doing it every frame. I also seem to find that setting length to 0 even if your original string is smaller will have garbage data in the string that is left over in the memory locations assigned to the string. This could be due the method use to get the string reference from the StringBuilder without generating garbage. I will have to test further to see if that is the case.
For convenience a nicely optimized Clear extension: Code (CSharp): public static void Clear(this StringBuilder sb, bool wipeout = false) { int oldlength = sb.Length; sb.Length = 0;//set to zero to put cursor at the beginning. if (wipeout) { //blank out the entire capacity of the StringBuilder sb.Append(new String(' ', oldlength)); sb.Length = 0;//Set cursor back at the beginning. } }
I was going to post a similar method to do exactly that. I thought of three different ones after awhile; Reset, Clear and Wipe Reset: reset the cursor setting length to 0. Clear: only clear out the data that will be replaced. Wipe: clear the whole thing. Reset used when incoming data is same length. Clear used when incoming data is larger. Wipe if you need it. I also thought about wrapping StringBuilder into another class that allowed for all this functionality to go on mostly behind the scenes. Then that's kinda like reinventing the wheel. I don't feel like reinventing the wheel to display a few strings and when most of this is already done for us, only in a new version of Mono/.net. That being said. This might not even be needed, if I am getting the string from the wrong place using the extension method. I might only need to set length to 0, this I still need to test. I come from XNA, and didn't use the extension to get the string from the StringBuilder as I only passed it directly to the SpriteBatch to draw the string. In Unity I cannot pass it in anywhere and ToString() generates garbage. One little thing about your method. This line: Code (CSharp): sb.Append(new String(' ', oldlength); This will generate garbage. You do not want to create another object by using new. Might as well create a new StringBuilder when you want to clear it then. I think even in c++, you want to avoid using new during game play, as even allocating memory takes some time, and that can be avoided by pre-instantiating any new objects before game play begins(lots of reusing variables and objects with object pools.) As a note these are all small optimizations, that I think we can include into our everyday coding practices, so we don't need to worry about them(I think I read someone else say that.) Quick and dirty is exactly that. Don't slack on code in the beginning of a project as it will only come back to haunt you while debugging/optimizing later.
So I did some tests looking at the other fields available and got 2 for strings _str and _cached_str. _cached_str doesn't work, no matter which way I try to clear. _str works which is what I am using. Code (CSharp): public static string GarbageFreeString(this StringBuilder sb) { string str = (string)sb.GetType().GetField( "_str", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(sb); return str; } I did this to get the fields available in the StringBuilder instance. Code (CSharp): Type t = sb.GetType(); foreach (var f in t.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)) UnityEngine.Debug.Log(f.Name); I'll have to stick to what I'm doing it works fine for me and the simple projects I'm working on. I have no garbage generated at runtime, and it runs 60+ fps on my crappy desktop. Still need to test on Android/iOS but I think it shouldn't be an issue. If I can do it in Flash, sure as hell I can do it in Unity. Unity is using less memory at the moment but I have only added sound effects and not the music.
Thanks for your reply, I had no idea new Generated garbage, that helps me a bit. I mainly avoid writing code that runs slow I sometimes don't know the reasons, such as new generating garbage overhead, interesting.
If so you should think twice before using reflection in frequently called method since this is also slow. In general i suggest to avoid such "premature" optimizations. you should only omptimize something which has been identified (by profiling!) as a real bottleneck in an advanced build on the target hardware. its often not worth it to spend time on such things in early development as code is changed anyway in most cases. and optimized code is harder to read and understand. so focus on readability and understandability first.
A little late to the Party, I know -- but I just found out that setting SrtringBuilder.Length = 0 also has the effect of clearing the contents. I did several tests where I would Append in a long string of characters, perform a Debug.Log(..), set the Length = 0, and Append a shorter string of characters and do another Debug.Log(..). The shorter length string printed with no "extra" characters. This works in Unity Editor as well as iOS IL2CPP. Using Unity 5.0.2p3 Manny
Hi there, I've written a blog post that covers this issue. In Unity 5 as long as your StringBuilder doesn't grow in size you can just use it normally and it won't create garbage. Here's the full article: http://www.defectivestudios.com/devblog/garbage-free-string-unity/
I have tested the StringBuilder.ToString() and it still generates garbage. You also might want to update the incorrect information you have in that blog post. Unity 5.0 is still using .net 2.0, and still requires the work around. IL2CPP seems to be the issue holding them back from upgrading. There was some confusion about what version 5.0 would be using and was clarified here.
You're right I was confused. Thanks for clearing that up. That said, I did test StringBuilder.ToString() at some point recently but to be honest I can't remember if it was a beta version or what. I did try to make sure that I tested it before updating the article but I also haven't been keeping the code that uses the workaround up-to-date. That said, I'm diving back into that project and once I get to the bottom of the situation I'll revise the post. Anyway, I think the fundamental workaround is still valid, if needed. Instantiate the string builder with a buffer large enough to handle any kind of string that you need. Or, just let the one StringBuilder re-allocate every time your string gets bigger, but instead of calling.ToString() access the internal buffer. It's still messy, but you don't have to make the reflection call every time you need to access the string, and you have to blank out extra characters if you buffer isn't exactly the right size, but you can still mess around with strings garbage-free, and it did the job for me while significantly reducing CPU load.
The cached string doesn't work to well with the new UI. I have to set the text to "" then call cachedTextGenerator.Invalidate(); then set the text to the cached string. Three calls to change the text, plus what ever all that is doing in behind the scenes, I get no garbage and haven't seen much from performance, but it looks ugly code wise. Can't wait for Unity to update the API to .net 4.5.
I have created a Special class for high intense use of concatenation between integers and text. It generates no garbage (until the moment i have tested) and the performance is better than StringBuilder.Append(int): Code (CSharp): using UnityEngine; using UnityEngine.UI; using System.Collections; using System.Text; public class SpeedString { public string string_base; public StringBuilder string_builder; private char[] int_parser = new char[11]; public SpeedString(int capacity){ string_builder = new StringBuilder(capacity,capacity); string_base = (string)string_builder.GetType().GetField( "_str", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(string_builder); } private int i; public void Clear(){ string_builder.Length = 0; for(i=0;i<string_builder.Capacity;i++){ string_builder.Append(' '); } string_builder.Length = 0; } public void Draw(ref Text text){ text.text = ""; text.text = string_base; text.cachedTextGenerator.Invalidate(); } public void Append(string value){ string_builder.Append(value); } int count; public void Append(int value){ if(value >= 0){ count = ToCharArray((uint)value,int_parser,0); } else{ int_parser[0] = '-'; count = ToCharArray((uint)-value,int_parser,1)+1; } for(i=0;i<count;i++){ string_builder.Append(int_parser[i]); } } private static int ToCharArray(uint value, char[] buffer,int bufferIndex) { if (value == 0) { buffer[bufferIndex] = '0'; return 1; } int len = 1; for (uint rem = value/10; rem > 0; rem /= 10) { len++; } for (int i = len-1; i>= 0; i--) { buffer[bufferIndex+i] = (char)('0'+(value%10)); value /= 10; } return len; } } An exemple usage for it: Code (CSharp): public class Panel_Test : MonoBehaviour { public Text velocity; public SpeedString velocity_string = new SpeedString(32); void Update(){ //Stats velocity_string.Clear(); velocity_string.Append(last_velocity); velocity_string.Append(" MPH"); velocity_string.Draw(ref velocity); } } The only problem i can't found nowhere how i can force the Text component to update without disable / enable it again.(This seens slow, not sure), but works better than must solutions i have readed. if you know a way to make it better for the community use, please extend it and put it here for us Edit: Improved Draw function with the comment above
It looks like this post found a solution to the UI problem: http://forum.unity3d.com/threads/forcibly-invalidating-the-ui.265665/
I'm finding that setting StringBuilder.Length allocates an amount proportional to the current capacity! Which is completely nonsensical, and means that StringBuilder just isn't reusable in a garbage-free way. This is with Unity 5.4.0f3, but I remember it happening back to 5.2 or so. I assume this is because of the rubbish old Mono runtime version - there's no logical reason for Length to allocate. Is anyone successfully clearing and reusing StringBuilders without them allocating memory, in 5.4 or later? For reference, the callstack I see in the Unity Profiler is: StringBuilder.set_Length StringBuilder.InternalEnsureCapacity String.CharCopy String.CharCopy String.memcpy4 String.InternalAllocateStr <-- this is the bit that allocates memory. Browsing the Mono StringBuilder source confirms this is what's happening; it's far too eager to allocate memory. See here: https://searchcode.com/codesearch/view/7227794/
I use the StringBuilder constructor that specifies initial capacity and max capacity and specify the same value for both. I don't have any runtime allocations.