69 votes

Calculer le temps restant

Quel est un bon algorithme pour déterminer le temps restant pour que quelque chose se termine ? Je sais combien de lignes il y a au total, et combien ont déjà été complétées, comment dois-je estimer le temps restant ?

76voto

JoshBerke Points 34238

Pourquoi pas ?

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

TimeLeft sera alors exprimée dans n'importe quelle unité de temps timeTaken est.

Edit :

Merci pour le commentaire, vous avez raison, cela devrait être le cas :

(TimeTaken / linesProcessed) * linesLeft = timeLeft

donc nous avons

(10 / 100) * 200 = 20 secondes maintenant 10 secondes passées
(20 / 100) * 200 = 40 Secondes restantes maintenant 10 secondes de plus et nous traitons 100 lignes de plus
(30 / 200) * 100 = 15 secondes et maintenant nous voyons tous pourquoi le dialogue de copie de fichier passe de 3 heures à 30 minutes :-)

1 votes

Peut-être que je rate quelque chose ici. Disons que nous avons traité 100 lignes en 10 secondes, avec 200 lignes restantes. Cela nous donne (100/10)*200 = 2000 secondes restantes. Maintenant, 10 secondes passent sans qu'aucune autre ligne ne soit traitée. Maintenant, le temps restant est de (100/20)*200 = 1000 secondes, même si aucun autre traitement n'a eu lieu.

2 votes

L'expression devrait être (Temps pris/lignes traitées)*lignesGauche

1 votes

Oui, l'expression était fausse;-) Mais c'était bien dans l'esprit des choses !

36voto

Scott Rippey Points 6921

Je suis surpris que personne n'ait répondu à cette question par un code !

La manière simple de calculer le temps, comme répondu par @JoshBerke, peut être codée comme suit :

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

Cet exemple simple fonctionne très bien pour un simple calcul de progression.
Cependant, pour une tâche plus compliquée, il existe de nombreuses façons d'améliorer ce calcul !

Par exemple, lorsque vous téléchargez un gros fichier, la vitesse de téléchargement peut facilement fluctuer. Pour calculer l'"ETA" la plus précise, un bon algorithme consisterait à ne prendre en compte que les 10 dernières secondes de progression. Consultez ETACalculator.cs pour une mise en œuvre de cet algorithme !

ETACalculator.cs vient de Progression -- une bibliothèque open source que j'ai écrite. Elle définit une structure très facile à utiliser pour toutes sortes de "calculs de progression". Il est facile d'avoir des étapes imbriquées qui rapportent différents types de progrès. Si vous êtes préoccupé par la performance perçue (comme @JoshBerke l'a suggéré), cela vous aidera énormément.

0 votes

C'est en fait une très bonne bibliothèque. Je pense que si vous créez un paquet NuGet pour elle, elle pourrait devenir populaire.

1 votes

Veuillez créer un paquet NuGet

0 votes

Désolé, j'ai environ 7 ans de retard dans mon expérience de .NET. Je serais heureux d'ajouter des collaborateurs au projet, ou même de transférer la propriété, si vous souhaitez contribuer au projet et aider à créer le paquet NuGet :)

19voto

Anthony Mastrean Points 8582

Assurez-vous de gérer performance perçue .

Bien que toutes les barres de progression aient pris exactement le même temps dans le test, deux caractéristiques ont fait croire aux utilisateurs que le processus était plus rapide, même s'il ne l'était pas :

  1. des barres de progression qui progressent doucement vers l'achèvement
  2. des barres de progression qui s'accélèrent vers la fin

1 votes

Oui, j'ai lu ce post. Et j'ai ressenti les effets de ce dont il parlait aussi. La copie me semblait VRAIMENT lente dans Vista, mais apparemment à cause de la barre de progression et non du temps réel que cela prenait.

1 votes

Je suis d'accord, mécontent. C'est bien mieux dans Windows 7, comme tout le reste dans Windows 7.

18voto

Chad Carisch Points 678

Je ne veux pas ranimer une question morte, mais je n'ai pas cessé de revenir pour consulter cette page.
Vous pourriez créer une méthode d'extension de la classe Chronomètre pour obtenir une fonctionnalité permettant d'obtenir une estimation du temps restant.

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

Ejemplo:

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

J'espère que cela sera utile à quelqu'un.

EDIT : Il faut noter que ceci est plus précis lorsque chaque boucle prend le même temps.
Edit 2 : Au lieu de sous-classer, j'ai créé une méthode d'extension.

1 votes

Bravo pour l'idée de mettre en œuvre une solution simple comme la méthode d'extension

3voto

coloboxp Points 81

J'ai fait ceci et cela fonctionne très bien, n'hésitez pas à changer la signature de la méthode en fonction des types de variables ou aussi du type de retour, probablement vous voudriez obtenir l'objet TimeSpan ou juste les secondes...

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

Vous devrez initialiser un DateTime lorsque le traitement commence et l'envoie à la méthode à chaque itération.

N'oubliez pas que votre fenêtre sera probablement verrouillée si le processus est assez long, donc lorsque vous placez la valeur de retour dans un contrôle, n'oubliez pas d'utiliser la balise .Refresh() de la méthode.

Si vous utilisez des threads, vous pouvez tenter de définir le texte à l'aide de la fonction Invoke(Action) il serait plus facile d'utiliser la méthode cette méthode d'extension pour l'archiver facilement.

Si vous utilisez une application console, vous ne devriez pas avoir de problèmes pour afficher la sortie ligne par ligne.

J'espère que cela aidera quelqu'un.

0 votes

J'obtiens une division par zéro à itemsPerSecond = ... Mon processStart est 23:53:26 et mon DateTime.Now est 23:53:28. Pourquoi une division par zéro ?

0 votes

Bonjour Daniel, le TimeSpan (résultat de l'addition ou de la soustraction des objets DateTime) a probablement une différence de "Zéro" seconde. Selon vos valeurs, il devrait être de "2" secondes. Veuillez vérifier la valeur de "Ticks" pour vous assurer qu'elle est différente entre "processStarted" et DateTime.Now.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X