17/09/2014

Zużycie procesora przez wskazany proces


Potrzebowałem ustalić programowo bieżące zużycie procesora przez wskazany proces. Zacząłem od przyjrzenia się licznikom wydajności i szybko naklepałem coś takiego:
var p = Process.GetProcessById(id);
var counter = new PerformanceCounter("Process", "% Processor Time", myProcess.ProcessName);
Console.WriteLine("Processor %: {0}", counter.NextValue());
Przetestowałem i wyniki okazały się dziwne ponieważ czasami uzyskiwałem zużycie większe niż 100%. Chwila, ale przecież mam 8 rdzeniowy procesor. Spróbujmy więc czegoś takiego:
Console.WriteLine("Processor %: {0}", counter.NextValue() / Environment.ProcessorCount);
To już działało dobrze. Postanowiłem jednak poszukać alternatywnego sposobu i przyjrzałem się co oferuje klasa Process. Szybko znalazłem właściwość Process.TotalProcessorTime, która zgodnie ze swoją nazwą zwraca całkowity czas procesora zużyty przez dany proces począwszy od jego uruchomienia. Ja potrzebowałem natomiast aktualnego zużycia. Trochę myślenia, szukania w Internecie (na przykład tutaj) i szybko dopisałem coś takiego:
public class Utils
{
    public class ProcessorUtilizationInfo
    {
        public TimeSpan LastProcessorTime { get; set; }
        public DateTime LastCheck { get; set; }
        public double Value { get; set; }
        public Process Process { get; set; }
    }

    public static ProcessorUtilizationInfo GetProcessorUtilization(ProcessorUtilizationInfo info)
    {
        info.Process.Refresh();

        var now = DateTime.Now;
        var elapsed = now - info.LastCheck;
        var processorTime = (info.Process.TotalProcessorTime - info.LastProcessorTime);

        info.LastProcessorTime = info.Process.TotalProcessorTime;
        info.LastCheck = now;
        info.Value = processorTime.TotalMilliseconds / elapsed.TotalMilliseconds / Environment.ProcessorCount;

        return info;
    }
}
Dla przejrzystości pominąłem walidację danych. Klasa pomocnicza ProcessorUtilizationInfo jest potrzebna gdybyśmy chcieli wołać metodę GetProcessorUtilization wielokrotnie dla tego samego procesu. Ktoś może marudzić, że używam DateTime.Now, że wynik może być nieprecyzyjny, ale moje testy pokazały, że zastosowanie licznika wydajności i metody GetProcessorUtilization daje podobny rezultaty. Przykład użycia metody GetProcessorUtilization wygląda następująco:
var p = Process.GetProcessById(id);
var info = new Utils.ProcessorUtilizationInfo {Process = p};
Console.WriteLine("Processor %: {0}", Utils.GetProcessorUtilization(info).Value * 100);
Do opisanych 2 sposobów uzyskania aktualnego zużycia procesora dla wskazanego procesu mam jedno zastrzeżenie. Otóż oba rozwiązania dają co prawda bardzo zbliżone wyniki (zaobserwowałem różnice rzędu 0.1 - 0.2%), ale wyniki te różniły się nawet o 1% od tego co pokazywał menadżer zadań. Ktoś wie dlaczego? Może znacie lepsze rozwiązanie postawionego problemu?

No comments:

Post a Comment