Fast Blur (Box Blur with accumulator)

This post is similar to my previous post, but this post is about blurring an existing image, the previous post was about rendering colored rectangles.

For a game I’m creating (check out a beta at Flow) I needed a really fast way to repeatedly blur images (WriteableBitmap in this case) in Silverlight.
There are blur functions as image effects, and if that satisfies your needs then you should go with that. But I needed to generate my bitmap, blur it, update it, blur it and so on.

I’ve submitted the code below to the WriteableBitmapEx project as a contribution, but it hasn’t been accepted nor rejected yet. I’m publishing it here in case anyone needs it.

Box Blur is a very fast method for blurring (http://www.vcskicks.com/box-blur.php) but for it to be fast you must implement it as an accumulator, not a convolution loop.

Finding implementations of Box Blur, in C#, that uses convolution kernels (as in the link above) is trivial, finding one that uses an accumulator proved more difficult. The method below uses an accumulator.

The code for using the blur method looks like this;


WriteableBitmap bitmap = new WriteableBitmap((BitmapSource)Image.Source);
bitmap.BoxBlur(13);
Image.Source = bitmap;
bitmap.Invalidate();

Before Box Blur;

After Box Blur (range=13);

Running BoxBlur on this 512×512 takes about 32 ms (averaged over 1000 runs) on my computer.

public static void BoxBlur(this WriteableBitmap bmp, int range)
{
	if ((range & 1) == 0)
	{
		throw new InvalidOperationException("Range must be odd!");
	}

	bmp.BoxBlurHorizontal(range);
	bmp.BoxBlurVertical(range);
}

public static void BoxBlurHorizontal(this WriteableBitmap bmp, int range)
{
	int[] pixels = bmp.Pixels;
	int w = bmp.PixelWidth;
	int h = bmp.PixelHeight;
	int halfRange = range / 2;
	int index = 0;
	int[] newColors = new int[w];

	for (int y = 0; y < h; y++)
	{
		int hits = 0;
		int r = 0;
		int g = 0;
		int b = 0;
		for (int x = -halfRange; x < w; x++)
		{
			int oldPixel = x - halfRange - 1;
			if (oldPixel >= 0)
			{
				int col = pixels[index + oldPixel];
				if (col != 0)
				{
					r -= ((byte)(col >> 16));
					g -= ((byte)(col >> 8 ));
					b -= ((byte)col);
				}
				hits--;
			}

			int newPixel = x + halfRange;
			if (newPixel < w)
			{
				int col = pixels[index + newPixel];
				if (col != 0)
				{
					r += ((byte)(col >> 16));
					g += ((byte)(col >> 8 ));
					b += ((byte)col);
				}
				hits++;
			}

			if (x >= 0)
			{
				int color =
					(255 << 24)
					| ((byte)(r / hits) << 16)
					| ((byte)(g / hits) << 8 )
					| ((byte)(b / hits));

				newColors[x] = color;
			}
		}

		for (int x = 0; x < w; x++)
		{
			pixels[index + x] = newColors[x];
		}

		index += w;
	}
}

public static void BoxBlurVertical(this WriteableBitmap bmp, int range)
{
	int[] pixels = bmp.Pixels;
	int w = bmp.PixelWidth;
	int h = bmp.PixelHeight;
	int halfRange = range / 2;

	int[] newColors = new int[h];
	int oldPixelOffset = -(halfRange + 1) * w;
	int newPixelOffset = (halfRange) * w;

	for (int x = 0; x < w; x++)
	{
		int hits = 0;
		int r = 0;
		int g = 0;
		int b = 0;
		int index = -halfRange * w + x;
		for (int y = -halfRange; y < h; y++)
		{
			int oldPixel = y - halfRange - 1;
			if (oldPixel >= 0)
			{
				int col = pixels[index + oldPixelOffset];
				if (col != 0)
				{
					r -= ((byte)(col >> 16));
					g -= ((byte)(col >> 8 ));
					b -= ((byte)col);
				}
				hits--;
			}

			int newPixel = y + halfRange;
			if (newPixel < h)
			{
				int col = pixels[index + newPixelOffset];
				if (col != 0)
				{
					r += ((byte)(col >> 16));
					g += ((byte)(col >> 8 ));
					b += ((byte)col);
				}
				hits++;
			}

			if (y >= 0)
			{
				int color =
					(255 << 24)
					| ((byte)(r / hits) << 16)
					| ((byte)(g / hits) << 8 )
					| ((byte)(b / hits));

				newColors[y] = color;
			}

			index += w;
		}

		for (int y = 0; y < h; y++)
		{
			pixels[y * w + x] = newColors[y];
		}
	}
}
public static void BoxBlur(this WriteableBitmap bmp, int range) { if ((range & 1) == 0) { throw new InvalidOperationException(“Range must be odd!”); }   bmp.BoxBlurHorizontal(range); bmp.BoxBlurVertical(range); } public static void BoxBlurHorizontal(this WriteableBitmap bmp, int range) { int[] pixels = bmp.Pixels; int w = bmp.PixelWidth; int h = bmp.PixelHeight; int halfRange = range / 2; int index = 0; int[] newColors = new int[w]; for (int y = 0; y < h; y++) { int hits = 0; int r = 0; int g = 0; int b = 0; for (int x = -halfRange; x < w; x++) { int oldPixel = x – halfRange – 1; if (oldPixel >= 0) { int col = pixels[index + oldPixel]; if (col != 0) { r -= ((byte)(col >> 16)); g -= ((byte)(col >> 8)); b -= ((byte)col); } hits–; } int newPixel = x + halfRange; if (newPixel < w) { int col = pixels[index + newPixel]; if (col != 0) { r += ((byte)(col >> 16)); g += ((byte)(col >> 8)); b += ((byte)col); } hits++; } if (x >= 0) { int color = (255 << 24) | ((byte)(r / hits) << 16) | ((byte)(g / hits) << 8) | ((byte)(b / hits)); newColors[x] = color; } } for (int x = 0; x < w; x++) { pixels[index + x] = newColors[x]; } index += w; } } public static void BoxBlurVertical(this WriteableBitmap bmp, int range) { int[] pixels = bmp.Pixels; int w = bmp.PixelWidth; int h = bmp.PixelHeight; int halfRange = range / 2; int[] newColors = new int[h]; int oldPixelOffset = -(halfRange + 1) * w; int newPixelOffset = (halfRange) * w; for (int x = 0; x < w; x++) { int hits = 0; int r = 0; int g = 0; int b = 0; int index = -halfRange * w + x; for (int y = -halfRange; y < h; y++) { int oldPixel = y – halfRange – 1; if (oldPixel >= 0) { int col = pixels[index + oldPixelOffset]; if (col != 0) { r -= ((byte)(col >> 16)); g -= ((byte)(col >> 8)); b -= ((byte)col); } hits–; } int newPixel = y + halfRange; if (newPixel < h) { int col = pixels[index + newPixelOffset]; if (col != 0) { r += ((byte)(col >> 16)); g += ((byte)(col >> 8)); b += ((byte)col); } hits++; } if (y >= 0) { int color = (255 << 24) | ((byte)(r / hits) << 16) | ((byte)(g / hits) << 8) | ((byte)(b / hits)); newColors[y] = color; } index += w; } for (int y = 0; y < h; y++) { pixels[y * w + x] = newColors[y]; } } }

Colored/Shaded Rectangle on a WriteableBitmap

I recently needed a way to render a colored rectangle on a WriteableBitmap in Silverlight – though the code could be used in WPF with minor changes. Typically, you’d not use WriteableBitmaps but different Shapes in WPF, but this time I needed to create my own bitmap.

I’ve submitted the code as a contribution to the WriteableBitmapEx.codeplex.com project, but it hasn’t been accepted nor rejected yet, so I figured I’d post it here in case anyone ever needs something similar.

Drawing a 255×255 shaded block takes 2.06 ms (averaged over 1000 draws).

Shaded rectangle rendered on picture of lena;

WriteableBitmap bitmap = new WriteableBitmap((BitmapSource)Image.Source);
int width = 255;
int start = (512 - width) / 2;
bitmap.DrawFilledRectangle(
 start, start,
 start + width, start + width,
 Colors.White,
 Colors.Red,
 Colors.Green,
 Colors.Blue);

Image.Source = bitmap;
bitmap.Invalidate();

public static void DrawFilledRectangle(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, Color color)
{
	// Add one to use mul and cheap bit shift for multiplicaltion
	var a = color.A + 1;
	var col = (color.A << 24)
			 | ((byte)((color.R * a) >> 8 ) << 16)
			 | ((byte)((color.G * a) >> 8 ) << 8 )
			 | ((byte)((color.B * a) >> 8 ));

	bmp.DrawFilledRectangle(x1, y1, x2, y2, col);
}

public static void DrawFilledRectangle(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, Color color00, Color color01, Color color10, Color color11)
{
	// Add one to use mul and cheap bit shift for multiplicaltion
	var a00 = color00.A + 1;
	var col00 = (color00.A << 24)
			 | ((byte)((color00.R * a00) >> 8 ) << 16)
			 | ((byte)((color00.G * a00) >> 8 ) << 8 )
			 | ((byte)((color00.B * a00) >> 8 ));

	var a01 = color01.A + 1;
	var col01 = (color01.A << 24)
			 | ((byte)((color01.R * a01) >> 8 ) << 16)
			 | ((byte)((color01.G * a01) >> 8 ) << 8 )
			 | ((byte)((color01.B * a01) >> 8 ));

	var a10 = color10.A + 1;
	var col10 = (color10.A << 24)
			 | ((byte)((color10.R * a10) >> 8 ) << 16)
			 | ((byte)((color10.G * a10) >> 8 ) << 8 )
			 | ((byte)((color10.B * a10) >> 8 ));

	var a11 = color11.A + 1;
	var col11 = (color11.A << 24)
			 | ((byte)((color11.R * a11) >> 8 ) << 16)
			 | ((byte)((color11.G * a11) >> 8 ) << 8 )
			 | ((byte)((color11.B * a11) >> 8 ));

	bmp.DrawFilledRectangle(x1, y1, x2, y2, col00, col01, col10, col11);
}

public static void DrawFilledRectangle(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int color)
{
	// Use refs for faster access (really important!) speeds up a lot!
	int w = bmp.PixelWidth;
	int h = bmp.PixelHeight;
	int[] pixels = bmp.Pixels;

	// Check boundaries
	if (x1 < 0) { x1 = 0; }
	if (y1 < 0) { y1 = 0; }
	if (x2 < 0) { x2 = 0; }
	if (y2 < 0) { y2 = 0; }
	if (x1 >= w) { x1 = w - 1; }
	if (y1 >= h) { y1 = h - 1; }
	if (x2 >= w) { x2 = w - 1; }
	if (y2 >= h) { y2 = h - 1; }

	int i = y1 * w;
	for (int y = y1; y < y2; y++)
	{
		int i2 = i + x1;
		while (i2 < i + x2)
		{
			pixels[i2++] = color;
		}
		i += w;
	}
}

public static void DrawFilledRectangle(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int color00, int color01, int color10, int color11)
{
	// Use refs for faster access (really important!) speeds up a lot!
	int w = bmp.PixelWidth;
	int h = bmp.PixelHeight;
	int[] pixels = bmp.Pixels;

	// Check boundaries
	if (x1 < 0) { x1 = 0; }
	if (y1 < 0) { y1 = 0; }
	if (x2 < 0) { x2 = 0; }
	if (y2 < 0) { y2 = 0; }
	if (x1 >= w) { x1 = w - 1; }
	if (y1 >= h) { y1 = h - 1; }
	if (x2 >= w) { x2 = w - 1; }
	if (y2 >= h) { y2 = h - 1; }

	// Retrieve the color channels
	byte a00 = (byte)(color00 >> 24); byte r00 = (byte)(color00 >> 16); byte g00 = (byte)(color00 >> 8 ); byte b00 = (byte)(color00);
	byte a10 = (byte)(color10 >> 24); byte r10 = (byte)(color10 >> 16); byte g10 = (byte)(color10 >> 8 ); byte b10 = (byte)(color10);
	byte a01 = (byte)(color01 >> 24); byte r01 = (byte)(color01 >> 16); byte g01 = (byte)(color01 >> 8 ); byte b01 = (byte)(color01);
	byte a11 = (byte)(color11 >> 24); byte r11 = (byte)(color11 >> 16); byte g11 = (byte)(color11 >> 8 ); byte b11 = (byte)(color11);

	//r01, r10
	int xrange = x2 - x1;
	int yrange = y2 - y1;

	for (int x = 0; x < xrange; x++)
	{
		int negx = xrange - x;
		byte atop = (byte)((a00 * negx + a01 * x) / xrange);
		byte rtop = (byte)((r00 * negx + r01 * x) / xrange);
		byte gtop = (byte)((g00 * negx + g01 * x) / xrange);
		byte btop = (byte)((b00 * negx + b01 * x) / xrange);

		byte abottom = (byte)((a10 * negx + a11 * x) / xrange);
		byte rbottom = (byte)((r10 * negx + r11 * x) / xrange);
		byte gbottom = (byte)((g10 * negx + g11 * x) / xrange);
		byte bbottom = (byte)((b10 * negx + b11 * x) / xrange);

		for (int y = 0; y < yrange; y++)
		{
			int negy = yrange - y;
			int p = (y + y1) * w + x + x1;

			int color =
				(byte)((atop * negy + abottom * y) / yrange) << 24 |
				(byte)((rtop * negy + rbottom * y) / yrange) << 16 |
				(byte)((gtop * negy + gbottom * y) / yrange) << 8 |
				(byte)((btop * negy + bbottom * y) / yrange);

			pixels[p] = color;
		}
	}
}

PicoGA: Evolving a 3D Projection Matrix

In my previous blog post PicoGA  – a tiny GA for C# I talk about PicoGA and I demonstrate a few problems that can be solved using it. One of the examples is evolving a 3D Projection Matrix from a few pixel to world coordinate mappings taken from a given photograph.

The example didn’t use “height” in the picture because it contained no heights that I could be sure of. In this example, I’m using the heights of the goal posts in the picture below to show that it  can correctly handle World (x,y,z) to Screen(x,y) mapping.

From this picture I’ve identified 9 points that I’ve determined the World(x,y,z) coordinates (the football field is 105×68 meters, the goal is 2.44 meters high and 7.32 meters wide). The coordinates are specified below the picture.

new-pitch-with numbers

I simply use MS Paint (yay!) to determine the pixel coordinate, and I use the given dimensions of the football field (105×68) to determine the world coordinates;

    new WorldToScreenCase(688, 349, 0,0 ,0), // 1
    new WorldToScreenCase(454, 155, 52.5,0,0), // 2
    new WorldToScreenCase(353, 70,  105,0,0), // 3
    new WorldToScreenCase(127, 169, 52.5, 34,0), // 4
    new WorldToScreenCase(155, 411, 0, 34,0), // 5
    new WorldToScreenCase(220, 364, 0, 34 - 7.32*0.5, 2.44), // 6
    new WorldToScreenCase(80,  378, 0, 34+7.32*0.5,2.44), // 7
    new WorldToScreenCase(92,  57,  105, 34 + 7.32*0.5, 2.44), // 8
    new WorldToScreenCase(145, 56,  105, 34-7.32*0.5,2.44), // 9

It takes a while, here’s a video of evolution in action;

but the program is successful in evolving an appropriate matrix;

image

As you can see, the evolved matrix (the red dots) hits the measured points (blue dots) with quite high accuracy. This matrix can now be used for animating something moving across the football field!

As I stated in my previous post, there are analytical methods for doing this, but I’ve been unable to locate any, and this is a cool use for GA…

This is the code that I use to map from world coordinates to screen coordinates, using my evolved matrix;

private static Point3D ProjectCase(Point3D point, List&lt;double&gt; l)
{
	Point3D p = new Point3D(
		point.X - l[12] * 100,
		point.Y - l[13] * 100,
		point.Z - l[14] * 100);

	Point3D result = new Point3D(
		l[0] * p.X + l[1] * p.Y + l[2] * p.Z + l[3],
		l[4] * p.X + l[5] * p.Y + l[6] * p.Z + l[7],
		l[8] * p.X + l[9] * p.Y + l[10] * p.Z + l[11]);

	if (result.Z != 0)
	{
		result.X *= l[15] / result.Z;
		result.Y *= l[15] / result.Z;
	}

	return result;
}

 

You can find the source code among the PicoGA demos.

SQL Server Profiler and LINQ to SQL

If you’re using LINQ to SQL, you’re eventually likely to be required to trace SQL statements from the SQL Server Profiler. Either for performance purposes or for debugging purposes. When you do, you’ll find that you’re in a world of hurt!

Below, I’ll demonstrate what I’m using and which may not be perfect, but it’s a huge step in the right direction!

A world of hurt

So, you want to trace a SQL statement back to the LINQ code that generated it? You’re really in it now! The reason is that when you execute LINQ to SQL statements, they can generate any number of SQL statements and it’s near impossible to trace them back to the individual statements that generated them. Especially on a busy production server where lots and lots of stuff is happening in the profiler

Even worse, you’ll typically have DAL (Data Access Layer) methods that are similar to how you’d use stored procedures previously, that execute a number of LINQ to SQL statements. So the offending SQL might be a tiny part of a large set of SQL statements originating from a particular DAL method.

What I’d like

I’d love to have a method for tracing every single LINQ to SQL statement from SQL Server Profiler back to my code in Visual Studio. But I haven’t been able to find a method for doing this.

What I’ve got out of the box

Basically, I have a long long long list of SQL statements in a log that I have no chance of matching up with the actual code.

What I’ve come up with

I’ve been able to add a line in the SQL Server Profiler logs as each DAL method starts and when it ends – that way I’m able to determine which DAL method a particular SQL Statements belongs to.

The naive way

Now, this can be done relatively straight forward, we’ll simply add a comment at the start and at the end of the DAL method – but this makes our code messy and hard to read. Especially if we get fancy and do a try/catch to make our code safer.

This is what the code looks to start out with (we’re using a data context factory);

        private static List<Employee> GetEmployees()
        {
            using (MyDataClassesDataContext context = 
new MyDataClassesDataContext())
            {
                return
                    context
                        .Employees
                        .Where(employee => employee.LastName.StartsWith("a"))
                        .ToList();
            }
        }

 

Adding some logging;

        private static List<Employee> GetEmployees()
        {
            using (MyDataClassesDataContext context = 
new MyDataClassesDataContext())
            {
                context.ExecuteCommand("BeginCall: GetEmployees");
                List<Employee> employees =
                    context
                        .Employees
                        .Where(employee => employee.LastName.StartsWith("a"))
                        .ToList();
                context.ExecuteCommand("EndCall: GetEmployees");
                return employees;
            }
        }

 

That

  • looks pretty bad
  • we had to refactor the code to use a temporary variable – which is ok if you need it but fairly pointless in this case
  • we must add the logging code to every DAL method!
  • the risks of missing one or a few are fairly large
  • we want to be safe against refactoring (the method changes name)
  • if the call fails with an exception, the exception will prevent the “EndCall” text from ever making it to the log.

What to do?

A cleverer way

Instead we add BeginCall every time the context is created and EndCall every time dispose is called, which allows us to structure our methods the same way as before and forces us to add the logging code. Also, instead of sending the actual name of the method, we use reflection and send MethodInfo – that makes it refactoring safe.

This is what the method call looks like – note that the logging part is invisible, the only change from the original method is the call to GetCurrentMethod

        private static List<Employee> GetEmployees()
        {
            using (MyDataClassesDataContext context =
new MyDataClassesDataContext(MethodBase.GetCurrentMethod()))
            {
                return
                    context
                        .Employees
                        .Where(employee => employee.LastName.StartsWith("a"))
                        .ToList();
            }
        }

 

Next we need to log stuff, MyDataClassesDataContext is a class that’s autogenerated by the DBML editor (as used with LINQ to SQL), but it’s partial so adding stuff is fairly straight forward.

The tricky part is that DataContext, which MyDataClassesDataContext  inherits from, implements Dispose and if we try to override the Dispose method of MyDataClassesDataContext, we’ll not be able to log anything, because that method is called once the context is allready disposed!

But not to worry, we simply re-implement Dispose and call the base method once we’ve logged what we need;

using System;
using System.Reflection;

namespace TraceLINQToSQL
{
    partial class MyDataClassesDataContext : IDisposable
    {
        private readonly string _methodName;

        public MyDataClassesDataContext(MethodBase methodBase)
            : this()
        {
            _methodName = methodBase.DeclaringType.FullName + "." + methodBase.Name;
            AddLogEntry("BeginCall Method={0}", _methodName);
        }

        public static bool LoggingEnabled { get; set; }

        public new void Dispose()
        {
            AddLogEntry("EndCall Method={0}", _methodName);
            base.Dispose();
        }

        private void AddLogEntry(string paramString, params object[] args)
        {
            if (LoggingEnabled)
            {
                ExecuteCommand("--" + string.Format(paramString, args));
            }
        }
    }
}

 

With this, every DAL method should start logging every call so that traceability is greatly improved. However, the cost is two extra roundtrips to the database per DAL method, so it might be a good idea to turn off logging for production. But in my tests, the overhead was negligible.

Going forward

From this, things could be greatly improved with a tool; a tool that groups all SQL statements from a single DAL call and sum up roundtrips/reads/writes/duration/CPU usage etc.

A method that counts how many times each DAL method is called. I haven’t had the time to create such a tool, but if you know of one, please let us know!

Reflection, Type.GetProperties and performance

In C#, you can use reflection to get a list of properties and fields for a type – which can be very useful when comparing objects for instance, or creating automated tests. However, if you’re repeatedly using GetProperties or GetFields, you should probably cache the results, because the call if fairly slow.

Caching can be done using a dictionary, for instance.

Performance Comparison

Running GetProperties 233.900 took 1351 ms (5.7 µs/call), which in turn means that we can call GetProperties 173.131 times per second. That might sound really fast, but using a cached version where we use a Dictionary<,> to perform the caching, 233.900 calls took 17 ms (0,07 µs /call), meaning that we can call our cached method 13.758.823 times per second!

The cached method is 80 times faster. If you use complex binding flags, the differences in the results are bound to be even greater! But hold on, the case isn’t that clear cut. Read on!

When to Optimize

Should you optimize? Well, that depends, because in this case, optimizing through caching;

  • reduces the flexibility of the code
  • increase the complexity of the code
  • increases the memory footprint (marginally)

80 times faster seems like a big difference, but if you’re going to use PropertyInfo.GetValue to access the properties that were returned, this optimization will be dwarfed by the performance implications of GetValue – see my post about get value.

Each GetProperties takes 5.7 µs – that’s the non-optimized version. Plenty fast. Say that that call returns 10 properties and you then proceed to call GetValue for each of those, that’ll be 10 calls at 7.4 µs a pop (computed from my link above), or 74 µs. So even if you optimize GetProperties to 70 ns, the optimized version runs at 74 µs instead of about 80 µs. Not a big save for the added complexity.

If you’re calling GetProperties a small, intermediate or even large number of times, don’t use this optimization!

If you call it a huge number of times, millions or billions of times throughout the execution of your application, you could consider using caching. Or, perhaps, you should rethink your design choices completely…

The Source Code

You’ll find my benchmark code below.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                const int iterations = 100;

                Console.WriteLine("Comparing Property Get Methods");
                long direct = TestGetPropertiesDirect(iterations);
                long cached = TestGetPropertiesCached(iterations);
                Console.WriteLine(
                    "Cached access is {0} times faster than reflection access",
                    1.0 * direct / cached);

                Console.WriteLine();
                Console.WriteLine();

                Console.WriteLine("Done, press r to go again!");
                if (Console.ReadKey().KeyChar != 'r')
                {
                    return;
                }
            }
        }

        private static long TestGetPropertiesDirect(int iterations)
        {
            Console.WriteLine("TestGetPropertiesDirect...");

            List<Type> types = GetTestTypes();
            Stopwatch sw = Stopwatch.StartNew();
            int gets = 0;
            for (int i = 0; i < iterations; i++)
            {
                foreach (Type type in types)
                {
                    List<PropertyInfo> properties = type.GetProperties().ToList();
                    gets++;
                }
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                gets,
                sw.ElapsedMilliseconds,
                gets / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static Dictionary<Type, List<PropertyInfo>> _propertyDictionary = 
            Dictionary<Type, List<PropertyInfo>>();

        private static long TestGetPropertiesCached(int iterations)
        {
            Console.WriteLine("TestGetPropertiesCached...");

            Stopwatch sw = Stopwatch.StartNew();
            List<Type> types = GetTestTypes();
            int gets=0;
            for (int i = 0; i < iterations; i++)
            {
                foreach (Type type in types)
                {
                    List<PropertyInfo> properties = GetCachedProperties(type);
                    gets++;
                }
            }

            Console.WriteLine(
                "  {0} gets took {1} ms, {2} iterations/s",
                gets,
                sw.ElapsedMilliseconds,
                gets / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static List<PropertyInfo> GetCachedProperties(Type type)
        {
            List<PropertyInfo> properties;
            if (_propertyDictionary.TryGetValue(type, out properties) == false)
            {
                properties = type.GetProperties().ToList();
                _propertyDictionary.Add(type, properties);
            }

            return properties;
        }

        // Returns a list of types to use for the subsequent tests
        private static List<Type> GetTestTypes()
        {
            return
                typeof(string)
                    .Assembly
                    .GetTypes()
                    .ToList();
        }
    }
}

Setting data through reflection; SetValue

In C#/.NET, you can set data from an object using reflection which can be useful for things like mapping and testing. Typically, you’d use direct access, but reflection allows you to set data to fields and properties that you were un-aware of at compile time. This can save you a lot of code. But what are the performance implications?

Turns out that setting data directly from a property or field is 60-150 times faster than using the “SetValue” method.

Also note that the “Reflection Field Accessor” is way faster (6.3 times) than the “Reflection Property Accessor”! So accessing fields through reflection is much faster than accessing properties through reflection, but both are much slower than direct access.

Test type Time Iterations/s Difference
Direct Property Accessor 34 ms 147.058.823 154 times faster than reflection
Reflection Property Accessor 5250 ms 952.380  
Direct Field Accessor 19 ms 263.157.894 59 times faster than reflection
Reflection Field Accessor 1243 ms 4.022.526  

Comparing Property Setter Methods
Direct Property Accessor…
  5000000 iterations took 34 ms, 147058823,529412 iterations/s
Reflection Property Accessor…
  5000000 iterations took 5250 ms, 952380,952380952 iterations/s
Direct access is 154 times faster than reflection access

Comparing Field Setter Methods
Direct Field Accessor…
  5000000 iterations took 19 ms, 263157894,736842 iterations/s
Reflection Field Accessor…
  5000000 iterations took 1243 ms, 4022526,14641995 iterations/s
Direct access is 65 times faster than reflection access
Done, press r to go again!

Here’s the actual code for performing these tests;

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                const int iterations = 5000000;

                Console.WriteLine("Comparing Property Setter Methods");
                long direct = TestDirectPropertyAccess(iterations);
                long reflection = TestReflectionPropertyAccess(iterations);
                Console.WriteLine(
                    "Direct access is {0} times faster than reflection access",
                    reflection / direct);

                Console.WriteLine();
                Console.WriteLine();

                Console.WriteLine("Comparing Field Setter Methods");
                direct = TestDirectFieldAccess(iterations);
                reflection = TestReflectionFieldAccess(iterations);
                Console.WriteLine(
                    "Direct access is {0} times faster than reflection access",
                    reflection / direct);

                Console.WriteLine("Done, press r to go again!");
                if (Console.ReadKey().KeyChar != 'r')
                {
                    return;
                }
            }
        }

        private static long TestDirectPropertyAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            Console.WriteLine("Direct Property Accessor...");

            Stopwatch sw = Stopwatch.StartNew();
            List<string> list = new List<string>();
            for (int i = 0; i < iterations; i++)
            {
                cwp.MyProperty = list;
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static long TestReflectionPropertyAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            PropertyInfo propertyInfo =
                typeof(ClassWithProperty)
                    .GetProperties()
                    .Single(prop => prop.Name == "MyProperty");

            object[] emptyArray = new object[0];

            Console.WriteLine("Reflection Property Accessor...");
            Stopwatch sw = Stopwatch.StartNew();
            List<string> list = new List<string>();
            for (int i = 0; i < iterations; i++)
            {
                propertyInfo.SetValue(cwp, list, emptyArray);
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static long TestDirectFieldAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            Console.WriteLine("Direct Field Accessor...");

            Stopwatch sw = Stopwatch.StartNew();
            List<string> list = new List<string>();
            for (int i = 0; i < iterations; i++)
            {
                cwp.MyField = list;
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static long TestReflectionFieldAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            FieldInfo fieldInfo =
                typeof(ClassWithProperty)
                    .GetFields()
                    .Single(prop => prop.Name == "MyField");

            object[] emptyArray = new object[0];

            Console.WriteLine("Reflection Field Accessor...");

            Stopwatch sw = Stopwatch.StartNew();
            List<string> list = new List<string>();
            for (int i = 0; i < iterations; i++)
            {
                fieldInfo.SetValue(cwp, list);
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }
    }

    public class ClassWithProperty
    {
        public ClassWithProperty()
        {
            MyProperty = new List<string>();
            MyField = new List<string>();
        }

        // I know, totally wrong naming and everything...
        public List<string> MyField;
        public List<string> MyProperty { get; set; }
    }
}

Getting data through reflection; GetValue

In C#/.NET, you can get data from an object using reflection which can be useful for things like mapping and testing. Typically, you’d use direct access, but reflection allows you to get data from fields and properties that you were un-aware of at compile time. This can save you a lot of code. But what are the performance implications?

Turns out that getting data directly from a property or field is 50-80 times faster than using the “GetValue” method. The code to create and access the delegate looks like this;

            ClassWithProperty cwp = new ClassWithProperty();

            PropertyInfo propertyInfo =
                typeof(ClassWithProperty)
                    .GetProperties()
                    .Single(prop => prop.Name == "MyProperty");

            MethodInfo method = propertyInfo.GetAccessors().First();

            Func<ClassWithProperty, List<string>> dlg =
                (Func<ClassWithProperty, List<string>>)
                Delegate.CreateDelegate(typeof(Func<ClassWithProperty, List<string>>), method);

            List<string> list = dlg(cwp);

For properties, you can also create a delegate that calls the setter method directly, which is plenty fast. This method can’t be used for fields though.

Note that the “Reflection Field Accessor” is way faster (6.3 times) than the “Reflection Property Accessor”! So accessing fields through reflection is much faster than accessing properties through reflection, but both are much slower than direct access.

Test type Time Iterations/s Time/iter Difference
Direct Property Accessor 36 ms 138.888.888 7,2 ns Baseline
Delegate Property Accessor 42 ms 119.047.619 8,4 ns 1.16 times slower
Reflection Property Accessor 6098 ms 819.940 1219 ns 107 times slower
Direct Field Accessor 15 ms 333.333.333 3 ns Baseline
Reflection Field Accessor 953 ms 5.246.589 190 ns 59 times slower

 

Here’s the actual code for performing these tests;

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                const int iterations = 5000000;

                Console.WriteLine("Comparing Property Get Methods");
                long direct = TestDirectPropertyAccess(iterations);
                long delegatea = TestDelegatePropertyAccess(iterations);
                long reflection = TestReflectionPropertyAccess(iterations);
                Console.WriteLine(
                    "Direct access is {0} times faster than reflection access",
                    1.0 * reflection / direct);
                Console.WriteLine(
                    "Direct access is {0} times faster than delegate access",
                    1.0 * delegatea / direct);

                Console.WriteLine();
                Console.WriteLine();

                Console.WriteLine("Comparing Field Access Methods");
                direct = TestDirectFieldAccess(iterations);
                reflection = TestReflectionFieldAccess(iterations);
                Console.WriteLine(
                    "Direct access is {0} times faster than reflection access",
                    reflection / direct);

                Console.WriteLine("Done, press r to go again!");
                if (Console.ReadKey().KeyChar != 'r')
                {
                    return;
                }
            }
        }

        private static long TestDirectPropertyAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            Console.WriteLine("Direct Property Accessor...");

            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++)
            {
                List<string> list = cwp.MyProperty;
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static long TestDelegatePropertyAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();

            PropertyInfo propertyInfo =
                typeof(ClassWithProperty)
                    .GetProperties()
                    .Single(prop => prop.Name == "MyProperty");

            MethodInfo method = propertyInfo.GetAccessors().First();

            Func<ClassWithProperty, List<string>> dlg =
                (Func<ClassWithProperty, List<string>>)
                Delegate.CreateDelegate(typeof(Func<ClassWithProperty, List<string>>), method);


            Console.WriteLine("Reflection Property Accessor...");
            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++)
            {
                List<string> list = dlg(cwp);
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static long TestReflectionPropertyAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            PropertyInfo propertyInfo =
                typeof(ClassWithProperty)
                    .GetProperties()
                    .Single(prop => prop.Name == "MyProperty");

            object[] emptyArray = new object[0];

            Console.WriteLine("Reflection Property Accessor...");

            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++)
            {
                List<string> list = (List<string>)propertyInfo.GetValue(cwp, emptyArray);
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static long TestDirectFieldAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            Console.WriteLine("Direct Field Accessor...");

            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++)
            {
                List<string> list = cwp.MyField;
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static long TestReflectionFieldAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            FieldInfo fieldInfo =
                typeof(ClassWithProperty)
                    .GetFields()
                    .Single(prop => prop.Name == "MyField");

            Console.WriteLine("Reflection Field Accessor...");

            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++)
            {
                List<string> list = (List<string>)fieldInfo.GetValue(cwp);
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }
    }

    public class ClassWithProperty
    {
        public ClassWithProperty()
        {
            MyProperty = new List<string>();
            MyField = new List<string>();
        }

        // I know, totally wrong naming and everything...
        public List<string> MyField;
        public List<string> MyProperty { get; set; }
    }
}

