- Home /
C# Int to Float conversion performance
What is the performance of int to float casts in C#. I'm looking at the lightning example from Unity Procedural Examples, and there are two int -> float casts inside the tight loop. My understanding is these casts are quite slow in C, What is the performance of these casts in C#?
I'm curious about both int->float and float->int.
Here is the code from the lightning example:
for (int i=0; i < particles.Length; i++) { Vector3 position = Vector3.Lerp(transform.position, target.position, oneOverZigs * (float)i); Vector3 offset = new Vector3(noise.Noise(timex + position.x, timex + position.y, timex + position.z), noise.Noise(timey + position.x, timey + position.y, timey + position.z), noise.Noise(timez + position.x, timez + position.y, timez + position.z)); position += (offset * scale * ((float)i * oneOverZigs));
particles[i].position = position;
particles[i].color = Color.white;
particles[i].energy = 1f;
}
The article you are referring to is explicitly about float->int casting, not int->float casting; there is no rounding problem when doing the latter. Also, it is very old - I wouldn't infer that what's said in that artice still to be valid with current CPUs and/or compilers.
Thanks for the feedback, I realize the article is referring specifically to float->int conversions. I doubt that performance has anything at all to do with rounding. Since a 32-bit float cannot accurately represent all 32-bit integer values, there will be rounding occurring regardless of the direction of the cast.
Answer by Eric5h5 · Jul 31, 2010 at 01:40 AM
Put this in front of the code:
float timer = Time.realtimeSinceStartup;
and this after it:
Debug.Log (Time.realtimeSinceStartup - timer);
Then compare times with and without the cast.
Thanks Eric, your suggestion was useful. The resolution of the timer is not high enough to test the code as is. I changed it to repeat that code block 10,000 times and recorded 5% boost when changing it to a constant float. Combining that with storing some of the look-ups in local variables I was able to get the code 10% faster. Nothing break-through but also not insignificant.
I completely agree, fortunately in this case it is not premature. This is an example, it is final. I think a more important axiom in this case is 'examples should be exemplary'. Sloppy code like tight loops with unnecessary look-ups should not be encouraged. If this is an example of procedural effects, performance of code is relevant.
Answer by Tetrad · Jul 31, 2010 at 02:03 AM
Not that I disagree with Eric's suggestion, but there's a built-in profiler option that I think more people should know about.
http://unity3d.com/support/documentation/ScriptReference/Profiler.BeginSample.html
Profiler.BeginSample ("MyPieceOfCode");
// do something that takes a lot of time
Profiler.EndSample ();
Then in the profiler you can see it referenced by name.
Of course this doesn't work on Unity iPhone as there's no profiler.
Aside from Unity iPhone, which will cease to be an issue with Unity 3, the other problem with that is it only works in Unity Pro. Although indeed it's nice if you have Pro, and is worth mentioning.
That's excellent, I will make good use of this when I get the pro version.
Answer by _Petroz · Jul 31, 2010 at 06:21 AM
I fixed a few things in this code:
- reduced the two int to float casts to one
- I moved the color and energy initialization into Start()
- reduced the amount of lookups by storing target.position and trasform.position in local variables
- I removed one float multiply by storing the product of i and oneOverZigs in a local variable
The final code which is roughly 18% faster is as follows:
Vector3 targetPos = target.position; Vector3 myPos = transform.position; for (int i=0; i < particles.Length; i++) { float f_i = (float)i; float dist = oneOverZigs * f_i; Vector3 position = Vector3.Lerp(myPos, targetPos, dist); Vector3 offset = new Vector3(noise.Noise(timex + position.x, timex + position.y, timex + position.z), noise.Noise(timey + position.x, timey + position.y, timey + position.z), noise.Noise(timez + position.x, timez + position.y, timez + position.z)); position += (offset * scale * dist);
particles[i].position = position;
}
Out of interest, why even cast i to a float? The compiler would do it implicitly if you did float dist = oneOverZigs * i; without the need for the intermediary variable
I wrote the two lines separately to illustrate that there were two separate optimizations being performed; it also made it easier to time both separately. You are right, they could be combined into a single line. AFAI$$anonymous$$ it's going on the stack either way, in which case the performance would be the same.
You can also increase performance further if you rearrange the code as following;
Vector3 targetPos = target.position;
Vector3 myPos = transform.position;
float f_i;
float dist;
Vector3 position;
Vector3 offset;
for (int i=0; i < particles.Length; i++)
{
f_i = (float)i;
dist = oneOverZigs * f_i;
position = Vector3.Lerp(myPos, targetPos, dist);
offset = new Vector3(noise.Noise(timex + position.x, timex + position.y, timex + position.z),
noise.Noise(timey + position.x, timey + position.y, timey + position.z),
noise.Noise(timez + position.x, timez + position.y, timez + position.z));
position += (offset * scale * dist);
particles[i].position = position;
}