.NET、Mono與Java、C++性能測(cè)試大PK
原創(chuàng)【51CTO外電頭條】任何計(jì)算設(shè)備硬件資源都是有限的,越多的程序和服務(wù)競(jìng)爭(zhēng)資源,用戶的體驗(yàn)越糟糕(通常表現(xiàn)為延遲較長(zhǎng)),性能下降的部分原因是因?yàn)榘惭b了不需要的組件,還有部分原因是程序內(nèi)部的設(shè)計(jì)問(wèn)題,如讓程序隨系統(tǒng)啟動(dòng)而啟動(dòng),或不管你是否會(huì)使用它,都讓它在后臺(tái)運(yùn)行著,這些運(yùn)行著但又未使用的進(jìn)程都會(huì)搶占有限的系統(tǒng)資源。
雖然我見(jiàn)過(guò)一些有關(guān)程序性能測(cè)試的文章,但卻未見(jiàn)過(guò)對(duì)程序的啟動(dòng)時(shí)間進(jìn)行測(cè)試的,更別說(shuō)是不同編程語(yǔ)言(框架),或同一框架的不同版本了,但這種測(cè)試結(jié)果對(duì)于選擇特定硬件系統(tǒng)后,確定編程語(yǔ)言是非常有幫助的。本文將介紹當(dāng)前比較流行的語(yǔ)言(框架) -.NET,Java,Mono和C++程序的啟動(dòng)性能對(duì)比,所有測(cè)試都是在它們各自的默認(rèn)設(shè)置下進(jìn)行的。但.NET,Mono,Java托管代碼和C++原生代碼誰(shuí)的啟動(dòng)時(shí)間最短,誰(shuí)的性能***呢?首先來(lái)看一下熱啟動(dòng)的對(duì)比結(jié)果吧!
圖 1 Mono,Java,.NET和C++程序熱啟動(dòng)性能對(duì)比(值越小越好)
由于測(cè)試中有諸多因素會(huì)影響結(jié)果,為了使測(cè)試結(jié)果顯得更公平,我們只使用了一些簡(jiǎn)單的,可重復(fù)的測(cè)試,所有語(yǔ)言都可執(zhí)行這些測(cè)試。
首先我們要測(cè)試的是從進(jìn)程創(chuàng)建到進(jìn)入main函數(shù)所花的時(shí)間,簡(jiǎn)稱為“啟動(dòng)時(shí)間”,要精確地測(cè)試出啟動(dòng)時(shí)間是很困難的,有時(shí)只有憑用戶的感覺(jué),接下來(lái)測(cè)量了內(nèi)存占用情況,內(nèi)核和用戶消耗的處理器時(shí)間。
如何計(jì)算啟動(dòng)時(shí)間
在下面的內(nèi)容中,凡是提到操作系統(tǒng)API,我指的操作系統(tǒng)都是指Windows XP,由于沒(méi)有現(xiàn)成的操作系統(tǒng)API可以獲得程序的啟動(dòng)時(shí)間,因此我用了自己發(fā)明的方法來(lái)計(jì)算,我使用了簡(jiǎn)單的進(jìn)程間通信機(jī)制來(lái)解決這個(gè)問(wèn)題,創(chuàng)建進(jìn)程時(shí)將創(chuàng)建時(shí)間作為一個(gè)命令行參數(shù)傳遞給測(cè)試進(jìn)程,執(zhí)行到退出代碼時(shí)返回當(dāng)前時(shí)間和創(chuàng)建時(shí)間的差,具體步驟說(shuō)明如下:
在調(diào)用者進(jìn)程(BenchMarkStartup.exe)中獲得當(dāng)前的UTC系統(tǒng)時(shí)間;
啟動(dòng)測(cè)試進(jìn)程,將前面獲得的進(jìn)程創(chuàng)建時(shí)間作為參數(shù)傳遞給它;
在分支進(jìn)程中,獲得main函數(shù)開(kāi)始執(zhí)行時(shí)的當(dāng)前系統(tǒng)UTC時(shí)間;
在同一進(jìn)程中,計(jì)算并調(diào)整時(shí)間差;
執(zhí)行到退出代碼時(shí)返回時(shí)間差;
在調(diào)用者進(jìn)程(BenchMarkStartup.exe)中捕捉退出代碼。
本文會(huì)使用到兩個(gè)啟動(dòng)時(shí)間:冷啟動(dòng)時(shí)間和熱啟動(dòng)時(shí)間,冷啟動(dòng)表示系統(tǒng)重啟后,程序的***次啟動(dòng)時(shí)間,熱啟動(dòng)時(shí)間表示程序關(guān)閉后,再次啟動(dòng)所花的時(shí)間。冷啟動(dòng)需要的時(shí)間往往會(huì)長(zhǎng)一些,因?yàn)樾枰虞dI/O組件,熱啟動(dòng)可以利用操作系統(tǒng)的預(yù)取功能,因此熱啟動(dòng)的時(shí)間要短得多。
影響性能的因素
對(duì)于托管的運(yùn)行時(shí),與原生代碼比起來(lái),JIT編譯器將會(huì)消耗額外的CPU時(shí)間和內(nèi)存。特別是對(duì)于冷啟動(dòng)時(shí)間的對(duì)比可能會(huì)有失公允,C++原生代碼肯定會(huì)占有優(yōu)勢(shì),而托管型的Mono,Java和.NET代碼需要更長(zhǎng)的加載時(shí)間。另外,如果其它程序加載了你需要的庫(kù),I/O操作也會(huì)減少,啟動(dòng)時(shí)間也會(huì)得到改善。在Java方面,也有一些啟動(dòng)加速程序,如Java Quick Starter,Jinitiator,為了公平起見(jiàn),應(yīng)該禁用它們。緩存和預(yù)取功能也應(yīng)該留給操作系統(tǒng)去管理,不要浪費(fèi)不必要的資源。
C++性能測(cè)試代碼
C++測(cè)試代碼是直接由調(diào)用者進(jìn)程調(diào)用的,當(dāng)它獲得一個(gè)命令行參數(shù)時(shí),它會(huì)將其轉(zhuǎn)換成__int64來(lái)表示FILETIME,其值是從1601/1/1到現(xiàn)在的100 毫微秒間隔數(shù),因此我們可以獲得時(shí)間差,以毫秒數(shù)返回,用32位大小就足夠了。
- int _tmain(int argc, _TCHAR* argv[])
- {
- FILETIME ft;
- GetSystemTimeAsFileTime(&ft);
- static const __int64 startEpoch2 = 0; // 1601/1/1
- if( argc < 2 )
- {
- ::Sleep(5000);
- return -1;
- }
- FILETIME userTime;
- FILETIME kernelTime;
- FILETIME createTime;
- FILETIME exitTime;
- if(GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime))
- {
- __int64 diff;
- __int64 *pMainEntryTime = reinterpret_cast<__int64 *>(&ft);
- _int64 launchTime = _tstoi64(argv[1]);
- diff = (*pMainEntryTime -launchTime)/10000;
- return (int)diff;
- }
- else
- return -1;
- }
下面是創(chuàng)建測(cè)試進(jìn)程的代碼,傳遞給它的是初始時(shí)間,返回的是啟動(dòng)時(shí)間。***個(gè)調(diào)用計(jì)算冷啟動(dòng)時(shí)間,后面的調(diào)用計(jì)算的是熱啟動(dòng)時(shí)間。
- DWORD BenchMarkTimes( LPCTSTR szcProg)
- {
- ZeroMemory( strtupTimes, sizeof(strtupTimes) );
- ZeroMemory( kernelTimes, sizeof(kernelTimes) );
- ZeroMemory( preCreationTimes, sizeof(preCreationTimes) );
- ZeroMemory( userTimes, sizeof(userTimes) );
- BOOL res = TRUE;
- TCHAR cmd[100];
- int i,result = 0;
- DWORD dwerr = 0;
- PrepareColdStart();
- ::Sleep(3000);//3秒延遲
- for(i = 0; i <= COUNT && res; i++)
- {
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- ZeroMemory( &si, sizeof(si) );
- si.cb = sizeof(si);
- ZeroMemory( &pi, sizeof(pi) );
- ::SetLastError(0);
- __int64 wft = 0;
- if(StrStrI(szcProg, _T("java")) && !StrStrI(szcProg, _T(".exe")))
- {
- wft = currentWindowsFileTime();
- _stprintf_s(cmd,100,_T("java -client -cp .\\.. %s \"%I64d\""), szcProg,wft);
- }
- else if(StrStrI(szcProg, _T("mono")) && StrStrI(szcProg, _T(".exe")))
- {
- wft = currentWindowsFileTime();
- _stprintf_s(cmd,100,_T("mono %s \"%I64d\""), szcProg,wft);
- }
- else
- {
- wft = currentWindowsFileTime();
- _stprintf_s(cmd,100,_T("%s \"%I64d\""), szcProg,wft);
- }
- // 啟動(dòng)子進(jìn)程
- if( !CreateProcess( NULL,cmd,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi ))
- {
- dwerr = GetLastError();
- _tprintf( _T("CreateProcess failed for '%s' with error code %d:%s.\n"),szcProg, dwerr,GetErrorDescription(dwerr) );
- return dwerr;
- //中斷;
- }
- //等待20秒,或直到子進(jìn)程退出
- dwerr = WaitForSingleObject( pi.hProcess, 20000 );
- if(dwerr != WAIT_OBJECT_0)
- {
- dwerr = GetLastError();
- _tprintf( _T("WaitForSingleObject failed for '%s' with error code %d\n"),szcProg, dwerr );
- // 關(guān)閉進(jìn)程和線程處理
- CloseHandle( pi.hProcess );
- CloseHandle( pi.hThread );
- break;
- }
- res = GetExitCodeProcess(pi.hProcess,(LPDWORD)&result);
- FILETIME CreationTime,ExitTime,KernelTime,UserTime;
- if(GetProcessTimes(pi.hProcess,&CreationTime,&ExitTime,&KernelTime,&UserTime))
- {
- __int64 *pKT,*pUT, *pCT;
- pKT = reinterpret_cast<__int64 *>(&KernelTime);
- pUT = reinterpret_cast<__int64 *>(&UserTime);
- pCT = reinterpret_cast<__int64 *>(&CreationTime);
- if(i == 0)
- {
- _tprintf( _T("cold start times:\nStartupTime %d ms"), result);
- _tprintf( _T(", PreCreationTime: %u ms"), ((*pCT)- wft)/ 10000);
- _tprintf( _T(", KernelTime: %u ms"), (*pKT) / 10000);
- _tprintf( _T(", UserTime: %u ms\n"), (*pUT) / 10000);
- _tprintf( _T("Waiting for statistics for %d warm samples"), COUNT);
- }
- else
- {
- _tprintf( _T("."));
- kernelTimes[i-1] = (int)((*pKT) / 10000);
- preCreationTimes[i-1] = (int)((*pCT)- wft)/ 10000;
- userTimes[i-1] = (int)((*pUT) / 10000);
- strtupTimes[i-1] = result;
- }
- }
- else
- {
- printf( "GetProcessTimes failed for %p", pi.hProcess );
- }
- // 關(guān)閉進(jìn)程和線程處理
- CloseHandle( pi.hProcess );
- CloseHandle( pi.hThread );
- if((int)result < 0)
- {
- _tprintf( _T("%s failed with code %d: %s\n"),cmd, result,GetErrorDescription(result) );
- return result;
- }
- ::Sleep(1000); //1秒延時(shí)
- }
- if(i <= COUNT )
- {
- _tprintf( _T("\nThere was an error while running '%s', last error code = %d\n"),cmd,GetLastError());
- return result;
- }
- double median, mean, stddev;
- if(CalculateStatistics(&strtupTimes[0], COUNT, median, mean, stddev))
- {
- _tprintf( _T("\nStartupTime: mean = %6.2f ms, median = %3.0f ms, standard deviation = %6.2f ms\n"),
- mean,median,stddev);
- }
- if(CalculateStatistics(&preCreationTimes[0], COUNT, median, mean, stddev))
- {
- _tprintf( _T("PreCreation: mean = %6.2f ms, median = %3.0f ms, standard deviation = %6.2f ms\n"),
- mean,median,stddev);
- }
- if(CalculateStatistics(&kernelTimes[0], COUNT, median, mean, stddev))
- {
- _tprintf( _T("KernelTime : mean = %6.2f ms, median = %3.0f ms, standard deviation = %6.2f ms\n"),
- mean,median,stddev);
- }
- if(CalculateStatistics(&userTimes[0], COUNT, median, mean, stddev))
- {
- _tprintf( _T("UserTime : mean = %6.2f ms, median = %3.0f ms, standard deviation = %6.2f ms\n"),
- mean,median,stddev);
- }
- return GetLastError();
- }
注意啟動(dòng)Mono和Java程序的命令行與.NET或原生代碼有些不同,我也沒(méi)有使用性能監(jiān)視計(jì)數(shù)器。
如果你想知道我為什么沒(méi)有使用GetProcessTimes提供的創(chuàng)建時(shí)間,我可以告訴你有兩個(gè)原因。首先,對(duì)于.NET和Mono,需要DllImport,對(duì)于Java需要JNI,這樣就使程序變得更加臃腫了;第二個(gè)原因是我發(fā)現(xiàn)創(chuàng)建時(shí)間不是CreateProcess API被調(diào)用的真正時(shí)間。從本地硬盤運(yùn)行測(cè)試時(shí),由這兩個(gè)因素引起的時(shí)間會(huì)相差0-10毫秒,如果是從網(wǎng)絡(luò)驅(qū)動(dòng)器運(yùn)行,時(shí)間會(huì)有數(shù)百毫秒的出入,如果是從軟盤上運(yùn)行,甚至可能達(dá)到幾秒。我把這個(gè)時(shí)間差叫做預(yù)創(chuàng)建時(shí)間,我猜測(cè)這是因?yàn)椴僮飨到y(tǒng)沒(méi)有考慮創(chuàng)建新進(jìn)程時(shí),從存儲(chǔ)介質(zhì)讀取文件所花的時(shí)間所致,因?yàn)橹辉诶鋯?dòng)時(shí)有這個(gè)差異,而熱啟動(dòng)就沒(méi)有。
#p#
.NET和Mono C#性能測(cè)試代碼
在調(diào)用的.NET代碼中計(jì)算啟動(dòng)時(shí)間和C++有點(diǎn)不同,它使用了DateTime中的FromFileTimeUtc輔助方法。
- private const long TicksPerMiliSecond = TimeSpan.TicksPerSecond / 1000;
- static int Main(string[] args)
- {
- DateTime mainEntryTime = DateTime.UtcNow;//100 nanoseconds units since 1601/1/1
- int result = 0;
- if (args.Length > 0)
- {
- DateTime launchTime = System.DateTime.FromFileTimeUtc(long.Parse(args[0]));
- long diff = (mainEntryTime.Ticks - launchTime.Ticks) / TicksPerMiliSecond;
- result = (int)diff;
- }
- else
- {
- System.GC.Collect(2, GCCollectionMode.Forced);
- System.GC.WaitForPendingFinalizers();
- System.Threading.Thread.Sleep(5000);
- }
- return result;
- }
使用Mono
要使用Mono必須先從這里下載并安裝好Mono,然后修改環(huán)境變量PATH,增加C:\PROGRA~1\MONO-2~1.4\bin\,注意你使用的Mono版本號(hào)可能會(huì)有些不同,另外,安裝時(shí)可以不選中GTK#和XSP組件,因?yàn)楸敬螠y(cè)試用不著它們,為了簡(jiǎn)化編譯操作,我特意寫了一個(gè)buildMono.bat批處理文件,已包含在本文提供的下載包中。
使用更多.NET版本
我還包括了1.1,2.0,3.5和4.0版本的C# Visual Studio項(xiàng)目,如果你只需運(yùn)行二進(jìn)制文件,需要下載和安裝對(duì)應(yīng)的運(yùn)行時(shí),生成(Build)時(shí)需要Visual Studio 2003和Visual Studio 2010,或如果你喜歡使用命令生成,還需要特定的SDK。為了強(qiáng)制加載目標(biāo)運(yùn)行時(shí)版本,我為所有.NET執(zhí)行文件創(chuàng)建了配置文件,內(nèi)容如下,不同的地方就是版本號(hào):
- <?xml version="1.0" encoding="utf-8" ?>
- <configuration>
- <startup>
- <supportedRuntime version="v1.1.4322" />
- </startup>
- </configuration>
Java性能測(cè)試代碼
首先要從這里下載并安裝Java SDK,同樣也需要向PATH環(huán)境變量添加Java路徑,在開(kāi)始生成前,還需要設(shè)置javac.exe的編譯路徑,如:
- set path=C:\Program Files\Java\jdk1.6.0_16\bin;%path%
- public static void main(String[] args)
- {
- long mainEntryTime = System.currentTimeMillis();//miliseconds since since 1970/1/1
- int result = 0;
- if (args.length > 0)
- {
- //FileTimeUtc adjusted for java epoch
- long fileTimeUtc = Long.parseLong(args[0]);//100 nanoseconds units since 1601/1/1
- long launchTime = fileTimeUtc - 116444736000000000L;//100 nanoseconds units since 1970/1/1
- launchTime /= 10000;//miliseconds since since 1970/1/1
- result = (int)(mainEntryTime - launchTime);
- }
- else
- {
- try
- {
- System.gc();
- System.runFinalization();
- Thread.sleep(5000);
- }
- catch (Exception e)
- {
- e.printStackTrace();
- }
- }
- java.lang.System.exit(result);
- }
由于Java缺乏測(cè)量持續(xù)時(shí)間的解決方案,我不得不使用毫秒,其它框架可以提供更細(xì)粒度的時(shí)間單位,但毫秒在這次的測(cè)試中已經(jīng)夠用了。
獲取內(nèi)存使用情況和處理器時(shí)間
Windows進(jìn)程有許多層面都會(huì)使用內(nèi)存,我將僅限于測(cè)量專用字節(jié),最小工作集和峰值工作集。如果你想知道沒(méi)有參數(shù)時(shí),調(diào)用的進(jìn)程為什么會(huì)等待5秒,現(xiàn)在你應(yīng)該有答案了。在等待2秒后,調(diào)用者將使用下面的代碼測(cè)量?jī)?nèi)存使用情況:
- BOOL PrintMemoryInfo( const PROCESS_INFORMATION& pi)
- {
- //wait 2 seconds while the process is sleeping for 5 seconds
- if(WAIT_TIMEOUT != WaitForSingleObject( pi.hProcess, 2000 ))
- return FALSE;
- if(!EmptyWorkingSet(pi.hProcess))
- printf( "EmptyWorkingSet failed for %x\n", pi.dwProcessId );
- BOOL bres = TRUE;
- PROCESS_MEMORY_COUNTERS_EX pmc;
- if ( GetProcessMemoryInfo( pi.hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)) )
- {
- printf( "PrivateUsage: %lu KB,", pmc.PrivateUsage/1024 );
- printf( " Minimum WorkingSet: %lu KB,", pmc.WorkingSetSize/1024 );
- printf( " PeakWorkingSet: %lu KB\n", pmc.PeakWorkingSetSize/1024 );
- }
- else
- {
- printf( "GetProcessMemoryInfo failed for %p", pi.hProcess );
- bres = FALSE;
- }
- return bres;
- }
最小工作集是調(diào)用的進(jìn)程占用的內(nèi)存由EmptyWorkingSet API收縮后,我計(jì)算出的一個(gè)值。
測(cè)試結(jié)果
這些測(cè)試產(chǎn)生的結(jié)果很多,我只挑選了與本文主題相關(guān)的一些數(shù)據(jù),并將熱啟動(dòng)的測(cè)試結(jié)果也一并展示出來(lái)了,如圖1所示。如果你以調(diào)試模式執(zhí)行測(cè)試,產(chǎn)生的結(jié)果會(huì)更多,對(duì)于熱啟動(dòng),我執(zhí)行了9次測(cè)試,而冷啟動(dòng)只有一次,我只采用了中間值(即去掉了***分和***分),處理器內(nèi)核和用戶時(shí)間被歸結(jié)到一塊兒,總稱為CPU時(shí)間,下表的結(jié)果是來(lái)自一臺(tái)奔四3.0GHz,2GB內(nèi)存的Windows XP機(jī)器的測(cè)試結(jié)果。
運(yùn)行時(shí)
|
冷啟動(dòng)時(shí)間(ms)
|
冷啟動(dòng)CPU時(shí)間(ms)
|
熱啟動(dòng)時(shí)間(ms)
|
熱啟動(dòng)CPU時(shí)間(ms)
|
專用字節(jié)(KB)
|
最小工作集(KB)
|
峰值工作集(KB)
|
.Net 1.1
|
1844
|
156
|
93
|
93
|
3244
|
104
|
4712
|
.Net 2.0
|
1609
|
93
|
78
|
93
|
6648
|
104
|
5008
|
.Net 3.5
|
1766
|
125
|
93
|
77
|
6640
|
104
|
4976
|
.Net 4.0
|
1595
|
77
|
46
|
77
|
7112
|
104
|
4832
|
Java 1.6
|
1407
|
108
|
94
|
92
|
39084
|
120
|
11976
|
Mono 2.6.4
|
1484
|
156
|
93
|
92
|
4288
|
100
|
5668
|
CPP code
|
140
|
30
|
15
|
15
|
244
|
40
|
808
|
注意其中.NET 2.0和.NET 4.0的熱啟動(dòng)時(shí)間比熱啟動(dòng)CPU時(shí)間要低,你可能認(rèn)為這違背了基本的物理定律,但需要注意這里的CPU時(shí)間指的是進(jìn)程的整個(gè)生命周期,而啟動(dòng)時(shí)間僅僅指進(jìn)入到main函數(shù)時(shí)的時(shí)間,通過(guò)這我們知道可以通過(guò)一些優(yōu)化提高這些框架的啟動(dòng)速度,正如你前面看到的,C++由于沒(méi)有框架,因此優(yōu)勢(shì)很明顯,調(diào)用者進(jìn)程通過(guò)預(yù)加載一些通用dll使啟動(dòng)更快。
我沒(méi)有所有運(yùn)行時(shí)的歷史數(shù)據(jù),但從.NET各版本的表現(xiàn)來(lái)看,越新的版本會(huì)通過(guò)消耗更多的內(nèi)存來(lái)提速,如下圖所示。
圖 2 .NET框架不同版本程序熱啟動(dòng)時(shí)性能表現(xiàn)(值越小越好)
為托管運(yùn)行時(shí)使用原生鏡像
除了C++原生代碼外,所有運(yùn)行時(shí)都使用了中間代碼,下一步如果可能應(yīng)該嘗試生成原生鏡像,并再次評(píng)估它們的性能,Java沒(méi)有一個(gè)易于使用的工具來(lái)完成這項(xiàng)任務(wù),GCJ只能完成一半的任務(wù),而且它還不是官方運(yùn)行時(shí)的一部分,因此我會(huì)忽略它。Mono有一個(gè)類似的功能叫做Ahead of Time(AOT),遺憾的是,AOT尚不能在Windows上工作。.NET從一開(kāi)始就支持原生代碼生成,ngen.exe就是運(yùn)行時(shí)的一部分。
為了方便你,我在本文提供的壓縮包中提供了一個(gè)make_nativeimages.bat批處理文件,用它快速生成測(cè)試用程序集的原生鏡像。下表展示了.NET框架各版本原生鏡像的測(cè)試結(jié)果。
運(yùn)行時(shí)
|
冷啟動(dòng)時(shí)間(ms)
|
冷啟動(dòng)CPU時(shí)間(ms)
|
熱啟動(dòng)時(shí)間(ms)
|
熱啟動(dòng)CPU時(shí)間(ms)
|
專用字節(jié)(KB)
|
最小工作集(KB)
|
峰值工作集(KB)
|
.Net 1.1
|
2110
|
140
|
109
|
109
|
3164
|
108
|
4364
|
.Net 2.0
|
1750
|
109
|
78
|
77
|
6592
|
108
|
4796
|
.Net 3.5
|
1859
|
140
|
78
|
77
|
6588
|
108
|
4800
|
.Net 4.0
|
1688
|
108
|
62
|
61
|
7044
|
104
|
4184
|
我們似乎又再次遇到違背物理定律的事情了,上表顯示原生編譯的程序集冷啟動(dòng)時(shí)間更高,不必大驚小怪,因?yàn)榧虞d原生鏡像也需要大量的I/O操作,從測(cè)試結(jié)果來(lái)看,它比加載框架所用的時(shí)間更多。
運(yùn)行測(cè)試
你可以將測(cè)試的可執(zhí)行文件作為一個(gè)參數(shù)傳遞給BenchMarkStartup.exe運(yùn)行一個(gè)特殊的測(cè)試,對(duì)于Java,包名必須匹配目錄結(jié)構(gòu),因此JavaPerf.StartupTest需要一個(gè)..\JavaPerf文件夾。
我在本文提供的壓縮包中提供了一個(gè)runall.bat批處理文件,但它無(wú)法捕捉現(xiàn)實(shí)的冷啟動(dòng)時(shí)間。
如果你想執(zhí)行真實(shí)的測(cè)試,你可以手動(dòng)重啟,或在夜間每隔20-30分鐘調(diào)度執(zhí)行release文件夾的benchmark.bat批處理文件,然后從文本日志文件獲得結(jié)果。重啟機(jī)器后,它將會(huì)運(yùn)行所有運(yùn)行時(shí)的真實(shí)測(cè)試。
***的計(jì)算機(jī)通常會(huì)控制CPU頻率以節(jié)約能源,但這可能會(huì)影響到測(cè)試結(jié)果,因此在運(yùn)行測(cè)試之前,除了前面我已經(jīng)提到的事情外,你還必須將電源使用方案設(shè)置為“高性能”,以便獲得一致的結(jié)果。
小結(jié)
如果你有條件下載文后提供的壓縮包按照本文介紹的內(nèi)容親自做一下對(duì)比測(cè)試,相信你對(duì)托管運(yùn)行時(shí)和原生代碼有更深刻的認(rèn)識(shí),如果你正在猶豫不決地選擇開(kāi)發(fā)平臺(tái),本文也可以幫助你確定清晰的方向,另外,你還可以參照本文創(chuàng)建其它運(yùn)行時(shí)或UI測(cè)試。
本文使用到的測(cè)試源代碼和批處理文件從這里下載,我還對(duì)Java和Mono專門制作了一個(gè)壓縮包,從這里下載。
原文名:Benchmark start-up and system performance for .Net, Mono, Java and C++ native code
【編輯推薦】
- 使用ASP.NET 4的自動(dòng)啟動(dòng)特性
- 詳解.NET 4.0并行計(jì)算支持歷史
- 詳讀.NET 4.0環(huán)境配置
- 詳解.NET 4.0中異常處理方面的新特性
- 三方面詮釋.NET 4.0的新特性