Современный язык программирования с интуитивно понятными абстракциями параллельного программирования [закрыто]

40

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

Чтобы привести некоторые примеры, я не считаю хорошим вариантом написания многопоточного кода на C, C ++ или Java, потому что ИМХО моя производительность снижается, а их модель программирования не интуитивна. С другой стороны, хорошими вариантами будут языки, которые повышают производительность и предлагают более интуитивные абстракции, такие как Python и модуль многопроцессорной обработки, Erlang, Clojure, Scala и т. Д.

Что бы вы посоветовали, основываясь на вашем опыте и почему?

РЕДАКТИРОВАТЬ: Спасибо всем за ваши интересные ответы. Трудно сделать вывод, фактически не пытаясь, так как есть много хороших кандидатов: Erlang, Clojure, Scala, Groovy и, возможно, Haskell. Я проголосовал за ответ с наиболее убедительными аргументами, но я попробую всех хороших кандидатов, прежде чем решить, какой из них выбрать :)

faif
источник
21
To give an example, I don't consider a good option writing multithreaded code in C, C++, or Java, Зачем? On the other hand, Python and the multiprocessing module, Erlang, Clojure, Scala, etc. are some of my options.Опять же почему? Расширьте свой вопрос, чтобы лучше определить, что вы на самом деле ищете.
Яннис
2
Итак, вы хотите научиться параллельному программированию со всеми проблемами или хотите скрыть некоторые его сложности и сосредоточиться на производительности?
MaR
@MaR Сосредоточиться на производительности и скрыть сложность :)
Сакиск
Просто отметьте, что в некоторых из этих языков избегаются многие важные понятия (некоторые могут сказать, что они решены), и, следовательно, C - действительно лучший язык для изучения параллелизма. (Или, по крайней мере, мне так кажется; я не знаю достаточно обо всех перечисленных языках). Повышение производительности часто противоречит всестороннему обучению.
user606723 22.11.11
1
@DeadMG Снижение производительности является их проблемой. Я не хочу сосредотачиваться на синтаксисе языка вместо проблемы. Я определенно не хочу в конечном итоге бороться с тупиками. В качестве простого примера я хочу использовать простые операторы, такие как begin transaction end transactionи все внутри должно быть без тупиков и либо успешно, либо с ошибками в целом.
Сакиск

Ответы:

33

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

Ключевые атрибуты:

  • Это функциональный язык , который является благом как для параллелизма, так и для вашей способности развиваться с использованием абстракций более высокого уровня. Он имеет полностью неизменяемые постоянные структуры данных и ленивые последовательности, которые будут знакомы всем, кто имеет опыт работы с функциональными языками, такими как Haskell.
  • Он оснащен очень новой программной системой транзакционной памяти для одновременного доступа без блокировки к изменяемому состоянию. Сделать код безопасным для параллелизма часто так же просто, как заключить его в блок (dosync ....).
  • Это Лисп - что делает его чрезвычайно мощным для макропрограммирования на основе макросов и генерации кода. Это может принести значительные преимущества в производительности (эссе Пола Грэма - «Обгоняя средние»)
  • Это язык JVM - поэтому вы не только получаете доступ к огромному массиву библиотек и инструментов в экосистеме Java, но также получаете выгоду от огромных инженерных усилий, направленных на то, чтобы сделать JVM эффективной платформой для параллельных серверных приложений. В практических целях это дает ему огромное преимущество перед языками, на которых нет такого рода оснований.
  • Это динамично - что приводит к очень лаконичному коду и большой производительности. Однако обратите внимание, что при необходимости вы можете использовать дополнительные статические подсказки типа для производительности.
  • Язык разработан на основе абстракций, которые довольно сложно объяснить, но в итоге вы получаете набор относительно ортогональных функций, которые вы можете комбинировать для решения своих проблем. Примером может служить абстракция последовательности, которая позволяет вам писать код, который имеет дело с каждым «последовательным» типом объекта (который включает в себя все от списков, строк, массивов Java, бесконечных ленивых последовательностей, строк, читаемых из файла и т. Д.)
  • Существует большое сообщество - полезное, проницательное, но самое главное очень прагматичное - в Clojure основное внимание, как правило, уделяется «достижению цели».

Некоторые мини-примеры кода с уклоном параллелизма:

;; define and launch a future to execute do-something in another thread
(def a (future (do-something)))

;; wait for the future to finish and print its return value
(println @a)

