I have heard that WaitHandle
derived synchronization primitives are expensive, but couldn't find further details. Is there any performance comparison to other equivalent or similar primitives available?
Asked
Active
Viewed 456 times
0

sharpener
- 1,383
- 11
- 22
-
They involve a kernel call, that's not very cheap. Otherwise necessary to ensure that the operating system can reschedule the thread when it gets blocked. A class like ManualResetEventSlim tries to do something about it, first checking a cheap internal lock and only calling the kernel function when it needs to block. You'll need to go slim shopping, I guess, the question is entirely too broad to give specific advice. – Hans Passant Oct 30 '15 at 16:56
1 Answers
0
For one particular case, where AutoResetEvent
and Monitor
can be interchanged, the performance of the monitor is at least 1.5 times better (2 times typically). This statement is based on the following raw producer/consumer test (Release build) and is valid in the case there is no error in the test logic :o) Anyway, might be usefull for someone...
Now, I'm trying to be the one who races the horses ;)
Code:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using Timer = System.Timers.Timer;
namespace ThreadSync2
{
static class Measurement
{
private static readonly Random rng = new Random();
private static long Evaluate(string methodName, Action signal, Action wait)
{
var isProducing = false;
var isConsuming = false;
var mtxData = new object();
var data = new Queue<Tuple<long,int>>();
var produced = 0L;
var consumed = 0L;
var watch = new Stopwatch();
var producer = new Thread(() =>
{
Debug.WriteLine("Production : started.");
while (isProducing)
{
var item = rng.Next(0, 100);
lock (mtxData)
data.Enqueue(new Tuple<long, int>(produced, item));
signal();
Debug.WriteLine($"{watch.Elapsed.Seconds,3:d}.{watch.Elapsed.Milliseconds:d3} Producer: {produced,6:d} [{item,3:d}]");
++produced;
}
Debug.WriteLine("Production : finished.");
}){Name = "Producer"};
var consumer = new Thread(() =>
{
Debug.WriteLine("Consumption: started.");
while ( (isConsuming)
|| (data.Count > 0))
{
while (data.Count > 0)
{
Tuple<long, int> record;
lock (mtxData)
record = data.Dequeue();
Debug.WriteLine($"{watch.Elapsed.Seconds,3:d}.{watch.Elapsed.Milliseconds:d3} Consumer: {record.Item1,6:d} [{record.Item2,3:d}]");
++consumed;
}
wait();
}
Debug.WriteLine("Consumption: finished.");
}){Name = "Consumer"};
var timer = new Timer(5000){AutoReset = false};
timer.Elapsed += (s, e) => {isProducing = false;};
Console.WriteLine($"Evaluating \"{methodName}\"...");
watch.Start();
timer.Enabled = true;
isConsuming = true;
isProducing = true;
consumer.Start();
producer.Start();
producer.Join();
isConsuming = false;
consumer.Join();
watch.Stop();
Console.WriteLine($"Produced items: {produced:### ### ##0}{((produced != consumed) ? $", Consumed items: {consumed:### ### ##0}" : "")}");
Console.WriteLine();
return produced;
}
public static void Evaluate()
{
const string strMonitorLock = "Monitor locking";
const string strWaitHandle = "AutoResetEvent";
const int waitTimeout = 500;
var semMonitorLock = new object();
var semWaitHandle = new AutoResetEvent(false);
var cntMonitorLock = Evaluate
(
strMonitorLock,
() => {lock (semMonitorLock) Monitor.Pulse(semMonitorLock);},
() => {lock (semMonitorLock) Monitor.Wait (semMonitorLock, waitTimeout);}
);
var cntWaitHandle = Evaluate
(
strWaitHandle,
() => semWaitHandle.Set(),
() => semWaitHandle.WaitOne(waitTimeout)
);
Console.WriteLine($"{strMonitorLock} / {strWaitHandle} = {((double)cntMonitorLock / cntWaitHandle):0.000}");
Console.WriteLine();
}
}
class Program
{
static Program()
{
Debug.Listeners.Add(new TextWriterTraceListener(Console.Out));
}
static void Main(string[] args)
{
Measurement.Evaluate();
}
}
}
Console output for repeated executions:
Evaluating "Monitor locking"... Produced items: 15 710 306 Evaluating "AutoResetEvent"... Produced items: 5 998 742 Monitor locking / AutoResetEvent = 2,619 Evaluating "Monitor locking"... Produced items: 11 697 953 Evaluating "AutoResetEvent"... Produced items: 7 119 778 Monitor locking / AutoResetEvent = 1,643 Evaluating "Monitor locking"... Produced items: 11 662 575 Evaluating "AutoResetEvent"... Produced items: 6 437 772 Monitor locking / AutoResetEvent = 1,812 Evaluating "Monitor locking"... Produced items: 12 981 254 Evaluating "AutoResetEvent"... Produced items: 5 934 954 Monitor locking / AutoResetEvent = 2,187 Evaluating "Monitor locking"... Produced items: 14 661 293 Evaluating "AutoResetEvent"... Produced items: 6 370 518 Monitor locking / AutoResetEvent = 2,301 Evaluating "Monitor locking"... Produced items: 15 050 786 Evaluating "AutoResetEvent"... Produced items: 5 912 671 Monitor locking / AutoResetEvent = 2,546
-
Good test code. But, I find your use of *Hungarian Notation* obscene in the extreme. – GreatAndPowerfulOz Oct 30 '15 at 16:43