1

可以使用 pickle/dill/cpickle 来腌制导入的模块以提高导入速度吗?例如,Shapely 模块在我的系统上需要 5 秒才能找到并加载所有必需的依赖项,我真的很想避免这种情况。

我可以腌制一次我的进口,然后重复使用该腌制而不是每次都进行缓慢的进口吗?

4

3 回答 3

1

导入延迟很可能是由于加载了 GEOS 库的相关共享对象。

优化这一点也许可以做到,但这将非常困难。一种方法是构建一个静态编译的自定义 python 解释器,其中包含所有 DLL 和扩展模块。但维护这将是一个主要的 PITA(相信我 - 我这样做是为了工作)。

另一种选择是将您的应用程序转变为服务,因此只会产生启动解释器一次的运行时成本。

这是否合适取决于您的实际问题。

于 2016-05-09T13:06:01.770 回答
1

虽然dill可以序列化一个模块,但您可以从它如何序列化一个模块中看出它并没有节省工作import。序列化模块时dill,它会调用一个函数,然后导入模块。所以,正如@dimo414 所说,答案是否定的。

>>> import dill
>>> import re
>>> _re = dill.dumps(re)
>>> re_ = dill.loads(_re)
>>> re_
<module 're' from '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/re.pyc'>
>>> _re
'\x80\x02cdill.dill\n_import_module\nq\x00U\x02req\x01\x85q\x02Rq\x03.'
>>> 
于 2016-05-09T13:01:06.850 回答
1

不,首先你不能腌制模块,你会得到一个错误:

>>> import pickle, re
>>> pickle.dump(re, open('/tmp/re.p', 'wb'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <class 'module'>: attribute lookup module on builtins failed

从概念上讲,即使您可以序列化一个模块,您也只会增加 Python 必须做的工作量。

通常,当您说 时import module,Python 必须:

  1. 查找模块的位置(通常是文件系统上的一个文件)
  2. 将源代码解析为内存中的字节码(如果可能,将解析后的字节码存储为.pyc文件),或者.pyc如果存在则直接将 a 加载到内存中
  3. 执行模块首次加载时应该运行的任何代码

如果你要以某种方式腌制一个模块,你基本上会用你自己的半生不熟的解决方案替换第 2 步。

  1. 找到pickle的位置(通常是文件系统上的一个文件)
  2. 将其解压到 Python 模块中
  3. 执行模块首次加载时应该运行的任何代码

我们可以放心地假设 unpickling 会比 Python 的内置字节码格式慢,因为如果不是,Python 无论如何都会在幕后使用 pickling。

更重要的是,解析 Python 文件并不(非常)昂贵,而且几乎不需要任何时间。任何真正的减速都会发生在第 3 步,我们没有改变这一点。您可能会问是否有某种方法可以通过酸洗跳过第三步,但在一般情况下,没有,这是不可能的,因为无法保证模块不会对环境的其余部分进行更改。

现在您可能知道 Shapely 模块的一些特别之处,它可以让您说“Shapely 在导入时所做的所有工作都可以在运行之间安全地缓存”。在这种情况下,正确的做法是将此类缓存行为贡献给库并缓存Shapely 正在加载的数据,而不是Python 正在导入的代码。

于 2016-01-22T05:43:34.113 回答