Подтвердить что ты не робот

Многопроцессорный дизайн Python

Я написал алгоритм, который берет геопространственные данные и выполняет ряд шагов. Входные данные представляют собой файл формы полигонов и ковариационных растров для большой области изучения растра (~ 150 миллионов пикселей). Эти шаги заключаются в следующем:

  • Пример точек из полигонов шейп файла
  • Для каждой точки выборки извлекайте значения из ковариационных растров
  • Построение прогнозной модели в точках выборки
  • Извлечь ковариации для целевых точек сетки.
  • Применить прогнозирующую модель к целевой сетке
  • Запись предсказаний в набор выходных сеток

Весь процесс нужно повторять несколько раз (скажем, 100), но каждая итерация в настоящее время занимает больше часа, когда обрабатывается последовательно. Для каждой итерации наиболее трудоемкими частями являются шаги 4 и 5. Поскольку целевая сетка настолько велика, я обрабатывал ее как блок (например, 1000 строк) за раз.

У меня 6-ядерный процессор с 32 Гб оперативной памяти, поэтому на каждой итерации я решил использовать модуль Python multiprocessing с объектом Pool для обработки нескольких блоков одновременно (шаги 4 и 5) а затем записывать выходные данные (предсказания) в общий набор выходных сеток с помощью функции обратного вызова, которая вызывает глобальную функцию вывода на выходе. Кажется, что это работает, но не быстрее (на самом деле это, вероятно, медленнее), чем обработка каждого блока последовательно.

Итак, мой вопрос: есть ли более эффективный способ сделать это? Меня интересует многопроцессорный модуль Queue, но я не уверен, как он работает. Например, мне интересно, если более эффективно иметь очередь, которая выполняет шаги 4 и 5, затем передает результаты в другую очередь, которая выполняет шаг 6. Или это даже то, для чего предназначена очередь?

Любые указатели будут оценены.

4b9b3361

Ответ 1

Текущее состояние возможностей многопроцессорной обработки Python не очень подходит для обработки связанных с ЦП обработки. Я боюсь сказать вам, что нет возможности ускорить его работу с помощью модуля multiprocessing, и это не проблема с multiprocessing.

Реальная проблема заключается в том, что Python по-прежнему связан правилами GlobalInterpreterLock (GIL) (я настоятельно рекомендую слайды). Были некоторые интересные теоретические и экспериментальные достижения по работе с GIL. Событие Python 3.2 содержит новый GIL, который решает некоторые проблемы, но представляет другие.

На данный момент быстрее выполнить много процессов Python с помощью одного последовательного потока, чем пытаться запустить много потоков в одном процессе. Это позволит вам избежать проблем с приобретением GIL между потоками (фактически имея больше GIL). Однако это полезно только в том случае, если накладные расходы IPC между вашими процессами Python не затмевают преимущества обработки.

Эли Бэндерски написал достойную обзорную статью о своем опыте с попыткой ускорить процесс привязки процессора при многопроцессорности.

Стоит отметить, что PEP 371 имел желание "перешагнуть" GIL с введением модуля multiprocessing ( ранее нестандартного пакета с именем pyProcessing). Однако GIL все еще играет слишком большую роль в интерпретаторе Python, чтобы он хорошо работал с алгоритмами, связанными с процессором. Многие разные люди работали над удалением/переписыванием GIL, но ничто не сделало достаточной тяги, чтобы превратить его в релиз Python.

Ответ 2

Некоторые из примеров многопроцессорности на python.org не очень понятны IMO, и легко начать с испорченного дизайна. Вот простой пример, который я сделал, чтобы начать работу над проектом:

import os, time, random, multiprocessing
def busyfunc(runseconds):
    starttime = int(time.clock())
    while 1:
        for randcount in range(0,100):
            testnum = random.randint(1, 10000000)
            newnum = testnum / 3.256
        newtime = int(time.clock())
        if newtime - starttime > runseconds:
            return

def main(arg):
    print 'arg from init:', arg
    print "I am " + multiprocessing.current_process().name

    busyfunc(15)

if __name__ == '__main__':

    p = multiprocessing.Process(name = "One", target=main, args=('passed_arg1',))
    p.start()

    p = multiprocessing.Process(name = "Two", target=main, args=('passed_arg2',))
    p.start()

    p = multiprocessing.Process(name = "Three", target=main, args=('passed_arg3',))
    p.start()

    time.sleep(5)

Для этого необходимо использовать 3 процессора в течение 15 секунд. Это должно быть легко изменить для большего. Возможно, это поможет отладить ваш текущий код и убедиться, что вы действительно генерируете несколько независимых процессов.

Если вам необходимо обмениваться данными из-за ограничений RAM, я предлагаю следующее: http://docs.python.org/library/multiprocessing.html#sharing-state-between-processes

Ответ 3

Поскольку python на самом деле не предназначен для интенсивного набора числа, я обычно начинаю преобразовывать критически важные компоненты программы python в C/С++ и быстро ускорять работу.

Кроме того, многопоточность python не очень хороша. Python продолжает использовать глобальный семафор для всех видов вещей. Поэтому даже когда вы используете потоки, предлагаемые python, все будет не так быстро. Темы полезны для приложений, где потоки обычно ждут таких вещей, как IO.

При создании C-модуля вы можете вручную освободить глобальный семафор при обработке своих данных (тогда, конечно, больше не получать значения python).

Это требует некоторой практики, использующей API C, но ее четко структурированной и гораздо более простой в использовании, чем, например, родной API Java.

См. "Расширение и внедрение" в документации python.

Таким образом, вы можете создавать критические моменты времени на C/С++, а более медленные части с более быстрым программированием работают на python...

Ответ 4

Я рекомендую вам сначала проверить, какие аспекты вашего кода занимают больше всего времени, поэтому вам нужно будет профилировать его, я использовал http://packages.python.org/line_profiler/#line-profiler с большим успехом, хотя для этого требуется cython.

Что касается очередей, они в основном используются для обмена данными/синхронизацией потоков, хотя я редко использовал его. Я все время использую многопроцессорность.

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

Вы можете попробовать сегментировать ваш файл и применить свой алгоритм к различным наборам.