Original on http://www.coopsoft.com/ar/ForkJoinArticle.html

Модель Вилкового соединения в Java ™ SE

Вилковые соединения для повседневных, мноядерных приложений Java ™

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

Эта статья описывает "потрясающий параллельный" подход вилочного соединения, хорошо работает с повседневными, многоядерными приложениями Java ™ SE, ME, а также Android. ™ (3000 слов)

Эдвард Харнд ( eh at coopsoft dot com )
Старший разработчик, Cooperative Software Systems, Inc.
Февраль, 2010 [обновлено в июне 2013]

Что такое вилковое соединение?

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

Вилково соединение разрывает приложение на несколько частей для параллельной обработки и в конце присоединяет результаты.

Рисунок 1: Структура вилочного соединения

Скажем, у нас есть массив тысячи чисел. Нам нужно выполнить вычисления по каждому из них и добавить результат.

Листинг 1: Обработка массивов

for (int i = 0; i <1000; i ++) {

total +=doProcedure(array [i]);

}

Если каждое вычисление требует одну секунду (физическое время), тогда нам понадобится тысяча секунд (более 16½ минут) для завершения задачи.

Вилково соединение может:

  • разделить (разветвившись) большой массив на 10 массивов по 100 элементов каждый,
  • обработать каждый массив на отдельном ЦБ,
  • объединить результаты после завершения.

На все это уйдет сто секунд (чуть более 1 ½ минуты), одна десятая от времени начала. Чем больше доступных ЦБ, тем быстрее выводится результат.

Вот и все, что представляют собой научные вычисления - одновременная обработка огромного объема данных на всех возможных доступных центральных процессорах. Эта абстракция напоминает стандартную научную модель - «Разделяй и властвуй».

«Разделяй и властвуй» - это естественная парадигма параллельных алгоритмов. После разделения задачи на две и более подзадачи, метод решает подзадачи параллельно. Как правило, подзадачи решаются рекурсивно и, таким образом, следующий шаг делится еще на подзадачи для решения их параллельно.

Рисунок 2: Разделяй и-властвуй

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

В создании задач проблемы нет; это только объекты. Проблема в большом количестве потоков, обрабатывающих задачи, когда таким задачам нужны:

подключение
Для доступа к удаленным службам (СУБД, сообщений и многого другого) требуется подключение. Как правило, отдаленные службы используют поток для обработки подключения, на что нужна память, переключение контекста, синхронизация и координация. Чем больше подключений к службе, тем больше ресурсов необходимо, и тем меньше подключений доступно для других задач в виртуальной машине Java. Это влияет на каждого пользователя.

Блокировка
Блокировка - это убийцы высокой производительности. Мертвые / живые блокировки, инверсия приоритетов, зависание, сопровождение и расходы ресурсов (что идет в геометрической прогрессии с длиной перечень адресов задач) возникают при использовании блокировок.

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

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

расширенная память
Чем больше объектов или чем объекты больше, тем больше требуется памяти. Чем активнее потоки, обрабатывающие задачи, тем больше памяти используется. Естественно, задачи, требующие много памяти, должны регулироваться.

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

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

Если парадигма динамической декомпозиции «разделяй и властвуй» соответствует вашим потребностям, то прочитайте эту статью о высокой производительности DSE версии Tymeac . В противном случае Структура функционального разветвления вам на помощь.

Структура функционального разветвления

Java ™ SE / ME многоядерные приложения, а также Android- ™ приложения, которые не могут использовать модель «разделяй и властвуй», не обрабатывают большие массивы чисел, или не имеют вычислительную интенсивную структуру, потребует структуре функционального разветвления для распараллеливания приложений. В частности, они должны разветвившись свою работу на функциональные компоненты, а не разбивать массив на одинаковые подзадачи.

Рисунок 3: Структура функционального разветвления

Структура функционального разветвления имеет два свойства. Она должна:

  • Ограничить соревнования.
  • Быть простой в использовании или чрезвычайно параллельной.

ограничения соревнования

Содержание числа активных конкурирующих потоков к абсолютному минимуму имеет первостепенное значение. Самый простой способ ограничить потоковое соревнования - это использовать пороговые значения для каждого пула потоков, обслуживающего очередь задач. Смотрите статью о Высокопродуктивных приоритетных очередях в Java SE качестве примера того, как с помощью списков ожидания можно сохранить потоковые ресурсы.

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

Возьмем, к примеру, задачу запрашивающей доступ к базе данных, требует [java.sql.] Оператор. При использовании очереди запросов, код задачи может разделить тот же оператор на много обращений, а не спрашивать новый оператор для каждого доступа. Общее использование оператора изрядно экономит на затратах ресурсов и ограничивает соревнования в коде управления.

Что значит чрезвычайно параллельный?

Очень параллельные алгоритмы - это такие, которые могут решать многие подобные, но независимых задач одновременно с малой необходимостью координации между задачами. Эти виды проблем так легко распараллеливаемые, что "со смущением" Стоит ли говорить, как просто задействовать эффективно процессоры.

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

Рисунок 4: Очень параллельный

К примеру:
Предположим, бизнес требует автоматизированной системы котируемой цены. Чтобы разработать смету, система нуждается в данных о базовой цене продукта (база данных цены), скидке клиента на продукты и отгрузки (база данных клиентов), об основных транспортных расходах (база данных грузоотправителя).

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

В параллельной системе, программа разветвляет () запрос в три очереди, каждая обслуживается пулом потока, ждет завершения последнего запроса доступа и объединяет () результаты вместе.

Рисунок 5: Котировки цены

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

Есть множество ситуаций, где желательно выполнить разветвления работы:

• Возьмите игровое приложение, в котором мы можем разветвившись событие на отдельные компоненты. Преимущество здесь - азарт; Чем больше сегментов событий происходят одновременно, тем интереснее игра.

• Возьмите приложение с несколькими анимациями, где мы можем разветвившись каждую анимацию для работы на своем собственном процессоре.

• Возьмите операцию обработки изображений, где каждый пиксель в изображении требует обратить свой цвет. Структура может легко распределить данные изображения для множества задач, которые будут работать независимо друг от друга.

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

• Возьмите медицинский приложение, где различные тесты - это компоненты в диагностике.

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

Как эта структура выглядит в приложении Java ™?

Для структуры ветвления запроса на его функциональные компоненты необходимо:

Информация о компонентах (очередях) для каждой операции запроса (функции). Простой класс, содержащий имя строковой функции и список соответствующей очереди является основным в Java ™ программировании.

Листинг 2: Класс функции

public class Function {

private String name; // Имя функции

private Queue[] que; // Очереди для этого класса
}

Поместите запрос (содержит входные объекты) в каждую из очередей, возвращая массив объектов вызывающему оператору или игнорируя возвращаемые объекты.

Листинг 3: Введение в очередь

public Object[] fork(Queue[] que, Object request) {Object[] return_obj = new Object[] {null, null, null};

for (int i = 0; i <que.length; i ++) {
putInQueue (que[i], return_obj[i], request);
}

return return_obj;
}

Ждите завершения/перерыва или не ждите.

Листинг 4: Ждите / Не ждите

public boolean join(Object[] obj) {/ * если иметенты НЕ нули, возвращается истинным
* Немного подождите
* После интервала, возвращается ложным
* /
}

Верните результат вызывающему оператору или игнорируйте объекты.

Рисунок 6: Возвращение вызывающему оператору

Для создания структуры, нам необходимо:

1. поддерживать фактический код задачи, который делает работу

2. поддерживать список очередей и функций

3. поддерживать класс "запуска", который загружает очереди и функции в память

(1) Код, который делает работу, должен выглядеть так:

Листинг 5: Рабочий код

public static Object main (Object obj) {}

Основной () метод, который принимает объект (введение от вызывающего оператора) и возвращает объект (результат работы).

(2) Мы могли бы поддерживать очереди и функции как объекты в пределах простого списка класса.

(3) При запуске могут быть просто загружены списки классов в память с новым (Списку класса) и начать потоки каждой очереди.

Как простой вызов может выглядеть:

Листинг 6: Простой вызов

Framework fw = new Framework(); // For each call:
Function func = fw.getFunction(name)
Object[] back = func.fork(request};

Структуру очень легко использовать, к смущению легко.

Итог

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

Сделать сервер

Какие изменения мы должны сделать, чтобы наладить эту простую структуру в качестве сервера?

1. Мы должны отделить вызывает оператора от работы.

2. Мы должны обеспечить восстановление после ошибок.

3. Мы должны поддерживать структуру в качестве удаленного объекта.

4. Мы должны обеспечить безопасность.

5. Мы должны обеспечить административную функциональность для управления сервером.

отделение
Во-первых нужно отделить посреднический запрос (это метод выше разветвления ()) фактической обработки. Мы должны отделить, но следить за каждым запросом в уникальном объекте.

Листинг 7: Запрос объекта

private long unique_id; // Уникальная идентификация этого запроса
private Object input; // Вступительное ссылки, если таковое имеется
private boolean type; // Тип запроса верно = sync не верно = async
private Object[] output // объекты вывода из задачи
private AtomicInteger next_output; // Добавить индекс к элементу выше
private Queue[] que_names; // Список всех очередей в функции private Queue agent; // Будущая очередь, если таковая имеется
private AtomicInteger nbr_remaining; // Очереди, оставшиеся для обработки
private int wait_time; // Макс время ожидания для запроса sync

Что означает поле "агент?"

агент
Синхронный запрос возвращает массив объектов по обработке вызывающему оператору. Что структуре делать с массивом объектов, которые вернулись из асинхронного запроса? Структура опционально расположит массив объектов (как введение) в новой очереди для обработки с помощью агента. Таким образом, агент будет действовать в зависимости от статуса завершения предыдущего обработки.

К примеру:
Функция создает котировки цены и отправляет ее пользователю в виде асинхронного запроса.

1. Вызывающий оператор использует асинхронный метод ветвления ().

2. Структура разветвляет запрос по своим соответствующим очередей.

3. Когда заканчивается последняя Очередь, структура передает возвращен массив объектов агенту, поставив запрос в очередь, указанную как "агент".

4. Агент отправляет неразделенной письмо

исправление ошибок
Второе изменение добавляет исправления ошибок, знак профессионализма.

Что может пойти здесь не так? "Все, что может пойти не так, пойдет не так." Закон Мерфи .

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

• У нас есть три очереди (A, B, C) в функции.

• Очереди А и Б успешно получить запрос.

• Очереди С не удается получить запрос, потому что очередь заполнена.

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

Исключение / Ошибка
У нас может возникнуть выключения / ошибка в рабочем коде задачи. Если он раз вышел из строя, скорее всего, это повторится снова. Поэтому, желательно, отключить очереди до тех пор, пока разработчик не решит проблему. Когда код задача чистый, мы не хотим, чтобы падал сервер. Мы хотим сообщить сервер, мы имеем новую копию кода задач, чистое, и мы хотим включить очереди.

остановка
Может произойти в асинхронном запросе, и называется остановкой (синхронные запросы прекращены, и могут очиститься из системы). Поскольку функции не могут завершиться, пока не закончатся все Очереди, мы должны поместить зашли в тупик запросы в Зависший Список. Когда очередь исправлена, мы можем заново запустить обработку с зависшего списка.

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

Вычеркивания является отдельным вопросом и требует ограничения потока.

Эта статья вводит в тему: Управление потоками в Java SE

отмена
Мы можем прописать вызывающему оператору желание отменить ранее поданный запрос. Отмена похожа на тайм-аут ошибки, но применима к обоим синхронным и асинхронным запросам. Хотя и отмены запроса более желательна, логика для обработки отмены в многокомпонентной запросе не для слабонервных.

мониторинг
Сроки бесполезны, если модуль-демон потока, контролирующий события, ищет реальные или потенциальные проблемы.

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

удаленный объект
Третья смена - это поддержка структуры в качестве удаленного объекта с дополнительными активации / деактивации в целях экономии ресурсов.

Удаленное обращение к методам проходит во многих разновидностях:

основной

Фабрика пользовательского разъема (Custom Socket Factory)

Интеллектуальный процессор ввода-вывода

Сносный объектный адаптер

Jini

Мижпроцесорная взаимодействие

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

Безопасность
Четвертое изменение - это безопасность

Технология безопасности Java ™ является частью SE / ME платформ, это требует связь сервера с классами безопасности для гибкости.

функции администратора
Пятое изменение - это добавление функции администратора.

Вход в систему скучный и бесполезный прежде чем что-то пойдет не так.

Статистика является основой для анализа и настройкой производительности.

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

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

Сколько займет времени построить такой сервер?

Около 5-6 секунд. Столько же, сколько и распаковать файл.

К счастью, есть общее назначение, структура вилочного соединения, поддерживающая особенности, описанные выше - для повседневных, многоядерных приложений в Java ™ SE, ME и Android- ™, доступных сегодня. А так как структура может работать как сервер RMI (стандартный / активируется, IIOP и POA) он доступен для приложений Java EE ™.

Tymeac ™ для Java ™ SE/ME/Android ™ платформ - это поддерживаемые программные проекты с открытым исходным кодом,

и вы можете скачать последние версии на сайте.

вывод

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

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

ссылка

Загрузка:

Загрузите последнюю версию SE Tymeac здесь . Со всей документацией, скриптами, классами и исходным кодом.

Загрузите последнюю версию ME Tymeac здесь . Со всей документацией и исходным кодом.

Загрузите последнюю версию AND Tymeac здесь . Со всей документацией и со всеми проектами затмение.

Загрузите последнюю версию DSE Tymeac здесь . Версия Разделяй и-Властвуй.

статьи:

Высокая производительность версии Разделяй и-Властвуй Tymeac - Завоеватель вилочного соединения

Высокая производительность Android- ™ версии Tymeac - Управление потоками в Android

Использование списков ожидания еффективности- Высокая производительность приоритетных очередей в Java SE

Java ™ SE потоковый контейнер - Управление потоками в Java SE

прочее:

Очередь вилочного соединения, вики - http://en.wikipedia.org/wiki/Fork-join_queue

Закон Мерфи - http://en.wikipedia.org/wiki/Murphy%27s_law

ЦБ кэш вики - http://en.wikipedia.org/wiki/CPU_cache

Когерентность кэша, вики - http://en.wikipedia.org/wiki/Cache_coherence

Чрезвычайная параллельность, вики - http://en.wikipedia.org/wiki/Embarrassingly_parallel

о разработчике

Эдвард Харнд является разработчиком программного обеспечения с опытом более тридцати лет. По первой он вел проекты в качестве работника в основных отраслях промышленности, а затем работал в качестве независимого консультанта. Сегодня, Эд старший разработчик программного обеспечения в Cooperative Software Systems, Inc ., Где в течение последних двенадцати лет, использует программирования на Java ™, в целях применения вилочного присоединения в широком круге задач.

© 2010 - 2011 E.П.Харнд Все права защищены