понедельник, 11 августа 2014 г.

Место MPI в иерархии средств параллельного программирования

Содержание




Место MPI в иерархии средств
параллельного программирования

Создание параллельной программы включает в себя две основных стадии:
  • последовательный алгоритм подвергается декомпозиции (распараллеливанию), т.е. разбивается на независимо работающие ветви; для взаимодействия в ветви вводятся две дополнительных нематематических операции: прием и передача данных.
  • распараллеленный алгоритм записывается в виде программы, в которой операции приема и передачи записываются в терминах конкретной системы связи между ветвями.
Система связи, в свою очередь, включает в себя два компонента: программный и аппаратный. В рамках данного документа мы будем воспринимать аппаратную часть как данность, без анализа того, как она будет развиваться в дальнейшем, какой тип лучше, а какой хуже, и почему.
С точки же зрения программиста базовых методик работы (или, как нынче принято говорить, парадигм) две - данные могут передаваться:
  • через разделяемую память, синхронизация доступа ветвей к такой памяти происходит посредством семафоров,
  • в виде сообщений.
Первый метод является базовым для SMP-машин, второй - для сетей всех типов. Однако в принципе одно может быть сымитировано через другое:
  • разделяемая память на не-SMP-комплексах - посредством организации единого виртуального адресного пространства на изолированных друг от друга физических ОЗУ; такая имитация аппаратными средствами нередко применяется на NUMA-машинах;
  • на SMP-машине вырожденным каналом связи для передачи сообщений служит разделяемая память.
Следовательно, единый интерфейс программиста может быть создан, тем не менее долгое время создатели компьютеров и операционных систем шли разными путями. Например, для работы с семафорами и разделяемой памятью в Windows, в Юниксах 4.4BSD и SVR4 примерно одни и те же действия представлены разными наборами системных вызовов. В мире локальных/глобальных сетей стандарт вроде бы выработан - это TCP/IP. Однако для работы с внутримашинными сетями, где состав абонентов и маршрутизация трафика жестко фиксированы, а вероятность доставки пакетов равна 100%, универсальность и гибкость TCP/IP отрицательно сказываются на скорости, поэтому для MPP-машин интерфейсы программиста создаются индивидуально (или, вернее, создавались индивидуально до тех пор, пока не появился MPI).
Таким образом, можно считать назревшей потребность в стандарте на интерфейс программиста, который бы сделал создание параллельных приложений таким же:
  • мобильным,
  • эффективным,
  • надежным,
... каким Си и Юникс сделали программирование вообще. Стандартом по выбору основных производителей ЭВМ решено сделать MPI. Ими образован MPI Forum, и в свет выпущена спецификация, которой должны удовлетворять все конкретные разработки. Головная организация проекта - Аргоннская национальная лаборатория США - распространяет пакет MPICH (MPI CHameleon), перенесенный на большинство платформ. Большая часть созданных другими разработчиками реализаций MPI базируется на MPICH. По идее, в итоге иерархия средств разработки должна стать примерно такой:
Параллельное приложение
Средства быстрой разработки приложений (RAD)
Распараллеливающие препроцессоры

M P I 

разделяемая память
и семафоры
индивидуальные интерфейсы
с передачей сообщений
TCP/IP
SMP-машиныMPP-машинысети


Правила работы с MPI

MPI расшифровывается как Message Passing Interface - Интерфейс с передачей сообщений, т.е. конкретному стандарту присвоено название всего представляемого им класса программного инструментария. В его состав входят, как правило, два обязательных компонента:
  • библиотека программирования для языков Си, Си++ и Фортран,
  • загрузчик исполняемых файлов.
Кроме того, может присутствовать справочная система (manual pages для Юникса), командные файлы для облегчения компиляции/компоновки программ и все такое прочее. В стандарте отсутствует все лишнее, например, нет средств автоматического переноса и построения копий исполняемого файла в сети. Это нужно, если MPI-приложение предстоит выполнять сетью машин - но это можно выполнить и утилитами Юникса. В стандарте нет никаких средств автоматической декомпозиции, нет отладчика (правда, есть функции хронометража и предусмотрена возможность профилирования). То есть это система межпроцессовой связи в чистом (можно даже сказать - в голом) виде, и не более того.
Для MPI принято писать программу, содержащую код всех ветвей сразу. MPI-загрузчиком запускается указываемое количество экземпляров программы. Каждый экземпляр определяет свой порядковый номер в запущенном коллективе, и в зависимости от этого номера и размера коллектива выполняет ту или иную ветку алгоритма. Такая модель параллелизма называется Single program/Multiple data ( SPMD ), и является частным случаем модели Multiple instruction/Multiple data ( MIMD ). Каждая ветвь имеет пространство данных, полностью изолированное от других ветвей. Обмениваются данными ветви только в виде сообщений MPI.
Все ветви запускаются загрузчиком одновременно как процессы Юникса. Количество ветвей фиксировано - в ходе работы порождение новых ветвей невозможно. Если MPI-приложение запускается в сети, запускаемый файл приложения должен быть построен на каждой машине.
Ниже, в следующих трех разделах, будут вкратце рассмотрены некоторые функциональные возможности MPI, причем упор будет сделан не на том, что они делают (все аналоги MPI так или иначе делает то же самое), а на том, как они это делают, какие нетривиальные решения были найдены для выполнения типовых действий.


Функции пересылки данных

Хотя с теоретической точки зрения ветвям для организации обмена данными достаточно всего двух операций (прием и передача), на практике все обстоит гораздо сложнее. Одними только коммуникациями "точка-точка" (т.е. такими, в которых ровно один передающий процесс и ровно один принимающий) занимается порядка 40 функций. Пользуясь ими, программист имеет возможность выбрать:
  • Способ зацепления процессов - в случае неодновременного вызова двумя процессами парных функций приема и передачи могут быть произведены:
    • Автоматический выбор одного из трех нижеприведенных вариантов;
    • Буферизация на передающей стороне - функция передачи заводит временный буфер, копирует в него сообщение и возвращает управление вызвавшему процессу. Содержимое буфера будет передано в фоновом режиме;
    • Ожидание на приемной стороне,
      завершение с кодом ошибки на передающей стороне;
    • Ожидание на передающей стороне,
      завершение с кодом ошибки на приемной стороне.
  • Способ взаимодействия коммуникационного модуля MPI с вызывающим процессом:
    • Блокирующий - управление вызывающему процессу возвращается только после того, как данные приняты или переданы (или скопированы во временный буфер);
    • Неблокирующий - управление возвращается немедленно (т.е. процесс блокируется до завершения операции), и фактическая приемопередача происходит в фоне. Функция неблокирующего приема имеет дополнительный параметр типа "квитанция". Процесс не имеет права производить какие-либо действия с буфером сообщения, пока квитанция не будет "погашена";
    • Персистентный - в отдельные функции выделены:
      • создание "канала" для приема/передачи сообщения,
      • инициация приема/передачи,
      • закрытие канала.
      Такой способ эффективен, к примеру, если приемопередача происходит внутри цикла, а создание/закрытие канала вынесены за его границы.
2 простейшие (но и самые медленные) функции - MPI_Recv и MPI_Send - выполняют блокирующую приемопередачу с автоматическим выбором зацепления (кстати сказать, все функции приема совместимы со всеми функциями передачи).
Таким образом, MPI - весьма разветвленный инструментарий. Приведу цитату из раннего себя: "То, что в конкурирующих пакетах типа PVM реализовано одним-единственным способом, в MPI может быть сделано несколькими, про которые говорится: способ А прост в использовании, но не очень эффективен; способ Б сложнее, но эффективнее; а способ В сложнее и эффективнее при определенных условиях".
Замечание о разветвленности относится и к коллективным коммуникациям (при которых получателей и/или отправителей несколько): в PVM эта категория представлена одной функцией, в MPI - 9 функций 5 типов:
  • broadcast: один-всем,
  • scatter: один-каждому,
  • gather: каждый-одному,
  • allgather: все-каждому,
  • alltoall: каждый-каждому.
На упреки типа "Зачем столько всего сваливать в одну кучу? Программисту легче будет в случае необходимости самому написать такую функцию, нежели разбираться в разбухшей документации?" можно возразить, что, например, вариант-самоделка "один-всем" будет, скорее всего, выглядеть примерно так:
    if( myRank == 0 )
    {
        for( i=1; i < numLoops; i++ )
            MPI_Send( ... , i, ... );
    } else
        MPI_Recv( ... , 0, ... ); 
... в то время, как имеющиеся в MPI функции оптимизированы - не пользуясь функциями "точка-точка", они напрямую (на что, согласно идеологии MPI, программа пользователя права не имеет) обращаются:
  • к разделяемой памяти и семафорам на SMP-машине, при этом происходит одно копирование в разделяемую память и numLoops-1копирований из нее просто функцией memcpy();
  • к TCP/IP при работе в сети: в качестве адреса получателя при передаче используется т.н. "широковещательный", с "-1" в поле адреса машины.
А если такая архитектурно-зависимая оптимизация невозможна, используется оптимизация архитектурно-независимая (интересно, как много программистов захотело бы делать ее вручную?): передача производится не напрямую от одного ко всем (время передачи линейно зависит от количества ветвей-получателей), а по двоичному дереву (время передачи логарифмически зависит от количества). Как следствие, скорость работы повышается.


Коллективы ветвей

В MPI хорошо продумано объединение ветвей в коллективы. В сущности, такое деление служит той же цели, что и введение идентификаторов для сообщений: помогает надежнее отличать сообщения друг от друга. В большинстве функций MPI имеется параметр типа "коммуникатор", который можно рассматривать как дескриптор (номер) коллектива. Он ограничивает область действия данной функции соответствующим коллективом. Коммуникатор коллектива, который включает в себя все ветви приложения, создается автоматически при старте и называется MPI_COMM_WORLD.
Идентификаторы программист назначает сообщениям вручную, и существует вероятность, что вследствие его ошибки два разных сообщения получат одинаковые идентификаторы. Коллективы создаются функциями самого MPI, так, чтобы гарантированно избежать случайных совпадений.
В качестве идентификатора ожидаемого сообщения функции приема может быть передан т.н. "джокер" - принять первое пришедшее сообщение независимо от его идентификатора и/или отправителя. Такой вызов может по ошибке перехватывать и те сообщения, которые должны быть приняты и обработаны в другом месте ветви. Для коммуникаторов "джокера" не существует, поэтому работа разных функций через разные коммуникаторы гарантированно предохраняет их от взаимных краж информации: коммуникаторы являются несообщающимися сосудами.
Коммуникаторы, помимо собственного номера и состава входящих в них ветвей, хранят и другие данные. Например, кроме обязательной линейной нумерации, ветвям коллектива может быть дополнительно назначена нумерация картезианская или в виде произвольного графа - это может оказаться удобным для решения некоторых классов задач. Коллективу может быть назначена функция-обработчик ошибок взамен назначаемой по умолчанию. Пользуясь механизмом "атрибутов", для коллектива пользователь может завести набор совместно используемых его ветвями данных.


Зачем MPI знать о типах данных

