Skip to content

Semaphore performance issue #113411

Description

@eduardo-vp

Description

There's a performance issue regarding Semaphore in scenarios where threads request and release the semaphore with no work in between as shown in the snippet below. The rate of transactions is up to 3-4 times lower compared to other runtimes.

using System;
using System.Diagnostics;
using System.Threading;

namespace MultiThreadedScaling
{
  internal class ScalingIssue
  {
    static ReaderWriterLockSlim slimRWlock = null;
    static Semaphore normalSemaphore = null;

    static int numThreads = 8;
    static int run_time = 30; // seconds

    static void Main(string[] args)
    {
      Console.WriteLine("Running test with " + numThreads + " threads for " + run_time + " seconds");

      slimRWlock = new ReaderWriterLockSlim();
      normalSemaphore = new Semaphore(numThreads, Int32.MaxValue);
      for (int i = 0; i < numThreads; i++)
      {
        Thread t1 = new Thread(new ParameterizedThreadStart(OpenCloseSimulation));
        t1.Name = i.ToString();
        t1.Start(i);
      }
    }

    static void OpenCloseSimulation(object state)
    {
      int index = (int)state;
      int numTxns = 0;
      var start_time = DateTime.UtcNow;
      while (DateTime.UtcNow - start_time < TimeSpan.FromSeconds(run_time))
      {
        OpenConnSimulation(index);
        CloseConnSimulation(index);
        numTxns += 1;
      }
      var end_time = DateTime.UtcNow;
      long totRunTime = (long)(end_time - start_time).TotalSeconds;
      Console.WriteLine("Thread " + Thread.CurrentThread.Name + " Transaction rate: commits= " + (numTxns / totRunTime));
    }

    static void OpenConnSimulation(int index)
    {
      normalSemaphore.WaitOne();
      DoWork();
      slimRWlock.EnterReadLock();
      DoWork();
      slimRWlock.ExitReadLock();
    }

    static void CloseConnSimulation(int index)
    {
      DoWork();
      normalSemaphore.Release();
    }

    internal static void DoWork()
    {
      return;
    }
  }
}

This scenario was tested with .NET 9, arch x64 for both Windows and Linux. Machines had 16 vcpus and the test was ran using 8 threads during 30 seconds.

The transactions per second rate on Windows is ~320K whereas on Linux the rate is ~130K. Other runtimes can achieve around ~500K+ on both platforms.

Native AOT achieved similar results to the non Native AOT versions.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions