Wydajność: spinning a synchronizacja kernel

Wielokrotnie pisałem o różnych metodach definiowania sekcji krytycznej w kodzie. Do dyspozycji mamy spinning, który nie usypia wątku. Tak naprawdę dla systemu Windows, taki wątek wciąż istnieje i wykonuje pracę – innymi słowy marnuje  czas CPU. Jeśli chcemy zatrzymać wątek na krótko wtedy jest to bardzo wydajne ponieważ nie musimy zmieniać kontekstu (BARDZO kosztowne), korzystać z funkcji Windows (spinning to czysta metoda .NET) czy planować (scheduling) następnych wątków. Jeśli mechanizmy takie jak spinning czy semafor nie są jasne, koniecznie zachęcam do przeczytania poprzednich wpisów. Dzisiaj wyłącznie zaprezentuję pewny test, który pokaże różnice w liczbach – część teoretyczna została już omówiona kiedyś na tym blogu.

Test:

internal static class Sample
{
   public static void Main()
   {
       Task.Factory.StartNew(Run);
       Console.ReadLine();
   }
   private static void Run()
   {
       int value = 0;
       const Int32 iterations = 10000000;

       // bez synchronizacji
       Stopwatch sw = Stopwatch.StartNew();
       for (int i = 0; i < iterations; i++)
           value++;

       Console.WriteLine("Bez synchronizacji:{0}", sw.ElapsedMilliseconds);

       // spinlock
       sw.Restart();
       value = 0;
       SpinLock spinLock = new SpinLock();
       for (int i = 0; i < iterations; i++)
       {
           bool lockTaken = false;
           try
           {
               spinLock.Enter(ref lockTaken);
               value++;
           }
           finally
           {
               if (lockTaken)
                   spinLock.Exit();
           }
       }
       Console.WriteLine("Synchronizacja spinlock:{0}", sw.ElapsedMilliseconds);

       // semafor (kernel)
       sw.Restart();
       value = 0;
       Semaphore semaphore=new Semaphore(1,1);
       for (int i = 0; i < iterations; i++)
       {
           semaphore.WaitOne();
               value++;
           semaphore.Release();
       }
       Console.WriteLine("Synchronizacja lock: {0}", sw.ElapsedMilliseconds);
   }
}

Wynik to:

  1. Bez synchronizacji: 35
  2. Spinning: 1586
  3. Wywołanie funkcji systemowej: 21604

Spinning jest dużo szybszy gdy nie ma konfliktów. W powyższym przykładzie wyłącznie jeden wątek próbuje w tym samym czasie wejść do sekcji krytycznej. Oczywiście w praktyce w takiej sytuacji nie korzystalibyśmy z synchronizacji. Celem wpisu jest jednak pokazanie, że dla krótkotrwałych blokad, szybszy jest spinning. Semaphore to funkcja systemowa (kernel) i  nawet same wywołanie jest wolniejsze. Ponadto powoduje ona uśpienie wątku, zmianę kontekstu i ponowne planowanie. W jednym z następnych wpisów, przyjrzymy się lock, który jest tak naprawdę dość skomplikowany.

Leave a Reply

Your email address will not be published.