注意:
所有效能時間值和“硬體條件”和“作業系統的當前進程數”有很大的關係,所有效能測試時間長度僅代表我的電腦的資料,但是從這些值的比例關係你可以看出他們的效能差別的。
測試電腦的資訊:
軟體:Windows 7,測試時均運行67個進程,.NET Framework 4.0 Client Profile。
硬體:CPU: Intel Core i5 M480,記憶體: 2GB。
目錄
- 22.3359171秒:使用WMI的效能計數器資料
- 03.5501914秒:使用效能計數器的ID Process
- 02.1461682秒:更智能的使用ID Process
- 01.9123017秒:名稱上的大膽改變
返回目錄
22.3359171秒:使用WMI的效能計數器資料
沒想到WMI的Win32_PerfFormattedData_PerfProc_Process這麼慢,或許WMI本身有些慢加上Win32_PerfFormattedData_PerfProc_Process會提取許多關於進程的資訊,所以整個過程非常之慢,進程名稱一個接一個慢慢得才出來。
代碼:
//+ using System.Diagnostics
//+ using using System.Management (需要引用System.Management.dll)
static void Main()
{
var watch = Stopwatch.StartNew();
//
// 使用WMI的Win32_PerfFormattedData_PerfProc_Process類
//
foreach (var process in Process.GetProcesses())
Console.WriteLine(WMI(process.Id));
watch.Stop();
Console.WriteLine(watch.Elapsed);
}
static string WMI(int pid)
{
var searcher = new ManagementObjectSearcher(new SelectQuery("Win32_PerfFormattedData_PerfProc_Process", "IDProcess=" + pid.ToString()));
foreach (var mobj in searcher.Get())
return (string)mobj["Name"];
throw new Exception();
}
返回目錄
03.5501914秒:使用效能計數器的ID Process
這個比WMI的方法快多了(約7倍),其中有可觀的一部分時間應該花在PerformanceCounter的GetInstanceNames中,其次就是依次對每一個執行個體名稱進行ID Process效能計數器值的擷取,知道找到相應PID為止。這種方法邏輯上比較笨,不過目前沒有更好的方法了,況且效能上還是可以的。
主方法:GetProcessInstanceName就是通過PID擷取效能計數器執行個體名稱的方法,直接從這裡拷貝的(我做了少許修改):http://weblogs.thinktecture.com/ingo/2004/06/getting-the-current-process-your-own-cpu-usage.html
代碼:
//+ using System.Diagnostics
//+ using using System.Management (需要引用System.Management.dll)
static void Main()
{
var watch = Stopwatch.StartNew();
//
// 使用效能計數器的ID Process
//
foreach (var process in Process.GetProcesses())
Console.WriteLine(GetProcessInstanceName(process.Id));
watch.Stop();
Console.WriteLine(watch.Elapsed);
}
static PerformanceCounterCategory cat = new PerformanceCounterCategory("Process");
private static string GetProcessInstanceName(int pid)
{
string[] instances = cat.GetInstanceNames();
foreach (string instance in instances)
{
using (PerformanceCounter cnt = new PerformanceCounter("Process",
"ID Process", instance, true))
{
int val = (int)cnt.RawValue;
if (val == pid)
{
return instance;
}
}
}
throw new InvalidOperationException();
}
返回目錄
02.1461682秒:更智能的使用ID Process
效能計數器的執行個體名稱和普通進程名稱區別就在於重名的進程名稱的效能計數器執行個體名稱後面要加#xxx,而不重名的進程名稱的效能計數器執行個體名稱顯然是一樣的。那麼我們在方法二的基礎上做一個最佳化,首先在所得到的進程中判斷重複的,最後在輸出時如果進程屬於重複的進程那麼調用上面的從PID擷取執行個體名稱的方法。如果不是,那麼我們可以直接輸出名稱。不過之前要對進程資料進行一些處理,當然所謂“磨刀不誤砍柴工”,資料處理需要一些時間,但後續輸出會很快,因為不重複的進程名稱會直接被輸出。
整個操作相比上一個幾乎提升了1秒。
注意:
常駐的重名的Windows進程還是很少的,比如在我這裡就是:svchost, conhost, csrss, taskhost。當然如果你開啟多個相同的應用程式,相同的進程名稱會很多。
代碼:
//+ using System.Diagnostics
//+ using using System.Management (需要引用System.Management.dll)
static void Main()
{
var watch = Stopwatch.StartNew();
//
// 更智能的使用效能計數器的ID Process
//
//重複的進程名稱
var duplicate = Process.GetProcesses()
.GroupBy(pro => pro.ProcessName)
.Where(g => g.Count() > 1)
.Select(g => g.ElementAt(0).ProcessName);
//重複的進程名稱儲存在HashSet中
var hashSet = new HashSet<string>(duplicate);
//判斷並輸出
foreach (var pro in Process.GetProcesses())
if (hashSet.Contains(pro.ProcessName))
Console.WriteLine(GetProcessInstanceName(pro.Id));
else
Console.WriteLine(pro.ProcessName);
//
watch.Stop();
Console.WriteLine(watch.Elapsed);
}
static PerformanceCounterCategory cat = new PerformanceCounterCategory("Process");
/* GetProcessInstanceName(int pid) 方法代碼和上面相同,這裡省去 */
返回目錄
01.9123017秒:名稱上的大膽改變
在上一個代碼的基礎上,首先做了一項大膽改變,我們把PerformanceCounter的GetInstanceNames方法放在了主函數裡,所以它只運行一次而不是每次查詢都要去重新整理。這個可能會有些爭議,因為效能計數器的執行個體名稱有可以會變。但是如果程式中Process.GetProcess能夠及時重新整理的話,那麼效能計數器名稱的重新整理有些沒有必要,即便是某個發生了未重新整理效能計數器名稱所引起的錯誤,後續Process.GetProcess的重新整理也會儘快更新資料了。
其次就是在進程名稱的枚舉時,用String.StartsWith先判斷如果名稱一樣才會進行ID Process的值索取。不過貌似StartsWith方法並沒有比PerformanceCounter值的索取方法快多少。
注意:
這個方法並沒有提升多少效能(爭議倒是引起不少),時間有時是1.5有時是2.9。不過相比上一個方法效能上還是有一些提高的,後者有時是3.5大多數穩定在2秒內,但是從來沒少於2秒過。
(代碼僅有兩處改變,且都很簡單就不重複粘貼了)