Истекшее время с помощью Environment.TickCount () - как избежать переноса - PullRequest
1 голос
/ 16 апреля 2009

Защищает ли абсолютное значение следующий код от Environment.TickCount переноса?

If Math.Abs((Environment.TickCount And Int32.MaxValue) - StartTime) >= Interval Then
    StartTime = Environment.TickCount And Int32.MaxValue ' set up next interval
    ...
    ...
    ...
End If

Есть ли лучший способ защиты от Environment.TickCount обхода?

(Это .NET 1.1.)

Редактировать - Измененный код в соответствии с Microsoft Environment.TickCount help.

Ответы [ 8 ]

8 голосов
/ 29 ноября 2009

Избежать проблемы переноса легко, при условии, что промежуток времени, который вы хотите измерить, составляет не более 24,8 дня (что-либо больше не может быть представлено в целом числе со знаком). В C #:

int start = Environment.TickCount;
DoLongRunningOperation();
int elapsedTime = Environment.TickCount - start;

Я не очень знаком с VB.NET, но пока вы используете непроверенную математику, это будет работать, и вам не придется беспокоиться о переносе.

Например, если Environment.TickCount переносится с 2147483600 до -2147483596, это не имеет значения. Вы все еще можете вычислить разницу между ними, которая составляет 100 миллисекунд.

2 голосов
/ 17 августа 2015

Поскольку я изучал это и наткнулся на эту страницу, я поделюсь небольшим тестом, который я сделал.

При запуске кода игнорируйте первые 1000 результатов каждого теста (используется для прогрева). Также игнорируйте число, напечатанное после «ignore:». Это просто для того, чтобы гарантировать отсутствие оптимизации компилятора, которая могла бы повлиять на результат.

Результаты на моем компьютере:

Test1 1000000: 7ms    - Environment.TickCount
Test2 1000000: 1392ms - DateTime.Now.Ticks
Test3 1000000: 933ms  - Stopwatch.ElapsedMilliseconds

Оба DateTime.Now.Ticks (тест 2) и Секундомер.ElapsedMilliseconds (тест 3) значительно медленнее, чем Environment.TickCount (тест 1) , но недостаточно, чтобы быть заметным, если вы не выполняете много вычислений. Например, я занимался этим, потому что мне нужен дешевый способ провести время в тесных игровых циклах.

Я думаю, что мое решение должно быть примерно таким (непроверенным):

var elapsed = Environment.TickCount - start;
if (elapsed < 0)
   elapsed = Int32.MaxValue - start + Environment.TickCount;

Код эталона:

void Main()
{
    Test1(1000);
    Test1(1000000);
    Test2(1000);
    Test2(1000000);
    Test3(1000);
    Test3(1000000);
}

void Test1(int c) {
    var sw = new Stopwatch();
    sw.Start();
    long sum = 0;
    for (var i = 0; i < c; i++)
    {
        sum += Environment.TickCount;
    }
    sw.Stop();
    Console.WriteLine("Test1 " + c + ": " + sw.ElapsedMilliseconds + "ms   (ignore: " + sum + ")");}

void Test2(int c) {
    var sw = new Stopwatch();
    sw.Start();
    long sum = 0;
    for (var i = 0; i < c; i++)
    {
        sum += DateTime.Now.Ticks;
    }
    sw.Stop();
    Console.WriteLine("Test2 " + c + ": " + sw.ElapsedMilliseconds + "ms   (ignore: " + sum + ")");
}
void Test3(int c) {
    var sw = new Stopwatch();
    sw.Start();
    long sum = 0;
    var stopwatch = new Stopwatch();
    stopwatch.Start();
    for (var i = 0; i < c; i++)
    {
        sum += stopwatch.ElapsedMilliseconds;
    }
    sw.Stop();
    Console.WriteLine("Test3 " + c + ": " + sw.ElapsedMilliseconds + "ms   (ignore: " + sum + ")");
}
2 голосов
/ 29 ноября 2009

Просто используйте секундомер :

Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
Thread.Sleep(10000);
stopWatch.Stop();
// Get the elapsed time as a TimeSpan value.
TimeSpan ts = stopWatch.Elapsed;

Возвращает int64, а не int32. Плюс это легче понять.

1 голос
/ 16 апреля 2009

Я не уверен, чего вы пытаетесь достичь. Похоже, вы пытаетесь определить, произошел ли определенный интервал и, если это так, выполнить какую-то определенную логику.

Это может причинить вам боль, если вы будете использовать Environment.TickCount для этой цели. Как вы указали, это значение можно и будет переносить примерно каждые 25 дней. Нет никакого способа предотвратить это, и если вы попытаетесь это сделать, вы получите ошибки в своем коде.

Вместо этого, почему бы не сделать следующее:

  • Использовать фактический таймер для внутреннего и выполнения события
  • Сохраните StartTime как значение DateTime (или просто Date в VB). Это значение имеет гораздо больший диапазон. Маловероятно, что ваше приложение будет работать достаточно долго, чтобы это значение можно было обернуть (ОС потребуется перезагрузка задолго до этого :)).
0 голосов
/ 03 мая 2017

Секундомер является наиболее подходящим выбором для большинства целей. Environment.TickCount имеет смысл только для редких целей, где:

  1. Необходимое время для измерения времени само по себе критично, и вам нужна лучшая производительность, потому что вы должны измерять несколько миллионов или миллиард раз. Здесь у Tickcount есть свои преимущества, потому что он может быть ок. В 20-25 раз быстрее, чем Секундомер () (измерено на моей машине i7).

  2. Но вам не нужно измерять с точностью более 1 миллисекунды

  3. Вы уверены, что вам не нужно считать более 24 дней, потому что это макс. (ролловер) время 32-битного int для TickCount.

Обычно это будет не случай, и, по крайней мере, пункт 1. не имеет значения. Секундомер (который обычно измеряет, по крайней мере, примерно в 1000 раз более точно (микросекундный диапазон и лучше)), как правило, является наиболее подходящим выбором.

Помимо .ElapsedMilliseconds вы также можете использовать статический метод
Stopwatch.GetTimestamp();

И вот почему: для вызова секундомера 10 миллионов раз в цикле на моем старом i7 требуется 1,3 секунды. GetTickCount64 немного быстрее, чуть более 1 секунды для того же. Так что выбирайте один, и вам не придется бороться с 32-битными вещами.

Конечно, данные характеристики являются приблизительными на одном (моем) оборудовании и могут отличаться.

Но Microsoft заявляет, что делает все, чтобы секундомер (счетчики производительности) был лучшим на всех системах. Маловероятно, что кто-то легко победит:

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_

Котировки:

"На точность QPC влияют изменения частоты процессора, вызванные управлением питанием или технологией Turbo Boost? Нет. Если процессор имеет инвариантный TSC, то изменения QPC не затрагиваются. [...] "

"Надежно ли работает QPC на многопроцессорных системах, многоядерных системах и системах с гиперпоточностью? Да

Как определить и проверить, работает ли QPC на моей машине? Вам не нужно выполнять такие проверки. [...] «

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

0 голосов
/ 24 октября 2016

Я предполагаю, что StartTime является целым положительным знаком; Я предполагаю, что вы ранее сделали StartTime = (Environment.TickCount And Int32.MaxValue).

Abs() не не решает проблему. Если интервал <12,43 дней, код будет корректно рассчитываться, пока StartTime невелик. Но иногда, когда StartTime> (24,86 дней - интервал), тест проходит раньше, чем должен, в тот момент, когда (TickCount And Int32.MaxValue) переходит на 0. Кроме того, если Interval> 12,43 дней AND (TickCount) И Int32.MaxValue) близко к 12,43 дням, тест никогда не пройдет.

Делать Environment.TickCount And Int32.MaxValue не нужно и не полезно. Он создает целое число с положительной подписью, представляющее время в мс, которое оборачивается до 0 каждые 24,86 дня. Альтернативы труднее понять, но они позволяют вам без затруднений использовать временные интервалы до 49,71 дня. Необработанный Environment.TickCount оборачивается в Int32.MinValue через 24,86 дня и каждые 49,71 дня после этого. Приведение unchecked((Uint32)Environment.TickCount) создает [положительное] целое число без знака32, представляющее время в мс, которое оборачивается до 0 каждые 49,71 дней. (Потому что Environment.TickCount вызывает функцию Windows GetTickCount (), которая возвращает DWORD (UInt32) и не проверяет, приводит ли его к Int32.)

Свертывание Environment.TickCount не ухудшает измерение прошедшего времени. Непроверенное вычитание всегда дает правильный результат.

Вы можете вычислить прошедшее время как int32 со знаком (от -24,86 до +24,86 дней), что более полезно, если вы сравниваете независимое время или потенциально смешиваете будущее и прошлое; или без знака int32 (от 0 до +49,71 дней). Вы можете захватить T0 как со знаком int32 или без знака int32; это не имеет никакого значения для результатов. Вам нужно только [unchecked] приведение, если T0 или прошедшее время не подписаны; с подписанным T0 и прошедшим подписанным временем, вам не нужно приводить.

