У меня есть коллекция вычислительных моделей, которые можно описать как асинхронные клеточные автоматы. Эти модели напоминают модель Изинга, но немного сложнее. Кажется, что такие модели выиграли бы от работы на GPU, а не на CPU. К сожалению, распараллелить такую модель довольно непросто, и мне совершенно не понятно, как это сделать. Я знаю, что по этому вопросу есть литература, но все это, по-видимому, предназначено для компьютерных ученых, которые интересуются деталями алгоритмической сложности, а не для того, кто, как я, просто хочет описать что-то, что я могу реализовать, и следовательно, я нахожу это довольно непроницаемым.
Для ясности я ищу не столько оптимальный алгоритм, сколько что-то, что я могу быстро реализовать в CUDA, что может значительно ускорить реализацию моего процессора. В этом проекте время программиста является гораздо более ограничивающим фактором, чем время компьютера.
Я также должен пояснить, что асинхронный клеточный автомат довольно сильно отличается от синхронного, и методы распараллеливания синхронных СА (такие как жизнь Конвея) не могут быть легко адаптированы к этой проблеме. Разница в том, что синхронный CA обновляет каждую ячейку одновременно на каждом временном шаге, тогда как асинхронный обновляет случайно выбранную локальную область на каждом временном шаге, как описано ниже.
Модели, которые я хочу распараллелить, реализованы на решетке (обычно гексагональной), состоящей из ~ 100000 ячеек (хотя я бы хотел использовать больше), и непараллельный алгоритм их запуска выглядит следующим образом:
Выберите соседнюю пару ячеек наугад
Вычислить «энергетическую» функцию на основе локальной окрестности, окружающей эти клетки
С вероятностью, которая зависит от (с β параметром), либо поменяйте местами состояния двух ячеек, либо ничего не делайте.
Повторите вышеуказанные шаги до бесконечности.
Есть также некоторые сложности, связанные с граничными условиями, но я полагаю, что они не будут создавать больших трудностей для распараллеливания.
Стоит отметить, что меня интересует не только состояние равновесия, но и динамика переходного процесса в этих системах, поэтому мне нужно нечто, имеющее динамику, эквивалентную приведенной выше, а не то, что будет приближаться к тому же равновесному распределению. (Таким образом, вариации алгоритма chequerboard не то, что я ищу.)
Основная сложность в распараллеливании вышеуказанного алгоритма - столкновения. Поскольку все вычисления зависят только от локальной области решетки, многие узлы решетки могут обновляться параллельно, если их окрестности не перекрываются. Вопрос в том, как избежать таких совпадений. Я могу придумать несколько способов, но я не знаю, какой из них лучше всего реализовать. Это следующие:
Используйте ЦП для генерации списка случайных узлов сетки и проверки на наличие коллизий. Когда количество узлов сетки равно количеству процессоров графического процессора или если обнаружено столкновение, отправьте каждый набор координат в блок графического процессора для обновления соответствующего узла сетки. Это было бы легко реализовать, но, вероятно, не дало бы значительного ускорения, поскольку проверка на конфликты в ЦП, вероятно, не была бы намного дешевле, чем полное обновление ЦП.
Разделите решетку на области (по одной на единицу графического процессора), и пусть один блок графического процессора отвечает за случайный выбор и обновление ячеек сетки в своей области. Но есть много проблем с этой идеей, которые я не знаю, как решить, наиболее очевидным из которых является то, что именно должно произойти, когда подразделение выбирает район, перекрывающий край его региона.
Приблизьте систему следующим образом: пусть время протекает дискретно. Разделите решетку на другуюнабор областей на каждом временном шаге в соответствии с некоторой предварительно определенной схемой, и каждый блок графического процессора случайным образом выбирает и обновляет пару ячеек сетки, соседство которых не перекрывает границу региона. Поскольку границы меняются каждый раз по времени, это ограничение может не сильно влиять на динамику, если регионы относительно велики. Это кажется простым для реализации и, вероятно, будет быстрым, но я не знаю, насколько хорошо он будет приближаться к динамике, или какова лучшая схема выбора границ региона на каждом временном шаге. Я нашел некоторые ссылки на «блочно-синхронные клеточные автоматы», которые могут совпадать или не совпадать с этой идеей. (Я не знаю, потому что кажется, что все описания метода либо на русском языке, либо в источниках, к которым у меня нет доступа.)
Мои конкретные вопросы следующие:
Являются ли какие-либо из вышеперечисленных алгоритмов разумным способом приблизиться к распараллеливанию GPU асинхронной модели CA?
Есть ли способ лучше?
Существует ли существующий библиотечный код для этого типа проблемы?
Где я могу найти четкое англоязычное описание метода "block-синхронный"?
Прогресс
Я полагаю, что придумала способ распараллеливания асинхронного ЦС, который мог бы подойти. Алгоритм, описанный ниже, предназначен для обычного асинхронного ЦС, который обновляет только одну ячейку за раз, а не для соседней пары ячеек, как моя. Есть некоторые проблемы с обобщением этого в моем конкретном случае, но я думаю, что у меня есть идея, как их решить. Тем не менее, я не уверен, какую выгоду от скорости это даст по причинам, обсуждаемым ниже.
Идея состоит в том, чтобы заменить асинхронный CA (далее ACA) стохастическим синхронным CA (SCA), который ведет себя эквивалентно. Для этого сначала представим, что ACA - это пуассоновский процесс. То есть время протекает непрерывно, и каждая ячейка в качестве постоянной вероятности в единицу времени выполняет свою функцию обновления независимо от других ячеек.
это параметр, значение которого можно выбрать произвольно.)
На каждом логическом временном шаге ячейки SCA обновляются следующим образом:
Я полагаю, что это гарантирует, что ячейки будут обновлены в порядке, который может быть «декодирован», чтобы соответствовать исходному ACA, избегая коллизий и позволяя некоторым ячейкам обновляться параллельно. Однако из-за первого пункта выше, это означает, что большинство процессоров GPU будут в основном простаивать на каждом временном шаге SCA, что не идеально.
Мне нужно еще немного подумать о том, можно ли улучшить производительность этого алгоритма и как расширить этот алгоритм, чтобы справиться со случаем, когда несколько ячеек обновляются одновременно в ACA. Тем не менее, это выглядит многообещающе, поэтому я подумал, что опишу это здесь на случай, если кто-то (а) узнает что-либо подобное в литературе или (б) сможет предложить какое-либо понимание этих оставшихся вопросов.
exp()
), поэтому я бы не подумал, что имеет смысл распределять ее по нескольким потокам. Я думаю, что лучше (и мне проще) попытаться обновить несколько пар параллельно, по одной паре на поток.