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
35 changes: 35 additions & 0 deletions Benchmark/Eviction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using BenchmarkDotNet.Attributes;
using CacheTable;

namespace Benchmark
{
[ClrJob]
[RankColumn]
public class Eviction
{
private CacheTable<WrappedInt, int> cacheTable;
private int i;

[Params(4, 8)]
public int N;

[GlobalSetup]
public void Setup()
{
this.cacheTable = new CacheTable<WrappedInt, int>(10, this.N);

for (int i = 0; i < this.N; i++)
{
this.cacheTable[i] = i;
}

this.i = this.N;
}

[Benchmark]
public void CacheTable()
{
this.cacheTable[this.i] = this.i++;
}
}
}
86 changes: 86 additions & 0 deletions Benchmark/GuidKeysReadFullTable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using CacheTable;
using System;
using BenchmarkDotNet.Attributes;
using System.Collections.Generic;
using System.Collections.Concurrent;

namespace Benchmark
{
[CoreJob]
[RPlotExporter, RankColumn]
public class GuidKeysReadFullTable
{
private readonly CacheTable<WrappedString, int> cacheTable = new CacheTable<WrappedString, int>(10, 4);
private readonly Dictionary<WrappedString, int> dictionary = new Dictionary<WrappedString, int>();
private WrappedString[] keys;

struct WrappedString : IEquatable<WrappedString>
{
public string Value;

public bool Equals(WrappedString other) => this.Value.Equals(other.Value);

public override int GetHashCode() => this.Value.GetHashCode();

public override bool Equals(object obj) => this.Equals((WrappedString)obj);

public static implicit operator WrappedString(string str) => new WrappedString { Value = str };
}

[GlobalSetup]
public void Setup()
{
while (cacheTable.Count != 40)
{
this.cacheTable[Guid.NewGuid().ToString()] = 42;
}

this.keys = new WrappedString[40];
int i = 0;
foreach (KeyValuePair<WrappedString, int> kvp in cacheTable)
{
this.dictionary[kvp.Key] = kvp.Value;
this.keys[i++] = kvp.Key;
}

Random rng = new Random();
Shuffle(rng, this.keys);
}

public static void Shuffle<T>(Random rng, T[] array)
{
int n = array.Length;
while (n > 1)
{
int k = rng.Next(n--);
T temp = array[n];
array[n] = array[k];
array[k] = temp;
}
}

[Benchmark]
public int CacheTable()
{
int sum = 0;
foreach (var k in this.keys)
{
sum += this.cacheTable[k];
}

return sum;
}

[Benchmark(Baseline = true)]
public int Dictionary()
{
int sum = 0;
foreach (var k in this.keys)
{
sum += this.dictionary[k];
}

return sum;
}
}
}
30 changes: 10 additions & 20 deletions Benchmark/HashCollisions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,41 +15,31 @@ public class HashCollisions
private readonly Dictionary<WrappedInt, int> dictionary = new Dictionary<WrappedInt, int>();
private readonly ConcurrentDictionary<WrappedInt, int> concurrentDictionary = new ConcurrentDictionary<WrappedInt, int>();

struct WrappedInt : IEquatable<WrappedInt>
{
public int Value;

public bool Equals(WrappedInt other) => this.Value == other.Value;

public override int GetHashCode() => 42;

public override bool Equals(object obj) => this.Equals((WrappedInt)obj);

public static implicit operator WrappedInt(int i) => new WrappedInt { Value = i };
}
[Params(32, 64)]
public int N;

[Benchmark]
public void CacheTable()
{
for (int i = 0; i < 4; i++) this.cacheTable[i] = i;
for (int i = 0; i < this.N; i++) this.cacheTable[i] = i;
}

[Benchmark]
public void ConcurrentCacheTable()
[Benchmark(Baseline = true)]
public void Dictionary()
{
for (int i = 0; i < 4; i++) this.concurrentCacheTable[i] = i;
for (int i = 0; i < this.N; i++) this.dictionary[i] = i;
}

[Benchmark(Baseline = true)]
public void Dictionary()
[Benchmark]
public void ConcurrentCacheTable()
{
for (int i = 0; i < 4; i++) this.dictionary[i] = i;
for (int i = 0; i < this.N; i++) this.concurrentCacheTable[i] = i;
}

[Benchmark]
public void ConcurrentDictionary()
{
for (int i = 0; i < 4; i++) this.concurrentDictionary[i] = i;
for (int i = 0; i < this.N; i++) this.concurrentDictionary[i] = i;
}
}
}
26 changes: 26 additions & 0 deletions Benchmark/RngPerf.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using CacheTable;
using System;

namespace Benchmark
{
[CoreJob]
[RankColumn]
public class RngPerf
{
private readonly Random random = new Random();
private readonly XorShiftRandom xorshift = new XorShiftRandom();

[Benchmark(Baseline = true)]
public int Random()
{
return this.random.Next(12);
}

[Benchmark]
public int XortShiftRandom()
{
return this.xorshift.Next(12);
}
}
}
17 changes: 17 additions & 0 deletions Benchmark/WrappedInt.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;

namespace Benchmark
{
struct WrappedInt : IEquatable<WrappedInt>
{
public int Value;

public bool Equals(WrappedInt other) => this.Value == other.Value;

public override int GetHashCode() => 42;

public override bool Equals(object obj) => this.Equals((WrappedInt)obj);

public static implicit operator WrappedInt(int i) => new WrappedInt { Value = i };
}
}
2 changes: 1 addition & 1 deletion CacheTable/CacheTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class CacheTable<TKey, TValue> : ICacheTable<TKey, TValue>
{
private CacheTableInternal<TKey, TValue> table;
private int count;
private readonly Random rng = new Random();
private readonly XorShiftRandom rng = new XorShiftRandom();

/// <summary>
/// Creates a set associative cache with specified number of rows and columns.
Expand Down
2 changes: 1 addition & 1 deletion CacheTable/CacheTableInternal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
}

// Returns true if item was inserted into empty slot. False otherwise.
public bool Set(TKey key, TValue value, int row, Random rng)
public bool Set(TKey key, TValue value, int row, XorShiftRandom rng)
{
(int rowStart, int rowEnd) = this.GetRowRange(row);
int empty = -1;
Expand Down
8 changes: 4 additions & 4 deletions CacheTable/ConcurrentCacheTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class ConcurrentCacheTable<TKey, TValue> : ICacheTable<TKey, TValue>
{
private readonly CacheTableInternal<TKey, TValue> table;
private readonly object[] lockObjects;
private readonly Random[] rngs;
private readonly XorShiftRandom[] rngs;
private readonly int[] counts;

/// <summary>
Expand All @@ -35,10 +35,10 @@ public ConcurrentCacheTable(int rows, int columns, int concurrency)
this.lockObjects[i] = new object();
}

this.rngs = new Random[concurrency];
this.rngs = new XorShiftRandom[concurrency];
for (int i = 0; i < concurrency; i++)
{
this.rngs[i] = new Random();
this.rngs[i] = new XorShiftRandom();
}
}

Expand Down Expand Up @@ -248,7 +248,7 @@ private object GetLockObjectForRow(int row)
return this.lockObjects[row % this.lockObjects.Length];
}

private Random GetRng(int row)
private XorShiftRandom GetRng(int row)
{
return this.rngs[row % this.rngs.Length];
}
Expand Down
50 changes: 50 additions & 0 deletions CacheTable/XorShiftRandom.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.Runtime.CompilerServices;

namespace CacheTable
{
public class XorShiftRandom
{
[ThreadStatic]
private static Random seedRng;

private uint state;

public XorShiftRandom() : this(GetNonZeroSeed())
{
}

public XorShiftRandom(uint seed)
{
this.state = seed;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Next(int max)
{
uint x = this.state;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
this.state = x;
return (int)(x % max);
}

private static uint GetNonZeroSeed()
{
if (seedRng == null)
{
seedRng = new Random();
}

while (true)
{
uint seed = (uint)seedRng.Next();
if (seed != 0)
{
return seed;
}
}
}
}
}