Эффективный способ перебора больших файлов npz - PullRequest
0 голосов
/ 14 марта 2019

У меня есть 50000+ файлов npz (размер файла 15 МБ) каждый с тремя массивами A, B, C.

A - массив массивов формы (33000,);B - массив массивов формы (33000,), а C - массив битов (33000,224,224) типа uint8.

npz_file_dict = np.load(npz_file_path)
num_records = len(npz_file_dict['A'])

A = npz_file_dict['A']
B = npz_file_dict['B']
C = npz_file_dict['C']


def get_items(num_records):
   for i in range(num_records)
      yield A[i], B[i], C[i]
for a,b,c in get_items(num_records):
   # do some work with (a,b,c)
   # store the result in a json format with {'a' : a, 'b' : b, 'c' : c}

Завершение цикла for занимает очень много времени (иногда больше 35 минут на 30000 записей).Есть ли эффективный способ анализа записей?

Редактировать: я пытался сделать numpy массивы с отображением в памяти, но, похоже, это не улучшило производительность, поскольку они влияют только на файлы npy.

Редактировать: Нужна критика моего предложенного подхода.

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

num_records = len(npz_file_dict['np_cids'])
print(num_records)


start_memmap = time()
data = npz_file_dict['C']

print('Time to read data {}'.format(time() - start_memmap))


filename = path.join(mkdtemp(), 'newfile.dat')

print('Path to file',  filename)
fp = np.memmap(filename, dtype='float32', mode='w+', shape=(num_records, 224,224))
fp[:] = data[:]

finish_memmap = time()

print('Time to create memmap {}'.format(finish_memmap - start_memmap))

После завершения этой настройки я просто перебираю массивы A и B в цикле for и использую fp, чтобы получить соответствующий i^thзапись.Преимущество этого подхода в том, что итерации довольно быстрые.Недостатком является то, что время установки колеблется в диапазоне от 50 секунд в режиме одного процесса до 100 секунд в режиме многопроцессорной обработки.

Если вы амортизируете время установки для набора из 30000 записей, оно составляет около 3,3 мс.Во время итерации чтение выполняется довольно быстро - несколько сотен микросекунд.Таким образом, общее время записи каждой записи составляет около 5 мс.Это скорость 96% по сравнению с моим ранним подходом, который занимал 150 мс на запись.

1 Ответ

1 голос
/ 14 марта 2019

Я думаю, вы можете очистить код с помощью:

npz_file_dict = np.load(npz_file_path)
A = npz_file_dict['A']   # (33000,) shape array
B = npz_file_dict['B']
C = npz_file_dict['C']

for a,b,c in zip(A,B,C):
   # do some work with (a,b,c)
   # store the result in a json format with {'a' : a, 'b' : b, 'c' : c}

Это дает понять, что вы работаете с массивами, которые вы загрузили из файла. Я не знаю, ускорит ли это вещи или нет; это зависит от того, использует ли загрузчик npz какой-либо кэш с повторным доступом npz_file_dict['A] или нет.

Но независимо от того, как они создаются или загружаются, итерация по пустым массивам происходит медленно - гораздо медленнее, чем если бы вы использовали над ними скомпилированные операции «целый массив». Итерации по спискам быстрее. На самом деле это может помочь сделать

for a,b,c in zip(A.tolist(), B.tolist(), list(C):
    ...

A.tolist() - быстрый способ превратить массив в список. Я не использую его на C, потому что это 3d, и я предполагаю, что вы хотите использовать C[i,:,:] как массив, а не как список гнезд. Хотя, поскольку вы пишете c в json, вы все равно можете захотеть, чтобы он был списком списков.

===

Итерация по массиву memmap немного медленнее, чем итерация по массиву в памяти:

In [1]: C=np.ones((3000,224,224))                                               
In [2]: np.savez('bigC.npz',C=C)                                                
In [3]: fp = np.memmap('bigC.dat', dtype=C.dtype, mode='w+', shape=C.shape)     
In [4]: fp[:] = C[:]                                                            
In [5]: %%timeit  
   ...: for i in range(3000): 
   ...:     c = C[i] 
   ...:                                                                         
566 µs ± 2.55 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [7]: %%timeit  
   ...: for i in range(3000): 
   ...:     c = fp[i] 
   ...:      
   ...:                                                                         
9.74 ms ± 94.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Итерация по npz нагрузке действительно медленная - мой тест все еще выполняется:

In [8]: %%timeit d = np.load('bigC.npz') 
   ...: for i in range(3000): 
   ...:     c = d['C'][i] 

Тест с одной начальной нагрузкой:

In [243]: d = np.load('bigC.npz')                                               
In [244]: %%timeit  
     ...: D = d['C'] 
     ...: for i in range(3000): 
     ...:     c = D[i] 
     ...:                                                                       
2.17 s ± 27.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [245]: %%timeit  
     ...: D = d['C'] 
     ...:  
     ...:                                                                       
2.14 s ± 6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Так что одноразовой загрузки большого массива из npz можно избежать, но вы, конечно, не хотите многократно загружать его для каждой строки. Выгрузка его в memmap может помочь, если массив настолько велик, что не помещается в память, это не улучшение по сравнению с массивом в памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...