在给定位置(重新)开始时如何正确计算 ProgressBar 中的 ETA?
How to properly calculate ETA in ProgressBar when (re)starting at a given position?
我正在为我的控制台命令使用 ProgressBar
,并允许命令在处理过程中的某个时间点重新启动:
$itemCount = 1_000_000;
$startItem = 150_000;
$progressBar = new ProgressBar($output, $itemCount);
$progressBar->setFormat(
$progressBar->getFormatDefinition(ProgressBar::FORMAT_DEBUG)
);
$progressBar->start();
if ($startItem !== 0) {
// unfortunately taken into account in ETA calculation
$progressBar->advance($startItem);
}
for ($i = $startItem; $i < $itemCount; $i++) {
usleep(50_000);
$progressBar->advance();
}
问题是,即使该命令每秒仅处理约 20 个项目,计算 ETA 时就好像 $startItem
个项目在处理开始时就已处理:
150038/1000000 [====>-----------------------] 15% 2 secs/13 secs 20.0 MiB
我预计预计到达时间约为 12 小时,而不是 13 秒。
我该如何解决这个问题?我能以某种方式在开始之前给出进度条的起始位置吗?计算正确吗?
ProgressBar
助手不支持此功能。
预估时间是通过ProgressBar#getEstimated()
方法计算出来的,很简单:
public function getEstimated(): float
{
if (!$this->step) {
return 0;
}
return round((time() - $this->startTime) / $this->step * $this->max);
}
这只考虑了自进度条开始以来的时间量(这是在调用 ProgressBar#start()
时设置的,否则在构造函数中设置),以及它的“步数”总数到目前为止。
“真”步和“假”步之间没有区别。即使在调用 start()
之前修改 ProgresBar#step
(例如在构造函数中),结果也是一样的,因为估计时间的计算方式完全相同。
class 被标记为 final
,我认为这对于助手 class 来说是不幸的,因此您不能简单地扩展它并添加您的逻辑(例如,使用额外的 int $resumedAt
属性 可以在计算估计剩余时间时使用)。
如果您真的非常需要这个,我只需复制您项目中的 class 并在其中添加必要的逻辑。
作为一个简单的概念证明,我将 ProgressBar
复制到 App\ProgressBar
,并添加了:
private int $resumedSteps = 0;
public function resume(int $max = null, int $step = 0): void
{
$this->startTime = time();
$this->resumedStep = $step;
$this->step = $step;
if (null !== $max) {
$this->setMaxSteps($max);
}
$this->setProgress($step);
$this->display();
}
public function getEstimated(): float
{
if ($this->step === 0 || $this->step === $this->resumedStep) {
return 0;
}
return round((time() - $this->startTime) / ($this->step - $this->resumedStep) * $this->max);
}
public function getRemaining(): float
{
if ($this->step === 0 || $this->step === $this->resumedStep) {
return 0;
}
return round((time() - $this->startTime) / ($this->step - $this->resumedStep) * ($this->max - $this->step));
}
人们可以简单地将其用作:
$itemCount = 1_000_000;
$progressBar = new App\ProgressBar($output, $itemCount);
$progressBar->setFormat(
$progressBar->getFormatDefinition(ProgressBar::FORMAT_DEBUG)
);
$startItem > 0 ? $progressBar->resume($startItem) : $progressBar->start();
我正在为我的控制台命令使用 ProgressBar
,并允许命令在处理过程中的某个时间点重新启动:
$itemCount = 1_000_000;
$startItem = 150_000;
$progressBar = new ProgressBar($output, $itemCount);
$progressBar->setFormat(
$progressBar->getFormatDefinition(ProgressBar::FORMAT_DEBUG)
);
$progressBar->start();
if ($startItem !== 0) {
// unfortunately taken into account in ETA calculation
$progressBar->advance($startItem);
}
for ($i = $startItem; $i < $itemCount; $i++) {
usleep(50_000);
$progressBar->advance();
}
问题是,即使该命令每秒仅处理约 20 个项目,计算 ETA 时就好像 $startItem
个项目在处理开始时就已处理:
150038/1000000 [====>-----------------------] 15% 2 secs/13 secs 20.0 MiB
我预计预计到达时间约为 12 小时,而不是 13 秒。
我该如何解决这个问题?我能以某种方式在开始之前给出进度条的起始位置吗?计算正确吗?
ProgressBar
助手不支持此功能。
预估时间是通过ProgressBar#getEstimated()
方法计算出来的,很简单:
public function getEstimated(): float
{
if (!$this->step) {
return 0;
}
return round((time() - $this->startTime) / $this->step * $this->max);
}
这只考虑了自进度条开始以来的时间量(这是在调用 ProgressBar#start()
时设置的,否则在构造函数中设置),以及它的“步数”总数到目前为止。
“真”步和“假”步之间没有区别。即使在调用 start()
之前修改 ProgresBar#step
(例如在构造函数中),结果也是一样的,因为估计时间的计算方式完全相同。
class 被标记为 final
,我认为这对于助手 class 来说是不幸的,因此您不能简单地扩展它并添加您的逻辑(例如,使用额外的 int $resumedAt
属性 可以在计算估计剩余时间时使用)。
如果您真的非常需要这个,我只需复制您项目中的 class 并在其中添加必要的逻辑。
作为一个简单的概念证明,我将 ProgressBar
复制到 App\ProgressBar
,并添加了:
private int $resumedSteps = 0;
public function resume(int $max = null, int $step = 0): void
{
$this->startTime = time();
$this->resumedStep = $step;
$this->step = $step;
if (null !== $max) {
$this->setMaxSteps($max);
}
$this->setProgress($step);
$this->display();
}
public function getEstimated(): float
{
if ($this->step === 0 || $this->step === $this->resumedStep) {
return 0;
}
return round((time() - $this->startTime) / ($this->step - $this->resumedStep) * $this->max);
}
public function getRemaining(): float
{
if ($this->step === 0 || $this->step === $this->resumedStep) {
return 0;
}
return round((time() - $this->startTime) / ($this->step - $this->resumedStep) * ($this->max - $this->step));
}
人们可以简单地将其用作:
$itemCount = 1_000_000;
$progressBar = new App\ProgressBar($output, $itemCount);
$progressBar->setFormat(
$progressBar->getFormatDefinition(ProgressBar::FORMAT_DEBUG)
);
$startItem > 0 ? $progressBar->resume($startItem) : $progressBar->start();