it-swarm.com.de

Verbleibende Zeit berechnen

Was ist ein guter Algorithmus, um die verbleibende Zeit zu bestimmen, bis etwas abgeschlossen ist? Ich weiß, wie viele Zeilen insgesamt vorhanden sind und wie viele bereits fertig sind. Wie schätze ich die verbleibende Zeit ab?

49
Aaron Smith

Warum nicht? 

(linesProcessed / TimeTaken) (timetaken / linesProcessed) * LinesLeft = TimeLeft

TimeLeft wird dann in der Zeiteinheit timeTaken ausgedrückt.

Bearbeiten:

Vielen Dank für den Kommentar, dass Sie Recht haben sollten:

(TimeTaken / linesProcessed) * linesLeft = timeLeft

also haben wir

(10 / 100) * 200 = 20 Sekunden vergehen jetzt 10 Sekunden
(20 / 100) * 200 = 40 Sekunden sind jetzt noch 10 Sekunden und wir verarbeiten 100 weitere Zeilen
(30 / 200) * 100 = 15 Sekunden und nun sehen wir alle, warum der Dialog zum Kopieren von Dateien von 3 Stunden auf 30 Minuten springt :-) 

54
JoshBerke

Ich bin überrascht, dass niemand diese Frage mit Code beantwortet hat!

Die von @JoshBerke beantwortete einfache Methode zum Berechnen der Zeit kann wie folgt codiert werden:

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
    ...
}

Dieses einfache Beispiel eignet sich hervorragend für die einfache Fortschrittsberechnung.
Für eine kompliziertere Aufgabe gibt es viele Möglichkeiten, diese Berechnung zu verbessern!

Wenn Sie beispielsweise eine große Datei herunterladen, kann die Downloadgeschwindigkeit leicht schwanken. Um die genaueste "ETA" zu berechnen, wäre ein guter Algorithmus, nur die letzten 10 Sekunden des Fortschritts zu berücksichtigen. Check out ETACalculator.cs für eine Implementierung dieses Algorithmus!

ETACalculator.cs stammt aus Progression - einer Open-Source-Bibliothek, die ich geschrieben habe. Es definiert eine sehr benutzerfreundliche Struktur für alle Arten von "Fortschrittsberechnung". Es macht es einfach, verschachtelte Schritte zu haben, die verschiedene Arten von Fortschritt anzeigen. Wenn Sie über Perceived Performance besorgt sind (wie @JoshBerke vorgeschlagen hat), wird Ihnen dies enorm helfen.

26
Scott Rippey

Stellen Sie sicher, dass Sie wahrgenommene Leistung verwalten.

Obwohl alle Fortschrittsbalken im Test exakt die gleiche Zeit benötigten, wurde der Prozess aufgrund zweier Merkmale als schneller angesehen, auch wenn dies nicht der Fall war:

  1. fortschrittsbalken, die sich reibungslos in Richtung Fertigstellung bewegten
  2. fortschrittsbalken, die gegen Ende beschleunigten
16

Um eine tote Frage nicht wieder zu beleben, habe ich immer wieder auf diese Seite verwiesen.
Sie können eine Erweiterungsmethode für die Stopwatch-Klasse erstellen, um Funktionalität zu erhalten, die eine geschätzte verbleibende Zeitspanne ergibt. 

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;
    }
}

Beispiel:

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

Hoffentlich hilft es jemandem.


EDIT: Es sollte beachtet werden, dass dies am genauesten ist, wenn jede Schleife die gleiche Zeit in Anspruch nimmt .
Edit 2: Statt Unterklassen habe ich eine Erweiterungsmethode erstellt. 

9
Chad Carisch

Im Allgemeinen wissen Sie während der Verarbeitung zu jedem Zeitpunkt drei Dinge:

  1. Wie viele Einheiten/Stücke/Artikel wurden bis zu diesem Zeitpunkt verarbeitet (A).
  2. Wie lange hat es gedauert, diese Gegenstände zu verarbeiten (B).
  3. Die Anzahl der verbleibenden Elemente (C).

In Anbetracht dieser Positionen wird die Schätzung der verbleibenden Zeit (es sei denn, die Zeit zur Verarbeitung einer Position ist konstant) sein

B * C/A

3
casperOne

Ich habe das gemacht und es funktioniert ganz gut, zögern Sie nicht, die Methodensignatur nach Ihren Variablentypen oder auch nach dem Rückgabetyp zu ändern, wahrscheinlich möchten Sie das TimeSpan-Objekt oder nur die Sekunden abrufen ...

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

Sie müssen eine Variable DateTime initialisieren, wenn die Verarbeitung beginnt, und sie bei jeder Iteration an die Methode senden.

Vergessen Sie nicht, dass Ihr Fenster wahrscheinlich gesperrt wird, wenn der Prozess sehr lang ist. Wenn Sie also den Rückgabewert in ein Steuerelement einfügen, vergessen Sie nicht, die Methode .Refresh() zu verwenden.

Wenn Sie Threads verwenden, können Sie versuchen, den Text mit der Invoke(Action) -Methode festzulegen. Diese Erweiterungsmethode ist einfacher zu verwenden , um den Text zu archivieren leicht.

