Skip to content
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
37 changes: 37 additions & 0 deletions Xledger.Collections.Bench/ImmArrayConstruction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace Xledger.Collections.Bench;

[MemoryDiagnoser(displayGenColumns: false)]
[SimpleJob(RuntimeMoniker.Net48)]
[SimpleJob(RuntimeMoniker.Net80)]
[SimpleJob(RuntimeMoniker.Net10_0)]
public class ImmArrayConstruction {
[Params(300)]
public int Length;

[Benchmark(Baseline = true)]
public string[] NewArray() {
var arr = new string[this.Length];
for (int i = 0; i < arr.Length; ++i) {
arr[i] = Guid.NewGuid().ToString();
}
return arr;
}

[Benchmark]
public ImmArray<string> ToImmArray() {
var arr = new string[this.Length];
for (int i = 0; i < arr.Length; ++i) {
arr[i] = Guid.NewGuid().ToString();
}
return arr.ToImmArray();
}

[Benchmark]
public ImmArray<string> ImmArray_Build() {
return ImmArray.Build<string>(arr => {
for (int i = 0; i < arr.Length; ++i) {
arr[i] = Guid.NewGuid().ToString();
}
}, length: this.Length);
}
}
97 changes: 97 additions & 0 deletions Xledger.Collections.Bench/ImmArrayReflection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace Xledger.Collections.Bench;

[MemoryDiagnoser(displayGenColumns: false)]
[SimpleJob(RuntimeMoniker.Net48)]
[SimpleJob(RuntimeMoniker.Net80)]
[SimpleJob(RuntimeMoniker.Net10_0)]
public class ImmArrayReflection {
[Params(300)]
public int Length;

public static string[] NewArray(int length) {
var arr = new string[length];
for (int i = 0; i < arr.Length; ++i) {
arr[i] = Guid.NewGuid().ToString();
}
return arr;
}

[Benchmark(Baseline = true)]
public ImmArray<string> ImmArray_Build() {
return ImmArray.Build<string>(FillSpan, length: this.Length);
}

static readonly ConstructorInfo CI_NoCopy = typeof(ImmArray<string>).GetConstructor(
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.ExactBinding,
binder: null,
[typeof(string[])],
modifiers: null);
static readonly MethodInfo MI_Build = typeof(ImmArray).GetMethods()
.Where(mi =>
mi.Name == nameof(ImmArray.Build)
#if NET10_0_OR_GREATER
&& mi.CustomAttributes.Where(
a => a.AttributeType == typeof(System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute)
).Any()
#endif
)
.Single()
.MakeGenericMethod(typeof(string));

static readonly MethodInfo MI_NewArray = typeof(ImmArrayReflection).GetMethod(nameof(NewArray));
static readonly MethodInfo MI_FillSpan = typeof(ImmArrayReflection).GetMethod(nameof(FillSpan));

static readonly Func<int, ImmArray<string>> newNoCopy = Compile_NewNoCopy();
static readonly Func<int, ImmArray<string>> build = Compile_Build();

public static void FillSpan(Span<string> arr) {
for (int i = 0; i < arr.Length; ++i) {
arr[i] = Guid.NewGuid().ToString();
}
}

[Benchmark]
public ImmArray<string> DynInvoke_NewNoCopy() {
return (ImmArray<string>)CI_NoCopy.Invoke([NewArray(this.Length)]);
}

[Benchmark]
public ImmArray<string> NewNoCopy() {
return new ImmArray<string>(NewArray(this.Length));
}

static Func<int, ImmArray<string>> Compile_NewNoCopy() {
var pi_length = Expression.Parameter(typeof(int), "length");
var newImmArray =
Expression.New(CI_NoCopy,
Expression.Call(MI_NewArray, pi_length));
var fn = Expression.Lambda<Func<int, ImmArray<string>>>(newImmArray, tailCall: false, pi_length).Compile();
return fn;
}

static Func<int, ImmArray<string>> Compile_Build() {
var pi_length = Expression.Parameter(typeof(int), "length");
#if NET10_0_OR_GREATER
var fillSpanDelegate = MI_FillSpan.CreateDelegate<Action<Span<string>>>();
#else
var fillSpanDelegate = MI_FillSpan.CreateDelegate(typeof(ImmArray.FillSpan<string>));
#endif
var newImmArray = Expression.Call(MI_Build, Expression.Constant(fillSpanDelegate), pi_length);
var fn = Expression.Lambda<Func<int, ImmArray<string>>>(newImmArray, tailCall: false, pi_length).Compile();
return fn;
}

[Benchmark]
public ImmArray<string> Expr_NewNoCopy() {
return newNoCopy(this.Length);
}

[Benchmark]
public ImmArray<string> Expr_Build() {
return build(this.Length);
}
}
40 changes: 40 additions & 0 deletions Xledger.Collections.Bench/ImmDictConstruction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
namespace Xledger.Collections.Bench;

[MemoryDiagnoser(displayGenColumns: false)]
[SimpleJob(RuntimeMoniker.Net48)]
[SimpleJob(RuntimeMoniker.Net80)]
[SimpleJob(RuntimeMoniker.Net10_0)]
public class ImmDictConstruction {
[Params(0, 300)]
public int Capacity;

[Params(300, 500)]
public int Count;

[Benchmark(Baseline = true)]
public Dictionary<string, int> NewDictionary() {
var dict = new Dictionary<string, int>(this.Capacity);
for (int i = 0; i < this.Count; ++i) {
dict.Add(Guid.NewGuid().ToString(), i);
}
return dict;
}

[Benchmark]
public ImmDict<string, int> ToImmDict() {
var dict = new Dictionary<string, int>(this.Capacity);
for (int i = 0; i < this.Count; ++i) {
dict.Add(Guid.NewGuid().ToString(), i);
}
return dict.ToImmDict();
}

[Benchmark]
public ImmDict<string, int> ImmDict_Build() {
return ImmDict.Build<string, int>(dict => {
for (int i = 0; i < this.Count; ++i) {
dict.Add(Guid.NewGuid().ToString(), i);
}
}, capacity: this.Capacity);
}
}
101 changes: 101 additions & 0 deletions Xledger.Collections.Bench/ImmDictReflection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace Xledger.Collections.Bench;

