Pull to refresh

Comments 11

Спасибо за статью! Давно на C#, но как то упустил этот момент!

На счёт HPET'а есть несколько вопросов:

1) как в C# узнать, есть ли вообще этот HPET?
2) какой драйвер отвечает за HPET (в линуксе и в винде)? Можно ли настроить HPET в Линуксе+Mono? (Или если это продукция Microsoft, то типа нельзя...)
3*) что будет, если драйвер HPET'а будет настроен некорректно? в функциях класса Stopwatch есть try тела или же ждёт нас fotal error? (Больше всего интересно для Линукса...)
4) какова погрешность «замера» с учётом различных проверок в самом Stopwatch?
5) Верно ли я понимаю, что в многопроцессорных системах HPET не используется? Если используется, то как решаются «проблема гонок»? Или каждый проц имеет свой HPET?..
1) как в C# узнать, есть ли вообще этот HPET?

Выше можно взглянуть на код конструктора, там используется такая строка:

bool succeeded = SafeNativeMethods.QueryPerformanceFrequency(out Frequency); 
Хотя нет, так не выйдет. Действительно лучше использовать свойство IsHighResolution.
Попробую ответить на Ваши вопросы:

1) У Stopwatch есть статическое свойство IsHighResolution — которое как раз определяет используется ли HPET;
2) Вики говорит, что следующие операционные системы поддерживают и используют HPET:
Windows Vista, Windows 2008, Windows 7, Windows 8
Mac OS X (версия для x86)
Linux версий 2.6 (драйвер rtc-cmos вместо rtc)
FreeBSD;
3) Если он будет настроен некорректно, я думаю, что он просто не будет использоваться, функция QueryPerformanceFrequency вернет false, ни один метод класса Stopwatch не имеет блоков try/catch;
4) Сложный вопрос. В общем все методы класса очень просты, поэтому замеры должны быть весьма точные;
5) На многопроцессорных системах HPET есть у каждого процессора и как я понял они не синхронизированны… Проблемы возникали на первых многоядерных процессорах, но сейчас производители исправили их. Кстати у класса Stopwatch есть на этот случай проверка в методе Stop, я писал о ней.

Вот нашел документацию от Intel по HPET: www.intel.com/content/www/us/en/software-developers/software-developers-hpet-spec-1-0a.html?wapkw=hpet
По поводу пункта 2: раз уж Linux поддерживает HPET, то как бы под Mono сделать нормальные замеры времени? Есть какие-нибудь способы?
Взять и добавить поддержку в Mono. И сделать Pull request ;)
Использовать или не использовать класс Stopwatch решать Вам. Однако как мне кажется преимуществ у данного класса, все же больше чем недостатков.

А что тут решать — альтернатив-то нет. По крайней мере это самый точный способ замера времени, опирающийся на аппаратные особенности. И по крайней мере лично у меня ни разу не было случая, чтобы его разрешающей точности не хватило. Если с ElapsedMilliseconds это случалось, то вот с тиками все всегда точно считается.
Кстати, про точность — у Stopwatch метод ElapsedMilliseconds грубый, т.к. выдаёт long,
и часто удобнее пользоваться методом Elapsed.TotalMilliseconds (он выдаёт double).
Главное — не наступить на известные грабли, написав по-ошибке Elapsed.Milliseconds — это может прекрасно работать (по опыту выгребания багов — годами), но только до тех пор, пока измеряемый интервал не превысит 1 секунду :)
Лично мне работать со Stapwatch классом показалось не очень удобно. Написал простую оболочку StopwatchOperation задача которой быстро обвернуть код и вывести в трейс (как пример) результат замера.

Пример использования:
using (new StopwatchOperation("TODO: Ваше имя тут"))
{
	// Мой код тут
}


Код:
public class StopwatchOperation: IDisposable
	{
		public string Name { get; set; }
		protected Stopwatch InnerStopwatch { get; set; }

		public StopwatchOperation(string name)
		{
			this.InnerStopwatch = new Stopwatch();
			this.Name = name;

			this.InnerStopwatch.Start();
		}

		#region IDisposable Members

	public void Dispose()
	{
		Dispose(true);
		GC.SuppressFinalize(this);
	}


	protected virtual void Dispose(bool disposing)
	{
		if (disposing)
		{
			if(this.InnerStopwatch!=null)
			{
				this.InnerStopwatch.Stop();

				Trace.WriteLine(string.Format("Duration of '{0}' is {1} milliseconds.", this.Name, this.InnerStopwatch.ElapsedMilliseconds));

				this.InnerStopwatch = null;
			}
		}
	}
	#endregion
}
не знаю, мне Extension всегда хватало, тем более, что время можно вернуть, а не просто писать в Trace:
public static class StopwatchExtension
{
    public static TimeSpan GetElapsedTime(this Action action)
    {
        var sw = Stopwatch.StartNew();
        action();
        sw.Stop();
        return sw.Elapsed;
    }
}

используем
var elapsed = StopwatchExtension.GetElapsedTime(() =>
                                                {
                                                    for (int i = 0; i < 1000; i++)
                                                    {
                                                        Debug.WriteLine(i);
                                                    }
                                                });
Console.WriteLine(elapsed);

в случае Action можно вызыать и просто как Extension:
Action action = () =>
{
   for (int i = 0; i < 1000; i++)
   {
      Debug.WriteLine(i);
   }
};
var elapsed = action.GetElapsedTime();
Console.WriteLine(elapsed);
А сам автор сталкивался хоть раз с «ошибкой в BIOS/HAL»? :) Я боюсь, это какие-то совсем пограничные и не интересные случаи.

А касательно точности, тут всё ещё проще: если вы замеряете отработку «a + b», она пролетает за мгновения мысли. Соотв. измерения такой величины грешат больше, чем сама величина. Поэтому если речь о бенчмарках, там ставят циклы по тысяче/миллионам операций и измеряют уже в секундах. А раз секунды, тут хоть DateTime меряй.
Sign up to leave a comment.

Articles