ritter.vg
tech > code > adventures in code > comparing loop hoisting in .net
8 Nov 2009 1:24:23 EST

For reasons I can't fully justify, I decided to see just what, if anything, happens when you do loop hoisting yourself.

using System;
using System.Collections.Generic;

class Program
{
	static void Main(string[] args)
	{
		List<int> ints = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
		int j = ints.Count;
		for (int i = 0; i < j; i++)
		{
			Console.WriteLine(ints[i]);
		}
		Console.ReadLine();
	}
}

//and

        static void Main(string[] args)
        {
                List<int> ints = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
                for (int i = 0; i < ints.Count; i++)
                {
                        Console.WriteLine(ints[i]);
                }
                Console.ReadLine();
        }

//Compiled:
$ csc hoist.cs /debug:pdbonly /o+ /platform:x86

So, I take it down to IL and find that the difference in IL is pretty straightforward, saving a callvirt in each iteration of the loop - just what you'd expect. Here's the line that's cut off:

  IL_0019:  callvirt   instance int32 class [mscorlib]System.Collections.Generic.List`1<int32>::get_Count()
Diff of the IL between hoisting and not hoisting.

But here's the thing. I read somewhere that the JIT compiler will optimize these things. So for no particurally good reason, I decided to go read assembly. Only by the grace of Mike Dimmick's excellent blog post was I able to learn enough WinDbg to do this. The commands in WinDbg, for reference, were the following, although you'll of course have to adjust the memory locations.

.loadby sos.dll mscorwks
!dumpdomain 
!dumpmodule -mt 01d42c5c
!dumpmt -md 01d43014
!U 02320070 

Here's where things get interesting! In the image below I cleaned up and manipulated things to produce a clean diff image. Removed memory addresses and the like and replaced them with labels. You won't see this exactly from the !U WinDbp dump, but it'll be close. Diff of the Assembly between hoisting and not hoisting.

You can see the differences in the assembly and that ultimatly, they amount to nothing! I'll prove it. Comparing the efficiency of the assembly.

Now an extra memory read is pretty much what we expected, right? Yea, on the non-hoisting code! This is on the hoisting code!

So what did we learn? Pre-mature optimization is a bad idea. What? You already knew that? Well then this is more proof you didn't need, and a fun exercise for me then. Good night.

This blog post would not have been possible without the following resources: Mike Dimmick's aforementioned blog post on WinDbg, endeavormac's excellent x86 Assembly for C Programmers blog post that helped me brush up on Assembly, this awesome opcode cheat sheet, and two old usenet postings.

Comments
Add a comment...
required
required, hidden, gravatared

required, markdown enabled (help)
you type:you see:
*italics*italics
**bold**bold
[stolen from reddit!](http://reddit.com)stolen from reddit!
* item 1
* item 2
* item 3
  • item 1
  • item 2
  • item 3
> quoted text
quoted text
Lines starting with four spaces
are treated like code:

    if 1 * 2 < 3:
        print "hello, world!"
Lines starting with four spaces
are treated like code:
if 1 * 2 < 3:
    print "hello, world!"