Python: самый быстрый способ повторить это через большой файл - PullRequest
2 голосов
/ 23 февраля 2010

Да, я перебираю большой двоичный файл

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

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, для которых я хочу найти величину (отсюда и Пифагора).

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

РЕДАКТИРОВАТЬ: На самом деле это может быть полезно знать, как я перемещаюсь внутри файла. У меня есть функция следующим образом:

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

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

Приветствия

Ответы [ 5 ]

3 голосов
/ 23 февраля 2010

Поскольку вы знаете длину блока после прочтения заголовка, прочитайте весь блок сразу. Затем измените массив (очень быстро, влияет только на метаданные) и используйте 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 мс на блок.

3 голосов
/ 23 февраля 2010
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 это FAST . Я избил C вручную, просто потому, что он использует LAPack и векторное ускорение. Однако, чтобы получить эту власть, вы должны позволить ей манипулировать большим количеством данных одновременно. Вот почему ваш цикл выборки был свернут для чтения в полном примере для приемника и канала в одном большом чтении. Затем используйте высшие векторные степени числа, чтобы вычислить вашу величину по точечному произведению.

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

1 голос
/ 23 февраля 2010

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

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

1 голос
/ 23 февраля 2010

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

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

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

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

0 голосов
/ 23 февраля 2010

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

...