Skip to content

Commit

Permalink
Merge pull request #953 from MattKotsenas/refactor/attribute-allocs
Browse files Browse the repository at this point in the history
Use Attribute.GetCustomAttributes to reduce allocations / improve performance
  • Loading branch information
EdwardCooke authored Aug 31, 2024
2 parents 2e317a5 + a561c02 commit 6d89d8d
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 36 deletions.
29 changes: 23 additions & 6 deletions YamlDotNet.Benchmark/Program.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
using System.Diagnostics;
// This file is part of YamlDotNet - A .NET library for YAML.
// Copyright (c) Antoine Aubry and contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

using BenchmarkDotNet.Running;
using YamlDotNet.Benchmark;
using YamlDotNet.Serialization;

var serializer = new SerializerBuilder().JsonCompatible().Build();
var v = new { nan = float.NaN, inf = float.NegativeInfinity, posinf = float.PositiveInfinity, max = float.MaxValue, min = float.MinValue, good = .1234f, good1 = 1, good2 = -.1234, good3= -1 };
var yaml = serializer.Serialize(v);
Console.WriteLine(yaml);
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
54 changes: 54 additions & 0 deletions YamlDotNet.Benchmark/SerializationBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// This file is part of YamlDotNet - A .NET library for YAML.
// Copyright (c) Antoine Aubry and contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

using System.Text;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using YamlDotNet.Serialization;

namespace YamlDotNet.Benchmark;

[MemoryDiagnoser]
[MediumRunJob(RuntimeMoniker.Net80)]
[MediumRunJob(RuntimeMoniker.Net47)]
public class SerializationBenchmarks
{
public class SampleRecord
{
public SampleRecord(string name, string description)
{
Name = name;
Description = description;
}

public string Name { get; private set; }
public string Description { get; private set; }
}

private readonly IReadOnlyCollection<SampleRecord> configs = Enumerable.Range(0, 10_000).Select(i => new SampleRecord("MyName", "MyDescription")).ToList();
private readonly ISerializer serializer = new SerializerBuilder().DisableAliases().Build();

[Benchmark]
public string Serializer()
{
return serializer.Serialize(configs);
}
}
8 changes: 5 additions & 3 deletions YamlDotNet.Benchmark/YamlDotNet.Benchmark.csproj
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFrameworks>net8.0;net47</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>10.0</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
<PackageReference Include="System.Reflection.Metadata" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
29 changes: 2 additions & 27 deletions YamlDotNet/ReflectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -279,34 +279,9 @@ public static bool IsInstanceOf(this Type type, object o)

public static Attribute[] GetAllCustomAttributes<TAttribute>(this PropertyInfo member)
{
// IMemberInfo.GetCustomAttributes ignores it's "inherit" parameter for properties,
// and the suggested replacement (Attribute.GetCustomAttributes) is not available
// on netstandard1.3
var result = new List<Attribute>();
var type = member.DeclaringType;
var name = member.Name;

while (type != null)
{
var property = type.GetPublicProperty(name);

if (property != null)
{
result.AddRange(property.GetCustomAttributes(typeof(TAttribute)));

if ((property.GetGetMethod()?.IsHideBySig == true)
|| (property.GetSetMethod()?.IsHideBySig == true))
{
// Don't continue up the hierarchy.
break;
}
}

type = type.BaseType();
}

return result.ToArray();
return Attribute.GetCustomAttributes(member, typeof(TAttribute), inherit: true);
}

private static readonly ConcurrentDictionary<Type, bool> typesHaveNullContext = new ConcurrentDictionary<Type, bool>();
public static bool AcceptsNull(this MemberInfo member)
{
Expand Down

0 comments on commit 6d89d8d

Please sign in to comment.