torsdag 10 november 2011

CPU load generator and stackdump

Today I am concerned with trying to generate a test CPU load on my machine. It all started out with a tool for creating dumps of .NET processes that I have tried to improve. Using full process dumps gets you all the details you need and more, but sometimes it is overkill. It is also sometimes difficult to instruct others on how to do this. And the dump files produced can be VERY large.

The stackdump tool mentioned above creates a nice stacktrace for each managed thread executing in the chosen process. I wanted to add some information giving me a hint of which thread is causing the problem in case of a CPU load problem.

Adding this and getting reliable results was a little trickier than I imagined. As a result of this a need to generate a test load arised. This lead me, once again, into dealing with the not so perfect timing functions of .NET. The best I could come up with was to use Thread.Sleep() combined with a self adjusting for loop that produced the load. I also found the best way available method for measuring time was the Stopwatch class.

The code below produces a good enough result for producing the desired load in one thread. You can  also fire it up in multiple threads if needed.

        private static void ThreadLoop(int percentLoad)
        {
            int loopMax = 1000;
            long ixStable = 0;
            var stopwatch = new Stopwatch();
            stopwatch.Start();

            var workPeriod = percentLoad * IntervalTargetTicks / 100;
            var idlePeriod = IntervalTargetTicks - workPeriod;

            while (!ThreadsAborted)
            {
                var t1 = stopwatch.Elapsed.Ticks;
                for (int i = 0; i < loopMax; i++)
                {
                    count++;
                }
                var t2 = stopwatch.Elapsed.Ticks;
                var tLoop = t2 - t1;

                // Do the sleep
                Thread.Sleep((int)(idlePeriod / TimeSpan.TicksPerMillisecond));

                var tMeasuredSleep = stopwatch.Elapsed.Ticks - t2;

                if (tLoop < workPeriod - workPeriod / 4) loopMax += loopMax / 4;
                else if (tLoop > workPeriod + workPeriod / 4) loopMax -= loopMax / 4;
                else
                {
                    ixStable++;
                }

                if (ixStable == 50) 
                {
                    Console.Out.WriteLine(
                        "OS Thread ID {0}({1}%): Stable with expected sleep {2} ms, loop {3} ms, actual sleep {4} ms",
                        AppDomain.GetCurrentThreadId(),
                        percentLoad,
                        (float)idlePeriod / TimeSpan.TicksPerMillisecond,
                        (float)tLoop / TimeSpan.TicksPerMillisecond,
                        (float)tMeasuredSleep / TimeSpan.TicksPerMillisecond);
                }
            }
        }

Inga kommentarer:

Skicka en kommentar