Determine CPU usage of current process (C++ and C#)

Like this? Please check out my latest book, Writing High-Performance .NET Code.

Updated 2/4/2009: I changed the implementation of these classes from the original:

  • Instead of a critical section, InterlockedIncrement/Decrement is used.
  • The sample driver program now demos using multiple threads using the CpuUsage class to show thread safety.

Download the C++ and C# projects that accompany this article.

Just to make it clear, there is no API called GetProcessCpuPercentage(). To find out the percentage, we can use some other, real APIs and do some calculations. Before getting to the equation and code, let’s discuss the different types of time available.

There are four types of time:

  1. Wall time – The actual, real-world progression of time as measured by you on your watch.
  2. Kernel time – The amount of time spent in kernel mode (protected, high-order mode of operation)
  3. User time – the amount of time spent in user-mode (often by the process itself)
  4. Idle time – nothing going on at all

Kernel, User, and Idle sum to total time, which is approximately wall-time.

Each process spends some time in kernel mode and some time in user mode. We just need to compare the time spent by a process to the time spent by all processes on the computer, since the last time we made such a measurement. It is important to note that we do NOT take into account the idle time.

Thus, the equation is

CpuPercentageEquation

There are two APIs that are useful:

The times reported are absolute, so what we are actually interested is in the difference between the current times and those from a previous run.

Armed with this information, we can calculate the CPU usage for the current process (or any arbitrary process, for that matter).

Let’s do it first in C++ to demonstrate usage of the APIs.

CPU Usage Percentage in C++

Here’s the header file:

   1: #pragma once
   2: #include <windows.h>
   3:
   4: class CpuUsage
   5: {
   6: public:
   7:     CpuUsage(void);
   8:
   9:     short  GetUsage();
  10: private:
  11:     ULONGLONG SubtractTimes(const FILETIME& ftA, const FILETIME& ftB);
  12:     bool EnoughTimePassed();
  13:     inline bool IsFirstRun() const { return (m_dwLastRun == 0); }
  14:
  15:     //system total times
  16:     FILETIME m_ftPrevSysKernel;
  17:     FILETIME m_ftPrevSysUser;
  18:
  19:     //process times
  20:     FILETIME m_ftPrevProcKernel;
  21:     FILETIME m_ftPrevProcUser;
  22:
  23:     short m_nCpuUsage;
  24:     ULONGLONG m_dwLastRun;
  25:
  26:     volatile LONG m_lRunCount;
  27: };

The GetUsage() method is where the work occurs. The other methods are to help in the calculations. The critical section run count enables the code to be called in a multi-threaded environment without problems. I also prevent the code from being called more often than every 250ms. Here is the complete implementation:

   1: #include "StdAfx.h"
   2: #include <windows.h>
   3: #include "CpuUsage.h"
   4:
   5: CpuUsage::CpuUsage(void)
   6: :m_nCpuUsage(-1)
   7: ,m_dwLastRun(0)
   8: ,m_lRunCount(0)
   9: {
  10:     ZeroMemory(&m_ftPrevSysKernel, sizeof(FILETIME));
  11:     ZeroMemory(&m_ftPrevSysUser, sizeof(FILETIME));
  12:
  13:     ZeroMemory(&m_ftPrevProcKernel, sizeof(FILETIME));
  14:     ZeroMemory(&m_ftPrevProcUser, sizeof(FILETIME));
  15:
  16: }
  17:
  18:
  19: /**********************************************
  20: * CpuUsage::GetUsage
  21: * returns the percent of the CPU that this process
  22: * has used since the last time the method was called.
  23: * If there is not enough information, -1 is returned.
  24: * If the method is recalled to quickly, the previous value
  25: * is returned.
  26: ***********************************************/
  27: short CpuUsage::GetUsage()
  28: {
  29:     //create a local copy to protect against race conditions in setting the 
  30:     //member variable
  31:     short nCpuCopy = m_nCpuUsage;
  32:     if (::InterlockedIncrement(&m_lRunCount) == 1)
  33:     {
  34:         /*
  35:         If this is called too often, the measurement itself will greatly 
  36:         affect the results.
  37:         */
  38:
  39:         if (!EnoughTimePassed())
  40:         {
  41:             ::InterlockedDecrement(&m_lRunCount);
  42:             return nCpuCopy;
  43:         }
  44:
  45:         FILETIME ftSysIdle, ftSysKernel, ftSysUser;
  46:         FILETIME ftProcCreation, ftProcExit, ftProcKernel, ftProcUser;
  47:
  48:         if (!GetSystemTimes(&ftSysIdle, &ftSysKernel, &ftSysUser) ||
  49:             !GetProcessTimes(GetCurrentProcess(), &ftProcCreation,
  50:                 &ftProcExit, &ftProcKernel, &ftProcUser))
  51:         {
  52:             ::InterlockedDecrement(&m_lRunCount);
  53:             return nCpuCopy;
  54:         }
  55:
  56:         if (!IsFirstRun())
  57:         {
  58:             /*
  59:             CPU usage is calculated by getting the total amount of time 
  60:             the system has operated since the last measurement 
  61:             (made up of kernel + user) and the total
  62:             amount of time the process has run (kernel + user).
  63:             */
  64:             ULONGLONG ftSysKernelDiff =
  65:                 SubtractTimes(ftSysKernel, m_ftPrevSysKernel);
  66:             ULONGLONG ftSysUserDiff =
  67:                 SubtractTimes(ftSysUser, m_ftPrevSysUser);
  68:
  69:             ULONGLONG ftProcKernelDiff =
  70:                 SubtractTimes(ftProcKernel, m_ftPrevProcKernel);
  71:             ULONGLONG ftProcUserDiff =
  72:             SubtractTimes(ftProcUser, m_ftPrevProcUser);
  73:
  74:             ULONGLONG nTotalSys =  ftSysKernelDiff + ftSysUserDiff;
  75:             ULONGLONG nTotalProc = ftProcKernelDiff + ftProcUserDiff;
  76:
  77:             if (nTotalSys > 0)
  78:             {
  79:                 m_nCpuUsage = (short)((100.0 * nTotalProc) / nTotalSys);
  80:             }
  81:         }
  82:
  83:         m_ftPrevSysKernel = ftSysKernel;
  84:         m_ftPrevSysUser = ftSysUser;
  85:         m_ftPrevProcKernel = ftProcKernel;
  86:         m_ftPrevProcUser = ftProcUser;
  87:
  88:         m_dwLastRun = GetTickCount64();
  89:
  90:         nCpuCopy = m_nCpuUsage;
  91:     }
  92:
  93:     ::InterlockedDecrement(&m_lRunCount);
  94:
  95:     return nCpuCopy;
  96: }
  97:
  98: ULONGLONG CpuUsage::SubtractTimes(const FILETIME& ftA, const FILETIME& ftB)
  99: {
 100:     LARGE_INTEGER a, b;
 101:     a.LowPart = ftA.dwLowDateTime;
 102:     a.HighPart = ftA.dwHighDateTime;
 103:
 104:     b.LowPart = ftB.dwLowDateTime;
 105:     b.HighPart = ftB.dwHighDateTime;
 106:
 107:     return a.QuadPart - b.QuadPart;
 108: }
 109:
 110: bool CpuUsage::EnoughTimePassed()
 111: {
 112:     const int minElapsedMS = 250;//milliseconds
 113:
 114:     ULONGLONG dwCurrentTickCount = GetTickCount64();
 115:     return (dwCurrentTickCount - m_dwLastRun) > minElapsedMS;
 116: }

In order to test this, here is a simple program that starts two threads that run an infinite loop, eating the processor. On a dual-core system, this process will take roughly 85-95% of the CPU. I also start two threads to access the usage object and poll the CPU usage in order to demonstrate the thread safety of the object.

   1: // CpuUsageCpp.cpp : Defines the entry point for the console application.
   2: //
   3:
   4: #include "stdafx.h"
   5: #include <windows.h>
   6: #include "CpuUsage.h"
   7:
   8: DWORD WINAPI EatItThreadProc(LPVOID lpParam);
   9: DWORD WINAPI WatchItThreadProc(LPVOID lpParam);
  10:
  11: CpuUsage usage;
  12:
  13: int _tmain(int argc, _TCHAR* argv[])
  14: {
  15:     //start threads to eat the processor
  16:     CreateThread(NULL, 0, EatItThreadProc, NULL, 0, NULL);
  17:     CreateThread(NULL, 0, EatItThreadProc, NULL, 0, NULL);
  18:
  19:     //start threads to watch the processor (to test thread-safety)
  20:     CreateThread(NULL, 0, WatchItThreadProc, NULL, 0, NULL);
  21:     CreateThread(NULL, 0, WatchItThreadProc, NULL, 0, NULL);
  22:
  23:     while (true)
  24:     {
  25:         Sleep(1000);
  26:     }
  27:
  28:     return 0;
  29: }
  30:
  31:
  32: DWORD WINAPI WatchItThreadProc(LPVOID lpParam)
  33: {
  34:     while (true)
  35:     {
  36:         short cpuUsage = usage.GetUsage();
  37:
  38:         printf("Thread id %d: %d%% cpu usage\n", ::GetCurrentThreadId(), cpuUsage);
  39:         Sleep(1000);
  40:     }
  41: }
  42:
  43: DWORD WINAPI EatItThreadProc(LPVOID lpParam)
  44: {
  45:     ULONGLONG accum = 0;
  46:     while (true)
  47:     {
  48:         accum++;
  49:     }
  50:
  51:     printf("%64d\n", accum);
  52: }

C# Version

In C#, The System.Diagnostics.Process can give us the time information for a specific process. However, we still need the Win32 API call for getting the total system times (GetSystemTimes). The Process class reports times in TimeSpans, not FILETIME, so our class is modified accordingly.

   1: using System;using System.Collections.Generic;
   2: using System.Linq;
   3: using System.Text;
   4: using System.Runtime.InteropServices;
   5: using ComTypes = System.Runtime.InteropServices.ComTypes;
   6: using System.Threading;
   7: using System.Diagnostics;
   8:
   9: namespace CpuUsageCs
  10: {
  11:     class CpuUsage
  12:     {
  13:         [DllImport("kernel32.dll", SetLastError = true)]
  14:         static extern bool GetSystemTimes(
  15:                     out ComTypes.FILETIME lpIdleTime,
  16:                     out ComTypes.FILETIME lpKernelTime,
  17:                     out ComTypes.FILETIME lpUserTime
  18:                     );
  19:
  20:         ComTypes.FILETIME _prevSysKernel;
  21:         ComTypes.FILETIME _prevSysUser;
  22:
  23:         TimeSpan _prevProcTotal;
  24:
  25:         Int16 _cpuUsage;
  26:         DateTime _lastRun;
  27:         long _runCount;
  28:
  29:         public CpuUsage()
  30:         {
  31:             _cpuUsage = -1;
  32:             _lastRun = DateTime.MinValue;
  33:             _prevSysUser.dwHighDateTime = _prevSysUser.dwLowDateTime = 0;
  34:             _prevSysKernel.dwHighDateTime = _prevSysKernel.dwLowDateTime = 0;
  35:             _prevProcTotal = TimeSpan.MinValue;
  36:             _runCount = 0;
  37:         }
  38:
  39:         public short GetUsage()
  40:         {
  41:             short cpuCopy = _cpuUsage;
  42:             if (Interlocked.Increment(ref _runCount) == 1)
  43:             {
  44:                 if (!EnoughTimePassed)
  45:                 {
  46:                     Interlocked.Decrement(ref _runCount);
  47:                     return cpuCopy;
  48:                 }
  49:
  50:                 ComTypes.FILETIME sysIdle, sysKernel, sysUser;
  51:                 TimeSpan procTime;
  52:
  53:                 Process process = Process.GetCurrentProcess();
  54:                 procTime = process.TotalProcessorTime;
  55:
  56:                 if (!GetSystemTimes(out sysIdle, out sysKernel, out sysUser))
  57:                 {
  58:                     Interlocked.Decrement(ref _runCount);
  59:                     return cpuCopy;
  60:                 }
  61:
  62:                 if (!IsFirstRun)
  63:                 {
  64:                     UInt64 sysKernelDiff =
  65:                         SubtractTimes(sysKernel, _prevSysKernel);
  66:                     UInt64 sysUserDiff =
  67:                         SubtractTimes(sysUser, _prevSysUser);
  68:
  69:                     UInt64 sysTotal = sysKernelDiff + sysUserDiff;
  70:
  71:                     Int64 procTotal = procTime.Ticks - _prevProcTotal.Ticks;
  72:
  73:                     if (sysTotal > 0)
  74:                     {
  75:                         _cpuUsage = (short)((100.0 * procTotal) / sysTotal);
  76:                     }
  77:                 }
  78:
  79:                 _prevProcTotal = procTime;
  80:                 _prevSysKernel = sysKernel;
  81:                 _prevSysUser = sysUser;
  82:
  83:                 _lastRun = DateTime.Now;
  84:
  85:                 cpuCopy = _cpuUsage;
  86:             }
  87:             Interlocked.Decrement(ref _runCount);
  88:
  89:             return cpuCopy;
  90:
  91:         }
  92:
  93:         private UInt64 SubtractTimes(ComTypes.FILETIME a, ComTypes.FILETIME b)
  94:         {
  95:             UInt64 aInt =
  96:             ((UInt64)(a.dwHighDateTime << 32)) | (UInt64)a.dwLowDateTime;
  97:             UInt64 bInt =
  98:             ((UInt64)(b.dwHighDateTime << 32)) | (UInt64)b.dwLowDateTime;
  99:
 100:             return aInt - bInt;
 101:         }
 102:
 103:         private bool EnoughTimePassed
 104:         {
 105:             get
 106:             {
 107:                 const int minimumElapsedMS = 250;
 108:                 TimeSpan sinceLast = DateTime.Now - _lastRun;
 109:                 return sinceLast.TotalMilliseconds > minimumElapsedMS;
 110:             }
 111:         }
 112:
 113:         private bool IsFirstRun
 114:         {
 115:             get
 116:             {
 117:                 return (_lastRun == DateTime.MinValue);
 118:             }
 119:         }
 120:     }
 121: }

These classes can now be used wherever you need to monitor the CPU usage of a process.

Notice any improvements to be made? Leave a comment.

Download C++ and C# projects

42 thoughts on “Determine CPU usage of current process (C++ and C#)

  1. Deekshit

    Hi Ben,

    This is a great example and is quite what I have been looking for, but not exactly. I need to modify this to get me the CPU % used by a particular process…for example, I need to monitor the CPU Usage for “iexplore”.

    I got stuck while trying to initialize the procTime variable

    Console.Write(“Enter process name: “);
    String procName = Console.ReadLine();
    foreach (Process process in Process.GetProcessesByName(procName))
    procTime = process.TotalProcessorTime;

    I get an error while compiling at the procTime initialization. I am not really good at C#, just a beginner..so could you help me out with this one please?

    –Deekshit

  2. Deekshit

    Hi, I found the solution to caputre the usage of the process that I want to monitor, but the CpuUsageCs.exe program seems to be taking up 100% of the CPU on Dual Core machines…is there any reason why it is so intensive? or is there a leak?

    –Deekshit

  3. Ben Post author

    Deekshit, the demo program deliberately uses a lot of the processor in order to demo the functionality of the CpuUsage class

  4. Deekshit

    any tips on how I can optimize this program to reduce the CPU Usage?

    I used the following method to set the thread affinity to Low

    public static void setCurrentProgAffinity(String proc)
    {
    foreach (Process myCurrentProcess in Process.GetProcessesByName(proc))
    {
    myCurrentProcess.PriorityClass = System.Diagnostics.ProcessPriorityClass.Idle;

    }
    }

  5. Deekshit

    Hi Ben,

    Just need one small bit of help if you can. I am trying to get the CPU usage of more than one process at the same time. I am passing an array to the GetUsage method as follows:

    public short GetUsage(String[] procName)
    {
    String[] myProcs = procName;
    if (!Monitor.TryEnter(_syncLock))
    {
    return _cpuUsage;
    }

    if (!EnoughTimePassed)
    {
    Monitor.Exit(_syncLock);
    return _cpuUsage;
    }

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

    //Process process = Process.GetCurrentProcess();
    for(int i=0;i 0)
    {
    _cpuUsage = (short)((100.0 * procTotal) / sysTotal);
    }
    }

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

    _lastRun = DateTime.Now;

    Monitor.Exit(_syncLock);
    }
    }
    }

    return _cpuUsage;

    This logic seems to work for the first iteration on the first process, but when it gets to the second process, it throws an error

    “{“Object synchronization method was called from an unsynchronized block of code.”}”

    Any idea how I can get around this? We are not locking anywhere to Exit out of the lock are we? Can you please help me out with this?

    –Deekshit

  6. Ben Post author

    Deekshit, I think some important lines are missing from the code sample you posted.

    However, I don’t think this approach is going to work with my code. The class stores the time values for a single process, which it needs in order to calculate cpu usage.

    A better approach might be to have an array of CpuUsage objects, one for each process.

    Also, you should check out the updated code samples–they use interlocked operations instead of locking, so they are much more efficient and performant now.

  7. Deekshit

    Hi Ben,

    What do you mean by an array of CpuUsage objects for each process? How do I create a dynamic array of CpuUsage objects? I might want to monitor 1 process or 3 at anytime. Can you please help me out?

    –Deekshit

  8. Deekshit

    CpuUsage[] usage = new CpuUsage[nProcs];

    Is this what you are referring to Ben? I seem to be a bit lost here. Can you please help Ben?

    Regards,
    Deekshit

  9. Deekshit

    I can get it to work if I create the CpuUsage objects in this way:

    Assuming I want to monitor 2 processes

    CpuUsage usage1 = new CpuUsage();
    CpuUsage usage2 = new CpuUsage();

    and then I can run usage1.getUsage(procName[i]);
    and usage2.getUsage(procName[i+1]);

    and this approach works, but is not dynamic at all..any tips on how I can get around this problem?

    Regards,
    Deekshit

  10. Ben Post author

    I think you have the right idea with the array.

    CpuUsage[] usage = new CpuUsage[nProcs];
    for (int i=0;i

  11. Deekshit

    Ben,

    that approach doesnt work…
    usage[i] will never be initialized to any value. Trying to work on this further

    –Deekshit

  12. Deekshit

    Hi Ben,

    Any idea how to make this just one exe program so that I can copy the exe file to any machine and run the program?
    I havent been able to get past the objects problem, but I modified my program to accept command line arguments, so I’ll just put this into a batch file and run it.

    Any ideas?

    –Deekshit

  13. Peter

    I have tried to run this program but always get entry point not fount GetTickCount64. I am using xp sp2 and tried to debug the source code from VS2008. do you know the reason?

  14. bharath

    Hi Ben thats a great program, But how do I make it to monitor a particular process. I still didnot run the code but just want to make sure if we can monitor any process or not.

    Thanks,
    Bharath

  15. bharath

    hi Ben, i tried building the solution it said it is successfull but i dont get any executable.

  16. bharath

    Hi Ben, Sorry about the previous two messages but i am trying to use this program to monitor a particular process so I replaced GetCurrentProcess() by GetProcessesByName(“notepad”) but I am getting an error

  17. kao

    Hi Ben, I don’t understand every detail why you use thread method “EatItThreadProc”, and every thread method was used 2 times (in your code):

    static void Main(string[] args)
    {
    Console.WriteLine(“Input:”);
    string processName = Console.ReadLine();
    usage.setProcessName(processName);

    ThreadPool.QueueUserWorkItem(EatItThreadProc);
    ThreadPool.QueueUserWorkItem(EatItThreadProc);

    ThreadPool.QueueUserWorkItem(WatchItThreadProc);
    ThreadPool.QueueUserWorkItem(WatchItThreadProc);

    while (true)
    {
    Thread.Sleep(1000);
    }
    }

    Can you explain for me?

    Moreoverer, I try to compare your program and Task manager of Window, they have a little difference. So, your program ran exactly?

    Thanks for your reply!

  18. Ben Post author

    That code is just test code to use the processor as much as possible, in multiple threads, in order to test the processor usage code.

    I would not expect it to match task manager exactly–they are measuring the program at different times, so there will be a little difference. Over time, they should average out to the same, though.

  19. Remy

    Hi Ben,

    I wanted to use your program for my project, but the code uses GetSystemTimes() and GetTickCounts() which don’t seem to be supported by XP/2000. Is there a quick fix for that?

    Thanks ,
    Remy

  20. Ben Post author

    Remy, GetTickCount() is supported by Windows 2000 (http://msdn.microsoft.com/en-us/library/ms724408(VS.85).aspx), but GettickCount64 is not supported until Vista/2008.

    For GetSystemTimes(), I think your only solution is to use the officially-unsupported function NtQuerySystemInformation (http://msdn.microsoft.com/en-us/library/ms724509(VS.85).aspx, look for SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION). I would put usage of this inside some #ifdef so it’s only used in Windows 2000/XP, because this method could change or be removed in future versions of Windows.

  21. P.C.

    I would disagree to sentence “Kernel, User, and Idle sum to total time, which is approximately wall-time.”

    Idle time returned from GetSystemTimes is most probably a part of the Kernel time.

    GetProcessTimes returns Kernel time without any Idle time periods because they belong to a different process.

    So, the calculation takes the Idle time into account but I would say, it is correct.

  22. vaishali

    Deekshit,

    As you have mentioned that your code is working for single process or program ,
    please let me know what i need to add to the exiting code.
    please!! 😥
    i want a code that takes a program name as argument and gives it cpu utilization as output.

    thank u.

  23. Fedot

    Our testers found problem with
    method GetTickCount64()
    at Win x86 OS.
    and now we use
    method GetTickCount()
    🙂

  24. susheel

    Hi All,
    I am looking code to find out processes which have been idle for a long time and want to kill them like javaw processes.

    any help

    thanks

  25. nisar

    Hi,

    the conversion in C# SubtractTimes (((UInt64)(a.dwHighDateTime << 32)) | (UInt64)a.dwLowDateTime) is incorrect. The correct conversion is (((UInt64)f.dwHighDateTime) << 32) | (UInt32)f.dwLowDateTime.

  26. Adam Smith

    Hi Ben,

    THANKS for the code! Some machines don’t have perf counters installed correctly so this is great!

    Some quick tips:
    * Instead of using DateTime.Now, .UtcNow would be better as it’s both faster and less dependent on user settings. But even better is Environment.TickCount since it is not dependent on the user not touching their clock.

    * SubtractTimes can be static.

    * I rolled the two properties into the code, inline, since they were only used once. Probably personal preference but the code seemed more readable for me that way.

    But mostly THANKS again for the code!

    Adam

  27. taylor

    hi every body.
    i want a c sorce code to run cpu usage on code blocks.
    it was be on ansi c programming.
    can you help me immediatelly because my project is immergency.
    regard

  28. Jason

    This is terrific code, but when I use attempt to implement it, it only returns 0% :

    int main ()
    {

    double cpuUsage = usage.GetUsage();
    cpuUsage = usage.GetUsage();
    //…work

    int t, ct;

    scanf (“%d”, &t);
    for (ct = 1; ct <= t; ct ++)
    {
    scanf ("%d%s", &k, s);
    n = strlen(s);
    printf ("Case #%d: %d\n", ct, work());
    }

    // … work

    cpuUsage = usage.GetUsage();
    printf("Thread id %d: %d%% cpu usage\n", ::GetCurrentThreadId(), cpuUsage);

    getchar();
    }

    Any reason why I only get 0%? There is for(i<1000) in the work() function, so I know it is over 250ms.

  29. Ben Post author

    I think the most likely reason it shows 0% is because it really is very low. How many CPUs are there? Also, I’m not sure how for (i<1000) translates into something taking longer than 250ms.

  30. Pingback: A C++ Win32 App for CPU Load – tungcyang

  31. Pingback: C++ GetProcessTimes() does not change value over time – Windows Questions

Comments are closed.