Until I know how you want to handle multi key dictionaries in your
code base I'm not going to submit a patch. Below is how I did it with
a class I usually use for that, more details at:
http://www.codeproject.com/KB/recipes/ClassKey.aspx

If anyone is interested, the LINQ expressions used in the LCG have a
checked/unchecked version which is why it didn't have the overflow
issue. As far as I can tell type converters don't have that feature
and using an unchecked block doesn't stop it from throwing an
exception. When the type converters are cached they are better than
the cached LCG unless I'm doing something wrong here.

Performance from worst to best, with the original test rows plus one
more for overflow condition:

10K iterations * 5 tests per iteration:

LCG compiled each time - 8043ms
TypeConverter with try/catch : 869ms
LCG cached: 143ms
TypeConverter without try/catch (exception on overflow): 82ms
Type converter with try/catch cached: 23ms

    class Program
    {
        static void Main(string[] args)
        {
            RunComparisons(CreateComparerLCG);
            RunComparisons(CreateComparerTypeConverter);
            RunComparisons(CreateComparerLCGCachedClass);
            RunComparisons(CreateComparerTypeConverterCached);
        }

        //Comparison types

        private static Func<object, object, bool>
CreateComparerTypeConverter(Type firstType, Type secondType)
        {
            if (firstType == secondType)
                return Equals;

            TypeConverter converterFirstType =
TypeDescriptor.GetConverter(firstType);
            TypeConverter converterSecondType =
TypeDescriptor.GetConverter(secondType);

            return delegate(object first, object second)
            {
                try
                {
                    return object.Equals(
                        first,
                        converterFirstType.ConvertTo(second,
firstType)
                    );
                }
                catch (OverflowException)
                {
                    return object.Equals(
                        second,
                        converterSecondType.ConvertTo(first,
secondType)
                    );
                }
            };
        }

        private static Func<object, object, bool> CreateComparerLCG
(Type firstType, Type secondType)
        {
            if (firstType == secondType)
                return Equals;

            var firstParameter = Expression.Parameter(typeof(object),
"first");
            var secondParameter = Expression.Parameter(typeof(object),
"second");

            var equalExpression = Expression.Equal(Expression.Convert
(firstParameter, firstType),
                Expression.Convert(Expression.Convert(secondParameter,
secondType), firstType));

            return Expression.Lambda<Func<object, object, bool>>
(equalExpression, firstParameter, secondParameter).Compile();
        }

        private class CacheKey : ClassKey<CacheKey>
        {
            public Type FirstType = null;
            public Type SecondType = null;

            public override object[] GetKeyValues()
            {
                return new object[] { FirstType, SecondType };
            }
        }

        private class ComparerCacheClass : Dictionary<CacheKey,
Func<object, object, bool>> { }
        private static readonly ComparerCacheClass comparerCacheClass
= new ComparerCacheClass();
        private static Func<object, object, bool>
CreateComparerLCGCachedClass(Type firstType, Type secondType)
        {
            if (firstType == secondType)
                return Equals;

            var firstParameter = Expression.Parameter(typeof(object),
"first");
            var secondParameter = Expression.Parameter(typeof(object),
"second");

            var equalExpression = Expression.Equal(Expression.Convert
(firstParameter, firstType),
                Expression.Convert(Expression.Convert(secondParameter,
secondType), firstType));

            Func<object, object, bool> compareExpression = null;
            CacheKey key = new CacheKey { FirstType = firstType,
SecondType = secondType };
            if (!comparerCacheClass.TryGetValue(key, out
compareExpression))
            {
                compareExpression = Expression.Lambda<Func<object,
object, bool>>(equalExpression, firstParameter,
secondParameter).Compile();
                comparerCacheClass.Add(key, compareExpression);
            }

            return compareExpression;
        }

        private static Func<object, object, bool>
CreateComparerTypeConverterCached(Type firstType, Type secondType)
        {
            if (firstType == secondType)
                return Equals;

            Func<object, object, bool> compareExpression = null;
            CacheKey key = new CacheKey { FirstType = firstType,
SecondType = secondType };
            if (!comparerCacheClass.TryGetValue(key, out
compareExpression))
            {
                TypeConverter converterFirstType =
TypeDescriptor.GetConverter(firstType);
                TypeConverter converterSecondType =
TypeDescriptor.GetConverter(secondType);

                compareExpression = delegate(object first, object
second)
                {
                    try
                    {
                        return object.Equals(
                            first,
                            converterFirstType.ConvertTo(second,
firstType)
                        );
                    }
                    catch (OverflowException)
                    {
                        return object.Equals(
                            second,
                            converterSecondType.ConvertTo(first,
secondType)
                        );
                    }
                };

                comparerCacheClass.Add(key, compareExpression);
            }

            return compareExpression;
        }

        private static void RunComparisons(Func<Type, Type,
Func<object, object, bool>> createComparer)
        {
            List<Comparison> comparisonsToMake = new List<Comparison>
{
                new Comparison { item = (byte)1, otherItem = (int)1 },
                new Comparison { item = (int)1, otherItem = (long)1 },
                new Comparison { item = (long)1, otherItem = (float)
1 },
                new Comparison { item = (float)1, otherItem = (double)
1 },
                new Comparison { item = (byte)byte.MaxValue, otherItem
= (int)int.MaxValue, expectedValue = false },
            };

            Program program = new Program();

            Stopwatch watch = new Stopwatch();
            watch.Start();
            for (int i = 0; i < 10000; i++)
            {
                foreach (var comparison in comparisonsToMake)
                {
                    if (program.Compare(comparison, createComparer) !=
comparison.expectedValue)
                        throw new ApplicationException("Comparison
didn't work");
                }
            };
            watch.Stop();
            Console.WriteLine("All comparisons took " +
watch.ElapsedMilliseconds + "ms");
        }

        private class Comparison
        {
            public object item;
            public object otherItem;
            public bool expectedValue = true;
        }

        private bool Compare(Comparison comparison, Func<Type, Type,
Func<object, object, bool>> createComparer)
        {
            object item = comparison.item;
            object otherItem = comparison.otherItem;

            if (item == null | otherItem == null)
                return item == null & otherItem == null;

            var equalityComparer = createComparer(item.GetType(),
otherItem.GetType());

            return equalityComparer(item, otherItem);
        }

    }


On Mar 7, 8:36 am, Simone Busoli <[email protected]> wrote:
> Sorry Paul, I'm not going to reply anymore. Write a test, write a patch and
> submit it if you think that using type converters is a better option.
>
>
>
> On Sat, Mar 7, 2009 at 15:32, webpaul <[email protected]> wrote:
>
> > Type converter is still faster than dictionary cached LCG (without
> > overflow problem solved) and I'm not even caching the type converter
> > yet... The LCG solution does handle the overflow condition gracefully
> > and I don't fully understand how it isn't overflowing, can you explain
> > how what you are doing doesn't fail when converting int.MaxValue to a
> > byte?
>
> > Also, how do you guys handle multi-key dictionaries typically? I have
> > a way but am curious how you usually do it.
>
> > On Mar 6, 10:02 am, Simone Busoli <[email protected]> wrote:
> > > Try caching the LCG comparer, you'll be surprised.
>
> > > On Fri, Mar 6, 2009 at 16:46, webpaul <[email protected]> wrote:
>
> > > > This one takes 1000ms vs 6500ms but it handles one of the types being
> > > > an overflow for the other. I couldn't find a way to detect if there
> > > > will be an overflow for a specific value without actually catching the
> > > > overflow exception which makes this take much longer. Any ideas?
>
> > > >    class Program
> > > >    {
> > > >        static void Main(string[] args)
> > > >        {
> > > >            RunComparisons(CreateComparerLCG);
> > > >             RunComparisons(CreateComparerTypeConverter);
> > > >         }
>
> > > >        //Comparison types
>
> > > >        private static Func<object, object, bool>
> > > > CreateComparerTypeConverter(Type firstType, Type secondType)
> > > >        {
> > > >            if (firstType == secondType)
> > > >                return Equals;
>
> > > >            TypeConverter converterFirstType =
> > > > TypeDescriptor.GetConverter(firstType);
> > > >            TypeConverter converterSecondType =
> > > > TypeDescriptor.GetConverter(secondType);
>
> > > >            return delegate(object first, object second)
> > > >            {
> > > >                 try
> > > >                {
> > > >                    return object.Equals(
> > > >                        first,
> > > >                        converterFirstType.ConvertTo(second,
> > > > firstType)
> > > >                    );
> > > >                }
> > > >                catch (OverflowException)
> > > >                {
> > > >                    return object.Equals(
> > > >                        second,
> > > >                        converterSecondType.ConvertTo(first,
> > > > secondType)
> > > >                    );
> > > >                 }
> > > >            };
> > > >        }
>
> > > >        private static Func<object, object, bool> CreateComparerLCG
> > > > (Type firstType, Type secondType)
> > > >        {
> > > >            if (firstType == secondType)
> > > >                return Equals;
>
> > > >            var firstParameter = Expression.Parameter(typeof(object),
> > > > "first");
> > > >            var secondParameter = Expression.Parameter(typeof(object),
> > > > "second");
>
> > > >            var equalExpression = Expression.Equal(Expression.Convert
> > > > (firstParameter, firstType),
> > > >                Expression.Convert(Expression.Convert(secondParameter,
> > > > secondType), firstType));
>
> > > >            return Expression.Lambda<Func<object, object, bool>>
> > > > (equalExpression, firstParameter, secondParameter).Compile();
> > > >        }
>
> > > >        private static void RunComparisons(Func<Type, Type,
> > > > Func<object, object, bool>> createComparer)
> > > >        {
> > > >            List<Comparison> comparisonsToMake = new List<Comparison>
> > > > {
> > > >                new Comparison { item = (byte)1, otherItem = (int)1 },
> > > >                new Comparison { item = (int)1, otherItem = (long)1 },
> > > >                new Comparison { item = (long)1, otherItem = (float)
> > > > 1 },
> > > >                new Comparison { item = (float)1, otherItem = (double)
> > > > 1 },
> > > >                 new Comparison { item = (byte)byte.MaxValue, otherItem
> > > > = (int)int.MaxValue, expectedValue = false },
> > > >             };
>
> > > >            Program program = new Program();
>
> > > >            Stopwatch watch = new Stopwatch();
> > > >            watch.Start();
> > > >            for (int i = 0; i < 10000; i++)
> > > >            {
> > > >                foreach (var comparison in comparisonsToMake)
> > > >                {
> > > >                     if (program.Compare(comparison, createComparer) !=
> > > > comparison.expectedValue)
> > > >                         throw new ApplicationException("Comparison
> > > > didn't work");
> > > >                }
> > > >            };
> > > >            watch.Stop();
> > > >            Console.WriteLine("All comparisons took " +
> > > > watch.ElapsedMilliseconds + "ms");
> > > >        }
>
> > > >        private class Comparison
> > > >        {
> > > >            public object item;
> > > >            public object otherItem;
> > > >             public bool expectedValue = true;
> > > >         }
>
> > > >        private bool Compare(Comparison comparison, Func<Type, Type,
> > > > Func<object, object, bool>> createComparer)
> > > >        {
> > > >            object item = comparison.item;
> > > >            object otherItem = comparison.otherItem;
>
> > > >            if (item == null | otherItem == null)
> > > >                return item == null & otherItem == null;
>
> > > >            var equalityComparer = createComparer(item.GetType(),
> > > > otherItem.GetType());
>
> > > >            return equalityComparer(item, otherItem);
> > > >        }
>
> > > >    }
>
> > > > On Mar 6, 9:23 am, webpaul <[email protected]> wrote:
> > > > > This does it in 93ms vs. 6500ms on my box, could be improved with a
> > > > > Dictionary as well. Also, it always converts to the first type so if
> > > > > you compare byte.MaxValue with int.MaxValue you get an exception. It
> > > > > should convert to the largest type so I'll see what I can do on that
> > > > > as well.
>
> > > > >         private static Func<object, object, bool>
> > > > > CreateComparerTypeConverter(Type firstType, Type secondType)
> > > > >         {
> > > > >             if (firstType == secondType)
> > > > >                 return Equals;
>
> > > > >             TypeConverter converterFirstType =
> > > > > TypeDescriptor.GetConverter(firstType);
> > > > >             TypeConverter converterSecondType =
> > > > > TypeDescriptor.GetConverter(firstType);
>
> > > > >             return delegate(object first, object second)
> > > > >             {
> > > > >                 return
> > > > >                     object.Equals(
> > > > >                         converterFirstType.ConvertTo(first,
> > > > > firstType),
> > > > >                         converterFirstType.ConvertTo(
> > > > >                             converterSecondType.ConvertTo(second,
> > > > > secondType),
> > > > >                             firstType
> > > > >                         )
> > > > >                     )
> > > > >                 ;
> > > > >             };
> > > > >         }
>
> > > > > On Mar 6, 9:10 am, webpaul <[email protected]> wrote:
>
> > > > > > It's pretty slow as is - takes 6.5 seconds for 10K iterations. I'm
> > > > > > going to try it a few different ways and see which is fastest.
>
> > > > > >     class Program
> > > > > >     {
> > > > > >         static void Main(string[] args)
> > > > > >         {
> > > > > >             RunComparisons(CreateComparerLCG);
> > > > > >         }
>
> > > > > >         //Comparison types
>
> > > > > >         private static Func<object, object, bool> CreateComparerLCG
> > > > > > (Type firstType, Type secondType)
> > > > > >         {
> > > > > >             if (firstType == secondType)
> > > > > >                 return Equals;
>
> > > > > >             var firstParameter =
> > Expression.Parameter(typeof(object),
> > > > > > "first");
> > > > > >             var secondParameter =
> > Expression.Parameter(typeof(object),
> > > > > > "second");
>
> > > > > >             var equalExpression =
> > Expression.Equal(Expression.Convert
> > > > > > (firstParameter, firstType),
>
> > Expression.Convert(Expression.Convert(secondParameter,
> > > > > > secondType), firstType));
>
> > > > > >             return Expression.Lambda<Func<object, object, bool>>
> > > > > > (equalExpression, firstParameter, secondParameter).Compile();
> > > > > >         }
>
> > > > > >         private static void RunComparisons(Func<Type, Type,
> > > > > > Func<object, object, bool>> createComparer)
> > > > > >         {
> > > > > >             List<Comparison> comparisonsToMake = new
> > List<Comparison>
> > > > > > {
> > > > > >                 new Comparison { item = (byte)1, otherItem = (int)1
> > },
> > > > > >                 new Comparison { item = (int)1, otherItem = (long)1
> > },
> > > > > >                 new Comparison { item = (long)1, otherItem =
> > (float)
> > > > > > 1 },
> > > > > >                 new Comparison { item = (float)1, otherItem =
> > (double)
> > > > > > 1 },
> > > > > >             };
>
> > > > > >             Program program = new Program();
>
> > > > > >             Stopwatch watch = new Stopwatch();
> > > > > >             watch.Start();
> > > > > >             for (int i = 0; i < 10000; i++)
> > > > > >             {
> > > > > >                 foreach (var comparison in comparisonsToMake)
> > > > > >                 {
> > > > > >                     if (!program.Compare(comparison,
> > createComparer))
> > > > > >                         throw new ApplicationException("Comparison
> > > > > > didn't work");
> > > > > >                 }
> > > > > >             };
> > > > > >             watch.Stop();
> > > > > >             Console.WriteLine("All comparisons took " +
> > > > > > watch.ElapsedMilliseconds + "ms");
> > > > > >         }
>
> > > > > >         private class Comparison
> > > > > >         {
> > > > > >             public object item;
>
> ...
>
> read more »- Hide quoted text -
>
> - Show quoted text -
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Rhino Tools Dev" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/rhino-tools-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to