Writing object to object mapper: moving to generics

In my previous posting about object to object mapping Writing object to object mapper: first implementations I wrote first and simple implementations of mapper. These implementations based heavily on reflection. In this posting I will boost up mapper performance by using generics.

In most parts the code here is same as before but instead of laying heavily on objects we make use of generics so we can maybe achieve better performance than before.

Base class and property map

Property map is still the same but object base is moved to generic methods.

public class PropertyMap
{
   
public PropertyInfo SourceProperty { get; set
; }
   
public PropertyInfo TargetProperty { get; set
; }
}
 

public abstract class ObjectCopyBase
{
 
   
public abstract void
MapTypes<T, TU>();
   
public abstract void
Copy<T, TU>(T source, TU target);
 
   
protected virtual IList<PropertyMap
>
        GetMatchingProperties<T, TU>()
    {
       
var sourceProperties = typeof
(T).GetProperties();
       
var targetProperties = typeof
(TU).GetProperties();
 
       
var properties = (from s in
sourceProperties
                         
from t in
targetProperties
                         
where
s.Name == t.Name &&
                                s.CanRead &&
                                t.CanWrite &&
                                s.PropertyType == t.PropertyType
                         
select new PropertyMap
                          {
                              SourceProperty = s,
                              TargetProperty = t
                          }).ToList();
       
return
properties;
    }
 
   
protected virtual string
GetMapKey<T, TU>()
    {
       
var className = "Copy_"
;
        className +=
typeof(T).FullName.Replace(".", "_"
);
        className +=
"_"
;
        className +=
typeof(TU).FullName.Replace(".", "_"
);
 
       
return className;
    }
}

These changes to non-abstract methods are marginal and we don’t have to do these changes if we don’t want. They don’t affect performance as we see later.

ObjectCopyReflection

Here is the generic implementation of ObjectCopyReflection.

public class ObjectCopyReflection : ObjectCopyBase
{
   
private readonly Dictionary<string, PropertyMap
[]> _maps =
       
new Dictionary<string, PropertyMap
[]>();
 
   
public override void
MapTypes<T, TU>()
    {
       
var source = typeof
(T);
       
var target = typeof
(TU);
       
var
key = GetMapKey<T, TU>();
       
if
(_maps.ContainsKey(key))
           
return
;
 
       
var
props = GetMatchingProperties<T, TU>();
        _maps.Add(key, props.ToArray());
    }
 
   
public override void
Copy<T, TU>(T source, TU target)
    {
       
var
key = GetMapKey<T, TU>();
       
if
(!_maps.ContainsKey(key))
            MapTypes<T, TU>();
 
       
var
propMap = _maps[key];
 
       
for (var
i = 0; i < propMap.Length; i++)
        {
           
var
prop = propMap[i];
           
var
sourceValue = prop.SourceProperty.GetValue
                              (source,
null
);
            prop.TargetProperty.SetValue(target, sourceValue,
                                        
null);
        }
    }
}

Running this implementation gives us 0,0242 ms as result. It is practically same as before and moving this implementation to generics had no impact on performance.

ObjectCopyDynamicCode

Now let’s take ObjectCopyDynamicCode implementation.

public class ObjectCopyDynamicCode : ObjectCopyBase
{
   
private readonly Dictionary<string, Type
> _comp =
       
new Dictionary<string, Type
>();
 
   
public override void
MapTypes<T, TU>()
    {
       
var source = typeof
(T);
       
var target = typeof
(TU);
 
       
var
key = GetMapKey<T, TU>();
       
if
(_comp.ContainsKey(key))
           
return
;
 
       
var builder = new StringBuilder
();
        builder.Append(
"namespace Copy {\r\n"
);
        builder.Append(
"    public class "
);
        builder.Append(key);
        builder.Append(
" {\r\n"
);
        builder.Append(
"        public static void CopyProps("
);
        builder.Append(source.FullName);
        builder.Append(
" source, "
);
        builder.Append(target.FullName);
        builder.Append(
" target) {\r\n"
);
 
       
var
map = GetMatchingProperties<T, TU>();
       
foreach (var item in
map)
        {
            builder.Append(
"            target."
);
            builder.Append(item.TargetProperty.Name);
            builder.Append(
" = "
);
            builder.Append(
"source."
);
            builder.Append(item.SourceProperty.Name);
            builder.Append(
";\r\n"
);
        }
 
        builder.Append(
"        }\r\n   }\r\n}"
);
 
       
var myCodeProvider = new
CSharpCodeProvider();
       
var
myCodeCompiler = myCodeProvider.CreateCompiler();
       
var myCompilerParameters = new
CompilerParameters();
        myCompilerParameters.ReferencedAssemblies.Add(
           
typeof
(LinqReflectionPerf).Assembly.Location
        );
        myCompilerParameters.GenerateInMemory =
true
;
       
var
results = myCodeCompiler.CompileAssemblyFromSource
            (myCompilerParameters, builder.ToString());
 
       
var
copierType = results.CompiledAssembly.GetType
                         (
"Copy."
+ key);
        _comp.Add(key, copierType);
    }
 
   
public override void
Copy<T, TU>(T source, TU target)
    {
       
var
key = GetMapKey<T, TU>();
       
if
(!_comp.ContainsKey(key))
            MapTypes<T, TU>();
 
       
var flags = BindingFlags.Public | BindingFlags
.Static |
                   
BindingFlags
.InvokeMethod;
       
var args = new object
[] { source, target };
        _comp[key].InvokeMember(
"CopyProps", flags, null, null,
                                 args);
    }
}

The result is 0,0059 ms and it is also same as before. So we don’t have any luck here too.

ObjectCopyLcg

Before all hope is gone let’s see what LCG (Lightweight Code Generation) implementation of mapper thinks about generics.