О типах передаваемых данных MPI должен постольку-поскольку при работе в сетях на разных машинах данные могут иметь разную разрядность (например, тип int - 4 или 8 байт), ориентацию (младший байт располагается в ОЗУ первым на процессорах Intel, последним - на всех остальных), и представление (это, в первую очередь, относится к размерам мантиссы и экспоненты для вещественных чисел). Поэтому все функции приемопередачи в MPI оперируют не количеством передаваемых байт, а количеством ячеек, тип которых задается параметром функции, следующим за количеством: MPI_INTEGER, MPI_REAL и т.д. Это переменные типа MPI_Datatype (тип "описатель типов", каждая его переменная описывает для MPI один тип). Они имеются для каждого базового типа, имеющегося в используемом языке программирования.
Однако, пользуясь базовыми описателями, можно передавать либо массивы, либо одиночные ячейки (как частный случай массива). А как передавать данные агрегатных типов, например, структуры? В MPI имеется механизм конструирования пользовательских описателей на базе уже имеющихся (как пользовательских, так и встроенных).
Более того, разработчики MPI создали механизм конструирования новых типов даже более универсальный, чем имеющийся в языке программирования. Действительно, во всех мне известных языках программирования ячейки внутри агрегатного типа (массива или структуры):
  • не налезают друг на друга,
  • не располагаются с разрывами (выравнивание полей в структурах не в счет).В MPI сняты оба этих ограничения! Это позволяет весьма причудливо "вырезать", в частности, фрагменты матриц для передачи, и размещать принимаемые данные между собственных. В спецификации MPI приведен пример создания пользовательского описателя типа, передача матрицы с использованием которого приводит к ее транспонированию.
    Выигрыш от использования механизма конструирования типов очевиден - лучше один раз вызвать функцию приемопередачи со сложным шаблоном, чем двадцать раз - с простыми.


    Чего нет в MPI, но будет в MPI-2

    В функциональности MPI есть пробелы, которые устранены в следующем проекте, MPI-2. Спецификация на MPI-2 уже выпущена в свет, а появление первых реализаций планируется в конце 1998 года. Вкратце перечислим наиболее важные нововведения:
    • Взаимодействие между приложениями. Поддержка механизма "клиент-сервер". Станет возможным писать на MPI не только расчетные математические задачи, но и системы массового обслуживания (базы данных и проч.).
       
    • Динамическое порождение ветвей. Для программирования расчетных задач это не нужно (хотя многие по привычке хотели бы иметь в MPI аналоги fork() и spawn() ).
      Действительно, не имеет смысла запускать ветвей больше чем... и нет особого вреда в том, чтобы запускать ветвей меньше, чем... реально имеется процессоров, не так ли? Процессор не станет работать в N раз быстрее, если вместо одной ветви на нем запустить N ветвей. И наоборот, ветвь, для которой не найдется работы, в "спящем" состоянии будет потреблять сущие крохи от процессорного времени. 
      Однако такая возможность однозначно необходима для написания системы массового обслуживания.
       
    • Для работы с файлами создан архитектурно-независимый интерфейс. Это имеет значение, особенно если диск находится на одной ЭВМ, а ветвь, которая должна с ним работать - на другой. В отсутствие такого интерфейса пересылку данных приходится либо организовывать вручную, либо полагаться на сетевые возможности операционной системы (NFS, Parix и т.д.). По сравнению и с тем, и с другим, MPI гарантирует лучший баланс между универсальностью и быстродействием.
       
    • Сделан шаг в сторону SMP-архитектуры. Теперь разделяемая память может быть не только каналом связи между ветвями, но и местом совместного хранения данных. Для этого ветви делегируют в MPI т.н. буфера-"окна". Интерфейс выполнен так, чтобы в-принципе его можно было реализовать и через передачу сообщений на не-SMP-комплексах. MPI автоматически поддерживает идентичность содержимого всех "окон" с одинаковым идентификатором.

      Для этого механизма придуман термин "One-sided communications" ("односторонние коммуникации"), так как ветви-получателю не требуется явно вызывать функцию приема для получения новой информации; функция передачи в ветви-отправители осуществляет "Remote memory access" ("удаленный доступ к памяти", сокращенно RMA).

      Правда, возникает вопрос: раз уж передача данных становится неявной, а синхронизация ветвей при доступе к "окнам" - явной, не станут ли возможными в MPI т.н. нестабильные ошибки, вызванные неправильной синхронизацией - бич программ, взаимодействующих через разделяемую память?


    Сравнение с PVM

    Сравнение необходимо, так как названия MPI и PVM часто упоминаются рядом.
    Итак, общие характеристики:
    • одна и та же решаемая задача: обеспечение межпроцессовой связи,
    • методика программирования: SPMD с передачей сообщений,
    • методика использования: библиотека для Си/Фортрана + загрузчик,
    • мобильность инструмента и, как следствие, мобильность создаваемых программ.
    Отличия PVM: в PVM...
    • интерфейс программиста проще и примитивнее,
    • интерфейс пользователя сложнее и запутаннее,
    • предусмотрено динамическое размножение ветвей (есть в MPI-2),
    • возможно взаимодействие приложений, запущенных одним и тем же пользователем (есть в MPI-2),
    • загрузчик приложений, создающий собственно Виртуальную Параллельную Машину, является постоянно запущенным (полезно для исследовательских целей); в отличие от MPI, где срок жизни загрузчика строго совпадает со сроком жизни одного конкретного приложения (более простое, надежное и компактное решение),
    • PVM старше на 6 лет, но не продвигается производителями ЭВМ на роль единого стандарта.
    Следует, однако, отметить, что здесь сравниваются не реализации, а спецификации. Стандарт MPI предлагает больший спектр возможностей, нежели стандарт PVM, но есть определенные основания считать, что в настоящее время над реализацией PVM работает более квалифицированный коллектив разработчиков.Подробнее о PVM читайте тут.


    Сравнение с SHM

    Преимущества модели shared memory ( SHM ) по сравнению с MPI:
    • Модель программирования межпроцессных связей через общую память с синхронизацией семафорами - базовая для SMP- и однопроцессорных компьютеров. Функции работы с Shared memory непосредственно входят в состав каждой многозадачной операционной системы. В пределах одного компьютера все остальные средства межпроцессовой коммуникации реализуются через SHM, и потому заведомо являются менее быстрыми,
       
    • В модели с общей памятью совместно используемые данные хранятся в такой памяти в единственном экземпляре, без создания локальных копий для каждой ветви приложения. Ветви, работающие с такими данными, затрачивают незначительное время на синхронизацию доступа к данным, и в-целом соотношение скорости коммуникаций и скорости вычислений в модели с общей памятью выше, чем в модели с передачей сообщений. Следовательно, класс задач, пригодных к распараллеливанию в SHM, в-принципе шире, нежели в MPI.
       
    • SHM намного дольше используется для программирования высокопроизводительных вычислений, и многие проблемные программисты либо изучили эту методику лучше, либо вообще изучили только ее одну.
    Эти доводы нельзя было не привести здесь потому, что в реальной жизни их регулярно, и не без основания, приводят уже упомянутые проблемные программисты. Но в документе, посвященном MPI, им, конечно, будут противопоставлены определенные контрдоводы.
    Итак, почему MPI не(намного) хуже SHM:
    • в хорошо распараллеленном приложении на собственно взаимодействие между ветвями (пересылки данных и синхронизацию) тратится небольшая доля времени - несколько процентов от общего времени работы; таким образом, замедление пересылок, допустим, в два раза не означает общего падения производительности вдвое - она понизится на несколько процентов; подобное незначительное понижение производительности можно считать приемлемым,
       
    • средства, имеющиеся в MPI (асинхронные/перманентные коммуникации) и особенно в MPI-2 (удаленный доступ к памяти) предоставляют скорость, сравнимую со скоростью SHM.
    Почему MPI предпочтительнее SHM:
    • все имеющиеся реализации SHM API либо в-принципе непереносимы за пределы SMP-машин, либо недостаточно эффективны, либо недостаточно распространены;
       
    • стоимость вычислений и коммуникаций в SMP-машинах падает медленнее, чем в MPP-комплексах и сетях;
       
    • методика обмена данными в виде сообщений обладает изначально большей наглядностью для программиста,
       
    • в отличие от MPI, SHM API, как правило, реализует только самые базовые операции,
    Таким образом:
      MPI сокращает разрыв в скорости выполнения программ, и увеличивает разрыв в скорости их написания.


    Какое программное обеспечение существует
    между программистом и MPI

    MPI сам по себе является средством:
    • сложным: спецификация на MPI-1 содержит 300 страниц, на MPI-2 - еще 500 (причем это только отличия и добавления к MPI-1), и программисту для эффективной работы так или иначе придется с ними ознакомиться, помнить о наличии нескольких сотен функций, и о тонкостях их применения;
    • специализированным: это система связи - и все.Кстати, можно сказать, что сложность (т.е. многочисленность функций и обилие аргументов у большинства из них) является ценой за компромисс между эффективностью и универсальностью. С одной стороны, на SMP-машине должны существовать способы получить почти столь же высокую скорость при обмене данными между ветвями, как и при традиционном программировании через разделяемую память и семафоры. С другой стороны, все функции должны работать на любой платформе.
    Таким образом, программист заинтересован в инструментах, которые облегчали бы:
    • проведение декомпозиции,
    • запись ее в терминах MPI.
    То есть, в данном случае это средства, генерирующие на базе неких входных данных текст программы на стандартном Си или Фортране, обладающей явным параллелизмом, выраженным в терминах MPI; содержащий вызовы MPI-процедур, наиболее эффективные в окружающем контексте. Такие средства, в-частности, делают написание программы не только легче, но и надежнее, так как:
    • ошибки, которые MPI в-принципе не может обнаружить в момент выполнения, генератор имеет возможность обнаруживать в момент построения программы, например:
          Ветвь 1:                     Ветвь 2:
          MPI_Recv( ... , 2, ... );    MPI_Recv( ... , 1, ... );
          MPI_Send( ... , 2, ... );    MPI_Send( ... , 1, ... );
                          |                            |
                   Это номер ветви-отправителя/получателя
      Примечание: здесь произойдет блокировка - ветви не смогут завершить прием, потому что не смогут начать передачу, потому что не смогут завершить прием. Как итог, программа повиснет.Еще одно примечание: а вот если вызовы функций приема и передачи поменять местами, то блокировки не произойдет - MPI выберет для передачи буферизованный режим, и MPI_Send, скопировав данные во временный буфер, вернет управление сразу же, не дожидаясь, пока сообщение будет фактически принято приемной стороной. Таким образом, по сравнению с низкоуровневыми коммуникациями, MPI все-таки делает программу более устойчивой к ошибкам программиста. Правда, буферизация означает падение быстродействия тем более ощутимое, чем быстрее происходит собственно пересылка данных. 
       
    • некоторые ошибки исключаются вообще, например, одинаковый числовой идентификатор для разных сообщений, или неверный (больше общего количества ветвей в коллективе) номер ветви в аргументах функции приемопередачи - в отличие от человека, программа-генератор, если только она сама написана правильно, не в состоянии описАться.
    Назовем некоторые перспективные типы такого инструментария, который лишал бы программиста необходимости вообще помнить о присутствии MPI.
    Средства автоматической декомпозиции. Идеалом является такое оптимизирующее средство, которое на входе получает исходный текст некоего последовательного алгоритма, написанный на обычном языке программирования, и выдает на выходе исходный текст этого же алгоритма на этом же языке, но уже в распараллеленном на ветви виде, с вызовами MPI. Что ж, такие средства созданы (например, в состав полнофункционального пакета Forge входит, наряду с прочим, и такой препроцессор), но до сих пор, насколько мне известно, никто не торопится раздавать их бесплатно. Кроме того, вызывает сомнение их эффективность.
    языки программирования. Это наиболее популярные на сегодняшний день средства полуавтоматической декомпозиции. В синтаксис универсального языка программирования (Си или Фортрана) вводятся дополнения для записи параллельных конструкций кода и данных. Препроцессор переводит текст в текст на стандартном языке с вызовами MPI. Примеры таких систем: mpC (massively parallel C) и HPF (High Performance Fortran).
    Общим недостатком инструментов, производящих преобразование "текст в текст", является то, что синтаксическому разбору подвергаются оба текста: и исходный (его обрабатывает распараллеливающий препроцессор), и генерируемый (его обрабатывает компилятор). Это уменьшает скорость построения программы, и, кроме того, необходимость делать синтаксический разбор усложняет написание препроцессора. Поэтому, например, те фирмы-производители, которые поставляют свои ЭВМ вместе с Фортраном, встраивают HPF прямо в компилятор машинно-зависимого кода. Для расширений языка Си аналогичное решение может быть найдено в использовании GNU C.
    Оптимизированные библиотеки для стандартных языков. В этом случае оптимизация вообще может быть скрыта от проблемного программиста. Чем больший объем работы внутри программы отводится подпрограммам такой библиотеки, тем бОльшим будет итоговый выигрыш в скорости ее (программы) работы. Собственно же программа пишется на обычном языке программирования безо всяких упоминаний об MPI, и строится стандартным компилятором. От программиста потребуется лишь указать для компоновки имя библиотечного файла MPI, и запускать полученный в итоге исполняемый не непосредственно, а через MPI-загрузчик. Популярные библиотеки обработки матриц, такие как Linpack, Lapack и ScaLapack, уже переписаны под MPI.
    Средства визуального проектирования. Действительно, почему бы не расположить на экране несколько окон с исходным текстом ветвей, и пусть пользователь легким движением мыши протягивает стрелки от точек передачи к точкам приема - а визуальный построитель генерирует полный исходный текст? Тем, кто стряпал базы данных в Access'e, такая технология покажется наиболее естественной.
    Отладчики и профайлеры. Об отладчиках мне пока нечего сказать, кроме того, что они нужны. Должна быть возможность одновременной трассировки/просмотра нескольких параллельно работающих ветвей - что-либо более конкретное мне пока сказать трудно.

  • основы mpi

    Теория


    Начнем с начала

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

    MPI — Message Passing Interface. Это специфический API, который реализуют производители кластеров для того, чтобы можно было легко переносить программы с кластера на кластер не изменяя ни байта исходного кода(!).
    Параллельная программа должна эффективно использовать вычислительные мощности и коммуникационную среду. В MPI вся работа по распределению нагрузки на узлы и сеть ложатся на программиста и для максимальной производительности необходимо знать особенности конкретного кластера. MPI очень элегантно решает вопрос топологии сети: имеются понятия коммуникаторов — группы процессов, которые можно пронумеровать в соответствии с топологией сети (для этого используется функция MPI_Cart_create, которая позволяет задать любую топологию от решётки до гиперкуба).

    Целесообразность распараллеливания

    Некоторые примеры в учебных пособиях весьма синтетические — в них считается какой-нибудь ряд в пределах стандартного типа (например, double), что на практике вычисляется за время много меньшее того, которое тратится на инициализацию и передачу чего-либо по сети (вычисление числа pi в double на двух компьютерах с Gigabit Ethernet примерно в два раза медленнее вычисления на одном компьютере). Однако, MPI позволяет использовать многоядерные процессоры (что почему-то многие забывают), а между ядрами скорость передачи совершенно другого порядка, поэтому всегда нужно знать архитектуру и топологию системы.

    Практика


    Про теорию можно много писать, но лучше постигать теорию соразмерно с практикой.
    Для начала установим какую-нибудь реализацию MPI на свой компьютер. Одной из самых распространённых реализаций MPI является MPICH (MPI Chameleon).
    Установка

    В убунте устанавливается в одну строчку:

    sudo apt-get install mpich2

    Напишем простенькую программку, которая ничего полезного не делает:

    #include <stdio.h>
    #include <mpi.h>

    int main (int argc, char* argv[])
    {
      int errCode;

      if ((errCode = MPI_Init(&argc, &argv)) != 0)
      {
        return errCode;
      }

      int myRank;

      MPI_Comm_rank(MPI_COMM_WORLD, &myRank);

      if (myRank == 0)
      {
        printf("It works!\n");
      }

      MPI_Finalize();
      return 0;
    }

    * This source code was highlighted with Source Code Highlighter.


    Скомпилируем эту программку:

    mpicc -o test.bin ./test.c

    Попробуем запустить:

    mpirun ./test.bin

    И (если еще не настроили демон mpd) получим сообщение о том, что демон mpd не запущен.

    mpiexec: cannot connect to local mpd (/tmp/mpd2.console_valery); possible causes:
    1. no mpd is running on this host
    2. an mpd is running but was started without a "console" (-n option)
    In case 1, you can start an mpd on this host with:
    mpd &
    and you will be able to run jobs just on this host.
    For more details on starting mpds on a set of hosts, see
    the MPICH2 Installation Guide.


    При попытке запустить mpd будет сказано об отсутствии настроек (почему, собственно, не запускается демон)

    configuration file /home/valery/.mpd.conf not found
    A file named .mpd.conf file must be present in the user's home
    directory (/etc/mpd.conf if root) with read and write access
    only for the user, and must contain at least a line with:
    MPD_SECRETWORD=<secretword>
    One way to safely create this file is to do the following:
    cd $HOME
    touch .mpd.conf
    chmod 600 .mpd.conf
    and then use an editor to insert a line like
    MPD_SECRETWORD=mr45-j9z
    into the file. (Of course use some other secret word than mr45-j9z.)


    Секретное слово нужно только для подключения узлов. Если мы будем подключать ещё компьютеры, то надо будет и на них ставить MPICH и надо будет занести узел в список узлов, а также не будет лишним настроить подключение по ssh с использованием ключей (для общения с узлами).
    Если всё сделано правильно, то получим примерно такой вывод:

    $ mpirun ./test.bin
    It works!


    MPI_Init — обязательна для вызова, так как выполняет инициализацию библиотеки MPI.
    MPI_COMM_WORLD — идентификатор глобального коммуникатора, содержащего все процессы.
    MPI_Comm_rank — возвращает идентификатор (номер, ранг) процесса в рамках заданного коммуникатора.

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

    Вместо вывода

    Можно написать параллельную программу не имея почти никаких знаний о параллельном программировании, но написание эффективных программ является трудоёмким процессом выбора алгоритма и его реализации, подгонки под систему и так далее. Но если начать писать простенькие программки и при этом читать спецификации и литературу о вычислительных системах (об их архитектуре, коммуникационных средах и прочем), то со временем, %username%, будешь способен подчинить себе даже такие страшные машины как те, которые представлены в списке топ-500

    как писать и читать письма

    Перейти к концу метаданных
    Переход к началу метаданных
    Сегодня на мое громкое замечание "И нафига меня поставили в копию этого письма" Леша прислал ссылку в тему.
    Пост о том, как писать письма, в конце ссылка на то, как читать тексты. Привожу текст, подвергнутый цензуре. 
    Вопрос, казалось бы, не стоит выеденного яйца. Каждый думает, что он умеет отправлять письма, не так ли? Так в чем же тогда дело? 
    А тогда почему многие на работе регулярно пишут письма так, что им хоть почтовый аккаунт вырубай? Если кто не в курсе - электронная почта есть самый опасный способ коммуникации на работе. Это - пост-памятка, который я пишу для себя.  Надоело объяснять в двадцатый раз - буду давать ссылку.
    Начнем с того, что почта все помнит, и ничего не забывает. Сюрприз! Ваши письма легко поднять в любой удобный момент, и кому-нибудь втихую форварднуть, причем без вашего ведома. Или же, распечатать, и предъявить вам же при "разборе полетов". Проникнетесь этим, и
    1. Никогда не пишите в почте того, что вы не хотите, чтобы видели и помнили все. 
    Более того, можно форварднуть не всю переписку (ее один всю читать никто не будет), а дернуть из контекста самое интересное. И продолжим тем, что оправленную почту назад не вернуть. Не плюй в колодец. Вылетит - не поймаешь.
    2. Хорошенько подумайте, что вы пишете, и как оно будет звучать будучи вырванным из контекста. Избегайте любых эмоциональных формулировок. Сделайте это до отправки письма! И если есть хоть малейшие сомнения по поводу содержимого - не отправляйте такое письмо. Это проще, чем кажется - достаточно не нажимать кнопку оправить, и все.
    На самом деле, Outlook умеет отзывать почту, если ее не успели прочитать, а вы вдруг одумались. Но я бы не советовал уповать на это. 
    Да, кстати, капитализация, то есть те самые ЗАГЛАВНЫЕ БУКОВКИ, которыми вы выделяете наиболее значимые слова, воспринимаются людьми как КРИК. То есть, крайнее проявление бурлящих, понимаете ли, эмоций. И если даже вы никого не обвиняете, и не кидаете в письме конкретных предъяв - люди подумают, что вы именно этим и занимаетесь. В противном случае – почему вы тогда ОРЕТЕ?
    3. Никогда, слышите - НИКОГДА не применяйте капитализацию. Вместо этого можно _подчеркнуть_ слово, или *выделить* его, если вам так приспичило.
    То же самое касается восклицательных знаков!!! С ними надо обращаться аккуратнее!!!!! Я сказал - АККУРАТНЕЕ!!!!!!!!!
    На чем мы остановились? Ах да. На вырывании из контекста. Так вот, убить весь контекст очень просто. Для этого отлично подходит подстрочный ответ.
    > Он
    делается
    > примерно
    вот
    > так.
    Раздербаниваем, понимаете ли, чужое письмо на абзацы (а лучше - на предложения), и начинаем по каждому предложению нести свой поток сознания. Таким образом можно легко свести дискуссию на, или доказать кому-нибудь, что он идиот.
    4. Отвечайте на мысль, содержащуюся в письме, целиком. Не комментируйте предложения, и даже абзацы. Если только они не являются единицей мысли. И даже тогда - отвечайте не на абзацы, а на все письмо. 
    Да, кстати, абзац - это вовсе не какая-то там ненужная штука, выдуманная филологами. Абзац - он единица мысли, придуманный специально, чтобы отличать осмысленный текст от выплеска потока сознания. Мысль у вас в письме - есть? А абзацы? 
    5. Используйте абзацы для отделения тезисов и мыслей друг от друга 
    Тогда, может быть, ваши письма кто-нибудь будет читать. Потому, как всем, в том числе и вам, приходит и так много корпоративного спама, который вы не читаете. Поэтому, собравшись писать письмо, трижды подумайте - может быть его, письмо вообще писать не нужно? Кому оно, в сущности, нужно, ваше письмо, а? 
    6. Ограничьтесь минимумом получателей письма. Иначе его не прочтут те, кому оно действительно адресовано.
    Подумали, но не передумали. Что ж, тогда вы можете сильно повысить шансы вашего письма на прочтение, сделав его коротким. Три абзаца - это почти предел объема корпоративной писанины, которую способен вынести средний читатель. 
    Читатель-же не средний, а топ уровня - получает ежедневно настолько много спама, что у них вырабатывается привычка читать письма не дальше первого абзаца. Учтите это, когда соберетесь писать очередное длинное письмо. 
    7. Уже первый абзац должен давать ясное понимание, о чем это вообще, достаточную для принятия решения - читать сейчас письмо дальше, или нет. Опишите контекст проблемы (о чем это вообще), содержательную часть (что вы хотите сказать), и какого именно действия вы хотите от получателя письма после его прочтения. 
    А что вы, собственно, от человека добиться хотите? Хотите, чтобы он сделал что? Решил немедленно указанную проблему? Отписал вам свое мнение по ней? Проверил ваше решение на предмет ошибок? Что-то согласовал?
    Или, может быть, вы хотите, чтобы он принял ваше письмо к сведению, и никак не прореагировал? Так именно так он и сделает, если вы не напишете ему в конце: "прошу подтвердить, что описанные изменения можно выпускать в релиз". Можно на "принятие к сведению" обижаться - а можно просто научиться пользоваться электронной почтой. И все.
    Если вы хотите, чтобы письмо не проигнорировали не смотря ни на что - есть еще одно сильнодействующее средство. Но его надо использовать с осторожностью. Не, речь не о дурацких штуках вроде выставления приоритета письму в аутлуке. Есть средство посильнее:
    8. Ставьте в копию важного письма, на которое нужен срочный ответ, своего начальника, или начальника адресата 
    Начальник все равно особо вникать не будет. Но адресат-то этого не знает, не так ли? Злоупотреблять этим не советую, ровно как писать в таких письмах ерунду. Вылетит. Не поймаешь.
    Ну и главное. Почта - самый узкий канал коммуникации. Тонкостей ваших эмоций, которые вы испытываете при написании, адресат не видит. Но люди не могут без эмоций, и поэтому они будут за вас выдуманы. В конфликтной ситуации - не в вашу пользу. Учитывая это, редко кому удается почтой помириться, зато конфликт почтой потрясающе легко раздуть на ровном месте. Большинство конфликтов разогревается именно на электронной почте.
    Имея это в виду,
    9. Никогда не используйте почту для решения любых скользких эмоциональных вопросов. Чуть что не так - встречайтесь лично, на худой конец - телефон. 
    Ага, именно так. Не надо вот этих вот затяжных веток слово - за слово с десятками постов и адресатов ради выяснения какой-то никому не важной ерунды. Думаете, весь десяток адресатов с замиранием сердца следит за вашей дискуссией? Нет. Всем наплевать.
    Такие дела. Ну, может быть, я погорячился, когда говорил, что буду давать на этот текст ссылку как на обучающий материал :). Ибо он нарушает как минимум парочку принципов деловой переписки. :) Но я надеюсь, "всем наплевать". :)