PyPy 和 CPython 的机能较量测试
当前位置:以往代写 > Python教程 >PyPy 和 CPython 的机能较量测试
2019-06-14

PyPy 和 CPython 的机能较量测试

PyPy 和 CPython 的机能较量测试

最近我在维基百科上完成了一些数据挖掘方面的任务。它由这些部门构成:

理会enwiki-pages-articles.xml的维基百科转储;

把种别和页存储到MongoDB内里;

对种别名称举办从头分门别类。

我对CPython 2.7.3和PyPy 2b的实际任务机能举办了测试。我利用的库是:

redis 2.7.2

pymongo 2.4.2

另外CPython是由以下库支持的:

hiredis

pymongo c-extensions

测试主要包括数据库理会,所以我没预推测会从PyPy获得多大长处(况且CPython的数据库驱动是C写的)。

下面我会描写一些有趣的功效。

抽取维基页名

我需要在所有维基百科的种别中成立维基页名到page.id的联接并存储从头分派好的它们。最简朴的办理方案应该是导入enwiki-page.sql(界说了一个RDB表)到MySQL内里,然后传输数据、举办重分派。但我不想增加MySQL需求(有节气!XD)所以我用纯Python写了一个简朴的SQL插入语句理会器,然后直接从enwiki-page.sql导入数据,举办重分派。

这个任务对CPU依赖更大,所以我再次看好PyPy。

/ time

PyPy 169.00s 用户态 8.52s 系统态 90% CPU

CPython 1287.13s 用户态 8.10s 系统态 96% CPU

我也给page.id->种别做了雷同的联接(我条记本的内存太小了,不能生存供我测试的信息了)。

从enwiki.xml中筛选种别

为了利便事情,我需要从enwiki-pages-articles.xml中过滤种别,并将它们存储沟通的XML名目标种别。因此我选用了SAX理会器,在PyPy和CPython中都合用的包装器理会。对外的原生编译包(同事在PyPy和CPython 中) 。

代码很是简朴:

class WikiCategoryHandler(handler.ContentHandler):
    """Class which detecs category pages and stores them separately
    """
    ignored = set(('contributor', 'comment', 'meta'))
 
    def __init__(self, f_out):
        handler.ContentHandler.__init__(self)
        self.f_out = f_out
        self.curr_page = None
        self.curr_tag = ''
        self.curr_elem = Element('root', {})
        self.root = self.curr_elem
        self.stack = Stack()
        self.stack.push(self.curr_elem)
        self.skip = 0
 
    def startElement(self, name, attrs):
        if self.skip>0 or name in self.ignored:
            self.skip += 1
            return
        self.curr_tag = name
        elem = Element(name, attrs)
        if name == 'page':
            elem.ns = -1
            self.curr_page = elem
        else:   # we don't want to keep old pages in memory
            self.curr_elem.append(elem)
        self.stack.push(elem)
        self.curr_elem = elem
 
    def endElement(self, name):
        if self.skip>0:
            self.skip -= 1
            return
        if name == 'page':
            self.task()
            self.curr_page = None
        self.stack.pop()
        self.curr_elem = self.stack.top()
        self.curr_tag = self.curr_elem.tag
 
    def characters(self, content):
        if content.isspace(): return
        if self.skip == 0:
            self.curr_elem.append(TextElement(content))
            if self.curr_tag == 'ns':
                self.curr_page.ns = int(content)
 
    def startDocument(self):
        self.f_out.write("<root>\n")
 
    def endDocument(self):
        self.f_out.write("<\root>\n")
        print("FINISH PROCESSING WIKIPEDIA")
 
    def task(self):
        if self.curr_page.ns == 14:
            self.f_out.write(self.curr_page.render())
 
 
class Element(object):
    def __init__(self, tag, attrs):
        self.tag = tag
        self.attrs = attrs
        self.childrens = []
        self.append = self.childrens.append
 
    def __repr__(self):
        return "Element {}".format(self.tag)
 
    def render(self, margin=0):
        if not self.childrens:
            return u"{0}<{1}{2} />".format(
                " "*margin,
                self.tag,
                "".join([' {}="{}"'.format(k,v) for k,v in {}.iteritems()]))
        if isinstance(self.childrens[0], TextElement) and len(self.childrens)==1:
            return u"{0}<{1}{2}>{3}</{1}>".format(
                " "*margin,
                self.tag,
                "".join([u' {}="{}"'.format(k,v) for k,v in {}.iteritems()]),
                self.childrens[0].render())
 
        return u"{0}<{1}{2}>\n{3}\n{0}</{1}>".format(
            " "*margin,
            self.tag,
            "".join([u' {}="{}"'.format(k,v) for k,v in {}.iteritems()]),
            "\n".join((c.render(margin+2) for c in self.childrens)))
 
class TextElement(object):
    def __init__(self, content):
        self.content = content
 
    def __repr__(self):
        return "TextElement" def render(self, margin=0):
        return self.content

Element和TextElement元素包换tag和body信息,同时提供了一个要领来渲染它。

下面是我想要的PyPy和CPython较量功效。

/ time

PyPy 2169.90s

CPython 4494.69s

我很对PyPy的功效很受惊。

计较有趣的种别荟萃

#p#分页标题#e#

我曾经想要计较一个有趣的种别荟萃——在我的一个应用配景下,以Computing种别衍生的一些种别为开始举办计较。为此我需要构建一个提供类的类图——子类干系图。

布局类——子类干系图

这个任务利用MongoDB作为数据来历,并对布局举办从头分派。算法是:

for each category.id in redis_categories (it holds *category.id -> category title mapping*) do:
    title = redis_categories.get(category.id)
    parent_categories = mongodb get categories for title
    for each parent_cat in parent categories do:
        redis_tree.sadd(parent_cat, title) # add to parent_cat set title

歉仄写这样的伪码,但我想这样看起来越发紧凑些。

所以说这个任务仅把数据从一个数据库拷贝到另一个。这里的功效是MongoDB预热完毕后得出的(不预热的话数据会有毛病——这个Python任务只淹灭约10%的CPU)。计时如下:

/ time

PyPy 175.11s 用户态 66.11s 系统态 64% CPU

CPython 457.92s 用户态 72.86s 系统态 81% CPU

遍历redis_tree(再分派过的树)

假如我们有redis_tree数据库,仅剩的问题就是遍历Computing种别下所有可实现的结点了。为制止轮回遍历,我们需要记录已会见过的结点。自从我想测试Python的数据库机能,我就用再分派荟萃列来办理这个问题。

/ time

PyPy 14.79s 用户态 6.22s 系统态 69% CPU 30.322 总计

CPython 44.20s 用户态 13.86s 系统态 71% CPU 1:20.91 总计

说实话,这个任务也需要构建一些tabu list(克制列表)——来制止进入不需要的种别。但那不是本文的重点。

结论

举办的测试仅仅是我最终事情的一个简介。它需要一个常识体系,一个我从抽取维基百科中适当的内容中获得的常识体系。

PyPy对比CPython,在我这个简朴的数据库操纵中,提高了2-3倍的机能。(我这里没有算上SQL理会器,约莫8倍)

多亏了PyPy,我的事情越发愉悦了——我没有改写算法就使Python有了效率,并且PyPy没有像CPython一样把我的CPU弄挂了,以至于一段时间内我没法正常的利用我的条记本了(看看CPU时间占的百分比吧)。

任务险些都是数据库操纵,而CPython有一些加快的参差不齐的C语言模块。PyPy不利用这些,但功效却更快!

我的全部事情需要大量的周期,所以我真兴奋能用PyPy。

    关键字:

在线提交作业