Linux磁盘缓冲区缓存是否让python cPickle比shelve更高效?

4 投票
1 回答
739 浏览
提问于 2025-04-16 06:20

在Linux系统中,IO(输入输出)操作是否因为磁盘缓存的存在,而在把经常使用的Python对象单独存成cPickle文件时更高效,而不是把所有对象放在一个大文件里呢?

在这两种情况下,磁盘缓存的工作方式是否有所不同,从而影响效率呢?

可能会有成千上万个大文件(一般大约100Mb,有时会达到1Gb),但内存却很大(比如64Gb)。

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函数生成的文件非常小。你需要在更接近你实际使用情况的数据上进行测试。

撰写回答