Поиск по сайту:

Распределенное параллельное программирование на Python: MPI4PY


На этой странице

  1. 1 Введение
  2. 2 Отправка и получение сообщений
    1. 2.1 Введение в метод send()
    2. 2.2 Введение в функцию recv()
    3. 2.3 Пример с простыми функциями send() и recv()
    4. 2.4 Примечания
    5. 2.5 Динамическая отправка и получение
    6. 2.6 Пример динамической отправки и получения данных

    1. 3.1 Пример

    1. 4.1 Пример

    1. 5.1 Пример

    1. 6.1 Пример

    1. Введение

    MPI означает интерфейс передачи сообщений. Реализация MPI, такая как MPICH или OpenMPI, используется для создания платформы для написания параллельных программ в распределенной системе, такой как кластер Linux с распределенной памятью. Как правило, построенная платформа позволяет программировать на C с использованием стандарта MPI. чтобы запускать параллельные программы в этой среде на python, нам нужно использовать модуль под названием MPI4py, что означает «MPI для Python». Этот модуль предоставляет стандартные функции для выполнения таких задач, как получение рейтинга процессоров, отправка и получение сообщений./данные с разных узлов в кластерах. Это позволяет программе выполняться параллельно с передачей сообщений между узлами. Важно, чтобы в вашей системе были установлены MPIch2 и MPI4py. Итак, если вы не установили MPI4Py, следуйте 2 руководствам по обратитесь к инструкции по установке, сборке и тестированию примера программы в MPI4PY.

    https://seethesource.wordpress.com/2015/01/05/raspberypi-hacks-part1/
    https://seethesource.wordpress.com/2015/01/14/raspberypi-hacks-part2/
    

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

    2 Отправка и получение сообщений

    Связь в mpi4py осуществляется с помощью методов send() и recv(). Как следует из названия, он используется для отправки и получения сообщений от узлов соответственно.

    2.1 Введение в функцию send()

    Общий синтаксис этой функции: comm.send(data,dest)

    здесь «данные» могут быть любыми данными/сообщением, которое должно быть отправлено другому узлу, а «назначение» указывает ранг процесса узла (узлов), чтобы отправить его.

    Пример: comm.send((rank+1)*5,dest=1).
    Это отправляет сообщение \(rank+1)*5\ узлу с рангом процесса=1. Так что только этот узел может получить его.

    2.2 Введение в recv()

    Общий синтаксис этой функции:comm.recv(источник)

    Это говорит конкретному процессу получать данные/сообщение только от процесса с рангом, упомянутым в параметре источника.

    Пример: comm.recv(source=1)
    Получает сообщение только от процесса с рангом=1.

    2.3 Пример с простыми функциями send() и recv()

    if rank==0 :
    	    data= (rank+1)*5
    	    comm.send(data,dest=1)
    if rank==1:
    	    data=comm.recv(source-0)
    	    print data
    

    2.4 Примечания

    • Когда на узле выполняется метод recv(), он ждет, пока не получит некоторые данные из ожидаемого источника. Как только он получает некоторые данные, он продолжает работу с остальной частью программы.
    • Здесь параметр \dest\ в send() и параметр \source\ в recv() не обязательно должны иметь просто постоянное значение (или ранг), это может быть выражение.
    • Член \size\ объекта \comm\ является хорошим способом условной обработки методов send() и receive(), что приводит к динамической отправке и получению сообщений.

    2.5 Динамическая отправка и получение

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

    2.6 Пример динамической отправки и получения данных

    comm.send(data_shared,dest=(rank*2)%size)
    comm.recv(source=(rank-3)%size)
    

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

    3 Тегированные функции send() и recv()

    Когда мы помечаем send() и recv(), мы можем гарантировать порядок получения сообщений, поэтому мы можем быть уверены, что одно сообщение будет доставлено раньше другого.

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

    Например, send() может выглядеть так: comm.send(shared_data,dest=2,tag=1), а соответствующий recv() приведенному выше оператору будет выглядеть так: comm. recv(источник=1,тег=1)

    Таким образом, эта структура вызывает совпадение, что приводит к синхронизации передачи данных. Преимущество тегирования заключается в том, что recv() можно заставить ждать, пока он не получит данные от соответствующего send() с ожидаемым тегом. Но это нужно использовать с особой осторожностью, так как это может привести к тупиковому состоянию.

    3.1 Пример

      If rank==0:
    	shared_data1 = 23
    	comm.send(shared_data1,dest=3,tag=1)
    	shared_data2 = 34
    	comm.send(shared_data2,dest=3,tag=2)
      if rank==3:
    	recv_data1 = comm.recv(source=0,tag=2)
    	print  recv_data1
    	recv_data2 = comm.recv(source=0,tag=1)
    	print  recv_data2
    

    Результат этого будет выглядеть так:

    34
    23
    

    Таким образом, мы видим, что несмотря на то, что shared_data1 был отправлен первым, первый recv() дождался send() с tag=2 и получил данные, распечатал их и переслал следующему методу recv().

    4 Трансляция

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

    4.1 Пример

       if rank == 0
           data = {'a':1,'b':2,'c':3}
       else
           data = None
       data = comm.bcast(data,root=0)
       print  "rank", rank, data
    

    В приведенной выше программе метод bcast() первый параметр «данные» представляет то, что должно быть передано в эфир, а второй параметр «root=0» указывает, откуда мы получаем данные. Если мы запустим это программу, использующую 5 процессов, вывод должен выглядеть примерно так:

    rank 0 {'a':1,'b':2,'c':3}
    rank 4 {'a':1,'b':2,'c':3}
    rank 3 {'a':1,'b':2,'c':3}
    rank 1 {'a':1,'b':2,'c':3}
    rank 2 {'a':1,'b':2,'c':3}
    

    5 Скаттер

    Разброс — это процесс разбиения данных и отправки или распределения каждой части на другой узел. Примером может быть разбивка списка и отправка каждого элемента списка на другой узел.

    5.1 Пример

       comm = MPI.COMM_WORLD
       size=comm.get_size()
       rank-comm.get_rank()
       if rank == 0: 
           data = [(x+1) ** x for x in range (size)]
           print 'scattering data',data
       else:
           data = None
       data = comm.scatter(data,root=0)
       print 'rank',rank,'has data: ', data
    

    Вывод для этой программы будет:

    scattering data : [1,2,9,64,625]
    
    rank 0 has data : 1
    rank 1 has data : 2
    rank 2 has data : 9
    rank 3 has data : 64
    rank 4 has data : 625
    

    здесь размер данных должен быть равен количеству данных, которое можно ожидать, то есть если данные имеют 10 элементов, и если существует только 5 процессов, возникает ошибка.

    6 Собрать

    Сбор противоположен разбросу. Он используется для сбора данных с разных узлов и хранения их как одного. Примером может быть сбор элементов с разных узлов и создание из них единого списка.

    6.1 Пример

    Пример: (Обратите внимание, что это то же самое, что и предыдущий пример для разброса, но с некоторыми дополнениями)

     comm = MPI.COMM_WORLD
       size=comm.get_size()
       rank-comm.get_rank()
       if rank == 0: 
           data = [(x+1) ** x for x in range (size)]
           print 'scattering data',data
       else:
           data = None
       data = comm.scatter(data,root=0)
       print 'rank',rank,'has data: ', data
         
       new_data = comm.gather(data, root=0)
       if rank == 0:
           print  'master collected: ', new_data
       
    

    Вывод для этой программы будет:

    rank 0 has data : 1
    rank 1 has data : 2
    rank 2 has data : 9
    rank 3 has data : 64
    rank 4 has data : 625 
    master collected:  [ 1, 2, 3, 9,64, 625]  
    

    В приведенном выше выводе последняя строка вызвана сборкой. Еще одна вещь, которую можно сделать, это изменить данные, как только они будут разбросаны, то есть когда каждый узел получит часть разбросанных данных. Пример: data= data+1
    Таким образом, когда происходит сбор, данные не совпадают с отправленными, но сохраняют измененный шаблон, тем самым доказывая, что он работает правильно.

    7 Заключение

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

    • Автоматическое суммирование текста – ветвь обработки естественного языка, в которой может быть написана распределенная программа для суммирования нескольких текстовых документов одновременно.
    • Сортировка и поиск. Распределенную среду можно использовать, разделяя данные для сортировки или поиска, чтобы можно было обработать огромное количество данных за небольшой промежуток времени.
    • Математические решения задач — например, распределение ядов можно решить намного быстрее в параллельной среде.

    Таким образом, мы можем писать параллельные программы для любой области и использовать преимущества Python.