Skip to content
JulianR edited this page Jun 21, 2011 · 1 revision

General

The goal of ThisMember in terms of performance is to have minimal, if any, impact on performance. To achieve this, ThisMember makes use of the expanded Expression API of .NET 4. This allows it to craft complex expressions, that are tuned specifically for the types that you're mapping, but that still allow users of the library to influence the generated expressions by plugging in their own. It then takes these expressions and compiles it into a reusable method.

Compilation stage

The first stage of the mapping process is building up the expression tree. This is a pretty fast process and is done only once. This expression tree is then compiled, this too only happens once for every map. The compilation stage takes somewhere in the ballpark area of 25ms.

Mapping

After compilation, the map is available as a delegate to a dynamically generated method, which is then cached by the MemberMapper. There's two ways you can actually perform the mapping:

var result = mapper.Map<Customer, CustomerDto>(customer);

This involves a few object instantiations and a Dictionary look-up, but is still pretty zippy. This is how you would normally use it. However, in case you really need it, the other option is to get the map once and invoke the delegate directly:

var map = mapper.GetMap<Customer, CustomerDto>();

for(var i = 0; i < 1000000; i++)
{
  var result = map.MappingFunction(customer, new CustomerDto());
}

Given the following types:

public class Order
{
  public decimal Amount { get; set; }
}

public class Customer
{
  public int CustomerID { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public IList<Order> Orders { get; set; }
}

public class CustomerDto
{
  public int CustomerID { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string FullName { get; set; }
  public decimal OrderAmount { get; set; }
}

And the following mapping definition in ThisMember:

var map = mapper.CreateMap<Customer, CustomerDto>(customMapping: src => new CustomerDto
{
  FullName = src.FirstName + " " + src.LastName,
  OrderAmount = src.Orders.Sum(o => o.Amount)
});

And the following customer:

var customer = new Customer
{
  CustomerID = 1,
  FirstName = "John",
  LastName = "Doe",
  Orders = new List<Order>
  {
    new Order
    {
      Amount = 100m
    },
    new Order
    {
      Amount = 15m
    },
    new Order
    {
      Amount = 150m
    }
  }
};

The performance characteristics for mapping those classes 1 million times look as follows:

  • Map compilation: 36ms.
  • Manually written inline mapping code: 370ms.
  • ThisMember "slow" (mapper.Map<TSource, TDestination>): 700ms.
  • ThisMember "fast" (map.MappingFunction): 370ms.

So there's really no performance difference at all if you use the delegate directly. Of course, out of convenience you'll probably use the easier way (mapper.Map<TSource, TDestination>) most of the time and as you can see, the performance difference is minimal.

The performance characteristics are different if your mapping meets one of the following criteria:

  • One of the types to map is non-public.
  • One of the types to map is generic.
  • Your custom mapping uses a non-public method or property.
  • Your custom mapping captured a local variable in a closure.
  • You turned off the option mapper.Options.Compilation.CompileToDynamicAssembly.

In that case, the expression tree does not get compiled to a new dynamic assembly, because it won't be able to access the non-public types, but a new DynamicMethod is created, which for unknown reasons carries a small but noticeable invocation penalty.

The same benchmark, but now Customer is an internal class:

  • ThisMember "slow" (mapper.Map<TSource, TDestination>): 980ms.
  • ThisMember "fast" (map.MappingFunction): 640ms.

So ideally, all classes that you map should be public and non-generic.

Clone this wiki locally