Точность таймера/секундомера PowerShell

Я обнаружил, что класс System.Diagnostics.Stopwatch имеет то, что кажется измеримой неточностью даже в течение короткого периода времени (например, 20 секунд). Мой процесс показывает прошедшее время более 20,3 секунд для процесса, закодированного на 20 секунд:

$elapsed = [System.Diagnostics.Stopwatch]::StartNew()
write-host "Started at $(get-date)"

for ($t=1; $t -le 20; $t++) {
    Write-Host "Elapsed Time: $($elapsed.Elapsed.ToString())"
    sleep 1
}

write-host "Ended at $(get-date)"
write-host "Total Elapsed Time: $($elapsed.Elapsed.ToString())"

    Started at 02/02/2013 15:08:43
    Elapsed Time: 00:00:00.0329924
    Elapsed Time: 00:00:01.0417435
    Elapsed Time: 00:00:02.0547547
    Elapsed Time: 00:00:03.0689716
    Elapsed Time: 00:00:04.0820497
    Elapsed Time: 00:00:05.0963413
    Elapsed Time: 00:00:06.1113915
    Elapsed Time: 00:00:07.1244044
    Elapsed Time: 00:00:08.1396105
    Elapsed Time: 00:00:09.1528952
    Elapsed Time: 00:00:10.1659905
    Elapsed Time: 00:00:11.1800884
    Elapsed Time: 00:00:12.1940009
    Elapsed Time: 00:00:13.2081824
    Elapsed Time: 00:00:14.2223585
    Elapsed Time: 00:00:15.2375023
    Elapsed Time: 00:00:16.2506360
    Elapsed Time: 00:00:17.2656845
    Elapsed Time: 00:00:18.2790676
    Elapsed Time: 00:00:19.2928700
    Ended at 02/02/2013 15:09:04
    Total Elapsed Time: 00:00:20.3080067

Следует ли ожидать такого отклонения точности при использовании класса Stopwatch в PowerShell? Кажется, что оно увеличивается непропорционально промежутку времени (т. е. 10 секунд отличается на 0,1, а 20 — на 0,3). Мой код виноват?


person Local Needs    schedule 03.02.2013    source источник


Ответы (4)


Вопрос в том, что вы пытаетесь сделать? Если вы пытаетесь точно определить, сколько времени требуется для запуска сценария, вам следует использовать секундомер. Однако, если вы пытаетесь запустить новую последовательность команд после указанного периода, используйте [System.Timers.Timer] и зарегистрируйте событие для истекшего события в объекте таймера. Затем укажите действие зарегистрированного события, которое вы пытаетесь выполнить.

person Shaun    schedule 09.11.2013

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

Это по-прежнему имеет 20-секундную паузу, и прошедшее время ближе к 20 секундам, потому что PowerShell должен делать меньше дополнительных действий:

$elapsed = [System.Diagnostics.Stopwatch]::StartNew()
write-host "Started at $(get-date)"
sleep 20
write-host "Ended at $(get-date)"
write-host "Total Elapsed Time: $($elapsed.Elapsed.ToString())"
person Rynant    schedule 03.02.2013
comment
Избавьтесь от вызовов Started и Ended Write-Host, и он станет еще ближе. Секундомер использует API-интерфейс высокочастотного счетчика Windows, поэтому он довольно точен. - person Keith Hill; 03.02.2013
comment
Райнант, это будет 20 секунд. Но это уже не будет секундомер — просто таймер сна. Это остановит запуск моего скрипта до тех пор, пока сон не будет завершен. Мне это нужно для запуска процесса и, в конце, скажите, сколько времени потребовалось для его завершения. - person Local Needs; 08.02.2013
comment
Я думаю, вы упустили суть моего примера. Я пытался показать, что класс Diagnostics.Stopwatch не является неточным. Причина, по которой секундомер в вашем скрипте показывает 20,3080067 секунд, заключается в том, что ваш скрипт помимо сна делает и другие вещи. Кроме того, причина, по которой операторы Get-Date кажутся разнесенными ровно на 20 секунд, заключается в том, что отображаемое время округляется до ближайшей секунды. - person Rynant; 08.02.2013
comment
Попался. Итак, если выполнение моих дополнительных команд отключает секундомер, как можно использовать секундомер для точного времени процесса? Или секундомер не подходит для чего-то подобного? Мой фактический код может занять несколько часов, и я хотел бы получить более точное прошедшее время, чем это. - person Local Needs; 12.02.2013
comment
Запуск дополнительных команд не отключает секундомер. Выполнение дополнительных команд занимает больше времени; следовательно, секундомер показывает правильное время. Используйте секундомер, это точно. - person Rynant; 12.02.2013

Секундомер отличается высокой точностью. Сна нет. Сон в течение 1 секунды обычно дает сон от 0,985 до 1,015 с; однако это может быть значительно дольше, если проводятся другие мероприятия.

person David Berg    schedule 17.03.2020

Общее время работы вашего кода зависит от других факторов, например, от того, насколько загружена ваша система.

Глядя на исходный код .NET, мы видим, что [System.Diagnostics.Stopwatch]::StartNew() использует счетчик времени с высоким разрешением. Он вызывает WINAPI QueryPerformanceCounter< /а>. То есть: класс такой очень точный!

Я смотрю на последний исходный код Start-Sleep и отметил, что он использует события режима ядра для реализации времени ожидания. В дополнение к деталям, уже упомянутым пользователями, расписание Windows может запускать другие потоки, когда powershel завершает ожидание времени ожидания, что приводит к увеличению времени до того, как ваш код вычислит прошедшее время.

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

$elapsed = [System.Diagnostics.Stopwatch]::StartNew()
write-host "Started at $(get-date)"

$out = new-object 'string[]' 100;

for ($t=1; $t -le 20; $t++) {
    $out[$t] = "Elapsed Time: $($elapsed.Elapsed.ToString())"
    sleep 1
}
$out;
write-host "Ended at $(get-date)"
write-host "Total Elapsed Time: $($elapsed.Elapsed.ToString())"

Посмотрите на запуски в системе, powershell 5.1, Desktop, Windows 10, CPU Intel 3.0 Ghz.

Во-первых, я запустил ваш исходный код с обычным приоритетом и низким параллелизмом: Сценарий занял 20,14 с

Затем я запустил исходный код с другим ресурсоемким процессом:заняло 24,21 с.

Вернувшись, я запустил свою версию кода без записи на хост в бездействующей системе:Скрипт занял всего 20,03 с.

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

person Rodrigo    schedule 13.02.2020