O cache do buffer de disco do Linux torna o python cPickle mais eficiente do que arquivar?
Pergunta
O IO é mais eficiente, devido ao cache do buffer de disco do Linux, ao armazenar objetos python acessados com frequência como arquivos cPickle separados, em vez de armazenar todos os objetos em uma prateleira grande?
O cache do buffer de disco opera de maneira diferente nesses dois cenários em relação à eficiência?
Pode haver milhares de arquivos grandes (geralmente em torno de 100 Mb, mas às vezes 1 Gb), mas muita RAM (por exemplo, 64 Gb).
Solução
Não conheço nenhuma maneira teórica de decidir qual método é mais rápido e, mesmo que conhecesse, não tenho certeza se confiaria nele.Então, vamos escrever um código e testá-lo.
Se empacotarmos nossos gerenciadores pickle/shelve em classes com uma interface comum, será fácil trocá-los dentro e fora do seu código.Portanto, se em algum momento futuro você descobrir que uma é melhor que a outra (ou descobrir alguma maneira ainda melhor), tudo o que você precisa fazer é escrever uma classe com a mesma interface e você poderá conectar a nova classe ao seu código com muito pouca modificação em qualquer outra coisa.
teste.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)
Normalmente, você usaria o PickleManager assim:
manager=PickleManager()
manager.load(...)
manager.store(...)
enquanto você usaria o ShelveManager assim:
with ShelveManager('/tmp/shelve.dat') as manager:
manager.load(...)
manager.store(...)
Mas para testar o desempenho, você poderia fazer algo assim:
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())'
Pelo menos na minha máquina, os resultados foram assim:
read (ms) write (ms)
PickleManager 9.26 7.92
ShelveManager 5.32 30.9
Portanto, parece que o ShelveManager pode ser mais rápido na leitura, mas o PickleManager pode ser mais rápido na escrita.
Certifique-se de executar esses testes você mesmo.Os resultados do Timeit podem variar devido à versão do Python, sistema operacional, tipo de sistema de arquivos, hardware, etc.
Além disso, observe meu write
e read
funções geram arquivos muito pequenos.Você desejará testar isso em dados mais semelhantes ao seu caso de uso.