Bookmark and Share

Optimizing the Fill Extension Method

The first thing you may have noticed about the fill extension method was the improper generic constraints. This was due to Array being extended rather than specific arrays. To correct this, it is necessary to set the original method to private and implement overloads for each multidimensional array. Here are the overloads for one and two dimensions.

public static void Fill<T>(this T[] array, Func<int[], T> func)
{
    Fill((Array)array, func);
}

public static void Fill<T>(this T[,] array, Func<int[], T> func)
{
    Fill((Array)array, func);
}

It was brought to my attention by Scott Briercheck at Precyse Solutions that this may cause performance issues in large multidimensional arrays. Indeed, my testing proved that it does. Consider the following example.

int[,] nums = new int[10000, 10000];
for (int x = 0; x < 10000; x++)
    for (int y = 0; y < 10000; y++)
        nums[x, y] = x + y;

On my computer, this takes nearly two seconds to execute. If I were to rewrite this as nums.Fill(i => i.Sum()), it would take nearly 27 seconds! That is a huge difference, but part of that is due to the LINQ to Objects call in the lambda statement. If that were changed to i[0] + i[1], it reduces the time to 17 seconds. Still, the performance is horrible for large arrays.

The differences between the extension method and the imperative way of writing the code are primarily related to the use of delegates, the known number of ranks, and the known number of elements. We just finished refactoring the code to correct a mistake in the generic constraints, and it gives us information about the known number of ranks. We can use this to optimize the extension methods. We can’t completely eliminate the functions, but since a single extension method for each multidimensional array ranking is included we can change the signature of the functions which may help.

public static void Fill<T>(this T[] array, Func<int, T> func)
{
    for (int a = array.GetLowerBound(0); a < array.GetUpperBound(0); a++)
    {
        array[a] = func(a);
    }
}

public static void Fill<T>(this T[,] array, Func<int, int, T> func)
{
    for (int a = array.GetLowerBound(0); a < array.GetUpperBound(0); a++)
    {
        for (int b = array.GetLowerBound(1); b < array.GetUpperBound(1); b++)
        {
            array[a, a] = func(a, b);
        }
    }
}

I can then test our original scenario using the new extension method.

int[,] nums = new int[10000, 10000];
nums.Fill((x, y) => x + y);

My timing on it comes in just over six seconds. That is a vast improvement over the original!

blog comments powered by Disqus

KodefuGuru.GetInfo()

Chris Eargle
LinkedIn Twitter Technorati Facebook

Chris Eargle
Telerik Developer Evangelist, C# MVP

JustCode

Telerik .NET Ninja

 

INETA Community Speakers Program

 

MVP - Visual C#

 

Friend of RedGate

World Map

Month List

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer’s view in any way.