как я могу гарантировать, что я пишу код, максимально параллельный с самого начала?
В общем, проблема связана с зависимостью данных.Операция зависит от другой операции, если ей нужны данные, чтобы начать выполнять себя.Здесь есть несколько важных трюков.Предположим, вы знаете, что операция может иметь только 2 возможных результата.Затем вы можете начать оба вычисления, а затем «выбрать» правильное.Фактически, дизайн ЦП часто использует эту идею в вычислительных единицах (см., Например, статью в Википедии о Carry Select Adders ).Еще один трюк, когда вы знаете, что результат будет определенным значением, скажем, с вероятностью 99%.Затем вы можете умело выполнять следующую операцию и надеяться, что ваш прогноз окажется верным.Если это не удается, вы всегда можете откатить вычисления и повторить их.Это также делается в современных процессорах, см., Например, прогнозирование ветвлений со спекулятивным выполнением и буферизацией переупорядочения.
На этом этапе вам должно быть ясно, что современные архитектуры ЦП уже используют огромное количество параллелизациифокусы .Проблема в том, что они вытеснили все локальные распараллеливания, которые мы можем надеяться получить, и теперь нам нужно внедрить идеи «на один уровень» в наши программы.
Как я могу гарантировать, чтомой код получит максимально возможную выгоду от добавления нескольких ядер?
Что убивает параллельные вычисления - это зависимости.Если вы зависите от другой части вычислений, вам нужно связать между параллельными потоками выполнения.Этот ход приводит к остановкам , так как вы ожидаете отправки сообщений другими частями.Часто используемым приемом является скрытие задержки : вместо того, чтобы ждать прибытия сообщения, вы делаете что-то еще между ними, надеясь, что данные были полностью переданы, когда вам это нужно.Но еще лучше, если вы можете организовать свой код так, чтобы он вообще не взаимодействовал.
Именно поэтому функциональное программирование рассматривается как мощный инструмент для параллелизма.В FP нет общего состояния, и поэтому код уже находится в состоянии, в котором легко разбить небольшие пакеты вычислений на разные процессоры.На мой взгляд, Haskell является наиболее зрелым языком для этой идеи благодаря ключевому слову par
.
В общем, если ваша программа имеет мало зависимостей в потоке данных, ее легко сделать быстро.при добавлении нескольких ядер.Вы хотите как можно больше избегать дросселирования сериализации в вашем потоке данных.
Erlang Я не хочу упоминать Erlang в том, что Erlang получает свой параллелизм косвенно.В Erlang мы описываем программу как набор одновременных действий, а именно набор процессов , которые работают вместе для решения проблемы.Процессы изолированы и обмениваются друг с другом сообщениями .Главная уникальная сила Erlangs - отказоустойчивость: ошибка в одном процессе не может повлиять на другой процесс из-за изоляции - так что вы можете создать программное обеспечение, которое будет восстанавливаться в случае одиночного процесса, переходящего в состояние мертвого шагающего зомби.
Теперь эта модель в значительной степени опирается на очевидную стратегию параллельной оценки: когда одно ядро обрабатывает один процесс, у нас могут быть лишние ядра, захватывающие другие процессы для обработки.Опять же, если процессы не зависят друг от друга, ожидая прибытия сообщения, то добавление большего количества ядер приведет к ускорению.
Хотя это не волшебная серебряная пуля.Если вы сериализуете все свои сообщения через один процесс как «дроссель», тогда ваш код не будет параллельным, в игру вступят Амдаль или Густафссон, и вы останетесь с последовательной программой.