3

I'm trying to sort a pair of int arrays (int[] a; int[] b;)

If I use Array.Sort(a,b) then the performance is great.

However, I'd prefer to use a List<> and load the int pairs in a struct. I can get this to work using Array.Sort() with an overload that provides a simple comparer for the struct but it's about 4 times slower than the Array.Sort(a,b)

Is that normal?

rob
  • 210
  • 3
  • 8
  • (note edited answer to add metrics for IComparable) – Marc Gravell May 30 '09 at 23:06
  • I suppose both List.Sort and Array.Sort use quicksort which means that the reason of the performance difference must be the way the elements are accessed. – Tamas Czinege May 30 '09 at 23:29
  • Yep. Both use quicksort. List.Sort is a fraction slower than for a regular array, but nothing like the difference for sorting pairs of values. – rob May 31 '09 at 16:01

1 Answers1

9

That sounds realistic; you are introducing more complexity (more lookups etc, more virtual methods, more range checks), so it can only get slower (array access is very direct and very quick).

It looks like you can get it a bit quicker (but not as quick as the array) by implementing an IComparer<T> instead of the delegate approach; (edit) and faster again using IComparable<T>:

Array.Sort: 2241ms
List.Sort (delegate): 8714ms
List.Sort (interface): 6976ms
List.Sort (comparable): 5456ms

With code:

using System;
using System.Collections.Generic;
using System.Diagnostics;

struct MyStruct : IComparable<MyStruct>
{
    private readonly int key, value;
    public int Key { get { return key; } }
    public int Value { get { return value; } }
    public MyStruct(int key, int value)
    {
        this.key = key;
        this.value = value;
    }
    public int CompareTo(MyStruct other)
    {
        return key.CompareTo(other.key);
    }
}
static class Program
{
    static void Main()
    {
        const int SIZE = 10000000;
        int[] a = new int[SIZE], b = new int[SIZE];
        Random rand = new Random();
        for(int i = 0 ; i < SIZE ; i++) {
            a[i] = rand.Next();
            b[i] = i;
        }
        var list = new List<MyStruct>(SIZE);
        for (int i = 0; i < SIZE; i++)
        {
            list.Add(new MyStruct(a[i], b[i]));
        }
        var list2 = new List<MyStruct>(list);
        var list3 = new List<MyStruct>(list);

        var watch = Stopwatch.StartNew();
        Array.Sort(a, b);
        watch.Stop();
        Console.WriteLine("Array.Sort: " + watch.ElapsedMilliseconds + "ms");

        watch = Stopwatch.StartNew();
        list.Sort((x, y) => x.Key.CompareTo(y.Key));
        watch.Stop();
        Console.WriteLine("List.Sort (delegate): " + watch.ElapsedMilliseconds + "ms");

        watch = Stopwatch.StartNew();
        list2.Sort(MyComparer.Default);
        watch.Stop();
        Console.WriteLine("List.Sort (interface): " + watch.ElapsedMilliseconds + "ms");

        watch = Stopwatch.StartNew();
        list3.Sort();
        watch.Stop();
        Console.WriteLine("List.Sort (comparable): " + watch.ElapsedMilliseconds + "ms");
    }
    sealed class MyComparer : IComparer<MyStruct>
    {
        private MyComparer() { }
        public static readonly MyComparer Default = new MyComparer();
        public int Compare(MyStruct x, MyStruct y)
        {
            return x.Key.CompareTo(y.Key);
        }
    }
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Cheers. Thanks Marc. Not sure I can live with the performance loss so will probably write something along the lines of a List class that can use an Array.Sort behind the scenes. – rob May 31 '09 at 15:58
  • The trick really is to use IComparable and not just derive from the non generic IComparable. This way the comparision does not have to determine the type from Object. – fmuecke Mar 12 '12 at 12:44
  • @fmuecke in many cases generics aren't very useful (especially anything involving data-binding or reflection); however, in the example here, it **is** using the generic API... – Marc Gravell Mar 12 '12 at 12:57