Разное

Erlang vs go: Go vs. Erlang comparison | vsChart.com

Содержание

Erlang. Что это, зачем, как и для кого. / Хабр

Статья короткая, если понравиться, попробую осветить этот язык программирования подробнее.

Что

Erlang, это функциональный язык программирования с динамической типизацией, главной особенностью которого является программирование на уровне отдельных процессов(почти аналог threads в других ЯП), коммуникация между которыми реализуется с помощью MPI(Message Passing Interface).

Зачем

Рост частот центральных процессоров остановился. Растет количество ядер и количество узлов в кластерах. Erlang создан для максимального упрощения разработки программ которые могут использовать всю мощь многоядерных и/или много узловых систем.

Успешные примеры использования Erlanga — Jabber(ура!) сервер ejabberd, web-сервер YAWS и многочисленные эксперименты, например comet-программа(специфический стиль программирования веб-сайтов, когда сервер не разрывает подключения с клиентом, а продолжает пересылать ему данные при необходимости) способная держать 1,000,000(миллион) TCP-подключений.

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

Как

Программирование на уровне отдельных, изолированных процессов дает много преимуществ перед обычным стилем программирования параллельного ПО.

Начнем из истоков — почему трудно программировать такое ПО на жаве, си или шарпе? Проблема очень глобальная и существует во всех этих языках — общий доступ к памяти. Программируя на этих ЯП вы не можете быть уверены, что вместимое памяти, куда ссылаются ваши переменные не изменил какой-то другой поток(трэд, thread, нить…). Из-за этого, очень часто приходиться прибегать к разнообразным, проверенным временем, трюкам — локи, мутексы, семафоры. И делать это трудно. Трудно не только начинающему программисту, но и программисту с опытом, от которого зависит работоспособность системы, если над ней работают еще несколько опытных/неопытных программистов.

Вообще говоря, в Erlang’е ета проблема не решена. Она просто изолирована на уровень ниже самого языка. Каждый процесс изолирован и не имеет доступа к памяти других процессов.

В двух словах: если нет общей памяти то нет проблемы с доступом к этой памяти.

Для кого

Erlang очень прост в изучении, в синтаксисе можно разобраться в течении дня-двух, в принципах программирования — неделя-другая. Но вот парадигма программирования достаточно сложна и переключиться на нее(особенно если есть огромный опыт в императивных ЯП) достаточно сложно и иногда совсем не хочется. Не раз слышал — как можно программировать не объектами? Весь мир состоит из объектов и взаимодействия между объектами! Ответ банальный — весь мир состоит из процессов и взаимодействия между процессами в той же мере в какой он состоит из объектов.

С использованием этого ЯП многие задачи решаються тривиально, и я считаю, что Erlang — лучший ЯП, с которого стоит начинать знакомство с функциональными языками. Особенно если программы должны быть параллелизированы и кластеризированы.

Редкие языки: Erlang | GeekBrains

Что за зверь и зачем нужен.

https://d2xzmw6cctk25h.cloudfront.net/post/546/og_cover_image/2f34e56fe22de605ca910a0203739a28

Здравствуйте!

Языки программирования бывают известные и не очень. Про известные вы слышали — Java, C/C++, Python, Ruby и еще полдесятка названий у всех на слуху. Сегодня речь не о них.

Поговорим о языке, который редко используют в прикладной разработке. Этот язык — Erlang.

Зачем нужен

Erlang — очень узкоспециализированный язык. На нем сложно писать утилиты, вебсайты или прикладные программы. Он создавался для написания ПО для телекоммуникационных коммутаторов в 1980-х годах.

Сегодня он используется для написания серверов видеостриминга и чатов. Erlang идеален там, где нет сложных вычислений или работы с большими объемами данных, но есть много клиентов и жесткие требования к отказоустойчивости.

Я не буду рассказывать о синтаксисе и основах, зато расскажу о трех фишках, которые восхищают меня больше всего.

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

Модель памяти

Чтобы понять, чем прекрасна организация памяти в Erlang, придется разобраться с тем, как вообще организована работа с памятью из разных потоков в современных языках.

Три основных подхода к организации памяти:

Блокировки

Самый простой для машины и сложный для программиста подход. Вот как это работает: есть два потока и одна общая переменная. Когда поток хочет записать в переменную значение, он блокирует к ней доступ, чтобы другие потоки не помешали ему. Другие потоки в это время вынуждены ждать.

STM

Программная транзакционная память(STM) — альтернатива блокировкам. Суть метода в организации транзакционного доступа к памяти: все потоки пишут и читают без блокировки, но при этом все изменения памяти пишутся в лог. В конце транзакция фиксируется, и все изменения применяются. В случае конфликтов изменения откатываются.

Изоляция

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

Erlang использует модель изоляции с одним исключением, о котором я расскажу ниже. Возникает вопрос — как тогда потоки обмениваются данными? Для этого предусмотрен механизм сообщений и почтовых ящиков. У каждого потока есть ящик, в который можно отправить сообщение. Поток в любой момент может получить из ящика все или определенные сообщения и обработать их.

Легкие потоки

Многопоточное программирование всегда было непростым. В C или Java каждый поток — почти отдельная программа: ему нужно выделить память, запустить, следить и корректно завершить. При этом потоки нельзя назвать легкими: так, по умолчанию один поток в Java съест от трехсот килобайт памяти только при запуске.

Erlang решил эту проблему: один поток запускается практически мгновенно и для своей работы берет всего четыре килобайта памяти. При этом потоки можно запускать сотнями тысяч на обычном ноутбуке.

Все дело в том, что потоки Erlang управляются не операционной системой, а самим Erlang, его виртуальной машиной (BEAM). Для операционной системы все выглядит так, будто поток один, но внутри BEAM их может быть много. За счет того, что операционная система не вмешивается в управление потоками, скорость работы с ними получается очень высокой.