;; call two functions protected in a single STM transaction
(dosync
  (function-one)
  (function-two))

В частности, стоит посмотреть одно или несколько из этих видео:

mikera
источник
21
Цель статических объявлений типов в строго типизированных языках не состоит в том, чтобы «повысить производительность там, где это необходимо», и я немного устал от сторонников Лиспа, преследующих этого старого соломенного чучела. Объявления типов имеют две цели: обеспечить определенные гарантии корректности во время компиляции и сделать код более легким для чтения, особенно для кого-то, кроме первоначального автора. По своей природе лучшая производительность, которую обеспечивает статическая типизация, является лишь бонусом.
Мейсон Уилер
8
В последнее время мне приходилось работать с кодом JavaScript другого разработчика на работе, и это самая болезненная часть процесса: без типов в аргументах функции, мне приходится охотиться по всей базе кода, чтобы выяснить, что они должны быть и что они могут сделать, основываясь на том, откуда они вызваны. Это было бы не проблема, если бы JavaScript сохранил систему типов Си в дополнение к ее общему синтаксису.
Мейсон Уилер
1
@MasonWheeler: ИМХО, если вы не можете понять, как вызывать функцию без аннотаций типов, это проблема с документацией (или ее отсутствием). Даже в типизированных утиных языках все обычно должно удовлетворять некоторым структурным ограничениям типа (например, должно поддерживать арифметические операции, должно быть повторяемым, должно быть индексируемым и т. Д.). Статические типы помогли бы только минимально, потому что они не давали бы большого намека на то, что делает функция .
Димча
2
@ Мейсон Я никогда не говорил, что у статических объявлений типов нет других преимуществ. На самом деле мне нравятся статические объявления типов именно по тем причинам, о которых вы говорите. Однако мне также нравится прирост производительности динамической типизации. Это компромисс. Если у вас есть хороший набор тестов, я обычно нахожу, что это уменьшает многие недостатки динамической типизации как с точки зрения обеспечения корректности, так и с помощью примера кода, чтобы помочь новичкам понять правильное использование. YMMV.
Микера
1
@dsimcha - альтернативой проектированию вокруг абстракций будет проектирование вокруг конкретной реализации. Например, большинство старых функций Lisp работали только со связанными списками, хранящимися в cons-ячейках. Вам нужны разные функции для разных структур данных. В Clojure основная библиотечная функция работает над чем угодно (как в ответе).
Микера
27

Вы можете попробовать D. Он предлагает три модели. Я рекомендую либо первое, либо второе.

  1. std.concurrency . Если вы используете этот модуль для всех ваших потребностей параллелизма, то сочетание языка и стандартной библиотеки обеспечивает изоляцию между потоками. Потоки в основном обмениваются сообщениями посредством передачи сообщений с ограниченной поддержкой общей памяти таким образом, чтобы обеспечить безопасность, прежде всего, и запретить низкоуровневые гонки данных. К сожалению, документация std.concurrency нуждается в улучшении, но модель описана в бесплатной главе книги Андрея Александреску «Язык программирования D».

  2. std.parallelism . Этот модуль разработан специально для многоядерного параллелизма, а не общего параллелизма. ( Параллелизм и параллелизм - это не одно и то же, хотя параллелизм необходим для реализации параллелизма. ) Поскольку суть параллелизма - это производительность, std.parallelism не делает никаких гарантий изоляции, поскольку они затруднят написание эффективного параллельного кода. Однако он абстрагирует множество подверженных ошибкам низкоуровневых деталей, поэтому очень сложно обойтись, если вы распараллеливаете рабочие нагрузки, которые вы проверяли вручную, взаимно независимы.

  3. core.thread - это низкоуровневая оболочка для API-интерфейсов потоков, специфичных для ОС. И std.concurrency, и std.parallelism используют его под капотом, но я бы порекомендовал использовать его только в том случае, если вы пишете собственную библиотеку параллелизма или находите какой-то нелепый угловой пример, который не может быть хорошо реализован ни в std.parallelism, ни в std. .concurrency. Никто не должен использовать что-то такого низкого уровня для повседневной работы.

dsimcha
источник
Вы должны были упомянуть неизменяемость / чистоту, локальное хранилище потока по умолчанию и общее, которое навязывает мутацию в последовательном порядке. То есть поддержка языков отсутствует в C / C ++ для написания параллельного кода.
Deadalnix
@deadalnix: Для меня большинство из них - это детали модели std.concurrency (как применяется изоляция). Я хотел, чтобы этот пост был кратким.
дсимча
Ну на самом деле нет. Конкуренция требует как библиотечной, так и языковой поддержки.
Deadalnix
@deadalnix: Да, но они были созданы в основном для поддержки std.concurrency.
dsimcha
23

Erlang, безусловно, отличный вариант, но что-то более практичное может быть Go , новый язык Google.

Это не так далеко от других распространенных языков, поэтому его легко получить, если вы уже знаете другие «простые» языки. Многие сравнивают его с Python или даже Lua с точки зрения того, насколько «удобно» это программировать.

Хавьер
источник
@faif спрашивает об уровне приложения / пользователя, а не о параллельном программировании системы. Как Erlang подходит для этого?
Хирон
@Raynos: зависит от сообщества.
Донал Феллоуз
@DonalFellows ваше право, я думаю, что мое заявление было слишком узким
Рэйнос
1
@Chiron: Erlang - это язык программирования, он используется для создания приложений. Как правило, многопроцессорные приложения. Я не знаю, где он подходит как «параллельное программирование систем», я не слышал ни об одной ОС, написанной на Erlang.
Хавьер
1
Кратко рассмотрев учебник Go, я хочу сказать, что IMHO язык с C-подобным синтаксисом, в котором используются (ограниченные) указатели, определенно не является современным языком, повышающим производительность.
Сакиск
23

Посмотрите на параллельное программирование Microsoft для .net. Это очень интуитивно понятно.

Многие персональные компьютеры и рабочие станции имеют два или четыре ядра (то есть процессоры), которые позволяют выполнять несколько потоков одновременно. Ожидается, что компьютеры в ближайшем будущем будут иметь значительно больше ядер. Чтобы воспользоваться преимуществами оборудования сегодняшнего и завтрашнего дня, вы можете распараллелить свой код для распределения работы между несколькими процессорами. В прошлом распараллеливание требовало низкоуровневых манипуляций с потоками и блокировками. Visual Studio 2010 и .NET Framework 4 расширяют поддержку параллельного программирования, предоставляя новую среду выполнения, новые типы библиотек классов и новые диагностические инструменты. Эти функции упрощают параллельную разработку, так что вы можете писать эффективный, детализированный и масштабируемый параллельный код в естественной идиоме, не работая непосредственно с потоками или пулом потоков. http://i.msdn.microsoft.com/dynimg/IC292903.png

A ----------------------- -----
источник
+1 Это именно то, что он просит. Однако, когда проблемы все же возникают, будет трудно их отладить без понимания параллелизма на более низком уровне. Не говоря уже о том, что начинать с C # как новичка может оказаться ... интересно.
P.Brian.Mackey
@ P.Brian.Mackey - согласен. Тем не менее, это не редкость, было бы не
сложно
1
Особенно PLINQ. Хотя это полезно только для небольшого подмножества задач, оно может быть очень простым в использовании.
svick
21

И Эрланг, и Скала имеют параллелизм , основанный на актерах , который я нашел очень интуитивным и легким для изучения.

Модель Actor в информатике - это математическая модель параллельных вычислений, которая рассматривает «акторов» как универсальные примитивы одновременных цифровых вычислений: в ответ на полученное сообщение субъект может принимать локальные решения, создавать больше актеров, отправлять больше сообщений и определить, как реагировать на следующее полученное сообщение ... Оно использовалось как в качестве основы для теоретического понимания вычислений, так и в качестве теоретической основы для нескольких практических реализаций параллельных систем.

TMN
источник
19

Я узнаю о Haskell прямо сейчас, и чтение этой статьи убедило меня, что Haskell - хороший вариант для параллельного программирования. Поскольку он является чисто функциональным (система типов знает, выполняет ли функция какой-либо ввод, вывод или чтение / изменение глобального состояния), он может выполнять такие операции, как программная транзакционная память (очень хорошо описанная в вышеприведенном документе), которая ведет себя аналогично транзакциям. в базах данных - вы получаете кучу приятных вещей, таких как атомарность, с небольшим добавлением сахара. AFAIK, Нити Haskell тоже очень легкие. В дополнение к этим фактам, тот факт, что Haskell является чисто функциональным, позволяет даже простым задачам выполняться параллельно с чуть более чем одним ключевым словом (par). источник

оборота AlexWebr
источник
7

