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 - --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---
