Linux磁盘缓存是否使得Python cPickle比shelve更加高效?

4

如果将频繁访问的Python对象存储为单独的cPickle文件而不是将所有对象存储在一个大的shelf中,是否由于Linux磁盘缓冲区缓存,IO更有效?

这两种情况下磁盘缓冲区缓存的效率是否不同?

可能有成千上万个大文件(通常约100Mb,但有时为1Gb),但有很多RAM(例如64 Gb)。


1
一如既往,最安全的方法是尝试而不是思考这个或那个。 - user395760
当然可以,但我想了解Python如何与Linux磁盘缓存交互,这方面我一无所知。 - ricopan
我相信Python不会直接与任何操作系统缓存交互……性能将取决于文件系统本身。只需将pickle的大小作为参数,并运行一些基准测试。我的直觉告诉我,如果您需要频繁读写,则最好使用shelf。如果您偶尔进行读/写操作,请使用单独的pickles。 - Daniel Naab
我听到很多人都在提这个建议,而且unutbu在下面提供了一些不错的测试代码,我会运行一下。我曾经以为文件大小和缓存缓冲效率之间可能有一些简单的关系,但现在我打算停止胡思乱想了。 - ricopan
1个回答

3

我不知道有任何理论方法来决定哪种方法更快,即使我知道,我也不确定我会相信它。所以让我们编写一些代码并进行测试。

如果我们将pickle / shelve管理器打包到具有公共接口的类中,则可以轻松地将它们互换到您的代码中。因此,如果在将来某个时间点您发现其中一种比另一种更好(或发现了更好的方法),则只需编写具有相同接口的类,即可将新类插入到您的代码中,而无需对其他任何内容进行大量修改。

test.py:

import cPickle
import shelve
import os

class PickleManager(object):
    def store(self,name,value):
        with open(name,'w') as f:
            cPickle.dump(value,f)
    def load(self,name):
        with open(name,'r') as f:
            return cPickle.load(f)

class ShelveManager(object):
    def __enter__(self):
        if os.path.exists(self.fname):
            self.shelf=shelve.open(self.fname)
        else:
            self.shelf=shelve.open(self.fname,'n')
        return self
    def __exit__(self,ext_type,exc_value,traceback):
        self.shelf.close()
    def __init__(self,fname):
        self.fname=fname
    def store(self,name,value):
        self.shelf[name]=value        
    def load(self,name):
        return self.shelf[name]

def write(manager):                
    for i in range(100):
        fname='/tmp/{i}.dat'.format(i=i)
        data='The sky is so blue'*100
        manager.store(fname,data)
def read(manager):        
    for i in range(100):
        fname='/tmp/{i}.dat'.format(i=i)        
        manager.load(fname)

通常,您会像这样使用PickleManager:
manager=PickleManager()
manager.load(...)
manager.store(...)

您可以像这样使用ShelveManager:

with ShelveManager('/tmp/shelve.dat') as manager:        
    manager.load(...)
    manager.store(...)

但是为了测试性能,你可以尝试做这样的事情:
python -mtimeit -s'import test' 'with test.ShelveManager("/tmp/shelve.dat") as s: test.read(s)'
python -mtimeit -s'import test' 'test.read(test.PickleManager())'
python -mtimeit -s'import test' 'with test.ShelveManager("/tmp/shelve.dat") as s: test.write(s)'
python -mtimeit -s'import test' 'test.write(test.PickleManager())'

至少在我的电脑上,结果是这样的:

                  read (ms)     write (ms)
PickleManager     9.26          7.92 
ShelveManager     5.32          30.9 

看起来ShelveManager在读取方面可能更快,但PickleManager在写入方面可能更快。

请务必自己运行这些测试。Timeit的结果可能因Python版本、操作系统、文件系统类型、硬件等而有所不同。

此外,请注意我的writeread函数生成的是非常小的文件。您需要在更接近实际用例的数据上进行测试。


好的例子,谢谢。我会在我的测试用例上进行更广泛的运行,并报告结果。 - ricopan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接