Comments 20
спасибо! как раз стоял выбор между подобными библиотеками, теперь очевидно — EmitMapper, да и его fluent интерфейс (после fluentHibernate, Moq и Autofaq) очень и очень приятен.
Спасибо за наводку на EmitMapper и за сравнение производительности. Попробую в своем проекте заменить AutoMapper на EmitMapper. Было бы здорово, если бы еще описали, какие именно особенности реализации дали такую разницу производительности.
Опять какая-то синтетика, да еще и с неправильными замерами. Уж сколько ж раз повторяли, что JIT компилирует каждый метод отдельно, так что для получения хоть сколько-нибудь корректных данных надо каждый метод выполнить отдельно и без замеров производительности.
Время работы тестов по 30-50 секунд. Сами тесты из 5 строк, во всех текст самих тестов практически одинаковый. Так что временем работы JIT'a можно пренебречь
А попробовать?
Проверил. Перед каждым тестом добавил холодный прогон и GC.Collect(), чтобы все находились в равных условиях. Вот результаты:
Auto Mapper (simple): 37652 milliseconds
BLToolkit (simple): 34886 milliseconds
Emit Mapper (simple): 113 milliseconds
Handwritten Mapper (simple): 37 milliseconds
Auto Mapper (Nested): 52456 milliseconds
Emit Mapper (Nested): 91 milliseconds
Handwritten Mapper (Nested): 86 milliseconds
Auto Mapper (Custom): 49348 milliseconds
Emit Mapper (Custom): 188 milliseconds
Вобщем, они практически не отличаются от тех, что в заметке.
Auto Mapper (simple): 37652 milliseconds
BLToolkit (simple): 34886 milliseconds
Emit Mapper (simple): 113 milliseconds
Handwritten Mapper (simple): 37 milliseconds
Auto Mapper (Nested): 52456 milliseconds
Emit Mapper (Nested): 91 milliseconds
Handwritten Mapper (Nested): 86 milliseconds
Auto Mapper (Custom): 49348 milliseconds
Emit Mapper (Custom): 188 milliseconds
Вобщем, они практически не отличаются от тех, что в заметке.
Из особенностей, которые так сильно повлияли на производительность самые главные это:
1) В EmitMapper есть возможность заранее сгенерировать мэппер и где-то его сохранить (например, во время старта программы в статическом поле). В AutoMapper и BLToolkit мы вынуждены использовать одну единственную глобальную точку входа типа: «Map.ObjectToObject(foo)». А это затраты на синхронизацию, поиск по словарю и т.д.
2) AutoMapper был изначально спроектирован и написан для Reflection и лишь потом переведен на Emit. Из-за этого там осталось очень много ненужного оверхеда.
3) EmitMapper практически нигде не использует boxing/unboxing в отличии от его конкурентов
1) В EmitMapper есть возможность заранее сгенерировать мэппер и где-то его сохранить (например, во время старта программы в статическом поле). В AutoMapper и BLToolkit мы вынуждены использовать одну единственную глобальную точку входа типа: «Map.ObjectToObject(foo)». А это затраты на синхронизацию, поиск по словарю и т.д.
2) AutoMapper был изначально спроектирован и написан для Reflection и лишь потом переведен на Emit. Из-за этого там осталось очень много ненужного оверхеда.
3) EmitMapper практически нигде не использует boxing/unboxing в отличии от его конкурентов
В EmitMapper есть возможность заранее сгенерировать мэппер и где-то его сохранить (например, во время старта программы в статическом поле). В AutoMapper и BLToolkit мы вынуждены использовать одну единственную глобальную точку входа типа: «Map.ObjectToObject(foo)». А это затраты на синхронизацию, поиск по словарю и т.д.
Это вранье… «Map.ObjectToObject(foo)» это просто один из вариантов использования. Посмотрите на MappingEngine. Свободно кешиться в синглтоне. Для мебя мы вообще сделали хелперы, и забыли.
Простите, это было про AutoMapper, но быстрее всего если вы глубже копнете, тока окажеться что и в BLTollkit есть что-то подобное.
Я основывался на бенчмарке автора AutoMapper
code.google.com/p/automapperhome/source/browse/trunk/src/Benchmark/FlatteningMapper.cs
Вот кусок кода оттуда:
public void Map()
{
Mapper.Map<ModelObject, ModelDto>(_source);
}
Я полагаю, что автору виднее, как правильно использовать его собственную библиотеку.
code.google.com/p/automapperhome/source/browse/trunk/src/Benchmark/FlatteningMapper.cs
Вот кусок кода оттуда:
public void Map()
{
Mapper.Map<ModelObject, ModelDto>(_source);
}
Я полагаю, что автору виднее, как правильно использовать его собственную библиотеку.
Сам автор BlToolkit подтвердил, что такого интерфейса нет.
Не нашел в MappingEngine ничего подобного. Вот его код: code.google.com/p/automapperhome/source/browse/trunk/src/AutoMapper/MappingEngine.cs
Можете носом ткнуть?
Можете носом ткнуть?
ессно
я им когда-то сабмитил патч, тобы попроще выглядело, но что есть то есть.
и использование
Проблема в том что мы не всерано придеться посыпать голову пеплом. При том что про Map.ObjectToObject это действительно вранье. Кеширование конфигруции практически никчему не приводит. Как то так.
Простите. Я просто должен был указать на неточность не более того.
var configuration = new Configuration(new TypeMapFactory(), MapperRegistry.AllMappers.Invoke()); configuration.CreateMap<B2, A2>(); configuration.CreateMap<char, int>(); autoMapper = new MappingEngine(configuration);
я им когда-то сабмитил патч, тобы попроще выглядело, но что есть то есть.
и использование
d = autoMapper.Map(s, d);
Проблема в том что мы не всерано придеться посыпать голову пеплом. При том что про Map.ObjectToObject это действительно вранье. Кеширование конфигруции практически никчему не приводит. Как то так.
Простите. Я просто должен был указать на неточность не более того.
Во-первых, я не понял, в чем заключается «вранье» про «Map.ObjectToObject»?
Во-вторых, я говорил не про кеширование конфигураций, а про кеширование сгенерированного маппера (то есть при маппинге такой маппер не анализирует типы переданных объектов и не генерирует или достает из кеша код. Если бы вы прочитали о чем шла речь, то увидели, что я объяснил просадку производительности. Дак вот, производительность проседает из-за поиска кода по словарю и из-за синхронизации. Об этом я писал выше.
В-третьих у MappingEngine нет метода «Map», который принимает два объекта. Назовите мне номер строки с этим методом здесь:
code.google.com/p/automapperhome/source/browse/trunk/src/AutoMapper/MappingEngine.cs
Во-вторых, я говорил не про кеширование конфигураций, а про кеширование сгенерированного маппера (то есть при маппинге такой маппер не анализирует типы переданных объектов и не генерирует или достает из кеша код. Если бы вы прочитали о чем шла речь, то увидели, что я объяснил просадку производительности. Дак вот, производительность проседает из-за поиска кода по словарю и из-за синхронизации. Об этом я писал выше.
В-третьих у MappingEngine нет метода «Map», который принимает два объекта. Назовите мне номер строки с этим методом здесь:
code.google.com/p/automapperhome/source/browse/trunk/src/AutoMapper/MappingEngine.cs
Метод Map с двумя параметрами нашел, но это не сгенерированный маппер.
public TDestination Map<TSource, TDestination>(ResolutionContext parentContext, TSource source)
{
Type destinationType = typeof(TDestination);
Type sourceType = typeof(TSource);
…
}
Это не именни никакого отношение к тому, о чем я говорил
public TDestination Map<TSource, TDestination>(ResolutionContext parentContext, TSource source)
{
Type destinationType = typeof(TDestination);
Type sourceType = typeof(TSource);
…
}
Это не именни никакого отношение к тому, о чем я говорил
А как можно получить тестовый код, так что бы я мог написать «правильный» код для AutoMapper?
Sign up to leave a comment.
Benchmark: AutoMapper vs BLToolkit vs EmitMapper