Sto cercando di capire perché l'esecuzione di più parser in thread paralleli non acceleri l'analisi dell'HTML. Un thread esegue 100 attività due volte più velocemente di due thread con 50 attività ciascuno.Perché il multithreading non velocizza l'analisi dell'HTML con lxml?
Ecco il mio codice:
from lxml.html import fromstring
import time
from threading import Thread
try:
from urllib import urlopen
except ImportError:
from urllib.request import urlopen
DATA = urlopen('http://lxml.de/FAQ.html').read()
def func(number):
for x in range(number):
fromstring(DATA)
print('Testing one thread (100 job per thread)')
start = time.time()
t1 = Thread(target=func, args=[100])
t1.start()
t1.join()
elapsed = time.time() - start
print('Time: %.5f' % elapsed)
print('Testing two threads (50 jobs per thread)')
start = time.time()
t1 = Thread(target=func, args=[50])
t2 = Thread(target=func, args=[50])
t1.start()
t2.start()
t1.join()
t2.join()
elapsed = time.time() - start
print('Time: %.5f' % elapsed)
uscita sulla mia macchina CPU 4 core:
Testing one thread (100 job per thread)
Time: 0.55351
Testing two threads (50 jobs per thread)
Time: 0.88461
Secondo la FAQ (http://lxml.de/FAQ.html#can-i-use-threads-to-concurrently-access-the-lxml-api) due thread dovrebbero lavorare più velocemente di un thread.
A partire dalla versione 1.1, lxml libera la GIL (blocco interprete globale di Python) internamente durante l'analisi da disco e della memoria, fino a quando si utilizza il parser di default (che viene replicato per ogni thread) o creare un parser per ogni filo te stesso.
...
La più del vostro elaborazione XML si muove in lxml, tuttavia, più alto sarà il guadagno. Se la tua applicazione è vincolata dall'analisi e serializzazione XML, o da espressioni XPath molto selettive e XSLT complessi, la tua velocità su macchine multiprocessore può essere notevole.
Quindi, la domanda è: perché due thread sono più lenti di un thread?
Il mio ambiente: linux debian, lxml 3.3.5-1 + b1, stessi risultati su python2 e python3
BTW, il mio amico ha cercato di eseguire questo test su MacOS e ottenuto stessi tempi per uno e per due fili . Ad ogni modo, non è come dovrebbe essere secondo la documentazione (due thread dovrebbero essere il doppio più veloce).
UPD: Grazie agli extra. Ha indicato che è necessario creare un parser in ogni thread. Il codice aggiornato della funzione func
è:
from lxml.html import HTMLParser
from lxml.etree import parse
def func(number):
parser = HTMLParser()
for x in range(number):
parse(StringIO(DATA), parser=parser)
L'output è:
Testing one thread (100 jobs per thread)
Time: 0.53993
Testing two threads (50 jobs per thread)
Time: 0.28869
che è esattamente quello che volevo! :)
Mi stavo preparando a far apparire il GIL, ma sembra che tu lo abbia già pensato :). – Cyphase