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

Adding benchmarks from Akade.IndexedSet #3410

Merged
merged 6 commits into from
Nov 20, 2023
Merged
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
32 changes: 32 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,21 @@ jobs:
channels:
- main

# Akade.IndexedSet benchmarks
- template: /eng/performance/build_machine_matrix.yml
parameters:
jobTemplate: /eng/performance/benchmark_jobs.yml
buildMachines:
- win-x64
- ubuntu-x64
isPublic: true
jobParameters:
kind: akadeindexedset
csproj: src\benchmarks\real-world\Akade.IndexedSet.Benchmarks\Akade.IndexedSet.Benchmarks.csproj
runCategories: 'AkadeIndexedSet'
channels:
- main

# Roslyn benchmarks
- template: /eng/performance/build_machine_matrix.yml
parameters:
Expand Down Expand Up @@ -511,6 +526,23 @@ jobs:
channels:
- main

# Akade.IndexedSet benchmarks
- template: /eng/performance/build_machine_matrix.yml
parameters:
jobTemplate: /eng/performance/benchmark_jobs.yml
buildMachines:
- win-x64
- ubuntu-x64
- win-arm64
- ubuntu-arm64-ampere
isPublic: false
jobParameters:
kind: akadeindexedset
csproj: src\benchmarks\real-world\Akade.IndexedSet.Benchmarks\Akade.IndexedSet.Benchmarks.csproj
runCategories: 'AkadeIndexedSet'
channels:
- main

# ML.NET benchmarks
- template: /eng/performance/build_machine_matrix.yml
parameters:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsTestProject>false</IsTestProject>
</PropertyGroup>

<PropertyGroup>
<TargetFrameworks>$(PERFLAB_TARGET_FRAMEWORKS)</TargetFrameworks>
<TargetFrameworks Condition="'$(TargetFrameworks)' == ''">net8.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Akade.IndexedSet" Version="1.0.1" />
<PackageReference Include="Bogus" Version="34.0.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\harness\BenchmarkDotNet.Extensions\BenchmarkDotNet.Extensions.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Akade.IndexedSet.Benchmarks;