LINQ to SQL Performance Optimization, Re-Revisited

What Came Before

In my seemingly endless series of LINQ to SQL Performance Optimization series (see this initial post and this revisiting post), I’ve demonstrated some methods for improving LINQ to SQL loading times for hierarchies of data. This is the data we want to load;

And we want to do a deep load, we want the entire hierarchy. If you need really fast performance, you might need to do a bit of hand tweaking. This article series demonstrates some of the tweaks you might want to try.

The Fastest Method

My fastest method is fairly cumbersome and requires a lot of maintenance.

Basically, you must;

  • Create a stored procedure that retrieves all the data your require (I’ve somewhat automated the creation of the stored procedure, but you would still need to execute this by hand as a manual development step, which is bad)
  • A method for executing the query and loading all the data it delivers
  • A method that assembles the data from the bullet above to the appropriate hierarchy

If you add a new field/change an existing field for an object, you need to update the dbml file and the stored procedure, as typically with LINQ to SQL, you’d only need to update the dbml file.

There is another option, that isn’t as fast as my fastest method, but the maintenance requirements are lower and it’s easier to keep up to date. This is probably a better method than the Full Load version in the vast majority of cases.

The Last (But One) Method

So, tell me tell me, what is this clever new method? Well, we can load the required data using only LINQ instead of using a stored procedure. This means that we can do away with maintaining the stored procedure. The downside is that it generates more queries and is slightly slower.