public class ObjectCopyLcg : ObjectCopyBase
{
   
private delegate void CopyPublicPropertiesDelegate
<T, TU>
        (T source, TU target);
 
   
private readonly Dictionary<string, object
> _del =
       
new Dictionary<string, object
>();
 
   
public override void
MapTypes<T, TU>()
    {
       
var
key = GetMapKey<T, TU>();
       
if
(_del.ContainsKey(key))
           
return
;
 
       
var source = typeof
(T);
       
var target = typeof
(TU);
 
       
var args = new
[] { source, target };
       
var mod = typeof
(Program).Module;
 
       
var dm = new DynamicMethod(key, null
, args, mod);
       
var
il = dm.GetILGenerator();
       
var
maps = GetMatchingProperties<T, TU>();
 
       
foreach (var map in
maps)
        {
            il.Emit(
OpCodes
.Ldarg_1);
            il.Emit(
OpCodes
.Ldarg_0);
            il.EmitCall(
OpCodes
.Callvirt,
                        map.SourceProperty.GetGetMethod(),
null
);
            il.EmitCall(
OpCodes
.Callvirt,
                        map.TargetProperty.GetSetMethod(),
null
);
        }
        il.Emit(
OpCodes
.Ret);
       
var
del = dm.CreateDelegate(
                 
typeof(CopyPublicPropertiesDelegate
<T, TU>));
        _del.Add(key, del);
    }
 
   
public override void
Copy<T, TU>(T source, TU target)
    {
       
var
key = GetMapKey<T, TU>();
       
var del = (CopyPublicPropertiesDelegate<T, TU>)_del[key];
        del.Invoke(source, target);
    }
}

The result is 0,0019 ms and it is ~4.5x better than before. Although LCG implementation was not the fastest one before it is now the best implementation we have.

Results

Let’s put previous and current results to table and let’s compare them.

ImplementationNon-genericGenericDifference
ObjectCopyReflection0,0240 ms0,0242 ms~1,0 x
ObjectCopyDynamicCode0,0058 ms0,0059 ms~1,0 x
ObjectCopyLcg0,0084 ms0,0019 ms~4,5 x

As we can see then first two implementations gave different results but differences were very-very small. We can practically say that there were no changes in performance. ObjectCopyLcg gave us ~4.5x times better result when we moved to generics.

Conclusion

This posting is good example about how generics are not silver bullets that fix performance problems automagically because they know the types. In our situation we achieved better results only in one implementation while other implementations stayed practically the same. In my next object to object mapper posting I will compare my results with AutoMapper.

Gunnar Peipman

Gunnar Peipman is ASP.NET, Azure and SharePoint fan, Estonian Microsoft user group leader, blogger, conference speaker, teacher, and tech maniac. Since 2008 he is Microsoft MVP specialized on ASP.NET.

    6 thoughts on “Writing object to object mapper: moving to generics

    • October 21, 2010 at 5:41 am
      Permalink

      Where i may get LinqReflectionPerf type?
      Couldn’t find it anywhere.

    • December 7, 2010 at 2:50 pm
      Permalink

      Just for completeness:

      public class ObjectCopyExpression : ObjectCopyBase
      {
      private readonly Dictionary<Tuple, List> _props = new Dictionary<Tuple, List>();

      public override void MapTypes()
      {
      Type sourceType = typeof(TSource);
      Type targetType = typeof(TTarget);
      Tuple key = new Tuple(sourceType, targetType);

      if (this._props.ContainsKey(key) == true)
      {
      return;
      }

      IEnumerable props = this.GetMatchingProperties();

      this._props [ key ] = new List();

      foreach (PropertyMap prop in props)
      {
      String propName = prop.SourceProperty.Name;

      ParameterExpression targetExpression = Expression.Parameter(targetType, “target”);

      ParameterExpression sourceExpression = Expression.Parameter(sourceType, “source”);

      PropertyInfo getter = prop.SourceProperty;

      PropertyInfo setter = prop.TargetProperty;

      MemberExpression sourcePropertyAccess = Expression.MakeMemberAccess(sourceExpression, getter);

      MemberExpression targetPropertyAccess = Expression.MakeMemberAccess(targetExpression, setter);

      BinaryExpression call = Expression.Assign(targetPropertyAccess, sourcePropertyAccess);

      LambdaExpression lambda = Expression.Lambda(call, sourceExpression, targetExpression);

      Delegate del = lambda.Compile();

      this._props [ key ].Add(del);
      }
      }

      public override void Copy(TSource source, TTarget target)
      {
      Type sourceType = typeof(TSource);
      Type targetType = typeof(TTarget);
      Tuple key = new Tuple(sourceType, targetType);

      if (this._props.ContainsKey(key) == false)
      {
      this.MapTypes();
      }

      foreach (Delegate del in this._props [ key ])
      {
      Object result = del.DynamicInvoke(source, target);
      }
      }
      }

      However, in my computer (.NET 4.0) ObjectCopyReflection is the fastest.

    • December 7, 2010 at 3:32 pm
      Permalink

      Thanks for feedback, Ricardo! :)

      This is strange that ObjectCopyReflection performs best in your machine. It has a lot more overhead than LCG – I mean .NET framework has to go through more instructions to run. LCG stuff is small and compact – there is not additional code used besides the one that makes actual work for us.

    • Pingback:Writing object to object mapper: my mapper vs AutoMapper | Gunnar Peipman - Programming Blog

    • Pingback:My object to object mapper source released | Gunnar Peipman - Programming Blog

    • Pingback:Using AutoMapper to build base class for mappers between domain classes and models | Gunnar Peipman - Programming Blog

    Leave a Reply

    Your email address will not be published. Required fields are marked *