Захват T0 с подписью: (C #)

...
int iT0 = Environment.TickCount;  // T0 is NOW.
...
iElapsed = unchecked(Environment.TickCount - iT0)  // -24.81 to +24.81 days
uiElapsed = unchecked((uint)Environment.TickCount - (uint)iT0)  // 0 to +49.71 days
if (uiElapsed >= uiTimeLimit)  // IF time expired,
{
    iT0 = Environment.TickCount;  // For the next interval, new T0 is NOW.
    ...
}

Захват T0 без знака (мои предпочтения; Environment.TickCount никогда не должен был быть подписан):

...
uint uiT0 = unchecked((uint)Environment.TickCount);  // T0 is NOW.
...
iElapsed = unchecked(Environment.TickCount - (int)uiT0)  // -24.81 to +24.81 days
uiElapsed = unchecked((uint)Environment.TickCount - uiT0)  // 0 to +49.71 days
if (uiElapsed >= uiTimeLimit)  // IF time expired,
{
    uiT0 = unchecked((uint)Environment.TickCount);  // For the next interval, new T0 is NOW.
    ...
}

Если ваш TimeLimit близок к предельному периоду (24,81 или 49,71 дня), время может истечь без прохождения теста. Вы должны проверить хотя бы один раз, пока Elapsed> = TimeLimit. (Если вы не уверены в том, что тестирование проводится достаточно часто, вы можете добавить резервное тестирование для Elapsed. Если Elapsed когда-либо уменьшится, он будет перенесен, поэтому время должно истечь.)

=====

Для интервалов времени, превышающих 49,71 дней, вы можете подсчитать, сколько раз uiElapsed обертывают вокруг, или вы можете подсчитать, сколько раз Environment.TickCount оборачивается вокруг. Вы можете составить счетчики в 64-битное значение, эмулирующее GetTickCount64() (доступно только в Windows Vista и новее). 64-разрядное значение имеет полный диапазон (292 миллиона лет) и полное разрешение (1 мс). Или вы можете сделать 32-битное значение с уменьшенным диапазоном и / или разрешением. Код, проверяющий упаковку, должен выполняться не реже одного раза в 49,71 дня, чтобы гарантировать, что упаковка не обнаружена.

uint uiTickCountPrev = 0;
uint uiTickWrap = 0;
Int64 TickCount64;
...
uiTickCount = unchecked((uint)Environment.TickCount)  // 0 to +49.71 days
if (uiTickCount < uiTickCountPrev)  // IF uiElapsed decreased,
    uiWrapcount++;  count that uiElapsed wrapped.
uiElapsedPrev = uiElapsed;  // Save the previous value.
TickCount64 = (Int64)uiTickWrap << 32 + (Int64)uiTickCount;

Примечания:

Environment.TickCount указывает время с момента загрузки в мс с разрешением 10-16 мс.

Не проверенная разница Int32 дает разницу во времени, от -24,81 до +24,81 дней, с циклическим изменением. Непроверенное вычитание 32-разрядных целых чисел переполняется и переносится к правильному значению. Знак Environment.TickCount никогда не имеет значения. Пример, обтекание по одному: iBase = Environment.TickCount получает Int32.MaxValue; через один тик Environment.TickCount переносится на Int32.MinValue; и unchecked(Environment.TickCount - iBase) дает 1.

Непроверенная разница UInt32 дает разницу во времени, от 0 до +49,71 дня, с циклическим переходом. (Истекшее время никогда не может быть отрицательным, так что это лучший выбор.) Непроверенное вычитание 32-разрядных целых чисел без знака переполняется и возвращается к правильному значению. Например, обтекание по одному: iBase = Environment.TickCount получает UInt32.MaxValue; через один тик Environment.TickCount переносится на UInt32.MinValue; и unchecked(Environment.TickCount - iBase) дает 1.

0 голосов
/ 16 апреля 2009

Ваш код, скорее всего, не будет работать. Если приложение должно было работать на Vista или более новой версии Windows, можно было бы использовать GetTickCount64 , но я предполагаю, что это не так, учитывая, что вы используете .NET 1.1.

Ваш код не должен проверять равенство с интервалом, так как это, вероятно, пойдет не так. Используйте Int64 для хранения тиков, сохраните последний обработанный тик в переменной, и если эта переменная больше, чем новое значение тика, у вас есть перенос и вам нужно увеличить общую базу тиков (Int64) - при условии, что вы проверяйте галочку чаще, чем 1 раз в 12 дней ...;)

0 голосов
/ 16 апреля 2009

В зависимости от вашей точности, почему бы вам просто не использовать:

DateTime.Now.Ticks

Там нет упаковки для этого клещей.

...