Basically, you must;

  • Create a method that loads all the datasets you need to assemble your hierarchy – this is written in LINQ as opposed to the Stored Procedure in the previous example
  • A method that assembles the data from the bullet above to the appropriate hierarchy

The reason I call this the last but one method is because we can either run the LINQ queries as deferred execution or immediate execution, and the difference is significant. Which one is faster will depend on your particular situation, but in my case, deferred execution was considerably faster. I’m guessing it will most often be faster.

Deferred execution looks like this in C# (this only retrieves part of the hierarchy, suppliers=>products=>order_details);

            IEnumerable<Supplier> suppliers = context.Suppliers;
            IEnumerable<Product> products =
                context
                    .Products
                    .Join(
                    suppliers,
                    product => product.SupplierID,
                    supplier => supplier.SupplierID,
                    (product, supplier) => product)
                    .Distinct();

            IEnumerable<Order_Detail> orderDetails =
                context
                    .Order_Details
                    .Join(
                    products,
                    detail => detail.ProductID,
                    product => product.ProductID,
                    (child, parent) => child)
                    .Distinct();

And the generated SQL looks like this;

SELECT [t0].[SupplierID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax], [t0].[HomePage]
FROM [dbo].[Suppliers] AS [t0]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1

SELECT DISTINCT [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID], [t0].[CategoryID], [t0].[QuantityPerUnit], [t0].[UnitPrice], [t0].[UnitsInStock], [t0].[UnitsOnOrder], [t0].[ReorderLevel], [t0].[Discontinued]
FROM [dbo].[Products] AS [t0]
INNER JOIN [dbo].[Suppliers] AS [t1] ON [t0].[SupplierID] = ([t1].[SupplierID])
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1

