Какой самый быстрый способ сортировки и распаковки большого байтового массива? - PullRequest
0 голосов
/ 02 мая 2019

У меня большой двоичный файл, который необходимо преобразовать в формат файла hdf5.

Я использую Python3.6. Моя идея - прочитать файл, отсортировать соответствующую информацию, распаковать ее и сохранить. Моя информация хранится таким образом, что за 8-байтовым временем следуют 2 байта энергии, а затем 2 байта дополнительной информации, затем снова время ... Мой текущий способ сделать это следующий (моя информация читается как bytearray, с именем byte_array):

for i in range(0, len(byte_array)+1, 12):

    if i == 0:
        timestamp_bytes = byte_array[i:i+8]
        energy_bytes = byte_array[i+8:i+10]
        extras_bytes = byte_array[i+10:i+12]
    else:
        timestamp_bytes += byte_array[i:i+8]
        energy_bytes += byte_array[i+8:i+10]
        extras_bytes += byte_array[i+10:i+12]


timestamp_array = np.ndarray((len(timestamp_bytes)//8,), '<Q',timestamp_bytes)
energy_array = np.ndarray((len(energy_bytes) // 2,), '<h', energy_bytes)
extras_array = np.ndarray((len(timestamp_bytes) // 8,), '<H', extras_bytes)

Я предполагаю, что есть гораздо более быстрый способ сделать это, возможно, избегая обхода всего этого. Мои файлы имеют размер до 15 ГБ, поэтому каждое улучшение может помочь.

Ответы [ 3 ]

1 голос
/ 02 мая 2019

Вы должны просто указать NumPy интерпретировать данные как структурированный массив и извлечь поля:

as_structured = numpy.ndarray(shape=(len(byte_array)//12,),
                              dtype='<Q, <h, <H',
                              buffer=byte_array)
timestamps = as_structured['f0']
energies = as_structured['f1']
extras = as_structured['f2']

Это создаст три массива, поддерживаемых входным байтовым массивом. Создание этих массивов должно быть практически мгновенным, но я не могу гарантировать, что работа с ними будет быстрой - я думаю, что NumPy, возможно, потребуется выполнить неявное копирование для обработки проблем выравнивания с этими массивами. Возможно (я не знаю), что их явное копирование с помощью .copy() сначала может ускорить процесс.

0 голосов
/ 02 мая 2019

Я не эксперт по NumPy, но вот мои 5 центов: У вас много данных, и, вероятно, это больше, чем ваша оперативная память. Это указывает на самое простое решение - не пытайтесь разместить все данные в вашей программе. Когда вы читаете файл в переменную - тогда X GB читается в RAM. Если это больше, чем доступная оперативная память, то замена осуществляется вашей ОС. Подкачка замедляет работу, поскольку у вас не только есть операции с диском для чтения из исходного файла, но теперь у вас также есть запись на диск для сброса содержимого оперативной памяти в файл подкачки. Вместо этого напишите сценарий так, чтобы он по мере необходимости использовал части входного файла (в любом случае вы читаете файл в любом случае и не возвращаетесь назад или не прыгаете далеко вперед).

Попробуйте открыть входной файл как структура данных с отображением в памяти (обратите внимание на различия в использовании между средами Unix и Windows)

Затем вы можете делать простые read([n]) байты за раз и добавлять их в свои массивы. За кулисами данные считываются в ОЗУ постранично за страницей по мере необходимости и также не будут превышать доступную память, оставляя больше места для роста ваших массивов.

Также учтите тот факт, что ваши результирующие массивы также могут перерасти ОЗУ, что приведет к замедлению, аналогичному чтению большого файла.

0 голосов
/ 02 мая 2019

Вы можете использовать numpy.frombuffer с пользовательским типом данных:

import struct
import random

import numpy as np


data = [
    (random.randint(0, 255**8), random.randint(0, 255*255), random.randint(0, 255*255))
    for _ in range(20)
    ]

Bytes = b''.join(struct.pack('<Q2H', *row) for row in data)
dtype = np.dtype([('time', np.uint64), 
                  ('energy', np.uint16), # you may need to change that to `np.int16`, if energy can be negative
                  ('extras', np.uint16)])

original = np.array(data, dtype=np.uint64)
result = np.frombuffer(Bytes, dtype)

print((result['time'] == original[:, 0]).all())
print((result['energy'] == original[:, 1]).all())
print((result['extras'] == original[:, 2]).all())

print(result)

Пример вывода:

True
True
True
[(6048800706604665320, 52635, 291) (8427097887613035313, 15520, 4976)
 (3250665110135380002, 44078, 63748) (17867295175506485743, 53323, 293)
 (7840430102298790024, 38161, 27601) (15927595121394361471, 47152, 40296)
 (8882783920163363834, 3480, 46666) (15102082728995819558, 25348, 3492)
 (14964201209703818097, 60557, 4445) (11285466269736808083, 64496, 52086)
 (6776526382025956941, 63096, 57267) (5265981349217761773, 19503, 32500)
 (16839331389597634577, 49067, 46000) (16893396755393998689, 31922, 14228)
 (15428810261434211689, 32003, 61458) (5502680334984414629, 59013, 42330)
 (6325789410021178213, 25515, 49850) (6328332306678721373, 59019, 64106)
 (3222979511295721944, 26445, 37703) (4490370317582410310, 52413, 25364)]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...