Skip to content

Commit

Permalink
Add categories
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreyAkinshin committed May 13, 2017
1 parent fe3032b commit 2e7427c
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 7 deletions.
21 changes: 21 additions & 0 deletions samples/BenchmarkDotNet.Samples/Intro/IntroCategories.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Threading;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Columns;
using BenchmarkDotNet.Attributes.Jobs;

namespace BenchmarkDotNet.Samples.Intro
{
[DryJob]
[CategoriesColumn]
[BenchmarkCategory("ClassCategory")]
public class IntroCategories
{
[Benchmark]
[BenchmarkCategory("CategoryA")]
public void A() => Thread.Sleep(10);

[Benchmark]
[BenchmarkCategory("CategoryB")]
public void B() => Thread.Sleep(10);
}
}
18 changes: 18 additions & 0 deletions src/BenchmarkDotNet.Core/Attributes/BenchmarkCategoryAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;

namespace BenchmarkDotNet.Attributes
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)]
public class BenchmarkCategoryAttribute : Attribute
{
public string[] Categories { get; }

// CLS-Compliant Code requires a constuctor without an array in the argument list
protected BenchmarkCategoryAttribute() { }

public BenchmarkCategoryAttribute(params string[] categories)
{
Categories = categories;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using BenchmarkDotNet.Columns;

namespace BenchmarkDotNet.Attributes.Columns
{
public class CategoriesColumnAttribute : ColumnConfigBaseAttribute
{
public CategoriesColumnAttribute() : base(CategoriesColumn.Default) { }
}
}
25 changes: 25 additions & 0 deletions src/BenchmarkDotNet.Core/Columns/CategoriesColumn.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Linq;
using BenchmarkDotNet.Extensions;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;

namespace BenchmarkDotNet.Columns
{
public class CategoriesColumn : IColumn
{
public static readonly IColumn Default = new CategoriesColumn();

public string Id => nameof(CategoriesColumn);
public string ColumnName => "Categories";
public string GetValue(Summary summary, Benchmark benchmark) => string.Join(",", benchmark.Target.Categories);
public string GetValue(Summary summary, Benchmark benchmark, ISummaryStyle style) => GetValue(summary, benchmark);
public bool IsDefault(Summary summary, Benchmark benchmark) => false;
public bool IsAvailable(Summary summary) => summary.Benchmarks.Any(b => !b.Target.Categories.IsEmpty());
public bool AlwaysShow => false;
public ColumnCategory Category => ColumnCategory.Job;
public int PriorityInCategory => 100;
public bool IsNumeric => false;
public UnitType UnitType => UnitType.Dimensionless;
public string Legend => "All categories of the corresponded method, class, and assembly";
}
}
32 changes: 27 additions & 5 deletions src/BenchmarkDotNet.Core/Running/BenchmarkConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,22 @@ private static IEnumerable<Target> GetTargets(MethodInfo[] targetMethods, Type t
Where(m => m.HasAttribute<BenchmarkAttribute>()).
Select(methodInfo => CreateTarget(type, setupMethod, methodInfo, cleanupMethod, methodInfo.ResolveAttribute<BenchmarkAttribute>(), targetMethods));

private static Target CreateTarget(Type type, MethodInfo setupMethod, MethodInfo methodInfo, MethodInfo cleanupMethod, BenchmarkAttribute attr, MethodInfo[] targetMethods)
private static Target CreateTarget(
Type type,
MethodInfo setupMethod,
MethodInfo methodInfo,
MethodInfo cleanupMethod,
BenchmarkAttribute attr,
MethodInfo[] targetMethods)
{
var target = new Target(
type,
methodInfo,
type,
methodInfo,
setupMethod,
cleanupMethod,
attr.Description,
baseline: attr.Baseline,
attr.Description,
baseline: attr.Baseline,
categories: GetCategories(methodInfo),
operationsPerInvoke: attr.OperationsPerInvoke,
methodIndex: Array.IndexOf(targetMethods, methodInfo));
AssertMethodHasCorrectSignature("Benchmark", methodInfo);
Expand Down Expand Up @@ -131,6 +138,21 @@ private static MethodInfo GetWrappingMethod<T>(MethodInfo[] methods, string meth
return setupMethod;
}

private static string[] GetCategories(MethodInfo method)
{
var attributes = new List<BenchmarkCategoryAttribute>();
attributes.AddRange(method.GetCustomAttributes(typeof(BenchmarkCategoryAttribute), false).OfType<BenchmarkCategoryAttribute>());
var type = method.DeclaringType;
if (type != null)
{
attributes.AddRange(type.GetTypeInfo().GetCustomAttributes(typeof(BenchmarkCategoryAttribute), false).OfType<BenchmarkCategoryAttribute>());
attributes.AddRange(type.GetTypeInfo().Assembly.GetCustomAttributes().OfType<BenchmarkCategoryAttribute>());
}
if (attributes.Count == 0)
return Array.Empty<string>();
return attributes.SelectMany(attr => attr.Categories).ToArray();

This comment has been minimized.

Copy link
@adamsitnik

adamsitnik May 13, 2017

Member

should we add .Distinct() here?

This comment has been minimized.

Copy link
@AndreyAkinshin

AndreyAkinshin May 13, 2017

Author Member

Yeah, a good idea. In fact, it will not affect filtering, but Distinct will help to remove duplicates in the Categories column.

}

private static void AssertMethodHasCorrectSignature(string methodType, MethodInfo methodInfo)
{
if (methodInfo.GetParameters().Any())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ from type in compilerResults.CompiledAssembly.GetTypes()
from benchmark in TypeToBenchmarks(type, config)
let target = benchmark.Target
select new Benchmark(
new Target(target.Type, target.Method, target.SetupMethod, target.CleanupMethod, target.MethodDisplayInfo, benchmarkContent, target.Baseline, target.OperationsPerInvoke),
new Target(target.Type, target.Method, target.SetupMethod, target.CleanupMethod, target.MethodDisplayInfo, benchmarkContent, target.Baseline, target.Categories, target.OperationsPerInvoke),
benchmark.Job,
benchmark.Parameters)).ToArray();
}
Expand Down
6 changes: 5 additions & 1 deletion src/BenchmarkDotNet.Core/Running/Target.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class Target
public string MethodDisplayInfo { get; }
public int MethodIndex { get; }
public bool Baseline { get; }
public string[] Categories { get; }

private string TypeInfo => Type?.Name ?? "Untitled";
private string MethodFolderInfo => Method?.Name ?? "Untitled";
Expand All @@ -25,23 +26,26 @@ public class Target

public Target(
Type type,
MethodInfo method,
MethodInfo method,
MethodInfo setupMethod = null,
MethodInfo cleanupMethod = null,
string description = null,
string additionalLogic = null,
bool baseline = false,
string[] categories = null,
int operationsPerInvoke = 1,
int methodIndex = 0)
{
Type = type;
Method = method;
Categories = categories;

This comment has been minimized.

Copy link
@adamsitnik

adamsitnik May 13, 2017

Member

nit: Categories is assigned twice in this ctor

This comment has been minimized.

Copy link
@AndreyAkinshin

AndreyAkinshin May 13, 2017

Author Member

Good catch, thanks!

SetupMethod = setupMethod;
CleanupMethod = cleanupMethod;
OperationsPerInvoke = operationsPerInvoke;
AdditionalLogic = additionalLogic ?? string.Empty;
MethodDisplayInfo = FormatDescription(description) ?? method?.Name ?? "Untitled";
Baseline = baseline;
Categories = categories ?? Array.Empty<string>();
MethodIndex = methodIndex;
}

Expand Down

0 comments on commit 2e7427c

Please sign in to comment.