Приятный бонус этого — Erlang способен равномерно занимать все процессорные ядра. Вот как выглядит приложение Erlang на продакшн-сервере — все ядра заняты делом:

Erlang не прожорлив до памяти еще из-за того, что когда поток завершается, вся его память быстро очищается и возвращается системе.

Не стреляет в ногу

Erlang прекрасен еще тем, что в нем сложно фатально выстрелить себе в ногу. Можно написать неэффективный, некрасивый и сложный в поддержке код, но если он заработает — он будет работать. Есть исключения, и про самое большое я сейчас расскажу. Это связано с памятью.

Как я писал выше, память в Erlang изолированная, но есть одно исключение: бинарные данные. У каждого потока своя куча памяти, но бинарные данные размером больше 1024 байт сохраняются не в память потока, а в общую память для бинарных данных, а в памяти потока хранится ссылка на бинарник.

Все хорошо, пока ссылка на бинарные данные не отправляется другому процессу. Если бы ссылка не ушла другому процессу, Erlang при завершении потока удалил бы ненужные больше бинарные данные из общей кучи и все. А теперь он не может этого сделать, потому что другой поток тоже ссылается на эти же данные.

Возникает утечка памяти. Это и есть самая большая проблема, которая может случиться из-за программиста. Остальные грабли мельче и на них нужно наступить специально.

Минусы

Кроме узкой сферы применения, у Erlang есть еще недостатки. Вот они:

  • Мало библиотек и инструментов
  • Небольшое сообщество
  • Сложная идеология разработки
  • Язык медленно развивается

Почитать

Блоги
http://lionet.livejournal.com/tag/erlang
http://levgem.livejournal.com/tag/erlang
http://eax.me/tag/erlang

Книги
http://learnyousomeerlang.com

Мартин Логан, «Erlang and OTP in action»

Фред Геберт, «Erlang in anger», PDF — о темных сторонах Erlang

Джо Армстронг, «Programming Erlang» — книга от одного из разработчиков языка

 

К вам два вопроса. Первый — что думаете о Erlang?

Второй — нравится новая тема? Хотите еще о редких языках? Предложения в комментарии. 


Нередкие языки: профессия «Веб-разработчик».

знакомимся с языком программирования Go — «Хакер»

Содержание статьи

Мы привыкли думать, что по-настоящему универсальных языков программирования не существует. Когда нам нужна эффективность — мы пишем на Си и миримся с его ограничениями. Когда нужна скорость разработки — кодим на Python и ожидаем получить медленный код. Erlang позволяет создавать высокораспараллеленные распределенные приложения, но его очень трудно вписать в существующие проекты. Язык Go полностью ломает такую систему мышления, сочетая в себе преимущества многих языков и освобождая программистов от их недостатков.

Когда десять лет назад Кена Томпсона, принимавшего активное участие в разработке языка Си, спросили, каким бы он сделал этот язык на тот момент, он ответил, что язык был бы похож на Limbo. Прошло немало времени, и Томпсон совместно с еще одним автором языка Си, Робом Пайком, принял участие в создании Go — языка, который стал переосмыслением и последующим развитием Limbo. Go был представлен миру 10 ноября 2009 года и практически сразу стал бестселлером. Одни только имена авторов, известных как создатели операционной системы UNIX, языка программирования Си и кодировки UTF-8, а также покровительство Google, в лабораториях которых был создан язык, дали Go отличный старт. Однако даже это не позволило бы языку долго продержаться на плаву, если бы он не смог предложить программистам что-то действительно новое — что-то, что упростило бы их жизнь и сделало Go по-настоящему незаменимым. И это «что-то» в языке было. В большом количестве.

 

Си сегодняшнего дня

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

В основу Go положено три фундаментальных идеи:

  1. Гарантия высокой скорости компиляции и производительности приложений.
  2. Простота разработки и поддержки приложений, свойственная высокоуровневым скриптовым языкам.
  3. Встроенные средства параллельного программирования, позволяющие задействовать все имеющиеся ядра современных процессоров.

Что все это значит на деле? Разберемся с каждым из пунктов.

 

Производительность

Даже очень простая референсная реализация компилятора с языка Go способна за какие-то доли секунды сгенерировать на удивление быстрый код, скорость исполнения которого будет сопоставима со скоростью исполнения кода, написанного на таких языках, как Си и C++. При этом, в отличие от своих предков, компилятор Go гарантирует проверку типов, а результирующий код получает встроенный сборщик мусора и собственный механизм распараллеливания.

С самого начала язык проектировался таким образом, чтобы быть легко понятным и простым в «переваривании» не только человеку, но и машине. Многие  синтаксические и архитектурные элементы Go были задуманы если и не с главной целью, то, по крайней мере, с оглядкой на возможность их простого разбора программой, будь то компилятор, дебаггер или даже среда разработки. Язык получился очень прямолинейным и недопускающим неочевидностей и спорных мест, которые могли бы привести компилятор в замешательство (язык C++ — яркий пример такого неочевидного синтаксиса и общей механики, которые заставляют головы программистов трещать, а компилятор — медленно буксовать на месте).

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

В стандартную поставку Go входят плагины для всех популярных сред программирования, в том числе Vim

 

Простота разработки и сопровождения

Go — системный язык, что, тем не менее, не мешает ему быть достаточно высокоуровневым для того, чтобы обеспечить программиста всем необходимым для комфортного и быстрого написания кода. Язык включает в себя такие высокоуровневые конструкции, как ассоциативные массивы и строки (которые можно сравнивать, копировать, вычислять длину, делать срезы). Он имеет средства для создания собственных типов данных (подобных классам в других языках), средства создания потоков и обмена данными между ними, и, конечно же, он лишен указателей, способных ссылаться на любой участок памяти (срыв стека в программе, написанной на Go, невозможен в принципе). Однако главное, что дает Go программисту, это та самая прямолинейность и очевидность синтаксиса, о которой мы говорили в предыдущем разделе. В этом смысле Go очень похож на языки Pascal, Modula и Oberon: практически любой синтаксический элемент языка следует общей логике и может быть явно и безошибочно интерпретирован вне зависимости от его положения в коде. Например, совершить знаменитую ошибку объявления переменных, описанную во всех гайдах по стилистике оформления кода на языке Си, в Go просто невозможно:

int* a, b; // В Си и C++ переменная "a" будет указателем, но "b" — нет
var a, b *int; // В Go обе переменные будут указателями

Go — язык, созданный программистами и для программистов. Это проявляется во всем, начиная от обрамления блоков кода в стиле Си, неявного объявления типов, отсутствии необходимости ставить точку с запятой после каждого выражения и заканчивая такими архитектурными решениями, как отсутствие механизма исключений и полноценных классов (они были созданы для упрощения жизни, но вместо этого приводят к запутыванию кода). Основная идея языка в том, чтобы быть инструментом, который позволяет писать программы, вместо того, чтобы думать о том, заработают ли они вообще (эта черта свойственна Си и, в еще большей степени, C++).

 

Средства параллельного программирования

Встроенные средства параллельного программирования — это самая сильная черта Go, и здесь среди языков общего назначения ему просто нет равных (за исключением разве что Limbo, но он привязан к ОС Inferno). И выигрыш здесь не столько в том, что эти средства встроены в сам язык, сколько в том, что они реализуют очень простую и эффективную модель, полностью следующую теории взаимодействующих последовательных процессов (CSP). Читатели, знакомые с Occam и Limbo, должны хорошо понимать все преимущества CSP, а для остальных поясню. Вместо того, чтобы городить огород из потоков, блокировок, мьютексов и прочих систем синхронизации, которые делают параллельное программирование невыносимой мукой и приводят к изданию многостраничных книг о том, как писать многопоточные приложения, автор CSP Тони Хоар предлагает простое и элегантное решение: позволить приложению в любой момент создать новую нить, которая сможет общаться с родителем и другими нитями с помощью отправки синхронных сообщений.

В Go эта идея выглядит так:

  1. Создание переменной-канала.
  2. Определение функции, которая принимает переменную-канал в качестве аргумента, а в своем теле содержит код, который должен быть выполнен в отдельной нити. В конце функция должна отправить результат своего выполнения в канал (это делается с помощью специального оператора).
  3. Запуск функции в отдельном потоке с помощью ключевого слова «go».
  4. Чтение из канала.

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

 

Пример

Один из моих любимых примеров, демонстрирующих мощь языка Go, — это реализация таймера, который выполняется в отдельном потоке и «стучит» основному потоку через определенные интервалы времени, в течение которых уходит в сон. Код этой программы, написанный на одном из «классических» языков программирования, выглядел бы громоздким и запутанным, но Go позволяет сделать его простым и красивым.

Код нашей программы:

1     package main
2
3     import "time"
4     import "fmt"
5
6     func timer(ch chan string, ns, count int) {
7         for j := 1; j <= count; j++ {
8             time.Sleep(int64(ns))
9             if j == count {
10                fmt.Printf("[timer] Отправляю последнее сообщение...n")
11                ch <- "стоп!"
12            } else {
13                fmt.Printf("[timer] Отправляю...n")
14                ch <- "продолжаем"
15            }
16            fmt.Printf("[timer] Отправил!n")
17        }
18     }
19
20     func main() {
21         var str string
22
23         ch := make(chan string)
24         go timer(ch, 1000000000, 10)
25
26         for {
27             fmt.Printf("[main] Принимаю...n")
28             str = <-ch
29             if str == "стоп!" {
30                 fmt.Printf("[main] Принял последнее сообщение, завершаю работу.n")
31                 return
32             } else {
33                 fmt.Printf("[main] Принято!n")
34             }
35         }
36     }

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

Результат работы программы после пяти итераций цикла

На первый взгляд листинг очень напоминает код программы, написанной на языке Си, C++ или даже Java, но при более детальном изучении становятся видны различия — Go унаследовал от Си только базовый синтаксис, в то время как большинство ключевых слов и лексика изменились. Исходный код начинается с ключевого слова package, следом за которым идет имя пакета, к которому этот код относится. Все запускаемые пользователем программы должны иметь имя main, тогда как библиотеки могут иметь произвольное имя, которое будет использовано для доступа к ее функциям и переменным после импортирования. При этом для пометки, должна ли функция или переменная быть экспортируемой, используется верхний регистр: все объекты, имена которых начинаются с большой буквы, будут экспортированы, остальные останутся приватными.

В строках 3 и 4 происходит импортирование пакетов time и fmt, функции которых понадобятся нам позже. Импортирование пакетов во многом очень похоже на включение в программу заголовочных файлов, как это делается в Си и C++, с тем исключением, что Go, во-первых, следит за пространством имен и все импортированные функции, переменные и типы данных будут иметь префикс в виде имени пакета, а во-вторых, не требует наличия самих заголовочных файлов. Никакой возни с хидерами и пространством имен!

Со строки 6 начинается описание функции timer() нашего главного действующего лица. В последующем коде она будет отправлена в отдельный поток, и большую часть времени проведет во сне, а просыпаясь, будет отчитываться головному потоку. Чтобы сделать это, ей нужен доступ к каналу, поэтому первый аргумент функции — это ch типа «канал для передачи строк». Также ей нужно знать временной отрезок, в течение которого она может спать, и то, сколько раз она сможет это сделать. Поэтому второй и третий аргументы — это ns и count типа int. Обрати внимание на форму описания аргументов. В отличие от Си, в Go сначала идет имя переменной, и лишь после — ее тип (что логично и согласуется с системой мышления человека: «переменная такая-то такого-то типа»). Тип возвращаемого функцией значения в Go следует помещать в конец, сразу за закрывающей скобкой (что, кстати, тоже логично). При этом, если функция должна возвращать несколько значений (в Go это возможно), их типы и (опционально) имена должны быть перечислены через запятую и обрамлены скобками. У нашей функции возвращаемого значения нет — уйдя в отдельный поток, она так или иначе ничего вернуть не сможет. Функция должна повторить процедуру «сон — отчет» указанное в переменной count число раз, поэтому в строке 7 начинается цикл for, запись которого полностью аналогична своему собрату в языке Си, за исключением отсутствия скобок.