SELECT DISTINCT [t0].[OrderID], [t0].[ProductID], [t0].[UnitPrice], [t0].[Quantity], [t0].[Discount]
FROM [dbo].[Order Details] AS [t0]
INNER JOIN (
    SELECT DISTINCT [t1].[ProductID], [t1].[ProductName], [t1].[SupplierID], [t1].[CategoryID], [t1].[QuantityPerUnit], [t1].[UnitPrice], [t1].[UnitsInStock], [t1].[UnitsOnOrder], [t1].[ReorderLevel], [t1].[Discontinued]
    FROM [dbo].[Products] AS [t1]
    INNER JOIN [dbo].[Suppliers] AS [t2] ON [t1].[SupplierID] = ([t2].[SupplierID])
    ) AS [t3] ON [t0].[ProductID] = [t3].[ProductID]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1

Note how we’re not using any data that’s been returned from the SQL Server, LINQ is building more and more complex SQL statements. But these are nicely optimized by the SQL Server, so we don’t need to worry too much about them.

The immediately executed version looks like this (we execute the query by calling ".ToList()”, which is the immediate part);

            List<Supplier> suppliers = context.Suppliers.ToList();
            List<Product> products =
                context
                    .Products
                    .Where(product => suppliers.Select(supplier => (int?)supplier.SupplierID).Contains(product.SupplierID))
                    .Distinct()
                    .ToList();

            List<Order_Detail> orderDetails =
                context
                    .Order_Details
                    .Where(detail => products.Select(product => product.ProductID).Contains(detail.ProductID))
                    .Distinct()
                    .ToList();

This generates these SQL statements;

SELECT [t0].[SupplierID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax], [t0].[HomePage]
FROM [dbo].[Suppliers] AS [t0]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1

SELECT DISTINCT [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID], [t0].[CategoryID], [t0].[QuantityPerUnit], [t0].[UnitPrice], [t0].[UnitsInStock], [t0].[UnitsOnOrder], [t0].[ReorderLevel], [t0].[Discontinued]
FROM [dbo].[Products] AS [t0]
WHERE [t0].[SupplierID] IN (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13, @p14, @p15, @p16, @p17, @p18, @p19, @p20, @p21, @p22, @p23, @p24, @p25, @p26, @p27, @p28)
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
-- @p1: Input Int (Size = 0; Prec = 0; Scale = 0) [2]
-- @p2: Input Int (Size = 0; Prec = 0; Scale = 0) [3]
-- @p3: Input Int (Size = 0; Prec = 0; Scale = 0) [4]
-- @p4: Input Int (Size = 0; Prec = 0; Scale = 0) [5]
-- @p5: Input Int (Size = 0; Prec = 0; Scale = 0) [6]
-- @p6: Input Int (Size = 0; Prec = 0; Scale = 0) [7]
...snip...
-- @p23: Input Int (Size = 0; Prec = 0; Scale = 0) [24]
-- @p24: Input Int (Size = 0; Prec = 0; Scale = 0) [25]
-- @p25: Input Int (Size = 0; Prec = 0; Scale = 0) [26]
-- @p26: Input Int (Size = 0; Prec = 0; Scale = 0) [27]
-- @p27: Input Int (Size = 0; Prec = 0; Scale = 0) [28]
-- @p28: Input Int (Size = 0; Prec = 0; Scale = 0) [29]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1

SELECT DISTINCT [t0].[OrderID], [t0].[ProductID], [t0].[UnitPrice], [t0].[Quantity], [t0].[Discount]
FROM [dbo].[Order Details] AS [t0]
WHERE [t0].[ProductID] IN (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13, @p14, @p15, @p16, @p17, @p18, @p19, @p20, @p21, @p22, @p23, @p24, @p25, @p26, @p27, @p28, @p29, @p30, @p31, @p32, @p33, @p34, @p35, @p36, @p37, @p38, @p39, @p40, @p41, @p42, @p43, @p44, @p45, @p46, @p47, @p48, @p49, @p50, @p51, @p52, @p53, @p54, @p55, @p56, @p57, @p58, @p59, @p60, @p61, @p62, @p63, @p64, @p65, @p66, @p67, @p68, @p69, @p70, @p71, @p72, @p73, @p74, @p75, @p76)
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
-- @p1: Input Int (Size = 0; Prec = 0; Scale = 0) [2]
-- @p2: Input Int (Size = 0; Prec = 0; Scale = 0) [3]
-- @p3: Input Int (Size = 0; Prec = 0; Scale = 0) [4]
-- @p4: Input Int (Size = 0; Prec = 0; Scale = 0) [5]
-- @p5: Input Int (Size = 0; Prec = 0; Scale = 0) [6]
-- @p6: Input Int (Size = 0; Prec = 0; Scale = 0) [7]
...snip...
-- @p65: Input Int (Size = 0; Prec = 0; Scale = 0) [71]
-- @p66: Input Int (Size = 0; Prec = 0; Scale = 0) [72]
-- @p67: Input Int (Size = 0; Prec = 0; Scale = 0) [73]
-- @p68: Input Int (Size = 0; Prec = 0; Scale = 0) [74]
-- @p69: Input Int (Size = 0; Prec = 0; Scale = 0) [75]
-- @p70: Input Int (Size = 0; Prec = 0; Scale = 0) [76]
-- @p71: Input Int (Size = 0; Prec = 0; Scale = 0) [77]
-- @p72: Input Int (Size = 0; Prec = 0; Scale = 0) [82]
-- @p73: Input Int (Size = 0; Prec = 0; Scale = 0) [83]
-- @p74: Input Int (Size = 0; Prec = 0; Scale = 0) [84]
-- @p75: Input Int (Size = 0; Prec = 0; Scale = 0) [85]
-- @p76: Input Int (Size = 0; Prec = 0; Scale = 0) [86]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1

The Last (But One) Method : Deferred Execution

Deferred execution takes about 0.191 seconds, it produces 7 queries in and the actual query code comes out to 6kb of data. This is only slightly slower than the Full Load version, but I’m certain that there are examples were the Full Load version will be considerably faster. You’ll find my full code for this method below.

The Last Method : Immediate Execution

Immediate execution takes about 0.4 seconds, it also produces 7 queries – though since each query gets a very large list of parameters, the amount of data is much larger, it comes out to 261kb of data. That’s 43 times more data!! You’ll find my full code for this method below.

Leader Board

Here’s the leader board as it currently stands;

Method Execution time Queries Query text
Full Load 0.137s 1 query 1kb (actually 195 bytes)
LINQ Full Load – Deferred Execution 0.191s 7 queries 6kb
LINQ Full Load – Immediate Execution 0.4s 7 queries 261kb
Smarter Load Options 0.45s 78 queries 114kb
Default/Lazy Load 1.4s 1031 queries 448kb
Naive Load Options 2.4s 31 queries 77kb

The code – and there’s a lot of it!

Assemble the hierarchy;

public partial class NorthwindDataContext
    {
        public static List<Supplier> AssembleHierarchy(List<Supplier> suppliers, List<Product> products, List<Category> categories, List<Order_Detail> orderDetails, List<Order> orders, List<Shipper> shippers, List<Customer> customers)
        {
            suppliers.ForEach(
                supplier => supplier.Products.SetSource(products.Where(product => product.SupplierID == supplier.SupplierID)));

            products.ForEach(
                product =>
                {
                    product.Category = categories.Single(category => category.CategoryID == product.CategoryID);
                    product.Order_Details.SetSource(
                        orderDetails.Where(detail => detail.ProductID == product.ProductID));
                });

            orderDetails.ForEach(
                detail =>
                {
                    detail.Order = orders.Single(order => order.OrderID == detail.OrderID);
                });

            orders.ForEach(
                order =>
                {
                    order.Shipper = shippers.Single(shipper => shipper.ShipperID == order.ShipVia);
                    order.Customer = customers.Single(customer => customer.CustomerID == order.CustomerID);
                });

            return suppliers;
        }
}

Deferred data fetch and hierarchy assembly;

        private static List<Supplier> ExecuteSimpleFullFetchDeferredExecution(NorthwindDataContext context)
        {
            IEnumerable<Supplier> suppliers = context.Suppliers;
            IEnumerable<Product> products =
                context
                    .Products
                    .Join(
                    suppliers,
                    product => product.SupplierID,
                    supplier => supplier.SupplierID,
                    (product, supplier) => product)
                    .Distinct();

            IEnumerable<Category> categories =
                context
                    .Categories
                    .Join(
                    products,
                    category => category.CategoryID,
                    product => product.CategoryID,
                    (category, product) => category)
                    .Distinct();

            IEnumerable<Order_Detail> orderDetails =
                context
                    .Order_Details
                    .Join(
                    products,
                    detail => detail.ProductID,
                    product => product.ProductID,
                    (child, parent) => child)
                    .Distinct();

            IEnumerable<Order> orders =
                context
                    .Orders
                    .Join(
                    orderDetails,
                    detail => detail.OrderID,
                    order => order.OrderID,
                    (child, parent) => child)
                    .Distinct();

            IEnumerable<Shipper> shippers =
                context
                    .Shippers
                    .Join(
                    orders,
                    shipper => shipper.ShipperID,
                    order => order.ShipVia,
                    (child, parent) => child)
                    .Distinct();

            IEnumerable<Customer> customers =
                context
                    .Customers
                    .Join(
                    orders,
                    customer => customer.CustomerID,
                    order => order.CustomerID,
                    (child, parent) => child)
                    .Distinct();

            return
                NorthwindDataContext.AssembleHierarchy(
                    suppliers.ToList(),
                    products.ToList(),
                    categories.ToList(),
                    orderDetails.ToList(),
                    orders.ToList(),
                    shippers.ToList(),
                    customers.ToList());
        }

Immediate data fetch and hierarchy assembly;

        private static List<Supplier> ExecuteSimpleFullFetchImmediateExecution(NorthwindDataContext context)
        {
            List<Supplier> suppliers = context.Suppliers.ToList();
            List<Product> products =
                context
                    .Products
                    .Where(product => suppliers.Select(supplier => (int?)supplier.SupplierID).Contains(product.SupplierID))
                    .Distinct()
                    .ToList();

            List<Category> categories =
                context
                    .Categories
                    .Where(category => products.Select(product => product.CategoryID).Contains(category.CategoryID))
                    .Distinct()
                    .ToList();

            List<Order_Detail> orderDetails =
                context
                    .Order_Details
                    .Where(detail => products.Select(product => product.ProductID).Contains(detail.ProductID))
                    .Distinct()
                    .ToList();

            List<Order> orders =
                context
                    .Orders
                    .Where(order => orderDetails.Select(detail => detail.OrderID).Contains(order.OrderID))
                    .Distinct()
                    .ToList();

            List<Shipper> shippers =
                context
                    .Shippers
                    .Where(shipper => orders.Select(order => order.ShipVia).Contains(shipper.ShipperID))
                    .Distinct()
                    .ToList();

            List<Customer> customers =
                context
                    .Customers
                    .Where(customer => orders.Select(order => order.CustomerID).Contains(customer.CustomerID))
                    .Distinct()
                    .ToList();

            return
                NorthwindDataContext.AssembleHierarchy(
                    suppliers,
                    products,
                    categories,
                    orderDetails,
                    orders,
                    shippers,
                    customers);
        }

All of a sudden, my Silverlight Application stops working

It’s happened several times, all of a sudden, my Silverlight Business Application stops working! When I hit F5, Mozilla comes up as usual – but the page is blank and there’s no error message – and no application. Sucks.

It happened just now as I re-checked out the entire project from the SVN source. Turns out that to fix it, all I have to do is select a startup page other than the Default.aspx page.

For some unknown reason, Default.aspx has become the startup page for my web application, and for some other unknown reason, that just isn’t good enough.

To correct the issue, I select another startup page (ImageEvolvatron.GuiTestPage.aspx) in the Solution Explorer, right click and select “Set As Start Page” and my application is good to go again.

Pointless, but one of those things you’ve got to learn, I guess;

If your Silverlight web application stops responding, try changing the “Start Page”

For(int i= , using extension´s

I’m a big fan of using ForEach and lambda instead of the regular kind of foreach;

// Old regular type
foreach(Bob bob in Bobs)
{
    Console.WriteLine(bob);
}

// Nicer extension lambda style
Bobs.ForEach(bob => Console.WriteLine(bob));

Not everyone agrees that the second version is clearer, but this is my blog, they can start their own blogs… 😉

But what about when you also need an index for the list, as I sometimes do? Then we’re stuck with the ancient “for(int i=” version, right? But no, Extensions to the rescue;

public static class Extensions
{
    public static void ForEachNumbered<T>(
        this IList<T> list, 
        Action<T, int> action)
    {
        for (int i = 0; i < list.Count; i++)
        {
            action(list[i], i);
        }
    }
}

Now I can write code link this instead (I’m not so convinced that anyone will ever find this clearer)

// Old regular type
for(int i=0;i<Bobs.Count;i++)
{
    Bob bob=Bobs[i];
    Console.WriteLine(string.Format("{0}: {1}", i, bob));
}

// Nicer extension lambda style
Bobs.ForEachNumbered((bob,i) => string.Format("{0}: {1}", i, bob));

 

Glorious!!?