Как стать автором
Обновить

What's all this fuss about Erlang?

Время на прочтение 5 мин
Количество просмотров 5.8K
Автор оригинала: Joe Armstrong
by Joe Armstrong

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

Предположим, что Intel правы, что их проект Keifer выстрелит. Если это случится, то 32-х ядерные процессоры появятся на рынке не позже 2009-2010.

Ничего удивительного здесь нет. Sun уже продает восьмиядерные Niagara с 4-мя «hyperthreads» на каждом ядре, что эквивалентно 32-ум ядрам.

Это разработка, которая осчастливит программистов на Erlang. Они 20 лет ждали этого события, и теперь настало время расплаты.

Хорошие новости для Erlang-программистов:

На N-ядерном процессоре ваша программа будет работать в N раз быстрее.



Верно ли это?

Почти. Пока что еще рановато об этом говорить, но мы оптимистичны (дико оптимистичны — я не видел такого оптимизма за последние 20 лет!).

В некоторых случаях вам придётся немного подправить ваш код — когда я запускал свою программу генерации документации для Erlang на Sun Niagara (эквивалентно 32-х ядерному процессору), то изменения коснулись лишь одной строчки кода (поменял map на pmap — извиняюсь за тех. подробности, pmap это просто «параллельный map»).

Программа (которая генерировала 63 документа из вики-разметки) стала работать в 7 раз быстрее. Не в 32 раза, конечно — но, я уверяю вас, намного быстрее (впоследствии выяснилось, что узким местом был дисковый I/O, а так как он не был распараллелен, то мы уперлись в семикратный прирост :).

В Ericsson, где я работал, и где был разработан Erlang, мы портируем некоторые приложения на 4-х ядерные процессоры — и знаете, что? После легкого допиливания они работают практически в 4 раза быстрее. У нас уже терпения не хватает дождаться 80-ядерных камешков, с которыми Intel играется в лабораториях…

Так почему же наши программы с такой простотой работают быстрее? Всё дело в изменяемом состоянии и параллелизме.

Переменные и параллелизм


C давних времён (диких 20 лет назад) существуют две модели параллелизма:
  • С разделяемой памятью (shared state)
    На обмене сообщениями (message passing)

    С тех пор весь мир пошел по одному пути (разделяемая память), ну а мы выбрали другой.

    Лишь очень малое количество языков пошло по дороге параллелизма на обмене сообщениями — к примеру, Oz и Occam.

    Говоря о параллелизме на обмене сообщениями мы постулируем: «разделяемой памяти нет». Все вычисления производятся в изолированных процессах и единственный способ обменяться информацией — асинхронный обмен сообщениями.

    Так чем же это хорошо?

    Параллелизм с разделяемой памятью построен на принципе «изменяемого состояния» (буквально — память, которую можно менять). Все языки вроде C, Java, C++ и т.п. несут в себе понятие «памяти», которую мы можем менять.

    Это хорошо только в том случае, если у ровно один процесс, производящий изменения.

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

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

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

    Как программисты решают эти проблемы? Они молятся. На одноядерном процессоре их программы могут «просто работать», но на многоядерном — всё падает.

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

    В Erlang нет изменяемых структур данных


    (вообще говоря, есть, но это неважно)

    Нет изменяемых данных → нет блокировок.
    Нет изменяемых данных → легко распараллелить.

    Ну и как же мы осуществляем параллелизацию? Легко — программист разбивает решение проблемы на несколько параллельных процессов.

    Этот стиль программирования называется…

    (барабанная дробь)

    Concurrency Oriented Programming


    Erlang — не про объекты. У него своя метафора.

    Объекты не в моде. Встречайте параллелизм.

    Мир — параллелен. Куча событий вокруг нас происходит одновременно. Я бы не смог вести свой автомобиль по шоссе, не понимая принцип параллельности на интуитивном уровне — чистой параллельности на обмене сообщениями, чем мы и живём.

    Представьте группу людей. У них нет общего состояния.

    У меня своя личная память (в моей голове), а у вас — своя. И она не общая. Мы общаемся передавая сообщения (световые и звуковые волны). Мы обновляем наше внутреннее состояние основываясь на восприятии этих сигналов.

    Это то, чем является параллельное программирование в своей сути.

    Мы словно скрываем изменяемое состояние внутри объекта — то самое свойство, которое делает паралеллизацию до невозможности сложной проблемой.

    И это работает?


    Да. Erlang используется по всему миру в хай-тек проектах, где требуется надежность. Флагманский проект на Erlang, разработанный Ericsson, шведской телефонной компанией — коммутатор AXD301. В нём более двух миллионов строк Erlang'а.

    AXD301 — это девять девяток надежности (да, ваши глаза вас не обманывают — 99.999999999%). Пять девяток считаются хорошим показателем (downtime — 5.2 минуты в год). Семь девяток практически недостижимы… но нам удалось достичь 9-и.

    Благодаря чему? Отсутствие разделяемой памяти плюс продвинутая модель восстановления из крэшей. Вы можете прочитать обо всём этом подробнее в моей диссертации на Ph.D.

    Пользователи Erlang'а?

    • те, кто «в теме»
      стартапы
      Ericsson
      wings, программа для 3D-моделирования
      ejabberd, IM-сервер для jabber/XMPP
      tsung, многопротокольный инструмент для распределенного тестирования нагрузки
      yaws, высокопроизводительный веб-сервер
      тысячи хакеров, мечтающих: «вот бы заюзать это на моей работе в офисе»


      А Erlang сложный?


      Нет. Но он необычный.

      У Erlang нет «похожего на C синтаксиса, чтобы было полегче учить». Он не «объектно-ориентированный», у него нет «переменных» и он — «функциональный язык программирования».

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

      Вам наверняка уже стало интересно, как выглядит код на Erlang. Язык предполагает повсеместное использование синтаксиса pattern matching (сопоставления с образцом) — вот маленький пример кода на Erlang (взято из новой книги о нём):

      -module(geometry).
      -export([area/1]).

      area({rectangle, Width, Ht}) -> Width * Ht;
      area({square, X}) -> X * X;
      area({circle, R}) -> 3.14159 * R * R.



      Скомпилируем его и запустим в оболочке Erlang:

      1> c(geometry).
      {ok,geometry}

      2> geometry:area({rectangle, 10, 5}).
      50

      3> geometry:area({circle, 1.4}).
      6.15752



      В общем-то, несложно… Вот Java-код, делающий то же самое:

      abstract class Shape {
        abstract double area();
      }

      class Circle extends Shape {
         final double radius;
         Circle(double radius) { this.radius = radius; }
         double area() { return Math.PI * radius*radius; }
      }

      class Rectangle extends Shape {
         final double ht;
         final double width;
         Rectangle(double width, double height) {
           this.ht = height;
           this.width = width;
         }
         double area() { return width * ht; }
      }

      class Square extends Shape {
        final double side;
        Square(double side) {
          this.side = side;
        }
        double area() { return side * side; }
      }



      От переводчика: код на Erlang, создающий два процесса ping и pong, пингующие друг друга несколько раз:

      -module(pingpong).

      -export([start/0, ping/2, pong/0]).

      ping(0, Pong_PID) ->
         Pong_PID! finished,
         io:format(«ping finished~n», []);

      ping(N, Pong_PID) ->
         Pong_PID! {ping, self()},
         receive
           pong ->
             io:format(«Ping received pong~n», [])
         end,
         ping(N — 1, Pong_PID).

      pong() ->
         receive
           finished ->
             io:format(«Pong finished~n», []);
           {ping, Ping_PID} ->
             io:format(«Pong received ping~n», []),
             Ping_PID! pong,
             pong()
         end.

      start() ->
         Pong_PID = spawn(pingpong, pong, []),
         spawn(pingpong, ping, [3, Pong_PID]).



      Вывод:

      1> c(pingpong).
      {ok,pingpong}
      2> pingpong:start().
      <0.36.0>
      Pong received ping
      Ping received pong
      Pong received ping
      Ping received pong
      Pong received ping
      Ping received pong
      ping finished
      Pong finished



      Где мне достать Erlang?


      Скачайте его с erlang.org

      Прим. переводчика: куча ссылок из конца оригинальной статьи поскипана
Теги:
Хабы:
+69
Комментарии 271
Комментарии Комментарии 271

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн