marekuana

Hi!

I would like to know one thing about compiler optimizations:

Given Scenario:
I have a class for example Vector3 that has a function length.Like this:

Code Block

public float Length()
{
return (float)Math.Sqrt((x * x) + (y * y) + (z * z));
}



Now i write in my code something like this:

Code Block

Vector3 vector1 = new Vector3(5,5,5);
float a = 3.0f;
float b = 2.0f;

a += vector1.Length();
b += vector1.Length();



Now the Question: Does the compiler recognize, that there has been no change to the vector1 object, and so substitutes the call of the second vector1.Length() with the result of the first vector1.Length()

Because in C# you often use objects like this without thinking a lot about it. So i would like to know if i have to store the result myself in a variable instead of calling the handy function.

Thanks
Marek



Re: Visual C# Language Compiler Optimizations

Pony teh p0ny

I don't beleive it does, unfortunately.




Re: Visual C# Language Compiler Optimizations

Geoff.Cox

I built a program containing your code sample and opened it up in ildasm to see the IL (see below). You can see the two calls to Length(). I tried a couple of things - making the fields readonly, and sealing the class just to see if that gave the compiler a hint. It didn't - there were still two calls.

BTW - I did this in a Release build.

.method private hidebysig static void Main(string[] args) cil managed

{

.entrypoint

// Code size 40 (0x28)

.maxstack 4

.locals init ([0] class ConsoleApplication1.Vector3 vector1,

[1] float32 a,

[2] float32 b)

IL_0000: ldc.i4.5

IL_0001: ldc.i4.5

IL_0002: ldc.i4.5

IL_0003: newobj instance void ConsoleApplication1.Vector3::.ctor(int32,

int32,

int32)

IL_0008: stloc.0

IL_0009: ldc.r4 3.

IL_000e: stloc.1

IL_000f: ldc.r4 2.

IL_0014: stloc.2

IL_0015: ldloc.1

IL_0016: ldloc.0

IL_0017: callvirt instance float32 ConsoleApplication1.Vector3::Length()

IL_001c: add

IL_001d: stloc.1

IL_001e: ldloc.2

IL_001f: ldloc.0

IL_0020: callvirt instance float32 ConsoleApplication1.Vector3::Length()

IL_0025: add

IL_0026: stloc.2

IL_0027: ret

} // end of method Program::Main






Re: Visual C# Language Compiler Optimizations

marekuana

What a pity. But good to know. Please take this as a strongly requested feature i think it would speed up a lot of applications ;-)



Re: Visual C# Language Compiler Optimizations

marekuana

I thought once again about it. Maybe it is not that trivial like it seemed, because it would not produce threadsafe code. But it would be a nice idea to write something like a pre-compiler checking the code for optimization suggestions...




Re: Visual C# Language Compiler Optimizations

IsshouFuuraibou

The other thing to realize is that there are two areas where optimizations are performed. One is in the generation of MSIL, the other is in the JIT when the MSIL is processed. I'm not sure if this type of thing would get optimized, but it seems to me something I'd prefer to be optimized in the JIT rather than in MSIL.





Re: Visual C# Language Compiler Optimizations

Pon t3h pony

Indeed; it would be one step back to "dll hell" if it inlined it in the MSIL.




Re: Visual C# Language Compiler Optimizations

Peter Ritchie

No, it does not. The JIT assumes the worst case; which would be in a multithreaded environment where and object is publicly available to be changed by any number of threads. So, while you code may show that vector1.Length() doesn't change the JIT can't make that optimization because of the case where it was changed by another thread.






Re: Visual C# Language Compiler Optimizations

IsshouFuuraibou

JIT has to assume the worst, but the problem is that the snippet of code would work perfectly as

Code Block

Vector3 vector1 = new Vector3(5,5,5);
float a = 3.0f;
float b = 2.0f;

void SomeMethod()
{

a += vector1.Length();
b += vector1.Length();

}


or

Code Block

void SomeMethod()
{

Vector3 vector1 = new Vector3(5,5,5);
float a = 3.0f;
float b = 2.0f;

a += vector1.Length();
b += vector1.Length();

}



The first cast, it obviously can't do anything with vector1.Length() being optimized, anything else in the class running on a separate thread could modify vector1. However in this specific case another thread changing vector1 and the SomeMethod not accounting for it would be a sign of malformed logic because you can't gaurentee where vector1 will be changed during the method.

However, in the second case no other thread could access vector1, thus it would not be able to be changed between the two calls of Length(). So why would it not be able to optimize that during JIT Is the JIT not able to distinguish between these two cases





Re: Visual C# Language Compiler Optimizations

Peter Ritchie

IsshouFuuraibou wrote:
The other thing to realize is that there are two areas where optimizations are performed. One is in the generation of MSIL, the other is in the JIT when the MSIL is processed. I'm not sure if this type of thing would get optimized, but it seems to me something I'd prefer to be optimized in the JIT rather than in MSIL.
The C# compiler does precious little optimization. See http://msdn.microsoft.com/msdnmag/issues/05/01/COptimizations/default.aspx (See the sidebar) for the optimizations that it performs.




Re: Visual C# Language Compiler Optimizations

Peter Ritchie

IsshouFuuraibou wrote:
JIT has to assume the worst, but the problem is that the snippet of code would work perfectly as

Code Block

Vector3 vector1 = new Vector3(5,5,5);
float a = 3.0f;
float b = 2.0f;

void SomeMethod()
{

a += vector1.Length();
b += vector1.Length();

}


or

Code Block

void SomeMethod()
{

Vector3 vector1 = new Vector3(5,5,5);
float a = 3.0f;
float b = 2.0f;

a += vector1.Length();
b += vector1.Length();

}



The first cast, it obviously can't do anything with vector1.Length() being optimized, anything else in the class running on a separate thread could modify vector1. However in this specific case another thread changing vector1 and the SomeMethod not accounting for it would be a sign of malformed logic because you can't gaurentee where vector1 will be changed during the method.

However, in the second case no other thread could access vector1, thus it would not be able to be changed between the two calls of Length(). So why would it not be able to optimize that during JIT Is the JIT not able to distinguish between these two cases
The problem with C# is that you can't declare a const method. Which means the JIT can't tell if Vector3.Length modifies the instance or not. So, it cannot optimize away a call to a method.




Re: Visual C# Language Compiler Optimizations

IsshouFuuraibou

Ah, the problem is with the fact that Length is a method. Would it be different if it was programmed as a property instead of a method

Length as a property for the benefit of others:
Code Block

public float Length
{

get{ return (float)Math.Sqrt((x * x) + (y * y) + (z * z)); }

}




Marek, are you getting the answers you need If you have any more specific questions or other situations don't hesitate to bring them up. There's some good information provided by Peter about optimizations already.





Re: Visual C# Language Compiler Optimizations

Peter Ritchie

No, it wouldn't be different with a property because properties are implemented as methods (get_PropertyName and set_PropertyName).