Merged
Conversation
.NET boxes Nullable<T> as T, stripping the wrapper. When CreateMap<T?, U>() is registered, the TypePair key uses Nullable<T> but source.GetType() at runtime returns T — lookup fails silently. Added fallback: after initial FrozenMaps miss, try Nullable<sourceType> as the source type in the key. Fixes ProductTrack.TrackMedia mapping where source is (FlacFile, OriginFile)? nullable tuple. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Guard against Nullable<Nullable<T>> crash in MapInternal — check Nullable.GetUnderlyingType before MakeGenericType 2. Add nullable boxing fallback to MapSlow — was only in MapInternal, Map<T?, U>() via generic path would miss the registered mapping 3. Add nullable boxing fallback to FindElementDelegate — collection elements with boxed nullable value types now resolve correctly 4. Same-type auto-map: skip abstract classes, interfaces, and types without parameterless constructors — fail fast instead of runtime MissingMethodException Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
52ce491 to
06116b8
Compare
Contributor
📊 Benchmark Results
🔵 Flat Mapping — 10-property object
🟡 Flattening — 2 nested objects → 8 flat properties
🟣 Deep Mapping — 2 nested address objects
🟢 Complex Mapping — nested object + collection
🟠 Collection — 100-item
|
| Method | Mean | Error | StdDev | Min | Median | Max | Ratio | RatioSD | Rank | Gen0 | Gen1 | Allocated | Alloc Ratio |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Manual | 1.692 μs | 0.4062 μs | 0.0223 μs | 1.671 μs | 1.690 μs | 1.716 μs | 1.00 | 0.02 | 1 | 0.5283 | 0.0172 | 8.65 KB | 1.00 |
| EggMapper | 1.786 μs | 0.3209 μs | 0.0176 μs | 1.766 μs | 1.791 μs | 1.800 μs | 1.06 | 0.01 | 1 | 0.5283 | 0.0172 | 8.65 KB | 1.00 |
| AutoMapper | 2.475 μs | 1.5995 μs | 0.0877 μs | 2.378 μs | 2.496 μs | 2.550 μs | 1.46 | 0.05 | 2 | 0.6065 | 0.0191 | 9.95 KB | 1.15 |
| Mapster | 1.771 μs | 0.7654 μs | 0.0420 μs | 1.727 μs | 1.777 μs | 1.810 μs | 1.05 | 0.02 | 1 | 0.5283 | 0.0172 | 8.65 KB | 1.00 |
| MapperlyMap | 1.765 μs | 0.2068 μs | 0.0113 μs | 1.755 μs | 1.763 μs | 1.777 μs | 1.04 | 0.01 | 1 | 0.5283 | 0.0172 | 8.65 KB | 1.00 |
| AgileMapper | 2.501 μs | 0.3376 μs | 0.0185 μs | 2.485 μs | 2.498 μs | 2.521 μs | 1.48 | 0.02 | 2 | 0.5417 | 0.0153 | 8.91 KB | 1.03 |
🟠 Collection — 100-item List<T>
| Method | Mean | Error | StdDev | Min | Median | Max | Ratio | RatioSD | Rank | Gen0 | Gen1 | Allocated | Alloc Ratio |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Manual | 5.174 μs | 0.1965 μs | 0.0108 μs | 5.164 μs | 5.174 μs | 5.185 μs | 1.00 | 0.00 | 1 | 1.6708 | 0.0916 | 27.4 KB | 1.00 |
| EggMapper | 5.657 μs | 0.7458 μs | 0.0409 μs | 5.615 μs | 5.659 μs | 5.696 μs | 1.09 | 0.01 | 1 | 1.6708 | 0.0916 | 27.4 KB | 1.00 |
| AutoMapper | 6.517 μs | 0.8210 μs | 0.0450 μs | 6.469 μs | 6.524 μs | 6.558 μs | 1.26 | 0.01 | 1 | 1.7548 | 0.1068 | 28.7 KB | 1.05 |
| Mapster | 5.770 μs | 1.7767 μs | 0.0974 μs | 5.664 μs | 5.788 μs | 5.857 μs | 1.12 | 0.02 | 1 | 1.6708 | 0.0916 | 27.4 KB | 1.00 |
| MapperlyMap | 5.359 μs | 1.4822 μs | 0.0812 μs | 5.279 μs | 5.358 μs | 5.441 μs | 1.04 | 0.01 | 1 | 1.6785 | 0.0992 | 27.42 KB | 1.00 |
| AgileMapper | 5.225 μs | 0.8870 μs | 0.0486 μs | 5.190 μs | 5.206 μs | 5.281 μs | 1.01 | 0.01 | 1 | 1.0223 | 0.0610 | 16.72 KB | 0.61 |
🟠 Collection — 100-item List<T>
| Method | Mean | Error | StdDev | Min | Median | Max | Ratio | RatioSD | Rank | Gen0 | Gen1 | Allocated | Alloc Ratio |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Manual | 17.07 μs | 12.899 μs | 0.707 μs | 16.41 μs | 16.98 μs | 17.81 μs | 1.00 | 0.05 | 1 | 5.2490 | 1.3123 | 85.99 KB | 1.00 |
| EggMapper | 17.15 μs | 3.187 μs | 0.175 μs | 17.01 μs | 17.10 μs | 17.35 μs | 1.01 | 0.04 | 1 | 5.2490 | 1.3123 | 85.99 KB | 1.00 |
| AutoMapper | 21.74 μs | 10.308 μs | 0.565 μs | 21.41 μs | 21.42 μs | 22.39 μs | 1.28 | 0.05 | 1 | 5.7678 | 1.4343 | 94.34 KB | 1.10 |
| Mapster | 17.55 μs | 1.984 μs | 0.109 μs | 17.43 μs | 17.56 μs | 17.65 μs | 1.03 | 0.04 | 1 | 5.2490 | 1.3123 | 85.99 KB | 1.00 |
| MapperlyMap | 18.59 μs | 6.689 μs | 0.367 μs | 18.27 μs | 18.52 μs | 18.99 μs | 1.09 | 0.04 | 1 | 5.2490 | 1.2817 | 86.02 KB | 1.00 |
| AgileMapper | 20.05 μs | 2.982 μs | 0.163 μs | 19.88 μs | 20.06 μs | 20.21 μs | 1.18 | 0.04 | 1 | 5.2795 | 1.3123 | 86.25 KB | 1.00 |
⚪ Startup / Configuration time
| Method | Mean | Error | StdDev | Min | Median | Max | Ratio | RatioSD | Rank | Gen0 | Gen1 | Allocated | Alloc Ratio |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| EggMapperStartup | 1,284.509 μs | 2,022.2581 μs | 110.8468 μs | 1,160.353 μs | 1,319.643 μs | 1,373.532 μs | 1.005 | 0.11 | 3 | 3.9063 | 1.9531 | 95.29 KB | 1.00 |
| AutoMapperStartup | 395.170 μs | 718.5274 μs | 39.3849 μs | 353.748 μs | 399.621 μs | 432.140 μs | 0.309 | 0.04 | 2 | 5.8594 | - | 104.52 KB | 1.10 |
| MapsterStartup | 2.497 μs | 0.4115 μs | 0.0226 μs | 2.478 μs | 2.490 μs | 2.522 μs | 0.002 | 0.00 | 1 | 0.7019 | 0.0267 | 11.51 KB | 0.12 |
EggMapper.Benchmarks.ColdStartBenchmark-report-github
| Method | Mean | Error | StdDev | Min | Median | Max | Ratio | RatioSD | Rank | Gen0 | Gen1 | Allocated | Alloc Ratio |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| EggMapper | 1.319 ms | 0.8678 ms | 0.0476 ms | 1.264 ms | 1.344 ms | 1.349 ms | 1.00 | 0.04 | 1 | 5.8594 | 3.9063 | 95.8 KB | 1.00 |
| AutoMapper | 4.037 ms | 9.7928 ms | 0.5368 ms | 3.536 ms | 3.970 ms | 4.604 ms | 3.06 | 0.37 | 2 | 15.6250 | 7.8125 | 309.85 KB | 3.23 |
| Mapster | 4.318 ms | 9.9590 ms | 0.5459 ms | 3.714 ms | 4.461 ms | 4.777 ms | 3.28 | 0.37 | 2 | 39.0625 | 15.6250 | 757.88 KB | 7.91 |
📝 Notes
- Each benchmark class is decorated with
[MemoryDiagnoser]and[RankColumn]. - The global config (see
src/EggMapper.Benchmarks/Program.cs) addsMin,Median, andMaxcolumns. - Manual is the hand-written baseline (ratio = 1.00). A ratio < 1 means faster than manual.
- Benchmarks run on GitHub-hosted runners — absolute times may vary between runs; focus on Ratio for comparisons.
- To reproduce locally:
cd src/EggMapper.Benchmarks dotnet run --configuration Release -- --filter '*'
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Changes
Checklist
dotnet test --configuration Release)