自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

.NET、Mono與Java、C++性能測(cè)試大PK

原創(chuàng)
開(kāi)發(fā) 后端 新聞
四年一屆的世界杯終于落下帷幕,四支強(qiáng)隊(duì)為金杯曾展開(kāi)最后的爭(zhēng)奪。而在開(kāi)發(fā)領(lǐng)域,.NET、Mono與Java、C++之間的爭(zhēng)論似乎一直沒(méi)有停過(guò),本文將為大家做一個(gè)橫向的對(duì)比,看看誰(shuí)的性能更好。

【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é)果吧!

熱啟動(dòng)性能對(duì)比

圖 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位大小就足夠了。

  1. int _tmain(int argc, _TCHAR* argv[])  
  2. {  
  3.    
  4.     FILETIME   ft;  
  5.     GetSystemTimeAsFileTime(&ft);  
  6.     static const __int64 startEpoch2 = 0; // 1601/1/1  
  7.     if( argc < 2 )  
  8.     {  
  9.     ::Sleep(5000);  
  10.     return -1;  
  11.     }  
  12.     FILETIME userTime;  
  13.     FILETIME kernelTime;  
  14.     FILETIME createTime;  
  15.     FILETIME exitTime;  
  16.    
  17.     if(GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime))  
  18.     {  
  19.       __int64 diff;  
  20.       __int64 *pMainEntryTime = reinterpret_cast<__int64 *>(&ft);  
  21.       _int64 launchTime = _tstoi64(argv[1]);  
  22.       diff = (*pMainEntryTime -launchTime)/10000;      
  23.       return (int)diff;  
  24.     }  
  25.     else 
  26.         return -1;  

