Списки / словари Python против массивов numpy: производительность и контроль памяти - PullRequest
8 голосов
/ 08 февраля 2011

Мне нужно итеративно читать файлы данных и сохранять данные в (пустых) массивах. Я решил сохранить данные в словаре «полей данных»: {'field1': array1, 'field2': array2, ...}.

Случай 1 (списки):

Используя списки (или collection.deque () ) для «добавления» новых массивов данных, код является эффективным . Но, когда я объединяю массивы, хранящиеся в списках, память увеличивается , и мне не удалось ее снова освободить. Пример:

filename = 'test'
# data file with a matrix of shape (98, 56)
nFields = 56
# Initialize data dictionary and list of fields
dataDict = {}

# data directory: each entry contains a list 
field_names = []
for i in xrange(nFields):
    field_names.append(repr(i))
    dataDict[repr(i)] = []

# Read a data file N times (it represents N files reading)
# file contains 56 fields of arbitrary length in the example
# Append each time the data fields to the lists (in the data dictionary)
N = 10000
for j in xrange(N):
    xy = np.loadtxt(filename)
    for i,field in enumerate(field_names):
        dataDict[field].append(xy[:,i])

# concatenate list members (arrays) to a numpy array 
for key,value in dataDict.iteritems():
    dataDict[key] = np.concatenate(value,axis=0)

Время вычислений : 63,4 с
Использование памяти (вверху): 13862 gime_se 20 0 1042 м 934 м 4148 S 0 5.8 1: 00.44 python

Случай 2 (массивы numpy):

Конкатенация массивов каждый раз при их чтении неэффективно , но память остается под контролем . Пример:

nFields = 56
dataDict = {}
# data directory: each entry contains a list 
field_names = []
for i in xrange(nFields):
    field_names.append(repr(i))
    dataDict[repr(i)] = np.array([])

# Read a data file N times (it represents N files reading)
# Concatenate data fields to numpy arrays (in the data dictionary)
N = 10000
for j in xrange(N):
    xy = np.loadtxt(filename)
    for i,field in enumerate(field_names):
        dataDict[field] = np.concatenate((dataDict[field],xy[:,i])) 

Время вычислений : 1377,8 с
Использование памяти (вверху): 14850 gime_se 20 0 650 м 542 м 4144 S 0 3.4 22: 31.21 python

Вопрос (ы):

  • Есть ли способ получить производительность Дело 1 , но с контролем памяти, как в Дело 2 ?

  • Похоже, что в случае 1 память увеличивается при конкатенации членов списка (np.concatenate (value, axis = 0)). Лучшие идеи сделать это?

1 Ответ

2 голосов
/ 09 февраля 2011

Вот что происходит на основе того, что я наблюдал.На самом деле нет утечки памяти.Вместо этого, код управления памятью Python (возможно, связанный с управлением памятью любой операционной системы, в которой вы находитесь) решает сохранить пространство, используемое исходным словарем (без объединенных массивов) в программе.Тем не менее, он может быть использован повторно.Я доказал это, сделав следующее:

  1. Превращение кода, который вы дали в качестве ответа, в функцию, возвращающую dataDict.
  2. Вызов функции дважды и присвоение результатов двум различным переменным.

Когда я это делаю, я обнаружил, что объем используемой памяти увеличился только с ~ 900 ГБ до ~ 1,3 ГБ.Без дополнительной словарной памяти, по моим подсчетам, сами данные Numpy должны занимать около 427 МБ, так что это складывается.Второй исходный словарь без конкатенации, созданный нашей функцией, только что использовал уже выделенную память.

Если вы действительно не можете использовать более ~ 600 МБ памяти, то я бы порекомендовал делать с вашими массивами Numpy, какэто делается внутренне с помощью списков Python: выделите массив с определенным количеством столбцов, а когда вы их использовали, создайте расширенный массив с большим количеством столбцов и скопируйте данные.Это сократит количество конкатенаций, а значит, будет быстрее (хотя и не так быстро, как списки), сохраняя при этом объем используемой памяти.Конечно, это также большая боль в реализации.

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