Вот обновленное и обновленное резюме наиболее полезных ответов и комментариев в этой теме + дополнительные тесты и варианты:
Перво-наперво: как уже отмечали другие в комментариях, за последние годы все изменилось, и с «современными» Windows (Win XP ++) и .NET, а также с современным оборудованием, нет или мало причин не использовать секундомер ().
Подробнее см. MSDN . Котировки:
"На точность QPC влияют изменения частоты процессора, вызванные управлением питанием или технологией Turbo Boost?
Нет. Если процессор имеет инвариант TSC , изменения QPC не будут затронуты подобными изменениями. Если процессор не имеет инвариантного TSC, QPC вернется к аппаратному таймеру платформы, на который не повлияют изменения частоты процессора или технология Turbo Boost.
Надежно ли работает QPC на многопроцессорных системах, многоядерных системах и системах с гиперпоточностью?
Да
Как определить и проверить, работает ли QPC на моей машине?
Вам не нужно выполнять такие проверки.
Какие процессоры имеют неинвариантные TSC?
[..Читай дальше ..]
«
Но если вам не нужна точность Stopwatch () или вы хотя бы хотите точно знать о производительности Stopwatch (статические или основанные на экземплярах) и других возможных вариантах, продолжайте читать:
Я взял вышеприведенный тест cskwg и расширил код для большего количества вариантов. Я измерял с i7 4700 MQ несколько лет назад и C # 7 с VS 2017 (точнее, скомпилирован с .NET 4.5.2, несмотря на двоичные литералы, это C # 6 (используется это: строковые литералы и 'использование статического '). Особенно похоже, что производительность Stopwatch () улучшена по сравнению с упомянутым тестом.
Это пример результатов 10 миллионов повторений в цикле, как всегда, абсолютные значения не важны, но даже относительные значения могут отличаться на другом оборудовании:
32 бита, режим разблокировки без оптимизации:
Измерено: GetTickCount64 () [мс]: 275
Измерено: Environment.TickCount [мс]: 45
Измерено: DateTime.UtcNow.Ticks [мс]: 167
Измерено: Секундомер: .ElapsedTicks [мс]: 277
Измерено: секундомер: .ElapsedMilliseconds [мс]: 548
Измерено: статический секундомер. GetTimestamp [мс]: 193
Измерено: Секундомер + преобразование в DateTime [мс]: 551
Сравните это с DateTime.Now.Ticks [мс]: 9010
32 бита, режим разблокировки, оптимизирован:
Измерено: GetTickCount64 () [мс]: 198
Измерено: Environment.TickCount [мс]: 39
Измерено: DateTime.UtcNow.Ticks [мс]: 66 (!)
Измерено: секундомер: .ElapsedTicks [мс]: 175
Измерено: Секундомер: .ElapsedMilliseconds [мс]: 491
Измерено: статический секундомер. GetTimestamp [мс]: 175
Измерено: Секундомер + преобразование в DateTime [мс]: 510
Сравните это с DateTime.Now.Ticks [мс]: 8460
64 бит, режим разблокировки без оптимизации:
Измерено: GetTickCount64 () [мс]: 205
Измерено: Environment.TickCount [мс]: 39
Измерено: DateTime.UtcNow.Ticks [мс]: 127
Измерено: Секундомер: .ElapsedTicks [мс]: 209
Измерено: Секундомер: .ElapsedMilliseconds [мс]: 285
Измерено: статический секундомер. GetTimestamp [мс]: 187
Измерено: Секундомер + преобразование в DateTime [мс]: 319
Сравните это с DateTime.Now.Ticks [мс]: 3040
64 бит, режим разблокировки, оптимизирован:
Измерено: GetTickCount64 () [мс]: 148
Измерено: Environment.TickCount [мс]: 31 (стоит ли оно того?)
Измерено: DateTime.UtcNow.Ticks [мс]: 76 (!)
Измерено: Секундомер: .ElapsedTicks [мс]: 178
Измерено: секундомер: .ElapsedMilliseconds [мс]: 226
Измерено: статический секундомер. GetTimestamp [мс]: 175
Измерено: Секундомер + преобразование в DateTime [мс]: 246
Сравните это с DateTime.Now.Ticks [мс]: 3020
Может быть очень интересно, что создание значения DateTime для распечатки времени секундомера, кажется, почти не требует затрат . Более академическим, чем практическим, интересным является то, что статический секундомер немного быстрее (как и ожидалось). Некоторые точки оптимизации довольно интересны.
Например, я не могу объяснить, почему Stopwatch.ElapsedMilliseconds только с 32-битным является настолько медленным по сравнению с его другими вариантами, например, статическим. Это и DateTime.Now более чем удвоить свою скорость с 64 бит.
Вы видите: только для миллионов казней время секундомера начинает иметь значение. Если это действительно так (но остерегайтесь микрооптимизации слишком рано), может быть интересно, что с GetTickCount64 (), но особенно с DateTime.UtcNow , у вас есть 64-битный (длинный) таймер с меньшим количеством точность, чем у секундомера, но быстрее, так что вам не придется возиться с 32-битным «некрасивым» Environment.TickCount.
Как и ожидалось, DateTime.Now является самым медленным из всех.
Если вы запустите его, код получит также вашу текущую точность секундомера и многое другое.
Вот полный код теста:
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using static System.Environment;
[...]
[DllImport("kernel32.dll") ]
public static extern UInt64 GetTickCount64(); // Retrieves a 64bit value containing ticks since system start
static void Main(string[] args)
{
const int max = 10_000_000;
const int n = 3;
Stopwatch sw;
// Following Process&Thread lines according to tips by Thomas Maierhofer: https://codeproject.com/KB/testing/stopwatch-measure-precise.aspx
// But this somewhat contradicts to assertions by MS in: https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396#Does_QPC_reliably_work_on_multi-processor_systems__multi-core_system__and_________systems_with_hyper-threading
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1); // Use only the first core
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
Thread.CurrentThread.Priority = ThreadPriority.Highest;
Thread.Sleep(2); // warmup
Console.WriteLine($"Repeating measurement {n} times in loop of {max:N0}:{NewLine}");
for (int j = 0; j < n; j++)
{
sw = new Stopwatch();
sw.Start();
for (int i = 0; i < max; i++)
{
var tickCount = GetTickCount64();
}
sw.Stop();
Console.WriteLine($"Measured: GetTickCount64() [ms]: {sw.ElapsedMilliseconds}");
//
//
sw = new Stopwatch();
sw.Start();
for (int i = 0; i < max; i++)
{
var tickCount = Environment.TickCount; // only int capacity, enough for a bit more than 24 days
}
sw.Stop();
Console.WriteLine($"Measured: Environment.TickCount [ms]: {sw.ElapsedMilliseconds}");
//
//
sw = new Stopwatch();
sw.Start();
for (int i = 0; i < max; i++)
{
var a = DateTime.UtcNow.Ticks;
}
sw.Stop();
Console.WriteLine($"Measured: DateTime.UtcNow.Ticks [ms]: {sw.ElapsedMilliseconds}");
//
//
sw = new Stopwatch();
sw.Start();
for (int i = 0; i < max; i++)
{
var a = sw.ElapsedMilliseconds;
}
sw.Stop();
Console.WriteLine($"Measured: Stopwatch: .ElapsedMilliseconds [ms]: {sw.ElapsedMilliseconds}");
//
//
sw = new Stopwatch();
sw.Start();
for (int i = 0; i < max; i++)
{
var a = Stopwatch.GetTimestamp();
}
sw.Stop();
Console.WriteLine($"Measured: static Stopwatch.GetTimestamp [ms]: {sw.ElapsedMilliseconds}");
//
//
DateTime dt=DateTime.MinValue; // just init
sw = new Stopwatch();
sw.Start();
for (int i = 0; i < max; i++)
{
var a = new DateTime(sw.Elapsed.Ticks); // using variable dt here seems to make nearly no difference
}
sw.Stop();
//Console.WriteLine($"Measured: Stopwatch+conversion to DateTime [s] with millisecs: {dt:s.fff}");
Console.WriteLine($"Measured: Stopwatch+conversion to DateTime [ms]: {sw.ElapsedMilliseconds}");
Console.WriteLine();
}
//
//
sw = new Stopwatch();
var tickCounterStart = Environment.TickCount;
sw.Start();
for (int i = 0; i < max/10; i++)
{
var a = DateTime.Now.Ticks;
}
sw.Stop();
var tickCounter = Environment.TickCount - tickCounterStart;
Console.WriteLine($"Compare that with DateTime.Now.Ticks [ms]: {sw.ElapsedMilliseconds*10}");
Console.WriteLine($"{NewLine}General Stopwatch information:");
if (Stopwatch.IsHighResolution)
Console.WriteLine("- Using high-resolution performance counter for Stopwatch class.");
else
Console.WriteLine("- Using high-resolution performance counter for Stopwatch class.");
double freq = (double)Stopwatch.Frequency;
double ticksPerMicroSec = freq / (1000d*1000d) ; // microsecond resolution: 1 million ticks per sec
Console.WriteLine($"- Stopwatch accuracy- ticks per microsecond (1000 ms): {ticksPerMicroSec:N1}");
Console.WriteLine(" (Max. tick resolution normally is 100 nanoseconds, this is 10 ticks/microsecond.)");
DateTime maxTimeForTickCountInteger= new DateTime(Int32.MaxValue*10_000L); // tickCount means millisec -> there are 10.000 milliseconds in 100 nanoseconds, which is the tick resolution in .NET, e.g. used for TimeSpan
Console.WriteLine($"- Approximated capacity (maxtime) of TickCount [dd:hh:mm:ss] {maxTimeForTickCountInteger:dd:HH:mm:ss}");
// this conversion from seems not really accurate, it will be between 24-25 days.
Console.WriteLine($"{NewLine}Done.");
while (Console.KeyAvailable)
Console.ReadKey(false);
Console.ReadKey();
}