Saturday, June 27, 2009

Template Method Pattern & IEnumerable

An Analysis Through C#'s Evolution

The template method is a very useful pattern commonly used within Data Access Layers. It's one of the simplest patterns within the gang of four, very similar to Command and Strategy and also belongs to the behavioral patterns. It makes good use of polymorphism in a simple class hierarchy.

Template Method's UML

Diagram copied from data & object factory.

A template method defines the skeleton of an algorithm delegating specific operations in it to subclasses. Whenever a process within an object is very similar between different siblings, then a Template Method is a candidate. I don't want to bore you with the details so I'll dig right into some code.

Consider the following abstract class for games:

protected abstract void InitializeGame();
protected abstract ArrayList GetPlayers();
protected abstract void MakeMove(Player player);
protected abstract bool GameEnded { get; }
protected abstract void DisplayResults();

public void Play()

    InitializeGame();
    while (!GameEnded)
    {
        foreach (Player player in GetPlayers())
        {
            MakeMove(player);
        }
    }
    DisplayResults();
}

And a simple Player class:

public class Player
{
    private readonly string name;

    public string Name
    {
        get { return this.name; }
    }

    public Player(string name)
    {
        this.name = name;
    }

    public override bool Equals(object obj)
    {
        Player player = obj as Player;
        return (player != null) && object.Equals(this.Name, player.Name);
    }

    public override int GetHashCode()
    {
        return this.Name == null ? 0 : this.Name.GetHashCode() * 53;
    }

}

The Play method is a Template Method, wherein InitializeGame, GetPlayers, MakeMove, GameEnded and DisplayResult are Primitive Operations.

Here's a basic implementation of a High Card game:

public class HighCard : GameBase
{

    private int rounds;
    private ArrayList players;
    private int currentRound;
    private Random random;
    private Card[] currentHand;

    public HighCard()
    {
        players = new ArrayList();
        random = new Random();
    }

    protected override void InitializeGame()
    {
        players.Clear();
        currentRound = 0;
        Hashtable names = new Hashtable();
        Console.WriteLine("-------------------------------------------------------------------------");
        Console.WriteLine(" Welcome to the Increbile High Card game. Please buckle your seat belts.");
        Console.WriteLine("-------------------------------------------------------------------------");
        Console.WriteLine();
        bool keepAsking = true;
        while (keepAsking)
        {
            if (players.Count < 2)
            {
                Console.Write("Enter player's name: ");
            }
            else
            {
                Console.Write("Enter player's name (leave blank for no more players): ");
            }
            string playerName = Console.ReadLine();
            if (playerName.Length > 0)
            {
                if (names.ContainsKey(playerName))
                {
                    Console.WriteLine("I'm sorry, but we already have a contestant named {0}.", playerName);
                }
                else
                {
                    Player player = new HighCardPlayer(playerName);
                    players.Add(player);
                    names.Add(playerName, null);
                }
            }
            else
            {
                if (players.Count >= 2)
                {
                    keepAsking = false;
                }
            }
        }
        currentHand = new Card[players.Count];
        Console.WriteLine();
        Console.Write("How many rounds would you like to play? ");
        while (!int.TryParse(Console.ReadLine(), out rounds))
        {
            Console.Write("I'm sorry, I don't understand what you mean by that. How many rounds? ");
        }
        Console.WriteLine();
    }

    protected override ArrayList GetPlayers()
    {
        return players;
    }

    protected override void MakeMove(Player player)
    {
        int playerIndex = players.IndexOf(player);

        Console.Write("{0} gets the... ", player.Name);
        Thread.Sleep(random.Next(500, 1000));

        Card card = GetNewCard();

        Console.WriteLine("{0} of {1}", card.Value, card.Suit);
        currentHand[playerIndex] = card;

        if (playerIndex == players.Count - 1)
        {
            EvaluateRound();
        }
    }

    protected override bool GameEnded
    {
        get 
        { 
            return rounds == currentRound; 
        }
    }

