Блокировка свободной только для чтения список в Python? - PullRequest
9 голосов
/ 20 января 2011

Я сделал некоторые базовые тесты производительности и потребления памяти, и мне было интересно, есть ли способ сделать вещи еще быстрее ...

  1. У меня огромный список из 70000 элементовс пустым ndarray и путем к файлу в кортеже в указанном списке.

  2. Моя первая версия передала нарезанную копию списка каждому из процессов в многопроцессорном модуле python,но это увеличило бы использование оперативной памяти до более чем 20 гигабайт

  3. Во второй версии я переместил ее в глобальное пространство и получил к ней доступ через индекс, такой как foo [i] в ​​цикле в каждом измои процессы, которые, кажется, помещают его в общую область памяти / семантику CoW с процессами, таким образом, это не взрывает использование памяти (остается на уровне ~ 3 гигабайта)

  4. Однако в зависимости от производительностибенчмарки / трассировка, кажется, что большая часть времени приложения теперь тратится в режиме «приобретения» ...

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

Edit 1: Вот вершинанесколько строк вывода профилирования приложения

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
   65 2450.903   37.706 2450.903   37.706 {built-in method acquire}
39320    0.481    0.000    0.481    0.000 {method 'read' of 'file' objects}
  600    0.298    0.000    0.298    0.000 {posix.waitpid}
   48    0.271    0.006    0.271    0.006 {posix.fork}

Edit 2: Вот пример структуры списка:

# Sample code for a rough idea of how the list is constructed
sim = []
for root, dirs, files in os.walk(rootdir):
    path = os.path.join(root, filename)
    image= Image.open(path)
    np_array = np.asarray(image)
    sim.append( (np_array, path) )

# Roughly it would look something like say this below
sim = List( (np.array([[1, 2, 3], [4, 5, 6]], np.int32), "/foobar/com/what.something") )

Тогда впредь список SIMтолько для чтения.

1 Ответ

11 голосов
/ 20 января 2011

Модуль multiprocessing предоставляет именно то, что вам нужно: общий массив с дополнительной блокировкой, а именно класс multiprocessing.Array. Передайте lock=False конструктору, чтобы отключить блокировку.

Редактировать (с учетом вашего обновления): На самом деле все значительно сложнее, чем я ожидал. Данные всех элементов в вашем списке должны быть созданы в общей памяти. Поместите ли вы сам список (то есть указатели на фактические данные) в общую память, не имеет большого значения, потому что он должен быть небольшим по сравнению с данными всех файлов. Чтобы сохранить данные файла в общей памяти, используйте

shared_data = multiprocessing.sharedctypes.RawArray("c", data)

где data - данные, которые вы читаете из файла. Чтобы использовать это как массив NumPy в одном из процессов, используйте

numpy.frombuffer(shared_data, dtype="c")

, который создаст представление массива NumPy для общих данных. Аналогично, чтобы поместить путь в общую память, используйте

shared_path = multiprocessing.sharedctypes.RawArray("c", path)

где путь - обычная строка Python. В ваших процессах вы можете получить к нему доступ в виде строки Python, используя shared_path.raw. Теперь добавьте (shared_data, shared_path) в свой список. Список будет скопирован в другие процессы, но фактические данные не будут.

Сначала я хотел использовать multiprocessing.Array для фактического списка. Это было бы вполне возможно и гарантировало бы, что сам список (то есть указатели на данные) находится в разделяемой памяти. Теперь я думаю, что это не так важно, пока фактические данные передаются.

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