Чтобы отправить поток timer в сон мы используем функцию Sleep (строка 8) из ранее импортированного пакета time. Ее аргумент, задающий длительность сна, должен иметь тип int64 (аналогичный типу long в Си), поэтому мы должны использовать приведение типов, компилятор не сделает это за нас (и правильно, мы умнее). Чтобы головной поток знал, когда поток timer завершится, и смог обработать эту ситуацию, timer должен предупредить его. Поэтому в строках с 9 по 15 происходит проверка на достижение максимального числа повторений сна. Для этого используется стандартный оператор if, который со времен Си остался неизменным, но так же, как и for, потерял скобки. Если это последнее повторение, на экран выводится «Отправляю последнее сообщение…», а в канал поступает сообщение «Стоп!», в противном случае на экране появится «Отправляю сообщения…», а в канал пойдет «Продолжаем». Каналы в Go типизированы, поэтому в канал ch, объявленный с типом chan string, можно отправить только строку (проверка типов в Go осуществляется во время компиляции, поэтому ошибки легко отловить). В строке 16 поток подтверждает отправку сообщения с помощью печати строки «Отправил!» на экран.

Как и в Си, в Go индикатором начала программы является функция main (строки с 20 по 36), в рамках которой будет выполняться основной поток. Все, что должна сделать наша функция main, это создать новый канал, передать его функции timer, отправить его в отдельный поток и ждать результатов.

Чтобы получать сообщения из канала, понадобится переменная-приемник. Эту роль будет выполнять переменная str типа string, объявленная в начале функции с помощью ключевого слова var (ее значением автоматически станет nil, что эквивалентно NULL в Си). Для создания канала используется встроенная функция make() (строка 23), которая просто выделяет память под указанный тип данных и инициализирует его нулевым значением. Кроме каналов с помощью make можно создавать ассоциативные массивы и срезы, для выделения памяти используется new(). Мы не можем просто объявить переменную типа chan string и работать с ней, потому что буфер, используемый для хранения передаваемых по каналу данных, не будет выделен. Также обрати внимание на неявное объявление переменной ch, которое происходит с помощью оператора «:=» (типизация при этом сохраняется, переменная будет иметь тип присваиваемого значения). В строке 24 timer наконец-то отправляется в отдельный поток. Причем делается это с помощью одного-единственного ключевого слова — go.

Теперь, когда timer был отправлен выполнять свое задание, головному потоку остается только ждать сообщений. Для приема сообщений из потока в Go используется уже описанный ранее оператор «<-«, который теперь следует направить «из потока в принимающую переменную»:

str = <-ch

Но если бы мы добавили в код только одну эту строку, то головной поток продолжил бы работать после получения первого сообщения и в конце концов завершился, не обработав остальные сообщения. Поэтому нам нужен бесконечный цикл. Он занимает строки с 26 по 35. Go не имеет в своем составе «настоящего» while, поэтому, если требуется создать условный оператор цикла, то следует просто поместить условие после ключевого слова for и не париться (или вообще ничего не указывать, как это сделал я).

При каждой итерации цикла в переменную str будет записываться сообщение, пришедшее от потока timer, и, в зависимости от содержимого сообщения, будет выбираться тот или иной вариант дальнейших действий. Обрати внимание, язык позволяет спокойно сравнивать строки без всяких подсобных функций. Кроме того, ты можешь получать их срезы и копии (на манер python или ruby) и вычислять длину с помощью ключевого слова len (все это справедливо и в отношении массивов).

Для запуска программы потребуется компилятор, который можно скачать с официального сайта языка (правда пока доступны только версии для UNIX, Plan9 и MacOS X). Если ставить его не хочется (или у тебя Windows), программу можно запустить, используя специальную форму на сайте golang.org (правда, из-за ограничения на длительность работы программы продолжительность сна потока timer придется сильно сократить). Это все.

 

Постойте, но ведь это не многопоточность?

Да, ты наверняка заметил, что из-за блокировок каналов даже на многоядерном процессоре одновременно активным будет только один поток нашей программы, тогда как другой будет ждать отправку/прием сообщения. Это действительно так, и для решения этой проблемы Go располагает рядом средств.

  1. Каналы можно проверять на наличие сообщений. Если строку «str = <-ch» заменить на «str, ok = <-ch«, то головной поток не будет заблокирован, даже если в канале нет сообщения. Вместо этого в переменную ok будет записано значение false и работа потока продолжится. Естественно, дальше можно поместить проверку на «ok == false» и успеть выполнить какую-то полезную работу, а затем начать новую итерацию цикла и вновь попробовать получить значение. Кстати, таким же образом можно выполнить проверку в потокеотправителе:

ok := ch <- "Продолжаем"

  1. Каналы в Go могут быть буферизированы, то есть уметь накапливать определенное количество сообщений до того, как отсылающая сторона будет заблокирована. Для этого достаточно всего лишь добавить один дополнительный аргумент в вызов функции make:

ch := make(chan string, 10) // создать канал с буфером в 10 позиций

Замечу, однако, что в нашей программе это не даст результата. Во время сна поток timer не сможет заполнить канал сообщениями единомоментно, так как после каждого его засыпания управление все равно будет переходить головному потоку.

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

select {
case str = <-ch2:
// обрабатываем сообщение от первого потока
case str = <-ch3:
// обрабатываем сообщение от второго потока
case str = <-ch4:
// обрабатываем сообщение от третьего потока
}

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

Оператор select очень широко используется в Go-программировании, именно с его помощью принято создавать «диспетчеры сообщений», которые разветвляют программу на множество потоков сразу после старта, а затем входят в бесконечный цикл и начинают обработку пришедших от них сообщений. В операционной системе Inferno (все приложения которой написаны на Go-предке Limbo) таким образом реализован многооконный графический интерфейс.

 

Выводы

Go пока еще молодой, но очень перспективный язык, при создании которого был учтен более чем тридцатилетний опыт в области разработки операционных систем и языков программирования (Роб Пайк двадцать лет занимался исследованиями в области многопоточного программирования, в течение которых были созданы языки Squeak, NewSqueak и Limbo). Go производителен, дружелюбен к программистам и красив.

Освоив этот язык, ты сможешь писать программы, которые будут гораздо эффективнее всего того, что написано на традиционных языках программирования.

 

Links

Почему Erlang — язык для очень специфичных задач

Вообще-то, мне не особо интересно начинать очередной срач не тему языков программирования. Но поскольку меня постоянно спрашивают, почему в последнее время я перестал писать на Erlang, мне проще написать заметку, чем раз за разом отвечать на один и тот же вопрос. Дискламер, как обычно — все написанное ниже является мнением одного человека, не претендующее на истину. Вы не обязаны это мнение разделять и должны винить только себя, если данное мнение ранит ваши чувства.

На Erlang я писал почти два года, писал много, писал за деньги. Это очень хороший язык. Erlang используется во многих компаниях, в том числе WhatsApp, Яндексе, Facebook и других. На Erlang написано немало хороших open source проектов — Riak, RabbitMQ, Couchbase, CouchDB, ejabberd. На Erlang написана библиотека riak_core, у которой нет аналогов, если верить @sum3rman, а я ему в таких вопросах доверяю 🙂

Сам язык имеет ряд примечательных особенностей. Это один из немногих функциональных языков, получивших достаточно широкое распространение. Один из немногих, в которых есть горячее обновление кода и возможность открыть REPL в работающем приложении, чтобы разобраться, что прямо сейчас происходит в кишках. Возможно, единственный язык, программы на котором будут обслуживать 10 тысяч TCP-соединений, не требуя от программиста особо приходить в сознание. Модель акторов, реализованная в Erlang, оказалась весьма удачна для написания многопоточных приложений. Не даром ее пытаются (с сомнительным успехом!) слизать в Akka и Cloud Haskell.

Я искренне считаю, что любой бэкенд разработчик должен в обязательном порядке изучить Erlang и написать на нем как минимум простенькую REST-обертку к PosgreSQL. Чтобы просто посмотреть на то, как выглядит правильная модель акторов, безо всяких там футур и блокирующих вызовов.

Erlang, конечно, имеет свои недостатки. Довольно необычный синтаксис. Баги в виртуальной машине. Лично у меня вызывает сильные опасения использование динамической типизации в проектах серьезнее «сходил в базу, сгенерил json». Назвать Dialyzer человеческим решением может только тот, кто никогда не писал на Haskell и Scala. Инклуды и макросы в третьем тысячелетии — по-моему, просто позор. Но, на самом деле, все это мелочи.

Настоящих, совершенно непростительных, проблем у Erlang две.

Во-первых, библиотеки. Ладно, если бы их было просто мало. В конце концов, можно подобрать ту СУБД, для которой есть клиент, а вместо AWS SDK вызывать консольный клиент. Но проблема в том, что с теми библиотеками, которые есть, творится полный бардак. Из недавнего — чем ходить по HTTP неясно, а клиент для MySQL в очередной раз пишется с нуля. Все это в виде кучи форков, автор каждого из которых исправил те баги, на которые натыкался сам. Одной библиотеки, где исправлено все, просто нет. Форки эти зачастую оказываются несовместимы между собой, и раскиданы по всему GitHub. К тому же, Rebar работает с зависимостями из рук вон плохо. Не собирается проект? А ты попробовал почистить каталог debs?

Во-вторых, Erlang медленный. Вроде как кто-то где-то пытается сделать JIT компилятор, но я с трудом представляю, как он будет работать, не сломав при этом гарантии по равномерному распределению процессорного времени между акторами. Воспользоваться в узких местах библиотекой на Си тоже так просто нельзя, так как она может повесить работу всего приложения, а то и вовсе уронить всю виртуальную машину. Эта проблема, как, в общем, и проблема с библиотеками, решается в мире Erlang написанием куска приложения на каком-нибудь другом языке, который будет взаимодействовать с кодом на Erlang по TCP или еще как-то.

В действительности, Erlang хорошо делает одну вещь, и только одну. Берет данные из одного места и кладет в какое-нибудь другое. Как только возникает потребность сделать что-то большее, например, закодировать 100 Мб JSON’а, или смасштабировать картинку, все сразу становится очень плохо. Особенно если с вашим приложением одновременно работает больше сотни человек. Поэтому не верьте, если вам говорят, что Erlang подходит для веба, он не подходит.

Усугубляется проблема еще тем, что многие стартапы пишутся на Erlang, потому что это модно, а не потому что он им подходит. Если WhatsApp построил на Erlang распределенное, отказоустойчивое, высоконагруженное приложение, значит и мы сможем. У нас как раз высокие нагрузки, нужно очень быстро матрички перемножать! Ну а потом, когда уже слишком поздно, никто не станет «плодить зоопарк языков программирования» в проекте. Или переписывать программу, например, на Scala. Некогда, фичей еще целый вагон нужно сделать, и вообще в JVM же stop the world!

