Этот ответ описывает, как я буду делать это с системой параллельного языка и среды выполнения, над которой я работаю ежедневно, Charm ++ . Обратите внимание, что язык, используемый для последовательного кода в этой среде, - это C или C ++, поэтому вам придется приложить некоторые усилия для переноса вычислительного кода. Charm ++ имеет некоторые механизмы для взаимодействия с кодом Python, хотя я менее знаком с этими аспектами. Вполне возможно, что вы можете сохранить код драйвера и интерфейса в Python и просто поместить тяжелый вычислительный код в C ++. В любом случае, портирование последовательного кода, скорее всего, принесет вам хороший прирост производительности.
Конструкция:
Создайте массив параллельных объектов (в нашей среде это называется chares ) и назначьте каждому рабочий список внутренних узлов дерева, начиная с некоторого корня поддерева и продолжая частично вниз. Любые листья, прикрепленные к этим узлам, также будут принадлежать этому шару.
Каждому параллельному объекту потребуются два асинхронных метода удаленного вызова, известных как методы ввода , passDown(float a, float b)
и passUp(int nodeID, float f)
, которые будут точками связи между ними. passDown
будет вызывать любой метод узла, используемый для вычисления предзаказа, а узлы, у которых есть дочерние объекты вне объекта, будут вызывать passDown
для этих объектов-потомков.
Как только вся нисходящая работа сделана, объект вычислит восходящую работу на своих листьях и будет ждать своих потомков. Вызов passUp
будет вычисляться как можно дальше в дерево принимающего объекта, пока не достигнет родителя, который не получил данные от всех своих дочерних объектов. Когда корневой узел объекта выполнен с восходящей работой, он вызовет passUp
для объекта, содержащего родительский узел. Когда корень всего дерева готов, вы знаете, что итерация завершена.
Результаты выполнения:
Как только это будет реализовано, система времени выполнения будет выполнять параллельное выполнение за вас. Он будет распределять объекты по процессорам и даже по отдельным вычислительным узлам (что значительно увеличивает потолок размера дерева, поскольку доступная память может масштабироваться гораздо выше). Связь между процессорами и узлами выглядит как внутрипроцессное взаимодействие - асинхронные вызовы методов. Среда выполнения может балансировать нагрузку на объекты, чтобы все ваши процессоры были заняты на протяжении как можно большей части каждой итерации.
Настройка:
Если вы пойдете этим путем и дойдете до точки настройки параллельной производительности, вы также можете установить приоритеты в сообщениях, чтобы сократить длину критического пути. Вдобавок к моей голове, расстановка приоритетов, которую я бы предложил, сработала бы в таком порядке
- Нисходящая работа, которая не равна нулю
- Работа наверх
- Ближе к листьям идет раньше
Charm ++ работает с инструментом анализа производительности, известным как «Проекции», чтобы лучше понять, как работает ваша программа.