Author Topic: “Dynamic .NET” transactions vs intellisense , type safety, etc....  (Read 11258 times)

0 Members and 1 Guest are viewing this topic.

TheMaster

  • Guest
Re: “Dynamic .NET” transactions vs intellisense , type safety, etc....
« Reply #15 on: April 06, 2012, 08:27:45 AM »
Using the code from the posted link:

Code - C#: [Select]
  1.  
  2. namespace Namespace1
  3. {
  4.  
  5.   public static class PropertyAccessTest
  6.   {
  7.     public static void Run()
  8.     {
  9.       var nTimes = 2500000;
  10.       var value = "Hello World";
  11.       var myTestType = new MyObject();
  12.       DateTime start, end;
  13.      
  14.       var property = typeof(MyObject).GetProperty("MyProperty");
  15.      
  16.       Stopwatch sw = new Stopwatch();
  17.       sw.Start();
  18.       for (var i = 0; i < nTimes; i++)
  19.       {
  20.         property.SetValue(myTestType, value, null);
  21.       }
  22.       sw.Stop();
  23.       Console.WriteLine("Straight reflection: PropertyInfo.SetValue(): {0} ms", sw.ElapsedMilliseconds);
  24.      
  25.       dynamic myTestType2 = new MyObject();
  26.       sw.Reset();
  27.       sw.Start();
  28.       for (int i = 0; i < nTimes; i++)
  29.       {
  30.         myTestType2.MyProperty = value;
  31.       }
  32.       sw.Stop();
  33.       Console.WriteLine("System.Dynamic: dynamic.MyProperty = value: {0} ms", sw.ElapsedMilliseconds);
  34.      
  35.       var myObject3 = new MyObject();
  36.       sw.Reset();
  37.       sw.Start();
  38.       for (int i = 0; i < nTimes; i++)
  39.       {
  40.         myObject3.MyProperty = value;
  41.       }
  42.       sw.Stop();
  43.       Console.WriteLine("Direct Code: MyObject.MyProperty = value: {0} ms", sw.ElapsedMilliseconds);
  44.    
  45.       // Given an instance of an object, and a string
  46.       // identifying a property of the object:
  47.      
  48.       FastProperty fastProp = new FastProperty( myObject3, "MyProperty" );
  49.      
  50.       // It's important to note that the instance of fastProp
  51.       // is both type- and member-specific, so it must be cached
  52.       // and keyed to both the type of the instance, and the
  53.       // property name, using some kind of caching scheme (which
  54.       // is merely an implementation detail):
  55.      
  56.       sw.Reset();
  57.       sw.Start();
  58.       for (int i = 0; i < nTimes; i++)
  59.       {
  60.         fastProp.Set( myObject3, value );
  61.       }
  62.       sw.Stop();
  63.       Console.WriteLine("FastProperty: fastProp.Set(myObject3, value): {0} ms", sw.ElapsedMilliseconds);
  64.     }
  65.    
  66.     public class MyObject
  67.     {
  68.       public string MyProperty { get; set; }
  69.     }
  70.    
  71.   }
  72.  
  73.   public class FastProperty
  74.   {
  75.     public PropertyInfo Property { get; set; }
  76.  
  77.     public Func<object, object> GetDelegate;
  78.     public Action<object, object> SetDelegate;
  79.    
  80.     public FastProperty( object instance, string property )
  81.       : this( instance.GetType().GetProperty( property ) )
  82.     {
  83.     }
  84.  
  85.     public FastProperty(PropertyInfo property)
  86.     {
  87.       this.Property = property;
  88.       InitializeGet();
  89.       InitializeSet();
  90.     }
  91.  
  92.     private void InitializeSet()
  93.     {
  94.       var instance = Expression.Parameter(typeof(object), "instance");
  95.       var value = Expression.Parameter(typeof(object), "value");
  96.  
  97.       // value as T is slightly faster than (T)value, so if it's not a value type, use that
  98.       UnaryExpression instanceCast = (!this.Property.DeclaringType.IsValueType) ? Expression.TypeAs(instance, this.Property.DeclaringType) : Expression.Convert(instance, this.Property.DeclaringType);
  99.       UnaryExpression valueCast = (!this.Property.PropertyType.IsValueType) ? Expression.TypeAs(value, this.Property.PropertyType) : Expression.Convert(value, this.Property.PropertyType);
  100.       this.SetDelegate = Expression.Lambda<Action<object, object>>(Expression.Call(instanceCast, this.Property.GetSetMethod(), valueCast), new ParameterExpression[] { instance, value }).Compile();
  101.     }
  102.  
  103.     private void InitializeGet()
  104.     {
  105.       var instance = Expression.Parameter(typeof(object), "instance");
  106.       UnaryExpression instanceCast = (!this.Property.DeclaringType.IsValueType) ? Expression.TypeAs(instance, this.Property.DeclaringType) : Expression.Convert(instance, this.Property.DeclaringType);
  107.       this.GetDelegate = Expression.Lambda<Func<object, object>>(Expression.TypeAs(Expression.Call(instanceCast, this.Property.GetGetMethod()), typeof(object)), instance).Compile();
  108.     }
  109.  
  110.     public object Get(object instance)
  111.     {
  112.       return this.GetDelegate(instance);
  113.     }
  114.  
  115.     public void Set(object instance, object value)
  116.     {
  117.       this.SetDelegate(instance, value);
  118.     }
  119.   }
  120.  
  121. }
  122.  
  123.  