下面是創(chuàng)建測(cè)試進(jìn)程的代碼,傳遞給它的是初始時(shí)間,返回的是啟動(dòng)時(shí)間。***個(gè)調(diào)用計(jì)算冷啟動(dòng)時(shí)間,后面的調(diào)用計(jì)算的是熱啟動(dòng)時(shí)間。

  1. DWORD BenchMarkTimes( LPCTSTR szcProg)  
  2. {  
  3.     ZeroMemory( strtupTimes, sizeof(strtupTimes) );  
  4.     ZeroMemory( kernelTimes, sizeof(kernelTimes) );  
  5.     ZeroMemory( preCreationTimes, sizeof(preCreationTimes) );  
  6.     ZeroMemory( userTimes, sizeof(userTimes) );  
  7.     BOOL res = TRUE;  
  8.     TCHAR cmd[100];  
  9.     int i,result = 0;  
  10.     DWORD dwerr = 0;  
  11.     PrepareColdStart();  
  12.     ::Sleep(3000);//3秒延遲  
  13.     for(i = 0; i <= COUNT && res; i++)  
  14.     {  
  15.         STARTUPINFO si;  
  16.         PROCESS_INFORMATION pi;  
  17.         ZeroMemory( &si, sizeof(si) );  
  18.         si.cb = sizeof(si);  
  19.         ZeroMemory( &pi, sizeof(pi) );  
  20.         ::SetLastError(0);  
  21.         __int64 wft = 0;  
  22.         if(StrStrI(szcProg, _T("java")) && !StrStrI(szcProg, _T(".exe")))  
  23.         {  
  24.             wft = currentWindowsFileTime();  
  25.             _stprintf_s(cmd,100,_T("java -client -cp .\\.. %s \"%I64d\""), szcProg,wft);  
  26.         }  
  27.         else if(StrStrI(szcProg, _T("mono")) && StrStrI(szcProg, _T(".exe")))  
  28.         {   
  29.                 wft = currentWindowsFileTime();  
  30.                 _stprintf_s(cmd,100,_T("mono %s \"%I64d\""), szcProg,wft);  
  31.         }  
  32.         else 
  33.         {  
  34.                 wft = currentWindowsFileTime();  
  35.                 _stprintf_s(cmd,100,_T("%s \"%I64d\""), szcProg,wft);           
  36.         }  
  37.    
  38.         // 啟動(dòng)子進(jìn)程   
  39.         if( !CreateProcess( NULL,cmd,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi ))   
  40.         {  
  41.             dwerr = GetLastError();  
  42.  _tprintf( _T("CreateProcess failed for '%s' with error code %d:%s.\n"),szcProg, dwerr,GetErrorDescription(dwerr) );  
  43.             return dwerr;  
  44.             //中斷;  
  45.         }  
  46.    
  47.         //等待20秒,或直到子進(jìn)程退出  
  48.         dwerr = WaitForSingleObject( pi.hProcess, 20000 );  
  49.         if(dwerr != WAIT_OBJECT_0)  
  50.         {  
  51.             dwerr = GetLastError();  
  52.  _tprintf( _T("WaitForSingleObject failed for '%s' with error code %d\n"),szcProg, dwerr );  
  53.             // 關(guān)閉進(jìn)程和線程處理   
  54.             CloseHandle( pi.hProcess );  
  55.             CloseHandle( pi.hThread );  
  56.             break;  
  57.         }  
  58.         res = GetExitCodeProcess(pi.hProcess,(LPDWORD)&result);  
  59.         FILETIME CreationTime,ExitTime,KernelTime,UserTime;  
  60.         if(GetProcessTimes(pi.hProcess,&CreationTime,&ExitTime,&KernelTime,&UserTime))  
  61.         {  
  62.             __int64 *pKT,*pUT, *pCT;  
  63.             pKT = reinterpret_cast<__int64 *>(&KernelTime);  
  64.             pUT = reinterpret_cast<__int64 *>(&UserTime);  
  65.             pCT = reinterpret_cast<__int64 *>(&CreationTime);  
  66.             if(i == 0)  
  67.             {  
  68.                 _tprintf( _T("cold start times:\nStartupTime %d ms"), result);  
  69.                 _tprintf( _T(", PreCreationTime: %u ms"), ((*pCT)- wft)/ 10000);   
  70.                 _tprintf( _T(", KernelTime: %u ms"), (*pKT) / 10000);  
  71.                 _tprintf( _T(", UserTime: %u ms\n"), (*pUT) / 10000);   
  72.                 _tprintf( _T("Waiting for statistics for %d warm samples"), COUNT);   
  73.             }  
  74.             else 
  75.             {  
  76.                 _tprintf( _T("."));  
  77.                 kernelTimes[i-1] = (int)((*pKT) / 10000);  
  78.                 preCreationTimes[i-1] = (int)((*pCT)- wft)/ 10000;   
  79.                 userTimes[i-1] = (int)((*pUT) / 10000);  
  80.                 strtupTimes[i-1] = result;      
  81.             }  
  82.         }  
  83.         else 
  84.         {  
  85.             printf( "GetProcessTimes failed for %p", pi.hProcess );   
  86.         }  
  87.         // 關(guān)閉進(jìn)程和線程處理   
  88.         CloseHandle( pi.hProcess );  
  89.         CloseHandle( pi.hThread );  
  90.         if((int)result < 0)  
  91.         {  
  92.             _tprintf( _T("%s failed with code %d: %s\n"),cmd, result,GetErrorDescription(result) );  
  93.             return result;  
  94.         }  
  95.         ::Sleep(1000); //1秒延時(shí)  
  96.     }  
  97.     if(i <= COUNT )  
  98.     {  
  99.        _tprintf( _T("\nThere was an error while running '%s', last error code = %d\n"),cmd,GetLastError());  
  100.        return result;  
  101.     }  
  102.     double median, mean, stddev;  
  103.     if(CalculateStatistics(&strtupTimes[0], COUNT, median, mean, stddev))  
  104.     {  
  105.         _tprintf( _T("\nStartupTime: mean = %6.2f ms, median = %3.0f ms, standard deviation = %6.2f ms\n"),   
  106.             mean,median,stddev);  
  107.     }  
  108.     if(CalculateStatistics(&preCreationTimes[0], COUNT, median, mean, stddev))  
  109.     {  
  110.         _tprintf( _T("PreCreation: mean = %6.2f ms, median = %3.0f ms, standard deviation = %6.2f ms\n"),   
  111.             mean,median,stddev);  
  112.     }  
  113.     if(CalculateStatistics(&kernelTimes[0], COUNT, median, mean, stddev))  
  114.     {  
  115.         _tprintf( _T("KernelTime : mean = %6.2f ms, median = %3.0f ms, standard deviation = %6.2f ms\n"),   
  116.             mean,median,stddev);  
  117.     }  
  118.     if(CalculateStatistics(&userTimes[0], COUNT, median, mean, stddev))  
  119.     {  
  120.         _tprintf( _T("UserTime   : mean = %6.2f ms, median = %3.0f ms, standard deviation = %6.2f ms\n"),   
  121.             mean,median,stddev);  
  122.     }  
  123.    
  124.     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è)試代碼

[[12874]]

在調(diào)用的.NET代碼中計(jì)算啟動(dòng)時(shí)間和C++有點(diǎn)不同,它使用了DateTime中的FromFileTimeUtc輔助方法。

  1. private const long TicksPerMiliSecond = TimeSpan.TicksPerSecond / 1000;  
  2. static int Main(string[] args)  
  3. {  
  4.     DateTime mainEntryTime = DateTime.UtcNow;//100 nanoseconds units since 1601/1/1  
  5.     int result = 0;  
  6.    
  7.     if (args.Length > 0)  
  8.     {  
  9.         DateTime launchTime = System.DateTime.FromFileTimeUtc(long.Parse(args[0]));  
  10.         long diff = (mainEntryTime.Ticks - launchTime.Ticks) / TicksPerMiliSecond;  
  11.         result = (int)diff;  
  12.    
  13.     }  
  14.     else 
  15.     {  
  16.         System.GC.Collect(2, GCCollectionMode.Forced);  
  17.         System.GC.WaitForPendingFinalizers();  
  18.         System.Threading.Thread.Sleep(5000);  
  19.     }  
  20.     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):

  1. <?xml version="1.0" encoding="utf-8" ?> 
  2. <configuration> 
  3. <startup> 
  4.  <supportedRuntime version="v1.1.4322" /> 
  5. </startup> 
  6. </configuration> 

