1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-14 17:52:56 +08:00

Merge pull request #16508 from peppy/optimise-set-detach

Create separate automapper configurations to reduce cyclic detach overhead
This commit is contained in:
Dan Balasescu 2022-01-19 13:20:25 +09:00 committed by GitHub
commit d663fcd045
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -74,21 +74,10 @@ namespace osu.Game.Database
private static readonly IMapper mapper = new MapperConfiguration(c =>
{
c.ShouldMapField = fi => false;
applyCommonConfiguration(c);
// This is specifically to avoid mapping explicit interface implementations.
// If we want to limit this further, we can avoid mapping properties with no setter that are not IList<>.
// Takes a bit of effort to determine whether this is the case though, see https://stackoverflow.com/questions/951536/how-do-i-tell-whether-a-type-implements-ilist
c.ShouldMapProperty = pi => pi.GetMethod?.IsPublic == true;
c.CreateMap<RealmKeyBinding, RealmKeyBinding>();
c.CreateMap<BeatmapMetadata, BeatmapMetadata>();
c.CreateMap<BeatmapDifficulty, BeatmapDifficulty>();
c.CreateMap<RulesetInfo, RulesetInfo>();
c.CreateMap<ScoreInfo, ScoreInfo>();
c.CreateMap<RealmUser, RealmUser>();
c.CreateMap<RealmFile, RealmFile>();
c.CreateMap<RealmNamedFileUsage, RealmNamedFileUsage>();
// This can be further optimised to reduce cyclic retrievals, similar to the optimised set mapper below.
// Only hasn't been done yet as we detach at the point of BeatmapInfo less often.
c.CreateMap<BeatmapInfo, BeatmapInfo>()
.MaxDepth(2)
.AfterMap((s, d) =>
@ -102,13 +91,29 @@ namespace osu.Game.Database
}
}
});
c.CreateMap<BeatmapSetInfo, BeatmapSetInfo>()
.MaxDepth(2)
.AfterMap((s, d) =>
{
foreach (var beatmap in d.Beatmaps)
beatmap.BeatmapSet = d;
});
}).CreateMapper();
/// <summary>
/// A slightly optimised mapper that avoids double-fetches in cyclic reference.
/// </summary>
private static readonly IMapper beatmap_set_mapper = new MapperConfiguration(c =>
{
applyCommonConfiguration(c);
c.CreateMap<BeatmapInfo, BeatmapInfo>()
.MaxDepth(1)
// This is not required as it will be populated in the `AfterMap` call from the `BeatmapInfo`'s parent.
.ForMember(b => b.BeatmapSet, cc => cc.Ignore());
}).CreateMapper();
private static void applyCommonConfiguration(IMapperConfigurationExpression c)
{
c.ShouldMapField = fi => false;
// This is specifically to avoid mapping explicit interface implementations.
// If we want to limit this further, we can avoid mapping properties with no setter that are not IList<>.
// Takes a bit of effort to determine whether this is the case though, see https://stackoverflow.com/questions/951536/how-do-i-tell-whether-a-type-implements-ilist
c.ShouldMapProperty = pi => pi.GetMethod?.IsPublic == true;
c.Internal().ForAllMaps((typeMap, expression) =>
{
@ -118,7 +123,23 @@ namespace osu.Game.Database
m.Ignore();
});
});
}).CreateMapper();
c.CreateMap<RealmKeyBinding, RealmKeyBinding>();
c.CreateMap<BeatmapMetadata, BeatmapMetadata>();
c.CreateMap<BeatmapDifficulty, BeatmapDifficulty>();
c.CreateMap<RulesetInfo, RulesetInfo>();
c.CreateMap<ScoreInfo, ScoreInfo>();
c.CreateMap<RealmUser, RealmUser>();
c.CreateMap<RealmFile, RealmFile>();
c.CreateMap<RealmNamedFileUsage, RealmNamedFileUsage>();
c.CreateMap<BeatmapSetInfo, BeatmapSetInfo>()
.MaxDepth(2)
.AfterMap((s, d) =>
{
foreach (var beatmap in d.Beatmaps)
beatmap.BeatmapSet = d;
});
}
/// <summary>
/// Create a detached copy of the each item in the collection.
@ -153,6 +174,9 @@ namespace osu.Game.Database
if (!item.IsManaged)
return item;
if (item is BeatmapSetInfo)
return beatmap_set_mapper.Map<T>(item);
return mapper.Map<T>(item);
}