My results (this was run on a netbook):

Code: [Select]

Straight reflection: PropertyInfo.SetValue(): 5950 ms
System.Dynamic: dynamic.MyProperty = value: 145 ms
Direct Code: MyObject.MyProperty = value: 9 ms
FastProperty: fastProp.Set(myObject3, value): 170 ms




kaefer

  • Guest
Re: “Dynamic .NET” transactions vs intellisense , type safety, etc....
« Reply #16 on: April 07, 2012, 12:47:11 PM »
Code: [Select]
Straight reflection: PropertyInfo.SetValue(): 5950 ms
System.Dynamic: dynamic.MyProperty = value: 145 ms
Direct Code: MyObject.MyProperty = value: 9 ms
FastProperty: fastProp.Set(myObject3, value): 170 ms

This is impressiv. Thanks for not pointing out that going by InvokeMember is slower still in the region of 12000 ms.

Not having yet seen any hints as to the implementation in AutoCAD 2013, I've tried to explore but one mechanism to access members dynamically. Your performance analysis lets me believe that in the best case, the cost of opening DBObjects transparently and accessing their properties will be 1-2 orders of magnitude slower. In the worst case, it could be more than 4, like I would experience it with my attempt at DynamicObject.

Incidentally, taking the mutability out of the FastProperty class makes it almost readable.

Code - F#: [Select]
  1. type FastProperty(propertyInfo : PropertyInfo) =
  2.    
  3.     let setDelegate =
  4.         let instance = Expression.Parameter(typeof<obj>, "instance")
  5.         let value = Expression.Parameter(typeof<obj>, "value")
  6.  
  7.         let instanceCast =
  8.             if propertyInfo.DeclaringType.IsValueType then Expression.Convert(instance, propertyInfo.DeclaringType)
  9.             else Expression.TypeAs(instance, propertyInfo.DeclaringType)
  10.         let valueCast =
  11.             if propertyInfo.PropertyType.IsValueType then Expression.Convert(value, propertyInfo.PropertyType)
  12.             else Expression.TypeAs(value, propertyInfo.PropertyType)
  13.         Expression.Lambda<System.Action<obj, obj>>(Expression.Call(instanceCast, propertyInfo.GetSetMethod(), valueCast), [| instance; value |]).Compile()
  14.  
  15.     let getDelegate =
  16.         let instance = Expression.Parameter(typeof<obj>, "instance")
  17.         let instanceCast =
  18.             if propertyInfo.DeclaringType.IsValueType then Expression.Convert(instance, propertyInfo.DeclaringType)
  19.             else Expression.TypeAs(instance, propertyInfo.DeclaringType)
  20.         Expression.Lambda<System.Func<obj, obj>>(Expression.TypeAs(Expression.Call(instanceCast, propertyInfo.GetGetMethod()), typeof<obj>), instance).Compile()
  21.  
  22.     new(instance, property) =
  23.         FastProperty(instance.GetType().GetProperty property)
  24.  
  25.     member __.Get instance =
  26.         getDelegate.Invoke instance
  27.    
  28.     member __.Set(instance, value) =
  29.         setDelegate.Invoke(instance, value)

TheMaster

  • Guest
Re: “Dynamic .NET” transactions vs intellisense , type safety, etc....
« Reply #17 on: April 08, 2012, 02:18:05 AM »
Actually, when running those tests I noticed a few issues with
that FastProperty class as posted. First, there is a needless cast
of the result of the getter's Expression.Call() and you can call
the FastProperty's GetDelegate() and SetDelegate() delegates
directly for even better performance.

The other issues are it doesn't deal correctly with read-only
or write-only properties, indexed properties, etc., and has a
few other minor issues.

I've been using something like it for a while (prior to .NET 3.5,
I used Reflection.Emit, but that's no longer needed in 3.5 or
later), along with custom TypeDescriptors, to make AutoCAD's
Data Extraction run between 10-20X faster (yes, that wasn't
a typo, it runs 10-20x faster, without exaggeration).

Code: [Select]
Straight reflection: PropertyInfo.SetValue(): 5950 ms
System.Dynamic: dynamic.MyProperty = value: 145 ms
Direct Code: MyObject.MyProperty = value: 9 ms
FastProperty: fastProp.Set(myObject3, value): 170 ms

This is impressiv. Thanks for not pointing out that going by InvokeMember is slower still in the region of 12000 ms.

Not having yet seen any hints as to the implementation in AutoCAD 2013, I've tried to explore but one mechanism to access members dynamically. Your performance analysis lets me believe that in the best case, the cost of opening DBObjects transparently and accessing their properties will be 1-2 orders of magnitude slower. In the worst case, it could be more than 4, like I would experience it with my attempt at DynamicObject.


TheMaster

  • Guest
Re: “Dynamic .NET” transactions vs intellisense , type safety, etc....
« Reply #18 on: April 08, 2012, 09:07:54 AM »

Incidentally, taking the mutability out of the FastProperty class makes it almost readable.


LINQPad will show the equivalent C# code for any expression.

For example, LINQPad shows the lambda expression for the
FastProperty's property getter as:

Code - C#: [Select]
  1.  
  2.      // when the instance is  a value type:
  3.      instance => ((<Type>)instance).get_<PropertyName>() as object;
  4.  
  5.      // when the instance is a reference type:
  6.      instance => (instance as <Type>).get_<PropertyName>() as object;
  7.  
  8.  
« Last Edit: April 08, 2012, 09:14:33 AM by TheMaster »