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.

Implementation Non-generic Generic Difference
ObjectCopyReflection 0,0240 ms 0,0242 ms ~1,0 x
ObjectCopyDynamicCode 0,0058 ms 0,0059 ms ~1,0 x
ObjectCopyLcg 0,0084 ms 0,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.



See also

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

  • Alexey says:

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

  • 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.

  • Gunnar says:

    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.

  • […] my object to object mapper is now almost completed and I am sure it is good idea to stay on LCG (Lightweight Code Generation) I can now compare the […]

  • […] I made some little changes too – you can set your own property mappings for types if you like. Of course, you can still use automatic mapping that is pretty primitive. Current package contains only LCG (Lightweight Code Generation) implementation of mapper – you can find other (slower) implementations from my blog posting Writing object to object mapper: moving to generics. […]

  • […] mappings between objects. Also you can modify existing and define your own mappings. Although it is possible to write way faster lightweight mappers still AutoMapper offers very good performance considering all the nice features it […]

Leave a Reply

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