Geliştirdiğimiz uygulamalarda kullanıcının ihtiyacı olsada olmasada bir tablonun tüm field’larını içeren nesneyi geri dönüş tipi olarak dönebiliyoruz. ilk zamanlar sayısı az olan kayıtlarda veya istek sayısı az olan projelerde bu durumun sorun olduğu farkedilmeyebilir fakat işin istek yada kayıt boyutu arttığında performans sorununu az-çok yaşamanız olasıdır.
Mapper kütüphaneleri Performans Testi
Bu makalede 6 farklı yöntemi benchmark’e sokarak hangi mapper yönteminin kaç kayıtta ne kadar sürede tamamlandığını göreceğiz ve sonuçlar karşısında şaşırabilirsiniz 🙂 Şimdi hangi mapper yöntemlerini kullanacağımızı görelim.
Kullanacağımız mapper kütüphaneleri listesi şöyle;
- TinyMapper
- Install-Package TinyMapper
- ExpressMapper
- Install-Package Expressmapper
- LightMapper
- Install-Package LightMapper
- AutoMapper
- Install-Package AutoMapper
- Linq
- Reflector ile oluşturduğumuz yöntem
Install-Package… ile belirtilmiş kütüphaneleri Manage NuGet Packages yada Package Manager Console‘dan yükleyebilirsiniz.
Linq sistemde yerleşik olarak zaten bulunduğundan indirmeye gerek yok ve Kendi Reflector yöntemimiz ile hazırladığımız kodlara makale devamından ulaşabilirsiniz.
Bu örnekte kullanacağımız Customer ve CustomerDto tanımları şu şekilde olacak.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | namespace MHG.Mapper { public class Customer { public string Name { get; set; } public string Surname { get; set; } public int Age { get; set; } public string Country { get; set; } public string City { get; set; } public string Address { get; set; } public string Phone { get; set; } public string Email { get; set; } public string Web { get; set; } } public class CustomerDto { public string Name { get; set; } public string Surname { get; set; } } } |
Üstteki her iki tanım arasındaki koca farkı görüyorsunuz Customer sınıfının veritabanımızdaki Customer tablosuna karşılık gelen sınıf olduğunu düşünelim ve CustomerDto‘da kullanıcılara Müşteri(Customer) bilgileri için gerekli olan ad(Name) soyad(Surname) bilgilerini geri dönderiyoruz.
Makaleyi uzatmamak açısından herhangi bir veritabanı yada tablo v.b oluşturma yada kullanma işlemleri ile uğraşmamak için Faker.NET4 adlı kütüphaneyi kullanarak rastgele veri üreteceğiz.
Faker.NET adlı kütüphaneyi indirmek için Install-Package Faker.Net komutunu kullanabilirsiniz.
Faker.NET kütüphanesini kullanarak Customer sınıfından 10 tane örnek almayı ve 4 farklı Mapper kütüphanesinin Intialize yöntemlerine ait örnek kodları tanımlayalım şimdi.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | var customerList = new List<Customer>(); for (var i = 0; i < 10; i++) { customerList.Add( new Customer { Address = Faker.Address.SecondaryAddress(), City = Faker.Address.City(), Age = Faker.RandomNumber.Next( 10, 100 ), Name = Faker.Name.First(), Surname = Faker.Name.Last(), Phone = Faker.Phone.Number(), Web = Faker.Internet.DomainName(), Email = Faker.Internet.Email(), Country = Faker.Address.Country() } ); } #region Initialize #region LightMapper var configListMapper = LightMapper.LightMapper.Instance; configListMapper.AddMapping( configListMapper.CreateMapping<Customer, CustomerDto>( true ) ); #endregion #region AutoMapper var configAutoMapper = new MapperConfiguration( cfg => cfg.CreateMap<Customer, CustomerDto>() ); AutoMapper.Mapper.Initialize( cfg => cfg.CreateMap<Customer, CustomerDto>() ); #endregion #region TinyMapper TinyMapper.Bind<Customer, CustomerDto>(); #endregion #region ExpressMapper ExpressMapper.Mapper.Register<Customer, CustomerDto>(); #endregion #endregion |
Gördüğünüz gibi 4 farklı mapper kütüphanesinin Initialize metodlarına Kaynağın Customer sınıfı ve hedefinde CustomerDto olduğunu tanımladık.
Şimdide bu Mapper kütüphanelerinin asıl amacı olan Dto nesnelerinin nasıl kolayca ve hızlıca oluşturabileceğimizi göreceğiz. Bu mapper kütüphanelerinin haricinde makale başında belirttiğimiz gibi Linq ve Reflector yöntemi ile kendi oluşturduğumuz mapper kod bloğunu nasıl kullanacağımızı göreceğiz.
Mapper’i kullanırken 10 tane rastgele üretilmiş Customer nesnesi üzerinden işlem yapacağımızı söylemiştik şimdi Faker.NET kütüphanesi ile rastgele verilerden üretilmiş bir Customer nesnesini görelim.
CustomerDto nesnesi içerisinde Sadece Name ve Surname adlı 2 property yer alıyor kullanıcının sadece bu alanlara ihtiyaç duyduğunu düşünerek yola çıktık. Hadi şimdide Mapper yöntemi ile CustomerDto nesnelerinin nasıl otomatik oluşturabileceğimizi görelim.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | var customerDtoList = new List<CustomerDto>(); #region AutoMapper var createAutoMapper = configAutoMapper.CreateMapper(); var dtoAutoMapper = customerList.Select( T => createAutoMapper.Map<CustomerDto>( T ) ).ToList(); #endregion #region LightMapper customerDtoList.Clear(); foreach( var customer in customerList ) { customerDtoList.Add( LightMapper.LightMapper.Instance.Map<Customer, CustomerDto>( customer ) ); } #endregion #region TinyMapper customerDtoList.Clear(); foreach( var customer in customerList ) { customerDtoList.Add( TinyMapper.Map<CustomerDto>( customer ) ); } #endregion #region TinyMapper customerDtoList.Clear(); foreach( var customer in customerList ) { customerDtoList.Add( ExpressMapper.Mapper.Map<Customer, CustomerDto>( customer ) ); } #endregion #region MyCustomMapper customerDtoList.Clear(); foreach( var customer in customerList ) { customerDtoList.Add( customer.Map<Customer, CustomerDto>() ); } #endregion #region LinqMapper customerDtoList.Clear(); customerDtoList.AddRange( customerList.Select( T => new CustomerDto { Name = T.Name, Surname = T.Surname } ).ToList() ); #endregion |
4 Farklı Mapper kütüphanesi, Linq ve Reflector yöntemini kullanarak kendi yazdığımız basit bir metod ile CustomerDto nesnesini Mapper yöntemi ile nasıl oluşturabileceğimizi üstteki örnek kodlarda görmüş olduk ve örnek çıktıya ait görüntü altta yer almaktadır.
Makale başındada şaşıracağınız bir nokta olduğunu söylemiştim oda Bencmarking sonuçlarıdır. 6 Mapper yönteminden hangisinin 1, 10, 100, 1000 Customer nesnesi üzerinden ne kadar sürdüğünü göreceğiz. Bu altı mapper yöntemine ait BenchmarkDotNet kütüphanesinin özellikleri kullanılarak oluşturulmuş MapperBenchmarking sınıfına ait içeriğe gözatalım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | using System.Collections.Generic; using System.Linq; using AutoMapper; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes.Exporters; using Nelibur.ObjectMapper; namespace MHG.Mapper { [HtmlExporter] public class MapperBenchmarking { [Params( 1, 10, 100, 1000 )] public int Param { get; set; } private readonly List<Customer> _customerList; private readonly MapperConfiguration _config; private readonly LightMapper.IMapper _configListMapper; public MapperBenchmarking() { #region LightMapper _configListMapper = LightMapper.LightMapper.Instance; _configListMapper.AddMapping( _configListMapper.CreateMapping<Customer, CustomerDto>( true ) ); #endregion #region AutoMapper _config = new MapperConfiguration( cfg => cfg.CreateMap<Customer, CustomerDto>() ); AutoMapper.Mapper.Initialize( cfg => cfg.CreateMap<Customer, CustomerDto>() ); #endregion #region TinyMapper TinyMapper.Bind<Customer, CustomerDto>(); #endregion #region ExpressMapper ExpressMapper.Mapper.Register<Customer, CustomerDto>(); #endregion _customerList = new List<Customer>(); } [Setup] public void Setup() { for( int i = 0; i < Param; i++ ) { _customerList.Add( new Customer { Address = Faker.Address.SecondaryAddress(), City = Faker.Address.City(), Age = Faker.RandomNumber.Next( 10, 100 ), Name = Faker.Name.First(), Surname = Faker.Name.Last(), Phone = Faker.Phone.Number(), Web = Faker.Internet.DomainName(), Email = Faker.Internet.Email(), Country = Faker.Address.Country() } ); } } [Benchmark] public void AutoMapperBenchmark() { var customerDtoList = new List<CustomerDto>(); var config = _config.CreateMapper(); foreach( var customer in _customerList ) { customerDtoList.Add( config.Map<CustomerDto>( customer )); } } [Benchmark] public void WithLinqBenchmark() { var dtoLinq = _customerList.Select( T => new CustomerDto { Name = T.Name, Surname = T.Surname } ).ToArray(); } [Benchmark] public void LightMapperBenchmark() { var customerDtoList = new List<CustomerDto>(); foreach( var customer in _customerList ) { customerDtoList.Add( LightMapper.LightMapper.Instance.Map<Customer, CustomerDto>( customer ) ); } } [Benchmark] public void MyCustomMapperBenchmark() { var customerDtoList = new List<CustomerDto>(); foreach( var customer in _customerList ) { customerDtoList.Add( customer.Map<Customer, CustomerDto>() ); } } [Benchmark] public void TinyMapperBenchmark() { var customerDtoList = new List<CustomerDto>(); foreach( var customer in _customerList ) { customerDtoList.Add( TinyMapper.Map<CustomerDto>( customer ) ); } } [Benchmark] public void ExpressMapperBencmark() { var customerDtoList = new List<CustomerDto>(); foreach( var customer in _customerList ) { customerDtoList.Add( ExpressMapper.Mapper.Map<Customer, CustomerDto>( customer ) ); } } } } |
Şimdide BencmarkDotNet kütüphanesi özelliklerini kullanarak oluşturduğumuz MapperBencmarking adlı sınıfın test sonuçlarını alabilmek için alttaki kodu kullanıyoruz.
1 2 3 | #region Benchmarking BenchmarkRunner.Run<MapperBenchmarking>(); #endregion |
Hemen makaleyi daha fazla uzatmadan test sonuçlarına gözatalım.
Method | Param | Mean | StdErr | StdDev | Median | Gen 0 | Allocated |
---|---|---|---|---|---|---|---|
AutoMapperBenchmark | 1 | 319.7565 ns | 1.0437 ns | 3.9052 ns | 320.2950 ns | 0.0310 | 136 B |
WithLinqBenchmark | 1 | 212.1524 ns | 2.1310 ns | 13.9738 ns | 204.2439 ns | 0.0214 | 108 B |
LightMapperBenchmark | 1 | 601.8155 ns | 0.8052 ns | 3.1186 ns | 601.8548 ns | 0.0896 | 360 B |
MyCustomMapperBenchmark | 1 | 1,653.7048 ns | 3.3275 ns | 12.8872 ns | 1,654.3241 ns | 0.0036 | 328 B |
TinyMapperBenchmark | 1 | 146.6442 ns | 0.3886 ns | 1.5050 ns | 146.8335 ns | 0.0157 | 68 B |
ExpressMapperBencmark | 1 | 408.4800 ns | 4.0692 ns | 25.0845 ns | 401.2972 ns | 0.0067 | 92 B |
AutoMapperBenchmark | 10 | 2,616.9514 ns | 5.6210 ns | 21.7702 ns | 2,622.6707 ns | 0.0387 | 400 B |
WithLinqBenchmark | 10 | 691.2115 ns | 1.2524 ns | 4.6861 ns | 692.8052 ns | 0.1054 | 408 B |
LightMapperBenchmark | 10 | 5,668.9246 ns | 11.4476 ns | 44.3365 ns | 5,662.0091 ns | 0.8311 | 3.25 kB |
MyCustomMapperBenchmark | 10 | 16,323.7172 ns | 25.4070 ns | 88.0124 ns | 16,320.4379 ns | 0.2319 | 2.93 kB |
TinyMapperBenchmark | 10 | 1,144.4695 ns | 2.4484 ns | 9.4827 ns | 1,146.7102 ns | 0.0552 | 332 B |
ExpressMapperBencmark | 10 | 3,680.4923 ns | 26.6748 ns | 103.3111 ns | 3,641.2996 ns | – | 572 B |
AutoMapperBenchmark | 100 | 23,562.4691 ns | 144.8435 ns | 560.9764 ns | 23,328.6245 ns | – | 2.77 kB |
WithLinqBenchmark | 100 | 4,666.6503 ns | 44.7121 ns | 204.8966 ns | 4,572.4668 ns | 0.8128 | 3.14 kB |
LightMapperBenchmark | 100 | 58,951.8880 ns | 219.6671 ns | 850.7671 ns | 59,024.8561 ns | 7.5358 | 31.9 kB |
MyCustomMapperBenchmark | 100 | 164,057.4851 ns | 1,314.4040 ns | 5,090.6648 ns | 161,583.4743 ns | 4.2318 | 28.7 kB |
TinyMapperBenchmark | 100 | 9,730.2982 ns | 18.1397 ns | 70.2549 ns | 9,732.4868 ns | 0.5127 | 2.7 kB |
ExpressMapperBencmark | 100 | 35,127.0622 ns | 346.7948 ns | 1,511.6434 ns | 34,380.3184 ns | 0.2848 | 5.1 kB |
AutoMapperBenchmark | 1000 | 233,207.6502 ns | 366.3461 ns | 1,370.7415 ns | 233,078.4614 ns | – | 24.38 kB |
WithLinqBenchmark | 1000 | 40,787.0660 ns | 163.4213 ns | 632.9281 ns | 40,830.9575 ns | 7.6742 | 28.34 kB |
LightMapperBenchmark | 1000 | 565,351.7151 ns | 3,781.0435 ns | 14,643.9185 ns | 561,134.7381 ns | 81.5430 | 316.32 kB |
MyCustomMapperBenchmark | 1000 | 1,711,422.0753 ns | 11,243.6670 ns | 43,546.5350 ns | 1,691,468.5695 ns | 25.3906 | 284.34 kB |
TinyMapperBenchmark | 1000 | 100,242.1564 ns | 675.8215 ns | 2,617.4456 ns | 100,887.5784 ns | 2.5065 | 24.31 kB |
ExpressMapperBencmark | 1000 | 353,028.2847 ns | 2,301.8477 ns | 8,612.7254 ns | 351,594.5319 ns | 5.7943 | 48.32 kB |
4 Farklı renklere ayrılma sebebi 1,10,100 ve 1000 kayıt üzerinde yapılan işlemler sonucunda hangi Mapper türünün ne kadar sürdüğünü ayırtedebilmek için. 4 farklı değerdeki parametre sonucunda mapper türlerinin performans testi sıralamasının bozulmadığını görüyoruz. En performanslı çalışan yöntem Linq Select metodudur. Fakat her dto nesnesi için oluşacak mappingleri kendiniz manuel yapacağınız için bu mapping türünü gözardı edebiliriz bu işi Source, Target eşleşmesi yardımıyla otomatik yapan diğer 4 Mapper kütüphanesine bakacak olursak TinyMapper’in Linq’dan sonra en performanlı Mapper yöntemi olduğu görüyoruz. TinyMapper’i AutoMapper ve Onuda ExpressMapper takip ediyor. Kendi yöntemimiz olan Reflector ile mapper yerlerde sürünüyor 🙂 Ona hiç bakmaya gerek yok.
[fa class=”fa-github”] Bu makaledeki örneklerin tümüne ve diğer tüm C# projelerine buradaki github repository’sinden ulaşabilirsiniz.
hocam hakikaten müthiş bilgiler çok tecrübelisiniz belli yazılarınızı okumaya devam ediyorum
Teşekkürler.
paylaşım için teşekkürler. baya detaylı bir makale olmuş.
Ben teşekkür ederim beğenmiş olmanıza sevindim.
Hocam bilgiler için çok teşekkürler. Benim sitem … için inceleyerek bana tavsiye verir misiniz.
İstenilen öneri tam olarak nedir siteniz için bu konuda biraz açıklama yaparmısınız.