Нет, третий бенчмарк использует скомпилированный делегат. Кроме того, накладные расходы «всего» в 10 раз, что было бы намного, намного больше, если бы это было чистое отражение. Например, я считаю, что библиотека AutoMapper, которая использует отражение для своего отображения, в 400 раз медленнее, чем ручное отображение из моих тестов.

ерирую дерево выражений, которое отображает свойства из исходного объекта в целевой объект, который затем компилируется вFunc<TSource, TDestination, TDestination> и выполнен.

Это отладочное представление полученногоLambdaExpression:

.Lambda #Lambda1<System.Func`3[MemberMapper.Benchmarks.Program+ComplexSourceType,MemberMapper.Benchmarks.Program+ComplexDestinationType,MemberMapper.Benchmarks.Program+ComplexDestinationType]>(
    MemberMapper.Benchmarks.Program+ComplexSourceType $right,
    MemberMapper.Benchmarks.Program+ComplexDestinationType $left) {
    .Block(
        MemberMapper.Benchmarks.Program+NestedSourceType $Complex$955332131,
        MemberMapper.Benchmarks.Program+NestedDestinationType $Complex$2105709326) {
        $left.ID = $right.ID;
        $Complex$955332131 = $right.Complex;
        $Complex$2105709326 = .New MemberMapper.Benchmarks.Program+NestedDestinationType();
        $Complex$2105709326.ID = $Complex$955332131.ID;
        $Complex$2105709326.Name = $Complex$955332131.Name;
        $left.Complex = $Complex$2105709326;
        $left
    }
}

Вычистить это будет:

(left, right) =>
{
    left.ID = right.ID;
    var complexSource = right.Complex;
    var complexDestination = new NestedDestinationType();
    complexDestination.ID = complexSource.ID;
    complexDestination.Name = complexSource.Name;
    left.Complex = complexDestination;
    return left;
}

Это код, который отображает свойства этих типов:

public class NestedSourceType
{
  public int ID { get; set; }
  public string Name { get; set; }
}

public class ComplexSourceType
{
  public int ID { get; set; }
  public NestedSourceType Complex { get; set; }
}

public class NestedDestinationType
{
  public int ID { get; set; }
  public string Name { get; set; }
}

public class ComplexDestinationType
{
  public int ID { get; set; }
  public NestedDestinationType Complex { get; set; }
}

Код руководства для этого:

var destination = new ComplexDestinationType
{
  ID = source.ID,
  Complex = new NestedDestinationType
  {
    ID = source.Complex.ID,
    Name = source.Complex.Name
  }
};

Проблема в том, что когда я компилируюLambdaExpression и сравнить полученнуюdelegate это примерно в 10 раз медленнее, чем ручная версия. Я понятия не имею, почему это так. И вся идея в этом заключается в максимальной производительности без утомительного ручного картирования.

Когда я беру код от Барта де Смета из егоСообщение блога по этой теме и сравните ручную версию вычисления простых чисел с скомпилированным деревом выражений, они полностью идентичны по производительности.

Что может вызвать эту огромную разницу, когда отладочный видLambdaExpression похоже на то, что вы ожидаете?

РЕДАКТИРОВАТЬ

По запросу я добавил эталонный тест, который использовал:

public static ComplexDestinationType Foo;

static void Benchmark()
{

  var mapper = new DefaultMemberMapper();

  var map = mapper.CreateMap(typeof(ComplexSourceType),
                             typeof(ComplexDestinationType)).FinalizeMap();

  var source = new ComplexSourceType
  {
    ID = 5,
    Complex = new NestedSourceType
    {
      ID = 10,
      Name = "test"
    }
  };

  var sw = Stopwatch.StartNew();

  for (int i = 0; i < 1000000; i++)
  {
    Foo = new ComplexDestinationType
    {
      ID = source.ID + i,
      Complex = new NestedDestinationType
      {
        ID = source.Complex.ID + i,
        Name = source.Complex.Name
      }
    };
  }

  sw.Stop();

  Console.WriteLine(sw.Elapsed);

  sw.Restart();

  for (int i = 0; i < 1000000; i++)
  {
    Foo = mapper.Map<ComplexSourceType, ComplexDestinationType>(source);
  }

  sw.Stop();

  Console.WriteLine(sw.Elapsed);

  var func = (Func<ComplexSourceType, ComplexDestinationType, ComplexDestinationType>)
             map.MappingFunction;

  var destination = new ComplexDestinationType();

  sw.Restart();

  for (int i = 0; i < 1000000; i++)
  {
    Foo = func(source, new ComplexDestinationType());
  }

  sw.Stop();

  Console.WriteLine(sw.Elapsed);
}

