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

Python Discussion :

Mesurer un temps d'execution avec multiprocessing & timeit


Sujet :

Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Inscrit en
    Février 2013
    Messages
    94
    Détails du profil
    Informations forums :
    Inscription : Février 2013
    Messages : 94
    Par défaut Mesurer un temps d'execution avec multiprocessing & timeit
    Bonjour à toutes et à tous,

    Je précise d'emblée que je suis très débutant en Python, mais que je m'instruit.
    Je cherche à mesurer finement le temps d’exécution d'une fonction() quelconque en fonction:
    • du nombre de processeurs utilisés
    • du pourcentage de données utilisé pour le calcul

    Plus précisément, je suis intéressé par la médiane des temps de calcul (pour un certain nombre de répétitions afin d'obtenir une estimation fine).
    Après avoir parcouru pas mal le net, j'arrive à produire un code minimal qui fonctionne plus ou moins. Je n'arrive toutefois pas à exécuter la fonction de manière synchrone. En fait j'utilise bien pool.apply() mais l’exécution semble être asynchrone car le print() est appelé avant même que le vecteur time[] ne soit rempli, ce que je ne m'explique pas (je comprends sans doute mal l'utilisation du package multiprocessing).

    J'ai également essayé d'autres manières de faire, notamment avec with Pool(processes=4) as pool: mais sans succès. Pouvez-vous m'éclairer?

    Un exemple minimal est fourni ici:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    # packages
    import pandas as pd
    import numpy as np
    import timeit
    from statsmodels.formula.api import ols
    import multiprocessing as mp
     
    # fonction à tester
    def fonctionq(x, y, sim):
        def boucle(x, y):
            x2 = np.random.randn(len(x))
            data = pd.DataFrame({'X': x2, 'Y': y})
            return (max(abs(ols('X ~ Y', data=data).fit())))
     
    # données
    D = [[3, 2], [6,10], [10,8], [12,15], [1,0], [3,5], [15,12], [12,24]]
    data = pd.DataFrame(D)
     
    # paramètres
    nbr = 150  # Nombre de simulations
    t = 100  # Nombre de répétitions
    percents = list(range(10, 101, 10))  # Pourcentage de données utilisées
     
    # Benchmark
    n = len(data)
    time = []
    for c in range(4):
        if __name__ == '__main__':
            pool = mp.Pool(c + 1)
            for p in percents:
                data2 = data.sample(int((p / 100) * n))
                time.append(np.median(timeit.repeat("pool.apply(fonctionq, args=(data2.iloc[:, 0], data2.iloc[:, 1], nbr))", globals=locals(), number=1, repeat=t)))
            pool.close()
            pool.join()
     
    print(time)
    Et donne par exemple comme résultat:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    []
    []
    []
    []
    []
    []
    []
    []
    []
    []
    [0.0005699500000000413, 0.0005136999999999503, 0.0005140000000000144, 0.0005127999999999799, 0.0005095999999998879, 0.0005151500000000198, 0.00041279999999999095, 0.00042349999999991006, 0.00042324999999998614, 0.00042204999999995163, 0.000545600000000146, 0.0004846499999999754, 0.00044050000000006584, 0.0004443000000000641, 0.000446949999999946, 0.0005021000000000608, 0.00045770000000011635, 0.000437600000000149, 0.00043624999999991587, 0.00042879999999989593, 0.0005235999999999574, 0.00043039999999994194, 0.00042809999999970927, 0.0004294499999999424, 0.00042729999999968626, 0.0004200499999997831, 0.0004208500000002502, 0.00042020000000064783, 0.00042019999999975965, 0.0004203500000001803, 0.0005252000000002255, 0.0004476499999999106, 0.00045410000000023487, 0.00044375000000007603, 0.00043755000000000877, 0.00043805000000007865, 0.0004298500000001759, 0.00043020000000026926, 0.0004302499999999654, 0.0004334000000003613]
    A noter que j'utilise Anaconda3 si ca peut être d'une quelconque utilité?

    Merci!

  2. #2
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 741
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 741
    Par défaut
    Salut,

    Déjà la fonctionq se résume à définir une fonction boucle qui n'est jamais appelée (et donc exécutée)... ce qui fait qu'elle se termine immédiatement.

    Pour le reste, faire du multiprocessing avec des dataframe pandas demande un certain niveau pour ne pas faire n'importe quoi. Si vous débutez, il y a plus simple...

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Membre confirmé
    Inscrit en
    Février 2013
    Messages
    94
    Détails du profil
    Informations forums :
    Inscription : Février 2013
    Messages : 94
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Salut,

    Déjà la fonctionq se résume à définir une fonction boucle qui n'est jamais appelée (et donc exécutée)... ce qui fait qu'elle se termine immédiatement.

    Pour le reste, faire du multiprocessing avec des dataframe pandas demande un certain niveau pour ne pas faire n'importe quoi. Si vous débutez, il y a plus simple...

    - W
    Merci pour ce retour. Tout à fait le fonction fonctionq() est un exemple je peux le modifier si besoin, mais ce ne me semble pas être le point essentiel de ma question.
    Je suis tout à fait preneur de faire plus simple, je récupère des données dans un csv donc j'ai choisi le dataframe car ca correspond à mon cas d'école. Qu'est-ce donc que faire plus simple si ce n'est pas trop indiscret? Je trouve énormément de tutoriaux qui pointent sur multiprocessing, il faut que je cherche une autre librairie?

  4. #4
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 741
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 741
    Par défaut
    Citation Envoyé par Grasshoper Voir le message
    Merci pour ce retour. Tout à fait le fonction fonctionq() est un exemple je peux le modifier si besoin, mais ce ne me semble pas être le point essentiel de ma question.
    Attendez là, si on lit votre code, il y aurait 50 questions/sujets à voir/aborder.... je me suis arrêté à la première...

    Citation Envoyé par Grasshoper Voir le message
    Je suis tout à fait preneur de faire plus simple, je récupère des données dans un csv donc j'ai choisi le dataframe car ca correspond à mon cas d'école.
    Qu'est-ce donc que faire plus simple si ce n'est pas trop indiscret?
    Séparez les sujets: une fonction avec un counter en paramètre qui se contente de faire un time.sleep du nombre de secondes correspondants (pour avoir du temps qui passe) suffit à être lancé via multiprocessing et valider qu'on mesure quelque chose.

    Après, une fois cette mécanique maîtrisée vous pourrez vous lancer dans des trucs plus touffus.

    Citation Envoyé par Grasshoper Voir le message
    Je trouve énormément de tutoriaux qui pointent sur multiprocessing, il faut que je cherche une autre librairie?
    Il n'y a pas de bons tutos sur le multiprocessing "en général" parce que c'est un avatar de l'informatique distribué qui "profite" de fonctionnalités système qu'on ne peut ignorer. "multiprocessing" met un voile là dessus pour permettre au programmeur Python d'en profiter mais... suppose qu'on en connaisse les détails.

    C'et un peu comme si vous partez à charger numpy pour faire du calcul matriciel sans avoir jamais fait d'algèbre linéaire.

    Citation Envoyé par Sve@r Voir le message
    Pour le reste, ton code fonctionne parfaitement chez-moi. Sauf que contrairement (je pense) à toi, je bosse sur un Linux, vrai OS multiprocessing. Alors que si tu es (par exemple) sous un OS type Windows (et Anaconda3 me laisse penser que c'est le cas), la parallélisation n'est arrivée que bien plus tard ce qui peut causer ces différences.
    Ici, la différence entre les Unix et Windows est dans la création des processus via forks pour les premiers et spawn pour le second. Et ce sont des fonctionnalités qui sont là depuis le début pour les 2 environnements.

    Ce qui a changé dans l'histoire, c'est la disponibilité de multi-processeurs et la possibilité d'écrire des applications partagées qui en profitent pour aller plus vite mais en dehors de Python, on préfèrera utiliser des threads (plutôt que des "processus").

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  5. #5
    Membre confirmé
    Inscrit en
    Février 2013
    Messages
    94
    Détails du profil
    Informations forums :
    Inscription : Février 2013
    Messages : 94
    Par défaut
    Merci à tous les deux pour vos retours, et en particulier pour avoir signalé que l'instruction if __name__ == "__main__" n'a rien à faire dans la boucle.
    Merci également pour la remarque sur number et repeat, j'avais effectivement vu dans la doc que ce sont deux paramètres différents, et c'est pour cette raison que j'ai mis number = 1, car je souhaite pouvoir avoir un meilleur contrôle sur la mesure, c-a-d prendre des indicateurs statistiques autres que la moyenne, min, max par exemple.
    J'ai réalisé deux autres versions de l'exemple, qui mettent mieux en valeur mon questionnement sur l'OP, c'est à dire la section multithreading.
    En particulier le fait que, malgré l'appel à un pool.join(), l'execution s'effectue de manière asynchrone, alors que je m'attendait plutôt à ce que le pool.join() termine les jobs en cours avant de passer à la suite (cad sortir de la boucle). Il y a là quelque chose que je ne comprend pas.

    La différence entre les 2 versions sont simplement l'emploi de timeit ou plus simplement de timeVersion timeit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    # packages
    import pandas as pd
    import numpy as np
    import timeit
    from statsmodels.formula.api import ols
    import multiprocessing as mp
     
    # fonction à tester
    def fonctionq(x, y):
        return pow(x,1/2) + pow(y,27/3)
     
    # données
    D = [[3, 2], [6,10], [10,8], [12,15], [1,0], [3,5], [15,12], [12,24],[3, 2],[3, 2], [6,10], [10,8], [12,15], [1,0], [3,5], [15,12], [12,24],[3, 2]]
    data = pd.DataFrame(D)
     
    # paramètres
    t = 10  # Nombre de répétitions
    percents = list(range(10, 101, 10))  # Pourcentage de données utilisées
     
    # Benchmark
    n = len(data)
    time = []
    if __name__ == '__main__':
        for c in range(4):
            pool = mp.Pool(c + 1)
            for p in percents:
                print('p={:d}, c={:d}'.format(p,c))
                data2 = data.sample(int((p / 100) * n))
                time.append(np.median(timeit.repeat("pool.apply(fonctionq, args=(data2.iloc[:, 0], data2.iloc[:, 1]))", globals=locals(), number=1, repeat=t)))
            pool.close()
            pool.join()
     
    print("je suis en dehors de la boucle")
    Version time:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    # packages
    import pandas as pd
    import numpy as np
    import timeit
    import time
    from statsmodels.formula.api import ols
    import multiprocessing as mp
     
    # fonction à tester
    def fonctionq(x, y):
        return pow(x,1/2) + pow(y,27/3)
     
    # données
    D = [[3, 2], [6,10], [10,8], [12,15], [1,0], [3,5], [15,12], [12,24],[3, 2],[3, 2], [6,10], [10,8], [12,15], [1,0], [3,5], [15,12], [12,24],[3, 2]]
    data = pd.DataFrame(D)
     
    # paramètres
    t = 10  # Nombre de répétitions
    percents = list(range(10, 101, 10))  # Pourcentage de données utilisées
     
    # Benchmark
    n = len(data)
    timed = []
    if __name__ == '__main__':
        for c in range(4):
            pool = mp.Pool(c + 1)
            for p in percents:
                print('p={:d}, c={:d}'.format(p,c))
                data2 = data.sample(int((p / 100) * n))
                rep = []
                for k in range(t):
                    ts = time.time()
                    pool.apply(fonctionq, args=(data2.iloc[:, 0], data2.iloc[:, 1]))
                    rep.append(time.time() - ts)
                timed.append(np.median(rep))
            pool.close()
            pool.join()
     
    print("je suis en dehors de la boucle")

  6. #6
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 741
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 741
    Par défaut
    Salut,

    Citation Envoyé par Grasshoper Voir le message
    En particulier le fait que, malgré l'appel à un pool.join(), l'execution s'effectue de manière asynchrone, alors que je m'attendait plutôt à ce que le pool.join() termine les jobs en cours avant de passer à la suite (cad sortir de la boucle). Il y a là quelque chose que je ne comprend pas.
    L'histoire d'un Pool est de découper le boulot en plusieurs paquets +/- identiques et d'attendre le résultat de chaque bout (pour en faire quelque chose).

    Là vous appliquez une fonction à un sous ensemble du Dataframe (qui n'est pas partagé) et ignorez le résultat. Or c'est la récupération de ces résultats qui rendent les Pool intéressants (et qui signalent que le boulot est terminé).

    Essayer d'arriver à faire faire un boulot raisonnable avant de mesurer le temps que çà prend plutôt que de tout mélanger en croisant les doigts pour que çà marche.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  7. #7
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 832
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par Grasshoper Voir le message
    Je précise d'emblée que je suis très débutant en Python, mais que je m'instruit.
    Ouais ben déjà je suis assez impressionné par tes tests et expériences !!! Face à certains spécimens qu'on a eu ces derniers jours dans le forum, c'est vraiment rafraichissant d'arriver ici et voir ce que tu nous montres. Un code full fonctionnel, rien que ça ça vaut le détour.

    Toutefois déjà une erreur qui me saute aux yeux: c'est ce if __name__ == "__main__" placé dans une boucle dans laquelle ce test ne peut pas changer. S'il est vrai hors de la boucle, alors il sera forcément vrai dans toutes les itérations de la boucle. Donc autant le sortir de la boucle, ce sera autant d'instructions à faire en moins (surtout quand on veut faire des tests de performances).
    Pour le reste, ton code fonctionne parfaitement chez-moi. Sauf que contrairement (je pense) à toi, je bosse sur un Linux, vrai OS multiprocessing. Alors que si tu es (par exemple) sous un OS type Windows (et Anaconda3 me laisse penser que c'est le cas), la parallélisation n'est arrivée que bien plus tard ce qui peut causer ces différences. De plus on a vu ici des soucis Python qui se passaient avec Anaconda mais qui ne se passaient pas quand on utilisait le programme Python directement (je ne dis pas que Anaconda est buggué, juste que si tu veux regarder plus en détail il vaudrait mieux se passer d'intermédiaires).

    Une remarque aussi sur les paramètres "repeat" et "number" de timeit car il faut savoir les distinguer. Les deux ont le même rôle, à savoir répéter les opérations. Donc si tu demandes par exemple un "repeat=4" et "number=5" tu auras au final 20 appels à la fonction test.
    Sauf que les "n" appels de number font l'objet d'une seule mesure totale. Et ce sont les "repeat" qui eux sont mesurés à chaque fois. Donc avec un "repeat=4" et "number=5" tu auras 4 mesures de chaque fois 5 appels à la fonction. Et ensuite timeit te donnera le minimum, le maximum et la moyenne de ces 4 mesures.
    Donc c'est là qu'on peut faire les réglages les plus fins pour mesurer les choses.

    Citation Envoyé par Grasshoper Voir le message
    Qu'est-ce donc que faire plus simple si ce n'est pas trop indiscret?
    Je m'étais amusé il y a quelques temps à comparer les différentes méthodes pour copier une liste, tu peux déjà regarder ce code....

    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    #!/usr/bin/env python3
    # coding: utf-8
     
    import random
    import timeit
    from functools import partial
     
    # Initialisation random
    random.seed()
     
    # Les fonctions à tester
    fct={
    	"slice" : lambda l: l[:],
    	"copy" : lambda l: l.copy(),
    	"list" : lambda l: list(l),
    }
     
    # Les données à traiter
    data=list(range(10_000))
     
    # Le nombre de répétitions (les moyennes se feront sur cette valeur)
    repeat=20
     
    # Appel des fonctions dans un ordre aléatoire et affichage du chrono
    print("start=%d, repeat=%d" % (len(data), repeat))
    for (k, v) in random.sample(fct.items(), len(fct)):
    	t=timeit.Timer(partial(v, data)).repeat(repeat=repeat, number=500_000)
    	print("%s: min=%f, max=%f, avg=%f" % (k, min(t), max(t), sum(t)/len(t)))
    # for

    Il te donne un résultat en 12 minutes (temps que tu peux affiner via ce "number" dont j'ai parlé). Juste qu'il n'est pas multiproc...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

Discussions similaires

  1. mesure de temps d'execution
    Par jamalmoundir dans le forum C
    Réponses: 6
    Dernier message: 29/05/2006, 12h59
  2. Mesurer le temps d'execution d'un thread?
    Par kobe dans le forum Concurrence et multi-thread
    Réponses: 20
    Dernier message: 12/05/2006, 12h05
  3. [Stratégie] Mesurer le temps d'exécution d'une requête
    Par nice dans le forum Général Java
    Réponses: 5
    Dernier message: 29/01/2006, 17h53
  4. limit et temps d'execution avec oracle et PHP
    Par dor_boucle dans le forum Oracle
    Réponses: 20
    Dernier message: 10/12/2005, 14h31
  5. [Test][Perf]Mesure du temps d'execution différente 2 fois de suite
    Par debdev dans le forum Tests et Performance
    Réponses: 11
    Dernier message: 22/07/2005, 12h04

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