    protected override void DisplayResults()
    {
        Console.WriteLine("GAME OVER");
        Console.WriteLine();
        Console.WriteLine("Final Scores:");

        ArrayList winners = new ArrayList();
        int winnerScore = int.MinValue;
        for (int i = 0; i < players.Count; i++)
        {
            HighCardPlayer player = (HighCardPlayer)players[i];
            Console.WriteLine("* {0}: {1} points", player.Name, player.Score);
            if (player.Score > winnerScore)
            {
                winnerScore = player.Score;
                winners.Clear();
                winners.Add (player);
            }
            else if (player.Score == winnerScore)
            {
                winners.Add (player);
            }
        }
        Console.WriteLine();
        if (winners.Count != players.Count)
        {
            if (winners.Count == 1)
            {
                Console.Write("And the winner is: ");
            }
            else
            {
                Console.Write("And the winners are: ");
            }
            Console.ForegroundColor = ConsoleColor.White;
            Console.Write(((Player)winners[0]).Name.ToUpper());
            for (int i = 1; i < winners.Count; i++)
            {
                Console.Write(" - {0}", ((Player)winners[i]).Name.ToUpper());
            }
            Console.WriteLine();
            Console.ForegroundColor = ConsoleColor.Gray;
        }
        else
        {
            Console.WriteLine("There are NO winners! It's an all tie!");
        }
    }

    private Card GetNewCard()
    {
        Card card = null;
        do
        {
            card = new Card(random.Next(1, 14), random.Next(1, 5));
        } while (CardIsInHand(card));

        return card;
    }

    private bool CardIsInHand(Card card)
    {
        for (int i = 0; i < currentHand.Length; i++)
        {
            if (card.Equals(currentHand[i]))
            {
                return true;
            }
        }
        return false;
    }

    private void EvaluateRound()
    {
        currentRound++;
        HighCardPlayer player = GetRoundsWinner();
        Console.WriteLine();
        Console.WriteLine("Congratulations {0}! You won this round!", player.Name);
        Console.WriteLine();
        player.Score++;
        ClearHand();
    }

    private HighCardPlayer GetRoundsWinner()
    {
        int winnerIndex = 0;
        for (int i = 1; i < currentHand.Length; i++)
        {
            if (currentHand[i].CompareTo(currentHand[winnerIndex]) > 0)
            {
                winnerIndex = i;
            }
        }
        return (HighCardPlayer)players[winnerIndex];
    }

    private void ClearHand()
    {
        for (int i = 0; i < currentHand.Length; i++)
        {
            currentHand[i] = null;
        }
    }

}

public class Card: IComparable
{

    int valueIndex;
    int suitIndex;

    private static string[] suits = new String[] { "Clubs", "Diamonds", "Hearts", "Spades" };
    private string[] values = new string[] { "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A" };

    public Card(int value, int suit)
    {
        if ((value < 1) || (value > 13)) throw new ArgumentOutOfRangeException("value");
        if ((suit < 1) || (suit > 4)) throw new ArgumentOutOfRangeException("suit");

        this.suitIndex = suit - 1;
        if (value == 1)
        {
            this.valueIndex = 12;
        }
        else
        {
            this.valueIndex = value - 2;
        }
    }

    public string Value
    {
        get { return values[valueIndex]; }
    }

    public string Suit
    {
        get { return suits[suitIndex]; }
    }

    public override bool Equals(object obj)
    {
        Card card = obj as Card;
        return (card != null) && object.Equals(card.Value, this.Value) && object.Equals(card.Suit, this.Suit);
    }

    public override int GetHashCode()
    {
        int code = 13;
        code = (code * 23) + (this.Value != null ? this.Value.GetHashCode() : 0);
        code = (code * 23) + (this.Suit != null ? this.Suit.GetHashCode() : 0);

        return code;
    }

