I need to calculate CPU and RAM usage for the overall system as well as for a specific process. I've never done this in C#. So I was able to come up with the following code (that I took primarily from samples on this web site):
try
{
Process proc = Process.GetCurrentProcess();
string strProcName = proc.ProcessName;
Console.WriteLine("Process: " + strProcName);
using (PerformanceCounter total_cpu = new PerformanceCounter("Process", "% Processor Time", "_Total", true))
{
using (PerformanceCounter process_cpu = new PerformanceCounter("Process", "% Processor Time", strProcName, true))
{
for (; ; )
{
Console.CursorTop = 1;
Console.CursorLeft = 0;
float t = total_cpu.NextValue() / Environment.ProcessorCount;
float p = process_cpu.NextValue() / Environment.ProcessorCount;
Console.WriteLine(String.Format("Total CPU (%) = {0}\t\t\nApp CPU (%) = {1}\t\t\nApp RAM (KB) = {2}",
t, p, Process.GetCurrentProcess().WorkingSet64 / (1024)
));
System.Threading.Thread.Sleep(100);
}
}
}
}
catch(Exception ex)
{
Console.WriteLine("Exception: " + ex);
}
But what it gives me is the data "all over the map". Take a look:
So can someone answer these points:
It seems like running such performance counter is a pretty costly operation by itself -- it raises CPU usage by about 5%. I did a ton of CPU counters with C++ and they take pretty much no CPU time to run.
Besides what I said above, the first time the loop above runs with a 2 second delay! It literally hangs up for 2 seconds. Is it normal? If it is, I can't believe they had an audacity to call it a performance counter :)
Even though my RAM reading is pretty close to the one reported by the Task Manager, the CPU output is totally off. Can someone tell me what am I doing wrong here?
Moreover, I can't seem to find any documentation for the PerformanceCounter class, that can explain all of these: % Processor Time
, _Total
, etc? And most importantly, are those English only? Are they supposed to be localized?
The process there is specified by its name. But what if I have more than one process under the same name running. What then?
Here's what I came up with, but that is using unmanaged APIs:
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetProcessTimes(IntPtr hProcess, out System.Runtime.InteropServices.ComTypes.FILETIME
lpCreationTime, out System.Runtime.InteropServices.ComTypes.FILETIME lpExitTime, out System.Runtime.InteropServices.ComTypes.FILETIME lpKernelTime,
out System.Runtime.InteropServices.ComTypes.FILETIME lpUserTime);
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.U4)]
static extern UInt32 GetTickCount();
static bool gbSetOldData = false;
static UInt32 gmsOldTickCount = 0;
static ulong gnsOldKernelTime = 0;
static ulong gnsOldUserTime = 0;
public static double getCPUUsageForProcess(int nProcID = 0)
{
//Get CPU usage for the process in with ID in 'nProcID'
//'nProcID' = process ID, or 0 for the current process
//RETURN:
// = CPU usage: [0.0 - 1.0]
// = Negative if error
double fCPUUsage = -1.0;
try
{
IntPtr hProcess = nProcID != 0 ? Process.GetProcessById(nProcID).Handle : Process.GetCurrentProcess().Handle;
System.Runtime.InteropServices.ComTypes.FILETIME ftCreated, ftExit, ftKernel, ftUser;
if (GetProcessTimes(hProcess, out ftCreated, out ftExit, out ftKernel, out ftUser))
{
UInt32 dwmsNewTickCount = GetTickCount();
ulong nsNewKernelTime = (ulong)ftKernel.dwHighDateTime;
nsNewKernelTime <<= 32;
nsNewKernelTime |= (ulong)(uint)ftKernel.dwLowDateTime;
ulong nsNewUserTime = (ulong)ftUser.dwHighDateTime;
nsNewUserTime <<= 32;
nsNewUserTime |= (ulong)(uint)ftUser.dwLowDateTime;
if (gbSetOldData)
{
//Adjust from 100-nanosecond intervals to milliseconds
//100-nanosecond intervals = 100 * 10^-9 = 10^-7
//1ms = 10^-3
fCPUUsage = (double)((nsNewKernelTime - gnsOldKernelTime) + (nsNewUserTime - gnsOldUserTime)) /
(double)((dwmsNewTickCount - gmsOldTickCount) * 10000);
//Account for multiprocessor architecture
fCPUUsage /= Environment.ProcessorCount;
//In case timer API report is inaccurate
if (fCPUUsage > 1.0)
fCPUUsage = 1.0;
}
else
{
//For the first run, assume no CPU usage
fCPUUsage = 0.0;
}
//Remember data
gnsOldKernelTime = nsNewKernelTime;
gnsOldUserTime = nsNewUserTime;
gmsOldTickCount = dwmsNewTickCount;
gbSetOldData = true;
}
}
catch
{
//Failed
fCPUUsage = -1.0;
}
return fCPUUsage;
}
And this is how you'd call it:
int nDummy = 1;
for (; ; )
{
double fCPU = getCPUUsageForProcess();
Console.CursorTop = 1;
Console.CursorLeft = 0;
int nCpu = (int)(fCPU * 100);
Console.WriteLine("CPU%: {0}\t\t", nCpu);
//Time filler
long j = 0;
for (; j < 1000000; j++)
{
nDummy += (int)Math.Cos(1 / (j + 1));
}
//Don't hog all CPU time!
System.Threading.Thread.Sleep(500);
}
I'm not sure if .NET has a similar method. If there's one (that doesn't have all the limitations I posted above) I'd like to hear it?