public static class Categories
{
/// <summary>
/// Benchmarks in this category are executed for CI jobs
/// </summary>
public const string AkadeIndexedSet = "AkadeIndexedSet";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using Akade.IndexedSet.Concurrency;
using BenchmarkDotNet.Attributes;
using Bogus;

namespace Akade.IndexedSet.Benchmarks;

[BenchmarkCategory(Categories.AkadeIndexedSet)]
public class ConcurrentSetBenchmarks
{
private readonly List<Person> _persons;
private readonly IndexedSet<Person> _indexedSet;
private readonly ConcurrentIndexedSet<Person> _concurrentIndexedSet;

public ConcurrentSetBenchmarks()
{
Randomizer.Seed = new Random(42);
_persons = Enumerable.Range(0, 1000)
.Select(_ => new Person())
.ToList();

_indexedSet = _persons.ToIndexedSet()
.WithUniqueIndex(x => x.Phone)
.WithFullTextIndex(x => x.FullName)
.WithRangeIndex(GetAge)
.Build();

_concurrentIndexedSet = _persons.ToIndexedSet()
.WithUniqueIndex(x => x.Phone)
.WithFullTextIndex(x => x.FullName)
.WithRangeIndex(GetAge)
.BuildConcurrent();
}

public static int GetAge(Person p)
{
DateTime today = DateTime.Today;
int age = today.Year - p.DateOfBirth.Year;

if (p.DateOfBirth.Date > today.AddYears(-age))
{
age--;
}
return age;
}

[Benchmark]
public bool UniqueLookup()
{
return _indexedSet.TryGetSingle(x => x.Phone, "random", out _);
}

[Benchmark]
public bool ConcurrentUniqueLookup()
{
return _concurrentIndexedSet.TryGetSingle(x => x.Phone, "random", out _);
}

[Benchmark]
public int LessThanLookup()
{
return _indexedSet.LessThan(GetAge, 12).Count();
}

[Benchmark]
public int ConcurrentLessThanLookup()
{
return _concurrentIndexedSet.LessThan(GetAge, 12).Count();
}

[Benchmark]
public int FullTextLookup()
{
return _indexedSet.FuzzyContains(x => x.FullName, "Peter", 1).Count();
}

[Benchmark]
public int ConcurrentFullTextLookup()
{
return _concurrentIndexedSet.FuzzyContains(x => x.FullName, "Peter", 1).Count();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using BenchmarkDotNet.Attributes;
using Bogus;

namespace Akade.IndexedSet.Benchmarks;

[BenchmarkCategory(Categories.AkadeIndexedSet)]
public class FullTextIndexBenchmarks
{
public record class Document(string Content);

private readonly IndexedSet<Document> _indexedSet;
private readonly List<Document> _document;

public FullTextIndexBenchmarks()
{
Randomizer.Seed = new Random(42);
_document = new Faker<Document>().CustomInstantiator(f => new Document(f.Rant.Review()))
.Generate(1000);

_indexedSet = _document.ToIndexedSet()
.WithFullTextIndex(x => x.Content)
.Build();

}

[Benchmark]
public Document[] Contains_IndexedSet()
{
return _indexedSet.Contains(x => x.Content, "cheeseburger").ToArray();
}

[Benchmark]
public Document[] FuzzyContains_IndexedSet()
{
return _indexedSet.FuzzyContains(x => x.Content, "cheeseburger", 2).ToArray();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using BenchmarkDotNet.Attributes;

namespace Akade.IndexedSet.Benchmarks;

[BenchmarkCategory(Categories.AkadeIndexedSet)]
public class MultiValueIndexBenchmarks
{
private record Order(int ProductId, decimal Price);
private readonly List<Order> _orders;
private readonly List<int> _productIds;
private readonly IndexedSet<Order> _indexedSet;

public MultiValueIndexBenchmarks()
{
Random r = new(42);
_orders = Enumerable.Range(0, 1000)
.Select(_ => new Order(r.Next(1, 10), (decimal)r.NextDouble() * 100m))
.ToList();

_productIds = Enumerable.Range(1, 10)
.ToList();

_indexedSet = _orders.ToIndexedSet()
.WithIndex(x => x.ProductId)
.Build();
}

[Benchmark]
public decimal Multivalue_IndexedSet()
{
decimal total = 0;
foreach (int productId in _productIds)
{
foreach (Order product in _indexedSet.Where(x => x.ProductId, productId))
{
total += product.Price;
}
}
return total;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using BenchmarkDotNet.Attributes;
using Bogus;

namespace Akade.IndexedSet.Benchmarks;

[BenchmarkCategory(Categories.AkadeIndexedSet)]
public class PrefixIndexBenchmarks
{
private readonly List<Person> _persons;
private readonly IndexedSet<Person> _indexedSet;

public PrefixIndexBenchmarks()
{
Randomizer.Seed = new Random(42);
_persons = Enumerable.Range(0, 1000)
.Select(_ => new Person())
.ToList();

_indexedSet = _persons.ToIndexedSet()
.WithPrefixIndex(x => x.FullName)
.Build();
}

[Benchmark]
public Person[] StartsWith_IndexedSet()
{
return _indexedSet.StartsWith(x => x.FullName, "Tiffany").ToArray();
}

[Benchmark]
public Person[] FuzzyStartsWith_IndexedSet()
{
return _indexedSet.FuzzyStartsWith(x => x.FullName, "Tiffany", 2).ToArray();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Akade.IndexedSet.Benchmarks;
using BenchmarkDotNet.Extensions;
using BenchmarkDotNet.Running;
using System.Collections.Immutable;

BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, RecommendedConfig.Create(
artifactsPath: new DirectoryInfo(Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location)!, "BenchmarkDotNet.Artifacts")),
mandatoryCategories: ImmutableHashSet.Create(Categories.AkadeIndexedSet)));
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Akade.IndexedSet.Benchmarks

Includes benchmarks from [Akade.IndexedSet](https://github.com/akade/Akade.IndexedSet) that benchmark the library itself. Hence, all the comparison benchmarks with LINQ-based solutions are removed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using BenchmarkDotNet.Attributes;
using Bogus;

namespace Akade.IndexedSet.Benchmarks;

[BenchmarkCategory(Categories.AkadeIndexedSet)]
public class RangeIndexBenchmarks
{
private record class Appointment(DateOnly Date);

private readonly IndexedSet<Appointment> _indexedSet;
private readonly List<Appointment> _appointments;

private readonly DateOnly _start = new(1879, 3, 14);
private readonly DateOnly _end = new(1955, 4, 18);

public RangeIndexBenchmarks()
{
Randomizer.Seed = new Random(42);
_appointments = new Faker<Appointment>().CustomInstantiator(f => new Appointment(f.Date.BetweenDateOnly(_start, _end)))
.Generate(10000);

_indexedSet = _appointments.ToIndexedSet()
.WithRangeIndex(x => x.Date)
.Build();

}


[Benchmark]
public int Range_IndexedSet()
{
DateOnly start = _start.AddYears(10);
DateOnly end = _start.AddYears(18);
return _indexedSet.Range(x => x.Date, start, end).Count();
}

[Benchmark]
public int Paging_IndexedSet()
{
return _indexedSet.OrderBy(x => x.Date, 100).Take(10).ToArray().Length;
}

[Benchmark]
public DateOnly Min_IndexedSet()
{
return _indexedSet.Min(x => x.Date);
}

[Benchmark]
public int LessThan_IndexedSet()
{
DateOnly end = _start.AddYears(10);
return _indexedSet.LessThan(x => x.Date, end).Count();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Collections.Immutable;

namespace Akade.IndexedSet.Benchmarks.RealWorld.EventSourcedAggregateCache;

public record class Aggregate(AggregateId Id, TenantId Owner, ImmutableHashSet<TenantId> SharedWith, ExternalAggregateId ExternalId, string FirstName, string LastName);
public record struct AggregateId(long Id);
public record struct TenantId(long Id);
public record struct ExternalAggregateId(long PartOne, long PartTwo);

public record class AggregateAdded(AggregateId Id, TenantId Owner, ExternalAggregateId ExternalId, string FirstName, string LastName) : AggregateEvent;
public abstract record class AggregateEvent();
public record class AggregateShared(AggregateId Id, TenantId SharedWith) : AggregateEvent;

internal static class AggregateIndices
{
internal static IEnumerable<TenantId> TenantsWithAccess(Aggregate aggregate)
{
return aggregate.SharedWith.Prepend(aggregate.Owner);
}

internal static string FullName(Aggregate aggregate)
{
return $"{aggregate.FirstName} {aggregate.LastName}";
}
}
Loading
Loading