    public int CompareTo(object obj)
    {
        Card card = obj as Card;
        if (obj == null) throw new ArgumentNullException("obj");
        if (card == null) throw new ArgumentException("The object has to be a card.", "obj");

        if (this.valueIndex.Equals(card.valueIndex))
        {
            return this.suitIndex.CompareTo(card.suitIndex);
        }
        else
        {
            return this.valueIndex.CompareTo(card.valueIndex);
        }
    }
}

class HighCardPlayer: Player
{
    private int score;

    public int Score
    {
        get { return this.score; }
        set { this.score = value; }
    }

    public HighCardPlayer(string name): base(name)
    { }

}

A couple of things to notice:

  • To really get the benefits of the Template Method Pattern, the method itself would have to be complex, relying on simple Primitive Operations implemented on derived classes. This example does a very bad job illustrating this. A good example for this is the Insert method of a data access object that would rely on derived classes operations like GetTableName and GetFieldMappings.
  • This implementation is done to work with the first .NET Framework. This is on purpose. The reason for this lies right ahead.
  • This game's implementation is not a very good one. It has a few flaws, but I didn't want the code to be more complex than it needed to. A Deck class and refactoring Card would be recommended, plus as it is, there is a better chance of having repeated value cards in the same hand than it should. This is beyond the scope of this post, so I'll leave it at that. (Originally planned on implementing Rock, Paper & Scissors, but it seemed too cliché).

From here onwards, the focus will be on the GetPlayers primitive operation.

C# 1.0 - Where it all Started

The GetPlayers method returns an ArrayList. All we do with the returned list is enumerating through it so changing it to return an IEnumerable makes perfect sense. This adds an extra drop of flexibility to game implementers. They can use an array, an ArrayList, or even their own IEnumerable implementation. To be fair, this isn't an extremely compelling reason, and I admit that on my C# 1.0 days, I would use ArrayLists for pretty much everything. Not one of the smartest practices, I reckon, but did it anyways. Anyhow, this is the modified abstract method:

protected abstract IEnumerable GetPlayers();

This adds no extra complexity to our previous implementation, although it does break it. We need to change our return type. New game implementations will thank us.

C# 2.0 - If I Wanted to do all this Castings, I'd be in the Acting Industry

I will absolutely not engage in a discussion about all the benefits of strong typed collections introduced at this point. I'll just say the world is now a better place thanks to generics and its very welcome nasty breaking changes. Unless there were an awful lot of game implementations out there, and little more to come, changing the method to return IEnumerable<Player> is a must!

protected abstract IEnumerable<Player> GetPlayers();

Admittedly, this does add extra complexity to our HighCard class. I chose to make the players field a List<HighCardPlayer>. This does avoid a whole lot of extra casts that had to be done before along the entire class, but the GetPlayers method did become longer:

protected override IEnumerable<Player> GetPlayers()
{
    return players.ConvertAll<Player>(delegate(HighCardPlayer player)
    {
        return player;
    });
}

Still, let analyze what the advantages are:

For one, if our players list was of the Player type, no extra code would've been required. Also, as for now, on our implementations we can take advantage on the newly incorporated yield keyword. If you don't like anonymous methods, the GetPlayers method can also be written this way:

protected override IEnumerable<Player> GetPlayers()
{
    foreach (Player player in players)
    {
        yield return player;
    }
}

Yes, it is that simple. But it gets even better; consider that the game you are implementing has a constant amount of players. You could be creating a backgammon game with player1 and player2. This would look just like this:

protected override IEnumerable<Player> GetPlayers()
{
    yield return player1;
    yield return player2;
}

Although this is two lines of code, it is way more readable than what we would've done before:

protected override IEnumerable<Player> GetPlayers()
{
    return new Player[] { player1, player2 };
}

Even for a solitaire game, return a 1 item enumeration is simpler, and in the probably not applicable case of a no playered game:

protected override IEnumerable<Player> GetPlayers()
{
    yield break;
}

So the yield keyword will offer our implementers a whole new universe of possibilities.

C# 3.0 – Linqing the OOP World with the Functional World

