development

남은 시간 계산

big-blog 2021. 1. 10. 19:51
반응형

남은 시간 계산


완료하는 데 남은 시간을 결정하는 데 좋은 알고리즘은 무엇입니까? 총 라인 수와 이미 완료된 라인 수를 알고 있습니다. 남은 시간을 어떻게 추정해야합니까?


왜 안돼?

(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"를 계산하려면 지난 10 초의 진행 상황 만 고려하는 것이 좋은 알고리즘입니다. 이 알고리즘의 구현은 ETACalculator.cs확인하세요 !

ETACalculator.cs내가 작성한 오픈 소스 라이브러리 인 Progression 에서 가져 왔습니다 . 모든 종류의 "진행률 계산"에 대해 매우 사용하기 쉬운 구조를 정의합니다. 다양한 유형의 진행 상황을보고하는 중첩 된 단계를 쉽게 만들 수 있습니다. 인지 된 성능 (@JoshBerke가 제안한대로)에 대해 염려한다면 대단히 도움이 될 것입니다.


인지 된 성능 을 관리해야합니다 .

모든 진행률 표시 줄이 테스트에서 정확히 동일한 시간이 걸렸지 만 두 가지 특성으로 인해 사용자는 그렇지 않은 경우에도 프로세스가 더 빠르다고 생각했습니다.

  1. 완료를 향해 부드럽게 이동하는 진행률 표시 줄
  2. 끝을 향해 가속되는 진행률 표시 줄

죽은 질문을 되살리려는 것이 아니라이 페이지를 참조하기 위해 계속 돌아 왔습니다.
예상 남은 시간 범위를 가져 오는 기능을 얻기 위해 Stopwatch 클래스에 확장 메서드를 만들 수 있습니다.

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 : 서브 클래 싱 대신 확장 메서드를 만들었습니다.


일반적으로 처리하는 동안 언제든지 세 가지 사항을 알고 있습니다.

  1. 해당 시점 (A)까지 처리 된 단위 / 청크 / 항목 수입니다.
  2. 해당 항목을 처리하는 데 걸린 시간 (B).
  3. 남은 항목 수 (C).

이러한 항목이 주어지면 남은 시간 추정치 (항목 처리 시간이 일정하지 않은 경우)는

B * C / A


I made this and it works quite good, feel free to change the method signature according to your variable types or also to the return type, probably you would like to get the TimeSpan object or just the seconds...

    /// <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();
    }

You will require to initialize a DateTime variable when the processing starts and send it to the method on each iteration.

Do not forget that probably your window will be locked if the process is quite long, so when you place the return value into a control, don't forget to use the .Refresh() method of it.

If you are using threads then you can attempt to set the text using the Invoke(Action) method, would be easier to use this extension method to archieve it easily.

If you use a console application, then you should not have problems displaying the output line by line.

Hope it helps someone.


It depends greatly on what the "something" is. If you can assume that the amount of time to process each line is similar, you can do a simple calculation:

TimePerLine = Elapsed / LinesProcessed
TotalTime = TimePerLine * TotalLines
TimeRemaining = TotalTime - LinesRemaining * TimePerLine

there is no standard algorithm i know of, my sugestion would be:

  • Create a variable to save the %
  • Calculate the complexity of the task you wish to track(or an estimative of it)
  • Put increments to the % from time to time as you would see fit given the complexity.

You probably seen programs where the load bar runs much faster in one point than in another. Well that's pretty much because this is how they do it. (though they probably just put increments at regular intervals in the main wrapper)


Where time$("ms") represents the current time in milliseconds since 00:00:00.00, and lof represents the total lines to process, and x represents the current line:

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)

That really depends on what is being done... lines are not enough unless each individual line takes the same amount of time.

The best way (if your lines are not similar) would probably be to look at logical sections of the code find out how long each section takes on average, then use those average timings to estimate progress.


If you know the percentage completed, and you can simply assume that the time scales linearly, something like

timeLeft = timeSoFar * (1/Percentage)

might work.


I already knew the percentage complete & time elapsed, so this helped me:

TimeElapsed * ((100 - %complete) / %complete) = TimeRemaining

I then updated this value every time %complete changed, giving me a constant varying ETA.


There is 2 ways of showing time

  1. Time elapsed and Time Remaining overall: so elapsed will increase but remaining will be likely stable total time needed (if per second is stable)

  2. Time elapsed and Time Left:
    so Time Left = Total Needed - Elapsed

My idea/formula is more likely like this:

Processed - updated from running thread from 0 to Total

I have timer with 1000ms interval that calculates processed per second:

processedPerSecond = Processed - lastTickProcessed;
lastTickProcessed = Processed;  //store state from past call

processedPerSecond and lastTickProcessed are global variables out of timer method

Now if we would like to get how many seconds is required to complete the processing (in ideal constant assumption) totalSecondsNeeded = TotalLines / PerSecond

but we want to show case 2. TimeLeft so TimeLeftSeconds = (TotalLines - Processed) / PerSecond

TimeSpan remaining = new TimeSpan(0, 0, (transactions.Count - Processed) / processedPerSecond);
labelTimeRemaining.Text = remaining.ToString(@"hh\:mm\:ss");

Of course TimeLeftSeconds will "jump" if PerSecond jumps, so if past PerSecond was 10 then 30 then back to 10, the user will see it.

There is a way to calculate average, but this may not show real time left if process speeds up at the end

int perSecond = (int)Math.Ceiling((processed / (decimal)timeElapsed.TotalSeconds));  //average not in past second

So it may be the choice for a developer to "pick" a method that will be most accurate based on prediction of how "jumpy" the processing is

We could also calculate and save each PerSecond, then take last 10 second and made average, but in this case user will have to wait 10 seconds to see first calculation or we could show time left starting from first per second and then progressively average summing up to 10 last PerSecond

I hope my "jumpy" thoughts will help someone to build something satisfying


How about this....

I used this to walk through a set of records (rows in an Excel file, in one case)

L is the current row number X is the total number of rows dat_Start is set to Now() when the routine begins

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")

PowerShell function

function CalculateEta([datetime]$processStarted, [long]$totalElements, [long]$processedElements) {
    $itemsPerSecond = $processedElements / [DateTime]::Now.Subtract($processStarted).TotalSeconds
    $secondsRemaining = ($totalElements - $processedElements) / $itemsPerSecond

    return [TimeSpan]::FromSeconds($secondsRemaining)
}

ReferenceURL : https://stackoverflow.com/questions/473355/calculate-time-remaining

반응형