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