Saturday, October 18, 2008

Comparing MSIL of F# Versus C#

Comparing MSIL:

To get a good understanding of what is going on "under the covers" of our example from last week, a person can view the generated MSIL (Microsoft Intermediate Language). While there are several options for doing this, I chose to use Reflector.

Here's a review of the F# code as well as the C# equivalent:

F#:

Test:

[TestMethod]
public void CanCalculateNumberSquared()
{
int
result = FSharpSample.CalculateNumberSquared(2);
Assert
.AreEqual(result, 4)
}

Code:

#light
let
CalculateNumberSquared x = x*x;;

Here's the C# code.

Test:

[TestMethod]
public void
CanCalculateNumberSquaredInCSharp()
{
CSharpSample.CSharpSample cSharpSample = new CSharpSample.CSharpSample();
int
result = CSharpSample.CalculateNumberSquared(2);
Assert
.AreEqual(result, 4);
}

Code:

public class CSharpSample
{
public int
CalculateNumberSquared(int numberToSquare)
{
return
numberToSquare * numberToSquare;
}
}

Here is the managed code with comments explaining what is happening:


C#:

.method public hidebysig instance int32 CalculateNumberSquared(int32 numberToSquare) cil managed {     
.maxstack
2 // maximum number of elements that can be pushed to the valuation stack
.locals init (
[0] int32 CS$1$0000) // this is a local variable of type int32
L_0000: nop // do nothing -- indicates compiled in debug mode
L_0001: ldarg.1 // push argument 1 onto the stack
L_0002: ldarg.1 // push argument 1 onto the stack (multiplying a number by itself)
L_0003: mul // multiply values
L_0004: stloc.0 // pop a value from the stack into a local variable
L_0005: br.s L_0007 // go to labl L_0007
L_0007: ldloc.0 // push local variable at index 0 onto the stack
L_0008: ret // return the value from the stack (if there is one) and return from the method
}
F#:


.method public static int32 CalculateNumberSquared(int32 x) cil managed {     
.maxstack
4 // maximum number of elements that can be pushed to the valuation stack
L_0000: nop // do nothing -- indicates compiled in debug mode
L_0001: ldarg.0 // push argument 0 onto the stack
L_0002: ldarg.0 // push argument 0 onto the stack
L_0003: mul // multiply values
L_0004: ret // return the value from the stack (if there is one) and return from the method
}

This first thing that I noticed when comparing the managed code from each example, was that
the F# code is more succinct. Each is accomplishing the same goal, but the CIL generated from
F# is five lines less that that which was generated from the C# example.
The second thing I
noticed was that the F# method is static while the C# method is not. To ensure that we are
comparing apples to apples, a new static method was generated in the C# example.


Here's the code:


Test:


[TestMethod]

public void
CanStaticCalculateNumberSquaredInCSharp()

{
int
result = CSharpSample.StaticCSharpSample.CalculateNumberSquared(2);
Assert
.AreEqual(result, 4);

}


Code:

public static class StaticCSharpSample

{

public static int CalculateNumberSquared(int numberToSquare)
{
return
numberToSquare * numberToSquare;
}
}

MSIL:
.method public hidebysig static int32 CalculateNumberSquared(int32 numberToSquare) cil managed {     
.maxstack
2 // maximum number of elements that can be pushed to the valuation stack
.locals init (
[0] int32 CS$1$0000) // this is a local variable of type int32
L_0000: nop // do nothing -- indicates compiled in debug mode
L_0001: ldarg.1 // push argument 1 onto the stack
L_0002: ldarg.1 // push argument 1 onto the stack multiplying a number by itself)
L_0003: mul // multiply values
L_0004: stloc.0 // pop a value from the stack into a local variable
L_0005: br.s L_0007 // go to labl L_0007
L_0007: ldloc.0 // push local variable at index 0 onto the stack
L_0008: ret // return the value from the stack (if there is one) and return from the method
}
As you can see, the MSIL is very similar to that which was generated in our first example.
While we could probably find a way to make the C# example generate the same managed
code as the F# example, it is clear that the F# example requires less work and less lines of code
to generate the more succinct result.

No comments:

Post a Comment