Подтвердить что ты не робот

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

Есть ли ошибка в моем коде или я не понимаю документацию?

4b9b3361

Ответ 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) он должен работать.