Java性能測(cè)試代碼

[[12875]]

首先要從這里下載并安裝Java SDK,同樣也需要向PATH環(huán)境變量添加Java路徑,在開(kāi)始生成前,還需要設(shè)置javac.exe的編譯路徑,如:

  1. set path=C:\Program Files\Java\jdk1.6.0_16\bin;%path% 
在本文提供的壓縮包中,我提供了一個(gè)buildJava.bat批處理文件來(lái)幫助你完成生成操作,Java性能測(cè)試代碼如下:
 
  1. public static void main(String[] args)  
  2. {  
  3.     long mainEntryTime = System.currentTimeMillis();//miliseconds since since 1970/1/1  
  4.     int result = 0;  
  5.     if (args.length > 0)  
  6.     {  
  7.         //FileTimeUtc adjusted for java epoch  
  8.         long fileTimeUtc = Long.parseLong(args[0]);//100 nanoseconds units since 1601/1/1  
  9. long launchTime = fileTimeUtc - 116444736000000000L;//100 nanoseconds units since 1970/1/1  
  10.         launchTime /= 10000;//miliseconds since since 1970/1/1  
  11.         result = (int)(mainEntryTime - launchTime);  
  12.     }  
  13.     else 
  14.     {  
  15.         try 
  16.         {  
  17.             System.gc();  
  18.             System.runFinalization();  
  19.             Thread.sleep(5000);  
  20.         }  
  21.         catch (Exception e)  
  22.         {  
  23.             e.printStackTrace();  
  24.         }  
  25.    
  26.     }  
  27.     java.lang.System.exit(result);  
  28. }  

由于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)存使用情況:

 
  1. BOOL PrintMemoryInfo( const PROCESS_INFORMATION& pi)  
  2. {  
  3.  //wait 2 seconds while the process is sleeping for 5 seconds  
  4.     if(WAIT_TIMEOUT != WaitForSingleObject( pi.hProcess, 2000 ))  
  5.      return FALSE;  
  6.     if(!EmptyWorkingSet(pi.hProcess))  
  7.     printf( "EmptyWorkingSet failed for %x\n", pi.dwProcessId );  
  8.     BOOL bres = TRUE;  
  9.     PROCESS_MEMORY_COUNTERS_EX pmc;  
  10. if ( GetProcessMemoryInfo( pi.hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)) )  
  11.     {  
  12.     printf( "PrivateUsage: %lu KB,", pmc.PrivateUsage/1024 );  
  13.         printf( " Minimum WorkingSet: %lu KB,", pmc.WorkingSetSize/1024 );  
  14.         printf( " PeakWorkingSet: %lu KB\n", pmc.PeakWorkingSetSize/1024 );   
  15.    
  16.     }  
  17.     else 
  18.    {  
  19.     printf( "GetProcessMemoryInfo failed for %p", pi.hProcess );   
  20.     bres = FALSE;  
  21.    }  
  22.     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)提速,如下圖所示。

.NET框架不同版本程序熱啟動(dòng)時(shí)性能表現(xiàn)

圖 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

【編輯推薦】

  1. 使用ASP.NET 4的自動(dòng)啟動(dòng)特性
  2. 詳解.NET 4.0并行計(jì)算支持歷史
  3. 詳讀.NET 4.0環(huán)境配置
  4. 詳解.NET 4.0中異常處理方面的新特性
  5. 三方面詮釋.NET 4.0的新特性 

 

責(zé)任編輯:彭凡 來(lái)源: 51CTO
相關(guān)推薦

2010-10-20 10:30:58

PHPJSP

2009-09-08 11:24:20

2014-04-17 10:37:43

C++.NET Native

2009-11-06 14:01:48

EPON接入技術(shù)

2023-11-21 16:13:38

C++代碼

2021-11-28 08:04:27

.Net性能測(cè)試

2019-07-29 09:19:26

編程語(yǔ)言PythonJava

2020-05-25 17:04:09

AMD云主機(jī)評(píng)測(cè)

2010-11-01 12:56:02

云計(jì)算高性能計(jì)算

2012-01-04 01:35:46

Power虛擬機(jī)x86性能大賽

2012-08-17 10:22:26

HTML5性能瀏覽器

2024-04-18 11:07:30

C++語(yǔ)言

2022-08-22 15:32:59

C++C代碼

2012-02-27 09:28:34

HadoopFourinone淘寶

2010-05-27 12:58:16

MySQL性能測(cè)試

2020-02-02 15:42:22

PythonC++編程語(yǔ)言

2021-06-10 09:40:12

C++性能優(yōu)化Linux

2017-12-13 08:20:15

芯片驍龍?人工智能

2010-01-12 09:48:51

無(wú)線交換機(jī)

2023-04-14 07:18:24

RTX 4090Boost頻率
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)