Второй, очевидно, медленнее, чем делать это вручную, поскольку он включает в себя поиск в словаре и несколько экземпляров объекта, но третий должен быть таким же быстрым, как и вызываемый там необработанный делегат, и приведение изDelegate вFunc происходит вне цикла.

Я также попытался обернуть ручной код в функцию, но я вспоминаю, что это не имело заметного значения. В любом случае, вызов функции не должен добавлять порядок издержек.

Я также дважды делаю тест, чтобы убедиться, что JIT не мешает.

РЕДАКТИРОВАТЬ

Вы можете получить код для этого проекта здесь:

https://github.com/JulianR/MemberMapper/

Я использовал расширение отладчика Sons-of-Strike, как описано в этом сообщении в блоге Барта де Смета, чтобы вывести сгенерированный IL динамического метода:

IL_0000: ldarg.2 
IL_0001: ldarg.1 
IL_0002: callvirt 6000003 ComplexSourceType.get_ID()
IL_0007: callvirt 6000004 ComplexDestinationType.set_ID(Int32)
IL_000c: ldarg.1 
IL_000d: callvirt 6000005 ComplexSourceType.get_Complex()
IL_0012: brfalse IL_0043
IL_0017: ldarg.1 
IL_0018: callvirt 6000006 ComplexSourceType.get_Complex()
IL_001d: stloc.0 
IL_001e: newobj 6000007 NestedDestinationType..ctor()
IL_0023: stloc.1 
IL_0024: ldloc.1 
IL_0025: ldloc.0 
IL_0026: callvirt 6000008 NestedSourceType.get_ID()
IL_002b: callvirt 6000009 NestedDestinationType.set_ID(Int32)
IL_0030: ldloc.1 
IL_0031: ldloc.0 
IL_0032: callvirt 600000a NestedSourceType.get_Name()
IL_0037: callvirt 600000b NestedDestinationType.set_Name(System.String)
IL_003c: ldarg.2 
IL_003d: ldloc.1 
IL_003e: callvirt 600000c ComplexDestinationType.set_Complex(NestedDestinationType)
IL_0043: ldarg.2 
IL_0044: ret 

Я не эксперт в IL, но это кажется довольно простым и именно то, что вы ожидаете, нет? Тогда почему это так медленно? Никаких странных операций с боксом, никаких скрытых реализаций, ничего. Это не совсем то же самое, что дерево выражений выше, так как есть такжеnull проверитьright.Complex в настоящее время.

Это код для ручной версии (полученной через Reflector):

L_0000: ldarg.1 
L_0001: ldarg.0 
L_0002: callvirt instance int32 ComplexSourceType::get_ID()
L_0007: callvirt instance void ComplexDestinationType::set_ID(int32)
L_000c: ldarg.0 
L_000d: callvirt instance class NestedSourceType ComplexSourceType::get_Complex()
L_0012: brfalse.s L_0040
L_0014: ldarg.0 
L_0015: callvirt instance class NestedSourceType ComplexSourceType::get_Complex()
L_001a: stloc.0 
L_001b: newobj instance void NestedDestinationType::.ctor()
L_0020: stloc.1 
L_0021: ldloc.1 
L_0022: ldloc.0 
L_0023: callvirt instance int32 NestedSourceType::get_ID()
L_0028: callvirt instance void NestedDestinationType::set_ID(int32)
L_002d: ldloc.1 
L_002e: ldloc.0 
L_002f: callvirt instance string NestedSourceType::get_Name()
L_0034: callvirt instance void NestedDestinationType::set_Name(string)
L_0039: ldarg.1 
L_003a: ldloc.1 
L_003b: callvirt instance void ComplexDestinationType::set_Complex(class NestedDestinationType)
L_0040: ldarg.1 
L_0041: ret 

Выглядит идентично мне ..

РЕДАКТИРОВАТЬ

Я перешел по ссылке в ответе Майкла Б. на эту тему. Я попытался реализовать трюк в принятом ответе, и это сработало! Если вы хотите получить краткую информацию об уловке: он создает динамическую сборку и компилирует дерево выражений в статический метод в этой сборке, и по какой-то причине это в 10 раз быстрее. Недостатком этого является то, что мои эталонные классы были внутренними (на самом деле, публичные классы вложены во внутренний), и это вызвало исключение, когда я попытался получить к ним доступ, потому что они были недоступны. Кажется, что нет обходного пути, но я могу просто определить, являются ли ссылочные типы внутренними или нет, и решить, какой подход к компиляции использовать.

Что до сих пор меня беспокоит, так это почему этот метод простых чиселявляется идентичны по производительности скомпилированному дереву выражений.

И снова, я приветствую всех, кто запускает код в этом репозитории GitHub, чтобы подтвердить мои измерения и убедиться, что я не сумасшедший :)

Ответы на вопрос(5)

Ваш ответ на вопрос