[MemoryDiagnoser(displayGenColumns: false)]
[SimpleJob(RuntimeMoniker.Net48)]
[SimpleJob(RuntimeMoniker.Net80)]
[SimpleJob(RuntimeMoniker.Net10_0)]
public class ImmDictReflection {
[Params(0, 300)]
public int Capacity;

[Params(300, 500)]
public int Count;

public static Dictionary<string, int> NewDictionary(int capacity, int count) {
var dict = new Dictionary<string, int>(capacity);
for (int i = 0; i < count; ++i) {
dict.Add(Guid.NewGuid().ToString(), i);
}
return dict;
}

public static ImmDict.BuildDict<string, int> MakeFillDict(int count) {
return (ImmDict.DictBuilder<string, int> dict) => {
for (int i = 0; i < count; ++i) {
dict.Add(Guid.NewGuid().ToString(), i);
}
};
}

[Benchmark(Baseline = true)]
public ImmDict<string, int> ImmDict_Build() {
return ImmDict.Build<string, int>(MakeFillDict(this.Count), capacity: this.Capacity);
}

static readonly ConstructorInfo CI_NoCopy = typeof(ImmDict<string, int>).GetConstructor(
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.ExactBinding,
binder: null,
[typeof(Dictionary<string, int>)],
modifiers: null);
static readonly MethodInfo MI_Build = typeof(ImmDict).GetMethods()
.Where(mi => mi.Name == nameof(ImmDict.Build))
.Last()
.MakeGenericMethod(typeof(string), typeof(int));

static readonly MethodInfo MI_NewDictionary = typeof(ImmDictReflection).GetMethod(nameof(NewDictionary));
static readonly MethodInfo MI_MakeFillDict = typeof(ImmDictReflection).GetMethod(nameof(MakeFillDict));

static readonly Func<int, int, ImmDict<string, int>> newNoCopy = Compile_NewNoCopy();
static readonly Func<int, int, ImmDict<string, int>> build = Compile_Build();

[Benchmark]
public ImmDict<string, int> DynInvoke_NewNoCopy() {
return (ImmDict<string, int>)CI_NoCopy.Invoke([NewDictionary(this.Capacity, this.Count)]);
}

[Benchmark]
public ImmDict<string, int> NewNoCopy() {
return new ImmDict<string, int>(NewDictionary(this.Capacity, this.Count));
}

static Func<int, int, ImmDict<string, int>> Compile_NewNoCopy() {
var pi_capacity = Expression.Parameter(typeof(int), "capacity");
var pi_count = Expression.Parameter(typeof(int), "count");
var newImmSet =
Expression.New(CI_NoCopy,
Expression.Call(MI_NewDictionary, pi_capacity, pi_count));
var fn = Expression.Lambda<Func<int, int, ImmDict<string, int>>>(
newImmSet,
tailCall: false,
pi_capacity,
pi_count).Compile();
return fn;
}

static Func<int, int, ImmDict<string, int>> Compile_Build() {
var pi_capacity = Expression.Parameter(typeof(int), "capacity");
var pi_count = Expression.Parameter(typeof(int), "count");
var fillSetDelegate = Expression.Call(MI_MakeFillDict, pi_count);
var newImmSet = Expression.Call(MI_Build, fillSetDelegate, pi_capacity, Expression.Constant(false));
var fn = Expression.Lambda<Func<int, int, ImmDict<string, int>>>(
newImmSet,
tailCall: false,
pi_capacity,
pi_count).Compile();
return fn;
}

[Benchmark]
public ImmDict<string, int> Expr_NewNoCopy() {
return newNoCopy(this.Capacity, this.Count);
}

[Benchmark]
public ImmDict<string, int> Expr_Build() {
return build(this.Capacity, this.Count);
}
}
40 changes: 40 additions & 0 deletions Xledger.Collections.Bench/ImmSetConstruction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
namespace Xledger.Collections.Bench;

[MemoryDiagnoser(displayGenColumns: false)]
[SimpleJob(RuntimeMoniker.Net48)]
[SimpleJob(RuntimeMoniker.Net80)]
[SimpleJob(RuntimeMoniker.Net10_0)]
public class ImmSetConstruction {
[Params(0, 300)]
public int Capacity;

[Params(300, 500)]
public int Count;

[Benchmark(Baseline = true)]
public HashSet<string> NewHashSet() {
var set = new HashSet<string>(this.Capacity);
for (int i = 0; i < this.Count; ++i) {
set.Add(Guid.NewGuid().ToString());
}
return set;
}

[Benchmark]
public ImmSet<string> ToImmSet() {
var set = new HashSet<string>(this.Capacity);
for (int i = 0; i < this.Count; ++i) {
set.Add(Guid.NewGuid().ToString());
}
return set.ToImmSet();
}

[Benchmark]
public ImmSet<string> ImmSet_Build() {
return ImmSet.Build<string>(set => {
for (int i = 0; i < this.Count; ++i) {
set.Add(Guid.NewGuid().ToString());
}
}, capacity: this.Capacity);
}
}
Loading