0

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?

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 Answers1

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
Community
  • 1
  • 1
sharpener
  • 1,383
  • 11
  • 22