首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python是否可以配置为缓存sys.path目录查找?

Python是否可以配置为缓存sys.path目录查找?
EN

Stack Overflow用户
提问于 2014-08-12 10:53:15
回答 3查看 1K关注 0票数 12

我们已经对通过远程连接运行的Python进行了大量的基准测试。该程序在异地运行,但在现场访问磁盘。我们在RHEL6下运行。我们用strace观看了一个简单的程序。它似乎花费了大量时间在文件上执行stat和open,以查看它们是否在那里。通过远程连接,这是昂贵的。有没有办法将Python配置为只读取目录内容一次,并缓存它的列表,这样它就不必再次检查它?

示例程序test_import.py:

代码语言:javascript
复制
import random
import itertools

我运行了以下命令:

代码语言:javascript
复制
$ strace -Tf python test_import.py >& strace.out
$ grep '/usr/lib64/python2.6/' strace.out | wc
331    3160   35350

因此,它在该目录中查找了大约331次。他们中的许多人的结果如下:

代码语言:javascript
复制
stat ( "/usr/lib64/python2.6/posixpath", 0x7fff1b447340 ) = -1 ENOENT ( No such file or directory ) < 0.000009 >

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

EN

回答 3

Stack Overflow用户

发布于 2014-08-12 21:43:57

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

我演示了easy_install加上一个基于Zope的web框架是如何生成73,477个系统调用的,只是为了完成足够的导入来启动和运行。

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

代码语言:javascript
复制
$ 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,我可以添加一个缓存导入器,即使使用非常简单的实现,也可以将未命中的数量减少近千次:

代码语言:javascript
复制
$ strace -c -e stat64,open python -c 'import bottle'
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00    0.000041           0       699       581 open
  0.00    0.000000           0       301       189 stat64
------ ----------- ----------- --------- --------- ----------------
100.00    0.000041                  1000       770 total

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

代码语言:javascript
复制
# 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文件、.so文件或Python可能寻找的任何其他扩展名。它也不知道__init__.py文件或包中的模块(这需要在sys.path条目的子目录中运行lsdir() )。但它至少说明了可以通过这样的方式消除数千个额外的调用,并展示了一个您可以尝试的方向。当它找不到一个模块时,正常的导入机制就简单地起作用。

我想知道是否有一个好的缓存导入器已经在PyPI上或其他地方可用?它看起来像是已经在不同的商店里写了几百次的东西。我以为Noah Gift已经写了一个,并将其放在博客文章或其他什么地方,但我找不到一个链接来证实我的记忆。

编辑:正如@ncoglan在评论中提到的,在PyPI:http://pypi.python.org/pypi/importlib2上有一个新的Python 3.3+导入系统到Python2.7的alpha发布后向端口-不幸的是,提问者看起来仍然停留在2.6版本上。

票数 9
EN

Stack Overflow用户

发布于 2014-08-12 12:04:49

我知道这不是你想要的,但我还是会回答:D

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

此解决方案的缺点是您不能将其与二进制模块一起使用(例如,.so),因为PythonPython用来加载这类模块的dlopen()缺乏支持。

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

PS。我希望你还记得我在PythonBrasil的时候,我帮你把迪斯尼/皮克斯的纪念品装进包里。

票数 1
EN

Stack Overflow用户

发布于 2014-08-15 10:26:57

除了使用导入程序或zipimport之外,您还应该考虑冻结您的代码。冻结将极大地减少状态调用。

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

冻结一个基本的脚本会使统计从232下降到88。

代码语言:javascript
复制
$ 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

您仍然容易受到sys.path中条目数量的影响(但这正是importlib2及其缓存可以为您提供帮助的地方)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25255438

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档