As you've already guessed, now C# 3.0 came along, and even more flexibility is added to our GetPlayers method. Thanks to the functional programming fellows, extension methods, lambda expressions and linq is introduced. Now more complex games, with say eliminations between rounds are pretty simple to implement.

protected override IEnumerable<Player> GetPlayers()
{
    return players
        .Where(player => !player.IsEliminated)
        .OrderByDescending(player => player.Score)
        .Cast<Player>();
}

Or even in query language:

protected override IEnumerable<Player> GetPlayers()
{
    return from highCardplayer in players
           let player = (Player)highCardplayer
           where !highCardplayer.IsEliminated
           orderby highCardplayer.Score descending
           select player;
}

Even our version in HighCard gets slightly simplified due to extension methods:

protected override IEnumerable<Player> GetPlayers()
{
    return players.Cast<Player>();
}

The beauty in this is that we have introduced no breaking changes what so ever, in fact, our GameBase class hasn't even changed.

C# 4.0 – A Peek into the Future

With C# 4.0 we will be able to take advantage of the covariant aspect of IEnumerable<T>. Basically, an IEnumerable<HighCardPlayer> can be implicitly converted to IEnumerable<Player>. This takes us back to our method being as simple as it started out being:

protected override IEnumerable<Player> GetPlayers()
{
    return players;
}

Notice how players is List<HighCardPlayer> which in turn is an IEnumerable<HighCardPlayer>, and thanks to covariance, it is also an IEnumerable<Player>.

Our GameBaseclass didn't change and no breaking changes were introduced. Still, interesting enough, there is a chance that switching to C# 4.0 will introduce a breaking change. If we had something like this:

IEnumerable playerEnum = players;

if (playerEnum is IEnumerable<Player>)
{
    // Do something
}
else if (playerEnum is IEnumerable<HighCardPlayer>)
{
    // Do something else
}

Now, the second block will be unreachable since every IEnumerable<HighCardPlayer> is in fact also an IEnumerable<Player>.

The Reason behind this Post

I've been working on a code generation project. For example, the class generator makes full use of Template Methods that use operations like GetFields, GetMethods, GetProperties and even GetDecorators. These operations have changed during time taking advantage of everything noted above, and I found it very interesting. I thought I'd share.

Random thoughts: The template method pattern isn't really the reason of all the things that were shown here, the responsible is the IEnumerable<T> interface. I might as well have used an interface with IEnumerable<T> methods or properties, but took this opportunity to talk a little about this pattern. I usually find it extremely useful, and the simplicity of it makes it yet more desirable. Pretty much like everything else, the value is benefit/complexity. Having such low complexity makes it very valuable, and I love the KISS principle.

On a personal note, I wish my very good friend Gonzalo a very happy birthday. I hope you enjoy this post and good luck on your future endeavors!

Thursday, June 11, 2009

MSDN Forums Browser

Yesterday was an exciting day; David Morton's MSDN Forums Browser was released at Codeplex. It's on a very alpha state, but it seems clear to me that has the potential to become an excellent and very valuable tool.

Yet today it got even better, he made me a part of his team! It's a wonderful opportunity to work side by side with some extremely talented developers. I'm thrilled about it, and I will embrace this opportunity to learn as much as I can, while hopefully becoming a valuable asset for the team.

Needless to say I encourage you to download it, try it out and give as much feedback as you can.

MSDN Forums Browser's Screenshot

Link to project: http://forumsbrowser.codeplex.com/

Saturday, May 30, 2009

Who's on First

This is a follow-up to my previous post: Swap Ints, Two Variables, No Waiting.

While analyzing my method's CIL, I thought that maybe I could implement a faster swap. Maybe there is a way to implement a method that doesn't need a temp variable but without all the overhead we've seen with xor. Well, the following came up:

