我试图理解为什么在并行线程中运行多个解析器不会加速解析HTML。一个线程完成100个任务的速度是两个线程的两倍,每个线程有50个任务。
这是我的代码:
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)
我的4核CPU机器上的输出:
Testing one thread (100 job per thread)
Time: 0.55351
Testing two threads (50 jobs per thread)
Time: 0.88461
根据FAQ(http://lxml.de/FAQ.html#can-i-use-threads-to-concurrently-access-the-lxml-api),两个线程的工作速度应该比一个线程快。
从1.1版开始,只要您使用默认解析器(为每个线程复制)或自己为每个线程创建解析器,lxml在从磁盘和内存进行解析时会在内部释放GIL(Python的全局解释器锁)
...
您的XML处理越多地进入lxml,您的收益就越高。如果您的应用程序受XML解析和序列化的约束,或者非常有选择性的XPath表达式和复杂的XSLT,那么您在多处理器计算机上的加速可能会很大。
所以,问题是为什么两个线程比一个线程慢?
我的环境:linux debian,lxml 3.3.5-1 + b1,python2和python3上的结果相同
顺便说一句,我的朋友试图在macos上运行这个测试,并为一个和两个线程获得相同的时间。无论如何,这不是根据文档所应的那样(两个线程应该快两倍)。 UPD:感谢观众。他指出需要在每个线程中创建一个解析器。 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)
输出结果为:
Testing one thread (100 jobs per thread)
Time: 0.53993
Testing two threads (50 jobs per thread)
Time: 0.28869
这正是我想要的! :)
答案 0 :(得分:5)
文档在那里提供了良好的领先优势:"只要您使用默认解析器(为每个线程复制)或自己为每个线程创建解析器。"
您绝对不会为每个线程创建解析器。您可以看到,如果您自己未指定解析器,fromstring
函数将使用全局解析器。
现在对于其他条件,您可以在文件底部看到html_parser
是lxml.etree.HTMLParser
的子类。没有特殊行为,最重要的是没有线程本地存储。我不能在这里测试,但我相信你最终会在你的两个线程中共享一个解析器,它不符合"默认解析器"。
你能尝试自己实现解析器并将它们提供给fromstring
吗?或者我会在一个小时左右的时间内完成并更新这篇文章。
def func(number):
parser = HTMLParser()
for x in range(number):
fromstring(DATA, parser=parser)
答案 1 :(得分:-1)
那是因为线程如何在python中工作。 并且python 2.7和python 3之间存在差异。 如果你真的想加速解析,你应该使用多处理而不是多线程。 读这个: How do threads work in Python, and what are common Python-threading specific pitfalls?
这是关于多处理的: http://sebastianraschka.com/Articles/2014_multiprocessing_intro.html
只要它不是io操作,当你使用线程时,你会增加上下文切换的开销,因为一次只能运行一个线程。 When are Python threads fast?
祝你好运。