Python中是否有无锁只读列表?
我做了一些基本的性能和内存消耗测试,想知道有没有办法让事情变得更快...
我有一个包含70,000个元素的大列表,里面是一个numpy的ndarray,以及这个列表中的文件路径以元组的形式存储。
我第一版的做法是把这个列表切片后传给每个进程,但这样会导致内存使用量飙升到超过20GB。
第二版我把它放到了全局空间,通过索引像foo[i]这样在每个进程的循环中访问,这样似乎把它放到了共享内存区域,进程之间的内存使用没有爆炸(保持在大约3GB)。
不过根据性能基准测试和跟踪,似乎大部分时间都花在了“获取”模式上...
所以我在想有没有办法把这个列表变成某种无锁/只读的状态,这样我就可以省去一部分获取的步骤,从而进一步加快访问速度。
编辑 1:这是应用程序性能分析的前几行输出:
ncalls tottime percall cumtime percall filename:lineno(function)
65 2450.903 37.706 2450.903 37.706 {built-in method acquire}
39320 0.481 0.000 0.481 0.000 {method 'read' of 'file' objects}
600 0.298 0.000 0.298 0.000 {posix.waitpid}
48 0.271 0.006 0.271 0.006 {posix.fork}
编辑 2:这是列表结构的一个例子:
# Sample code for a rough idea of how the list is constructed
sim = []
for root, dirs, files in os.walk(rootdir):
path = os.path.join(root, filename)
image= Image.open(path)
np_array = np.asarray(image)
sim.append( (np_array, path) )
# Roughly it would look something like say this below
sim = List( (np.array([[1, 2, 3], [4, 5, 6]], np.int32), "/foobar/com/what.something") )
因此,从此以后,SIM列表将是只读的。
1 个回答
multiprocessing
模块正好提供了你需要的功能:一个可以共享的数组,并且可以选择是否加锁,这个功能是通过multiprocessing.Array
类来实现的。如果你不想加锁,可以在创建的时候传入lock=False
。
编辑(考虑到你的更新):事情比我最开始想的要复杂得多。你列表中所有元素的数据需要在共享内存中创建。把列表本身(也就是指向实际数据的指针)放在共享内存中并不是特别重要,因为这部分数据相对于所有文件的数据来说是比较小的。要把文件数据存储在共享内存中,可以使用
shared_data = multiprocessing.sharedctypes.RawArray("c", data)
这里的data
是你从文件中读取的数据。要在某个进程中将其作为NumPy数组使用,可以用
numpy.frombuffer(shared_data, dtype="c")
这样可以为共享数据创建一个NumPy数组的视图。同样,要把路径名放入共享内存,可以使用
shared_path = multiprocessing.sharedctypes.RawArray("c", path)
这里的path是一个普通的Python字符串。在你的进程中,你可以通过shared_path.raw
来访问这个字符串。现在把(shared_data, shared_path)
添加到你的列表中。这个列表会被复制到其他进程中,但实际的数据不会被复制。
我最开始打算用multiprocessing.Array
来存储实际的列表。这是完全可行的,并且可以确保列表本身(也就是指向数据的指针)也在共享内存中。但现在我觉得这并不是特别重要,只要实际的数据是共享的就可以了。