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?
Warum nicht?
(linesProcessed / TimeTaken)
(timetaken / linesProcessed) * LinesLeft = TimeLeft
TimeLeft
wird dann in der Zeiteinheit timeTaken
ausgedrückt.
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 :-)
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.
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:
- fortschrittsbalken, die sich reibungslos in Richtung Fertigstellung bewegten
- fortschrittsbalken, die gegen Ende beschleunigten
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.
Im Allgemeinen wissen Sie während der Verarbeitung zu jedem Zeitpunkt drei Dinge:
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
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.
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
ich kenne keinen Standardalgorithmus, mein Vorschlag wäre:
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)
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)
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.
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")
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.
Es gibt zwei Möglichkeiten, die Zeit anzuzeigen
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).
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
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)
}
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.