Как исправить эту операцию с привязкой к вводу-выводу python в файле .bin размером 12 ГБ? - PullRequest
0 голосов
/ 05 августа 2020

Я читаю эту книгу Практическое машинное обучение для алгоритмов c Торговля , и я наткнулся на скрипт, который должен анализировать большой .bin двоичный файл и преобразовывать его в .h5. Этот файл состоит из так называемых данных ITCH, техническую документацию данных можно найти здесь . Сценарий очень неэффективен, он читает файл размером 12 ГБ (12952050754 байта) по 2 байта за раз, что очень медленно (может занять до 4 часов на каком-нибудь приличном экземпляре GCP с 4 процессорами), что неудивительно. Вы можете найти всю записную книжку здесь .

Моя проблема в том, что я не понимаю, как читается этот .bin файл. Я имею в виду, что я не понимаю, где необходимость чтения файл 2 байта за раз, я думаю, что есть способ читать при большом размере буфера, но я не уверен, как это сделать, или даже преобразовать скрипт в c ++, если после оптимизации этого скрипта он все еще работает медленно что я могу сделать, если я понимаю внутреннюю работу этого процесса ввода-вывода, есть ли у кого-нибудь предложения?

вот ссылка на файл источник данных ITCH, вы можете найти небольшие файлы (300 МБ или меньше), которые предназначены для меньших периодов времени, если вам нужно поэкспериментировать с кодом.

Узкое место:

with file_name.open('rb') as data:
    while True:

        # determine message size in bytes
        message_size = int.from_bytes(data.read(2), byteorder='big', signed=False)
        
        # get message type by reading first byte
        message_type = data.read(1).decode('ascii')        
        message_type_counter.update([message_type])

        # read & store message
        record = data.read(message_size - 1)
        message = message_fields[message_type]._make(unpack(fstring[message_type], record))
        messages[message_type].append(message)
        
        # deal with system events
        if message_type == 'S':
            seconds = int.from_bytes(message.timestamp, byteorder='big') * 1e-9
            print('\n', event_codes.get(message.event_code.decode('ascii'), 'Error'))
            print(f'\t{format_time(seconds)}\t{message_count:12,.0f}')
            if message.event_code.decode('ascii') == 'C':
                store_messages(messages)
                break
        message_count += 1

        if message_count % 2.5e7 == 0:
            seconds = int.from_bytes(message.timestamp, byteorder='big') * 1e-9
            d = format_time(time() - start)
            print(f'\t{format_time(seconds)}\t{message_count:12,.0f}\t{d}')
            res = store_messages(messages)
            if res == 1:
                print(pd.Series(dict(message_type_counter)).sort_values())
                break
            messages.clear()

А вот функция store_messages():

def store_messages(m):
    """Handle occasional storing of all messages"""
    with pd.HDFStore(itch_store) as store:
        for mtype, data in m.items():
            # convert to DataFrame
            data = pd.DataFrame(data)

            # parse timestamp info
            data.timestamp = data.timestamp.apply(int.from_bytes, byteorder='big')
            data.timestamp = pd.to_timedelta(data.timestamp)

            # apply alpha formatting
            if mtype in alpha_formats.keys():
                data = format_alpha(mtype, data)

            s = alpha_length.get(mtype)
            if s:
                s = {c: s.get(c) for c in data.columns}
            dc = ['stock_locate']
            if m == 'R':
                dc.append('stock')
            try:
                store.append(mtype,
                         data,
                         format='t',
                         min_itemsize=s,
                         data_columns=dc)
            except Exception as e:
                print(e)
                print(mtype)
                print(data.info())
                print(pd.Series(list(m.keys())).value_counts())
                data.to_csv('data.csv', index=False)
                return 1
    return 0

1 Ответ

1 голос
/ 05 августа 2020

Согласно коду, формат файла выглядит как 2 байта размера сообщения, один байт типа сообщения и затем n байтов фактического сообщения (определяется размером ранее прочитанного сообщения).

Низко висящий плод для оптимизации нужно сначала прочитать 3 байта в список, преобразовать [0: 1] в размер сообщения int и [2] в тип сообщения, а затем прочитать сообщение ..

Чтобы дополнительно исключить количество требуемых чтений, вы можете прочитать фиксированный объем данных из файла в список и начать извлечение из него. При извлечении сохраняйте индекс уже обработанных байтов, и как только этот индекс или индекс + объем данных для чтения превысит размер списка, вы заполняете список заранее. Это может привести к огромным требованиям к памяти, если не будет сделано должным образом ..

...