.method public hidebysig static void  SwapIntsCil(int32& first,
                                                  int32& second) cil managed
{
  // Code size       9 (0x9)
  .maxstack  4
  IL_0000:  ldarg.0
  IL_0001:  ldarg.1
  IL_0002:  ldind.i4
  IL_0003:  ldarg.1
  IL_0004:  ldarg.0
  IL_0005:  ldind.i4
  IL_0006:  stind.i4
  IL_0007:  stind.i4
  IL_0008:  ret
} // end of method IntSwapper::SwapIntsCil

Let's see what it does:

  • IL_0000 Pushes first's address into the stack.
  • IL_0001 Pushes second's address into the stack.
  • IL_0002 Pops the address to fetch and push the actual Int32 value.
  • IL_0003 Pushes second's address into the stack.
  • IL_0004 Pushes first's address into the stack.
  • IL_0005 Pops the address to fetch and push the actual Int32 value.
  • IL_0006 Pops first's value and second's address to store the value at the address.
  • IL_0007 Pops second's value and first's address to store the value at the address.
  • IL_0008 Gets the hell outta here.

Click to display diagram.

To sum up, this method would use 4 levels of stack, declare no local variables, load 4 addresses, load 2 values through these addresses, store 2 values to these addresses and perform no operations.

In order to implement this, I moved previous swaps into a separate Assembly called KeepItSharp (yes, I'm that original) and to a class called IntSwapper. Built it in release mode, opened it with ildasm and dumped the CIL code. Added my new method and compiled it with ilasm. Time to test it!

static void Main(string[] args)
{
    int a = 15;
    int b = 320;
    Console.WriteLine("a: {0}\tb: {1}", a, b);

    KeepItSharp.IntSwapper.SwapIntsCil(ref a, ref b);

    Console.WriteLine("a: {0}\tb: {1}", a, b);
}

And the result is... <drumroll>

a: 15   b: 320
a: 320  b: 15
Press any key to continue . . .

Cool… my first CIL code and it works! Well, can't wait to see if it actually is faster than the conventional swap. Here it goes:

Benchmarking code is omitted because it's analogue to the one on my previous post.

SwapIntsXor: 00.559 - SwapIntsCil: 00.190 - SwapInts: 00.200
SwapIntsXor: 00.543 - SwapIntsCil: 00.189 - SwapInts: 00.194
SwapIntsXor: 00.544 - SwapIntsCil: 00.191 - SwapInts: 00.194
SwapIntsXor: 00.545 - SwapIntsCil: 00.190 - SwapInts: 00.190
SwapIntsXor: 00.549 - SwapIntsCil: 00.193 - SwapInts: 00.192
Press any key to continue . . .

Darn it! It seems to be a close one. OK, let's call it a tie.

Now, it seemed like fun to see what Red Gate's .NET Reflector had to say about this method, and the result is pretty peculiar. Check it out:

public static void SwapIntsCil(ref int first, ref int second)
{
    second = first;
    first = second;
}

And this works? Actually I wish that reflector would have thrown an error instead of showing this mysterious piece of coding that should've never worked.

The whole time while writing these last two posts, I kept thinking about Abbott and Costello's "Who's on First". Who's on first, what's on second, and on third? I don't know, I'm not using a temp variable!

Random thoughts: Aside from being cool to code some CIL, what left me thinking most was Reflector's behavior. For one, I will not stop using Reflector since the benefits are huge and these scenarios very scarce. On another note, this means that using CIL code directly allows us to write code that can't be translated to C# with reflecting tools. This seems pretty useful for licensing code, for example, although it would be easier to just code it in C and p/invoke it.

You can download KeepItSharp.dll here.

Thursday, May 28, 2009

Swap Ints, Two Variables, No Waiting

Who hasn't had to swap two ints? Everyone who implemented a sort algorithm, for example, had to swap two values. What does this function look like? Probably something like this:

static void SwapInts(ref int first, ref int second)
{
    int aux = first;
    first = second;
    second = aux;
}

And it works too!

static void Main(string[] args)
{
    int a = 15;
    int b = 320;
    Console.WriteLine("a: {0}\tb: {1}", a, b);

    SwapInts(ref a, ref b);

    Console.WriteLine("a: {0}\tb: {1}", a, b);
}
a: 15   b: 320
a: 320  b: 15
Press any key to continue . . .

My parents are so proud!

Anyways, a co-worker challenged me to implement the SwapInts method without using an auxiliary variable. We all know that patience is a virtue, and I know that it is something I should work on. I got frustrated within minutes and demanded the solution to this. It turned out not so complex after all. Check it out:

static void SwapIntsXor(ref int first, ref int second)
{
    first ^= second;
    second ^= first;
    first ^= second;
}

This works because xoring first and second produces a number that when xored with first we get second and when xored with second we get first. Pretty slick! Let's take a deeper look. Remember the xor table?

XOR01
001
110

Now, a pretty cool characteristic about xor is that when you xor twice you get back where you started:

0011
0101
0110

And once again…

0110
0101
0011

Bingo!

Now lets breakdown our new method. Consider A to be the first value, B the second and C the xored value.

first ^= second; -> first is C / second is B
second ^= first; -> first is C / second is A
first ^= second; -> first is B / second is A

This got me thinking a little. Of course this has absolutely no use in real life code, but it seemed as in terms of performance we could have a little improvement. So, no time to waste, I benchmarked them:

static void Main(string[] args)
{
    int a = 15;
    int b = 320;

    // Warm up
    SwapIntsXor (ref a,ref b);
    SwapInts (ref a, ref b);

    Benchmark(100000000, 5);
}

static void Benchmark(int innerIterations, int outterIterations)
{
    int a = 15;
    int b = 320;
    Stopwatch sw = new Stopwatch();

    for (int i = 0; i < outterIterations; i++)
    {
        sw.Start();
        for (int j = 0; j < innerIterations; j++)
        {
            SwapInts(ref a, ref b);
        }
        sw.Stop();
        Console.Write("SwapInts: {0:ss.fff} - ", new DateTime(sw.Elapsed.Ticks));
        sw.Reset();

        sw.Start();
        for (int j = 0; j < innerIterations; j++)
        {
            SwapIntsXor(ref a, ref b);
        }
        sw.Stop();
        Console.WriteLine("SwapIntsXor: {0:ss.fff}", new DateTime(sw.Elapsed.Ticks));
        sw.Reset();
    }
}

Output:

SwapInts: 00.190 - SwapXor: 00.558
SwapInts: 00.185 - SwapXor: 00.548
SwapInts: 00.189 - SwapXor: 00.547
SwapInts: 00.187 - SwapXor: 00.554
SwapInts: 00.187 - SwapXor: 00.546
Press any key to continue . . .

The conventional swap is faster than the new one. I can't stress enough that this was just for experimental purposes and to satisfy my own curiosity. By any means, even if with xor was significantly faster, would I recommend it since the conventional one is well known, straight forward and much, much easier to understand.

Anyways, since I've never really tried to understand any CIL, this seemed like a sweet opportunity to take the first steps and understand what makes it slower. Let's analyze the SwapInts method:

.method private hidebysig static void  SwapInts(int32& first,
                                                int32& second) cil managed
{
  // Code size       11 (0xb)
  .maxstack  2
  .locals init ([0] int32 aux)
  IL_0000:  ldarg.0
  IL_0001:  ldind.i4
  IL_0002:  stloc.0
  IL_0003:  ldarg.0
  IL_0004:  ldarg.1
  IL_0005:  ldind.i4
  IL_0006:  stind.i4
  IL_0007:  ldarg.1
  IL_0008:  ldloc.0
  IL_0009:  stind.i4
  IL_000a:  ret
} // end of method Program::SwapInts
  • IL_0000 Pushes first's address into the stack.
  • IL_0001 Pops the address to fetch and push the actual Int32 value.
  • IL_0002 Pops first's value and stores it into local variable aux.
  • IL_0003 Pushes first's address into the stack.
  • IL_0004 Pushes second's address into the stack.
  • IL_0005 Pops the address to fetch and push the actual Int32 value.
  • IL_0006 Pops second's value and first's address to store the value at the address.
  • IL_0007 Pushes second's address into the stack.
  • IL_0008 Pushes the value at local variable aux.
  • IL_0009 Pops aux's value and second's address to store the value at the address.
  • IL_000a Gets the hell outta here.

Click to display diagram.

To sum up, this method uses only 2 levels of the stack, declares 1 local variable, loads 4 addresses, loads 2 values through these addresses, loads 1 value through a local variable, stores 2 values to addresses, stores 1 value to a local variable and performs no operations.

Note: The stack mentioned above is a conceptual stack called the Virtual Execution System. This is not "the stack" everyone talks about when referring to where the local and parameter value types are stored. That being said lets move on...

Now we should check out the SwapIntsXor method:

.method private hidebysig static void  SwapIntsXor(int32& first,
                                                   int32& second) cil managed
{
  // Code size       22 (0x16)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  dup
  IL_0002:  ldind.i4
  IL_0003:  ldarg.1
  IL_0004:  ldind.i4
  IL_0005:  xor
  IL_0006:  stind.i4
  IL_0007:  ldarg.1
  IL_0008:  dup
  IL_0009:  ldind.i4
  IL_000a:  ldarg.0
  IL_000b:  ldind.i4
  IL_000c:  xor
  IL_000d:  stind.i4
  IL_000e:  ldarg.0
  IL_000f:  dup
  IL_0010:  ldind.i4
  IL_0011:  ldarg.1
  IL_0012:  ldind.i4
  IL_0013:  xor
  IL_0014:  stind.i4
  IL_0015:  ret
} // end of method Program::SwapIntsXor

It's seems heavier already! Let's start:

  • IL_0000 Pushes first's address into the stack.
  • IL_0001 Duplicates the top value of the stack.
  • IL_0002 Pops the address to fetch and push the actual Int32 value.
  • IL_0003 Pushes second's address into the stack.
  • IL_0004 Pops the address to fetch and push the actual Int32 value.
  • IL_0005 Pops second's value and first's value to perform a xor and push the result.
  • IL_0006 Pops the xor's result and first's address to store the value at the address.
  • IL_0007 Pushes second's address into the stack.
  • IL_0008 Duplicates the top value of the stack.
  • IL_0009 Pops the address to fetch and push the actual Int32 value.
  • IL_000a Pushes first's address into the stack.
  • IL_000b Pops the address to fetch and push the actual Int32 value.
  • IL_000c Pops first's value and second's value to perform a xor and push the result.
  • IL_000d Pops the xor's result and second's address to store the value at the address.
  • IL_000e Pushes first's address into the stack.
  • IL_000f Duplicates the top value of the stack.
  • IL_0010 Pops the address to fetch and push the actual Int32 value.
  • IL_0011 Pushes second's address into the stack.
  • IL_0012 Pops the address to fetch and push the actual Int32 value.
  • IL_0013 Pops second's value and first's value to perform a xor and push the result.
  • IL_0014 Pops the xor's result and first's address to store the value at the address.
  • IL_0015 Gets the hell outta here.

Click to display diagram.

To sum up, this method uses 3 levels of stack, declares no local variables, loads 6 addresses, duplicates 3 of these, loads 6 values through these addresses, stores 3 values to addresses and performs 3 operations. This one is definitely heavier than the previous.

I believe is worth noting that CIL doesn't necessarily translate directly or proportionally to machine code, it's just one step closer. Analyzing the machine code is a whole other story; a story I don't plan getting close to anywhere in the near future.

Random thoughts: I wonder why .maxstack is set on 8 for SwapIntXor when 3 would suffice, but for what I've seen this is pretty common. Anyhow, I have no idea if anyone will find all this information useful at all, but it was an extremely helpful exercise for me and a lot of fun.

Edit: Switched the benchmark. It was previously done in debug mode.

Follow up: Who's of First