【问题标题】:Why does it take longer to dump and load with pickle.HIGHEST_PROTOCOL?为什么使用 pickle.HIGHEST_PROTOCOL 转储和加载需要更长的时间?
【发布时间】:2012-12-01 01:42:16
【问题描述】:

我用 pickle 测试了三种不同的协议; 0、1、2。

在我的测试中,我倾倒并加载了大约 270000 个(intint)对中的一个 dict 和一个带有大约 560000 个 intset

以下是我的测试代码(您可以放心地跳过我用来从数据库中获取数据的两个 fetch 函数):

protocol = 0 # Tested 0, 1, and 2
print 'Protocol:', protocol
t0 = time.time()
sku2spu_dict = fetch_sku2spu_dict()
pid_set = fetch_valid_pids()
t1 = time.time()
print 'Time in sql:', t1 - t0
pickle.dump(sku2spu_dict, open('sku.pcike_dict', 'w'), protocol)
pickle.dump(pid_set, open('pid.picke_set', 'w'), protocol)
t2 = time.time()
print 'Time in dump:', t2 - t1
sku2spu_dict = pickle.load(open('sku.pcike_dict', 'r'))
pid_set = pickle.load(open('pid.picke_set', 'r'))
t3 = time.time()
print 'Time in load:', t3 - t2

以下是每个人花费的时间:

Protocol: 0
Time in dump: 31.3491470814
Time in load: 29.8991980553

Protocol: 1
Time in dump: 32.3191611767
Time in load: 20.6666529179

Protocol: 2
Time in dump: 94.2163629532
Time in load: 42.7647490501

令我惊讶的是,协议 2 比 0 和 1 差得多。

但是,转储文件的大小在协议 2 中是最小的,大约是协议 0 和 1 的一半。

在文档中,它说:

协议版本 2 是在 Python 2.3 中引入的。它提供了更有效的新式类的酸洗。

对于new-style classes的定义,它说:

从对象继承的任何类。这包括所有内置类型,如 list 和 dict

所以我希望协议 2 在转储和加载对象方面更快。

有人知道为什么吗?

更新:

pickle 替换为cPickle 后问题解决。

现在loaddump 使用协议 2 需要 5 和 3 秒,而协议 0 和 1 需要超过 10 秒。

【问题讨论】:

  • 出于兴趣,您的计时方法是什么?凡涉及执行速度的问题,通常最好给出自己运行测试所需的一切。
  • @Lattyware 请查看更新后的帖子。
  • 那种测试不是很准确,不如看看the timeit module
  • 更多的是timeit 多次运行测试以检查它不是异常。它还可以更轻松地与替代品进行比较。
  • 听起来“更高效”是指磁盘上的空间,而不是在这种情况下的执行时间。

标签: python pickle


【解决方案1】:

当文档谈到“新样式类”时,它(可能)指的是用户定义的新样式类。如果你用它们做一个简单的基准测试,你会发现协议 2 在转储它们时比协议 0 快两倍:

>>> import cPickle
>>> import timeit
>>> class MyObject(object):
...     def __init__(self, val):
...             self.val = val
...     def method(self):
...             print self.val
... 
>>> timeit.timeit('cPickle.dumps(MyObject(100), 0)', 'from __main__ import cPickle, MyObject')
17.654622077941895
>>> timeit.timeit('cPickle.dumps(MyObject(100), 1)', 'from __main__ import cPickle, MyObject')
14.536609172821045
>>> timeit.timeit('cPickle.dumps(MyObject(100), 2)', 'from __main__ import cPickle, MyObject')
8.885567903518677

加载速度也提高了 2 倍:

>>> dumped = cPickle.dumps(MyObject(100), 0)
>>> timeit.timeit('cPickle.loads(dumped)', 'from __main__ import cPickle, dumped')
4.6161839962005615
>>> dumped = cPickle.dumps(MyObject(100), 1)
>>> timeit.timeit('cPickle.loads(dumped)', 'from __main__ import cPickle, dumped')
4.351701021194458
>>> dumped = cPickle.dumps(MyObject(100), 2)
>>> timeit.timeit('cPickle.loads(dumped)', 'from __main__ import cPickle, dumped')
2.3936450481414795

在您的特殊情况下,情况可能相反,但如果没有定义 fetch_sku2spu_dict 等的代码,我们将无话可说。我唯一可以假设的是返回值是 dict,但在这种情况下协议 2 大约快 6 倍:

>>> mydict = dict(zip(range(100), range(100)))
>>> timeit.timeit('cPickle.dumps(mydict, 0)', 'from __main__ import cPickle, mydict')
46.335021018981934
>>> timeit.timeit('cPickle.dumps(mydict, 1)', 'from __main__ import cPickle, mydict')
7.913743019104004
>>> timeit.timeit('cPickle.dumps(mydict, 2)', 'from __main__ import cPickle, mydict')
7.798863172531128

加载速度提高了大约 2.5 倍:

>>> dumped = cPickle.dumps(mydict, 0)
>>> timeit.timeit('cPickle.loads(dumped)', 'from __main__ import cPickle, dumped')
32.81050395965576
>>> dumped = cPickle.dumps(mydict, 1)
>>> timeit.timeit('cPickle.loads(dumped)', 'from __main__ import cPickle, dumped')
13.997781038284302
>>> dumped = cPickle.dumps(mydict, 2)
>>> timeit.timeit('cPickle.loads(dumped)', 'from __main__ import cPickle, dumped')
14.006750106811523

另一方面,在使用模块的python版本时,我发现:

>>> mydict = dict(zip(range(100), range(100)))
>>> timeit.timeit('pickle.dumps(mydict,0)', 'from __main__ import pickle, mydict', number=10000)
2.9552500247955322
>>> timeit.timeit('pickle.dumps(mydict,1)', 'from __main__ import pickle, mydict', number=10000)
3.831756830215454
>>> timeit.timeit('pickle.dumps(mydict,2)', 'from __main__ import pickle, mydict', number=10000)
3.842888116836548

因此,使用协议 1 和 2 转储内置对象似乎比在 python 版本中使用协议 0 慢。但是在加载对象时,协议 0 又是三个中最慢的:

>>> dumped = pickle.dumps(mydict, 0)
>>> timeit.timeit('pickle.loads(dumped)', 'from __main__ import pickle, dumped', number=10000)
2.988792896270752
>>> dumped = pickle.dumps(mydict, 1)
>>> timeit.timeit('pickle.loads(dumped)', 'from __main__ import pickle, dumped', number=10000)
1.2793281078338623
>>> dumped = pickle.dumps(mydict, 2)
>>> timeit.timeit('pickle.loads(dumped)', 'from __main__ import pickle, dumped', number=10000)
1.5425071716308594

正如您在上述迷你基准测试中所见,腌制所花费的时间取决于许多因素,从您腌制的对象类型到您使用的腌制模块版本。如果没有更多信息,我们将无法解释为什么在您的情况下协议 2 慢得多。

【讨论】:

    猜你喜欢
    • 2019-11-13
    • 2022-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-13
    • 2015-02-24
    • 1970-01-01
    相关资源
    最近更新 更多