C # System CPU Brug og synkronisering med Windows Task Manager

Indlæg af Hanne Mølgaard Plasc

Problem



Dette er et todelt spørgsmål, jeg ønskede at sende min kode her på stakken for at hjælpe andre med samme opgave.


Spørgsmål 1:


Jeg har en delmængde kode, som jeg mener korrekt måler CPU-brugen (på tværs af så mange kerner i systemet som pr. Tid, der hentes) i henhold til måleintervallet - jeg bruger 1 sekund i trådsamtalen.


Jeg måtte dechifrere dette fra de meget få artikler på nettet og fra C ++-koden. Mit spørgsmål er, at for spørgsmål 1 er er dette korrekt, hvad jeg har gjort?


Nogle gange er den returnerede værdi en minus figur, hvorfor jeg multiplicerer med -1. Igen antager jeg, da der er meget lidt dokumentation, at dette er hvad jeg skal gøre.


Jeg har følgende kode:


public static class Processor
{
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool GetSystemTimes(out ComTypes.FILETIME lpIdleTime, out ComTypes.FILETIME lpKernelTime, out ComTypes.FILETIME lpUserTime);

    private static TimeSpan \_sysIdleOldTs;
    private static TimeSpan \_sysKernelOldTs;
    private static TimeSpan \_sysUserOldTs;

    static Processor()
    {
    }

    public static void Test()
    {
        ComTypes.FILETIME sysIdle, sysKernel, sysUser;

        if(GetSystemTimes(out sysIdle, out sysKernel, out sysUser))
        {
            TimeSpan sysIdleTs = GetTimeSpanFromFileTime(sysIdle);
            TimeSpan sysKernelTs = GetTimeSpanFromFileTime(sysKernel);
            TimeSpan sysUserTs = GetTimeSpanFromFileTime(sysUser);

            TimeSpan sysIdleDiffenceTs = sysIdleTs.Subtract(\_sysIdleOldTs);
            TimeSpan sysKernelDiffenceTs = sysKernelTs.Subtract(\_sysKernelOldTs);
            TimeSpan sysUserDiffenceTs = sysUserTs.Subtract(\_sysUserOldTs);

            \_sysIdleOldTs = sysIdleTs;
            \_sysKernelOldTs = sysKernelTs;
            \_sysUserOldTs = sysUserTs;

            TimeSpan system = sysKernelDiffenceTs.Add(sysUserDiffenceTs);

            Double cpuUsage = (((system.Subtract(sysIdleDiffenceTs).TotalMilliseconds) * 100) / system.TotalMilliseconds);

            if (cpuUsage < 0)
            {
                Console.WriteLine("CPU: " + ((int) (cpuUsage)*-1) + "\%");
            }
            else
            {
                Console.WriteLine("CPU: " + (int) (cpuUsage) + "\%");
            }
            Console.WriteLine("");
        }
        else
        {
            Console.WriteLine("Couldn't get CPU usage!");
            Console.WriteLine("");
        }
    }

    private static TimeSpan GetTimeSpanFromFileTime(ComTypes.FILETIME time)
    {
        return TimeSpan.FromMilliseconds((((ulong)time.dwHighDateTime << 32) + (uint)time.dwLowDateTime) * 0.000001);
    }
}


Spørgsmål 2:


Er der alligevel for mig at synkronisere en tråd i mit program med Windows Task Manager, med det formål at matche målefigur, f.eks. CPU-brug med ovennævnte kode?


Hvad jeg mener er, hvis du åbner Windows Task Manager, vil du bemærke, at det afstemmer hvert sekund - hvilket i virkeligheden ikke behøver at være mindre end det. Hvad jeg vil gøre er at matche timingen med min tråd.


Så når Windows Task Manager polls, mine trådundersøgelser.





Nogle noter:


Jeg ville ikke bruge Performance Counters eller .NET indbyggede metoder. Faktisk tror jeg - fra det jeg har læst. NET har ikke metoder til at beregne CPU-brugen på en maskine, at Performance-tællere er påkrævet for dette ellers.


Performance counters har overhead og desuden gør GC vokse, for ikke at nævne forsinkelsen med at kalde det næste resultat . Selvom min software ikke behøver at være i realtid, har jeg brug for at være så lydhør og bruge så lidt CPU tid som muligt. Ovenstående kode kan kaldes og returneres på mindre end en millisekund. Faktisk på min udviklingsmaskine viser tidsforskellen forskellen 0ms. Jeg tror ikke, at Performance Counters er så lydhøre.


Hvis du er nysgerrig, samler min software en række elementer, CPU, Hukommelse, Event Log elementer osv., Som alle skal samles og gemmes i SQL CE, før næste afstemning, 1 sekund væk. Hver opgave, emne, er dog på sin egen tråd for at lette dette.


Også koden ovenfor er ikke optimeret i alligevel, og du vil bemærke jeg har endnu ikke kommentere det også. Årsagen er at jeg vil sørge for, at det er korrekt før optimering mv.


Opdater 1


Efter en kommentare lavede jeg ned, fjernede jeg det ekstra 'System' tidsrum, da det ikke er nødvendigt og ændret linjen, der henter 'CPU-brugen' og cast den korrekt.


int cpuUsage = (int)(((sysKernelDifferenceTs.Add(sysUserDifferenceTs).Subtract(sysIdleDifferenceTs).TotalMilliseconds) * 100.00) / sysKernelDifferenceTs.Add(sysUserDifferenceTs).TotalMilliseconds);


Selvom jeg stadig er usikker på formlen. Selvom det synes at være meget nøjagtigt, returnerer det til tider en minusfigur, hvorfor jeg multiplicerer den med -1, hvis det er tilfældet. Der er trods alt ikke sådan en -2\% CPU-brug osv.


Opdater 2


Så jeg gjorde en simpel test ved hjælp af 'System.Diagnostics.PerformanceCounter'. Mens det er utrolig praktisk og gør præcis, hvad det er meningen at gøre det, skaber det overhead.


Her er mine observationer:



  • Det tog Performance Counter, der var meget længere at initialisere. I størrelsesordenen omkring tre sekunder længere på min i7 2,6 Ghz.

  • Præstationsdisken syntes også at tilføje på en anden ca. 5 MB RAM-brug ved blot at bruge den. Hvad jeg mener med dette er: Med koden ovenfor, min app maxes ud på 7.5MB ram. Med performance tælleren 'starter' den på 12,5 MB.

  • I løbet af 5 sekunder, hvor min tråd kørte 5 gange - en gang pr. sekund, var min app's hukommelse vokset med 1 MB, og denne stigning er i overensstemmelse med tiden, selv om det ikke er i orden, 3-4MB over start. Så hvor min app normalt er 7,5 MB RAM med koden ovenfor, udgav PC-koden ud på 16,5 MB RAM - en stigning på 9 MB over koden ovenfor. Bemærk: Koden ovenfor ikke forårsager denne stigning.



Så hvis din ansøgning blev bygget på en måde, hvor ressourceforbrug og timing er nøgle, vil jeg foreslå mod at bruge Performance-tællere på grund af disse grunde. Ellers gå videre, da det virker uden alt rod.


Hvad angår min app, vil ydelsestællere være skadelige for min software s formål.

Bedste reference


Jeg tror, ​​du har en fejl i din formel. Du vil grundlæggende beregne CPU-brug som dette:


CPU Usage = KernelTimeDiff + UserTimeDiff
            --------------------------------------------
            KernelTimeDiff + UserTimeDiff + IdleTimeDiff


Således en hurtig mod til din kode som følger:


        //  TimeSpan system = sysKernelDiffenceTs.Add(sysUserDiffenceTs);
        //Double cpuUsage = (((system.Subtract(sysIdleDiffenceTs).TotalMilliseconds) * 100) / system.TotalMilliseconds); 

        TimeSpan totaltime = sysKernelDiffenceTs.Add(sysUserDiffenceTs);
        totaltime = totaltime.Add(sysIdleDifferenceTs);
        int cpuUsage = 100 - (sysIdleDifferenceTs.TotalMilliseconds * 100) / totaltime.TotalMilliseconds;
       Console.WriteLine("CPU: " + cpuUsage + "\%");


Du oprindeligt erklærede cpuUsage som 'Double'. Jeg er ikke sikker på, om du ville have flydende præcision, men i din kode ville du absolut ikke få noget andet end heltal præcision, fordi opgaveopgørelsen bare gjorde heltalmatematik. Hvis du har brug for højere præcision fra beregningen, kan du nemt få det ved at blande i nogle flydende punkter:


Double cpuUsage = 100.0 - (sysIdleDifferenceTs.TotalMilliseconds * 100.0) /totaltime.TotalMilliseconds;


Også med hensyn til at være synkroniseret med Task Manager. Task Manager, som jeg forstår det, bruger perf counters. (Og jeg ville have mistanke om, at GetSystemTimes laver perf-opkald under hooden, men måske ikke). Og jeg er ikke sikker på, hvorfor du heller ikke vil bruge PER-tællere. '\% Process Time' tælleren er en øjeblikkelig prøve tæller, der ikke kræver beregning af en diff med et tidligere resultat. (Der er en pr. Logisk cpu). Brug PDH-hjælperfunktionerne i stedet for den gamle registernøgle apis for at komme til det. Du kan gøre dette fra en ustyret C/C ++ DLL, der eksporterer en 'GetCpuUsage' -funktion tilbage til din C # -kode. Men jeg ved ikke, hvorfor du ikke bare kunne inddrage PDH-funktionerne fra C #. Jeg ved ikke om denne overhead, som du taler om. Jeg er ikke sikker på, at jeg forstår din henvisning til 'forsinkelsen med at kalde det næste resultat' heller ikke.