Итак, краткий ответ на вопрос «почему я сейчас не пишу на Erlang» — потому что, по моему мнению, этот язык хорошо подходит для довольно узкого класса задач, и прямо сейчас я занимаюсь задачами, которые к этому классу не относятся. Если вам нужно написать что-то, похожее на socks5-прокси или распределенную базу данных, Erlang, вероятно, будет неплохим выбором для решения этой задачи. Если же вам нужен более универсальный инструмент, на котором можно еще и матрички перемножать, писать веб, использовать Amazon SDK, писать GUI, а также под Android, экспортировать отчеты в Excel, или рисовать диаграммки, и при всем при этом хочется ФП, присмотритесь лучше к Scala, ну или Clojure хотя бы.

Метки: Erlang, Функциональное программирование.

Go против Erlang | 5 основных характеристик и сравнения Go и Erlang

Разница между Go и Erlang

В этой статье мы увидим обзор Go против Erlang, языка программирования, который приобрел популярность в мире несколько лет назад. Оба они по-своему уникальны и могут использоваться для нескольких целей. Go был разработан Google в 2007 году. Это интерактивный язык программирования, который следует синтаксису, аналогичному языку программирования C.Erlang был в основном введен для использования в области телекоммуникаций, но в последнее время он стал широко популярен в различных секторах, таких как исследования, ИТ и т. Д. Первоначально он использовался для поддержки систем в Ericson, а позже, в 1998 году, он был представлен в внешний мир как язык программирования с открытым исходным кодом. Многие компании, такие как WhatsApp, Uber, Google, Pinterest, Slack, Medium, используют языки программирования Go и Erlang.

Прямое сравнение React Go и Erlang (Инфографика)

Ниже приведено сравнение пятерки лучших между React Go и Erlang :

Ключевые различия между React Go и Erlang

Давайте обсудим некоторые из основных ключевых различий между Go и Erlang :

  • Язык Go прост и интересен для изучения, программа может содержать миллион строк, в то время как Erlang используется, когда мы хотим разрабатывать масштабируемые приложения, которые можно легко настраивать и обновлять.
  • В Go у нас есть каналы, по которым данные могут передаваться в различные подпрограммы или внутри подпрограмм, тогда как в Erlang он не использует какой-либо средний путь и следует принципу, известному как модель акторов.
  • Для высокой производительности Go предпочтительнее Erlang.
  • Go быстрее и эффективнее Erlang с точки зрения синтаксиса и используется для многих продвинутых сервисов.
  • Для данных в реальном времени и приложений, которые распространяются, Erlang выбран вместо Go.
  • Для отказоустойчивости Erlang предпочтительнее Go.
  • Go как язык программирования имеет статическую типизацию, в то время как Erlang как язык программирования динамически типизируется.
  • Для действий или функций, связанных с арифметикой, Go является предпочтительным языком, чем

.

Erlang против Go против Java против NodeJS

В этой статье мы сравниваем производительность веб-серверов, обычно используемых для реализации серверных приложений (в отличие от веб-серверов, используемых для обслуживания статического контента или в качестве прокси). Мы рассмотрим веб-серверы, реализованные на Erlang, Go, Java (OpenJDK) и JavaScript (NodeJS).

Мы тестируем следующие веб-серверы:

  • Cowboy 1.1.2 с Erlang OTP 22.2
  • Cowboy 2.7 с Erlang OTP 22.2
  • Mochiweb 2.20.0 с Erlang OTP 22.2
  • Go 1.13.5 net / http
  • FastHTTP с Go 1.13.5
  • NodeJS 13.3.0
  • Кластерный NodeJS 13.3.0
  • Netty 4.1.43 с OpenJDK 13.0.1
  • Rapidoid 5.5.5 с OpenJDK 13.0.1

Методология

Для моделирования общего поведения веб-приложения мы разработали следующую синтетическую рабочую нагрузку.

Клиентское устройство открывает соединение и отправляет 100 запросов с интервалом 900 ± 5% миллисекунд между каждым из них.Сервер обрабатывает запрос, засыпая на 100 ± 5% миллисекунд, чтобы имитировать взаимодействие с серверной базой данных, а затем возвращает 1 КБ полезной нагрузки.

Без дополнительных задержек это приводит к тому, что среднее время жизни соединения составляет 100 секунд, а нагрузка на устройство составляет в среднем 1 запрос в секунду и 0,01 соединение в секунду. Эта рабочая нагрузка выражается комбинацией следующего скрипта Stressgrid и «фиктивных» веб-приложений, созданных для каждого веб-сервера.

  0..100 |> Enum.each (fn _ ->
  получить("/")
  задержка (900, 0,05)
конец)
  

Мы тестируем экземпляр m5.2xlarge среднего размера с 8 виртуальными ЦП и 32 ГиБ ОЗУ. Тест структурирован как непрерывное 1-часовое увеличение количества устройств от 0 до 300 тыс. Мы выбрали число 300 тыс. На основе предела пропускной способности 1,25 млн пакетов в секунду, показанного m5.2xlarge в нашем предыдущем тесте.

Ниже приводится расчет, который учитывает сетевые пакеты, сгенерированные HTTP-запросом и ответом (транзакцией), а также установление и закрытие HTTP-соединения:

300 тыс. Транзакций / сек * 4 пакета / передача + 3 тыс. Соединений / сек * 6 пакетов / соединение = 1.218M пакетов / сек

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

Мы используем Ubuntu 18.04.3 с ядром 4.15.0-1054-aws и следующие переопределения sysctld.

  fs.file-max = 1000000
net.core.somaxconn = 1024
  

Рабочая нагрузка использует незашифрованный протокол HTTP и создается 40 генераторами c5.xlarge Stressgrid, размещенными в том же VPC с целевым хостом.

Результаты

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

FastHTTP

Go оказался лидером, достигнув почти 210 тысяч ответов в секунду. Netty на базе Java — не такая уж и далекая секунда с почти 170 тыс. Пик встроенного веб-сервера Go немного превысил 120 КБ, кластера NodeJS — 90 КБ, Cowboy 1 на основе Erlang.х при 80к. В диапазоне 50–60 КБ у нас есть еще один веб-сервер на базе Erlang, Mochiweb, затем Cowboy 2.x и Rapidoid на базе Java. Наконец, некластеризованный NodeJS набрал 25 тыс.

Clustered NodeJS и Rapidoid вылетали из-за нехватки оперативной памяти при перегрузке. Другие серверы при перегрузке сохраняли максимальную производительность, за исключением Mochiweb.

Давайте посмотрим на график задержки ответа 90-го процентиля.

На этом графике намного легче увидеть, как разные веб-серверы реагируют на перегрузку.Примечательно, что все серверы на базе Erlang после перегрузки сохраняли стабильную задержку ответа, а в Cowboy 1.x она составляла около 250 миллисекунд! Серверы Go и Java становились все медленнее. Некластеризованный NodeJS, ограниченный использованием только одного процессора, был самым медленным. Результаты для кластеризованных NodeJS и Rapidioid были неубедительными, поскольку у них закончилась доступная оперативная память.

Функция Stressgrid get имеет тайм-аут по умолчанию 5 секунд. Это означает, что график задержки ответа включает только ответы, полученные менее чем за 5 секунд.Давайте посмотрим на частоту тайм-аутов, чтобы получить полную картину того, что происходит, когда веб-серверы перегружены.

В частности, Java Netty отсутствует на этом графике. Это означает, что Netty поддерживает практически одинаковую задержку для всех запросов при перегрузке! У серверов Erlang совсем другой подход; они обслуживают часть запросов как можно быстрее, а остальным дают тайм-аут. Интересно, что со временем это поведение улучшается с уменьшением таймаутов. Мы наблюдаем подобное поведение для некластеризованного NodeJS.Серверы Go, будучи перегруженными, также начинают отключаться. Поскольку это происходит в конце теста, мы не можем сделать вывод, как это поведение меняется со временем.

Выводы

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

Веб-серверы на базе Go и Java оказались наиболее эффективными.Кластерный NodeJS достаточно эффективен, но при перегрузке у него заканчивается ОЗУ. Веб-серверы Erlang были наименее эффективными, но очень стабильными после перегрузки. Удивительно, но Cowboy 1.x работает значительно лучше, чем Cowboy 2.x, поэтому мы включили оба в этот тест. Мы исследуем и анализируем эту аномалию в специальной статье.

Ограничения на

пакетов в секунду, налагаемые EC2, могут стать проблемой при использовании с эффективными веб-серверами, такими как FastHTTP.

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


Обсуждение на lobste.rs

Обсуждение на Hacker News

.

Go против D против Erlang против C в реальной жизни: перестрелка по реализации брокера MQTT.

На работе мы недавно начали использовать протокол MQTT, который использует модель публикации / подписки. Это просто по-хорошему и хорошо продумано. Мы выбрали реализацию с открытым исходным кодом под названием Mosquitto. Несколько недель назад, возвращаясь с обеденного перерыва, мой коллега Джефф сказал мне, что пишет для брокера MQTT на Go, его новом любимом языке. Мы используем MQTT на работе, я думаю, он искал новый проект для написания на Go и вуаля.В конце концов, он должен хорошо подходить для этого типа приложений, для которых был создан Go. Но высокомерие настигло его, когда он сказал: «И, конечно, это будет очень быстро. Это будет несправедливо даже по отношению к другим языкам ». Я перефразирую, но вот как я это запомнил. Вы можете прочитать отчет Джеффа здесь.

Я совсем не фанат го. Я не был особенно впечатлен, когда впервые прочитал об этом, но, учитывая то, как много я продолжаю слышать об этом в proggit и от самого Джеффа, я решил попробовать несколько месяцев назад, написав в нем структуру генетического алгоритма.Я пришел к выводу, что это понравилось еще меньше. Это просто не для меня. Go — самоуверенный язык, что было бы хорошо, если бы я согласился с мнением его создателей. Мой мозг работает так, что я нахожусь на противоположной стороне почти всех из них. В нем есть кое-что, что мне нравится. Например, отсутствие точек с запятой и круглых скобок. Горутины и каналы — огромная победа. Я могу жить без исключений, хоть и не хочу, а дженерики? Они могут вырвать их из моих холодных мертвых рук.

D, с другой стороны… сейчас мы говорим.Все, что мне нравится в C ++ и не только, без всяких изъянов. Конечно, есть и свои бородавки, но ничего идеального. Так что, как фанат D, а не столько, сколько Go, я воспринял заявление Джеффа как удар в лицо. Я узнал о vibe.d, просмотрев видео о dconf2013, и мне очень понравилась его идея синхронного API поверх асинхронного ввода-вывода. Я был уверен, что могу, по крайней мере, соответствовать производительности реализации Go, если не превзойти ее. Итак, я написал достаточно о реализации брокера MQTT, чтобы иметь возможность запустить тест Jeff’s Go и сравнить производительность.Примерно через 2 дня я достиг версии, которая была быстрее его. Он разработал второй тест, и моя реализация работала плохо, поэтому я вернулся к оптимизации. Примерно в это же время другой коллега захотел принять участие в конкурсе и использовал это как предлог для изучения Erlang, и написал свою собственную реализацию. Несколько раундов оптимизации спустя, и результаты были в, которые я включил ниже. Далее следуют пояснения по методологии.

loadtest (пропускная способность - чем больше, тем лучше)
Соединения: 100 500 750 1k
D + вайб.г: 121,7 +/- 1,5 166,9 +/- 1,5 171,1 +/- 3,3 167,9 +/- 1,3
C (Москитто): 106,1 +/- 0,8 122,4 +/- 0,4 95,2 +/- 1,3 74,7 +/- 0,4
Эрланг: 104,1 +/- 2,2 124,2 +/- 5,9 117,6 +/- 4,6 117,7 +/- 3,2
Вперед: 90,9 +/- 11 100,1 +/- 0,1 99,3 +/- 0,2 98,8 +/- 0,3

pingtest (задержка - чем больше, тем лучше)
параметры: 400p 20w 200p 200w 100p 400w
D + vibe.d: 50,9 +/- 0,3 38,3 +/- 0,2 20,1 +/- 0,1
C (Москитто): 65,4 +/- 4.4 45,2 +/- 0,2 20,0 +/- 0,0
Эрланг: 49,1 +/- 0,8 30,9 +/- 0,3 15,6 +/- 0,1
Перейти: 45,2 +/- 0,2 27,5 +/- 0,1 16,0 +/- 0,1 

Все числа представляют собой тысячи сообщений, получаемых клиентским приложением в секунду. Все измерения проводились на моем ноутбуке, Lenovo W530 под управлением Arch Linux, поэтому все TCP-соединения были на локальном хосте. Каждое число представляет собой среднее значение нескольких измерений, и я использовал стандартное отклонение как оценку систематической ошибки.Все реализации брокера MQTT выполняются в одном системном потоке. Использование нескольких потоков не привело к увеличению производительности для задержки и снижению производительности для пропускной способности.

Mosquitto был скомпилирован с помощью gcc 4.8.2, реализация Go была выполнена с помощью go run, реализация D была скомпилирована с dmd 2.0.64.2, а с версией Erlang я не уверен. Я установил пакет Erlang для Arch Linux и использовал Makefile моего коллеги, не глядя на него.

Два теста: loadtest и pingtest.Первый измеряет пропускную способность, а второй — задержку. В нагрузочном тесте к брокеру устанавливается несколько сотен подключений. Половина из них подписывается на тему, а другая половина публикует эту тему как можно быстрее. Тест заканчивается, когда все подписчики получили определенное количество сообщений, определенное аргументом командной строки. Я варьировал количество подключений, чтобы увидеть, как это повлияет на каждого брокера. Здесь не было соревнований, реализация D была безусловно самой быстрой.Я думаю, что при 100 подключениях работы было недостаточно, поэтому все реализации в конечном итоге ожидали ввода-вывода. За исключением Mosquitto, все они неплохо масштабировались. У меня возникли проблемы с измерением реализации Джеффа из-за ошибки. Он знает об ошибке, но просто не может его исправить. Цифры были взяты из Go 1.1 (номера pingtest — Go 1.2). Когда его реализация работает, Go 1.2 создает двоичный файл, который работает примерно на 10-15% быстрее, чем приведенные выше числа, что может означать эквивалентную производительность реализации Erlang.Я даже думаю, что ошибка чаще появляется в Go 1.2 именно потому, что полученный двоичный файл более производительный.

В пингтесте Джефф попытался написать лучший тест, который измеряет задержку. Двумя основными аргументами командной строки являются количество пар соединений и количество подписчиков с подстановочными знаками. Для каждой пары одно из соединений подписывается на тему запроса, уникальную для этой пары, а партнерское соединение подписывается на тему ответа. Один партнер публикует запрос и ждет, пока другое соединение опубликует ответ.Количество сообщений, отправляемых в секунду, теперь зависит от времени приема-передачи между этими двумя. Кроме того, подписчики с подстановочными знаками получают сообщения с запросом и ответом от первой пары соединений. Число перед «p» — это количество пар соединений, а число перед «w» — количество подключений абонента с подстановочными знаками. Здесь Mosquitto является самым быстрым, но разница в производительности уменьшается с увеличением количества подстановочных знаков, что соответствует реализации D в последнем столбце.Не знаю, почему он самый быстрый. Я думаю, что есть вероятность, что vibe.d может переключиться на «неправильное» волокно, но с моей стороны это чистое предположение.

А как насчет удобочитаемости и простоты написания? Я не умею читать на Erlang, поэтому не могу это комментировать. Несмотря на то, что я предпочитаю D, я думаю, что реализации D и Go одинаково удобочитаемы. Поскольку модульные тесты Erlang находятся в тех же файлах, что и реализация, трудно точно определить, сколько в них строк. Ситуация становится еще хуже, поскольку она реализует большую часть MQTT, реализация D по существу реализует только то, что необходимо для запуска тестов.С учетом этих предостережений (и того факта, что зависимости не учитываются) 3 реализации синхронизируют от 800 до 1000 строк без фильтрации пустых строк и комментариев.

Можно ли их еще оптимизировать? Вероятно. В конце концов, выбор алгоритма и структур данных имеет большее значение, чем язык программирования, поэтому мой личный совет — выбрать язык, который сделает вас продуктивным. Ни один из них не сделал реализации волшебным образом работоспособными; мы все должны были профилировать, анализировать, оптимизировать, пробовать идеи и измерять.Я любил писать это на D, но опять же, я новообращенный. Мне особенно понравилось использовать библиотеку сериализации, которую я написал для нее, Cerealed. Большая часть типичного шаблона для битов в сетевом коде исчезла, и это стало возможным только благодаря отражению D во время компиляции и определяемым пользователем атрибутам.

Источник:

 D: https://github.com/atilaneves/mqtt
C: https://bitbucket.org/oojah/mosquitto/
Перейти: https://github.com/jeffallen/mqtt
Эрланг: https: // bitbucket.org / pvalsecc / erlangmqtt 

Нравится:

Нравится Загрузка …

Связанные

.

знаков препинания — в Erlang, когда я использую; или или .?

Переполнение стека

  1. Около
  2. Продукты

  3. Для команд
  1. Переполнение стека
    Общественные вопросы и ответы

  2. Переполнение стека для команд
    Где разработчики и технологи делятся частными знаниями с коллегами

  3. Вакансии
    Программирование и связанные с ним технические возможности карьерного роста

  4. Талант
    Нанимайте технических специалистов и создавайте свой бренд работодателя

  5. Реклама
    Обратитесь к разработчикам и технологам со всего мира

  6. О компании

.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *