Jednoduchý performance test v C# pomocou System.Diagnostics.Stopwach

Každý vývojár, ktorý sa zaoberá optimalizáciou výkonu, skôr alebo neskôr natrafí na na triedu StopWatch nachádzajúcej sa v mennom priestore System.Diagnostics. V prípade, ak ste už niekedy robili testy výkonu, určite ste si už stihli všimnúť že výsledky sa môžu líšiť až o 25%-30%. V tomto článku vám ukážem, ako optimálne navrhovať jednovláknové testovacie programy tak, aby rozdiel vo výsledkoch bol v rozmedzí 0.1%-0.2%.

V dnešnej dobe majú moderné počítače procesory s viacerými jadrami, veľkou cache atď. a inými vecami, ktoré môžu výrazne ovplyvniť čas vykonávania algoritmu. Tzv. White Box techniky s attachnutym debuggerom výrazne skresľujú výsledky testov a spomaľujú vykonávanie algoritmu, pretože CPU je zaťažované väčším množstvom inštrukcií. Na druhej strane Black Box techniky s neattachnutým debuggerom nám poskytujú ďaleko viac relevantných informácií.

Jedna z najdôležitejších vecí je zabezpečiť, aby sa funkcia vykonávala práve na jednom jadre procesora. To môžeme docieliť nastavením vlastnosti ProcessorAffinity nášho procesu:

Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2);

V ďalšom kroku musíme zabezpečiť, aby ostatné vlákna nevyužívali naše jadro/procesor. Toho docielime tak, že nastavíme nášmu vláknu najvyššiu prioritu a ostaným nižšiu:

Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
Thread.CurrentThread.Priority = ThreadPriority.Highest;

Ešte pred začatím samotného testovania, je nutné spraviť tzv. zahrievacie kolo (Warmup), po ktorom by mali byť výsledky už viac menej stabilné. V nasledujúcom príklade som zvolil dĺžku Warmup fázy 1200 ms (dĺžka warmupu sa môže na rôznych počítačoch meniť).

stopwatch.Start();
while (stopwatch.ElapsedMilliseconds < 1200)
{
    result = TestFunction(seed, count); 
}
stopwatch.Stop(); 

Kompletný príklad:

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

namespace PreciseMeasure
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch stopwatch = new Stopwatch();

            long seed = Environment.TickCount; long result = 0;
            int count = 100000000;

            Console.WriteLine("20 testov bez korektnej prípravy");
            Console.WriteLine("Warmup");
            for (int repeat = 0; repeat < 20; ++repeat)
            {
                stopwatch.Reset();
                stopwatch.Start();
                result ^= TestFunction(seed, count);
                stopwatch.Stop();
                Console.WriteLine("Ticks: " + stopwatch.ElapsedTicks +" mS: " + stopwatch.ElapsedMilliseconds);
            }

            Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2);
            Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High; Thread.CurrentThread.Priority = ThreadPriority.Highest;

            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("20 testov s korektnou pripravou");
            Console.WriteLine("Warmup");
            stopwatch.Reset();
            stopwatch.Start();
            while (stopwatch.ElapsedMilliseconds < 1200)
            {
                result = TestFunction(seed, count); // Warmup
            }
            stopwatch.Stop();

            for (int repeat = 0; repeat < 20; ++repeat)
            {
                stopwatch.Reset();
                stopwatch.Start();
                result ^= TestFunction(seed, count);
                stopwatch.Stop();
                Console.WriteLine("Ticks: " + stopwatch.ElapsedTicks +
                " mS: " + stopwatch.ElapsedMilliseconds);
            }
            Console.WriteLine(result);
        }

        public static long TestFunction(long seed, int count)
        {
            long result = seed;
            for (int i = 0; i < count; ++i)
            {
                result ^= i ^ seed;
            }
            return result;
        }
    }
}

Publikované Tuesday, November 08, 2011 6:44 PM kanasz.robert
Zaradené do: ,

Komentáre

# re: Jednoduchý performance test v C# pomocou System.Diagnostics.Stopwach

Tuesday, November 08, 2011 7:01 PM liero

jednoduche, zrozumitelne, uzitocne.

pekny prispevok

# re: Jednoduchý performance test v C# pomocou System.Diagnostics.Stopwach

Tuesday, November 08, 2011 7:27 PM kanasz.robert

vdaka. akurat v poslednom case sa zaoberam trochu optimalizaciou a ked budem mat cas, tak by som postupne pridaval dalsie prispevky na tuto temu.

# re: Jednoduchý performance test v C# pomocou System.Diagnostics.Stopwach

Tuesday, November 08, 2011 8:21 PM liero

co sa tyka optimalizacie, tak to budem tvoj verny citatel,v tomto mam medzery. najviac by ma zaujimalo, ktore optimalizacie robi kompilator a teda ich nemusim pisat na ukor prehladnosti kodu.

Napriklad definovat premennu mimo foreach bloku, ak ju chcem pouzivat iba vnutri nema vyznam, lebo kompilator ju aj tak vyhodi pred cyklus. ale takto ju mam aspon v spravnom scope

# re: Jednoduchý performance test v C# pomocou System.Diagnostics.Stopwach

Wednesday, November 09, 2011 11:42 AM dudok

hmmm to o tom foreach som vážne nevedel liero ... ďakujem :)