6

我发现 System.Diagnostics.Stopwatch 类似乎具有可测量的不准确性,即使在很短的时间段内(即 20 秒)也是如此。我的进程显示编码为运行 20 秒的进程的经过时间为 20.3+ 秒:

$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

在 PowerShell 中使用 Stopwatch 类时会出现这种精度漂移吗?它似乎与时间跨度不成比例地增加(即 10 秒减少 0.1 秒,20 秒减少 0.3 秒)。我的代码有问题吗?

4

4 回答 4

19

你有 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())"
于 2013-02-03T03:16:18.783 回答
2

问题是,你想做什么?如果您试图确定脚本运行需要多长时间,那么您应该使用秒表。但是,如果您尝试在指定时间段后启动新的命令序列,请使用 [System.Timers.Timer] 并在计时器对象上为经过的事件注册一个事件。然后将注册事件的操作指定为您要完成的操作。

于 2013-11-09T04:35:07.373 回答
1

秒表非常准确。睡眠不是。睡眠 1 秒通常会产生 0.985 秒到 1.015 秒的睡眠;但是,如果有其他活动正在进行,时间可能会更长。

于 2020-03-17T06:52:24.950 回答
0

代码运行的总时间取决于其他因素,例如系统的繁忙程度。

查看 .NET 的源代码,我们可以看到[System.Diagnostics.Stopwatch]::StartNew()使用了高分辨率时间计数器。它调用 WINAPI QueryPerformanceCounter。那就是:课程非常精确!

我查看了 Start-Sleep 的最新源代码,并注意到它使用内核模式事件来实现时间等待。除了用户已经提到的细节之外,Windows schedule 可以在 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、桌面、windows 10、CPU intel 3.0 Ghz 上运行。

首先,我以正常优先级、低并发运行您的原始代码:脚本耗时 20.14 秒

接下来,我运行原始代码,以及其他一些 CPU 密集型进程:耗时 24.21s

回来,我运行我的代码版本,在空闲系统下没有 write-host:脚本只用了 20.03s

最后一个与第一个有很大的不同(空闲并写入屏幕)。

于 2020-02-13T02:17:27.253 回答