Python 为什么泡菜要比np.save花这么长时间?

Python 为什么泡菜要比np.save花这么长时间?,python,numpy,save,pickle,Python,Numpy,Save,Pickle,我想保存一个dict或数组 我尝试了np.save和pickle两种方法,发现前者花费的时间要少得多 我的实际数据要大得多,但为了演示起见,我在这里只展示了一小部分: import numpy as np #import numpy.array as array import time import pickle b = {0: [np.array([0, 0, 0, 0])], 1: [np.array([1, 0, 0, 0]), np.array([0, 1, 0, 0]), np.ar

我想保存一个
dict
或数组

我尝试了
np.save
pickle
两种方法,发现前者花费的时间要少得多

我的实际数据要大得多,但为了演示起见,我在这里只展示了一小部分:

import numpy as np
#import numpy.array as array
import time
import pickle

b = {0: [np.array([0, 0, 0, 0])], 1: [np.array([1, 0, 0, 0]), np.array([0, 1, 0, 0]), np.array([0, 0, 1, 0]), np.array([0, 0, 0, 1]), np.array([-1,  0,  0,  0]), np.array([ 0, -1,  0,  0]), np.array([ 0,  0, -1,  0]), np.array([ 0,  0,  0, -1])], 2: [np.array([2, 0, 0, 0]), np.array([1, 1, 0, 0]), np.array([1, 0, 1, 0]), np.array([1, 0, 0, 1]), np.array([ 1, -1,  0,  0]), np.array([ 1,  0, -1,  0]), np.array([ 1,  0,  0, -1])], 3: [np.array([1, 0, 0, 0]), np.array([0, 1, 0, 0]), np.array([0, 0, 1, 0]), np.array([0, 0, 0, 1]), np.array([-1,  0,  0,  0]), np.array([ 0, -1,  0,  0]), np.array([ 0,  0, -1,  0]), np.array([ 0,  0,  0, -1])], 4: [np.array([2, 0, 0, 0]), np.array([1, 1, 0, 0]), np.array([1, 0, 1, 0]), np.array([1, 0, 0, 1]), np.array([ 1, -1,  0,  0]), np.array([ 1,  0, -1,  0]), np.array([ 1,  0,  0, -1])], 5: [np.array([0, 0, 0, 0])], 6: [np.array([1, 0, 0, 0]), np.array([0, 1, 0, 0]), np.array([0, 0, 1, 0]), np.array([0, 0, 0, 1]), np.array([-1,  0,  0,  0]), np.array([ 0, -1,  0,  0]), np.array([ 0,  0, -1,  0]), np.array([ 0,  0,  0, -1])], 2: [np.array([2, 0, 0, 0]), np.array([1, 1, 0, 0]), np.array([1, 0, 1, 0]), np.array([1, 0, 0, 1]), np.array([ 1, -1,  0,  0]), np.array([ 1,  0, -1,  0]), np.array([ 1,  0,  0, -1])], 7: [np.array([1, 0, 0, 0]), np.array([0, 1, 0, 0]), np.array([0, 0, 1, 0]), np.array([0, 0, 0, 1]), np.array([-1,  0,  0,  0]), np.array([ 0, -1,  0,  0]), np.array([ 0,  0, -1,  0]), np.array([ 0,  0,  0, -1])], 8: [np.array([2, 0, 0, 0]), np.array([1, 1, 0, 0]), np.array([1, 0, 1, 0]), np.array([1, 0, 0, 1]), np.array([ 1, -1,  0,  0]), np.array([ 1,  0, -1,  0]), np.array([ 1,  0,  0, -1])]}


start_time = time.time()
with open('testpickle', 'wb') as myfile:
    pickle.dump(b, myfile)
print("--- Time to save with pickle: %s milliseconds ---" % (1000*time.time() - 1000*start_time))

start_time = time.time()
np.save('numpy', b)
print("--- Time to save with numpy: %s milliseconds ---" % (1000*time.time() - 1000*start_time))

start_time = time.time()
with open('testpickle', 'rb') as myfile:
    g1 = pickle.load(myfile)
print("--- Time to load with pickle: %s milliseconds ---" % (1000*time.time() - 1000*start_time))

start_time = time.time()
g2 = np.load('numpy.npy')
print("--- Time to load with numpy: %s milliseconds ---" % (1000*time.time() - 1000*start_time))
它给出了一个输出:

--- Time to save with pickle: 4.0 milliseconds ---
--- Time to save with numpy: 1.0 milliseconds ---
--- Time to load with pickle: 2.0 milliseconds ---
--- Time to load with numpy: 1.0 milliseconds ---
对于我的实际大小(dict中约100000个键),时间差更为明显

为什么pickle的保存和加载时间都比np.save长


我应该在什么时候使用pickle?

,因为只要写入的对象不包含Python数据

  • numpy对象在内存中的表示方式比Python对象简单得多
  • save是用C编写的
  • numpy.save以需要最少处理的超级简单格式写入
同时

  • Python对象有很多开销
  • pickle是用Python编写的
  • pickle将数据从内存中的底层表示转换为磁盘上写入的字节

请注意,如果numpy数组确实包含Python对象,那么numpy只会对数组进行pickle处理,所有的胜利都会消失。

因为只要写入的对象不包含Python数据

  • numpy对象在内存中的表示方式比Python对象简单得多
  • save是用C编写的
  • numpy.save以需要最少处理的超级简单格式写入
同时

  • Python对象有很多开销
  • pickle是用Python编写的
  • pickle将数据从内存中的底层表示转换为磁盘上写入的字节

请注意,如果一个numpy数组确实包含Python对象,那么numpy只需对数组进行pickle处理,所有的胜利都将消失。

这是因为
pickle
可以处理各种Python对象,并且是用纯Python编写的,而
np.save
是为数组设计的,并以高效的格式保存它们

从表面上看,它实际上可以在幕后使用pickle。这可能会限制Python版本之间的可移植性,并有执行任意代码的风险(这是取消勾选未知对象时的一般风险)


有用的参考资料:

这是因为
pickle
可以处理各种Python对象,并且是用纯Python编写的,而
np.save
是为数组设计的,并以高效的格式保存它们

从表面上看,它实际上可以在幕后使用pickle。这可能会限制Python版本之间的可移植性,并有执行任意代码的风险(这是取消勾选未知对象时的一般风险)


有用的参考资料:

我认为你需要更好的时间安排。我也不同意公认的答案

b
是一本有9个键的字典;这些值是数组的列表。这意味着
pickle.dump
np.save
将相互使用-
pickle
使用
save
来pickle数组,
save
使用
pickle
来保存字典和列表

保存
写入数组。这意味着它必须将字典包装在对象数据类型数组中才能保存它

In [6]: np.save('test1',b)
In [7]: d=np.load('test1.npy')
In [8]: d
Out[8]: 
array({0: [array([0, 0, 0, 0])], 1: [array([1, 0, 0, 0]), array([0, 1, 0, 0]), .... array([ 1, -1,  0,  0]), array([ 1,  0, -1,  0]), array([ 1,  0,  0, -1])]},
      dtype=object)
In [9]: d.shape
Out[9]: ()
In [11]: list(d[()].keys())
Out[11]: [0, 1, 2, 3, 4, 5, 6, 7, 8]
一些时间安排:

In [12]: timeit np.save('test1',b)
850 µs ± 36.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [13]: timeit d=np.load('test1.npy')
566 µs ± 6.44 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [20]: %%timeit 
    ...: with open('testpickle', 'wb') as myfile:
    ...:     pickle.dump(b, myfile)
    ...:     
505 µs ± 9.24 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [21]: %%timeit 
    ...: with open('testpickle', 'rb') as myfile:
    ...:     g1 = pickle.load(myfile)
    ...:     
152 µs ± 4.83 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
在我的计时中,
pickle
更快

pickle文件稍微小一些:

In [23]: ll test1.npy testpickle
-rw-rw-r-- 1 paul 5740 Aug 14 08:40 test1.npy
-rw-rw-r-- 1 paul 4204 Aug 14 08:43 testpickle

我认为你需要更好的时间安排。我也不同意公认的答案

b
是一本有9个键的字典;这些值是数组的列表。这意味着
pickle.dump
np.save
将相互使用-
pickle
使用
save
来pickle数组,
save
使用
pickle
来保存字典和列表

保存
写入数组。这意味着它必须将字典包装在对象数据类型数组中才能保存它

In [6]: np.save('test1',b)
In [7]: d=np.load('test1.npy')
In [8]: d
Out[8]: 
array({0: [array([0, 0, 0, 0])], 1: [array([1, 0, 0, 0]), array([0, 1, 0, 0]), .... array([ 1, -1,  0,  0]), array([ 1,  0, -1,  0]), array([ 1,  0,  0, -1])]},
      dtype=object)
In [9]: d.shape
Out[9]: ()
In [11]: list(d[()].keys())
Out[11]: [0, 1, 2, 3, 4, 5, 6, 7, 8]
一些时间安排:

In [12]: timeit np.save('test1',b)
850 µs ± 36.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [13]: timeit d=np.load('test1.npy')
566 µs ± 6.44 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [20]: %%timeit 
    ...: with open('testpickle', 'wb') as myfile:
    ...:     pickle.dump(b, myfile)
    ...:     
505 µs ± 9.24 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [21]: %%timeit 
    ...: with open('testpickle', 'rb') as myfile:
    ...:     g1 = pickle.load(myfile)
    ...:     
152 µs ± 4.83 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
在我的计时中,
pickle
更快

pickle文件稍微小一些:

In [23]: ll test1.npy testpickle
-rw-rw-r-- 1 paul 5740 Aug 14 08:40 test1.npy
-rw-rw-r-- 1 paul 4204 Aug 14 08:43 testpickle

你说的“对象”是指方法、函数等?我指的是任何不是numpy值的值。触发
.dtype.hasobject
变为true的值。例如,
np.array([1,“foo”])
是可以的,
np.array([lambda x:x+1])
np.array([{}])
不是可以的。很抱歉,你不知道,但最后两个例子“不好”到底是什么意思?anbd“python对象有很多开销”是什么意思?我的意思是
np.array([{}]).dtype.hasobject
True
,因此
np.save
将使用pickle来表示它,而不是它自己的表示,这反过来意味着它实际上比pickle稍微慢一点。关于开销,python是一种动态语言,Python对象需要有很多C所没有的额外信息(因为编译器需要代码知道所有内容的位置和内容)。例如,对于
a=list(范围(100))
,比较
sys.getsizeof(a)+sum(sys.getsizeof(x)for a中的x)
(内存中Python列表的大小)与
np.array(a).nbytes
(相同numpy数组的大小)。如果你做了
np.array(a,dtype=np.uint8).nbytes
,甚至更大的区别,但把它改成非numpy类型(例如dicts),numpy就不能将它们存储在适当的位置,并存储指针:例如,对于
a=[{i:i}对于范围(100)]
你指的是方法、函数等?我指的是任何不是numpy值的值。触发
.dtype.hasobject
变为true的值。例如,
np.array([1,“foo”])
是可以的,
np.array([lambda x:x+1])
np.array([{}])
不是可以的。很抱歉,你不知道,但最后两个例子“不好”到底是什么意思?anbd“python对象有很多开销”是什么意思?我的意思是
np.array([{}]).dtype.hasobject
True
,因此