Wenn Sie eine Konsolenanwendung verwenden, sollten Sie keine Probleme haben, die Ausgabe zeilenweise anzuzeigen.

Hoffe es hilft jemandem.

3
coloboxp

Es hängt stark davon ab, was das "Etwas" ist. Wenn Sie davon ausgehen können, dass die Verarbeitungszeit für jede Zeile ähnlich ist, können Sie eine einfache Berechnung durchführen:

TimePerLine = Elapsed / LinesProcessed
TotalTime = TimePerLine * TotalLines
TimeRemaining = TotalTime - LinesRemaining * TimePerLine
2
Michael Meadows

ich kenne keinen Standardalgorithmus, mein Vorschlag wäre:

  • Erstellen Sie eine Variable, um das% zu speichern.
  • Berechnen Sie die Komplexität der Aufgabe, die Sie verfolgen möchten (oder eine Schätzung davon).
  • Setzen Sie von Zeit zu Zeit Inkremente auf%, wie Sie es angesichts der Komplexität für richtig halten würden.

Sie haben wahrscheinlich Programme gesehen, bei denen der Ladebalken an einem Punkt viel schneller abläuft als an einem anderen. Nun, das ist so ziemlich, weil sie es so machen. (obwohl sie wahrscheinlich nur in regelmäßigen Abständen Inkremente in den Wrapper einfügen)

1
fmsf

Dabei steht time$("ms") für die aktuelle Zeit in Millisekunden seit 00: 00: 00.00 und lof für die gesamte zu verarbeitende Zeile und x für die aktuelle Zeile:

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)
1
Alexander V

Das hängt wirklich davon ab, was gemacht wird ... Zeilen reichen nicht aus, es sei denn, jede einzelne Zeile braucht die gleiche Zeit.

Der beste Weg (wenn Ihre Zeilen nicht ähnlich sind) besteht wahrscheinlich darin, logische Abschnitte des Codes zu finden, um herauszufinden, wie lange jeder Abschnitt durchschnittlich dauert. Anschließend können Sie anhand dieser durchschnittlichen Zeiten den Fortschritt abschätzen.

0
chills42

Wie wäre es damit....

Ich habe damit eine Reihe von Datensätzen durchlaufen (Zeilen in einer Excel-Datei, in einem Fall).

L ist die aktuelle Zeilennummer X ist die Gesamtzahl der Zeilen Dat_Start wird auf Now () gesetzt, wenn die Routine beginnt

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")
0
Brian Battles

Wenn Sie wissen, wie viel Prozent Sie abgeschlossen haben, und Sie können einfach davon ausgehen, dass die Zeit linear skaliert, etwa so 

timeLeft = timeSoFar * (1/Prozentsatz)

könnte funktionieren.

0
GWLlosa

Es gibt zwei Möglichkeiten, die Zeit anzuzeigen

  1. Verstrichene Zeit und verbleibende Zeit insgesamt: Die verstrichene Zeit nimmt also zu, aber die verbleibende Zeit ist wahrscheinlich stabil. Die Gesamtzeit (wenn pro Sekunde stabil ist).

  2. Verstrichene Zeit und verbleibende Zeit:
    so verbleibende Zeit = insgesamt benötigt - abgelaufen

Meine Idee/Formel ist eher so:

Verarbeitet - vom laufenden Thread von 0 bis Gesamt aktualisiert 

Ich habe einen Timer mit einem Intervall von 1000 ms, der pro Sekunde verarbeitet wird:

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

processingPerSecond und lastTickProcessed sind globale Variablen außerhalb der Timer-Methode

Wenn wir nun wissen möchten, wie viele Sekunden erforderlich sind, um die Verarbeitung abzuschließen (in idealer konstanter Annahme) TotalSecondsNeeded = TotalLines/PerSecond

wir wollen jedoch Fall 2. TimeLeft so zeigen TimeLeftSeconds = (TotalLines - Processed)/PerSecond

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

Natürlich springt TimeLeftSeconds, wenn PerSecond springt. Wenn also PerSecond 10 war, dann 30, dann wieder 10, dann wird der Benutzer es sehen.

Es gibt eine Möglichkeit, den Durchschnitt zu berechnen, aber dies zeigt möglicherweise nicht die verbleibende Zeit an, wenn der Prozess am Ende schneller wird

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

Daher kann es die Wahl eines Entwicklers sein, eine Methode auszuwählen, die am genauesten ist, basierend auf der Vorhersage, wie "sprunghaft" die Verarbeitung ist

Wir könnten auch jede PerSecond berechnen und speichern, dann die letzten 10 Sekunden dauern und einen Durchschnitt bilden, aber in diesem Fall muss der Benutzer 10 Sekunden warten, um die erste Berechnung zu sehen durchschnittliche Summierung bis zu 10 letzte PerSecond

Ich hoffe, dass meine "nervösen" Gedanken jemandem helfen werden, etwas befriedigendes aufzubauen

0
Pawel Cioch

PowerShell-Funktion

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

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

Ich wusste bereits, wie viel Prozent und Zeit vergangen sind, deshalb half mir das:

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

Ich habe diesen Wert dann jedes Mal aktualisiert, wenn sich% complete geändert hat, wodurch ich eine konstant unterschiedliche ETA erhält.

0
Schrodo_Baggins