Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to specify benchmark description in outputs #2386

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions samples/BenchmarkDotNet.Samples/IntroRenameTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using BenchmarkDotNet.Attributes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace BenchmarkDotNet.Samples
{
[BenchmarkDescription("Used to be 'IntroRenameTest', now is 'My Renamed Test'")]
public class IntroRenameTest
{
// And define a method with the Benchmark attribute
[Benchmark]
public void Sleep() => Thread.Sleep(10);

// You can write a description for your method.
[Benchmark(Description = "Thread.Sleep(10)")]
public void SleepWithDescription() => Thread.Sleep(10);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace BenchmarkDotNet.Attributes
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class BenchmarkDescriptionAttribute : Attribute
timcassell marked this conversation as resolved.
Show resolved Hide resolved
{
public BenchmarkDescriptionAttribute(){ }
public BenchmarkDescriptionAttribute(string description)
=> Description = description;

public string Description { get; set; }
}
}
2 changes: 1 addition & 1 deletion src/BenchmarkDotNet/Columns/TargetMethodColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace BenchmarkDotNet.Columns
public class TargetMethodColumn : IColumn
{
public static readonly IColumn Namespace = new TargetMethodColumn(Column.Namespace, benchmark => benchmark.Descriptor.Type.Namespace);
public static readonly IColumn Type = new TargetMethodColumn(Column.Type, benchmark => benchmark.Descriptor.Type.GetDisplayName());
public static readonly IColumn Type = new TargetMethodColumn(Column.Type, benchmark => benchmark.Descriptor.TypeInfo);
public static readonly IColumn Method = new TargetMethodColumn(Column.Method, benchmark => benchmark.Descriptor.WorkloadMethodDisplayInfo, true);

private readonly Func<BenchmarkCase, string> valueProvider;
Expand Down
4 changes: 4 additions & 0 deletions src/BenchmarkDotNet/Extensions/ReflectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ private static IEnumerable<string> GetNestedTypeNames(Type type, bool includeGen
/// </summary>
private static string GetDisplayName(this TypeInfo typeInfo)
{
var customAttr = typeInfo.GetCustomAttribute<BenchmarkDescriptionAttribute>();
if (customAttr != null)
return customAttr.Description;

if (!typeInfo.IsGenericType)
return typeInfo.Name;

Expand Down
8 changes: 7 additions & 1 deletion src/BenchmarkDotNet/Running/BenchmarkConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ private static ImmutableConfig GetFullMethodConfig(MethodInfo method, ImmutableC
GetTargetedMatchingMethod(methodInfo, iterationSetupMethods),
GetTargetedMatchingMethod(methodInfo, iterationCleanupMethods),
methodInfo.ResolveAttribute<BenchmarkAttribute>(),
methodInfo.ResolveAttribute<BenchmarkDescriptionAttribute>(),
targetMethods,
config));
}
Expand Down Expand Up @@ -155,18 +156,23 @@ private static MethodInfo GetTargetedMatchingMethod(MethodInfo benchmarkMethod,
MethodInfo iterationSetupMethod,
MethodInfo iterationCleanupMethod,
BenchmarkAttribute attr,
BenchmarkDescriptionAttribute? methodDescription,
MethodInfo[] targetMethods,
IConfig config)
{
var categoryDiscoverer = config.CategoryDiscoverer ?? DefaultCategoryDiscoverer.Instance;
if (attr?.Description != null && methodDescription?.Description != null)
throw new InvalidOperationException($"Benchmark {methodInfo.Name} has 2 descriptions from different attributes");
string description = attr?.Description;
description ??= methodDescription?.Description;
var target = new Descriptor(
type,
methodInfo,
globalSetupMethod,
globalCleanupMethod,
iterationSetupMethod,
iterationCleanupMethod,
attr.Description,
description,
baseline: attr.Baseline,
categories: categoryDiscoverer.GetCategories(methodInfo),
operationsPerInvoke: attr.OperationsPerInvoke,
Expand Down
2 changes: 2 additions & 0 deletions src/BenchmarkDotNet/Running/Descriptor.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Extensions;
using BenchmarkDotNet.Helpers;
using BenchmarkDotNet.Portability;
Expand Down
14 changes: 14 additions & 0 deletions tests/BenchmarkDotNet.Tests/Exporters/JobBaseline_WithAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using BenchmarkDotNet.Attributes;

namespace BenchmarkDotNet.Tests.Exporters
{
[RankColumn, LogicalGroupColumn, BaselineColumn]
[SimpleJob(id: "Job1"), SimpleJob(id: "Job2")]
[BenchmarkDescription(Description = "MyRenamedTestCase")]
public class JobBaseline_MethodsJobs_WithAttribute
{
[Benchmark] public void Base() { }
[Benchmark] public void Foo() { }
[Benchmark] public void Bar() { }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
using VerifyXunit;
using Xunit;

using static BenchmarkDotNet.Tests.Exporters.MarkdownExporterVerifyTests.BaselinesBenchmarks;

namespace BenchmarkDotNet.Tests.Exporters
{
[Collection("VerifyTests")]
Expand Down Expand Up @@ -57,8 +59,31 @@ public Task GroupExporterTest(Type benchmarkType)
var settings = VerifySettingsFactory.Create();
settings.UseTextForParameters(benchmarkType.Name);
return Verifier.Verify(logger.GetLog(), settings);
}

}

[Fact]
public Task GroupExporterMultipleTypesTest()
{
Type[] benchmarkTypes = new Type[] { typeof(JobBaseline_MethodsJobs_WithAttribute), typeof(JobBaseline_MethodsJobs) };
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
var logger = new AccumulationLogger();
logger.WriteLine("=== " + benchmarkTypes + " ===");

var exporter = MarkdownExporter.Mock;
var summary = MockFactory.CreateSummary(benchmarkTypes);
exporter.ExportToLog(summary, logger);

var validator = BaselineValidator.FailOnError;
var errors = validator.Validate(new ValidationParameters(summary.BenchmarksCases, summary.BenchmarksCases.First().Config)).ToList();
logger.WriteLine();
logger.WriteLine("Errors: " + errors.Count);
foreach (var error in errors)
logger.WriteLineError("* " + error.Message);

var settings = VerifySettingsFactory.Create();
return Verifier.Verify(logger.GetLog(), settings);
}

public void Dispose() => Thread.CurrentThread.CurrentCulture = initCulture;

[SuppressMessage("ReSharper", "InconsistentNaming")]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
=== JobBaseline_RenameJob_MethodsJobs ===

BenchmarkDotNet=v0.10.x-mock, OS=Microsoft Windows NT 10.0.x.mock, VM=Hyper-V
MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores
Frequency=2531248 Hz, Resolution=395.0620 ns, Timer=TSC
[Host] : Clr 4.0.x.mock, 64mock RyuJIT-v4.6.x.mock CONFIGURATION
Job1 : extra output line
Job2 : extra output line


Method | Job | Mean | Error | StdDev | Rank | LogicalGroup | Baseline |
------- |----- |---------:|--------:|--------:|-----:|------------- |--------- |
Base | Job1 | 102.0 ns | 6.09 ns | 1.58 ns | 1 | * | No |
Foo | Job1 | 202.0 ns | 6.09 ns | 1.58 ns | 2 | * | No |
Bar | Job1 | 302.0 ns | 6.09 ns | 1.58 ns | 3 | * | No |
Base | Job2 | 402.0 ns | 6.09 ns | 1.58 ns | 4 | * | No |
Foo | Job2 | 502.0 ns | 6.09 ns | 1.58 ns | 5 | * | No |
Bar | Job2 | 602.0 ns | 6.09 ns | 1.58 ns | 6 | * | No |

Errors: 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
=== System.Type[] ===

BenchmarkDotNet v0.10.x-mock, Microsoft Windows NT 10.0.x.mock (Hyper-V)
MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores
Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC
[Host] : Clr 4.0.x.mock, 64mock RyuJIT-v4.6.x.mock CONFIGURATION
Job1 : extra output line
Job2 : extra output line


Type | Method | Job | Mean | Error | StdDev | Ratio | RatioSD | Rank | LogicalGroup | Baseline |
------------------------ |------- |----- |-----------:|--------:|--------:|------:|--------:|-----:|----------------------------- |--------- |
MyRenamedTestCase | Base | Job1 | 102.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | MyRenamedTestCase.Base | No |
MyRenamedTestCase | Base | Job2 | 402.0 ns | 6.09 ns | 1.58 ns | ? | ? | 2 | MyRenamedTestCase.Base | No |
| | | | | | | | | | |
MyRenamedTestCase | Foo | Job1 | 202.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | MyRenamedTestCase.Foo | No |
MyRenamedTestCase | Foo | Job2 | 502.0 ns | 6.09 ns | 1.58 ns | ? | ? | 2 | MyRenamedTestCase.Foo | No |
| | | | | | | | | | |
MyRenamedTestCase | Bar | Job1 | 302.0 ns | 6.09 ns | 1.58 ns | ? | ? | 1 | MyRenamedTestCase.Bar | No |
MyRenamedTestCase | Bar | Job2 | 602.0 ns | 6.09 ns | 1.58 ns | ? | ? | 2 | MyRenamedTestCase.Bar | No |
| | | | | | | | | | |
JobBaseline_MethodsJobs | Base | Job1 | 702.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | JobBaseline_MethodsJobs.Base | Yes |
JobBaseline_MethodsJobs | Base | Job2 | 1,002.0 ns | 6.09 ns | 1.58 ns | 1.43 | 0.00 | 2 | JobBaseline_MethodsJobs.Base | No |
| | | | | | | | | | |
JobBaseline_MethodsJobs | Foo | Job1 | 802.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | JobBaseline_MethodsJobs.Foo | Yes |
JobBaseline_MethodsJobs | Foo | Job2 | 1,102.0 ns | 6.09 ns | 1.58 ns | 1.37 | 0.00 | 2 | JobBaseline_MethodsJobs.Foo | No |
| | | | | | | | | | |
JobBaseline_MethodsJobs | Bar | Job1 | 902.0 ns | 6.09 ns | 1.58 ns | 1.00 | 0.00 | 1 | JobBaseline_MethodsJobs.Bar | Yes |
JobBaseline_MethodsJobs | Bar | Job2 | 1,202.0 ns | 6.09 ns | 1.58 ns | 1.33 | 0.00 | 2 | JobBaseline_MethodsJobs.Bar | No |

Errors: 0
33 changes: 17 additions & 16 deletions tests/BenchmarkDotNet.Tests/Mocks/MockFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,23 @@
namespace BenchmarkDotNet.Tests.Mocks
{
public static class MockFactory
{
public static Summary CreateSummary(Type benchmarkType)
{
var runInfo = BenchmarkConverter.TypeToBenchmarks(benchmarkType);
return new Summary(
"MockSummary",
runInfo.BenchmarksCases.Select((benchmark, index) => CreateReport(benchmark, 5, (index + 1) * 100)).ToImmutableArray(),
new HostEnvironmentInfoBuilder().WithoutDotNetSdkVersion().Build(),
string.Empty,
string.Empty,
TimeSpan.FromMinutes(1),
TestCultureInfo.Instance,
ImmutableArray<ValidationError>.Empty,
ImmutableArray<IColumnHidingRule>.Empty);
}

{
public static Summary CreateSummary(Type benchmarkType) => CreateSummary(new Type[] { benchmarkType });
public static Summary CreateSummary(Type[] benchmarkTypes)
{
var runInfos = benchmarkTypes.Select(benchmarkType => BenchmarkConverter.TypeToBenchmarks(benchmarkType));
return new Summary(
"MockSummary",
runInfos.SelectMany(runInfo => runInfo.BenchmarksCases).Select((benchmark, index) => CreateReport(benchmark, 5, (index + 1) * 100)).ToImmutableArray(),
new HostEnvironmentInfoBuilder().WithoutDotNetSdkVersion().Build(),
string.Empty,
string.Empty,
TimeSpan.FromMinutes(1),
TestCultureInfo.Instance,
ImmutableArray<ValidationError>.Empty,
ImmutableArray<IColumnHidingRule>.Empty);
}

public static Summary CreateSummary(IConfig config) => new Summary(
"MockSummary",
CreateReports(config),
Expand Down
54 changes: 54 additions & 0 deletions tests/BenchmarkDotNet.Tests/Running/BenchmarkConverterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,29 @@ public void MethodDeclarationOrderIsPreserved()
Assert.Equal(nameof(BAC.C), info.BenchmarksCases[2].Descriptor.WorkloadMethod.Name);
}
}
[Fact]
public void OnlyOneOfAttributeDescriptionIsUsed()
{
Assert.Throws<InvalidOperationException>(() => BenchmarkConverter.TypeToBenchmarks(typeof(BothAttributeDescriptionTests)));
}
[Fact]
public void DescriptorDescriptionNameOverride()
{
var description = BenchmarkConverter.TypeToBenchmarks(typeof(MethodDescriptionOverrideTests));

Assert.Equal("VoidTest", description.BenchmarksCases[0].Descriptor.WorkloadMethodDisplayInfo);
Assert.Equal("\'from Benchmark\'", description.BenchmarksCases[1].Descriptor.WorkloadMethodDisplayInfo);
Assert.Equal("OverrideFromAttribute", description.BenchmarksCases[2].Descriptor.WorkloadMethodDisplayInfo);
}
[Fact]
public void ClassDescriptorDescriptionNameOverride()
{
var description = BenchmarkConverter.TypeToBenchmarks(typeof(ClassDescriptionOverrideTests));

Assert.Equal("FromClassDescription", description.BenchmarksCases[0].Descriptor.WorkloadMethodDisplayInfo);
Assert.Equal("\'from Benchmark\'", description.BenchmarksCases[1].Descriptor.WorkloadMethodDisplayInfo);
Assert.Equal("OverrideFromAttribute", description.BenchmarksCases[2].Descriptor.WorkloadMethodDisplayInfo);
}

public class BAC
{
Expand Down Expand Up @@ -278,5 +301,36 @@ public class PrivateIterationCleanup
[IterationCleanup] private void X() { }
[Benchmark] public void A() { }
}
public class BothAttributeDescriptionTests
{
[Benchmark(Description = "BenchmarkAttributeDescription")]
[BenchmarkDescription("BenchmarkDescriptionAttributeDescription")]
public void BothDescriptionsUsed() { }
}
public class MethodDescriptionOverrideTests
{
[Benchmark]
public void VoidTest() { }

[Benchmark(Description = "from Benchmark")]
public void BenchmarkAttributeOverride() {}

[Benchmark]
[BenchmarkDescription("OverrideFromAttribute")]
public void BenchmarkDescriptionAttributeOverride() { }
}
[BenchmarkDescription("FromClassDescription")]
public class ClassDescriptionOverrideTests
{
[Benchmark]
public void VoidTest() { }

[Benchmark(Description = "from Benchmark")]
public void ClassBenchmarkAttributeOverride() { }

[Benchmark]
[BenchmarkDescription("OverrideFromAttribute")]
public void ClassBenchmarkDescriptionAttributeOverride() { }
}
}
}
Loading