- PVSM.RU - https://www.pvsm.ru -
Высоконагруженные системы, построенные по модели акторов – это тренд сегодняшнего времени. Вот далеко неполный перечень статей на хабре, в которых, в той или иной степени, упоминается данная модель или одна из ее реализаций, например,1 [1], 2 [2], 3 [3], 3 [4], 4 [5], 5 [6], 6 [7], 7 [8]. Есть хорошая статья в википедии [9], рассказывающая про акторы. К сожалению, после ее прочтения, у меня осталось много вопросов, ответы на которые я смог найти только в первоисточниках. Результаты этого обзора я и хочу представить Вашему вниманию.
Акторы – это набор практик, методология разработки, архитектурный паттерн, маркетинговый ход?
В моем университетском курсе, как и у многих, понятие вычислимости определялось через машины Тьюринга и Поста. У машины есть состояние – совокупность значений всех ячеек ленты. Процесс вычислений представляет собой последовательность шагов машины, каждый из которых меняет ее состояние. Каждый шаг машины – выполнение одного атомарного неделимого действия (операции). Далее буду называть это традиционной моделью вычислений.
Такая трактовка вычислений приводит к понятию глобального времени, когда в один и тот же момент времени может выполняться одна и только одна атомарная операция. Это свойство существенным образом используется для доказательства свойств различных примитивов синхронизации в случае многопоточного программирования на однопроцессорных машинах, например, в книге Грегори Р. Эндрюс [10] Основы многопоточного, параллельного и распределенного программирования [11].
Для многопроцессорных машин или распределенных систем требование глобального времени, в общем случае, неприемлемо. Следовательно, необходимо обобщить понятие вычислимости на случай параллельных вычислений. Одним из таких обобщений и является модель акторов.
Появление данной модели в далекие 70-е годы было обусловлено стойкой верой в то, что машины следующего поколения будут многопроцессорными, а программы обладать искусственным интеллектом. В 1973 году Carl Hewitt [12], Peter Bishop и Richard Steiger опубликовали статью A Universal Modular Actor Formalism For Artificial Intelligent [13]. В этой статье они ввели понятие актора и объяснили, что многие классы приложений являются частным случаем модели акторов. Кстати, в этом году был 40-летний юбилей!
Актор – это универсальная абстракция вычислительной сущности, которая в ответ на получаемое сообщение
Разница между ними такая же, как между телефоном и отправлением сообщения по почте. В первом случае (это вычисления на основе глобального времени), если несколько человек пытаются дозвониться на один телефонный номер, то они начинают конкурировать за доступ к общему разделяемому ресурсу – адресату. Офисные АТС, многоканальные телефоны, программное обеспечение call-центров – все это (а-ля примитивы синхронизации) нужно для того, чтобы обеспечить эффективную обработку входящих звонков. В случае же с почтовым отправлением отправитель письма (актор) просто посылает письмо адресату без каких-либо задержек из-за необходимости согласовывать свои действия с другими отправителями. Но! При этом нам неизвестно, когда получатель прочтет наше письмо.
Два крупнейших специалиста – это Carl Hewitt и его ученик Gul A. Agha [14]. Hewitt, в основном, занимается строгим обоснованием того, что другие подходы к вычислимости являются частным случаем акторной модели, а Agha – различными приложениями для распределенных систем.
Каждый из результатов сам по себе — это тема для отдельной большой статьи, поэтому приведу лишь резюме и ссылку на первоисточник.
События в модели акторов образуют частично упорядоченное множество. Аксиоматика этого множества под названием Ordering Laws была описана Carl Hewitt и Henry Baker [15] в статье Actors and Continous Functionals [16] (1977 г). Аксиом довольно много, а смысл их в том, чтобы обосновать, что модель акторов годится для использования на практике, например, что в любой момент времени количество сообщений, адресованных одному получателю, конечно.
В 1985 году Gul A. Agha под руководством Hewitt защитил диссертацию Actors: A Model Of Concurrent Computations in Distributed Systems [17]. В диссертации Agha описывается синтаксис минимального языка программирования, поддерживающего акторы, а также набор типовых задач и приемов их решения (глава 4).
Еще одним важным вкладом в практические подходы к реализации модели акторов можно считать статью Phillip Haller и Martin Odersky [18] Event-Based Programming with Inversion Control [19]. В ней был предложен новый подход для разработки акторных систем, который лег в основу Scala. Суть его в том, что получаемая параллельная программа по записи очень похожа на «обычное последовательное» программирование.
Такой же путь, например, был выбран для развития C#. Вот так выглядят акторы на C# 5:
static async Task SavePage(string file, string a)
{
using (var stream = File.AppendText(file))
{
var html = await new WebClient().DownloadStringTaskAsync(a);
await stream.WriteAsync(html);
}
}
Здесь ключевое слово await подразумевает асинхронный вызов соответствующего метода и возврат к вычислениям, когда будет получен ответ.
В 2011 году Gul A. Aga и Karmani подытожили многолетний опыт реализации акторных систем, описав наиболее распространенный способ реализации. Они назвали его Fog Cutter [20]. Правда, Hewitt неоднократно критиковал такую архитектуру, например, здесь [20] и здесь [21] Суть критики сводится к тому, что это всего лишь одна из возможных реализаций, но никак не общий подход.
В 1988 году Роберт Ковальски [22], создатель языка Пролог, выдвинул тезис, что «вычисления могу быть сгруппированы по логическим выводам». Это справедливо для последовательных вычислений и для некоторых моделей параллельных. Но в 1988 году Hewitt и Agha опубликовали статью Guarded Horn clause languages: are they deductive and Logical? [23], в которой показали, что для модели акторов это неверно в следующем смысле: текущее состояние программы может дедуктивно не следовать из предыдущего. Что это значит на практике: отладка программы на основе модели акторов не так эффективна, как в случае последовательных программ.
Почему функциональные языки возникают везде, где речь заходит о параллельном программировании? Чтобы ответить на этот вопрос, рассмотрим небольшой пример на C++:
int j = 0;
for(int i =0; ; ++i)
{
++j;
}
Поскольку данный кусок кода не содержит операций ввод-вывода, то, чтобы дать возможность другому потоку выполнить свой код на том же ядре процессора, требуется дождаться, когда планировщик потоков операционной системы принудительно произведет переключение потоков. Это может произойти либо, когда в другом потоке произойдет вызов системной функции, либо по событию от таймера. По умолчанию событие по таймеру срабатывает 1 раз в 15,6 мс [24], здесь [25] объясняется почему не стоит уменьшать это время до 1 мс. Получается, что императивный стиль позволяет написать «жадную» программу, которая пытается единолично использовать все ресурсы процессора, а мы ограничены в средствах на это повлиять.
Перепишем эту программку через хвостовую рекурсию.
void cycle(int i, int& j)
{
++j;
cycle(i+1, j);
}
Да простят меня почитатели функциональных языков программирования за такую вольную интерпретацию, но моя цель – только продемонстрировать идею.
Итерация цикла заменена на вызов функции. Предположим, что в точку вызова каждой функции компилятор встраивает код для подсчета времени, затраченного на выполнение текущего потока и переключение на другой поток в случае необходимости. Теперь у нас появляется возможность переключиться с одного потока на другой в течение наносекунд. Эта же идея позволяет реализовать легковесные потоки, например, как в Erlang.
За эти свойства функциональные языки удобно использовать там, где нужна параллельность и реакция в режиме реального времени.
Если нужен очень быстрый отклик, то функциональные языки вне конкуренции, но что если во время обработки запроса нам приходится обращаться в базу данных, а время отклика от нее составляет порядка 50-100 мс, или приходится выполнять много вычислений, то есть совершать много вызовов функций. Накладные расходы, связанные с подсчетом времени выполнения и переключением потоков на каждый вызов, дают о себе знать, и императивные языки оказываются в этом случае эффективнее. Чтобы убедиться в этом, достаточно посмотреть сайт benchmarksgame.alioth.debian.org [26]. Сайт предлагает измерять производительность программ, написанных на разных языках, сравнивая решения одной и той же задачи. Вот, некоторые примеры сравнений: reverse-complement [27], mandelbrot [28], regex-dna [29], pidigits [30].
Сразу оговорюсь, во-первых, это только тесты – не надо к ним относиться очень серьезно. С другой стороны, любой желающий, недовольный положением своего любимого языка программирования, может предложить собственное решение и поменять расстановку сил. Во-вторых, я привел только те ссылки, где императивные языки выигрывают за явным преимуществом, потому что моя цель – лишь проиллюстрировать идею, высказанную несколько абзацев раньше, о накладных расходах, связанных с организацией параллельности.
Еще одно интересное наблюдение – реализации модели акторов на императивных языках, как правило, в тестах выше функционального Erlang. Опять оговорюсь, все знают, что Erlang хорош не в вычислениях, а именно, там, где нужен мгновенный отклик, но это только лишний раз подтверждает мысль о накладных расходах.
Приведу одну метафору: есть бегуны на длинные дистанции – марафонцы, а есть – на короткие – спринтеры. От одних требуется выносливость при невысокой средней скорости (относительно спринтеров), а от вторых – максимальная производительность в очень короткое время (относительно марафонцев). Увы, но, просто физиологически, невозможно показывать одинаково высокие результаты на спринтерских и марафонских дистанциях, просто потому, что от мышц требуются совершенно разные свойства. Эти бегуны отличаются даже фигурами: марафонцы – сухие и жилистые, а спринтеры – атлетичные и мускулистые.
Еще одним подтверждением, что с параллельным программированием не все так однозначно, является проект по созданию 5-го поколения ЭВМ.
В 80-х годах прошлого века в Японии была предпринята попытка создания компьютеров 5-го поколения [31]. Проект был инициирован опять верой в то, что будущее за параллельными вычислениями, большими базами данных и логической обработкой больших баз данных.
Было затрачено 500 млн. долларов в ценах 80-х. Проект длился 10 лет и завершился полным провалом! Параллельные программы не дали существенных преимуществ в производительности над однопоточными. И на десятилетия пальму первенства захватила компания Intel со своими однопоточными процессорами. И только, когда производители процессоров уперлись в технологические ограничения по наращиванию тактовых частот, идея параллельности вновь начала набирать популярность.
Вопросы производительности затронутые выше, а также то, что Hewitt настойчиво делает акцент на том, что ни потоки, ни очереди сообщений, ни какие-либо известные конструкции не являются частью модели акторов привело к многообразию идей и способов реализации.
Выделяются три направления:
Читаешь статьи 70-х годов: будущее за многопроцессорными системами, искусственным интеллектом; 80-годов: будущее за многопроцессорными системами, большими базами данных, логической обработкой данных, сейчас: будущее за процессорами с большим количеством ядер, большими данными, облачными вычислениями, интеллектуальным анализом данных. Нетрудно провести аналогии между нынешней ситуацией и прогнозами 40-летней давности. Все это напоминает пример из тренингов по мотивации про ослика и морковку. Мы делаем шаг, чтобы приблизиться к заветной цели, а цель от нас отдаляется настолько, насколько мы к ней приблизились, но именно она заставляет нас развиваться и двигаться вперед.
Когда первый раз натолкнулся на модель акторов, а это было относительно недавно. У меня возник вопрос – почему про нее не узнал раньше. Специально посмотрел книжки Таненбаума, Грегори Эндрюса, Паттерны интеграции корпоративных приложений [32] и др. – все они довольно много места отводят концепции обмена сообщениями, но никто из них ни слова не говорит про модель акторов и Hewitt. Зато, когда читаешь Hewitt или Agha – достаточно много времени отводится критике традиционной модели вычислений. Такое ощущение, что акторы просто игнорируют. Так же происходит и в программировании: в мире императивного программирования практически ничего неизвестно о функциональном – его как бы нет, по крайней мере, это было до недавнего времени, а в мире функционального программирования императивное подвергается критике. Может есть у кого мысли, почему так произошло?
Если данная статья вызовет интерес, то планирую написать серию статей, например:
Если будут пожелания по статьям, то рад буду услышать.
Данная статья написана по мотивам одного моего доклада
Про акторы рассказ начинается с пятой минуты.
Автор: etyumentcev
Источник [33]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/news/50883
Ссылки в тексте:
[1] 1: http://habrahabr.ru/post/128772/
[2] 2: http://habrahabr.ru/post/195562/
[3] 3: http://habrahabr.ru/post/125717/
[4] 3: http://habrahabr.ru/post/127696/
[5] 4: http://habrahabr.ru/post/191916/
[6] 5: http://habrahabr.ru/post/50561/
[7] 6: http://habrahabr.ru/post/140368/
[8] 7: http://habrahabr.ru/post/200338/
[9] википедии: http://ru.wikipedia.org/wiki/%D0%9C%D0%BE%D0%B4%D0%B5%D0%BB%D1%8C_%D0%B0%D0%BA%D1%82%D0%BE%D1%80%D0%BE%D0%B2
[10] Грегори Р. Эндрюс: http://www.cs.arizona.edu/~greg/
[11] Основы многопоточного, параллельного и распределенного программирования: http://www.cs.arizona.edu/~greg/mpdbook/
[12] Carl Hewitt: http://carlhewitt.info/
[13] A Universal Modular Actor Formalism For Artificial Intelligent: http://worrydream.com/refs/Hewitt-ActorModel.pdf
[14] Gul A. Agha: http://cs.illinois.edu/directory/profile/agha
[15] Henry Baker: http://en.wikipedia.org/wiki/Henry_Baker_(computer_scientist)
[16] Actors and Continous Functionals: http://publications.csail.mit.edu/lcs/pubs/pdf/MIT-LCS-TR-194.pdf
[17] Actors: A Model Of Concurrent Computations in Distributed Systems: https://dspace.mit.edu/handle/1721.1/6952#files-area
[18] Martin Odersky: http://lampwww.epfl.ch/~odersky/
[19] Event-Based Programming with Inversion Control: http://lampwww.epfl.ch/~odersky/papers/jmlc06.pdf
[20] Fog Cutter: http://arxiv.org/abs/1008.1459
[21] здесь: http://lambda-the-ultimate.org/node/4853
[22] Роберт Ковальски: http://www.doc.ic.ac.uk/~rak/
[23] Guarded Horn clause languages: are they deductive and Logical?: http://www.researchgate.net/publication/220993254_Guarded_Horn_Clause_Languages_Are_They_Deductive_and_Logical
[24] 1 раз в 15,6 мс: http://habrahabr.ru/post/134559/
[25] здесь: http://habrahabr.ru/company/intel/blog/186998/
[26] benchmarksgame.alioth.debian.org: http://benchmarksgame.alioth.debian.org
[27] reverse-complement: http://benchmarksgame.alioth.debian.org/u64q/benchmark.php?test=revcomp&lang=all&data=u64q
[28] mandelbrot: http://benchmarksgame.alioth.debian.org/u64q/benchmark.php?test=mandelbrot&lang=all&data=u64
[29] regex-dna: http://benchmarksgame.alioth.debian.org/u64q/benchmark.php?test=regexdna&lang=all&data=u64q
[30] pidigits: http://benchmarksgame.alioth.debian.org/u64q/benchmark.php?test=pidigits&lang=all&data=u64q
[31] 5-го поколения: http://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BC%D0%BF%D1%8C%D1%8E%D1%82%D0%B5%D1%80%D1%8B_%D0%BF%D1%8F%D1%82%D0%BE%D0%B3%D0%BE_%D0%BF%D0%BE%D0%BA%D0%BE%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F
[32] Паттерны интеграции корпоративных приложений: http://www.enterpriseintegrationpatterns.com/
[33] Источник: http://habrahabr.ru/post/206300/
Нажмите здесь для печати.