Python 3 - Может ли мариновать байтовые объекты размером более 4 ГБ?
Основываясь на этом комментарие и связанной с ним документации, Pickle 4.0+ из Python 3.4+ должен иметь возможность сортировать байтовые объекты размером более 4 ГБ.
Однако, используя python 3.4.3 или python 3.5.0b2 в Mac OS X 10.10.4, я получаю сообщение об ошибке, когда я пытаюсь разложить большой массив байтов:
>>> import pickle
>>> x = bytearray(8 * 1000 * 1000 * 1000)
>>> fp = open("x.dat", "wb")
>>> pickle.dump(x, fp, protocol = 4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 22] Invalid argument
Есть ли ошибка в моем коде или я не понимаю документацию?
Ответы
Ответ 1
Вот простой обходной путь для проблемы 24658. Используйте pickle.loads
или pickle.dumps
и pickle.dumps
объект байтов на куски размером 2**31 - 1
чтобы получить его в файле или из него.
import pickle
import os.path
file_path = "pkl.pkl"
n_bytes = 2**31
max_bytes = 2**31 - 1
data = bytearray(n_bytes)
## write
bytes_out = pickle.dumps(data)
with open(file_path, 'wb') as f_out:
for idx in range(0, len(bytes_out), max_bytes):
f_out.write(bytes_out[idx:idx+max_bytes])
## read
bytes_in = bytearray(0)
input_size = os.path.getsize(file_path)
with open(file_path, 'rb') as f_in:
for _ in range(0, input_size, max_bytes):
bytes_in += f_in.read(max_bytes)
data2 = pickle.loads(bytes_in)
assert(data == data2)
Ответ 2
Подводя итог тому, что было ответировано в комментариях:
Да, Python может разбить байтовые объекты размером более 4 ГБ. Наблюдаемая ошибка вызвана ошибкой в реализации (см. Issue24658).
Ответ 3
Вот полный обходной путь, хотя кажется, что pickle.load больше не пытается выгружать огромный файл (я на Python 3.5.2), так что строго говоря, только для этого нужно pickle.dumps для правильной работы.
import pickle
class MacOSFile(object):
def __init__(self, f):
self.f = f
def __getattr__(self, item):
return getattr(self.f, item)
def read(self, n):
# print("reading total_bytes=%s" % n, flush=True)
if n >= (1 << 31):
buffer = bytearray(n)
idx = 0
while idx < n:
batch_size = min(n - idx, 1 << 31 - 1)
# print("reading bytes [%s,%s)..." % (idx, idx + batch_size), end="", flush=True)
buffer[idx:idx + batch_size] = self.f.read(batch_size)
# print("done.", flush=True)
idx += batch_size
return buffer
return self.f.read(n)
def write(self, buffer):
n = len(buffer)
print("writing total_bytes=%s..." % n, flush=True)
idx = 0
while idx < n:
batch_size = min(n - idx, 1 << 31 - 1)
print("writing bytes [%s, %s)... " % (idx, idx + batch_size), end="", flush=True)
self.f.write(buffer[idx:idx + batch_size])
print("done.", flush=True)
idx += batch_size
def pickle_dump(obj, file_path):
with open(file_path, "wb") as f:
return pickle.dump(obj, MacOSFile(f), protocol=pickle.HIGHEST_PROTOCOL)
def pickle_load(file_path):
with open(file_path, "rb") as f:
return pickle.load(MacOSFile(f))
Ответ 4
Чтение файла на 2 ГБ кусках требует в два раза больше памяти, если необходимо, если выполняется конкатенация bytes
, мой подход к загрузке соленья основан на bytearray:
class MacOSFile(object):
def __init__(self, f):
self.f = f
def __getattr__(self, item):
return getattr(self.f, item)
def read(self, n):
if n >= (1 << 31):
buffer = bytearray(n)
pos = 0
while pos < n:
size = min(n - pos, 1 << 31 - 1)
chunk = self.f.read(size)
buffer[pos:pos + size] = chunk
pos += size
return buffer
return self.f.read(n)
Использование:
with open("/path", "rb") as fin:
obj = pickle.load(MacOSFile(fin))
Ответ 5
Я также нашел эту проблему, чтобы решить эту проблему, я вырезаю код на несколько итераций. Скажем, в этом случае у меня есть 50 000 данных, которые я должен вычислять tf-idf и выполнять knn classfication. Когда я запускаю и прямо итерации 50.000, он дает мне "эту ошибку". Итак, чтобы решить эту проблему, я ее разблокирую.
tokenized_documents = self.load_tokenized_preprocessing_documents()
idf = self.load_idf_41227()
doc_length = len(documents)
for iteration in range(0, 9):
tfidf_documents = []
for index in range(iteration, 4000):
doc_tfidf = []
for term in idf.keys():
tf = self.term_frequency(term, tokenized_documents[index])
doc_tfidf.append(tf * idf[term])
doc = documents[index]
tfidf = [doc_tfidf, doc[0], doc[1]]
tfidf_documents.append(tfidf)
print("{} from {} document {}".format(index, doc_length, doc[0]))
self.save_tfidf_41227(tfidf_documents, iteration)
Ответ 6
Была та же проблема и исправлена путем обновления до Python 3.6.8.
Кажется, это пиарщик, который это сделал: https://github.com/python/cpython/pull/9937
Ответ 7
Вы можете указать протокол для дампа. Если вы делаете pickle.dump(obj,file,protocol=4)
он должен работать.