apa*_*erj 5 python pythonpath python-2.7
假设您有一个项目,该项目在不同的地方都有多个级别的文件夹,为了使导入调用更清晰,人们修改了整个项目的 PYTHONPATH。
这意味着而不是说:
from folder1.folder2.folder3 import foo
Run Code Online (Sandbox Code Playgroud)
他们现在可以说
from folder3 import foo
Run Code Online (Sandbox Code Playgroud)
并将folder1/folder2 添加到PYTHONPATH。这里的问题是,如果继续这样做,并向 PYTHONPATH 添加大量路径,这是否会对性能产生明显或显着的影响?
为了在性能方面增加一些规模感,我要求至少以毫秒为单位(即:100 毫秒?500 毫秒?)
PYTHONPATH因此,在系统调用中可以看到拥有大量不同目录和深度嵌套的包结构之间的性能权衡。所以假设我们有以下目录结构:
bash-3.2$ tree a\na\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 b\n \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 c\n \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 d\n \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 __init__.py\nbash-3.2$ tree e\ne\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.pyc\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 f\n \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py\n \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.pyc\n \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 g\n \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py\n \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.pyc\n \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 h\n \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py\n \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 __init__.pyc\nRun Code Online (Sandbox Code Playgroud)\n\n我们可以使用这些结构和strace程序来比较和对比我们为以下命令生成的系统调用:
strace python -c \'from e.f.g import h\'\nPYTHONPATH="./a/b/c:$PYTHONPATH" strace python -c \'import d\'\nRun Code Online (Sandbox Code Playgroud)\n\n因此,这里的权衡实际上是启动时的系统调用与导入时的系统调用。对于 中的每个条目PYTHONPATH,python首先检查该目录是否存在:
stat("./a/b/c", {st_mode=S_IFDIR|0776, st_size=4096, ...}) = 0\nstat("./a/b/c", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0\nRun Code Online (Sandbox Code Playgroud)\n\n如果该目录存在(确实存在...由右侧的 0 表示),Python 将在解释器启动时搜索许多模块。对于每个模块,它检查:
\n\nstat("./a/b/c/site", 0x7ffd900baaf0) = -1 ENOENT (No such file or directory)\nopen("./a/b/c/site.x86_64-linux-gnu.so", O_RDONLY) = -1 ENOENT (No such file or directory)\nopen("./a/b/c/site.so", O_RDONLY) = -1 ENOENT (No such file or directory)\nopen("./a/b/c/sitemodule.so", O_RDONLY) = -1 ENOENT (No such file or directory)\nopen("./a/b/c/site.py", O_RDONLY) = -1 ENOENT (No such file or directory)\nopen("./a/b/c/site.pyc", O_RDONLY) = -1 ENOENT (No such file or directory)\nRun Code Online (Sandbox Code Playgroud)\n\n其中每一个都会失败,并且它会移至路径中的下一个条目,搜索要订购的模块。我的 3.5 解释器以这种方式查找 25 个模块,在152每个新条目启动时生成增量系统调用PYTHONPATH。
深层包结构不会对解释器启动造成任何影响,但是当我们从深层嵌套包结构导入时,我们确实看到了差异。d/__init__.py作为基线,这里是从a/b/c我们的目录中简单导入PYTHONPATH:
stat("/home/matt/a/b/c/d", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0\nstat("/home/matt/a/b/c/d/__init__.py", {st_mode=S_IFREG|0664, st_size=0, ...}) = 0\nstat("/home/matt/a/b/c/d/__init__", 0x7ffd900ba990) = -1 ENOENT (No such file or directory)\nopen("/home/matt/a/b/c/d/__init__.x86_64-linux-gnu.so", O_RDONLY) = -1 ENOENT (No such file or directory)\nopen("/home/matt/a/b/c/d/__init__.so", O_RDONLY) = -1 ENOENT (No such file or directory)\nopen("/home/matt/a/b/c/d/__init__module.so", O_RDONLY) = -1 ENOENT (No such file or directory)\nopen("/home/matt/a/b/c/d/__init__.py", O_RDONLY) = 3\nfstat(3, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0\nopen("/home/matt/a/b/c/d/__init__.pyc", O_RDONLY) = 4\nfstat(4, {st_mode=S_IFREG|0664, st_size=117, ...}) = 0\nread(4, "\\3\\363\\r\\n\\17\\3105[c\\0\\0\\0\\0\\0\\0\\0\\0\\1\\0\\0\\0@\\0\\0\\0s\\4\\0\\0\\0d\\0"..., 4096) = 117\nfstat(4, {st_mode=S_IFREG|0664, st_size=117, ...}) = 0\nread(4, "", 4096) = 0\nclose(4) = 0\nclose(3) = 0\nRun Code Online (Sandbox Code Playgroud)\n\n基本上,它所做的就是寻找d包或模块。当它找到时d/__init__.py,它会打开它,然后打开d/__init__.pyc并将内容读入内存,然后关闭这两个文件。
使用深度嵌套的包结构,我们必须额外重复此操作 3 次,这对于15每个目录的系统调用非常有利,总共可以有 45 次以上的系统调用。虽然这还不到通过添加路径添加的调用数量的一半PYTHONPATH,但read根据文件的大小,这些调用可能比其他系统调用更耗时(或需要更多系统调用)__init__.py。
考虑到这一切,这些差异几乎肯定不足以抵消您所需解决方案的设计优势。
\n\n如果您的流程是长期运行的(如网络应用程序)而不是短暂的,则尤其如此。
\n\n我们可以通过以下方式减少系统调用:
\n\nPYTHONPATH条目.pyc文件以避免需要写入它们我们可以通过删除您的py文件来更大幅度地提高性能,这样它们就不会与您的 PYC 文件一起出于调试目的而被读取......但这对我来说似乎太过分了。
希望这有用,这可能比必要的深入得多。
\n| 归档时间: |
|
| 查看次数: |
495 次 |
| 最近记录: |