Язык Google GO имеет несколько интересных инструментов для параллелизма - это было бы еще одной забавной вещью, которую можно попробовать. Смотрите: http://golang.org/doc/effective_go.html#concurrency и читайте немного примеров.

Параллельное программирование - это большая тема, и здесь есть место только для некоторых специфичных для Go основных моментов.

Параллельное программирование во многих средах затруднено из-за тонкостей, необходимых для реализации правильного доступа к разделяемым переменным. Go поощряет другой подход, при котором общие значения передаются по каналам и фактически никогда не используются совместно отдельными потоками выполнения. Только одна программа имеет доступ к значению в любой момент времени. Гонки данных не могут происходить, по замыслу. Для поощрения такого мышления мы сократили его до лозунга:

Не общайтесь, разделяя память; вместо этого делитесь памятью, общаясь.

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

Один из способов обдумать эту модель - рассмотреть типичную однопоточную программу, работающую на одном процессоре. Он не нуждается в синхронизации примитивов. Теперь запустите другой такой экземпляр; это тоже не нуждается в синхронизации. Теперь пусть эти двое общаются; если связь является синхронизатором, другие синхронизации не требуются. Например, конвейеры Unix идеально подходят для этой модели. Хотя подход Go к параллелизму берет свое начало в коммуникативных последовательных процессах (CSP) Хоара, его также можно рассматривать как безопасное для типов обобщение каналов Unix ...

комара
источник
6

В следующей версии C # делает это еще проще, чем показано на диаграмме. Появились два новых ключевых слова Async и Await.

Async используется в качестве модификатора функции и говорит: «эта операция выполняет свою работу в другом потоке.

Await используется в функции Async, и именно здесь происходит волшебство. По сути, Await указывает компилятору запустить операцию после ключевого слова в отдельном потоке и дождаться результатов. Любой код после вызова await выполняется после операции.

ТАКЖЕ, операция синхронизируется с вызывающим потоком (поэтому, если вы выполняете асинхронную операцию в ответ на нажатие кнопки, вам не нужно вручную отправлять обратно в поток пользовательского интерфейса). Два маленьких ключевых слова, и вы получаете много возможностей параллелизма. Узнайте больше здесь

Майкл Браун
источник
Обратите внимание, что любой достойный компилятор OS C # уже поддерживает C # 5, асинхронный и ожидающий.
Райнос
По сути, Await указывает компилятору запустить операцию после ключевого слова в отдельном потоке и дождаться результатов. Интересно, если этот ответ правильный - асинхронное ожидание не о потоках. Эта статья объясняет, приятно: нет
темы
Хорошая точка зрения. Я думаю, я слишком просто говорил об этом. На самом деле происходит "продолжение", которое подписывается на событие задачи в ожидаемом завершении. И да, определенные операции ввода-вывода и thread.sleep () (которые в основном отвечают на прерывание тактового сигнала) не имеют потока. но как насчет ручных заданий, которые не выполняют операций ввода-вывода, скажем, мы сделали ожидаемый калькулятор Фибоначчи? Технически статья верна: «Нет потока», но на самом деле его никогда не было, это всегда была концепция, которую мы использовали, чтобы скрыть детали того, что операционная система делала для нас.
Майкл Браун
6

Я все еще рекомендую C ++. Он более чем способен к необходимым абстракциям для написания приличного параллельного кода. Подавляющая вероятность состоит в том, что у вас просто плохая библиотека для выполнения работы, поскольку хорошие библиотеки для выполнения работы являются относительно новыми, и, действительно, знания для правильного использования C ++ не совсем обычны. TBB Intel существует всего несколько лет, а PPL от Microsoft поставляется только с прошлого года.

Если вы используете что-то вроде TBB или PPL, то параллельный код, на самом деле, писать не совсем тривиально , поскольку параллелизм никогда не бывает тривиальным, но далеко не трудным. Если вы напрямую используете потоки pthreads или Win32, то неудивительно, что вам это не нравится - вы практически пишете на ассемблере с такими функциями. Но с PPL, вы говорите о стандартных функциональных алгоритмах, которые распараллелены для вас, общих структурах данных для одновременного доступа, и подобных вещах.

DeadMG
источник
1
Проверьте Boost.Threads или C ++ 0x std::thread(или std::tr1::thread). На самом деле это очень хорошая абстракция, ИМО.
Greyfade
1
@greyfade: у них нет абсолютно ничего о PPL или TBB. boost::threadэто просто оболочка ОС с небольшим RAII. PPL и TBB - это реальные параллельные алгоритмы, контейнеры и т. Д.
DeadMG
6

Здесь нужен плагин для Ada , так как в нем есть все абстракции высшего уровня для параллелизма и параллелизма. иначе известный как управление задачами . Кроме того, поскольку OP попросил интуитивно (субъективные критерии!), Я думаю, что можно оценить другой подход к java-центрированному миру.

NWS
источник
5

Я хотел бы предложить Groovy / Java / GPars, если вы можете быть на основе JVM, поскольку он учитывает участников, поток данных, обмен последовательными процессами (CSP), параллелизм данных, программную транзакционную память (STM), агенты, ... Дело в том, что там Существует много моделей параллелизма и параллелизма высокого уровня, каждая из которых имеет свои «сладкие точки». Вы не хотите использовать модель, которая не согласуется с решением проблемы, которую вы пытаетесь построить. Языки и фреймворки только с одной моделью вынуждают вас взламывать алгоритмы.

Конечно, меня могут считать предвзятым, так как я участвую в Groovy и GPars. С другой стороны, я работаю с CSP и Python, ср. Python-CSP.

Еще один момент заключается в том, что первоначальный вопрос касается обучения, а не написания производственной системы. Таким образом, комбинация Groovy / Java / GPars является хорошим способом обучения, даже если возможная производственная работа выполняется в C ++ с использованием чего-то вроде Just :: Thread Pro или TBB, а не на основе JVM.

(Некоторые вполне разумные URL-ссылки пришлось удалить из-за некоторой паники по поводу спама на хост-сайте.)

Рассел Виндер
источник
Рассел, если ты хочешь, ты можешь сказать мне, что ты хочешь связать в Чате, и я добавлю их для тебя: chat.stackexchange.com/rooms/21/programmers
Дэн МакГрат,
4

Что насчет Clojure? Например, вы можете использовать Swing, но наслаждаетесь средствами параллельного программирования Clojure? Clojure имеет неплохую интеграцию с Java.

Кроме того, вы рассматривали Java 7 Fork / Join Framework ?

Chiron
источник
2

Вы также можете посмотреть Groovy и библиотеку GPars . GPars BTW отчасти похож на .NET Parallel Extension, упомянутый в другом ответе, но гибкий синтаксис Groovys делает его читаемым лучше в некоторых обстоятельствах.

Кристиан Хорсдал
источник
0

Scala упоминалась несколько раз в вопросах и ответах, но я не видел ссылок на Akka, которая является актерской реализацией, которую можно использовать как с Scala, так и с Java.

Джорджио
источник
Что не так с этим ответом? Ни один другой ответ не упомянул akka, и akka реализует высокоуровневую абстракцию для параллельного программирования.
Джорджио
-1

Я думаю, это зависит от того, что вы строите. Настольные приложения или сервер? Я слышал, что (но у меня нет личного опыта) node.js отлично подходит для параллельного программирования для серверов (как с точки зрения написания кода, так и с точки зрения производительности). Если бы я хотел написать новое серверное приложение, я бы, наверное, попробовал это. Не уверен насчет настольных приложений ... Я написал довольно много вещей на C #, и есть некоторые инструменты, которые красиво скрывают сложность, хотя в других случаях вам приходится иметь дело с этим в лоб.

Aerik
источник
-1

Я могу получить удар по голове, но вы читали главу 7 TAOUP ? Раздел, о котором я думаю, - это потоки и процессы. Я обнаружил, что концепция параллельной обработки заставляет большинство людей думать о потоках, но я никогда не видел случая, когда поток проще и быстрее использовать, чем порождать дочерний процесс.

Вы прорабатываете все детали обработки параллелизма умным парням, которые создали вашу ОС. Уже есть много способов коммуникации, и вам не нужно беспокоиться о блокировке общих ресурсов. По сути, потоки - это хак эффективности, который подпадает под правило оптимизации. Не оптимизируйте, если вы не проверяли по необходимости.

Найдите хорошую библиотеку подпроцессов, такую ​​как envoy для python . Или вы можете просто написать несколько отдельных программ на C и написать еще одну «основную» программу для использования fork и pipe для порождения и взаимодействия с подпроцессами.

Спенсер Рэтбун
источник
3
Это противоположно тому, что явно хочет OP: порождение процесса так же низкоуровнево, как и порождение потока вручную. ОП интересуется абстракциями высокого уровня параллелизма.
Конрад Рудольф