读取大文件使用 100% 内存,我的整个电脑都死机了
reading large file uses 100% memory and my whole pc frozes
我制作了一个简单的应用程序来加密和解密文件。但是当我加载一个像 2gb 这样的大文件时,我的程序使用了 100% 的内存。我使用多处理和多线程。
poolSize = min(cpu_count(), len(fileList))
process_pool = Pool(poolSize)
thread_pool = ThreadPool(len(fileList))
lock = Lock()
worker = partial(encfile, process_pool, lock)
thread_pool.map(worker, fileList)
def encfile(process_pool, lock, file):
with open(file, 'rb') as original_file:
original = original_file.read()
encrypted = process_pool.apply(encryptfn, args=(key, original,))
with open (file, 'wb') as encrypted_file:
encrypted_file.write(encrypted)
这是我的总体思路:
由于内存是个问题,你必须以更小的块读取文件,比如 64K 块并加密每个 64K 块并将它们写出。当然,加密块的长度不是 64K,所以问题就变成了如何解密。因此每个加密块必须以 fixed-length header 为前缀,它只不过是编码为 4 字节无符号整数的后续加密块的长度(这应该绰绰有余)。解密算法循环首先读取下一个4字节长度,然后从中知道后面的加密块有多少字节。
顺便说一下,如果您不使用 encfile
一个 lock
,则不需要传递给它,例如计算已处理的文件数。
from tempfile import mkstemp
from os import fdopen, replace
BLOCKSIZE = 64 * 1024
ENCRYPTED_HEADER_LENGTH = 4
def encfile(process_pool, lock, file):
"""
Encrypt file in place.
"""
fd, path = mkstemp() # make a temporary file
with open(file, 'rb') as original_file, \
fdopen (fd, 'wb') as encrypted_file:
while True:
original = original_file.read(BLOCKSIZE)
if not original:
break
encrypted = process_pool.apply(encryptfn, args=(key, original))
l = len(encrypted)
l_bytes = l.to_bytes(ENCRYPTED_HEADER_LENGTH, 'big')
encrypted_file.write(l_bytes)
encrypted_file.write(encrypted)
replace(path, file)
def decfile(file):
"""
Decrypt files in place.
"""
fd, path = mkstemp() # make a temporary file
with open(file, 'rb') as encrypted_file, \
fdopen (fd, 'wb') as original_file:
while True:
l_bytes = encrypted_file.read(ENCRYPTED_HEADER_LENGTH)
if not l_bytes:
break
l = int.from_bytes(l_bytes, 'big')
encrypted = encrypted_file.read(l)
decrypted = decryptfn(key, encrypted)
original_file.write(decrypted)
replace(path, file)
说明
块大小越大,需要的内存就越多(您的原始程序读取整个文件;该程序一次只能读取 64K)。但我假设块大小太小会导致对加密的调用太多,这是通过多处理完成的,这将需要更多 CPU 开销——所以这是一个权衡。 64K 是 任意 。如果你有记忆,可以增加很多。您甚至可以尝试 1024 * 1024 (1M)。
我之前试图解释以下内容,但让我详细说明一下:
假设当您加密一个 64K 块时,该特定 64K 块的加密大小最终为 67,986 字节长(不同的 64K 块加密通常具有不同的长度,除非其未加密的值恰好已被加密相同)。如果我只写出没有其他信息的数据,我需要一些方法来知道要解密文件,首先需要读回 67,986 字节的数据并将其传递给解密方法(使用正确的密钥,当然)因为你必须解密加密内容的精确结果,不能少也不能多。换句话说,您不能只读回任意块中的加密文件并将这些块传递给解密方法。但那样会怎样?因此,了解每个加密块有多大的唯一方法是在这些块前面加上一个 header 前缀,它给出了下一个块的长度。
l_bytes = l.to_bytes(ENCRYPTED_HEADER_LENGTH, 'big')
获取存储在变量 l
中的整数长度,并将其编码为大小为 ENCRYPTED_HEADER_LENGTH 的字节数组,采用“大端”顺序,这意味着字节从高位开始排列字节到低位字节:
>>> ENCRYPTED_HEADER_LENGTH = 4
>>> l = 67986
>>> l_bytes = l.to_bytes(ENCRYPTED_HEADER_LENGTH, 'big')
>>> l_bytes
b'\x00\x01\t\x92'
>>> l_bytes = l.to_bytes(ENCRYPTED_HEADER_LENGTH, 'little')
>>> l_bytes
b'\x92\t\x01\x00'
>>>
\t
是值为 \x09
的制表符,因此我们将写出 0010992,这是 67986
的 4 字节十六进制值
我制作了一个简单的应用程序来加密和解密文件。但是当我加载一个像 2gb 这样的大文件时,我的程序使用了 100% 的内存。我使用多处理和多线程。
poolSize = min(cpu_count(), len(fileList))
process_pool = Pool(poolSize)
thread_pool = ThreadPool(len(fileList))
lock = Lock()
worker = partial(encfile, process_pool, lock)
thread_pool.map(worker, fileList)
def encfile(process_pool, lock, file):
with open(file, 'rb') as original_file:
original = original_file.read()
encrypted = process_pool.apply(encryptfn, args=(key, original,))
with open (file, 'wb') as encrypted_file:
encrypted_file.write(encrypted)
这是我的总体思路:
由于内存是个问题,你必须以更小的块读取文件,比如 64K 块并加密每个 64K 块并将它们写出。当然,加密块的长度不是 64K,所以问题就变成了如何解密。因此每个加密块必须以 fixed-length header 为前缀,它只不过是编码为 4 字节无符号整数的后续加密块的长度(这应该绰绰有余)。解密算法循环首先读取下一个4字节长度,然后从中知道后面的加密块有多少字节。
顺便说一下,如果您不使用 encfile
一个 lock
,则不需要传递给它,例如计算已处理的文件数。
from tempfile import mkstemp
from os import fdopen, replace
BLOCKSIZE = 64 * 1024
ENCRYPTED_HEADER_LENGTH = 4
def encfile(process_pool, lock, file):
"""
Encrypt file in place.
"""
fd, path = mkstemp() # make a temporary file
with open(file, 'rb') as original_file, \
fdopen (fd, 'wb') as encrypted_file:
while True:
original = original_file.read(BLOCKSIZE)
if not original:
break
encrypted = process_pool.apply(encryptfn, args=(key, original))
l = len(encrypted)
l_bytes = l.to_bytes(ENCRYPTED_HEADER_LENGTH, 'big')
encrypted_file.write(l_bytes)
encrypted_file.write(encrypted)
replace(path, file)
def decfile(file):
"""
Decrypt files in place.
"""
fd, path = mkstemp() # make a temporary file
with open(file, 'rb') as encrypted_file, \
fdopen (fd, 'wb') as original_file:
while True:
l_bytes = encrypted_file.read(ENCRYPTED_HEADER_LENGTH)
if not l_bytes:
break
l = int.from_bytes(l_bytes, 'big')
encrypted = encrypted_file.read(l)
decrypted = decryptfn(key, encrypted)
original_file.write(decrypted)
replace(path, file)
说明
块大小越大,需要的内存就越多(您的原始程序读取整个文件;该程序一次只能读取 64K)。但我假设块大小太小会导致对加密的调用太多,这是通过多处理完成的,这将需要更多 CPU 开销——所以这是一个权衡。 64K 是 任意 。如果你有记忆,可以增加很多。您甚至可以尝试 1024 * 1024 (1M)。
我之前试图解释以下内容,但让我详细说明一下:
假设当您加密一个 64K 块时,该特定 64K 块的加密大小最终为 67,986 字节长(不同的 64K 块加密通常具有不同的长度,除非其未加密的值恰好已被加密相同)。如果我只写出没有其他信息的数据,我需要一些方法来知道要解密文件,首先需要读回 67,986 字节的数据并将其传递给解密方法(使用正确的密钥,当然)因为你必须解密加密内容的精确结果,不能少也不能多。换句话说,您不能只读回任意块中的加密文件并将这些块传递给解密方法。但那样会怎样?因此,了解每个加密块有多大的唯一方法是在这些块前面加上一个 header 前缀,它给出了下一个块的长度。
l_bytes = l.to_bytes(ENCRYPTED_HEADER_LENGTH, 'big')
获取存储在变量 l
中的整数长度,并将其编码为大小为 ENCRYPTED_HEADER_LENGTH 的字节数组,采用“大端”顺序,这意味着字节从高位开始排列字节到低位字节:
>>> ENCRYPTED_HEADER_LENGTH = 4
>>> l = 67986
>>> l_bytes = l.to_bytes(ENCRYPTED_HEADER_LENGTH, 'big')
>>> l_bytes
b'\x00\x01\t\x92'
>>> l_bytes = l.to_bytes(ENCRYPTED_HEADER_LENGTH, 'little')
>>> l_bytes
b'\x92\t\x01\x00'
>>>
\t
是值为 \x09
的制表符,因此我们将写出 0010992,这是 67986