Skip to content

Commit cbc1e5f

Browse files
authored
Merge pull request #951 from MapsterMapper/development
Push to master branch - Release v10.0.8
2 parents fde27d6 + de64044 commit cbc1e5f

101 files changed

Lines changed: 2871 additions & 1075 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎README.md‎

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -141,21 +141,29 @@ public static class StudentMapper {
141141

142142
### Performance & Memory efficient
143143

144-
Mapster was designed to be efficient on both speed and memory. You could gain a 4x performance improvement whilst using only 1/3 of memory.
145-
And you could gain up to 12x faster performance with:
144+
Mapster was designed to be efficient on both speed and memory. The repository includes a benchmark project in [`src/Benchmark`](https://github.com/MapsterMapper/Mapster/tree/master/src/Benchmark) that compares the local Mapster build, its compiler variants, and other modern mapping libraries like AutoMapper, Mapperly, and Facet.
146145
147146
- [Roslyn Compiler](https://mapstermapper.github.io/Mapster/articles/packages/ExpressionDebugging.html)
148147
- [FEC](https://mapstermapper.github.io/Mapster/articles/packages/FastExpressionCompiler.html)
149148
- Code generation
150-
151-
| Method | Mean | StdDev | Error | Gen 0 | Gen 1 | Gen 2 | Allocated |
152-
|-------------------------- |----------:|----------:|----------:|-----------:|------:|------:|----------:|
153-
| 'Mapster 6.0.0' | 108.59 ms | 1.198 ms | 1.811 ms | 31000.0000 | - | - | 124.36 MB |
154-
| 'Mapster 6.0.0 (Roslyn)' | 38.45 ms | 0.494 ms | 0.830 ms | 31142.8571 | - | - | 124.36 MB |
155-
| 'Mapster 6.0.0 (FEC)' | 37.03 ms | 0.281 ms | 0.472 ms | 29642.8571 | - | - | 118.26 MB |
156-
| 'Mapster 6.0.0 (Codegen)' | 34.16 ms | 0.209 ms | 0.316 ms | 31133.3333 | - | - | 124.36 MB |
157-
| 'ExpressMapper 1.9.1' | 205.78 ms | 5.357 ms | 8.098 ms | 59000.0000 | - | - | 236.51 MB |
158-
| 'AutoMapper 10.0.0' | 420.97 ms | 23.266 ms | 35.174 ms | 87000.0000 | - | - | 350.95 MB |
149+
- Facet
150+
- Mapperly
151+
152+
The snapshot below shows the `FlatTypes` scenario (`Person -> PersonDTO`), a best-case DTO with simple property-to-property mapping and no nested objects or collections.
153+
154+
> [!NOTE]
155+
> More complex object shapes can change the relative results. See the [complete benchmark results](https://mapstermapper.github.io/Mapster/articles/benchmarks.html) for `ComplexTypes`, `RecursiveTypes`, and `TotalAllTypes`.
156+
157+
| Method | MapOperations | Mean | StdDev | Error | Ns/Map | Ratio | Gen0 | Allocated | Alloc Ratio | Bytes/Map |
158+
|------------------------------------ |-------------- |----------:|----------:|----------:|-------:|------:|-----:|----------:|------------:|----------:|
159+
| `Mapster 10.0.8` | 1000000 | 6.849 ms | 0.6851 ms | 1.0358 ms | 6.849 | 1.01 | 4781 | 76.29 MB | 1.00 | 80 |
160+
| `Mapster 10.0.8 (Roslyn)` | 1000000 | 6.579 ms | 0.2782 ms | 0.4206 ms | 6.579 | 0.97 | 4781 | 76.29 MB | 1.00 | 80 |
161+
| `Mapster 10.0.8 (FEC)` | 1000000 | 6.549 ms | 0.9130 ms | 1.3803 ms | 6.549 | 0.97 | 4781 | 76.29 MB | 1.00 | 80 |
162+
| `Mapster 10.0.8 (Codegen)` | 1000000 | 5.868 ms | 0.3266 ms | 0.5488 ms | 5.868 | 0.86 | 4781 | 76.29 MB | 1.00 | 80 |
163+
| `AutoMapper 14.0.0` | 1000000 | 29.645 ms | 0.8963 ms | 1.5062 ms | 29.645 | 4.37 | 4750 | 76.29 MB | 1.00 | 80 |
164+
| `Facet 6.5.5` | 1000000 | 7.801 ms | 1.0231 ms | 1.5467 ms | 7.801 | 1.15 | 8601 | 137.33 MB | 1.80 | 144 |
165+
| `Facet 6.5.5 (Compiled Projection)` | 1000000 | 5.508 ms | 0.7064 ms | 1.0679 ms | 5.508 | 0.81 | 4781 | 76.29 MB | 1.00 | 80 |
166+
| `Mapperly 4.3.1` | 1000000 | 6.521 ms | 0.8369 ms | 1.2652 ms | 6.521 | 0.96 | 4781 | 76.29 MB | 1.00 | 80 |
159167

160168
### Step into debugging
161169

‎docs/api/Reference.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ uid: Mapster.References
1111
| `src.Adapt<Dest>()` | Mapping to new type | [basic](xref:Mapster.Mapping.BasicUsages) |
1212
| `src.Adapt(dest)` | Mapping to existing object | [basic](xref:Mapster.Mapping.BasicUsages) |
1313
| `query.ProjectToType<Dest>()` | Mapping from queryable | [basic](xref:Mapster.Mapping.BasicUsages) |
14-
| | Convention & Data type support | [data types](xref:Mapster.Mapping.DataTypes) |
14+
| | Convention & Data type support | [data types](xref:Mapster.Mapping.DataTypes.Overview) |
1515

1616
### Mapper instance (for dependency injection)
1717

@@ -84,7 +84,7 @@ uid: Mapster.References
8484
| `BeforeMapping` | Add steps before mapping start | | [before-after](xref:Mapster.Settings.BeforeAfterMapping) |
8585
| `ConstructUsing` | Define how to create object | x | [constructor](xref:Mapster.Settings.ConstructorMapping) |
8686
| `EnableNonPublicMembers` | Mapping non-public properties | | [non-public](xref:Mapster.Settings.Custom.NonPublicMembers) |
87-
| `EnumMappingStrategy` | Choose whether mapping enum by value or by name | | [data types](xref:Mapster.Mapping.DataTypes) |
87+
| `EnumMappingStrategy` | Choose whether mapping enum by value or by name | | [data types](xref:Mapster.Mapping.DataTypes.Primitives) |
8888
| `Fork` | Add new settings without side effect on main config | x | [nested mapping](xref:Mapster.Configuration.NestedMapping) |
8989
| `GetMemberName` | Define how to resolve property name | x | [custom naming](xref:Mapster.Settings.Custom.NamingConvention) |
9090
| `Ignore` | Ignore specific properties | x | [ignore](xref:Mapster.Settings.Custom.IgnoringMembers) |

‎docs/articles/_Sidebar.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,8 @@
5454
* [Fluent API](https://github.com/MapsterMapper/Mapster/wiki/Fluent-API-Code-generation)
5555
* [Attributes](https://github.com/MapsterMapper/Mapster/wiki/Attribute-base-Code-generation)
5656
* [Interfaces](https://github.com/MapsterMapper/Mapster/wiki/Interface-base-Code-generation)
57+
58+
## Benchmarks
59+
60+
* [Benchmark results](https://mapstermapper.github.io/Mapster/articles/benchmarks.html)
61+
*

‎docs/articles/benchmarks.md‎

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
---
2+
uid: Mapster.Benchmarks
3+
title: "Benchmark results"
4+
---
5+
6+
Mapster includes a benchmark project in [`src/Benchmark`](https://github.com/MapsterMapper/Mapster/tree/master/src/Benchmark) that compares the local Mapster build with AutoMapper, Facet, and Mapperly across several object shapes.
7+
8+
This page is a May 2026 snapshot of those benchmarks. Treat the numbers as a comparison point for this environment and benchmark configuration rather than an absolute guarantee for every machine or application.
9+
10+
## How to read the tables
11+
12+
- `Mean` is the total time for one BenchmarkDotNet benchmark invocation. Each invocation runs a manual hot loop with `MapOperations = 1,000,000`.
13+
- `Ns/Map` normalizes `Mean` to the approximate cost of one logical mapping call.
14+
- `Allocated` is the memory allocated by one benchmark invocation.
15+
- `Bytes/Map` normalizes `Allocated` to one logical mapping call.
16+
- `Ratio` and `Alloc Ratio` are relative to the default Mapster benchmark in the same scenario.
17+
- `TotalAllTypes` runs three mapping scenarios in one benchmark invocation, so its `Ns/Map` and `Bytes/Map` values are divided by `MapOperations * 3`.
18+
19+
## Benchmark scenarios
20+
21+
### FlatTypes
22+
23+
`FlatTypes` maps `Person -> PersonDTO`. It is a flat DTO shape: simple property-to-property copy, no nested objects, and no collections. This scenario mostly highlights mapper call overhead, generated IL quality, and allocation rate for a best-case DTO.
24+
25+
### ComplexTypes
26+
27+
`ComplexTypes` maps `Customer -> CustomerDTO`. It includes nested address mapping, array/list shape changes, and a flattening rule (`AddressCity <- Address.City`). This scenario is useful for typical DTOs that combine nested objects, collections, and a small amount of custom member mapping.
28+
29+
### RecursiveTypes
30+
31+
`RecursiveTypes` maps `Foo -> FooDTO`. The type shape is self-recursive: a `Foo` can contain another `Foo`, an enumerable of `Foo`, and an array of `Foo`. The sample data does not intentionally create a back-reference cycle, but the mapping graph is deeper and allocates more nested DTOs than the flat or customer scenarios.
32+
33+
### TotalAllTypes
34+
35+
`TotalAllTypes` runs `FlatTypes`, `RecursiveTypes`, and `ComplexTypes` sequentially in one benchmark method. `Mean` is therefore the total batch time for all three scenarios; use `Ns/Map` and `Bytes/Map` for normalized per-map interpretation.
36+
37+
## Compared methods
38+
39+
- `Mapster` uses the default Mapster expression compiler.
40+
- `Mapster (Roslyn)` uses the Roslyn/debug-info compiler path.
41+
- `Mapster (FEC)` uses FastExpressionCompiler.
42+
- `Mapster (Codegen)` uses generated mapping code.
43+
- `AutoMapper`, `Facet`, and `Mapperly` are included as external comparison points.
44+
- `Facet (Compiled Projection)` uses Facet's compiled projection path, which can behave very differently from the constructor path depending on the object shape.
45+
46+
## Results
47+
48+
### FlatTypes
49+
50+
| Scenario | Method | MapOperations | Mean | StdDev | Error | Ns/Map | Ratio | Gen0 | Allocated | Alloc Ratio | Bytes/Map |
51+
|---------- |------------------------------------ |-------------- |----------:|----------:|----------:|-------:|------:|-----:|----------:|------------:|----------:|
52+
| FlatTypes | `Mapster 10.0.8` | 1000000 | 6.849 ms | 0.6851 ms | 1.0358 ms | 6.849 | 1.01 | 4781 | 76.29 MB | 1.00 | 80 |
53+
| FlatTypes | `Mapster 10.0.8 (Roslyn)` | 1000000 | 6.579 ms | 0.2782 ms | 0.4206 ms | 6.579 | 0.97 | 4781 | 76.29 MB | 1.00 | 80 |
54+
| FlatTypes | `Mapster 10.0.8 (FEC)` | 1000000 | 6.549 ms | 0.9130 ms | 1.3803 ms | 6.549 | 0.97 | 4781 | 76.29 MB | 1.00 | 80 |
55+
| FlatTypes | `Mapster 10.0.8 (Codegen)` | 1000000 | 5.868 ms | 0.3266 ms | 0.5488 ms | 5.868 | 0.86 | 4781 | 76.29 MB | 1.00 | 80 |
56+
| FlatTypes | `AutoMapper 14.0.0` | 1000000 | 29.645 ms | 0.8963 ms | 1.5062 ms | 29.645 | 4.37 | 4750 | 76.29 MB | 1.00 | 80 |
57+
| FlatTypes | `Facet 6.5.5` | 1000000 | 7.801 ms | 1.0231 ms | 1.5467 ms | 7.801 | 1.15 | 8601 | 137.33 MB | 1.80 | 144 |
58+
| FlatTypes | `Facet 6.5.5 (Compiled Projection)` | 1000000 | 5.508 ms | 0.7064 ms | 1.0679 ms | 5.508 | 0.81 | 4781 | 76.29 MB | 1.00 | 80 |
59+
| FlatTypes | `Mapperly 4.3.1` | 1000000 | 6.521 ms | 0.8369 ms | 1.2652 ms | 6.521 | 0.96 | 4781 | 76.29 MB | 1.00 | 80 |
60+
61+
### ComplexTypes
62+
63+
| Scenario | Method | MapOperations | Mean | StdDev | Error | Ns/Map | Ratio | Gen0 | Gen1 | Allocated | Alloc Ratio | Bytes/Map |
64+
|------------- |------------------------------------ |-------------- |----------:|----------:|----------:|--------:|------:|-------:|-----:|-----------:|------------:|----------:|
65+
| ComplexTypes | `Mapster 10.0.8` | 1000000 | 78.59 ms | 1.519 ms | 2.553 ms | 78.586 | 1.00 | 28111 | - | 450.13 MB | 1.00 | 472 |
66+
| ComplexTypes | `Mapster 10.0.8 (Roslyn)` | 1000000 | 53.48 ms | 1.760 ms | 2.958 ms | 53.475 | 0.68 | 25818 | - | 411.99 MB | 0.92 | 432 |
67+
| ComplexTypes | `Mapster 10.0.8 (FEC)` | 1000000 | 69.29 ms | 1.539 ms | 2.326 ms | 69.288 | 0.88 | 28200 | - | 450.13 MB | 1.00 | 472 |
68+
| ComplexTypes | `Mapster 10.0.8 (Codegen)` | 1000000 | 51.86 ms | 2.514 ms | 3.801 ms | 51.863 | 0.66 | 25750 | - | 411.99 MB | 0.92 | 432 |
69+
| ComplexTypes | `AutoMapper 14.0.0` | 1000000 | 112.27 ms | 4.516 ms | 7.590 ms | 112.270 | 1.43 | 29142 | - | 465.39 MB | 1.03 | 488 |
70+
| ComplexTypes | `Facet 6.5.5` | 1000000 | 446.72 ms | 51.706 ms | 78.172 ms | 446.725 | 5.69 | 175000 | 500 | 2792.36 MB | 6.20 | 2928 |
71+
| ComplexTypes | `Facet 6.5.5 (Compiled Projection)` | 1000000 | 678.87 ms | 42.646 ms | 64.474 ms | 678.868 | 8.64 | 44000 | - | 709.53 MB | 1.58 | 744 |
72+
| ComplexTypes | `Mapperly 4.3.1` | 1000000 | 52.81 ms | 1.134 ms | 1.714 ms | 52.812 | 0.67 | 25769 | - | 411.99 MB | 0.92 | 432 |
73+
74+
### RecursiveTypes
75+
76+
| Scenario | Method | MapOperations | Mean | StdDev | Error | Ns/Map | Ratio | Gen0 | Gen1 | Allocated | Alloc Ratio | Bytes/Map |
77+
|--------------- |------------------------------------ |-------------- |----------:|----------:|----------:|--------:|------:|-------:|-----:|-----------:|------------:|----------:|
78+
| RecursiveTypes | `Mapster 10.0.8` | 1000000 | 404.49 ms | 63.593 ms | 96.14 ms | 404.486 | 1.02 | 49000 | - | 793.46 MB | 1.00 | 832 |
79+
| RecursiveTypes | `Mapster 10.0.8 (Roslyn)` | 1000000 | 383.00 ms | 34.563 ms | 52.25 ms | 382.999 | 0.97 | 49000 | - | 793.46 MB | 1.00 | 832 |
80+
| RecursiveTypes | `Mapster 10.0.8 (FEC)` | 1000000 | 84.82 ms | 10.777 ms | 16.29 ms | 84.816 | 0.21 | 45875 | 125 | 732.42 MB | 0.92 | 768 |
81+
| RecursiveTypes | `Mapster 10.0.8 (Codegen)` | 1000000 | 82.81 ms | 11.104 ms | 16.79 ms | 82.806 | 0.21 | 49625 | 125 | 793.46 MB | 1.00 | 832 |
82+
| RecursiveTypes | `AutoMapper 14.0.0` | 1000000 | 585.42 ms | 88.660 ms | 134.04 ms | 585.425 | 1.48 | 168000 | 1000 | 2693.18 MB | 3.39 | 2824 |
83+
| RecursiveTypes | `Facet 6.5.5` | 1000000 | 302.24 ms | 9.444 ms | 18.06 ms | 302.241 | 0.76 | 150000 | 666 | 2395.63 MB | 3.02 | 2512 |
84+
| RecursiveTypes | `Facet 6.5.5 (Compiled Projection)` | 1000000 | 708.77 ms | 58.207 ms | 88.00 ms | 708.770 | 1.79 | 73000 | - | 1174.93 MB | 1.48 | 1232 |
85+
| RecursiveTypes | `Mapperly 4.3.1` | 1000000 | 116.05 ms | 16.639 ms | 25.16 ms | 116.055 | 0.29 | 68833 | 166 | 1098.63 MB | 1.38 | 1152 |
86+
87+
### TotalAllTypes
88+
89+
| Scenario | Method | MapOperations | Mean | StdDev | Error | Ns/Map | Ratio | Gen0 | Gen1 | Allocated | Alloc Ratio | Bytes/Map |
90+
|-------------- |------------------------------------ |-------------- |-----------:|---------:|----------:|-------:|------:|-------:|-----:|----------:|------------:|----------:|
91+
| TotalAllTypes | `Mapster 10.0.8` | 1000000 | 413.9 ms | 16.88 ms | 25.52 ms | 137.97 | 1.00 | 82000 | - | 1.29 GB | 1.00 | 461 |
92+
| TotalAllTypes | `Mapster 10.0.8 (Roslyn)` | 1000000 | 546.4 ms | 29.01 ms | 43.86 ms | 182.12 | 1.32 | 80000 | - | 1.25 GB | 0.97 | 448 |
93+
| TotalAllTypes | `Mapster 10.0.8 (FEC)` | 1000000 | 155.7 ms | 24.48 ms | 37.01 ms | 51.91 | 0.38 | 78800 | - | 1.23 GB | 0.95 | 440 |
94+
| TotalAllTypes | `Mapster 10.0.8 (Codegen)` | 1000000 | 117.4 ms | 8.56 ms | 12.94 ms | 39.14 | 0.28 | 80250 | - | 1.25 GB | 0.97 | 448 |
95+
| TotalAllTypes | `AutoMapper 14.0.0` | 1000000 | 681.4 ms | 76.25 ms | 115.27 ms | 227.13 | 1.65 | 202000 | 1000 | 3.16 GB | 2.45 | 1130 |
96+
| TotalAllTypes | `Facet 6.5.5` | 1000000 | 677.4 ms | 33.30 ms | 55.96 ms | 225.80 | 1.64 | 329000 | 1000 | 5.14 GB | 3.99 | 1840 |
97+
| TotalAllTypes | `Facet 6.5.5 (Compiled Projection)` | 1000000 | 1,337.4 ms | 42.95 ms | 64.94 ms | 445.81 | 3.24 | 122000 | - | 1.91 GB | 1.49 | 685 |
98+
| TotalAllTypes | `Mapperly 4.3.1` | 1000000 | 141.5 ms | 3.04 ms | 5.81 ms | 47.10 | 0.34 | 99250 | 250 | 1.55 GB | 1.20 | 554 |
99+
100+
## Interpretation notes
101+
102+
- The fastest method depends on the object shape. Flat DTOs mostly measure low-level call overhead; recursive graphs and collection-heavy DTOs shift the bottleneck toward nested object creation and collection mapping.
103+
- `Facet` constructor and compiled-projection paths are shown separately because they exercise different APIs and can trade CPU time for allocation behavior differently across scenarios.

‎docs/articles/configuration/Configuration.md‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ TypeAdapterConfig.GlobalSettings.ForDestinationType<IValidator>()
7070
.AfterMapping(dest => dest.Validate());
7171
```
7272

73-
NOTE: `ForDestinationType` above will always apply to all types assignable to `IValidator`. If destination class implements `IValidator`, it will also apply the `AfterMapping` config.
73+
>[!NOTE]
74+
>`ForDestinationType` above will always apply to all types assignable to `IValidator`. If destination class implements `IValidator`, it will also apply the `AfterMapping` config.
7475
7576
## Open generics
7677

@@ -80,3 +81,14 @@ If the mapping type is generic, you can create a setting by passing generic type
8081
TypeAdapterConfig.GlobalSettings.ForType(typeof(GenericPoco<>), typeof(GenericDto<>))
8182
.Map("value", "Value");
8283
```
84+
>[!Note]
85+
> Starting with Mapster version 10.0.8, [validation](xref:Mapster.Configuration.ValidationAndCompilation#validating-mappings) for type pairs settings containing open generic type is disabled.
86+
>If you require validation of such settings, you must register the configuration for at least one pair of closed generic types:
87+
>```csharp
88+
>TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true; // check all destination member mapping
89+
>TypeAdapterConfig.GlobalSettings.ForType(typeof(GenericPoco<>), typeof(GenericDto<>))
90+
> .Map("value", "Value");
91+
>
92+
> TypeAdapterConfig<GenericPoco<int>, GenericDto<string>>.NewConfig();
93+
> TypeAdapterConfig.GlobalSettings.Compile(); // validate
94+
>```

0 commit comments

Comments
 (0)