首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何提高运行在大量数据上的脚本的性能?

如何提高运行在大量数据上的脚本的性能?
EN

Stack Overflow用户
提问于 2016-01-04 18:22:28
回答 2查看 436关注 0票数 1

我的机器学习脚本生成大量数据(包含在一个根BTree中的数百万个BTree),并将其存储在ZODB的FileStorage中,主要原因是所有这些数据都不适合内存。脚本还经常修改以前添加的数据。

当我增加了问题的复杂性,因此需要存储更多的数据时,我注意到了性能问题--脚本现在平均计算数据的速度从原来的2倍提高到了10倍(唯一改变的是要存储的数据量,以及后来要更改的数据量)。

我尝试将cache_size设置为1000到50000之间的各种值。老实说,速度的差别是可以忽略不计的。

我想切换到RelStorage,但不幸的是,在医生们中,他们只提到了如何配置框架,比如Zope或Plone。我只使用ZODB。

我不知道RelStorage在我的情况下会不会更快。

下面是我当前设置ZODB连接的方式:

代码语言:javascript
复制
import ZODB
connection = ZODB.connection('zodb.fs', ...)
dbroot = connection.root()

显然,ZODB目前是我的脚本的瓶颈。我正在寻求关于如何解决这个问题的建议。

我选择了ZODB,因为我认为NoSQL数据库更适合我的情况,我喜欢类似于Python的dict的接口的想法。

代码和数据结构:

  • 根数据结构: 如果不是hasattr(dbroot,'actions_values'):dbroot.actions_values = BTree()如果不是hasattr(dbroot,'games_played'):dbroot.games_played =0 actions_values从概念上构建如下: actions_values ={# BTree str(state):{# BTree # contiains操作(确切地说,我正在处理玩Connect 4的代理程序)#以及它们的值(这里只存在“愤怒者”以前采取的行动),例如: 1: 0.4356 5: 0.3456 },#其他状态} state是一个简单的二维数组,表示游戏板。它的字段可能有12None: 板= 在xrange(行)中
  • 主回路: should_play = 10000000 transactions_freq = 10000 packing_freq = 50000 player = ReinforcementPlayer(dbroot.actions_values,config),而dbroot.games_played < should_play:# max_epsilon在开始时线性下降到min_epsilon: epsilon = max_epsilon - (max_epsilon - min_epsilon) * dbroot.games_played / (should_play - 1) dbroot.games_played += 1 sys.stdout.write('\rPlaying对策%d = %d‘% (dbroot.games_played ))(Should_play) sys.stdout.flush() board_state = player.play_game(epsilon) if(dbroot.games_played % transactions_freq == 0):打印(‘Commiting.’)transaction.commit() if(dbroot.games_played % packing_freq == 0):打印(“打包DB.”)connection.db().pack() (packing也需要很长时间,但这不是主要问题,我可以在程序完成后打包数据库)
  • dbroot上操作的代码(在ReinforcementPlayer中): get_actions_with_values(self,player_id,如果player_id == 1: lookup_state = state lookup_state :lookup_state= state.switch_players() lookup_state_str = str(lookup_state)如果self.actions_values中的lookup_state_str :返回self.actions_valueslookup_state_str mirror_lookup_state_str =str(lookup_state.mirror() if mirror_lookup_state_str in self.actions_values:返回self.mirror_actions(self.actions_valuesmirror_lookup_state_str)返回None def get_value_of_action(self,player_id,state,action,default=0):actions = self.get_actions_with_values(player_id,state)如果操作为None:返回默认返回actions.get(动作,默认),def set_value_of_action(self,player_id,state,action,值):如果player_id == 1: lookup_state = state lookup_state :lookup_state= state.switch_players() lookup_state_str = str(lookup_state)如果lookup_state_str in self.actions_values: self.actions_valueslookup_state_str = value返回mirror_lookup_state_str = str(lookup_state.mirror())self.actions_values: self.actions_valuesmirror_lookup_state_str = value返回self.actions_valueslookup_state_str = BTree() self.actions_valueslookup_state_str = value (名称为镜像的函数只需反转列(操作)。因为连接4块板的垂直反射是等价的。)

经过550000场比赛,len(dbroot.actions_values)是6018450。

根据iotop,IO操作占90%的时间。

EN

回答 2

Stack Overflow用户

发布于 2016-01-04 19:15:38

使用任何(其他)数据库可能没有帮助,因为它们与ZODB一样受到磁盘IO和内存限制。如果您设法将计算卸载到数据库引擎本身(PostgreSQL +使用SQL脚本),这可能会有所帮助,因为数据库引擎将有更多的信息来做出如何执行代码的智能选择,但是这里没有什么神奇的东西,同样的事情也很可能很容易地用ZODB完成。

一些可以做的事情:

  • 拥有数据索引,而不是加载完整对象(等于SQL“完整表扫描”)。保存数据的智能预处理副本:索引、和、部分。
  • 使对象本身更小(Python类具有__slots__技巧)
  • 以智能方式使用事务。不要试图在一个大块中处理所有数据。
  • 并行处理-使用所有CPU核心而不是单线程方法。
  • 不要使用BTrees --也许在用例中有更有效的东西

拥有一些脚本的代码示例、实际内存和Data.fs大小等,将有助于提供更多的想法。

票数 2
EN

Stack Overflow用户

发布于 2016-01-06 22:15:17

为了澄清这一点,您实际使用的是哪个BTree类?OOBTree?

关于这些树状树的两个方面:

1)每个BTree由多个桶组成。每个桶将持有一定数量的项目,然后被分割。我不记得他们目前持有多少项,但我曾经尝试为它们调整C代码,并重新编译以保存更大的数量,因为所选择的值是在近20年前选择的。

2)有时可以构造非常不平衡的树状树。例如,如果您按排序顺序添加值(例如,只会增加时间戳),那么最终将有一棵树作为O(n)进行搜索。几年前,Jarn的人们编写了一个脚本,它可以在Zope的目录中重新平衡BTrees,这可能适合您。

3)与其使用OOBTree,不如使用OOBucket。这最终只是ZODB中的一个泡菜,所以在您的用例中可能会变得太大,但是如果您在一个事务中完成所有的写操作,速度可能会更快(代价是必须在更新时重写整个Bucket )。

-Matt

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

https://stackoverflow.com/questions/34597386

复制
相关文章

相似问题

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