2015-05-14 2 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); 

Кроме того, у меня есть проект с пустым основным методом. Моя логика заключалась в том, что когда я измеряю выполнение «Сита» и вычитаю время пустого проекта, результирующее число должно быть примерно таким же, как число, измеренное «TimedSieve». Так что я начал измерение ...

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

Очевидно, что эти результаты кажутся очень тусклыми:

  1. TimedSieve намного быстрее, чем как пустой проекта и решето
  2. Пустой проект медленнее чем сито!

Просто из любопытства я приурочил Сито и пустой проект с использованием Powershells «Measure-Command»

Sieve: 25 milliseconds 
Empty: 17 milliseconds 

Что-то я заметил, что тот факт, что порядок, в котором были измерены процессы под влиянием результаты, процесс, который был измерен, всегда всегда терялся. Я также заметил, что перенос начала секундомера после начала процесса, как этого

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

избавившись от вышеупомянутого эффекта (Empty теперь всегда быстрее, чем решето) и получают номера, которые гораздо ближе к результаты других методов измерения

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

Это часто забытое объяснение, иногда мы забываем (все мы, программисты, даже), что есть другие вещи, которые продолжаются в начале программы. Disk File 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. Время для других незначительных и важных вещей, которые происходят.

Это могут быть небольшие промежутки времени, но они все равно добавляют к времени выполнения полного приложения таким образом, который отличается от пустого.


Кроме того, чтобы сделать результаты более близко к даваемых PowerShell, вы можете попробовать использовать Process.StartTime и Process.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. –

Смежные вопросы