3

This question "How to get the CPU Usage in C#?" shows some of the ways to get the current CPU usage (%) of a computer in .NET.

Since the CPU usage fluctuates quite frequently, I think the current CPU usage is often not a good indicator of how busy a computer is (e.g. for scheduling or load balancing purpose). Is there an efficient way to get the average CPU usage in last x minute, e.g. last 5 minutes?

I am thinking of something like a method GetAverageCpuUsage(int period) that can be called by a load balancing or scheduling module.

Community
  • 1
  • 1
Louis Rhys
  • 34,517
  • 56
  • 153
  • 221

4 Answers4

8

Actually that is exactly what PerformanceCounter from the 2nd most upvoted answer in the other question does, they are just measuring over 1 second.

The % it gives you is the average % of cpu seance the last NextValue() was made on the counter. So if you want the average cpu over the last 5 min, just call NextValue() only once every 5 min.

Here is a good article explaining how to use the performance counters.

mbx
  • 6,292
  • 6
  • 58
  • 91
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • In this approach, you have to know when to call NextValue() the first time. In my case, this is the problem because I want GetAverageCpuUsage to be available on-demand and can be called any time. – Louis Rhys Feb 05 '13 at 07:37
  • The problem is it does not keep historical records for you. You could use [NextSample](http://msdn.microsoft.com/en-us/library/system.diagnostics.performancecounter.nextsample%28v=vs.80%29.aspx) instead of NextValue periodically then do the math yourself `(end value - start value)/(end time - start time)`, it would be your programs responsibility to store those value so some other user could query it and it could look up in it's database and calculate a value.. – Scott Chamberlain Feb 05 '13 at 07:39
  • Another option is [set up a counter in windows](http://technet.microsoft.com/en-us/library/dd744567%28v=ws.10%29.aspx) to a rolling log file and you query the file for the last 5 min of data. – Scott Chamberlain Feb 05 '13 at 07:46
  • @what's the difference between NextSample and NextValue? By the way, thanks for the answer, +1 – Louis Rhys Feb 05 '13 at 14:14
  • NextSample gives you the raw data as a struct. You can then use the properties of that struct (time stamp of the sample and the numeric value associated with the counter) then do the "start - end" value method I mentioned in my first comment. – Scott Chamberlain Feb 05 '13 at 14:16
  • See the example code in [this MSDN page](http://msdn.microsoft.com/en-US/library/system.diagnostics.performancecountertype%28v=vs.80%29.aspx) to see it in use. – Scott Chamberlain Feb 05 '13 at 14:21
1

The answers above give a pretty good example for a way to calculate CPU usage.But I would like to point out something that is incorrect and was misled. The SubtractTimes function needs to be slightly different.

private UInt64 SubtractTimes(ComTypes.FILETIME a, ComTypes.FILETIME b)
    {
        UInt64 aInt = ((UInt64)(a.dwHighDateTime << 32)) | (UInt32)a.dwLowDateTime;
        UInt64 bInt = ((UInt64)(b.dwHighDateTime << 32)) | (UInt32)b.dwLowDateTime;

        return aInt - bInt;
    }

NOTICE THE UINT32 for the lowDateTime. This is because, atleast in C#, the comtype.FILETIME struct casts the DWORD to an int32, so if the value is more than MAX signed Int you see a negative value which should never be the case. If you cast this directly to "UINT64" type, internally for preserving sign this is converted to Int64 first and then converted to UInt64 so you see an incorrect value. Instead You want to cast the lowDateTime to a Uint32 type to get rid of the negative sign, and then cast to UInt64 (this would happen automatically since it is part of | operation). Same should hold for the dwHighDateTime conponent but thats fine since it should not be more than MaxInt usually (well, that again depends on your usecase).

dharu
  • 35
  • 3
0

try something like:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using ComTypes = System.Runtime.InteropServices.ComTypes;
using System.Threading;
using System.Diagnostics;

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

        ComTypes.FILETIME _prevSysKernel;
        ComTypes.FILETIME _prevSysUser;

        TimeSpan _prevProcTotal;

        Int16 _cpuUsage;
        DateTime _lastRun;
        long _runCount;

        public CpuUsage()
        {
            _cpuUsage = -1;
            _lastRun = DateTime.MinValue;
            _prevSysUser.dwHighDateTime = _prevSysUser.dwLowDateTime = 0;
            _prevSysKernel.dwHighDateTime = _prevSysKernel.dwLowDateTime = 0;
            _prevProcTotal = TimeSpan.MinValue;
            _runCount = 0;
        }

        public short GetUsage()
        {
            short cpuCopy = _cpuUsage;
            if (Interlocked.Increment(ref _runCount) == 1)
            {
                if (!EnoughTimePassed)
                {
                    Interlocked.Decrement(ref _runCount);
                    return cpuCopy;
                }

                ComTypes.FILETIME sysIdle, sysKernel, sysUser;
                TimeSpan procTime;

                Process process = Process.GetCurrentProcess();
                procTime = process.TotalProcessorTime;

                if (!GetSystemTimes(out sysIdle, out sysKernel, out sysUser))
                {
                    Interlocked.Decrement(ref _runCount);
                    return cpuCopy;
                }

                if (!IsFirstRun)
                {
                    UInt64 sysKernelDiff = SubtractTimes(sysKernel, _prevSysKernel);
                    UInt64 sysUserDiff = SubtractTimes(sysUser, _prevSysUser);

                    UInt64 sysTotal = sysKernelDiff + sysUserDiff;

                    Int64 procTotal = procTime.Ticks - _prevProcTotal.Ticks;

                    if (sysTotal > 0)
                    {
                        _cpuUsage = (short)((100.0 * procTotal) / sysTotal);
                    }
                }

                _prevProcTotal = procTime;
                _prevSysKernel = sysKernel;
                _prevSysUser = sysUser;

                _lastRun = DateTime.Now;

                cpuCopy = _cpuUsage;
            }
            Interlocked.Decrement(ref _runCount);

            return cpuCopy;

        }

        private UInt64 SubtractTimes(ComTypes.FILETIME a, ComTypes.FILETIME b)
        {
            UInt64 aInt = ((UInt64)(a.dwHighDateTime << 32)) | (UInt64)a.dwLowDateTime;
            UInt64 bInt = ((UInt64)(b.dwHighDateTime << 32)) | (UInt64)b.dwLowDateTime;

            return aInt - bInt;
        }

        private bool EnoughTimePassed
        {
            get
            {
                const int minimumElapsedMS = 250;
                TimeSpan sinceLast = DateTime.Now - _lastRun;
                return sinceLast.TotalMilliseconds > minimumElapsedMS;
            }
        }

        private bool IsFirstRun
        {
            get
            {
                return (_lastRun == DateTime.MinValue);
            }
        }
    }
}

after using it so well in ur code:

   CpuUsage _cu = new CpuUsage();
   string cpuUsage = _cu.GetUsage();

have helped on something

Rafael Souza
  • 27
  • 10
  • I do not get why Process process = Process.GetCurrentProcess(); procTime = process.TotalProcessorTime; is involved here. A different example does not use this and instead: var thisTotal = thisKernel + thisUser; var totalSinceLast = thisTotal - pPrevTotal; var idleSinceLast = thisIdle - pPrevIdle; double headroom = (double)idleSinceLast / (double)totalSinceLast; double load = 1.0 - headroom; I imagine your example calculate cpu usage of the current process and not the system? – osexpert Jun 26 '19 at 21:14
0
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Management;
using System.Timers;
using System.Threading;

class Program
{
    static class PerfStatic
    {
        private static float[] m_list;
        private static int index = 0;
        private static int count = 60;
        private static float val = 0;
        private static bool inited = false;
        private static bool ready = false;
        private static PerformanceCounter cpuCounter = new PerformanceCounter("Processor Information", "% Processor Utility", "_Total");

        public static void tick(Object source, ElapsedEventArgs e)
        {
            val = cpuCounter.NextValue();
            index++;
            if (index >= count)
            {
                index = 0;
                ready = true;
            }
            m_list[index] = val;
        }
        public static float CPU1MinAverage
        {
            get
            {
                if (!inited)
                {
                    Init();
                    inited = true;
                    return 100;
                }
                if (!ready)                    
                    return 100;               
                
                return m_list.Average();
            }
        }
        public static void Init()
        {
            m_list = new float[count];
            var aTimer = new System.Timers.Timer(1000);
            aTimer.Elapsed += tick;
            aTimer.AutoReset = true;
            aTimer.Enabled = true;
        }
    }

    static void Main(string[] args)
    {
        while (true)
        {
            Console.WriteLine(PerfStatic.CPU1MinAverage);
            Thread.Sleep(1000);
        }
    }
}

Static class for 1 minute average, returns 100 if not enough data, needs to be called to start collecting data, modify for your needs.

You also need nuget extension on .net 6 - System.Diagnostics.PerformanceCounter

Twy1l0
  • 1