Skip to content

Instantly share code, notes, and snippets.

@MichalStrehovsky
Created June 29, 2019 21:21
Show Gist options
  • Save MichalStrehovsky/3ded26f244e0d60d2129f1a578874e07 to your computer and use it in GitHub Desktop.
Save MichalStrehovsky/3ded26f244e0d60d2129f1a578874e07 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
internal static class Program
{
private static readonly int ProcessorCount = Environment.ProcessorCount;
private static void Main(string[] args)
{
int ai = 1;
int threadCount;
if (args[ai].EndsWith("PcT"))
{
double pcMultiplier;
if (!double.TryParse(args[ai].Substring(0, args[ai].Length - "PcT".Length), out pcMultiplier))
return;
threadCount = Math.Max(1, (int)Math.Round(ProcessorCount * pcMultiplier));
}
else if (args[ai].EndsWith("T"))
{
if (!int.TryParse(args[ai].Substring(0, args[ai].Length - "T".Length), out threadCount))
return;
}
else
return;
++ai;
switch (args[0])
{
case "MresWaitDrainRate":
MresWaitDrainRate(threadCount);
break;
case "MresWaitLatency":
MresWaitLatency(threadCount);
break;
case "SemaphoreSlimWaitDrainRate":
SemaphoreSlimWaitDrainRate(threadCount);
break;
case "SemaphoreSlimLatency":
SemaphoreSlimLatency(threadCount);
break;
case "SemaphoreSlimThroughput":
SemaphoreSlimThroughput(threadCount);
break;
case "SpinLockLatency":
SpinLockLatency(threadCount);
break;
case "SpinLockThroughput":
SpinLockThroughput(threadCount);
break;
case "ConcurrentBagThroughput":
ConcurrentBagThroughput(threadCount);
break;
case "ConcurrentBagFairness":
ConcurrentBagFairness(threadCount);
break;
case "ConcurrentQueueThroughput":
ConcurrentQueueThroughput(threadCount);
break;
case "ConcurrentQueueFairness":
ConcurrentQueueFairness(threadCount);
break;
case "ConcurrentStackThroughput":
ConcurrentStackThroughput(threadCount);
break;
case "ConcurrentStackFairness":
ConcurrentStackFairness(threadCount);
break;
case "BarrierSyncRate":
BarrierSyncRate(threadCount);
break;
case "CountdownEventSyncRate":
CountdownEventSyncRate(threadCount);
break;
case "ThreadPoolSustainedWorkThroughput":
ThreadPoolSustainedWorkThroughput(threadCount);
break;
case "ThreadPoolBurstWorkThroughput":
{
if (ai >= args.Length || !args[ai].EndsWith("PcWi"))
return;
double workItemCountPcMultiplier;
if (!double.TryParse(args[ai].Substring(0, args[ai].Length - "PcWi".Length), out workItemCountPcMultiplier))
return;
int maxWorkItemCount = Math.Max(1, (int)Math.Round(ProcessorCount * workItemCountPcMultiplier));
ThreadPoolBurstWorkThroughput(threadCount, maxWorkItemCount);
break;
}
case "TaskSustainedWorkThroughput":
TaskSustainedWorkThroughput(threadCount);
break;
case "TaskBurstWorkThroughput":
{
if (ai >= args.Length || !args[ai].EndsWith("PcWi"))
return;
double workItemCountPcMultiplier;
if (!double.TryParse(args[ai].Substring(0, args[ai].Length - "PcWi".Length), out workItemCountPcMultiplier))
return;
int maxWorkItemCount = Math.Max(1, (int)Math.Round(ProcessorCount * workItemCountPcMultiplier));
TaskBurstWorkThroughput(threadCount, maxWorkItemCount);
break;
}
case "MonitorEnterExitThroughput_ThinLock":
MonitorEnterExitThroughput(1, false, false);
break;
case "MonitorEnterExitThroughput_AwareLock":
MonitorEnterExitThroughput(1, false, true);
break;
case "MonitorReliableEnterExitThroughput_ThinLock":
MonitorReliableEnterExitThroughput(1, false, false);
break;
case "MonitorReliableEnterExitThroughput_AwareLock":
MonitorReliableEnterExitThroughput(1, false, true);
break;
case "MonitorTryEnterExitWhenUnlockedThroughput_ThinLock":
MonitorTryEnterExitWhenUnlockedThroughput_ThinLock(1);
break;
case "MonitorTryEnterExitWhenUnlockedThroughput_AwareLock":
MonitorTryEnterExitWhenUnlockedThroughput_AwareLock(1);
break;
case "MonitorTryEnterWhenLockedThroughput_ThinLock":
MonitorTryEnterWhenLockedThroughput_ThinLock(1);
break;
case "MonitorTryEnterWhenLockedThroughput_AwareLock":
MonitorTryEnterWhenLockedThroughput_AwareLock(1);
break;
case "MonitorReliableEnterExitLatency":
MonitorReliableEnterExitLatency(threadCount);
break;
case "MonitorEnterExitThroughput":
MonitorEnterExitThroughput(threadCount, true, false);
break;
case "MonitorReliableEnterExitThroughput":
MonitorReliableEnterExitThroughput(threadCount, true, false);
break;
case "MonitorTryEnterExitThroughput":
MonitorTryEnterExitThroughput(threadCount, true, false);
break;
case "MonitorReliableEnterExit1PcTOtherWorkThroughput":
MonitorReliableEnterExit1PcTOtherWorkThroughput(threadCount);
break;
case "MonitorReliableEnterExitRoundRobinThroughput":
MonitorReliableEnterExitRoundRobinThroughput(threadCount);
break;
case "MonitorReliableEnterExitFairness":
MonitorReliableEnterExitFairness(threadCount);
break;
case "BufferMemoryCopyThroughput":
BufferMemoryCopyThroughput(threadCount);
break;
}
}
[ThreadStatic]
private static Random t_rng;
private static void MresWaitDrainRate(int threadCount)
{
var startTest = new ManualResetEvent(false);
var allWaitersWoken0 = new ManualResetEvent(false);
var allWaitersWoken1 = new ManualResetEvent(false);
int waiterWokenCount = 0;
var threadOperationCounts = new int[(threadCount + 1) * 16];
var e = new ManualResetEventSlim(false);
ThreadStart waitThreadStart = () =>
{
var localThreadCount = threadCount;
var localThreadOperationCounts = threadOperationCounts;
startTest.WaitOne();
var allWaitersWoken = allWaitersWoken0;
while (true)
{
e.Wait();
if (Interlocked.Increment(ref waiterWokenCount) == localThreadCount)
{
++localThreadOperationCounts[16];
waiterWokenCount = 0;
e.Reset();
(allWaitersWoken == allWaitersWoken0 ? allWaitersWoken1 : allWaitersWoken0).Reset();
allWaitersWoken.Set();
}
else
allWaitersWoken.WaitOne();
allWaitersWoken = allWaitersWoken == allWaitersWoken0 ? allWaitersWoken1 : allWaitersWoken0;
}
};
var waitThreads = new Thread[threadCount];
for (int i = 0; i < waitThreads.Length; ++i)
{
var t = new Thread(waitThreadStart);
t.IsBackground = true;
t.Start();
waitThreads[i] = t;
}
var signalThread = new Thread(() =>
{
var rng = new Random(0);
var allWaitersWoken = allWaitersWoken0;
startTest.WaitOne();
while (true)
{
Delay(RandomShortDelay(rng));
e.Set();
allWaitersWoken.WaitOne();
allWaitersWoken = allWaitersWoken == allWaitersWoken0 ? allWaitersWoken1 : allWaitersWoken0;
}
});
signalThread.IsBackground = true;
signalThread.Start();
Run(startTest, threadOperationCounts, hasOneResult: true);
}
private static void MresWaitLatency(int threadCount)
{
var startTest = new ManualResetEvent(false);
var continueWaitThreads = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var e = new ManualResetEventSlim(false);
ParameterizedThreadStart waitThreadStart = data =>
{
int threadIndex = (int)data;
var localThreadOperationCounts = threadOperationCounts;
startTest.WaitOne();
while (true)
{
e.Wait();
++localThreadOperationCounts[threadIndex];
continueWaitThreads.WaitOne();
}
};
var waitThreads = new Thread[threadCount];
for (int i = 0; i < waitThreads.Length; ++i)
{
var t = new Thread(waitThreadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
waitThreads[i] = t;
}
var signalThread = new Thread(() =>
{
var rng = new Random(0);
startTest.WaitOne();
while (true)
{
Delay(RandomShortDelay(rng));
continueWaitThreads.Reset();
e.Set();
e.Reset();
continueWaitThreads.Set();
}
});
signalThread.IsBackground = true;
signalThread.Start();
Run(startTest, threadOperationCounts);
}
private static void SemaphoreSlimWaitDrainRate(int threadCount)
{
var startTest = new ManualResetEvent(false);
var allWaitersWoken0 = new ManualResetEvent(false);
var allWaitersWoken1 = new ManualResetEvent(false);
int waiterWokenCount = 0;
var threadOperationCounts = new int[(threadCount + 1) * 16];
var ss = new SemaphoreSlim(0);
ThreadStart waitThreadStart = () =>
{
var localThreadCount = threadCount;
var localThreadOperationCounts = threadOperationCounts;
var allWaitersWoken = allWaitersWoken0;
startTest.WaitOne();
while (true)
{
ss.Wait();
if (Interlocked.Increment(ref waiterWokenCount) == localThreadCount)
{
++localThreadOperationCounts[16];
waiterWokenCount = 0;
(allWaitersWoken == allWaitersWoken0 ? allWaitersWoken1 : allWaitersWoken0).Reset();
allWaitersWoken.Set();
}
else
allWaitersWoken.WaitOne();
allWaitersWoken = allWaitersWoken == allWaitersWoken0 ? allWaitersWoken1 : allWaitersWoken0;
}
};
var waitThreads = new Thread[threadCount];
for (int i = 0; i < waitThreads.Length; ++i)
{
var t = new Thread(waitThreadStart);
t.IsBackground = true;
t.Start();
waitThreads[i] = t;
}
var signalThread = new Thread(() =>
{
var localThreadCount = threadCount;
var rng = new Random(0);
var allWaitersWoken = allWaitersWoken0;
startTest.WaitOne();
while (true)
{
Delay(RandomShortDelay(rng));
ss.Release(localThreadCount);
allWaitersWoken.WaitOne();
allWaitersWoken = allWaitersWoken == allWaitersWoken0 ? allWaitersWoken1 : allWaitersWoken0;
}
});
signalThread.IsBackground = true;
signalThread.Start();
Run(startTest, threadOperationCounts, hasOneResult: true);
}
private static void SemaphoreSlimLatency(int threadCount)
{
var startTest = new ManualResetEvent(false);
int previousLockThreadId = -1;
var threadOperationCounts = new int[(threadCount + 1) * 16];
var ss = new SemaphoreSlim(1);
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
int threadId = Environment.CurrentManagedThreadId;
var localThreadOperationCounts = threadOperationCounts;
var rng = new Random(threadIndex);
startTest.WaitOne();
while (true)
{
var d0 = RandomShortDelay(rng);
var d1 = RandomShortDelay(rng);
ss.Wait();
previousLockThreadId = threadId;
Delay(d0);
ss.Release();
++localThreadOperationCounts[threadIndex];
Delay(d1);
while (previousLockThreadId == threadId)
Delay(4);
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threads[i] = t;
}
Run(startTest, threadOperationCounts);
}
private static void SemaphoreSlimThroughput(int threadCount)
{
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var ss = new SemaphoreSlim(1);
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
var localThreadOperationCounts = threadOperationCounts;
var rng = new Random(threadIndex);
startTest.WaitOne();
while (true)
{
var d0 = RandomShortDelay(rng);
var d1 = RandomShortDelay(rng);
ss.Wait();
Delay(d0);
ss.Release();
++localThreadOperationCounts[threadIndex];
Delay(d1);
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threads[i] = t;
}
Run(startTest, threadOperationCounts);
}
private static void SpinLockLatency(int threadCount)
{
var startTest = new ManualResetEvent(false);
int previousLockThreadId = -1;
var threadOperationCounts = new int[(threadCount + 1) * 16];
var m = new SpinLock(enableThreadOwnerTracking: false);
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
int threadId = Environment.CurrentManagedThreadId;
var localThreadOperationCounts = threadOperationCounts;
var rng = new Random(threadIndex);
startTest.WaitOne();
while (true)
{
var d0 = RandomShortDelay(rng);
var d1 = RandomShortDelay(rng);
bool lockTaken = false;
m.Enter(ref lockTaken);
previousLockThreadId = threadId;
Delay(d0);
m.Exit();
++localThreadOperationCounts[threadIndex];
Delay(d1);
while (previousLockThreadId == threadId)
Delay(4);
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threads[i] = t;
}
Run(startTest, threadOperationCounts);
}
private static void SpinLockThroughput(int threadCount)
{
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var m = new SpinLock(enableThreadOwnerTracking: false);
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
var localThreadOperationCounts = threadOperationCounts;
var rng = new Random(threadIndex);
startTest.WaitOne();
while (true)
{
var d0 = RandomShortDelay(rng);
var d1 = RandomShortDelay(rng);
bool lockTaken = false;
m.Enter(ref lockTaken);
Delay(d0);
m.Exit();
++localThreadOperationCounts[threadIndex];
Delay(d1);
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threads[i] = t;
}
Run(startTest, threadOperationCounts);
}
private static void ConcurrentBagThroughput(int threadCount)
{
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var cb = new ConcurrentBag<int>();
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
int threadId = Environment.CurrentManagedThreadId;
var localThreadOperationCounts = threadOperationCounts;
var localCb = cb;
var rng = new Random(threadIndex);
threadReady.Set();
startTest.WaitOne();
while (true)
{
var d0 = RandomShortDelay(rng);
var d1 = RandomShortDelay(rng);
localCb.Add(threadId);
Delay(d0);
int item;
localCb.TryTake(out item);
++localThreadOperationCounts[threadIndex];
Delay(d1);
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
Run(startTest, threadOperationCounts);
}
private static void ConcurrentBagFairness(int threadCount)
{
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var threadWaitDurationsUs = new double[(threadCount + 1) * 16];
var cb = new ConcurrentBag<int>();
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
int threadId = Environment.CurrentManagedThreadId;
var localThreadOperationCounts = threadOperationCounts;
var localThreadWaitDurationsUs = threadWaitDurationsUs;
var localCb = cb;
var rng = new Random(threadIndex);
threadReady.Set();
startTest.WaitOne();
while (true)
{
var d0 = RandomShortDelay(rng);
var d1 = RandomShortDelay(rng);
var startTicks = Clock.Ticks;
localCb.Add(threadId);
var stopTicks = Clock.Ticks;
++localThreadOperationCounts[threadIndex];
localThreadWaitDurationsUs[threadIndex] +=
BiasWaitDurationUsAgainstLongWaits(Clock.TicksToUs(stopTicks - startTicks));
Delay(d0);
startTicks = Clock.Ticks;
int item;
localCb.TryTake(out item);
stopTicks = Clock.Ticks;
++localThreadOperationCounts[threadIndex];
localThreadWaitDurationsUs[threadIndex] +=
BiasWaitDurationUsAgainstLongWaits(Clock.TicksToUs(stopTicks - startTicks));
Delay(d1);
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
RunFairness(startTest, threadOperationCounts, threadWaitDurationsUs);
}
private static void ConcurrentQueueThroughput(int threadCount)
{
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var cq = new ConcurrentQueue<int>();
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
int threadId = Environment.CurrentManagedThreadId;
var localThreadOperationCounts = threadOperationCounts;
var localCq = cq;
var rng = new Random(threadIndex);
threadReady.Set();
startTest.WaitOne();
while (true)
{
var d0 = RandomShortDelay(rng);
var d1 = RandomShortDelay(rng);
localCq.Enqueue(threadId);
Delay(d0);
int item;
localCq.TryDequeue(out item);
++localThreadOperationCounts[threadIndex];
Delay(d1);
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
Run(startTest, threadOperationCounts);
}
private static void ConcurrentQueueFairness(int threadCount)
{
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var threadWaitDurationsUs = new double[(threadCount + 1) * 16];
var cq = new ConcurrentQueue<int>();
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
int threadId = Environment.CurrentManagedThreadId;
var localThreadOperationCounts = threadOperationCounts;
var localThreadWaitDurationsUs = threadWaitDurationsUs;
var localCq = cq;
var rng = new Random(threadIndex);
threadReady.Set();
startTest.WaitOne();
while (true)
{
var d0 = RandomShortDelay(rng);
var d1 = RandomShortDelay(rng);
var startTicks = Clock.Ticks;
localCq.Enqueue(threadId);
var stopTicks = Clock.Ticks;
++localThreadOperationCounts[threadIndex];
localThreadWaitDurationsUs[threadIndex] +=
BiasWaitDurationUsAgainstLongWaits(Clock.TicksToUs(stopTicks - startTicks));
Delay(d0);
startTicks = Clock.Ticks;
int item;
localCq.TryDequeue(out item);
stopTicks = Clock.Ticks;
++localThreadOperationCounts[threadIndex];
localThreadWaitDurationsUs[threadIndex] +=
BiasWaitDurationUsAgainstLongWaits(Clock.TicksToUs(stopTicks - startTicks));
Delay(d1);
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
RunFairness(startTest, threadOperationCounts, threadWaitDurationsUs);
}
private static void ConcurrentStackThroughput(int threadCount)
{
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var cs = new ConcurrentStack<int>();
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
int threadId = Environment.CurrentManagedThreadId;
var localThreadOperationCounts = threadOperationCounts;
var localCs = cs;
var rng = new Random(threadIndex);
threadReady.Set();
startTest.WaitOne();
while (true)
{
var d0 = RandomShortDelay(rng);
var d1 = RandomShortDelay(rng);
localCs.Push(threadId);
Delay(d0);
int item;
localCs.TryPop(out item);
++localThreadOperationCounts[threadIndex];
Delay(d1);
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
Run(startTest, threadOperationCounts);
}
private static void ConcurrentStackFairness(int threadCount)
{
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var threadWaitDurationsUs = new double[(threadCount + 1) * 16];
var cs = new ConcurrentStack<int>();
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
int threadId = Environment.CurrentManagedThreadId;
var localThreadOperationCounts = threadOperationCounts;
var localThreadWaitDurationsUs = threadWaitDurationsUs;
var localCs = cs;
var rng = new Random(threadIndex);
threadReady.Set();
startTest.WaitOne();
while (true)
{
var d0 = RandomShortDelay(rng);
var d1 = RandomShortDelay(rng);
var startTicks = Clock.Ticks;
localCs.Push(threadId);
var stopTicks = Clock.Ticks;
++localThreadOperationCounts[threadIndex];
localThreadWaitDurationsUs[threadIndex] +=
BiasWaitDurationUsAgainstLongWaits(Clock.TicksToUs(stopTicks - startTicks));
Delay(d0);
startTicks = Clock.Ticks;
int item;
localCs.TryPop(out item);
stopTicks = Clock.Ticks;
++localThreadOperationCounts[threadIndex];
localThreadWaitDurationsUs[threadIndex] +=
BiasWaitDurationUsAgainstLongWaits(Clock.TicksToUs(stopTicks - startTicks));
Delay(d1);
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
RunFairness(startTest, threadOperationCounts, threadWaitDurationsUs);
}
private static void BarrierSyncRate(int threadCount)
{
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var delayComplete0 = new ManualResetEvent(false);
var delayComplete1 = new ManualResetEvent(false);
int syncThreadCount = threadCount;
var threadOperationCounts = new int[(threadCount + 1) * 16];
var b = new Barrier(threadCount);
var rng = new Random(0);
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
var localThreadCount = threadCount;
var localDelayComplete0 = delayComplete0;
var localDelayComplete1 = delayComplete1;
var localThreadOperationCounts = threadOperationCounts;
var localB = b;
threadReady.Set();
startTest.WaitOne();
while (true)
{
localB.SignalAndWait();
if (Interlocked.Decrement(ref syncThreadCount) == 0)
{
syncThreadCount = localThreadCount;
localDelayComplete1.Reset();
localDelayComplete0.Set();
}
else
localDelayComplete0.WaitOne();
if (Interlocked.Decrement(ref syncThreadCount) == 0)
{
++localThreadOperationCounts[16];
Delay(RandomShortDelay(rng));
syncThreadCount = localThreadCount;
localDelayComplete0.Reset();
localDelayComplete1.Set();
}
else
localDelayComplete1.WaitOne();
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
Run(startTest, threadOperationCounts);
}
private static void CountdownEventSyncRate(int threadCount)
{
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var delayComplete0 = new ManualResetEvent(false);
var delayComplete1 = new ManualResetEvent(false);
int syncThreadCount = threadCount;
var threadOperationCounts = new int[(threadCount + 1) * 16];
var cde = new CountdownEvent(threadCount * 2);
var rng = new Random(0);
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
var localThreadCount = threadCount;
var localDelayComplete0 = delayComplete0;
var localDelayComplete1 = delayComplete1;
var localThreadOperationCounts = threadOperationCounts;
var localCde = cde;
threadReady.Set();
startTest.WaitOne();
while (true)
{
localCde.Signal(2);
if (Interlocked.Decrement(ref syncThreadCount) == 0)
{
syncThreadCount = localThreadCount;
localDelayComplete1.Reset();
localDelayComplete0.Set();
}
else
localDelayComplete0.WaitOne();
if (Interlocked.Decrement(ref syncThreadCount) == 0)
{
++localThreadOperationCounts[16];
Delay(RandomShortDelay(rng));
syncThreadCount = localThreadCount;
localCde.Reset(localThreadCount * 2);
localDelayComplete0.Reset();
localDelayComplete1.Set();
}
else
localDelayComplete1.WaitOne();
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
Run(startTest, threadOperationCounts);
}
private static void ThreadPoolSustainedWorkThroughput(int threadCount)
{
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
ThreadPool.SetMinThreads(threadCount, threadCount);
ThreadPool.SetMaxThreads(threadCount, threadCount);
WaitCallback workItemStart = null;
workItemStart = data =>
{
ThreadPool.QueueUserWorkItem(workItemStart);
var rng = t_rng;
if (rng == null)
t_rng = rng = new Random(0);
Delay(RandomShortDelay(rng));
Interlocked.Increment(ref threadOperationCounts[16]);
};
var producerThread = new Thread(() =>
{
var localWorkItemStart = workItemStart;
startTest.WaitOne();
int initialWorkItemCount = ProcessorCount + threadCount * 4;
for (int i = 0; i < initialWorkItemCount; ++i)
ThreadPool.QueueUserWorkItem(localWorkItemStart);
});
producerThread.IsBackground = true;
producerThread.Start();
Run(startTest, threadOperationCounts, hasOneResult: true);
}
private static void ThreadPoolBurstWorkThroughput(int threadCount, int maxWorkItemCount)
{
var startTest = new ManualResetEvent(false);
var workComplete = new AutoResetEvent(false);
int workItemCountToQueue = 0;
int workItemCountToComplete = 0;
var threadOperationCounts = new int[(threadCount + 1) * 16];
ThreadPool.SetMinThreads(threadCount, threadCount);
ThreadPool.SetMaxThreads(threadCount, threadCount);
WaitCallback workItemStart = null;
workItemStart = data =>
{
int n = Interlocked.Add(ref workItemCountToQueue, -2);
if (n >= -1)
{
var localWorkItemStart = workItemStart;
ThreadPool.QueueUserWorkItem(localWorkItemStart);
if (n >= 0)
ThreadPool.QueueUserWorkItem(localWorkItemStart);
}
var rng = t_rng;
if (rng == null)
t_rng = rng = new Random(0);
Delay(RandomShortDelay(rng));
Interlocked.Increment(ref threadOperationCounts[16]);
if (Interlocked.Decrement(ref workItemCountToComplete) == 0)
workComplete.Set();
};
var producerThread = new Thread(() =>
{
var localMaxWorkItemCount = maxWorkItemCount;
var localWorkItemStart = workItemStart;
startTest.WaitOne();
while (true)
{
workItemCountToQueue = localMaxWorkItemCount - 1;
workItemCountToComplete = localMaxWorkItemCount;
ThreadPool.QueueUserWorkItem(localWorkItemStart);
workComplete.WaitOne();
}
});
producerThread.IsBackground = true;
producerThread.Start();
Run(startTest, threadOperationCounts, hasOneResult: true);
}
private static void TaskSustainedWorkThroughput(int threadCount)
{
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
ThreadPool.SetMinThreads(threadCount, threadCount);
ThreadPool.SetMaxThreads(threadCount, threadCount);
Action workItemStart = null;
workItemStart = () =>
{
Task.Run(workItemStart);
var rng = t_rng;
if (rng == null)
t_rng = rng = new Random(0);
Delay(RandomShortDelay(rng));
Interlocked.Increment(ref threadOperationCounts[16]);
};
Action initialWorkItemStart = () =>
{
var localWorkItemStart = workItemStart;
for (int i = 0; i < 4; ++i)
Task.Run(localWorkItemStart);
};
var producerThread = new Thread(() =>
{
var localThreadCount = threadCount;
var localInitialWorkItemStart = initialWorkItemStart;
startTest.WaitOne();
int initialWorkItemCount = ProcessorCount + threadCount;
for (int i = 0; i < initialWorkItemCount; ++i)
Task.Run(localInitialWorkItemStart);
});
producerThread.IsBackground = true;
producerThread.Start();
Run(startTest, threadOperationCounts, hasOneResult: true);
}
private static void TaskBurstWorkThroughput(int threadCount, int maxWorkItemCount)
{
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
ThreadPool.SetMinThreads(threadCount, threadCount);
ThreadPool.SetMaxThreads(threadCount, threadCount);
Action<object> workItemStart = null;
workItemStart = async data =>
{
Task t0 = null, t1 = null;
int toQueue = (int)data;
if (toQueue > 1)
{
var localWorkItemStart = workItemStart;
--toQueue;
t0 = new Task(localWorkItemStart, toQueue - toQueue / 2);
t0.Start();
t1 = new Task(localWorkItemStart, toQueue / 2);
t1.Start();
}
else if (toQueue != 0)
{
t0 = new Task(workItemStart, 0);
t0.Start();
}
var rng = t_rng;
if (rng == null)
t_rng = rng = new Random(0);
Delay(RandomShortDelay(rng));
Interlocked.Increment(ref threadOperationCounts[16]);
if (t0 != null)
{
await t0;
if (t1 != null)
await t1;
}
};
var producerThread = new Thread(() =>
{
var localMaxWorkItemCount = maxWorkItemCount;
var localWorkItemStart = workItemStart;
startTest.WaitOne();
while (true)
{
var t = new Task(localWorkItemStart, localMaxWorkItemCount - 1);
t.Start();
t.Wait();
}
});
producerThread.IsBackground = true;
producerThread.Start();
Run(startTest, threadOperationCounts, hasOneResult: true);
}
private static void MonitorReliableEnterExitLatency(int threadCount)
{
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
int previousLockThreadId = -1;
var threadOperationCounts = new int[(threadCount + 1) * 16];
var m = new object();
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
int threadId = Environment.CurrentManagedThreadId;
var localThreadOperationCounts = threadOperationCounts;
var localM = m;
var rng = new Random(threadIndex);
threadReady.Set();
startTest.WaitOne();
while (true)
{
var d0 = RandomShortDelay(rng);
var d1 = RandomShortDelay(rng);
lock (localM)
{
previousLockThreadId = threadId;
Delay(d0);
}
++localThreadOperationCounts[threadIndex];
Delay(d1);
while (previousLockThreadId == threadId)
Delay(4);
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
Run(startTest, threadOperationCounts);
}
private static void MonitorReliableEnterExitThroughput(int threadCount, bool delay, bool convertToAwareLock)
{
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var m = new object();
if (convertToAwareLock)
Monitor.Enter(m);
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
var localDelay = delay;
var localThreadOperationCounts = threadOperationCounts;
var localM = m;
var rng = localDelay ? new Random(threadIndex) : null;
threadReady.Set();
if (convertToAwareLock)
{
Monitor.Enter(localM);
Monitor.Exit(localM);
}
startTest.WaitOne();
if (localDelay)
{
while (true)
{
var d0 = RandomShortDelay(rng);
var d1 = RandomShortDelay(rng);
lock (localM)
Delay(d0);
++localThreadOperationCounts[threadIndex];
Delay(d1);
}
}
else
{
while (true)
{
lock (localM)
{
}
++localThreadOperationCounts[threadIndex];
}
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
if (convertToAwareLock)
{
Thread.Sleep(50);
Monitor.Exit(m);
}
Run(startTest, threadOperationCounts);
}
private static void MonitorEnterExitThroughput(int threadCount, bool delay, bool convertToAwareLock)
{
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var m = new object();
if (convertToAwareLock)
Monitor.Enter(m);
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
var localDelay = delay;
var localThreadOperationCounts = threadOperationCounts;
var localM = m;
var rng = localDelay ? new Random(threadIndex) : null;
threadReady.Set();
if (convertToAwareLock)
{
Monitor.Enter(localM);
Monitor.Exit(localM);
}
startTest.WaitOne();
if (localDelay)
{
while (true)
{
var d0 = RandomShortDelay(rng);
var d1 = RandomShortDelay(rng);
Monitor.Enter(localM);
Delay(d0);
Monitor.Exit(localM);
++localThreadOperationCounts[threadIndex];
Delay(d1);
}
}
else
{
while (true)
{
Monitor.Enter(localM);
Monitor.Exit(localM);
++localThreadOperationCounts[threadIndex];
}
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
if (convertToAwareLock)
{
Thread.Sleep(50);
Monitor.Exit(m);
}
Run(startTest, threadOperationCounts);
}
private static void MonitorTryEnterExitThroughput(int threadCount, bool delay, bool convertToAwareLock)
{
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var m = new object();
if (convertToAwareLock)
Monitor.Enter(m);
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
var localDelay = delay;
var localThreadOperationCounts = threadOperationCounts;
var localM = m;
var rng = localDelay ? new Random(threadIndex) : null;
threadReady.Set();
if (convertToAwareLock)
{
Monitor.Enter(localM);
Monitor.Exit(localM);
}
startTest.WaitOne();
if (localDelay)
{
while (true)
{
var d0 = RandomShortDelay(rng);
var d1 = RandomShortDelay(rng);
if (!Monitor.TryEnter(localM, -1))
return;
Delay(d0);
Monitor.Exit(localM);
++localThreadOperationCounts[threadIndex];
Delay(d1);
}
}
else
{
while (true)
{
if (!Monitor.TryEnter(localM, -1))
return;
Monitor.Exit(localM);
++localThreadOperationCounts[threadIndex];
}
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
if (convertToAwareLock)
{
Thread.Sleep(50);
Monitor.Exit(m);
}
Run(startTest, threadOperationCounts);
}
private static void MonitorReliableEnterExit1PcTOtherWorkThroughput(int threadCount)
{
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var otherWorkThreadOperationCounts = new int[(ProcessorCount + 1) * 16];
var m = new object();
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
var localThreadOperationCounts = threadOperationCounts;
var localM = m;
var rng = new Random((int)data);
threadReady.Set();
startTest.WaitOne();
while (true)
{
var d0 = RandomShortDelay(rng);
var d1 = RandomShortDelay(rng);
lock (localM)
Delay(d0);
++localThreadOperationCounts[threadIndex];
Delay(d1);
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
ParameterizedThreadStart otherWorkThreadStart = data =>
{
int threadIndex = (int)data;
var localOtherWorkThreadOperationCounts = otherWorkThreadOperationCounts;
var rng = new Random(threadIndex);
threadReady.Set();
startTest.WaitOne();
while (true)
{
Delay(RandomShortDelay(rng));
++localOtherWorkThreadOperationCounts[threadIndex];
}
};
var otherWorkThreads = new Thread[ProcessorCount];
for (int i = 0; i < otherWorkThreads.Length; ++i)
{
var t = new Thread(otherWorkThreadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
otherWorkThreads[i] = t;
}
RunWithOtherWork(startTest, threadOperationCounts, otherWorkThreadOperationCounts);
}
private static void MonitorReliableEnterExitRoundRobinThroughput(int threadCount)
{
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var mutexes = new object[threadCount];
for (int i = 0; i < mutexes.Length; ++i)
mutexes[i] = new object();
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
var localThreadOperationCounts = threadOperationCounts;
var localMutexes = mutexes;
int mutexCount = localMutexes.Length;
int mutexIndex = (threadIndex / 16 - 1) % mutexCount;
var rng = new Random(threadIndex);
threadReady.Set();
startTest.WaitOne();
while (true)
{
var d0 = RandomShortDelay(rng);
var d1 = RandomShortDelay(rng);
lock (localMutexes[mutexIndex])
Delay(d0);
++localThreadOperationCounts[threadIndex];
Delay(d1);
mutexIndex = (mutexIndex + 1) % mutexCount;
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
Run(startTest, threadOperationCounts);
}
private static void MonitorReliableEnterExitFairness(int threadCount)
{
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var threadWaitDurationsUs = new double[(threadCount + 1) * 16];
var m = new object();
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
var localThreadOperationCounts = threadOperationCounts;
var localThreadWaitDurationsUs = threadWaitDurationsUs;
var localM = m;
var rng = new Random(threadIndex);
threadReady.Set();
startTest.WaitOne();
while (true)
{
var d0 = RandomShortDelay(rng);
var d1 = RandomShortDelay(rng);
var startTicks = Clock.Ticks;
long stopTicks;
lock (localM)
{
stopTicks = Clock.Ticks;
Delay(d0);
}
++localThreadOperationCounts[threadIndex];
localThreadWaitDurationsUs[threadIndex] +=
BiasWaitDurationUsAgainstLongWaits(Clock.TicksToUs(stopTicks - startTicks));
Delay(d1);
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
RunFairness(startTest, threadOperationCounts, threadWaitDurationsUs);
}
private static void MonitorTryEnterExitWhenUnlockedThroughput_ThinLock(int threadCount)
{
threadCount = 1;
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var m = new object();
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
var localThreadOperationCounts = threadOperationCounts;
var localM = m;
threadReady.Set();
startTest.WaitOne();
while (true)
{
if (!Monitor.TryEnter(localM))
return;
Monitor.Exit(localM);
++localThreadOperationCounts[threadIndex];
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
Run(startTest, threadOperationCounts);
}
private static void MonitorTryEnterExitWhenUnlockedThroughput_AwareLock(int threadCount)
{
threadCount = 1;
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var m = new object();
Monitor.Enter(m);
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
var localThreadOperationCounts = threadOperationCounts;
var localM = m;
threadReady.Set();
Monitor.Enter(localM);
Monitor.Exit(localM);
startTest.WaitOne();
while (true)
{
if (!Monitor.TryEnter(localM))
return;
Monitor.Exit(localM);
++localThreadOperationCounts[threadIndex];
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
Thread.Sleep(50);
Monitor.Exit(m);
Run(startTest, threadOperationCounts);
}
private static void MonitorTryEnterWhenLockedThroughput_ThinLock(int threadCount)
{
threadCount = 1;
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var m = new object();
Monitor.Enter(m);
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
var localThreadOperationCounts = threadOperationCounts;
var localM = m;
threadReady.Set();
startTest.WaitOne();
while (true)
{
if (Monitor.TryEnter(localM))
return;
++localThreadOperationCounts[threadIndex];
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
Run(startTest, threadOperationCounts);
Monitor.Exit(m);
}
private static void MonitorTryEnterWhenLockedThroughput_AwareLock(int threadCount)
{
threadCount = 1;
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
var m = new object();
Monitor.Enter(m);
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
var localThreadOperationCounts = threadOperationCounts;
var localM = m;
threadReady.Set();
if (Monitor.TryEnter(localM, 50))
return;
startTest.WaitOne();
while (true)
{
if (Monitor.TryEnter(localM))
return;
++localThreadOperationCounts[threadIndex];
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
Thread.Sleep(50);
Run(startTest, threadOperationCounts);
Monitor.Exit(m);
}
private static unsafe void BufferMemoryCopyThroughput(int maxBytes)
{
const int threadCount = 1;
int minBytes = maxBytes <= 8 ? 1 : maxBytes / 2 + 1;
var threadReady = new AutoResetEvent(false);
var startTest = new ManualResetEvent(false);
var threadOperationCounts = new int[(threadCount + 1) * 16];
ParameterizedThreadStart threadStart = data =>
{
int threadIndex = (int)data;
var localThreadOperationCounts = threadOperationCounts;
var rng = new Random(0);
var src = stackalloc byte[maxBytes];
var dst = stackalloc byte[maxBytes];
for (int i = 0; i < maxBytes; ++i)
src[i] = (byte)rng.Next();
threadReady.Set();
startTest.WaitOne();
while (true)
{
Buffer.MemoryCopy(src, dst, maxBytes, rng.Next(minBytes, maxBytes + 1));
++localThreadOperationCounts[threadIndex];
}
};
var threads = new Thread[threadCount];
for (int i = 0; i < threads.Length; ++i)
{
var t = new Thread(threadStart);
t.IsBackground = true;
t.Start((i + 1) * 16);
threadReady.WaitOne();
threads[i] = t;
}
Run(startTest, threadOperationCounts, iterations: 1);
}
private static void Run(
ManualResetEvent startTest,
int[] threadOperationCounts,
bool hasOneResult = false,
int iterations = 4)
{
var sw = new Stopwatch();
int threadCount = threadOperationCounts.Length / 16 - 1;
var afterWarmupOperationCounts = new long[threadCount];
var operationCounts = new long[threadCount];
startTest.Set();
// Warmup
Thread.Sleep(100);
//while (true)
for (int j = 0; j < iterations; ++j)
{
for (int i = 0; i < threadCount; ++i)
afterWarmupOperationCounts[i] = threadOperationCounts[(i + 1) * 16];
// Measure
sw.Restart();
Thread.Sleep(500);
sw.Stop();
for (int i = 0; i < threadCount; ++i)
operationCounts[i] = threadOperationCounts[(i + 1) * 16];
for (int i = 0; i < threadCount; ++i)
operationCounts[i] -= afterWarmupOperationCounts[i];
double score = operationCounts.Sum() / sw.Elapsed.TotalMilliseconds;
Console.WriteLine("Score: {0:0.000000}", score);
}
}
private static void RunWithOtherWork(
ManualResetEvent startTest,
int[] threadOperationCounts,
int[] otherWorkThreadOperationCounts,
int iterations = 4)
{
var sw = new Stopwatch();
int threadCount = threadOperationCounts.Length / 16 - 1;
int otherWorkThreadCount = otherWorkThreadOperationCounts.Length / 16 - 1;
var afterWarmupOperationCounts = new long[threadCount];
var otherWorkAfterWarmupOperationCounts = new long[otherWorkThreadCount];
var operationCounts = new long[threadCount];
var otherWorkOperationCounts = new long[otherWorkThreadCount];
var operationCountSums = new double[2];
startTest.Set();
// Warmup
Thread.Sleep(100);
//while (true)
for (int j = 0; j < iterations; ++j)
{
for (int i = 0; i < afterWarmupOperationCounts.Length; ++i)
afterWarmupOperationCounts[i] = threadOperationCounts[(i + 1) * 16];
for (int i = 0; i < otherWorkAfterWarmupOperationCounts.Length; ++i)
otherWorkAfterWarmupOperationCounts[i] = otherWorkThreadOperationCounts[(i + 1) * 16];
// Measure
sw.Restart();
Thread.Sleep(500);
sw.Stop();
for (int i = 0; i < operationCounts.Length; ++i)
operationCounts[i] = threadOperationCounts[(i + 1) * 16];
for (int i = 0; i < otherWorkOperationCounts.Length; ++i)
otherWorkOperationCounts[i] = otherWorkThreadOperationCounts[(i + 1) * 16];
for (int i = 0; i < operationCounts.Length; ++i)
operationCounts[i] -= afterWarmupOperationCounts[i];
for (int i = 0; i < otherWorkOperationCounts.Length; ++i)
otherWorkOperationCounts[i] -= otherWorkAfterWarmupOperationCounts[i];
operationCountSums[0] = operationCounts.Sum();
operationCountSums[1] = otherWorkOperationCounts.Sum();
double score = operationCountSums.GeometricMean(1, otherWorkThreadCount) / sw.Elapsed.TotalMilliseconds;
Console.WriteLine("Score: {0:0.000000}", score);
}
}
private static void RunFairness(
ManualResetEvent startTest,
int[] threadOperationCounts,
double[] threadWaitDurationsUs,
int iterations = 4)
{
var sw = new Stopwatch();
int threadCount = threadWaitDurationsUs.Length / 16 - 1;
var afterWarmupOperationCounts = new long[threadCount];
var afterWarmupWaitDurationsUs = new double[threadCount];
var operationCounts = new long[threadCount];
var waitDurationsUs = new double[threadCount];
startTest.Set();
// Warmup
Thread.Sleep(100);
//while (true)
for (int j = 0; j < iterations; ++j)
{
for (int i = 0; i < threadCount; ++i)
afterWarmupOperationCounts[i] = threadOperationCounts[(i + 1) * 16];
for (int i = 0; i < threadCount; ++i)
afterWarmupWaitDurationsUs[i] = threadWaitDurationsUs[(i + 1) * 16];
// Measure
sw.Restart();
Thread.Sleep(500);
sw.Stop();
for (int i = 0; i < threadCount; ++i)
{
int ti = (i + 1) * 16;
operationCounts[i] = threadOperationCounts[ti];
waitDurationsUs[i] = threadWaitDurationsUs[ti];
}
for (int i = 0; i < threadCount; ++i)
{
operationCounts[i] -= afterWarmupOperationCounts[i];
waitDurationsUs[i] -= afterWarmupWaitDurationsUs[i];
}
double averageWaitDurationUs = Math.Sqrt(waitDurationsUs.Sum() / operationCounts.Sum());
if (averageWaitDurationUs < 1)
averageWaitDurationUs = 1;
double score = 100_000 / averageWaitDurationUs;
Console.WriteLine($"Score: {score:0.000000}");
}
}
private static double BiasWaitDurationUsAgainstLongWaits(double waitDurationUs) =>
waitDurationUs <= 1 ? 1 : waitDurationUs * waitDurationUs;
internal static class Clock
{
private static readonly long s_swFrequency = Stopwatch.Frequency;
private static readonly double s_swFrequencyDouble = s_swFrequency;
public static long Ticks => Stopwatch.GetTimestamp();
public static double TicksToS(long ticks) => ticks / s_swFrequencyDouble;
public static double TicksToMs(long ticks) => ticks * 1000 / s_swFrequencyDouble;
public static double TicksToUs(long ticks) => ticks * (1000 * 1000) / s_swFrequencyDouble;
}
private static uint RandomShortDelay(Random rng) => (uint)rng.Next(4, 10);
private static uint RandomMediumDelay(Random rng) => (uint)rng.Next(10, 15);
private static uint RandomLongDelay(Random rng) => (uint)rng.Next(15, 20);
private static int[] s_delayValues = new int[32];
private static void Delay(uint n)
{
Interlocked.MemoryBarrier();
s_delayValues[16] += (int)Fib(n);
}
private static uint Fib(uint n)
{
if (n <= 1)
return n;
return Fib(n - 2) + Fib(n - 1);
}
public static double GeometricMean(this IEnumerable<double> values, params double[] weights)
{
double logSum = 0, weightSum = 0;
int weightIndex = 0;
foreach (var value in values)
{
if (weightIndex >= weights.Length)
throw new InvalidOperationException();
var weight = weights[weightIndex];
++weightIndex;
logSum += Math.Log(value) * weight;
weightSum += weight;
}
return Math.Exp(logSum / weightSum);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment