Utils.PerfCounter — простенький классик для точного замера производительности в дотнете.
Измеряет значительно точнее чем Environment.TickCount. Environment.TickCount имеет точность ~ 10 милисекунд, что для измерения коротких участков неприемлимо. Использование QueryPerformanceCounter позволяет обеспечить очень высокую точность.
Использовать так:
// Где нибудь объявляем переменную...
PerfCounter timer = new PerfCounter();
timer.Start();// Начало замера
// тестируемый код...
// Выводим результат в консоль.
Console.WriteLine("Время выполнения в секундах: {0:### ### ##0.0000}"timer.Finish());
// Одну переменную можно использовать многократно.
Код класса:
using System;
using System.Runtime.InteropServices;
namespace Utils
{
/// <summary>
/// Эта структура позволяет подсчитать скорость выполнения кода одним из
/// наиболее точным способов. Фактически вычисления производятся в тактах
/// процессора, а потом переводятся в милисекунд (десятичная часть
/// является долями секунды).
/// </summary>public struct PerfCounter
{
Int64 _start;
/// <summary>
/// Начинает подсчет вермени выполнения.
/// </summary>public void Start()
{
_start = 0;
QueryPerformanceCounter(ref _start);
}
/// <summary>
/// Завершает полсчет вермени исполнения и возвращает время в секундах.
/// </summary>
/// <returns>Время в секундах потраченое на выполнение участка
/// кода. Десятичная часть отражает доли секунды.</returns>public float Finish()
{
Int64 finish = 0;
QueryPerformanceCounter(ref finish);
Int64 freq = 0;
QueryPerformanceFrequency(ref freq);
return (((float)(finish - _start) /(float)freq));
}
[DllImport("Kernel32.dll")]
static extern bool QueryPerformanceCounter(ref Int64 performanceCount);
[DllImport("Kernel32.dll")]
static extern bool QueryPerformanceFrequency(ref Int64 frequency);
}
}
... << RSDN@Home 1.0 beta 6a >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Utils.PerfCounter — простенький классик для точного замера производительности в дотнете.
VD>Измеряет значительно точнее чем Environment.TickCount. Environment.TickCount имеет точность ~ 10 милисекунд, что для измерения коротких участков неприемлимо. Использование QueryPerformanceCounter позволяет обеспечить очень высокую точность.
Wrap'ы по осени считают. Целый класс всего для двух Win32API функций, которые все и так знают.
Добавил бы для приличия поддержку нескольких таймеров. Через System.Collections.Hashtable(для именованных таймеров) или самопальный heap(чтоб быстрее было ), или хотя бы просто массив. Для пущего приличия должны кидаться exception'ы собственного типа по случаю тайм-аута(в перегруженный start надо добавить аргумент time-out) или если не произошло замыкающего QueryPerfomanceCounter и был вызван start с этим же ключом. PerfCounterException
Можно сделать также следящий за тайм-аутами и ругающийся thread, динамический список типов таймеров, статистику по отдельным видам таймеров, вывод отчет в xml, поддержку вложенных таймеров, отчет по процентному отношению времени вложенных таймеров к родителю, статистику по всему приложению, загрузка из xml отчета и сверка отчетов
Если все сделаешь, я тогда этот класс буду использовать(это шутка была).
Re[2]: Точное измерение производительности в дотнете
Здравствуйте, VladD2, Вы писали:
VD>Измеряет значительно точнее чем Environment.TickCount. Environment.TickCount имеет точность ~ 10 милисекунд, что для измерения коротких участков неприемлимо. Использование QueryPerformanceCounter позволяет обеспечить очень высокую точность...
...но иногда с глюками
MSDN: Q274323
... << RSDN@Home 1.0 beta 6a >>
Re[2]: Точное измерение производительности в дотнете
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, WFrag, Вы писали:
WF>>...но иногда с глюками WF>>MSDN: Q274323
VD>Ну, это же при глючном железе. По кройне мере я такого не встречал.
Видимо, я везучий. У меня именно такое железо, похоже.
7. О чем невозможно говорить, о том следует молчать.
Здравствуйте, VladD2, Вы писали:
VD>Utils.PerfCounter — простенький классик для точного замера производительности в дотнете.
VD>Измеряет значительно точнее чем Environment.TickCount. Environment.TickCount имеет точность ~ 10 милисекунд, что для измерения коротких участков неприемлимо. Использование QueryPerformanceCounter позволяет обеспечить очень высокую точность.
VD>Использовать так:
Для больших кусков кода лучше дополнитеьно вызывать GetThreadTimes.
А для маленьких — rdtsc буде в разы порядков точнее, ибо один вызов этого каунтера занимает в среднем 4000 тактов. Потому и точность у него малая — 8 микросекунд.
Нужно участок кода гонять обязательно несколько раз.
Но для rdtsc нужно дллку писать, иногда это неудобно.
Re[2]: Точное измерение производительности в дотнете
Здравствуйте, Plutonia Experiment, Вы писали:
PE>А для маленьких — rdtsc буде в разы порядков точнее, ибо один вызов этого каунтера занимает в среднем 4000 тактов. Потому и точность у него малая — 8 микросекунд.
Какой ассемблер под дотнетом? А QueryPerformanceCounter его скорее всего и использует.
PE>Нужно участок кода гонять обязательно несколько раз.
Я бы сказал желательно. Так как в 90% случаев разницы нет (проверено).
PE>Но для rdtsc нужно дллку писать, иногда это неудобно.
QueryPerformanceCounter
... << RSDN@Home 1.0 beta 8 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Точное измерение производительности в дотнете
Здравствуйте, VladD2, Вы писали:
PE>>А для маленьких — rdtsc буде в разы порядков точнее, ибо один вызов этого каунтера занимает в среднем 4000 тактов. Потому и точность у него малая — 8 микросекунд.
VD>Какой ассемблер под дотнетом? А QueryPerformanceCounter его скорее всего и использует.
Никто ж не говорит, что это под дотнетом. У меня для того есть либина, из которой я просто импортирую так же, как и ты из kernel32.
PE>>Нужно участок кода гонять обязательно несколько раз.
VD>Я бы сказал желательно. Так как в 90% случаев разницы нет (проверено).
За 8 микросекунд — это довольно большой участок кода. Если нужно замерить нечно, состоящее из одной-двух инструкций, то лучше юзать rdtsc, ибо повторный прогон нужно выполнять, если результат отличиется от ожидаемго на немколько порядков — это элементарно.
Зачастую повторный вызо сделать нелегко — другая ситуация.
Пример — посылка сообщения окну. Первый вызов выпоняется очень долго, на несколько порядков дольше последующих. Таких примеров тыщи. А QPC тут ничего не покажет.
А на тех платформах, где нет rdtsc преимущество невелико — Perfomance counter аппаратно реализован в основном на x86. На других он эмулируется и точность имеет не лучше чем GetTickCount.
А было бы это не так, то тебе не пришлось бы имплементить классик — он был бы встроен ву фрэймворк.
Re[4]: Точное измерение производительности в дотнете
От:
Аноним
Дата:
19.07.04 15:24
Оценка:
это всё конечно хорошо, но вот если мне дают прогу без сорсов или просто не хочется ничего дополнительно в код писать, то что тогда ? Только Profiling API. Я вот просто мечтаю о проге типа prime.exe, которая меряла все тайминги в Java классах.
Re[5]: Точное измерение производительности в дотнете
Здравствуйте, Аноним, Вы писали:
А>это всё конечно хорошо, но вот если мне дают прогу без сорсов или просто не хочется ничего дополнительно в код писать, то что тогда ? Только Profiling API. Я вот просто мечтаю о проге типа prime.exe, которая меряла все тайминги в Java классах.
это форум "Исходники" — т.е. для тех у кого они есть или им хочется писать. А для твоего случая нужен, как ты правильно догадался — профайлер. В форуме "Инструменты разработки" он обсуждался дня 3 назад.
* thriving in a production environment *
Re[2]: Точное измерение производительности в дотнете
Здравствуйте, Plutonia Experiment, Вы писали:
PE>Для больших кусков кода лучше дополнитеьно вызывать GetThreadTimes. PE>А для маленьких — rdtsc буде в разы порядков точнее, ибо один вызов этого каунтера занимает в среднем 4000 тактов. Потому и точность у него малая — 8 микросекунд. PE>Нужно участок кода гонять обязательно несколько раз.
rdtsc идет в сад по другой причине — измерится общее прошедшее число тактов процессора. А нам нужно измерять время проведенное в _потоке_ (система может вообще его усыпить на 20 минут, и отработает он всего 33мс) вот и мы и должны получить 30-40мс, а не 20 минут
Здравствуйте, VladD2, Вы писали:
VD>Utils.PerfCounter — простенький классик для точного замера производительности в дотнете.
VD>Измеряет значительно точнее чем Environment.TickCount. Environment.TickCount имеет точность ~ 10 милисекунд, что для измерения коротких участков неприемлимо. Использование QueryPerformanceCounter позволяет обеспечить очень высокую точность.
QueryPerformaceCounter и QueryPerformanceFrequency — обе функции это "обертки" вокруг Nt(Zw)QueryPerformanceCounter. Почему не использовать именно её чтобы уменьшить overhead? А так получается обертка вокруг обертки...
Re[2]: Точное измерение производительности в дотнете
Здравствуйте, MShura, Вы писали:
MS>QueryPerformaceCounter и QueryPerformanceFrequency — обе функции это "обертки" вокруг Nt(Zw)QueryPerformanceCounter. Почему не использовать именно её чтобы уменьшить overhead? А так получается обертка вокруг обертки...
Думаю потому что это класс для .NET, а не обертка над API, да еще такими "нестандартными".
... << RSDN@Home 1.1.4 beta 2 rev. 157>>
Re[2]: Точное измерение производительности в дотнете
Здравствуйте, MShura, Вы писали:
MS>QueryPerformaceCounter и QueryPerformanceFrequency — обе функции это "обертки" вокруг Nt(Zw)QueryPerformanceCounter. Почему не использовать именно её чтобы уменьшить overhead? А так получается обертка вокруг обертки...
Птому-то это не документированные функции. И мотому что в этом нет нужды.
... << RSDN@Home 1.1.4 beta 2 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Public Structure PerfCounter
Private _start As Int64
Public Sub Start()
_start = 0
QueryPerformanceCounter(_start)
End Sub
Public Function Finish() As Double
Dim fin As Int64 = 0
QueryPerformanceCounter(fin)
Dim freq As Int64 = 0
QueryPerformanceFrequency(freq)
Return ((CType((Finish - _start), Single) / CType(freq, Single)))
End Function
Declare Function QueryPerformanceCounter Lib"Kernel32.dll" (ByVal performanceCount As Int64) As Boolean
Declare Function QueryPerformanceFrequency Lib"Kernel32.dll" (ByVal frequency As Int64) As Boolean
End Structure
Но он ругается:
System.NullReferenceException : Object reference not set to an instance of an object.
at PerfCounter.QueryPerformanceFrequency(Int64 frequency)
На таком коде:
Dim a As New PerfCounter
a.Start()
Console.WriteLine(a.Finish())
В чем может быть проблема?!
...Ei incumbit probatio, qui dicit, non qui negat...
Re[2]: Точное измерение производительности в дотнете
Public Structure PerfCounter
Private _start As Int64
Public Sub Start()
_start = 0
QueryPerformanceCounter(_start)
End Sub
Public Function Finish() As Single
Dim fin As Int64 = 0
QueryPerformanceCounter(fin)
Dim freq As Int64 = 0
QueryPerformanceFrequency(freq)
Return CType(fin - _start, Single) / CType(freq, Single)
End Function
Declare Function QueryPerformanceCounter Lib"Kernel32.dll" (ByRef performanceCount As Int64) As Boolean
Declare Function QueryPerformanceFrequency Lib"Kernel32.dll" (ByRef frequency As Int64) As Boolean
End Structure
...Ei incumbit probatio, qui dicit, non qui negat...