2017-07-06 3 views
-4

Temps nécessaire pour le code séquentiel (seq.py),Pourquoi GIL n'autorise pas le code thread sous-performant?

import time 

def countDown(n): 
    while n > 0: 
     n -= 1 

n = 50000000 
start = time.time() 
countDown(n) 
end = time.time() 
print(end-start) 

est,

$ python3.6 seq.py 
4.209718227386475 
$ python3.6 seq.py 
4.007786750793457 
$ python3.6 seq.py 
4.0265843868255615 
$ 

temps pris pour la version filetée (usingThreads.py),

from threading import Thread 
import time 
def countDown(n): 
    while n > 0: 
     n -= 1 


n = 50000000 

t1 = Thread(target=countDown, args=(n//2,)) 
t1.daemon = True 
t2 = Thread(target=countDown, args=(n//2,)) 
t2.daemon = True 

start = time.time() 
t1.start() 
t2.start() 
t1.join() 
t2.join() 
end = time.time() 
print(end-start) 

est

$ python3.6 usingThreads.py 
4.1083903312683105 
$ python3.6 usingThreads.py 
4.093154668807983 
$ python3.6 usingThreads.py 
4.092989921569824 
$ python3.6 usingThreads.py 
4.116031885147095 
$ 

$ nproc 
4 
$ 

interpréteur Python ne doit pas permettre à des fils liés CPU pour libérer GIL.

Attendons usingThreads.py de prendre plus de temps d'exécution que seq.py, parce que,

1) Tout un fil est en cours d'exécution à un moment, en dépit de 4 cœurs

2) Le temps pris pour tentatives infructueuses d'acquérir GIL de thread1 par thread2 (et vice versa) devrait ajouter un retard dans l'exécution.

Edit:

Avec n=500000000

$ python3.6 seq.py 
40.22602105140686 
$ python3.6 seq.py 
40.510098457336426 
$ python3.6 seq.py 
40.04688620567322 
$ 
$ python3.6 usingThreads.py 
40.91394829750061 
$ python3.6 usingThreads.py 
42.30081081390381 
$ python3.6 usingThreads.py 
41.328694581985474 

Question:

Pourquoi usingThread.py une meilleure performance que seq.py?

+0

Il me semble que l'utilisation de threads est généralement plus lente, mais seulement plus rapide que l'une des seq.py fois. – barny

+0

Je suppose que c'est à cause de votre utilisation de 'join()' dans la version filetée - qui supprime probablement la plupart des frais généraux généralement associés à l'utilisation des threads. – martineau

+0

@martineau C'est la seule façon que je connaisse d'attendre que les threads soient terminés, puis de calculer le temps nécessaire pour terminer le travail. Comment éviter 'join()'? – overexchange

Répondre

0

les deux versions du code ont fait la même quantité de travail, donc cela a pris presque le même laps de temps (les deux ont compté 50000000 fois).

le gil fait en sorte qu'ils ne courent pas en parallèle (donc la version des threads n'est pas plus rapide) mais le surcoût des commutateurs de contexte est relativly petit, donc vous avez presque le même résultat.

il y a une explication ici http://www.dabeaz.com/python/UnderstandingGIL.pdf

il utilise le même exemple que vous, et dans cette présentation, il a obtenu une version de fil plus lent quand il a utilisé un ordinateur avec plus de 1 CPUs, et il explique assez bien Lorsque vous utilisez plus de 1 cpus, vous obtenez plus de temps (plus de tentatives de changement de contexte), ce qui ralentit votre programme.

+0

Version à filetage plus lent? Les threads python3.6 fonctionnent mieux – overexchange

+0

Seule la première exécution de seq.py est plus lente que les threads d'utilisation, les deux autres sont plus rapides. Vous avez besoin d'un plus grand échantillon pour qu'il soit plus clair. – barny

+0

le temps moyen pour 'seq.py' dans vos courses est' 4.081363121668498', le temps moyen pour l'enfileur est '4.102641701698303' – DorElias