Python共享库
我了解到在Python(CPython)中有两种类型的模块:
- .so(C扩展模块)
- .py(Python脚本)
.so模块即使在不同的进程或解释器中被导入,也只会加载一次。
而.py模块则是每个进程或解释器都会加载一次,除非你明确地重新加载。
有没有办法让多个进程或解释器共享.py模块呢?
不过,这样的话还是需要一个地方来存储对模块所做的修改。我在想,或许可以把解释器嵌入到一个.so模块中,作为第一步。有没有现成的解决方案呢?
我承认我可能在这个问题上想得很远,抱歉我不太了解。
3 个回答
我能给你的最佳答案是“不是不可能,但我不知道是否真的发生过”。
你需要考虑一下实际发生了什么。当你遇到一个 .py 文件时,Python 需要先读取这个文件,然后把它编译成字节码,最后执行这个字节码。编译是在进程内部进行的,所以不能被共享。
而当你遇到一个 .so 文件时,操作系统会把这个库预留的内存链接进来。所有的进程都共享同一块内存区域,这样就节省了内存。
Python 其实还有第三种加载模块的方法。如果可以的话,在加载 .py 文件时,它会创建一个预编译的 .pyc 文件,这样加载起来更快(因为省去了编译的步骤)。下次再加载的时候就直接加载 .pyc 文件。理论上,它们可以通过将 .pyc 文件映射到内存中来实现这一点。(使用 MAP_PRIVATE,以防其他东西之后修改了这个字节码。)如果真这样做了,那么共享模块默认就会放在共享内存里。
我不知道这是否真的以这种方式实现过。
不,这个是没办法的。Python语言非常灵活,每个进程的情况都不一样,所以这样做可能也没什么意义。比如说,你可以随意修改模块的内容。也许有办法共享代码,但这样做的好处非常小,而且可能需要付出很多努力。
之所以 .so
(或者 .pyd
)文件只占用一次内存空间(除了它们的变量部分),是因为操作系统内核把它们当作目标代码来识别。而 .py
文件则只是被当作文本文件或数据来处理;真正让它们有“代码”身份的是Python解释器。把Python解释器嵌入到共享库中并不能解决这个问题。
即使在多个进程中使用 .py
文件,想要让它们只加载一次,也需要对CPython内部做很大的改动。
如果你想节省内存空间,最好的办法是使用 Cython 将Python模块编译成 .so
文件。这可能需要对模块做一些修改。