2010-02-23 5 views
2

Права, я перебор большого двоичного файлPython: Самый быстрый способ итерации это через большой файл

мне нужно, чтобы минимизировать время этого цикла:

def NB2(self, ID_LEN): 
    r1=np.fromfile(ReadFile.fid,dTypes.NB_HDR,1) 
    num_receivers=r1[0][0] 
    num_channels=r1[0][1] 
    num_samples=r1[0][5] 

    blockReturn = np.zeros((num_samples,num_receivers,num_channels)) 

    for rec in range(0,num_receivers): 
     for chl in range(0,num_channels): 
      for smpl in range(0,num_samples): 
       r2_iq=np.fromfile(ReadFile.fid,np.int16,2) 
       blockReturn[smpl,rec,chl] = np.sqrt(math.fabs(r2_iq[0])*math.fabs(r2_iq[0]) + math.fabs(r2_iq[1])*math.fabs(r2_iq[1])) 

    return blockReturn 

Итак, что происходит на это следующим образом: r1 является заголовок файла, dTypes.NB_HDR тип я сделал:

NB_HDR= np.dtype([('f3',np.uint32),('f4',np.uint32),('f5',np.uint32),('f6',np.int32),('f7',np.int32),('f8',np.uint32)]) 

это получает всю информацию о предстоящем блоке данных, и красиво ставит нас в праве позиции в файле (начало блока данных!).

В этом блоке данных есть: 4096 выборок на канал, 4 канала на приемник, 9 приемников.

Так что num_receivers, num_channels, num_samples всегда будут одинаковыми (на данный момент в любом случае), но, как вы можете видеть, это довольно большой объем данных. Каждый «образец» представляет собой пару значений int16, которые я хочу найти величиной (следовательно, Pythagoras).

Этот код NB2 выполняется для каждого «Блока» в файле, для 12-гигабайтного файла (насколько он велик) имеется около 20 900 блоков, и мне нужно пройти через 1000 из этих файлов (так , 12 ТБ в целом). Любое преимущество в скорости, даже если это миллисекунды, будет широко оценено.

EDIT: На самом деле может быть полезно знать, как я перемещаюсь внутри файла. У меня есть функция следующим образом:

def navigateTo(self, blockNum, indexNum): 
    ReadFile.fid.seek(ReadFile.fileIndex[blockNum][indexNum],0) 
    ReadFile.currentBlock = blockNum 
    ReadFile.index = indexNum 

Перед тем, как запустить весь этот код я просканировать файл и список местоположений индексов в ReadFile.fileIndex просматриваемых с помощью этой функции, а затем «стремятся» к абсолютному месту - Это эффективно?

Приветствие

ответ

3

Поскольку вы знаете длину блока после чтения заголовка, сразу прочитайте весь блок. Затем перекроить массив (очень быстро, влияет только метаданные) и принимать использовать np.hypot ufunc:

blockData = np.fromfile(ReadFile.fid, np.int16, num_receivers*num_channels*num_samples*2) 
blockData = blockData.reshape((num_receivers, num_channes, num_samples, 2)) 
return np.hypot(blockData[:,:,:,0], blockData[:,:,:,1]) 

На моей машине он работает в 11мс на блоке.

+0

Это удивительное решение, если у вас достаточно памяти для одновременного загрузки всех каналов приемника в память. –

+0

Удивительно, буквально, спасибо! Выполнение этого в течение 10 мс на блок, то есть в 10 раз улучшение! –

0

Это больше наблюдения, чем решение, но перенос этой функции на C++ и загрузить его в с API Python будет получить вам много выигрыша в скорости, чтобы начать с до оптимизации контура.

+0

Я вообще не знаю C++, я боюсь (смущаю, что знаю). Любые идеи о том, как вы это сделаете? Я предполагаю, что 3-мерные массивы наряду с бинарным извлечением непосредственно на int16 без бита и все, что низкоуровневая гадость не так просто? –

+0

Я не думаю, что это так плохо, как вы думаете. Это, как говорится, я с трудом представляю вашу структуру данных, поскольку я не очень хорошо владею Python. С помощью функции ifstream :: seekg() вы можете захватывать свои номера в последовательности, зависящей от количества необходимых байтов и заливки + сохранения в векторах. – Xorlev

+0

@ Duncan Tait: У вас нет абсолютно никаких оснований быть смущенным. –

1

Я бы попытался использовать как можно меньше циклов и как можно больше констант. Все, что можно сделать линейным способом, должно быть сделано так. Если значения не изменяются, используйте константы для уменьшения поиска и т. Д., , потому что это ест циклы процессора.

Это с теоретической точки зрения ;-)

Если возможно использование оптимизированных библиотек. Я не exaclty знаю, что вы пытаетесь достичь, но я предпочел бы использовать существующий FFT-Lib, чем писать его сам:>

еще одно: http://en.wikipedia.org/wiki/Big_O_notation (может быть откровением)

3
import numpy as np 
def NB2(self, ID_LEN): 
    r1=np.fromfile(ReadFile.fid,dTypes.NB_HDR,1) 
    num_receivers=r1[0][0] 
    num_channels=r1[0][1] 
    num_samples=r1[0][5] 

    # first, match your array bounds to the way you are walking the file 
    blockReturn = np.zeros((num_receivers,num_channels,num_samples)) 

    for rec in range(0,num_receivers): 
     for chl in range(0,num_channels): 
      # second, read in all the samples at once if you have enough memory 
      r2_iq=np.fromfile(ReadFile.fid,np.int16,2*num_samples) 
      r2_iq.shape = (-1,2) # tell numpy that it is an array of two values 

      # create dot product vector by squaring data elementwise, and then 
      # adding those elements together. Results is of length num_samples 
      r2_iq = r2_iq * r2_iq 
      r2_iq = r2_iq[:,0] + r2_iq[:,1] 
      # get the distance by performing the square root "into" blockReturn 
      np.sqrt(r2_iq, out=blockReturn[rec,chl,:]) 

    return blockReturn 

Это должно помочь вашей работе. Две основные идеи в работе numpy. Во-первых, размеры массивов результатов должны соответствовать тому, как создаются размеры вашего цикла, для локальности памяти.
Во-вторых, Numpy is FAST. Я избил ручную кодировку C с numpy, просто потому, что использует LAPack и векторное ускорение.Однако, чтобы получить эту власть, вы должны позволить ей манипулировать большим количеством данных за раз. Вот почему ваш цикл выборки был свернут для чтения в полном образце для приемника и канала в одном большом чтении. Затем используйте высшие векторные силы из numpy, чтобы рассчитать величину вашей величины точкой.

В вычислении величины есть немного больше оптимизаций, но numpy перерабатывает буферы для вас, делая его менее важным, чем вы думаете. Надеюсь, это поможет!

+0

Спасибо за это за подробный ответ - я пробовал и то, и другое ниже, и это было немного быстрее. –

1

Главное, что вы не должны делать доступ к файлам на самом низком уровне тройного вложенного цикла, независимо от того, выполняете ли вы это на C или Python. Вы должны читать в больших кусках данных одновременно.

Чтобы ускорить это, читайте в больших объемах данных за раз и обрабатывайте эти данные с помощью индексации numpy (то есть, векторизовать свой код). Это особенно удобно в вашем случае, поскольку все ваши данные - int32. Просто читайте в больших фрагментах данных и переформатируйте данные в массив, который отражает структуру (приемник, канал, образец), а затем используйте соответствующую индексацию для умножения и добавления элементов для Pythagoras, а команда «sum» для добавления термины в результирующем массиве.

Смежные вопросы