Reputation: 1770
I'm working on a benchmark tool which among other things measures the time and memory used by an external process performing an operation. I'm mostly interested in the peak pageable memory size (a.k.a. PageFileBytesPeak performance counter / Process.PeakPagedMemorySize64 / peak private bytes). This is a .NET project so a pure .NET solution would be preferable, however this is most likely not a possibility.
The problem here is that I won't know the peak memory usage before the process has exited. I can't read the performance counters for the process when it no longer exists. So I could instead poll it while the process is running.
However this is not preferable as if I poll too often I will interfere with the time it takes for the process to complete its work, and if poll too rarely the result won't be accurate (the process will most likely hit its peak memory usage right before it exits). So I'm hoping there is some way to do it reliably that is less hacky than the solutions I have come up with so far:
Upvotes: 0
Views: 984
Reputation: 1770
Turns out that GetProcessMemoryInfo works even after the process has exited as long as you have an active handle to it. The virtual memory usage isn't available this way though if you happen to need that.
Only caveat is that the size of the values depends on the bitness of the process it's called from, so the values may overflow if a 32 bit process measures the memory usage of a 64 bit process.
Example:
[DllImport("psapi.dll", SetLastError=true)]
static extern bool GetProcessMemoryInfo(IntPtr hProcess, out PROCESS_MEMORY_COUNTERS counters, int size);
[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_MEMORY_COUNTERS
{
public uint cb;
public uint PageFaultCount;
public UIntPtr PeakWorkingSetSize;
public UIntPtr WorkingSetSize;
public UIntPtr QuotaPeakPagedPoolUsage;
public UIntPtr QuotaPagedPoolUsage;
public UIntPtr QuotaPeakNonPagedPoolUsage;
public UIntPtr QuotaNonPagedPoolUsage;
public UIntPtr PagefileUsage;
public UIntPtr PeakPagefileUsage;
}
public long BenchmarkProcessMemoryUsage(string fileName, string arguments)
{
ProcessStartInfo startInfo = new ProcessStartInfo(fileName, arguments);
startInfo.UseShellExecute = false;
Process process = Process.Start();
process.WaitForExit();
PROCESS_MEMORY_COUNTERS counters;
if (!GetProcessMemoryInfo(process.Handle, out counters, Marshal.SizeOf(typeof(PROCESS_MEMORY_COUNTERS))))
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
return (long)counters.PeakPagefileUsage;
}
Upvotes: 1
Reputation: 54158
Reading extant PerfMon counters should be a very low overhead operation, esp. for system counters like the ones you want to work with, since the counters are typically (possibly always? not sure) implemented using a block of shared memory (mapped file).
I'd implement polling with a runtime configurable interval, and only resort to more complex techniques if you find this is affecting your application materially. If you want to sanity check this first, set up PerfMon to monitor the counter(s) of interest and see if that kills your application when running at a usable refresh interval.
Upvotes: 1