Found a way to compare the type sizes. Using this method for previous
examples, it returns in 37ms which would seem to be faster than the
LCG. I wanted to rule out a larger startup time for LCG as opposed to
execution time, so for 1 million rows the cached LCG takes 13 seconds
and the type converter method below takes 3 seconds.

Separate caches of course. :)

        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);

                int firstTypeSize =
System.Runtime.InteropServices.Marshal.SizeOf(firstType);
                int secondTypeSize =
System.Runtime.InteropServices.Marshal.SizeOf(secondType);

                if (secondTypeSize >= firstTypeSize)
                {
                    compareExpression = delegate(object first, object
second)
                    {
                        return object.Equals(
                            second,
                            converterSecondType.ConvertTo(first,
secondType)
                        );
                    };

                }
                else
                {
                    compareExpression = delegate(object first, object
second)
                    {
                        return object.Equals(
                            first,
                            converterFirstType.ConvertTo(second,
firstType)
                        );
                    };
                }

                comparerCacheClass.Add(key, compareExpression);
            }

            return compareExpression;
        }


On Mar 7, 12:34 pm, webpaul <[email protected]> wrote:
> Doh! Nice catch.
>
> Looks like it is really 800ms, the try/catch is a killer because it's
> in the delegate so caching it doesn't give a big benefit. If there was
> an unchecked type converter or some way to tell which type was bigger
> so I could always cast to that then I suspect the type converter would
> be better. But as-is (including the overflow scenario) the cached LCG
> is the best.
>
> On Mar 7, 10:29 am, Simone Busoli <[email protected]> wrote:
>
>
>
> > Did you realize that you're using a shared cache, which is already filled
> > with delegates when you run the fourth step?
>
> > On Sat, Mar 7, 2009 at 16:45, webpaul <[email protected]> wrote:
>
> > > 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)
>
> > ...
>
> > read more »- Hide quoted text -
>
> > - Show quoted text -- 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