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 :

threads ou autres solutions ?


Sujet :

Python

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Août 2004
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 38
    Par défaut threads ou autres solutions ?
    Bonjour,

    Comment procéderiez vous pour accélérer mon programme dont le schéma est le suivant :
    à partir d'une liste de plusieurs milliers d'éléments, je lance une boucle for pour scanner ceux-ci. je récupère, via le module urllib, un fichier txt sur le net. je le traite pour extraire les données qui m'intéressent et je les stocke dans un fichier.

    il me faut presque 30 min pour récupérer les infos de ma liste
    faut il que je m'intéresse aux threads ?
    y aurait il d'autres solutions ?

    merci

  2. #2
    Membre Expert
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Par défaut salut. questions
    je lance une boucle for pour scanner ceux-ci
    qu'entends-tu par scanner les éléments de ta liste ?
    a priori, rien n'est plus rapide qu'une list comprehension pour traiter une liste.

    que retires-tu du scanning de ta liste ? une donnée ou plusieurs ? je demande ça parce que tu dis qu'après avoir scanné ta liste, tu récupère UN fichier txt sur le net

    qu'est ce que tu entends par fichier txt d'ailleurs ?
    s'agit-il d'un code source de page web ?

    dans quoi mets-tu ton fichier récupéré sur le net ?
    je conseille de mettre dans une liste personnellement, ça facilite le traitement, avec des list comprehension justement

    il me faut presque 30 min pour récupérer les infos de ma liste
    tu parles uniquement du scanning initial ou de l'ensemble du processus ?

    les threads, je ne maitrise pas du tout

    Quelle puissance ton ordinateur aussi ?

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Août 2004
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 38
    Par défaut
    qu'entends-tu par scanner les éléments de ta liste ?
    c'est juste le fait de les traiter 1 par 1 avec une boucle for.

    que retires-tu du scanning de ta liste ? une donnée ou plusieurs ?
    j'en retire une seule chaine correspondant à un code

    qu'est ce que tu entends par fichier txt d'ailleurs ?
    s'agit-il d'un code source de page web ?
    je télécharge un fichier txt qui contient des infos actualisées sur le code
    non pas de code source

    tu parles uniquement du scanning initial ou de l'ensemble du processus ?
    l'ensemble du processus


    en fait je pensais aux thread (que je n'ai jamais utilisé) pour traiter plusieurs éléments de ma liste en même temps. je ne sais pas du tout si c'est adapté à mon cas (listes).

  4. #4
    Membre Expert
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Par défaut
    Scanner, oui bon j'avais compris que tu passes en revue les éléments les uns après les autres. Mais je voulais savoir si tu avais plusieurs tests ou un seul à mener sur chaque élément, pour trouver un seul ou plusieurs éléments,etc....des renseignements un peu plus renseignant sur ce que tu fais.

    Je ne sais pas utiliser les threads, donc je ne voudrais pas t'induire en erreur, mais avec ce que tu dis, il me semble qu'il faut plutôt que tu cherches du coté des list comprehension que des threads.

    Comme tu cherches à identifier un seul élément dans ta liste, je suppose que le test de détection est assez simple et je pense qu'une ou plusieurs list comprehension en cascade devrait faire le travail très rapidement.
    Si le test est un peu sophistiqué il te faudra recourir aux regular expressions.
    Maintenant si tu n'en dis pas plus, on ne peut pas beaucoup t'aider.

    Exemple de list comprehension:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    extr = [ item[23:34] for item in liste if '890RTZ' in item ]

    Il serait bon aussi de déterminer les temps d'exécution des deux parties de ton programme:
    - la recherche de chaine dans ta liste
    - l'obtention du fichier txt sur le net et son traitement
    De facon sommaire, tu peux faire ça avec des clock() placés avant-au milieu et après ton programme.

  5. #5
    Membre chevronné Avatar de dapounet
    Profil pro
    Étudiant
    Inscrit en
    Juillet 2007
    Messages
    469
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juillet 2007
    Messages : 469
    Par défaut
    Bonjour,

    Citation Envoyé par hercule4 Voir le message
    en fait je pensais aux thread (que je n'ai jamais utilisé) pour traiter plusieurs éléments de ma liste en même temps. je ne sais pas du tout si c'est adapté à mon cas (listes).
    Les threads de Python ont une mauvaise réputation, voilà ce qu'on peut lire sur Wikipedia par exemple :
    CPython uses a GIL to allow only one thread to execute at a time while the Stackless Python threads are independent of the OS and can run concurrently.
    http://en.wikipedia.org/wiki/Python_...ming_language)

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Août 2004
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 38
    Par défaut
    désolé eyquem, je me suis mal expliqué.
    je reprends chacune de mes étapes :

    j'ai au départ:
    1/ une base mysql qui regroupe des noms de laboratoires et des codes spécifiques à chaque labo (ex labo trucmuche a un code qui lui est propre z4521).
    2/ un répertoire contenant pour chaque labo un fichier de données ex : z4521.txt

    mon but est de mettre à jour ces fichiers txt en récupérant pour chaque journée les données d'activité des labos.

    voilà comment je procède actuellement :
    1/ je crée une liste contenant tous les codes labos à partir de ma base mysql
    ex [z4521, z2251, z3678,...]

    2/ je parcours cette liste élément par élément.

    3/ pour chaque élément, je récupère un fichier txt sur le serveur des labos via urllib. le lien est facile puisque il est de la forme : http:\\www.labocentral.com\activite_jour\z4521.txt
    puis je parse le fichier pour récupérer les infos de l'activité du jour.
    je mets alors à jour mon fichier de stockage

    j'espère avoir été plus explicite

  7. #7
    Membre Expert
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Par défaut
    Ah ça devient beaucoup plus clair, en effet.

    Et j'avais mal compris: tu télécharges UN fichier txt mais PAR CODE. Donc grosso modo autant de fichiers txt que de codes labo dans la liste. Si la liste est longue, le temps de 30 minutes devient plus compréhensible.

    Quelques remarques qui me viennent tout de suite:

    - si tu crées une liste des codes labo à partir de la base mysql alors que la liste des labos ne change pas d'un jour à l'autre, c'est une perte de temps.
    Je ne connais pas bien mysql, mais d'aprés ce que tu dis, je pense qu'il y aurait intérêt
    = soit à réorganiser la BDD pour avoir une base constituée uniquement des codes labo + un répertoire qui donnerait pour chaque code le nom du labo et les renseignements le concernant + le répertoire actuel des fichiers de données
    = soit de créer un répertoire supplémentaire qui contiendrait ces codes
    De cette façon au lieu de traiter à chaque fois la base actuelle, la liste des codes serait immédiatement disponible dans la base ou le répertoire supplémentaire

    - actuellement tu fais
    1/ création de liste, en parcourant la base
    2/ reparcours de la liste
    Donc deux parcours sur des données similaires
    Il vaudrait mieux faire tout de suite à partir de chaque code trouvé la recherche et le téléchargement du fichier txt correspondant. AMA

    - la façon dont tu parses le fichier txt obtenu sur le web est à voir aussi. Le code n'est peut être pas optimal. Faudrait voir.
    Tu parses avec un parseur tout fait ou c'est toi qui a écrit le parseur ?
    Dans quoi mets tu le fichier txt obtenu sur le web avant de le traiter ?

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Août 2004
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 38
    Par défaut
    - si tu crées une liste des codes labo à partir de la base mysql alors que la liste des labos ne change pas d'un jour à l'autre, c'est une perte de temps.
    Je ne connais pas bien mysql, mais d'aprés ce que tu dis, je pense qu'il y aurait intérêt
    = soit à réorganiser la BDD pour avoir une base constituée uniquement des codes labo + un répertoire qui donnerait pour chaque code le nom du labo et les renseignements le concernant + le répertoire actuel des fichiers de données
    = soit de créer un répertoire supplémentaire qui contiendrait ces codes
    De cette façon au lieu de traiter à chaque fois la base actuelle, la liste des codes serait immédiatement disponible dans la base ou le répertoire supplémentaire
    la liste évolue, j'ai un script qui met à jour ces labos et la base contient toutes les infos des labos (adresse, contact, tel,....)
    la base ne me semble pas un problème puisque la requête pour obtenir les codes prend quelques secondes.

    effectivement je peux améliorer ma façon de parser : les données ne sont pas compliquées à extraire. j'utilise un split et je récupère les données avec
    des expressions régulières. il y a sans doute un gain de temps à affiner le code.

    par contre la récupération du fichier est l'élément qui ralentit le plus mon programme. c'est sur ce point que j'aimerai l'améliorer.

  9. #9
    Membre Expert
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Par défaut
    la liste évolue, j'ai un script qui met à jour ces labos et la base contient toutes les infos des labos (adresse, contact, tel,....)
    la base ne me semble pas un problème puisque la requête pour obtenir les codes prend quelques secondes.
    Bon, c'est déjà ça. Ça aurait été bien de le savoir dès le début.
    Donc la demie heure d'actualisation est occupée quasi exclusivement à télécharger et parser. Je n'aurais pas dû en douter, c'est évident que traiter une base pour obtenir une liste prend très peu de temps. Quelques secondes me parait déjà trop d'ailleurs.

    Sans le détail d'un fichier txt à parser et ton code, on ne peut rien dire de plus.

    Télécharger une page, ce n'est pas très long si la connexion est bonne. Pour moi ça prend environ 1 seconde. Je ne vois pas ce qu'on peut y améliorer.
    Tu fais un sock =urllib.urlopen('http:\\www.labocentral.com\activite_jour\z4521.txt') ?

    Je ne vois pas ce que je peux apporter de plus.

  10. #10
    Membre Expert
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Par défaut
    Je viens de tester urllib avec des thread et le gain n'est pas négligeable:
    J'ai un ping assez constant de 125 ms sur un serveur.
    pour récupérer 100 fichiers d'une trentaine d'octets avec un seul thread: 30 secondes
    pour récupérer 400 de ces fichiers avec 40 threads: 8 secondes (soit 15 fois plus rapide)

  11. #11
    Membre averti
    Profil pro
    Inscrit en
    Août 2004
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 38
    Par défaut
    Merci Eyquem pour le temps que tu as pris pour me répondre.

    dividee, ton test m'intéresse beaucoup. Peux tu me décrire la façon de procéder pour utiliser les threads dans mon cas ? est ce que je dois splitter ma liste en x morceaux et attribuer chaque morceau à un thread ?

  12. #12
    Membre Expert
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Par défaut de rien, de rien
    Je regrette simplement que tu n'aies pas expliqué mieux ton problème dès le départ. En réalité, croyant que tu télécharges UN seul fichier, je m'étais orienté vers l'idée que c'était la création de la liste des codes labo qui était longue.
    Mais en fait c'est ton idée exprimée dans ton premier post qui est la bonne: faire appel aux threads, et dividee le confirme bien avec son test.
    Je vais essayer de m'intéresser aux threads, j'attends aussi la réponse de dividee.

  13. #13
    Membre Expert
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Par défaut
    Le plus simple c'est effectivement de découper la liste en X morceaux attribués à X threads; le seul défaut c'est que certains threads finiront plus vite que d'autres et ce n'est donc pas optimal, mais c'est déjà pas mal.

    Pour faire mieux il faudrait découper la liste en plus petits morceaux (la taille optimale serait à déterminer; ça pourrait être un seul élément par morceau) et créer un pool de thread; tu assignes un morceau à un thread et dès qu'il a fini tu lui en assigne un autre. Mais c'est plus complexe...

    Pour retourner les résultats, chaque thread pourrait écrire dans une Queue (module standard du même nom); c'est le plus simple pour gérer l'exclusion mutuelle. Bien entendu, les résultats ne seront pas dans l'ordre dans la Queue.

    Maintenant que j'y pense on peut aussi mettre dès le départ les éléments de la liste (url) dans une Queue et les threads s'y serviront... Voici un exemple:
    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
     
    from urllib import urlopen
    import threading
    from Queue import Queue, Empty
    from time import time
     
    NUM_THREADS = 40
    start = time()
    q_in = Queue(0)
    q_out = Queue(0)
     
    # on remplit q_in avec les url (ici c'est juste un exemple pour tester)
    for i in xrange(400):
        q_in.put("http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing=%d" % i)
     
    def getfiles():
        # fonction exécutée par les threads
        # ici je me contente de sauver le contenu avec l'url dans q_out
        try:
            while True:
                url = q_in.get_nowait()
                f = urlopen(url)
                q_out.put((url,f.read()))
                f.close()
        except Empty:
            # q_in est vide; on a terminé
            pass
     
    # on crée et on démarre les threads
    for i in xrange(NUM_THREADS):
        t = threading.Thread(target = getfiles)
        t.start()
     
    count = 0
    # tant qu'il y a des threads actifs ou des résultats disponibles
    while threading.activeCount() > 1 or not q_out.empty():
        try:
            # on retire les résultats de la queue
            # on attend au plus 500 ms si la queue est vide
            # le timeout est nécessaire pour éviter une race condition
            url, contents = q_out.get(True, 0.5)
            count += 1
            print url, ':', contents
        except Empty:
            pass
     
    print "%d fichiers récupérés en %f secondes" % (count, time() - start)

  14. #14
    Membre éclairé Avatar de ctiti60
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2007
    Messages
    75
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Octobre 2007
    Messages : 75
    Par défaut Quelques remarques
    Bonjour à tout le monde !

    Comme eyquem, j'aurais tendance à dire qu'il faudrait éviter de recréer tes listes au début de ton programme. Peut être que tu ne perds pas beaucoup de temps, mais moins on en fait mieux c'est en général. Tu pourrais les stocker dans une vue dans la base ou alors régénérer un fichier à chaque changement par exemple.

    Sinon, à propos de la partie qui prend du temps. Tu dis:

    3/ pour chaque élément, je récupère un fichier txt sur le serveur des labos via urllib. le lien est facile puisque il est de la forme : http:\\www.labocentral.com\activite_jour\z4521.txt
    puis je parse le fichier pour récupérer les infos de l'activité du jour.
    je mets alors à jour mon fichier de stockage
    Est-ce que tous les fichiers se trouvent au même endroit ?
    Et comment sont tes fichiers : est-ce que l'info qui t'intéresse se trouvent à la fin du fichier ?

  15. #15
    Membre averti
    Profil pro
    Inscrit en
    Août 2004
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 38
    Par défaut
    Je n'ai pas eu le temps d'appliquer la méthode de dividee (je le ferai ce we et vous tiendrais au courant)

    Est-ce que tous les fichiers se trouvent au même endroit ?
    non il existe une dizaine de dossiers différents.


    Comme eyquem, j'aurais tendance à dire qu'il faudrait éviter de recréer tes listes au début de ton programme. Peut être que tu ne perds pas beaucoup de temps, mais moins on en fait mieux c'est en général. Tu pourrais les stocker dans une vue dans la base ou alors régénérer un fichier à chaque changement par exemple.
    je suis d'accord avec toi mais gagner 2 secondes sur un programme qui tourne 45min pour exécuter toutes ses taches, ça me paraît négligeable dans un 1er temps.


    Et comment sont tes fichiers : est-ce que l'info qui t'intéresse se trouvent à la fin du fichier ?
    non j'ai besoin de 80% des données du fichier

    merci de votre aide

  16. #16
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Je rebondis sur l'idée de dividee de "threads qui se servent tout seuls dans une queue": j'ai fait quelque chose comme ça il y a quelque temps, et c'est disponible ici: http://python.jpvweb.com/mesrecettes...d_tableaublanc.

    L'idée est d'avoir un "tableau blanc" qui serve de communication de messages entre les threads. Dans mon exemple simplifié, l'un des threads est fournisseur de messages (qui portent une expression à calculer), et chacun des autres threads:
    - cherche si un message est disponible dans le tableau,
    - si oui le prend en compte en le marquant comme pris,
    - calcule l'expression portée par le message,
    - donne le résultat et marque le message comme résolu
    - recommence en cherchant un nouveau message disponible.

    Le programme principal pilote l'ensemble, récupère les messages résolus, les efface du tableau et établit des statistiques.

    Le tableau blanc est basé sur une pile FIFO, mais il est nécessaire de pouvoir retrouver et manipuler des messages à l'intérieur de la pile.

    Dans cet exemple, les threads sont tous créés en début de programme, et donc ne s'arrêtent jamais. Mais on pourrait imaginer que:
    - le pilote (=le programme principal) puisse créer les threads au fur et à mesure des besoins (taille de la file d'attente de la pile par exemple),
    - que les threads puissent se "suicider" en cas d'attente de nouveau message de plus de 5 secondes par exemple,
    - il ne reste plus au programme principal que de détecter quand il n'y a plus rien à faire (tableau vide et aucun thread actif par exemple).

    L'idée du tableau blanc permet de comprendre comment fonctionne le verrou: tous les threads ont les même droits de lecture/écriture des messages, mais seul celui qui a le "marqueur" (=le verrou) peut le faire à un moment donné.

    Tyrtamos

Discussions similaires

  1. Réponses: 9
    Dernier message: 01/06/2006, 23h34
  2. [Tkinter] faire disparaitre un label ou autre solution
    Par thierry_b dans le forum Tkinter
    Réponses: 3
    Dernier message: 06/01/2006, 17h22
  3. Etat : fond de page ou autre solution ?
    Par Mulele dans le forum IHM
    Réponses: 3
    Dernier message: 11/11/2005, 20h15
  4. Autre solution que IBEvents
    Par aallal dans le forum Bases de données
    Réponses: 3
    Dernier message: 17/10/2005, 13h12
  5. segment memoire partagee, thread, ou autre?
    Par Pouic dans le forum POSIX
    Réponses: 9
    Dernier message: 26/10/2004, 18h54

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