计算剩余时间
什么是一个好的algorithm来确定剩下的时间来完成一些事情? 我知道总共有多less条线路,已经有多less条线路了,我应该如何估算剩余的时间?
为什么不?
(linesProcessed / TimeTaken)
(timetaken / linesProcessed) * LinesLeft = TimeLeft
TimeLeft
将随后以任何时间timeTaken
表示。
编辑:
感谢您的评论,你应该这样做:
(TimeTaken / linesProcessed) * linesLeft = timeLeft
所以我们有
(10 / 100) * 200
= 20秒现在10秒过去了
(20 / 100) * 200
= 40秒现在剩下10秒,我们处理100多行
(30 / 200) * 100
= 15秒,现在我们都明白为什么复制文件对话框从3小时跳到30分钟:-)
我很惊讶没有人用代码回答这个问题!
计算时间的简单方法,如@JoshBerke回答,可以编码如下:
DateTime startTime = DateTime.Now; for (int index = 0, count = lines.Count; index < count; index++) { // Do the processing ... // Calculate the time remaining: TimeSpan timeRemaining = TimeSpan.FromTicks(DateTime.Now.Subtract(startTime).Ticks * (count - (index+1)) / (index+1)); // Display the progress to the user ... }
这个简单的例子很适合简单的进度计算。
但是,对于一个更复杂的任务,这个计算有很多方法可以改进!
例如,当您下载一个大文件时,下载速度很容易波动。 要计算最准确的“ETA”,一个好的algorithm是只考虑过去10秒的进度。 查看ETACalculator.cs以获得该algorithm的实现!
ETACalculator.cs来自Progression – 我写的一个开源库。 它为各种“进度计算”定义了一个非常易于使用的结构。 这使得报告不同types进度的嵌套步骤变得很容易。 如果你关心感知的performance(就像@JoshBerkebuild议的那样),它将会非常有帮助。
确保pipe理感知的性能 。
尽pipe所有的进度条在testing中花费的时间完全相同,但有两个特点使得用户认为过程更快,即使不是:
- 进度栏顺利完成
- 进度条加快到最后
不要复活一个死的问题,但我不断回来引用这个页面。
您可以在Stopwatch类上创build一个扩展方法,以获得估计剩余时间跨度的function。
static class StopWatchUtils { /// <summary> /// Gets estimated time on compleation. /// </summary> /// <param name="sw"></param> /// <param name="counter"></param> /// <param name="counterGoal"></param> /// <returns></returns> public static TimeSpan GetEta(this Stopwatch sw, int counter, int counterGoal) { /* this is based off of: * (TimeTaken / linesProcessed) * linesLeft=timeLeft * so we have * (10/100) * 200 = 20 Seconds now 10 seconds go past * (20/100) * 200 = 40 Seconds left now 10 more seconds and we process 100 more lines * (30/200) * 100 = 15 Seconds and now we all see why the copy file dialog jumps from 3 hours to 30 minutes :-) * * pulled from http://stackoverflow.com/questions/473355/calculate-time-remaining/473369#473369 */ if (counter == 0) return TimeSpan.Zero; float elapsedMin = ((float)sw.ElapsedMilliseconds / 1000) / 60; float minLeft = (elapsedMin / counter) * (counterGoal - counter); //see comment a TimeSpan ret = TimeSpan.FromMinutes(minLeft); return ret; } }
例:
int y = 500; Stopwatch sw = new Stopwatch(); sw.Start(); for(int x = 0 ; x < y ; x++ ) { //do something Console.WriteLine("{0} time remaining",sw.GetEta(x,y).ToString()); }
希望对某人有用处。
编辑:应该指出,这是最准确的时候,每个循环需要相同的时间。
编辑2:而不是inheritance我创build了一个扩展方法。
一般来说,在处理的任何时候你都知道三件事:
- (A)已经处理了多less个单位/块/物品。
- (B)处理这些项目花了多长时间。
- 剩余项目数(C)。
给定这些项目,剩余时间的估计 (除非处理项目的时间是恒定的)
B * C / A
我做了这个,它工作的很好,随意改变方法签名根据你的variablestypes或也返回types,可能你想获得TimeSpan对象或只是秒…
/// <summary> /// Calculates the eta. /// </summary> /// <param name="processStarted">When the process started</param> /// <param name="totalElements">How many items are being processed</param> /// <param name="processedElements">How many items are done</param> /// <returns>A string representing the time left</returns> private string CalculateEta(DateTime processStarted, int totalElements, int processedElements) { int itemsPerSecond = processedElements / (int)(processStarted - DateTime.Now).TotalSeconds; int secondsRemaining = (totalElements - processedElements) / itemsPerSecond; return new TimeSpan(0, 0, secondsRemaining).ToString(); }
您将需要在处理开始时初始化DateTime
variables,并在每次迭代中将其发送给方法。
不要忘记,如果进程很长,可能窗口会被锁住,所以当你把返回值放到一个控件中时,不要忘记使用它的.Refresh()
方法。
如果你正在使用线程,那么你可以尝试使用Invoke(Action)
方法来设置文本,使用这个扩展方法很容易实现。
如果您使用控制台应用程序,那么您应该没有问题逐行显示输出。
希望它可以帮助别人。
这很大程度上取决于“东西”是什么。 如果您可以假定每行处理的时间量相似,则可以进行简单的计算:
TimePerLine = Elapsed / LinesProcessed TotalTime = TimePerLine * TotalLines TimeRemaining = TotalTime - LinesRemaining * TimePerLine
没有我知道的标准algorithm,我的消化会是:
- 创build一个variables来保存%
- 计算你想要跟踪的任务的复杂性(或估计它)
- 根据复杂程度的不同,将时间增加到%。
您可能已经看到负载栏在一个点上比另一个点快得多的程序。 那么这很重要,因为他们是这样做的。 (尽pipe他们可能只是在主包装中定期增加)
其中time$("ms")
表示从00:00:00开始的当前时间(以毫秒为单位), lof
表示要处理的总行数, x
表示当前行:
if Ln>0 then Tn=Tn+time$("ms")-Ln 'grand total of all laps Rn=Tn*(lof-x)/x^2 'estimated time remaining in seconds end if Ln=time$("ms") 'start lap time (current time)
这真的取决于正在做什么……线路是不够的,除非每条线路都花费相同的时间。
最好的方法(如果你的线不相似)可能会看代码的逻辑部分,找出每个部分平均需要多长时间,然后使用这些平均时间来估计进度。
如果你知道完成的百分比,你可以简单地假设时间线性地变化,类似
timeLeft = timeSoFar *(1 / Percentage)
可能会工作。
我已经知道完成百分比和时间,所以这帮助了我:
TimeElapsed *((100 – %完成)/%完成)= TimeRemaining
然后我每次更新完这个数值,完成改变,给我一个不断变化的ETA。
有两种performance时间的方式
-
经过的时间和剩余的时间:经过的时间会增加,但剩下的时间可能是稳定的总时间(如果每秒稳定)
-
经过的时间和剩余的时间:
所以剩下的时间=总需要 – 已经过去了
我的想法/公式更像这样:
已处理 – 从运行的线程从0更新为Total
我有1000毫秒计时间隔计算每秒处理:
processedPerSecond = Processed - lastTickProcessed; lastTickProcessed = Processed; //store state from past call
processedPerSecond和lastTickProcessed是超出定时器方法的全局variables
现在如果我们想要得到完成处理需要多less秒钟(在理想常数假设下)totalSecondsNeeded = TotalLines / PerSecond
但我们要显示案例2. TimeLeft so TimeLeftSeconds =(TotalLines – Processed)/ PerSecond
TimeSpan remaining = new TimeSpan(0, 0, (transactions.Count - Processed) / processedPerSecond); labelTimeRemaining.Text = remaining.ToString(@"hh\:mm\:ss");
当然,如果PerSecond跳跃,TimeLeftSeconds将会“跳跃”,所以如果过去的PerSecond是10,然后是30,然后回到10,用户就会看到它。
有一种方法可以计算平均值,但是如果进程加速到最后,这可能不会显示实时剩余
int perSecond = (int)Math.Ceiling((processed / (decimal)timeElapsed.TotalSeconds)); //average not in past second
因此,开发人员可以根据预测处理“跳跃”的程度来“挑选”一种最准确的方法
我们也可以计算并保存每个PerSecond,然后取最后10秒并取平均值,但在这种情况下,用户将不得不等待10秒才能看到第一个计算,或者我们可以显示从第一个每秒开始的剩余时间,然后逐步平均地总结到10个最后的每秒
我希望我的“跳跃”思想能帮助别人创造令人满意的东西
这个怎么样….
我用它来遍历一组logging(在一个Excel文件中的行,在一个例子中)
L是当前行号X是例程开始时,dat_Start被设置为Now()的总行数
Debug.Print Format((L / X), "percent") & vbTab & "Time to go:" & vbTab & Format((DateDiff("n", dat_Start, Now) / L) * (X - L), "00") & ":" & Format(((DateDiff("s", dat_Start, Now) / L) * (X - L)) Mod 60, "00")