我有一个python模块,使用的一个巨大的字典的全球变量,当前我把计算代码顶端部分,每一个第一次的进口或重新装的模块需要更多的然后一分钟,这是完全不能接受的。我怎么可以节省的计算结果的地方,所以,下一次的进口/重新加载不必要的计算吗?我试图cPickle,但是载入的词典的变从一个文件(1.3M)需要大约同一时间计算。

得到更多的信息,我的问题

FD = FreqDist(word for word in brown.words()) # this line of code takes 1 min
有帮助吗?

解决方案

只是为了澄清:每次导入模块时,模块正文中的代码都是而不是执行 - 它只运行一次,之后将来导入找到已创建的模块,而不是重新创造它。查看sys.modules以查看缓存模块列表。

但是,如果您的问题是程序运行后第一次导入所需的时间,您可能需要使用除python dict之外的其他方法。可能最好使用磁盘形式,例如sqlite数据库,其中一个dbm模块。

对于界面中的最小变化,搁置模块可能是您的最佳选择 - 这会在dbm模块之间建立一个非常透明的接口,使它们像任意python dict一样,允许存储任何可选值。这是一个例子:

# Create dict with a million items:
import shelve
d = shelve.open('path/to/my_persistant_dict')
d.update(('key%d' % x, x) for x in xrange(1000000))
d.close()

然后在下一个过程中使用它。应该没有大的延迟,因为只对磁盘表单上请求的密钥执行查找,因此所有内容都不必加载到内存中:

>>> d = shelve.open('path/to/my_persistant_dict')
>>> print d['key99999']
99999

它比真正的字典慢一点,如果你做了需要所有键的事情(例如尝试打印它), 仍需要很长时间才能加载,但可能会解决你的问题。

其他提示

首次使用时计算全局变量。

class Proxy:
    @property
    def global_name(self):
        # calculate your global var here, enable cache if needed
        ...

_proxy_object = Proxy()
GLOBAL_NAME = _proxy_object.global_name

或者更好的是,通过特殊数据对象访问necessery数据。

class Data:
    GLOBAL_NAME = property(...)

data = Data()

示例:

from some_module import data

print(data.GLOBAL_NAME)

请参阅 Django设置

我假设你已经将dict文字粘贴到了源代码中,这就是花了一分钟的时间?我不知道怎么解决这个问题,但是你可以避免在 import 上实例化这个dict ...你可以在它第一次实际使用时懒惰地实例化它。

您可以尝试使用编组模块而不是c ?泡椒一个;它可能会更快。 python使用此模块以二进制格式存储值。请特别注意以下段落,看看元帅是否符合您的需求:

  

并非所有Python对象类型都受支持;通常,此模块只能编写和读取其值独立于Python的特定调用的对象。支持以下类型:无,整数,长整数,浮点数,字符串,Unicode对象,元组,列表,集,字典和代码对象,应该理解元组,列表和字典仅支持因为其中包含的价值本身得到支持;并且不应该写入递归列表和字典(它们将导致无限循环)。

为了安全起见,在解组dict之前,请确保解组dict的Python版本与执行编组的Python版本相同,因为无法保证向后兼容性。

如果'搁置的解决方案原来是速度太慢或繁琐,还有其他的可能性:

对于大型数据集,

shelve变得非常慢。我一直在使用 redis 成功地,围绕它写了一个 FreqDist包装器。它非常快,可以同时访问。

您可以使用搁置来存储光盘上的数据,而不是将整个数据加载到内存中。因此启动时间会非常快,但权衡时间会较慢。

Shelve也会腌制dict值,但会在启动时为所有项目执行(un)pickle,但仅限于每个项目本身的访问时间。

有助于加快进口的一些事情:

  1. 运行python时,您可以尝试使用-OO标志运行python。这将进行一些优化,以减少模块的导入时间。
  2. 你有没有理由不能将字典分解成可以更快加载的单独模块中的小字典?
  3. 作为最后的手段,您可以异步进行计算,以便在需要结果之前不会延迟程序。或者甚至可以将字典放在一个单独的进程中,如果你想利用多核架构,可以使用IPC来回传递数据。
  4. 话虽如此,我同意您在第一次导入模块后不应该遇到导入模块的任何延迟。以下是其他一些一般性想法:

    1. 您是在函数中导入模块吗?如果是这样,这个可以导致性能问题,因为它必须检查并在每次命中导入语句时加载模块。
    2. 您的程序是多线程的吗?我已经看到了在多线程应用程序中导入模块时执行代码会导致某些困难和应用程序不稳定的情况(最明显的是cgitb模块)。
    3. 如果这是一个全局变量,请注意全局变量查找时间可能比局部变量查找时间长得多。在这种情况下,如果您在同一个上下文中多次使用它,则可以通过将字典绑定到局部变量来实现显着的性能提升。
    4. 话虽如此,如果没有更多的背景,给你提供任何具体的建议有点困难。更具体地说,你在哪里导入它?什么是计算?

  1. 将计算密集型部分视为一个单独的模块。那么至少在重装时,你不必等待。

  2. 尝试使用协议2转储数据结构。要尝试的命令是cPickle.dump(FD, protocol=2)。来自cPickle.Pickler的文档字符串:

    Protocol 0 is the
    only protocol that can be written to a file opened in text
    mode and read back successfully.  When using a protocol higher
    than 0, make sure the file is opened in binary mode, both when
    pickling and unpickling. 
    

我正在经历同样的问题...... 搁置,数据库等......对于这类问题来说都太慢了。您需要点击一次,将其插入像Redis这样的内存键/ val存储中。它只会存在于内存中(警告它可能会占用大量内存,因此您可能需要一个专用的盒子)。你永远不必重新加载它,你只需要在内存中寻找密钥

r = Redis()
r.set(key, word)

word = r.get(key)

扩展延迟计算的想法,为什么不把dict变成一个必要时提供(和缓存)元素的类?

您也可以使用psyco加速整体执行......

OR 您可以使用数据库存储值吗?查看SQLObject,这样可以很容易地将东西存储到数据库中。

对于这个问题,还有另一个非常明显的解决方案。重新加载代码时,原始范围仍然可用。

所以......做这样的事情会确保这段代码只执行一次。

try:
    FD
except NameError:
    FD = FreqDist(word for word in brown.words())
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top