Python可以配置为缓存吗系统路径目录查找?

2024-10-01 15:48:02 发布

您现在位置:Python中文网/ 问答频道 /正文

我们已经对运行在远程连接上的Python做了很多基准测试。程序在异地运行,但在现场访问磁盘。我们在RHEL6下运行。我们和斯特拉斯一起看了一个简单的节目。它似乎花费了大量的时间来执行stat和打开文件,看看它们是否存在。通过一个昂贵的远程连接。有没有一种方法可以配置Python一次读取目录内容并缓存它的列表,这样就不必再检查它了?在

样本程序测试_导入.py公司名称:

import random
import itertools

我运行了以下命令:

^{pr2}$

所以它在那个目录中查找了大约331次。很多人的结果是:

stat ( "/usr/lib64/python2.6/posixpath", 0x7fff1b447340 ) = -1 ENOENT ( No such file or directory ) < 0.000009 >

如果它缓存了目录,就不必统计文件来查看它是否在那里。在


Tags: 文件方法import程序目录远程时间基准
3条回答

除了使用导入器或zipimport,还应该考虑冻结代码。冻结将大大减少stat调用。在

Python的一部分:https://wiki.python.org/moin/Freeze 第三方:http://cx-freeze.readthedocs.org/en/latest/

冻结一个基本的脚本,统计数据从232下降到88。在

$ strace -c -e stat64,open python2 hello.py
hello
% time     seconds  usecs/call     calls    errors syscall
         -      -     -     -         
100.00    0.000011           0       232       161 open
         -      -     -     -         
100.00    0.000011                   232       161 total
$ strace -c -e stat64,open ./hello
hello
% time     seconds  usecs/call     calls    errors syscall
         -      -     -     -         
  -nan    0.000000           0        88        73 open
         -      -     -     -         
100.00    0.000000                    88        73 total

你仍然容易受到系统路径(但这正是importlib2及其缓存可以帮助您的地方)。在

您可以通过迁移到python3.3或用替代品替换标准导入系统来避免这种情况。在我两周前在PyOhio所做的strace演讲中,我讨论了旧导入机制不幸的O(nm)性能(对于n目录和m可能的后缀);从this slide开始。在

我演示了easy_install加上一个Zope支持的web框架如何生成73477个系统调用,以便进行足够的导入来启动和运行。在

例如,在我的笔记本电脑上的virtualenv中快速安装了bottle之后,我发现Python导入该模块并启动并运行需要整整1000个调用:

$ strace -c -e stat64,open python -c 'import bottle'
% time     seconds  usecs/call     calls    errors syscall
         -      -     -     -         
100.00    0.000179           0      1519      1355 open
  0.00    0.000000           0       475       363 stat64
         -      -     -     -         
100.00    0.000179                  1994      1718 total

但是,如果我跳转到os.py,我可以添加一个缓存导入器,即使使用非常幼稚的实现,也可以将未命中的数量减少近1000:

^{pr2}$

我选择os.py作为实验,因为strace显示它是Python导入的第一个模块,而且我们越快安装我们的导入器,Python在其旧的可怕的缓慢机制下必须导入的标准库模块就越少!在

# Put this right below "del _names" in os.py

class CachingImporter(object):

    def __init__(self):
        self.directory_listings = {}

    def find_module(self, fullname, other_path=None):
        filename = fullname + '.py'
        for syspath in sys.path:
            listing = self.directory_listings.get(syspath, None)
            if listing is None:
                try:
                    listing = listdir(syspath)
                except OSError:
                    listing = []
                self.directory_listings[syspath] = listing
            if filename in listing:
                modpath = path.join(syspath, filename)
                return CachingLoader(modpath)

class CachingLoader(object):

    def __init__(self, modpath):
        self.modpath = modpath

    def load_module(self, fullname):
        if fullname in sys.modules:
            return sys.modules[fullname]
        import imp
        mod = imp.new_module(fullname)
        mod.__loader__ = self
        sys.modules[fullname] = mod
        mod.__file__ = self.modpath
        with file(self.modpath) as f:
            code = f.read()
        exec code in mod.__dict__
        return mod

sys.meta_path.append(CachingImporter())

当然,这有点粗糙-它不会尝试检测.pyc文件或{}文件或Python可能会寻找的任何其他扩展名。它也不知道__init__.py文件或包中的模块(这需要在sys.path条目的子目录中运行lsdir())。但它至少说明了,通过这样的方法可以消除成千上万的额外呼叫,并展示了一个你可以尝试的方向。当它找不到模块时,通常的导入机制就会启动。在

我想知道在PyPI或其他地方是否已经有一个好的缓存导入器可用?这类东西似乎已经在不同的商店里写了几百遍了。我以为诺亚·吉夫特写了一篇文章放在博客上,但我找不到一个链接来证实我的记忆。在

编辑:正如@ncoglan在评论中提到的,PyPI:http://pypi.python.org/pypi/importlib2上提供了一个新的python3.3+导入系统到python2.7的alpha发布后端口-不幸的是,提问者似乎仍然停留在2.6上。在

我知道这不是你想要的,但无论如何我都会回答:D

sys.path目录没有缓存系统,但是zipimport.zip文件中创建了一个模块索引。此索引用于加快模块查找速度。在

这种解决方案的缺点是不能将它与二进制模块(例如..so)一起使用,因为Python无法使用dlopen()来加载这种模块。在

另一个问题是,有些模块(比如示例中使用的posixpath)是由CPython解释器在引导过程中加载的。在

我希望你还记得我在PythonBrasil的时候,我帮你把迪斯尼/皮克斯的纪念品塞进一些袋子里:D

相关问题 更多 >

    热门问题