В настоящее время я создаю язык программирования для развлечения, идея которого заключается в том, чтобы каждый вызов функции / новый блок (если предложения, циклы и т. Д.) Работал в отдельном потоке. Вместо того, чтобы создавать новые потоки, стандарт должен заключаться в том, что он делает это автоматически, и если вы хотите, чтобы он запускался в основном потоке, вам придется это указать.
Я не очень осведомлен о многопоточном, параллельном программировании, но я знаю основы (Futures, объекты, защищенные от потоков). Поэтому мне интересно, как такой язык может выглядеть с точки зрения синтаксиса и возможно ли вообще начать с него? Цель состоит не в том, чтобы сделать его «полезным», а в большей степени для удовольствия и обучения.
(Извините, если это неправильное место для публикации. Если это так, я был бы рад, если вы укажете мне правильное место, где разрешен вопрос, подобный моему.)
Ответы:
Прочитайте больше о продолжениях и стиле прохождения продолжения (и их отношении к потокам или сопрограммам). Я предлагаю прочитать SICP & Lisp In Small Pieces . Кроме того, язык программирования Pragmatics дает полезный обзор нескольких языков и поможет вам создать свой собственный.
Синтаксис не имеет большого значения для изучения идей . Семантика имеет гораздо большее значение. Я предлагаю сначала использовать синтаксис типа S-expr (чтобы вы могли создавать прототипы, используя Scheme и ее вызов / cc ).
Как только ваши идеи станут более ясными (после некоторых экспериментов), вы можете обсудить их на лямбда-предельном , вы можете определить более сексуальный синтаксис (который на самом деле имеет значение, если вы хотите, чтобы люди переняли ваш язык, а также важно качество реализации, образец реализации бесплатного программного обеспечения, документация, стандартная библиотека, интерфейс сторонних функций и т. д.)
источник
Возможно, вам будет интересно почитать об исследовании параллельных данных на Haskell . Если вы будете искать на YouTube, Саймон Пейтон Джонс выступил с некоторыми интересными докладами, связанными с этой темой.
Если я правильно помню из его выступлений, в чисто функциональном программировании почти тривиально найти возможности для создания потоков. Его главная проблема в его исследованиях - иметь слишком много слишком коротких, так что накладные расходы на создание потоков и передачу их результатов существенно перевешивают преимущества параллелизма. Например, легко научить компилятор раскручивать до 100 потоков для вычислений
sum $ fmap (+1) <100 length vector>
, но стоит ли это делать?Хитрость заключается в том, чтобы консолидировать потоки в выгодные размеры, не налагая бремени на программиста, чтобы обозначать это вручную. Это сложная проблема, которую необходимо решить, чтобы эффективно использовать будущие ПК с тысячами ядер.
источник
Это именно то, что делает Эрланг. Он обрабатывает присоединение потоков в основном с использованием очередей. Это блестящая концепция, но немного сложнее сразу обернуть голову, если вы работаете с языками процедурного типа. Я настоятельно рекомендую изучить это.
источник
Во-первых, я бы порекомендовал вам взглянуть на PROMELA , язык, используемый для описания параллельного алгоритма, чтобы средство проверки модели могло грубо форсировать все возможные исполнения, чтобы убедиться, что оно не в состоянии некорректно работать. (Общеизвестно, что параллельное программирование трудно понять правильно, поэтому такие методы проверки важны.) Он не запускает все конструкции в отдельных потоках, но имеет довольно странный синтаксис и семантику, поскольку его фокусом является недетерминированность параллельных программ.
Если говорить более абстрактно, то π-исчисление является прекрасным подходом к моделированию параллельных вычислений. Трудно разобраться, если вы не достанете книгу Робин Милнер « Коммуникационные и мобильные системы: исчисление Пи ». Это помогло мне подумать о параллельных вычислениях в более широком смысле, так как «несколько потоков обращаются к общей памяти». Интересно, как условные выражения, "gotos" и т. Д. Могут быть построены из более простых, естественно параллельных примитивов.
Что касается синтаксиса ... лучший способ решить это - написать несколько примеров программ. Напишите программу для сортировки массива или одновременно пропингуйте несколько серверов и сообщите, какой из них отвечает быстрее всего, или попробуйте решить лабиринт параллельно или еще что-нибудь. Пока вы это делаете, то, что отсутствует в вашем синтаксисе, станет очевидным, и вы сможете добавить их. После того, как вы добавили несколько вещей, спросите себя, есть ли у них что-то общее, и если да, то, возможно, вы сможете найти более простой подход, который может служить нескольким целям.
источник
Подобные проекты были предприняты в прошлом. Я предлагаю читать классику, чтобы добыть идеи. (Все ссылки идут на Википедию)
Unity Этот язык использовался / используется для обучения параллельному программированию. Я не думаю, что это было на самом деле реализовано. Синтаксис несколько загадочный, но в основном у вас есть набор операторов, которые выполняются в неизвестном порядке и многократно, пока больше ничего не будет сделано. Это ближе всего к тому, что вы просите.
Occam Этот язык был разработан для реального использования, но он никогда не завоевывал популярность. Здесь есть ключевое слово PAR, которое означает, что список операторов должен выполняться параллельно.
Эрланг Другой язык реального мира. Этот используется телекоммуникационной компанией Ericsson и имеет довольно много последователей. Они приложили много усилий, чтобы сделать параллелизм практичным и полезным.
Google GO Это моя любимая группа. Концептуально почти так же, как Erlang, но с лучшим синтаксисом и весом Google за ним. Что может пойти не так?
Я хотел бы закончить с предупреждением: параллелизм очень трудно понять правильно. Большинство ошибок в современных программах - результат неправильного понимания . Вы уверены, что хотите пойти туда?
источник
Это возможно, но это не будет полезно для 99 +% всех мыслимых приложений. Логика типично привязана к последовательности, это поток. Шаг за шагом вы достигаете решения проблемы, и порядок шагов имеет значение, главным образом потому, что выходные данные одного шага будут входными для следующего.
В тех немногих случаях у вас есть много задач, которые могут быть выполнены независимо друг от друга, как правило, дешево устанавливать их последовательно, прежде чем запускать параллельно.
Итак, я думаю, что ваше время было бы лучше потратить на изучение того, как использовать функции многопоточности на вашем любимом языке программирования.
источник
Clojure может стоить посмотреть на некоторые идеи.
http://clojure-doc.org/articles/language/concurrency_and_parallelism.html
Вот некоторые соображения: если мы называем вычислительную единицу, которая может быть выполнена независимо, задачей: 1. Задачи независимы, поэтому могут выполняться одновременно 2. Для разных задач требуются разные ресурсы и для их выполнения требуется разное время 3. Поэтому задачи должны планироваться для максимальной пропускной способности 4. Единственная программа, которая может выполнять функции планировщика, - это операционная система
Такие вещи, как яблочная грандиозная центральная диспетчеризация, являются попыткой предоставить такой планировщик.
Вышеуказанное означает, что ответственность за выполнение задач не обязательно лежит на языке программирования.
Вторая мысль - максимально снизить нагрузку на программирование параллельных систем. Единственный способ сделать это - удалить любую спецификацию того, как что-то сделать из программы. Программа должна указывать только то, что должно быть сделано, а остальное должно происходить автоматически.
Вышесказанное, вероятно, означает, что динамические языки и своевременная компиляция - это путь.
источник
То, что вы ищете, называется неявным параллелизмом, и есть языки, которые исследовали эту концепцию, такие как Sun / Oracle Fortress . Помимо прочего, он (потенциально) запускает циклы параллельно.
К сожалению, оно было прекращено, и есть много мертвых ссылок, но вы все равно можете найти несколько PDF-файлов, если вы достаточно усердно гуглите:
https://www.eecis.udel.edu/~cavazos/cisc879-spring2008/papers/fortress.pdf (спецификация языка)
http://stephane.ducasse.free.fr/Teaching/CoursAnnecy/0506-Master/ForPresentations/Fortress-PLDITutorialSlides9Jun2006.pdf
http://www.oracle.com/technetwork/systems/ts-5206-159453.pdf
http://dl.acm.org/citation.cfm?id=1122972 (paywalled)
Стоит отметить, что вы, как правило, не хотите запускать фактический поток для каждого оператора / выражения, поскольку создание и запуск потоков, как правило, обходятся дорого - вместо этого у вас будет пул потоков, в который вы отправляете куски работы, которые нужно выполнить , Но это деталь реализации.
источник
Хотя это и не язык программирования как таковой, вы должны взглянуть на VHDL . Он используется для описания цифровых схем, которые, естественно, делают все параллельно, если вы не указали это делать последовательно. Это может дать вам некоторые идеи, как разработать свой язык и для какой логики он может подойти.
источник
Это может быть легко смоделировано в C ++. Просто убедитесь, что «каждый» * вызов функции реализован с помощью
std::future
. Обработка возвращаемого значения просто осуществляется путем вызова.get()
на будущее.Следовательно, вы можете создать прототип своего языка путем компиляции в C ++. Это также говорит нам о том, как будет выглядеть синтаксис: главное отличие состоит в том, что вы отделяете точку вызова (где предоставляются входные аргументы) от точки возврата (где используется выход функции).
(*) Я говорю «каждая функция», но вам решать, что считать функцией. Это
memset
встроенная или функция? Является ли присвоение целых чисел или присвоение пользовательских типов вызовом функции?источник