Как получить время, необходимое для распаковки больших файлов bz2?
Мне нужно обрабатывать большие файлы bz2 (~6G), используя Python, распаковывая его построчно, используя BZ2File.readline()
, Проблема в том, что я хочу знать, сколько времени нужно для обработки всего файла.
Я провел много поисков, попытался определить фактический размер распакованного файла, чтобы я мог узнать процент обработанного на лету и, следовательно, оставшееся время, в то время как обнаружил, что невозможно узнать размер распакованного файла. без предварительной распаковки ( /questions/27683924/razmer-neszhatogo-fajla-python-bz2/27683936#27683936).
Помимо того, что распаковка файла занимает много памяти, распаковка занимает много времени. Итак, кто-нибудь может помочь мне получить оставшееся время обработки на лету?
3 ответа
Вы можете оценить оставшееся время на основе потребления сжатых данных вместо производства несжатых данных. Результат будет примерно таким же, если данные будут относительно однородными. (Если это не так, то использование входных данных или выходных данных в любом случае не даст точной оценки.)
Вы можете легко найти размер сжатого файла и использовать время, потраченное на сжатые данные, чтобы оценить время для обработки оставшихся сжатых данных.
Вот простой пример использования BZ2Decompress
Объект для обработки входных данных чанка за раз, показывающий ход чтения (Python 3, получение имени файла из командной строки):
# Decompress a bzip2 file, showing progress based on consumed input.
import sys
import os
import bz2
import time
def proc(input):
"""Decompress and process a piece of a compressed stream"""
dat = dec.decompress(input)
got = len(dat)
if got != 0: # 0 is common -- waiting for a bzip2 block
# process dat here
pass
return got
# Get the size of the compressed bzip2 file.
path = sys.argv[1]
size = os.path.getsize(path)
# Decompress CHUNK bytes at a time.
CHUNK = 16384
totin = 0
totout = 0
prev = -1
dec = bz2.BZ2Decompressor()
start = time.time()
with open(path, 'rb') as f:
for chunk in iter(lambda: f.read(CHUNK), b''):
# feed chunk to decompressor
got = proc(chunk)
# handle case of concatenated bz2 streams
if dec.eof:
rem = dec.unused_data
dec = bz2.BZ2Decompressor()
got += proc(rem)
# show progress
totin += len(chunk)
totout += got
if got != 0: # only if a bzip2 block emitted
frac = round(1000 * totin / size)
if frac != prev:
left = (size / totin - 1) * (time.time() - start)
print(f'\r{frac / 10:.1f}% (~{left:.1f}s left) ', end='')
prev = frac
# Show the resulting size.
print(end='\r')
print(totout, 'uncompressed bytes')
Можно использовать непосредственно существующие высокоуровневые API, предоставляемые
bz2
Модуль Python и в то же время получать информацию от базового обработчика файлов о том, сколько сжатых данных было обработано.
import bz2
import datetime
import time
with bz2.open(input_filename, 'rt', encoding='utf8') as input_file:
underlying_file = input_file.buffer._buffer.raw._fp
underlying_file.seek(0, io.SEEK_END)
underlying_file_size = underlying_file.tell()
underlying_file.seek(0, io.SEEK_SET)
lines_count = 0
start_time = time.perf_counter()
progress = f'{0:.2f}%'
while True:
line = input_file.readline().strip()
if not line:
break
process_line(line)
lines_count += 1
current_position = underlying_file.tell()
new_progress = f'{current_position / underlying_file_size * 100:.2f}%'
if progress != new_progress:
progress = new_progress
current_time = time.perf_counter()
elapsed_time = current_time - start_time
elapsed = datetime.timedelta(seconds=elapsed_time)
remaining = datetime.timedelta(seconds=(underlying_file_size / current_position - 1) * elapsed_time)
print(f"{lines_count} lines processed, {progress}, {elapsed} elapsed, {remaining} remaining")
Если вы читаете не текстовые файлы, а двоичные файлы, вам необходимо использовать:
with bz2.open(input_filename, 'rb') as input_file:
underlying_file = input_file._buffer.raw._fp
...
С помощью другого ответа, наконец, я нашел решение. Идея состоит в том, чтобы использовать размер обработанного сжатого файла, общий размер сжатого файла и время, используемое для оценки оставшегося времени. Для достижения этой цели,
- прочитать сжатый файл как объект байта в память:
byte_data
, что довольно быстро - рассчитать размер
byte_data
с помощьюtotal_size = len(byte_data)
- заворачивать
byte_data
какbyte_f = io.BytesIO(byte_data)
- заворачивать
byte_f
какbz2f = bz2.BZ2File(byte_f)
- во время обработки используйте
pos = byte_f.tell()
получить текущую позицию в сжатом файле - рассчитать точный процент обработки
percent = pos/total_size
- записать использованное время и рассчитать оставшееся время
Через несколько секунд оценка может стать довольно точной:
0.01% processed, 2.00s elapsed, 17514.27s remaining...
0.02% processed, 4.00s elapsed, 20167.48s remaining...
0.03% processed, 6.00s elapsed, 21239.60s remaining...
0.04% processed, 8.00s elapsed, 21818.91s remaining...
0.05% processed, 10.00s elapsed, 22180.76s remaining...
0.05% processed, 12.00s elapsed, 22427.78s remaining...
0.06% processed, 14.00s elapsed, 22661.80s remaining...
0.07% processed, 16.00s elapsed, 22840.45s remaining...
0.08% processed, 18.00s elapsed, 22937.07s remaining...
....
99.97% processed, 22704.28s elapsed, 6.27s remaining...
99.98% processed, 22706.28s elapsed, 4.40s remaining...
99.99% processed, 22708.28s elapsed, 2.45s remaining...
100.00% processed, 22710.28s elapsed, 0.54s remaining...