.NET 中 GetProcess 相关方法的性能
.NET 的 Process
类中提供了查找进程的若干方法,其中部分方法还比较消耗性能。如果你试图优化查找进程相关方法的性能,可能本文分享的一些耗时数据可以作为参考。
性能比较
Process
类中提供了四种查询进程的方法:
GetProcesses
- 获取当前计算机或远程计算机上运行的所有进程。
GetProcessById
- 获取当前计算机或远程计算机上 pid 为 指定值的进程。
GetProcessesByName
- 根据进程的名字查找当前计算机或远程计算机上的进程。
GetCurrentProcess
- 获取当前进程的
Process
实例。
- 获取当前进程的
先给出我的实测数据(100 次执行耗时):
Process.GetProcesses()
- 00:00:00.7254688
Process.GetProcessById(id)
- 00:00:01.3660640(实际数值取决于当前进程数)
Process.GetProcessesByName("Walterlv.Demo")
- 00:00:00.5604279
Process.GetCurrentProcess()
- 00:00:00.0000546
结果显示获取所有进程实例的 GetProcesses
方法速度竟然比获取单个进程实例的 GetProcessById
还要快得多!额外地,根据名称查找进程比前两者都快,获取当前进程实例的方法快得不是一个数量级。
这些速度差异源于哪里
我们先看看最慢的方法 GetProcessIds
,它的最本质的实现在 ProcessManager
类中:
// ProcessManager
public static int[] GetProcessIds() {
int[] processIds = new int[256];
int size;
for (;;) {
if (!NativeMethods.EnumProcesses(processIds, processIds.Length * 4, out size))
throw new Win32Exception();
if (size == processIds.Length * 4) {
processIds = new int[processIds.Length * 2];
continue;
}
break;
}
int[] ids = new int[size / 4];
Array.Copy(processIds, ids, ids.Length);
return ids;
}
先创建一个 256 长度的数组,然后使用本机函数枚举进程列表填充这个数组。如果实际所需的数组大小与传入的数组大小相等,说明数组用完了,有可能进程数比 256 个多。所以,将数组长度扩大为两倍,随后再试一次。直到发现申请的数组长度足够存下进程数为止。
这里用到了本机方法 EnumProcesses
来枚举进程。传入的 size
要乘以 4 是因为传入的是字节数,一个 int
是 4 个字节。
// NativeMethods
[DllImport("psapi.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto, SetLastError=true)]
public static extern bool EnumProcesses(int[] processIds, int size, out int needed);
所以我们可以得知,如果当前计算机中的进程数小于 256 个,那么枚举进程方法仅需执行一次;而如果大于或等于 256 个,则枚举进程的方法需要执行两次或更多次,这是性能很差的一个重要原因。
另外,GetProcesses
方法就要复杂得多,其核心调用的是 ProcessManager.GetProcessInfos
方法。方法很长,但其大体思路是获取当前计算机上的线程列表,然后将线程所在的进程储存到哈希表中(相当于去重),随后返回此哈希表的数组副本。
// ProcessManager
static ProcessInfo[] GetProcessInfos(IntPtr dataPtr, Predicate<int> processIdFilter) {
// 60 is a reasonable number for processes on a normal machine.
Hashtable processInfos = new Hashtable(60);
long totalOffset = 0;
while(true) {
IntPtr currentPtr = (IntPtr)((long)dataPtr + totalOffset);
SystemProcessInformation pi = new SystemProcessInformation();
Marshal.PtrToStructure(currentPtr, pi);
// Process ID shouldn't overflow. OS API GetCurrentProcessID returns DWORD.
int processInfoProcessId = pi.UniqueProcessId.ToInt32();
if (processIdFilter == null || processIdFilter(processInfoProcessId)) {
// get information for a process
ProcessInfo processInfo = new ProcessInfo();
processInfo.processId = processInfoProcessId;
processInfo.handleCount = (int)pi.HandleCount;
processInfo.sessionId = (int)pi.SessionId;
processInfo.poolPagedBytes = (long)pi.QuotaPagedPoolUsage;;
processInfo.poolNonpagedBytes = (long)pi.QuotaNonPagedPoolUsage;
processInfo.virtualBytes = (long)pi.VirtualSize;
processInfo.virtualBytesPeak = (long)pi.PeakVirtualSize;
processInfo.workingSetPeak = (long)pi.PeakWorkingSetSize;
processInfo.workingSet = (long)pi.WorkingSetSize;
processInfo.pageFileBytesPeak = (long)pi.PeakPagefileUsage;
processInfo.pageFileBytes = (long)pi.PagefileUsage;
processInfo.privateBytes = (long)pi.PrivatePageCount;
processInfo.basePriority = pi.BasePriority;
if( pi.NamePtr == IntPtr.Zero) {
if( processInfo.processId == NtProcessManager.SystemProcessID) {
processInfo.processName = "System";
}
else if( processInfo.processId == NtProcessManager.IdleProcessID) {
processInfo.processName = "Idle";
}
else {
// for normal process without name, using the process ID.
processInfo.processName = processInfo.processId.ToString(CultureInfo.InvariantCulture);
}
}
else {
string processName = GetProcessShortName(Marshal.PtrToStringUni(pi.NamePtr, pi.NameLength/sizeof(char)));
//
// On old operating system (NT4 and windows 2000), the process name might be truncated to 15
// characters. For example, aspnet_admin.exe will show up in performance counter as aspnet_admin.ex.
// Process class try to return a nicer name. We used to get the main module name for a process and
// use that as the process name. However normal user doesn't have access to module information,
// so normal user will see an exception when we try to get a truncated process name.
//
if (ProcessManager.IsOSOlderThanXP && (processName.Length == 15)) {
if (processName.EndsWith(".", StringComparison.OrdinalIgnoreCase)) {
processName = processName.Substring(0, 14);
}
else if (processName.EndsWith(".e", StringComparison.OrdinalIgnoreCase)) {
processName = processName.Substring(0, 13);
}
else if (processName.EndsWith(".ex", StringComparison.OrdinalIgnoreCase)) {
processName = processName.Substring(0, 12);
}
}
processInfo.processName = processName;
}
// get the threads for current process
processInfos[processInfo.processId] = processInfo;
currentPtr = (IntPtr)((long)currentPtr + Marshal.SizeOf(pi));
int i = 0;
while( i < pi.NumberOfThreads) {
SystemThreadInformation ti = new SystemThreadInformation();
Marshal.PtrToStructure(currentPtr, ti);
ThreadInfo threadInfo = new ThreadInfo();
threadInfo.processId = (int)ti.UniqueProcess;
threadInfo.threadId = (int)ti.UniqueThread;
threadInfo.basePriority = ti.BasePriority;
threadInfo.currentPriority = ti.Priority;
threadInfo.startAddress = ti.StartAddress;
threadInfo.threadState = (ThreadState)ti.ThreadState;
threadInfo.threadWaitReason = NtProcessManager.GetThreadWaitReason((int)ti.WaitReason);
processInfo.threadInfoList.Add(threadInfo);
currentPtr = (IntPtr)((long)currentPtr + Marshal.SizeOf(ti));
i++;
}
}
if (pi.NextEntryOffset == 0) {
break;
}
totalOffset += pi.NextEntryOffset;
}
ProcessInfo[] temp = new ProcessInfo[processInfos.Values.Count];
processInfos.Values.CopyTo(temp, 0);
return temp;
}
GetProcessesByName
方法就比较奇怪了,因为其本质上就是调用了 Process.GetProcesses
方法,并在其后额外执行了一些代码。理论上不应该出现耗时更短的情况。事实上,在测试中,我将 GetProcesses
和 GetProcessesByName
方法的执行调换顺序也能得到稳定一致的结果,都是 GetProcessesByName
更快。
public static Process[] GetProcessesByName(string processName, string machineName) {
if (processName == null) processName = String.Empty;
Process[] procs = GetProcesses(machineName);
ArrayList list = new ArrayList();
for(int i = 0; i < procs.Length; i++) {
if( String.Equals(processName, procs[i].ProcessName, StringComparison.OrdinalIgnoreCase)) {
list.Add( procs[i]);
} else {
procs[i].Dispose();
}
}
Process[] temp = new Process[list.Count];
list.CopyTo(temp, 0);
return temp;
}
至于 GetCurrentProcess
方法能够这么快,很好理解,毕竟是自己进程,有什么拿不到的呢?其内部调用的是本机方法:
[DllImport("kernel32.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto)]
public static extern int GetCurrentProcessId();
另外,有个有意思的现象:
.NET 中 GetProcess 相关方法的性能的更多相关文章
- Linux中查找最耗性能的JAVA代码
在这里总结一下查找Linux.Java环境下最耗CPU性能的代码段的方法.基本上原理就是使用top命令查看最耗cpu的进程和线程(子进程).使用jstack把java线程堆栈给dump下来.然后,在堆 ...
- Oracle中HWM与数据库性能的探讨
Oracle中HWM与数据库性能的探讨 一.什么是高水位 HWM(high water mark),高水标记,这个概念在segment的存储内容中是比较重要的.简单来说,HWM就是一个segment中 ...
- 品味性能之道<十一>:JAVA中switch和if性能比较
通常而言大家普遍的认知里switch case的效率高于if else.根据我的理解而言switch的查找类似于二叉树,if则是线性查找.按照此逻辑推理对于对比条件数目大于3时switch更优,并且对 ...
- sar命令,linux中最为全面的性能分析工具之一
sar是System Activity Reporter(系统活动情况报告)的缩写.这个工具所需要的负载很小,也是目前linux中最为全面的性能分析工具之一.此款工具将对系统当前的状态就行取样,然后通 ...
- 【转】PHP中被忽略的性能优化利器:生成器.md
PHP 如果是做Python或者其他语言的小伙伴,对于生成器应该不陌生.但很多PHP开发者或许都不知道生成器这个功能,可能是因为生成器是PHP 5.5.0才引入的功能,也可以是生成器作用不是很明 ...
- Mysql中Union和OR性能对比
博客已搬家,更多内容查看https://liangyongrui.github.io/ Mysql中Union和OR性能对比 在leetcode上看到一篇文章,整理一下 参考:https://leet ...
- Linux中常用的监控性能的命令(sar、mpstat,vmstat, iostat,)详解
Linux中常用的监控性能的命令有: sar:能查看CPU的平均信息,还能查看指定CPU的信息.与mpstat相比,sar能查看CPU历史信息 mpstat:能查看所有CPU的平均信息,还能查看指定C ...
- C#中那些[举手之劳]的性能优化
隔了很久没写东西了,主要是最近比较忙,更主要的是最近比较懒...... 其实这篇很早就想写了 工作和生活中经常可以看到一些程序猿,写代码的时候只关注代码的逻辑性,而不考虑运行效率 其实这对大多数程序猿 ...
- ASP.NET中常用的优化性能的方法
1. 数据库访问性能优化 数据库的连接和关闭 访问数据库资源需要创建连接.打开连接和关闭连接几个操作.这些过程需要多次与数据库交换信息以通过身份验证,比较耗费服务器资源.ASP.NET中提供了连接池( ...
随机推荐
- python的变量,对象的内存地址以及参数传递过程
作为一个由c/c++转过来的菜鸟,刚接触Python的变量的时候很不适应,应为他的行为很像指针,void* ,不知道大家有没有这样的感觉.其实Python是以数据为本,变量可以理解为标签.作为c/c+ ...
- ACM ICPC 2010–2011, Northeastern European Regional Contest St Petersburg – Barnaul – Tashkent – Tbilisi, November 24, 2010
ACM ICPC 2010–2011, Northeastern European Regional Contest St Petersburg – Barnaul – Tashkent – Tbil ...
- [sping]xml配置文件中factory-bean与factory-method(spring使用工厂方法注入bean)
public class CarFactory { //非静态方法 public Car createCar(){ Car car = new Car(); car.setBrand("BM ...
- 英语每日写作---4、VOA慢速英语(翻译+字幕+讲解):专家:城市发展将加剧住房危机
英语每日写作---4.VOA慢速英语(翻译+字幕+讲解):专家:城市发展将加剧住房危机 一.总结 一句话总结: takes place 发生deal with 处理:应付population grow ...
- 面向目标的场景设置--Goal-Oriented Scenario
在场景设置的时候会有两种场景设置方式: 1,手动模式(Manual Scenario) 2.面向目标的场景设置模式(Goal Oriented scenario) 其中手动模式使用较多,而且灵活应用, ...
- findContours函数参数详解
http://blog.csdn.net/dcrmg/article/details/51987348
- Linux命令详解-hwclock/chock
hwclock命令可以用来显示/设置硬件时钟命令. 在Linux中有硬件时钟与系统时钟等两种时钟.硬件时钟是指主机板上的时钟设备,也就是通常可在BIOS画面设定的时钟.系统时钟则是指kernel中 的 ...
- 第七天 Linux用户管理、RHEL6.5及RHEL7.2 root密码破解、RHEL6.5安装vmware tools
1.Linux用户管理 Linux系统中,存在三种用户 A.超级用户:root 最高权限,至高无上 在windows中 administrator是可以登录的最高权限,但是,system权限最高,不能 ...
- Lua学习笔记1,基本数据类型
1.字符串的连接使用的是 .. ,如 print(123 .. 44) 输出 12344 print ('a' .. 'b') 输出 ab print(123 .. 44)这句的时候 .. 两边要空 ...
- bzoj 1996 区间dp
1996: [Hnoi2010]chorus 合唱队 Time Limit: 4 Sec Memory Limit: 64 MBSubmit: 1727 Solved: 1115[Submit][ ...