Skip to content

Commit

Permalink
Adding benchmarks from Akade.IndexedSet (#3410)
Browse files Browse the repository at this point in the history
* Added benchmarks from Akade.IndexedSet

* Updated azure-pipelines.yml

* Removed NuGet.config <clear />

* Update README.md

* Fix typo.

---------

Co-authored-by: Jiri Cincura ↹ <[email protected]>
  • Loading branch information
akade and cincuranet authored Nov 20, 2023
1 parent 2128119 commit 73a1e1d
Show file tree
Hide file tree
Showing 17 changed files with 559 additions and 0 deletions.
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

0 comments on commit 73a1e1d

Please sign in to comment.