IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Calcul scientifique Python Discussion :

Gagner du temps sur moyennage


Sujet :

Calcul scientifique Python

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    36
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 36
    Points : 31
    Points
    31
    Par défaut Gagner du temps sur moyennage
    Bonjour bonjour,

    Voilà l'histoire : je suis en cours de finalisation d'un prog Python, et j'essaie entre autres de gagner du temps sur l'exécution.
    Or une fonction particulière de mon programme prend beaucoup de temps, et j'ose suputer qu'il existe un moyen détourné plus rapide d'arriver à mes fins!

    Voilà la bête :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    import numarray as n
    
    def miseEchelle(entree, long, moy):
    
    sortie = list() for i in range(0, long, moy): sortie.append ( n.average ( entree[i:i + moy - 1] ) ) return n.array(sortie, 'Float32')
    Comme c'est écrit, le but de la fonction est de mettre à l'échelle une image. La fonction s'applique à une ligne de 15000, 20000, 40000 pixels, le pas de moyennage est en général de 30, et l'image compte 800 lignes... ce qui donne:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    for i in range(800):
        # lecture d'une ligne
        ligne_moy = miseEchelle(ligne, 20000, 30)
        # ecriture de ligne_moy
    Tout est simplifié bien sûr. La boucle prend chez moi environ 100 s (j'utilise déjà psyco, et oui l'ordi est vieux!! ) et a priori à cause de average.

    Bon... quelqu'un est inspiré?

  2. #2
    Expert éminent sénior
    Avatar de Guigui_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2002
    Messages
    1 864
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2002
    Messages : 1 864
    Points : 10 067
    Points
    10 067
    Par défaut
    Quelques optimisations rapides:
    Toujours penser au variable locale quand on utilise une boucle for

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import numarray as n
     
    def miseEchelle(entree, long, moy):
        sortie = list()
        sortieappend = sortie.append
        naverage = n.average
        moy1 = moy-1
        for i in range(0, long, moy):
            sortieappend ( naverage ( entree[i:i + moy1] ) )     
        return n.array(sortie, 'Float32')

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    36
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 36
    Points : 31
    Points
    31
    Par défaut
    Oki
    J'teste ça demain, je donnerai les résultats en direct live.

    Mais je pensais que l'économie de variables était souvent importante... ça gêne pas d'en déclarer plein comme ça?

    ------
    Note : oui, ouiiiii, je ne suis pas très fort en prog... mais 'm'en sors quand même!

  4. #4
    Expert éminent sénior
    Avatar de Guigui_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2002
    Messages
    1 864
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2002
    Messages : 1 864
    Points : 10 067
    Points
    10 067
    Par défaut
    Citation Envoyé par Panthère Bleue
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    for i in range(800):
        # lecture d'une ligne
        ligne_moy = miseEchelle(ligne, 20000, 30)
        # ecriture de ligne_moy
    Est-ce que ligne est modifié dans la boulce for parce que sinon, tu fais 800 fois la même chose (donc autant le sortir de la boucle) ?

    L'économie de variable peut être intéressante lorsque tu utilises des objets d'une même classe en grandes quantité (donc moins il possède d'attribut moins la place en mémoire sera importante). Sinon, cela n'a pas trop d'importance.

  5. #5
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    36
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 36
    Points : 31
    Points
    31
    Par défaut
    Oui, oui... mais toutes ces réflexions me font penser que mon code est peut etre bien plus maladroit que je n'le pensais.

    La boucle ressemble plus à ça:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    flu = open('c:/fichier_a', 'rb')
    fecrit = open('c:/fichier_b', 'ab')
    
    for i in range(800):
        ecritureLigne( fecrit, miseEchelle( lectureLigne(flu), 20000, 30 ) )
    
    # fermeture de flu et fecrit.
    Pendant toute la boucle, les fichiers restent ouverts. Les lignes sont prises une à une, mises à l'échelle (en réalité, il y a 2 autres opérations avant la mise à l'échelle mais je simplifie...) puis écrites les unes à la suite des autres dans le fichier de sortie.

    Comme toutes les lignes de l'image sont distinctes, ligne est différent à chaque itération. C'est bien ça que tu demandais?... sinon, j'vais attendre demain, j'aurais les idées plus claires.

    Merci en tout cas de réfléchir avec moi là dessus, c'est instructif!

  6. #6
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    36
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 36
    Points : 31
    Points
    31
    Par défaut
    Alors j'ai fait plusieurs essais :
    a - en appliquant tes conseils,
    b - en utilisant psyco,
    c - en remplaçant average par un moyennage bidouillé

    Je calcule le temps d'exécution en enregistrant time.localtime() au début et à la fin et en soustrayant les 2. Les essais sont réalisés dans les mêmes conditions : aucune autre fenêtre ouverte que la console windows où le programme tourne.
    1. a : 162s
    2. a + b : 115s
    3. b : 121s
    4. a+c : 116s
    5. c: 115s
    6. b+c : 94s


    Bon, voilà. Ce n'est pas d'une précision folle, mais suffisante pour se rendre compte que average prend énormément de temps...
    En remplaçant:
    par:
    le résultat est identique. Mais le temps d'exécution est moins long dans le 2e cas... hum. Je vais aller étudier la méthode average, c'est vraiment bizarre.

  7. #7
    Membre éclairé Avatar de pop_up
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    877
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Avril 2006
    Messages : 877
    Points : 786
    Points
    786
    Par défaut
    bonjour,

    trés interressante cette discussion.
    et sinon, a tu essayer de stocker tes lignes dans une variable et de ne faire que l'ouverture du fichier une fois le calcul terminé ?

    juste pour savoir car je ne suis pas sur que tu y gagnes du temps.

  8. #8
    Expert éminent sénior
    Avatar de Guigui_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2002
    Messages
    1 864
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2002
    Messages : 1 864
    Points : 10 067
    Points
    10 067
    Par défaut
    Citation Envoyé par Panthère Bleue
    le résultat est identique. Mais le temps d'exécution est moins long dans le 2e cas... hum. Je vais aller étudier la méthode average, c'est vraiment bizarre.
    Attention, average n'a pas été conçue pour faire des moyennes simples mais des moyennes quantifiées
    average(x, axis=0, weights=None, returned=0)
    Donc forcément, ce n'est pas optimisée pour ce que tu veux faire (et surtout si tu l'exécute beaucoup de fois)
    préfère alors la fonction mean() qui fait une moyenne simple et qui devrait être beaucoup plus rapide

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    import numarray as n
     
    def miseEchelle(entree, long, moy):
        sortie = list()
        sortieappend = sortie.append
        moy1 = moy-1
        for i in range(0, long, moy):
            sortieappend (entree[i:i + moy1].mean() )     
        return n.array(sortie, 'Float32')

  9. #9
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    36
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 36
    Points : 31
    Points
    31
    Par défaut
    pop_up, je n'ai pas essayé de stocker les données traitées avant d'ouvrir le fichier en écriture. Je ne suis pas sûr que ce soit vraiment bénéfique... mais là pour aujourd'hui j'ai un peu la flemme d'essayer.

    Guigui, j'ai tenté le coup avec mean() mais... les calculs sont plus lents qu'avec average!
    Pour des conditions identiques :
    1. mean : 130s
    2. average : 113s
    3. sum: 97s

    C'est vrai que average est prévu pour des moyennes pondérées, sur des tableaux multidimensionnels en plus, alors que je n'ai que des lignes.
    Pour mean, j'ai du mal à comprendre... la méthode se résume à prendre sum et à diviser par nelements.
    En prenant sum et en divisant par float(len()), comme je fais, c'est carrément plus rapide.
    Bah...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    import numarray as n
     
    def miseEchelle(entree, long, moy):
        sortie = list()
        for i in range(0, long, moy):
            sortie.append (sum(entree[i:i + moy - 1]) / float(moy) )     
        return n.array(sortie, 'Float32')
    Note : la déclaration des variables locales sortieappend et moy1 n'accélère pas toujours le calcul, parfois ça prend même 1s de plus...

  10. #10
    Expert éminent sénior
    Avatar de Guigui_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2002
    Messages
    1 864
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2002
    Messages : 1 864
    Points : 10 067
    Points
    10 067
    Par défaut
    Citation Envoyé par Panthère Bleue
    Note : la déclaration des variables locales sortieappend et moy1 n'accélère pas toujours le calcul, parfois ça prend même 1s de plus...
    Normalement si sauf si tu utilises Pysco qui fait une compilation dynamique avec ses propres optimisations (qui peuvent donc contrecarrer les nôtres).

    Sinon, c'est normal que faire la moyenne par soit même soit plus rapide car on évite un appel de fonction (qui était donc répétée quelques milliers de fois). Par contre pourquoi mean est plus lang que average, ca je ne sais pas (il faudrait regarder directement dans le source de numarray pour voir comment s'est implanté).

    SI tu veux encore accélérer les choses, au lieu d'utiliser une fonction intermédiaire miseEchelle, tu peux directement copier le contenu dans la première boucle for avec un truc du genre (t'y perds un peu en lisibilité mais si le temps d'exécution est primordiale, tu devrais y gagner quelques secondes).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    for j in range(800):
        sortie = []
        for i in range(0, 20000, 30):
            sortie.append (sum(entree[i:i + 29]) / 29.0 )
    De toute façon, numarray n'est pas réputé pour sa rapidité. Il vaudrait peut-être mieux se tourner vers Numpy (dont la version 1.0 vient de sortir) et qui a pour vocation d'être le successeur ne numarray et numeric ( http://numeric.scipy.org/ )

    J'ai fait 2-3 tests et ca a l'air d'être extrêmement rapide. Très simple à mettre en place.
    Normalement, tu fais simplement un
    en supprimant l'importation de numarray et le reste du code ne change pas et ca devrait marcher (il faut quand même faire attention à l'encodage (pour que cela reste en float))

    Dernière chose:
    si avec tes données, tu te contentes de faire des calculs de moyenne (sans utiliser d'autres fonctions spécifiques à numarray), autant conservé tes données en tant que liste de base et faire des calculs de moyenne dessus, cela sera beaucoup plus rapide que de passer par une bibliothèque du type numarray ou numpy

  11. #11
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    36
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 36
    Points : 31
    Points
    31
    Par défaut
    Citation Envoyé par Guigui_
    Normalement si sauf si tu utilises Pysco qui fait une compilation dynamique avec ses propres optimisations (qui peuvent donc contrecarrer les nôtres).
    Effectivement, sans Psyco je passe de 126 à 115s, c'est toujours utile si le prog est amené à tourner sur un ordi qui n'a pas psyco (ce qui ne devrait pas être le cas).

    Citation Envoyé par Guigui_
    De toute façon, numarray n'est pas réputé pour sa rapidité. Il vaudrait peut-être mieux se tourner vers Numpy (dont la version 1.0 vient de sortir) et qui a pour vocation d'être le successeur ne numarray et numeric ( http://numeric.scipy.org/ )
    Je voulais me tourner vers numpy dès le départ (il y a 2 mois, donc) mais j'ai préféré numarray parce qu'au moins j'étais sûr de la stabilité. J'ai DL la version 1 avec ton lien, pour faire un test.

    Citation Envoyé par Guigui_
    J'ai fait 2-3 tests et ca a l'air d'être extrêmement rapide. Très simple à mettre en place.
    Normalement, tu fais simplement un
    en supprimant l'importation de numarray et le reste du code ne change pas et ca devrait marcher (il faut quand même faire attention à l'encodage (pour que cela reste en float))
    Malheureusement les tests sont très décevants... j'ai fait gaffe à l'encodage (il suffit de changer 'Float32' par 'float32', mais même sans le faire ça marche je crois) mais je me suis arrêté à 5 ou 6% parce que c'était incroyablement lent! Bizarre... pourtant je n'ai rien changé d'autre... Dans la boucle, les méthodes fromstring, tostring, et array sont utilisées. Je ne sais pas laquelle est lente à ce point...
    Autre point négatif : j'utilise nd_image de numarray, et je ne l'ai pas retrouvé sous numpy (ni en faisant un dir(n), ni dans la source).

  12. #12
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    36
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 36
    Points : 31
    Points
    31
    Par défaut
    Eureka!

    Après quelques petits profile, j'ai détecté où numpy ramait et j'ai modifié quelques lignes pour arriver à 79s (à peu près 20 de moins qu'avant), 83s sans utiliser psyco.

    L'erreur était très stupide, elle venait d'une autre fonction exécutée dans la boucle qui devait supprimer les valeurs négatives d'une ligne et les remplacer par des 0. Avec numarray, j'avais conservé une vieille boucle for, très maladroite, mais qui tournait vite et bien quand même:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    k = 0
    for point in ligne:
        if point <0:
            ligne[k] = 0
        k += 1
    Mais numpy n'a pas du tout aimé cette structure, et en changeant la boucle par:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    n.putmask(ligne, n.less(ligne, 0), 0)
    c'est 500 fois plus rapide (si on considère uniquement cette étape).

    Maintenant j'ai des lignes à modifier à d'autres endroits de mon programme, peut être pour des raisons similaires.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 0
    Dernier message: 05/10/2014, 16h32
  2. Réponses: 4
    Dernier message: 06/11/2007, 23h31
  3. [VBA-E]Decompte de temps sur un userform
    Par alex_95 dans le forum Macros et VBA Excel
    Réponses: 2
    Dernier message: 25/04/2007, 15h49
  4. ecrire en meme temps sur un fichier
    Par LesLemmings dans le forum Visual C++
    Réponses: 5
    Dernier message: 11/04/2007, 14h50
  5. VBA-E gagner du temps d'execution
    Par bernard38 dans le forum Macros et VBA Excel
    Réponses: 2
    Dernier message: 24/10/2006, 09h50

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo