multiprocessing.Pool () plus lent que d'utiliser simplement des fonctions ordinaires

(Cette question est sur la façon de faire de multitraitement.Piscine() exécuter du code plus rapide. J'ai finalement résolu, et la solution finale peut être trouvé au bas de cet article.)

Question D'Origine:

Je suis en train d'utiliser Python pour comparer un mot avec beaucoup d'autres mots dans une liste et de récupérer une liste de la plupart de ses semblables. Pour le faire, je suis en utilisant le difflib.get_close_matches fonction. Je suis relativement nouvelle et puissante de Windows 7 ordinateur Portable, avec Python 2.6.5.

Ce que je veux est d'accélérer le processus de comparaison parce que ma liste de comparaison des mots est très long et je dois répéter le processus de comparaison à plusieurs reprises. Quand j'ai entendu parler du multitraitement module, il semble logique que si la comparaison peut être divisé en travailleur tâches et d'exécuter simultanément (et donc de faire usage de la puissance de la machine en échange pour plus de rapidité) ma tâche de comparaison serait en finir plus vite.

Cependant, même après avoir essayé de nombreuses façons différentes, et utilisé des méthodes qui ont été présentées dans les docs et suggéré dans les messages du forum, la Piscine méthode semble être incroyablement lent, beaucoup plus lent que de simplement l'exécution de l'original get_close_matches de la fonction sur l'ensemble de la liste à la fois. Je voudrais de l'aide pour comprendre pourquoi, Piscine (en) est si lent et si je suis à l'utiliser correctement. Im seulement à l'aide de cette comparaison de chaînes de caractères scénario comme exemple parce que c'est l'exemple le plus récent que je pouvais penser de l'endroit où j'ai été incapable de comprendre ou d'obtenir le multitraitement de travail, plutôt que contre moi. Ci-dessous est juste un exemple de code à partir de la difflib scénario montrant les différences de temps entre l'ordinaire et le Commun des méthodes:

from multiprocessing import Pool
import random, time, difflib

# constants
wordlist = ["".join([random.choice([letter for letter in "abcdefghijklmnopqersty"]) for lengthofword in xrange(5)]) for nrofwords in xrange(1000000)]
mainword = "hello"

# comparison function
def findclosematch(subwordlist):
    matches = difflib.get_close_matches(mainword,subwordlist,len(subwordlist),0.7)
    if matches <> []:
        return matches

# pool
print "pool method"
if __name__ == '__main__':
    pool = Pool(processes=3)
    t=time.time()
    result = pool.map_async(findclosematch, wordlist, chunksize=100)
    #do something with result
    for r in result.get():
        pass
    print time.time()-t

# normal
print "normal method"
t=time.time()
# run function
result = findclosematch(wordlist)
# do something with results
for r in result:
    pass
print time.time()-t

Le mot est "bonjour", et la liste de mots dans laquelle vous trouverez à proximité les matchs de est une de 1 million longue liste de 5 au hasard rejoint caractères (uniquement à des fins d'illustration). J'utilise 3 cœurs de processeur et de la carte de fonction avec un chunksize de 100 (listitems être procesed par travailleur, je pense??) (J'ai aussi essayé chunksizes de 1000 et 10 000, mais il n'y a pas de différence réelle). Notez que dans les deux méthodes que j'démarrer la minuterie juste avant l'appel de ma fonction et à la fin juste après avoir bouclé dans les résultats. Comme vous pouvez le voir ci-dessous le calendrier résultats sont clairement en faveur de l'origine non-Piscine méthode:

>>> 
pool method
37.1690001488 seconds
normal method
10.5329999924 seconds
>>> 

La Piscine méthode est presque 4 fois plus lente que la méthode d'origine. Est-il quelque chose qui me manque ici, ou peut-être un malentendu à propos de la façon dont la mise/multitraitement œuvres? Je soupçonne qu'une partie du problème pourrait être que la carte fonction retourne Aucun et ajoute de milliers de unneccessary des éléments à la resultslist même si je ne veux matchs réels pour être renvoyé vers les résultats et de l'avoir écrit comme tel dans la fonction. Ce que je comprends c'est juste la façon dont la carte fonctionne. J'ai entendu parler de quelques autres fonctions comme un filtre qui ne recueille que les non-résultats Faux, mais je ne crois pas que le multitraitement/la Piscine prend en charge la méthode de filtrage. Existe-il d'autres fonctions en plus de la carte/imap dans le multitraitement module qui pourrait m'aider en retour de ce que ma fonction retourne? Appliquer la fonction est plus pour donner plusieurs arguments tel que je le comprends.

Je sais qu'il y a aussi la fonction imap, j'ai essayé, mais sans tout le temps des améliorations. La raison en est la même raison pourquoi j'ai eu des problèmes à comprendre ce qui est si grande sur le module itertools, soi-disant "ultra-rapide", que j'ai remarqué, c'est vrai pour l'appel de la fonction, mais dans mon expérience et de ce que j'ai lu c'est parce que l'appel de la fonction n'est pas réellement faire aucun calcul, donc quand il est temps de parcourir les résultats de recueillir et de les analyser (sans lequel il n'y aurait pas de point dans l'appel de la cuntion) il faut tout autant, voire parfois plus de temps qu'une simple utilisation de la version normale de la fonction straightup. Mais je suppose que c'est pour un autre post.

De toute façon, hâte de voir si quelqu'un peut me pousser dans la bonne direction ici, et j'apprécie vraiment que toute aide sur ce. Je suis plus intéressé par la compréhension de multitraitement en général que pour obtenir cet exemple fonctionne, mais il serait utile avec quelques exemple de solution code des suggestions pour faciliter ma compréhension.

La Réponse:

Semble que le ralentissement a eu à faire avec la lenteur des temps de démarrage de processus supplémentaires. Je ne pourrais pas obtenir le .Piscine() afin d'être assez rapide. Ma solution finale pour le rendre plus rapide a été manuellement répartir la charge de travail de la liste, utilisez plusieurs .Processus de() à la place de .Piscine(), et retourner les solutions dans une File d'attente. Mais je me demande si peut-être le plus crucial des changements sont susceptibles d'avoir été le fractionnement de la charge de travail en ce qui concerne les principales mot à rechercher plutôt que les mots pour comparer avec, peut-être parce que le difflib fonction de recherche est déjà si vite. Voici le nouveau code de l'exécution 5 processus en même temps, et s'est avéré sur x10 plus rapide que l'exécution d'un code simple (6 secondes vs 55 secondes). Très utile pour rapidement les recherches floues, sur le dessus de la vitesse à laquelle difflib l'est déjà.

from multiprocessing import Process, Queue
import difflib, random, time

def f2(wordlist, mainwordlist, q):
    for mainword in mainwordlist:
        matches = difflib.get_close_matches(mainword,wordlist,len(wordlist),0.7)
        q.put(matches)

if __name__ == '__main__':

    # constants (for 50 input words, find closest match in list of 100 000 comparison words)
    q = Queue()
    wordlist = ["".join([random.choice([letter for letter in "abcdefghijklmnopqersty"]) for lengthofword in xrange(5)]) for nrofwords in xrange(100000)]
    mainword = "hello"
    mainwordlist = [mainword for each in xrange(50)]

    # normal approach
    t = time.time()
    for mainword in mainwordlist:
        matches = difflib.get_close_matches(mainword,wordlist,len(wordlist),0.7)
        q.put(matches)
    print time.time()-t

    # split work into 5 or 10 processes
    processes = 5
    def splitlist(inlist, chunksize):
        return [inlist[x:x+chunksize] for x in xrange(0, len(inlist), chunksize)]
    print len(mainwordlist)/processes
    mainwordlistsplitted = splitlist(mainwordlist, len(mainwordlist)/processes)
    print "list ready"

    t = time.time()
    for submainwordlist in mainwordlistsplitted:
        print "sub"
        p = Process(target=f2, args=(wordlist,submainwordlist,q,))
        p.Daemon = True
        p.start()
    for submainwordlist in mainwordlistsplitted:
        p.join()
    print time.time()-t
    while True:
        print q.get()

source d'informationauteur Karim Bahgat