2015-05-14 49 views
2

我正嘗試構建一個小型基準測試應用程序,它允許用戶測量程序的執行時間和內存使用情況。使用C#測量其他進程的執行時間,奇數結果

這是衡量執行時間的代碼:

private static Stopwatch _stopwatch; 

static void Main(string[] args] 
{ 
    _stopwatch = new Stopwatch(); 

    //only for this test 
    Console.WriteLine(TimeProcess("Empty.exe")); 
    Console.WriteLine(TimeProcess("Sieve.exe")); 
    Console.ReadKey(); 
} 

private static long TimeProcess(String name) 
{ 
    Process process = new Process(); 
    process.StartInfo.FileName = name; 

    _stopwatch.Reset(); 
    _stopwatch.Start(); 
    process.Start(); 
    process.WaitForExit(); 
    _stopwatch.Stop(); 

    return _stopwatch.ElapsedMilliseconds; 
} 

要查看代碼是否工作正常,我決定實施「埃拉托色尼的篩」算法。我實施了兩次,一次使用內置秒錶,還有一次沒有。

int[] numbersToTest = Enumerable.Range(0, 1000).ToArray(); 
int posInArray = 2; 

while (numbersToTest[posInArray] != numbersToTest[numbersToTest.Length - 1]) 
{ 
    numbersToTest = numbersToTest.Where(x => x % numbersToTest[posInArray] != 0 || x == numbersToTest[posInArray]).ToArray(); 
    posInArray++; 
} 

TimedSieve:

Stopwatch stopwatch = new Stopwatch(); 
stopwatch.Start(); 
int[] numbersToTest = Enumerable.Range(0, 1000).ToArray(); 
int posInArray = 2; 

while (numbersToTest[posInArray] != numbersToTest[numbersToTest.Length - 1]) 
{ 
    numbersToTest = numbersToTest.Where(x => x % numbersToTest[posInArray] != 0 || x == numbersToTest[posInArray]).ToArray(); 
    posInArray++; 
} 
stopwatch.Stop(); 
Console.WriteLine(stopwatch.ElapsedTicks); 

而且,我有一個空的主要方法的項目。我的邏輯是,當我測量「Sieve」的執行並減去空項目的時間時,結果數字應該與「TimedSieve」所測量的數字大致相同。 於是我開始測量......

Empty: 79 milliseconds 
Sieve: 53 milliseconds 
TimedSieve: 4 milliseconds 

顯然,這些結果似乎很腥:

  1. 的TimedSieve是很多比這兩個空項目和篩
  2. 空項目是慢速度更快比篩子!

只是出於好奇,我也定時篩和使用Powershells「措施命令」

Sieve: 25 milliseconds 
Empty: 17 milliseconds 

東西我注意到的是空項目的順序進行測量的過程影響的事實結果,首先測量的過程總是失敗。我也注意到,過程是這樣

process.Start(); 
_stopwatch.Start(); 

開始後移動秒錶開始擺脫了上述效果(空現在總是比篩快),並生產出更接近一個很大的數字的其它測量方法

Empty: 34 
Sieve: 42 

在試圖解決這個問題的結果,我也讀了基準測試應包括一個「熱身」輪,我決定基準這兩個程序多次,取平均值,以取得更好的成績。

static void Main(string[] args) 
{ 
    _stopwatch = new Stopwatch(); 

    //discard results of the first run 
    TimeProcess("Sieve.exe"); 

    long sum = 0; 
    for (int i = 0; i < 100; i++) 
    { 
     sum += TimeProcess("Sieve.exe"); 
    } 
    Console.WriteLine(sum/100); 

    TimeProcess("Empty.exe"); 

    sum = 0; 
    for (int i = 0; i < 100; i++) 
    { 
     sum += TimeProcess("Empty.exe"); 
    } 
    Console.WriteLine(sum/100); 
    Console.ReadKey(); 
} 

這擺脫了「空比篩慢」的效果,這也是爲什麼我決定重新開始的過程之前秒錶。

如何改進此代碼以獲得可靠的結果?雖然這些數字變得更加合理,但它們仍然比Powershell和TimedSieve測量結果都要慢。

+0

您可能在那裏測量磁盤IO時間,這就是爲什麼結果很可疑。第一個過程比較慢,因爲磁盤需要尋找,而第二個過程很可能已經找到了,並且剛剛進入。不幸的是,沒有簡單的方法來消除這種影響。第二個解決方案(啓動一次,然後運行100次)的原因是Windows將程序緩存到內存中。如果緩存不受干擾(就像在這種情況下那樣),那麼它只能從緩存中加載程序。 –

+0

謝謝,這似乎是對第一個結果的合理解釋,我懷疑它會進入我的腦海 – Aifu

+1

這是一個經常被忽視的解釋,有時我們忘記了(我們所有的程序員,甚至)還有其他事情正在進行在節目的開始。磁盤文件IO,內存分配,堆分配等等。 –

回答

0

在非實際系統操作系統中測量某個其他進程的執行時間可能會導致不一致的結果。這只是沒有(主要)時間保證的系統的性質。

您已經或多或少地處理了預熱問題(IO和IO緩存相關...)問題,並通過多次運行使結果更加統計正確。

但是TimeOf(Algo)並不真正等於TimeOf(FullAppWithAlgo) - TimeOf(Empty)

這是接近,但TimeOf(Algo)不包括:

  1. 所花時間與JIT代碼,而不是隻是空主要的應用,
  2. 花費的時間來初始化靜態類(that doesn't occur if class is never used, like in Empty
  3. 發生其他次要和重大事件的時間。

它們可能是小時間跨度,但它們仍會以與Empty不同的方式增加完整應用程序的執行時間。


此外,爲了使結果更加接近通過PowerShell中給出的那些,你可以嘗試使用Process.StartTimeProcess.ExitTime代替StopWatch同時測量完整的應用程序運行時:

private static long TimeProcess(String name) 
{ 
    Process process = new Process(); 
    process.StartInfo.FileName = name; 

    process.Start(); 
    process.WaitForExit(); 

    return (process.EndTime - process.StartTime).TotalMilliseconds; 
} 

它不會改變空應用程序和完整應用程序之間的差異,但會爲每次運行提供更一致的計時,因爲您不必等待操作系統發出通知,這顯然會在應用程序結束後的一段時間內發生。

+0

謝謝你這個非常詳細的答案!實際上我想使用秒錶,因爲它的精確度很高,但考慮到我的大部分測量結果都會在毫秒級以上,我認爲您的DateTime解決方案更優越 – Aifu

+0

@Aifu是的,它會降低精度,但是對於較大的執行時間和多次運行就足夠了。另外,正如我已經說過的,它不會像StopWatch那樣在沒有啓動前和通知時間的情況下提供真正的執行時間。 –