Sign in to follow this  

[C#] GC and .tostring() method

This topic is 4197 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello, I have a simple question : How to avoid a temporary string variable declaration if I want to send a Float variable to a string. Exemple :

float fps;
string strFPS;
fps = 0f;

for (int a = 0; a <= 1000000; a++)
{
   fps++;
   strFPS = ((int)fps).ToString();
}



This simple exemple, will alocate 1000000 temporary string. A a game main boucle it could cause the GC to slow down from time to time to free up some memory ! Is there a way to sent my float value to the global string, without this anoying hiden string creation at each loop iteration ? Tx you !!!

Share this post


Link to post
Share on other sites
Hello,

It clearly seems that its the .ToString() method the create a temporary String before sending it the Global string.
So StringBuilder can't help me here.

I will have to build a "'number' to string" function that is using a static string for buffering !

Kind regards,

Share this post


Link to post
Share on other sites
C# strings are immutable (can't be modified once created), so you may wish to store a character array instead. It'll be a bit of a bother, but between a MemoryStream, a StreamWriter and a character array, you should be able to do what you want. Hopefully someone else will know a better way.

Share this post


Link to post
Share on other sites
If you declare your variable like that:
string strFPS;

you can only change it by creating a new string object and assigning the new string object to strFPS.


If you want to avoid object creation you need a variable of a mutable type like:
StringBuilder strFPS;
// stuff here
{
strFPS.length=0;
strFPS.append(yourfloat);
}

Share this post


Link to post
Share on other sites
Hello,

Tx for trying, but no, it's not true this code will also instanciate 10.000 temporary string (Following CLR Profiler) :



private StringBuilder sb = new StringBuilder();
private int a = 0;

public void Run()
{
for (int b = 0; b < 10000; b++)
{
sb.Length = 0;
sb.Append(a);
}
}





It seems that its the '.ToString()' method from the integer variable that is using a temporary String.

So :

A (Integer) --> Call to .ToString() --> MyTempString with A value --> Sent to StringBuilder !

Share this post


Link to post
Share on other sites
MSDN on StringBuilder.Append(int)
Quote:
Remarks:
Int32.ToString is used to get a string representation of value. The capacity of this instance is adjusted as needed.

That's just stupid :(

You could use StringBuilder.Append(char) and write the int->string conversion yourself, that would probably work.

On the other hand GC for short-lived objects is usually quite cheap, so it might not be worth avoiding it.

Share this post


Link to post
Share on other sites
Yes indead, it's really strange.

I could let the GC do its job, but when I see the CLRprofiler log it scares me : 100000 string instanciated for a size of 2.6Mbytes every 2 seconds !

It makes 78Mbytes by minutes allocated for .... nothing !
It's allocating a lot for nothing ...

Share this post


Link to post
Share on other sites
There are all kinds of tricks involved in writing garbage collectors - one of them is to exploit the knowledge that the longer an object has been in use, the less likely it is to need garbage collecting - which would make a well written GC perform well in the case you describe.

In general, write C# and let the GC do it's job for most code and write C++ for time critical code (and hook them together using C++CLR).

Share this post


Link to post
Share on other sites
the same would happen if you used a std::string declared in a function. It would get created and destroyed, but allocated and deallocated quickly.

Because the string object is a class rather then a struct, it is likely that it's stored in the heap (I dunno, the CLR might make exceptions / specific optimisations for the string class). But because the graph is shallow (as it's declared in the function) it is easy for the GC to pick up in a single pass. It's the longer lived memory allocations you need to be careful of.

For instance "string strFPS;" - you only need to declare strFPS if you intend on using it more then once. Otherwise just send it across to another function with a .ToString(), so the instance is just anon.

To make your code friendlier for the GC, try and keep as few references to the string as possible. The less references to a variable it has to check, the more likely it can get rid of it quickly.

Unless your applications memory footprint is steadily climbing just from your original example, there is no need to stress out about this stuff.

It's good you are using the CLR profiler and all that, but I would worry about this issue only if it becomes apparent that it is a problem later on when more of your game is done. There is always a danger of focusing too much on this kind of thing when it's not really a problem - it's just different from what you are used too.

I think the biggest problem some of the C++ guys have when it comes to C#, Java and the like is trusting what is going on behind the scenes because they are so used to controlling every single detail. I know it took me a while to get used to the idea, and learn optimisation techinques for C# that might not make sense in C++.

A general rule of thumb applies - do timings etc to see what the effect of things is. Just looking at the allocated / destroyed metric on it's own could be misleading without time taken to complete a loop and interuptions of the GC. So I would try and put this to the back of your mind and just get on with whatever you are doing.

Share this post


Link to post
Share on other sites
Quote:
Original post by paulecoyote
the same would happen if you used a std::string declared in a function. It would get created and destroyed, but allocated and deallocated quickly.


Sort of. If the std::string implementation includes a short string optimization, and none of the converted ints exceed that buffer length (and it's normally 16 chars or so, so they shouldn't), then yeah, everything gets iteratively pushed on and off the stack. Otherwise, the string would dynamically allocate and free memory for its buffer for every number. The difference being that the std::string would be forced to deallocate every iteration, while a GCed language will do it when it feels like doing it.

Note that putting things onto the heap doesn't necessarily cost so much in a GCed language. The allocation isn't done like malloc() with a free-list or anything; it will instead treat memory more or less like a stack. Most GC passes, the ones that scrape only the "young" generation, will effectively do the same; when full GC happens, objects get shuffled around and compacted, so that instead of leaving "holes" in memory that couldn't be used (unless you tracked them, in which case you'd be back to slow malloc-style heap allocation), all the memory is sifted to the top. (It can do this because it knows exactly which bits of object memory are pointers and which aren't, so it knows what's in use, and also what to change if it wants to move objects around).

Beware, if you use 'unsafe' C# code to "hide" a pointer, and GC happens before you can "reveal" it - kaboom. Just don't freakin' do it, ok? :) That's why it's called 'unsafe'.

Share this post


Link to post
Share on other sites
The only reason you're creating that many FPS strings per second is because your app is going hundred of frames per second and not rendering anything; don't worry about it unless it's causing you performance problems.

Share this post


Link to post
Share on other sites
If you must absolutely avoid as many temporary allocations as possible, you can do is something like: "string result = float.Parse(47.2f);", but note that this is more computationally intensive than ToString().

Otherwise, this really should not be much of a concern. If your performance is sluggish, use the CLR profiler to determine what's going wrong; it's unlikely that ToString() is responsible for a speed drop.

hope that helps,

Share this post


Link to post
Share on other sites

This topic is 4197 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this