Каков ответ Haskell на Node.js?
Я считаю, что сообщество Erlang не завидует Node.js, так как оно изначально не блокирует ввод-вывод и имеет способы легко масштабировать развертывания на более чем один процессор (что даже не встроено в Node.js). Более подробная информация на http://journal.dedasys.com/2010/04/29/erlang-vs-node-js и Node.js или Erlang
Что насчет Хаскелла? Может ли Haskell предоставить некоторые из преимуществ Node.js, а именно чистое решение, позволяющее избежать блокировки ввода-вывода без использования многопоточного программирования?
Есть много вещей, которые привлекательны с Node.js
- События: без манипуляций с потоками, программист предоставляет только обратные вызовы (как в Snap Framework)
- Обратные вызовы гарантированно выполняются в одном потоке: условия гонки невозможны.
- Хороший и простой UNIX-дружественный API. Бонус: отличная поддержка HTTP. DNS также доступен.
- Каждый ввод / вывод по умолчанию является асинхронным. Это позволяет избежать замков. Однако слишком большая загрузка ЦП в обратном вызове повлияет на другие соединения (в этом случае задача должна быть разбита на более мелкие подзадачи и перепланирована).
- Один и тот же язык для клиентской и серверной сторон. (Однако я не вижу в этом особого смысла. JQuery и Node.js разделяют модель программирования событий, но остальное сильно отличается. Я просто не вижу, как совместное использование кода на стороне сервера и на стороне клиента может быть полезным на практике.)
- Все это упаковано в одном продукте.
7 ответов
Итак, посмотрев небольшую презентацию node.js, на которую указал мне @gawi, я могу сказать немного больше о том, как Haskell сравнивается с node.js. В презентации Райан описывает некоторые преимущества Green Threads, но затем продолжает говорить, что не считает недостатком абстракции потоков недостаток. Я бы не согласился с его позицией, особенно в контексте Haskell: я думаю, что абстракции, которые предоставляют потоки, важны для того, чтобы сделать серверный код более легким для понимания и более надежным. Особенно:
использование одного потока на соединение позволяет вам писать код, который выражает связь с одним клиентом, а не писать код, который обрабатывает все клиенты одновременно. Думайте об этом так: сервер, который обрабатывает несколько клиентов с потоками, выглядит почти так же, как сервер, который обрабатывает один клиент; главное отличие в том, что есть
fork
где-то в первом. Если протокол, который вы реализуете, вообще сложен, управление конечным автоматом для нескольких клиентов становится довольно сложным, тогда как потоки позволяют вам просто написать сценарий связи с одним клиентом. Код легче понять, а также легче понять и поддерживать.обратные вызовы в одном потоке ОС - это совместная многозадачность, в отличие от вытесняющей многозадачности, которую вы получаете с потоками. Основным недостатком совместной многозадачности является то, что программист несет ответственность за отсутствие голода. Он теряет модульность: ошибиться в одном месте, и это может испортить всю систему. Это действительно то, о чем вам не нужно беспокоиться, и упреждение - это простое решение. Более того, связь между обратными вызовами невозможна (это может привести к тупику).
параллелизм не сложен в Haskell, потому что большая часть кода является чистой и поэтому поточно-ориентированной по построению. Есть простые примитивы связи. Гораздо сложнее выстрелить себе в ногу с параллелизмом в Haskell, чем на языке с неограниченными побочными эффектами.
Может ли Haskell предоставить некоторые из преимуществ Node.js, а именно чистое решение, позволяющее избежать блокировки ввода-вывода без использования многопоточного программирования?
Да, на самом деле события и потоки объединены в Haskell.
- Вы можете программировать в явных легких потоках (например, миллионы потоков на одном ноутбуке).
- Или же; Вы можете программировать в асинхронном управляемом событиями стиле, основанном на масштабируемом уведомлении о событии.
Потоки на самом деле реализуются в терминах событий и выполняются на нескольких ядрах, с плавной миграцией потоков, с документированной производительностью и приложениями.
Например, для
- массовая одновременная работа оркестровки
- масштабирование одновременных коллекций на 32 или 48 ядер
- поддержка инструментов для профилирования и отладки многопоточных / много событийных программ.
- высокопроизводительные управляемые событиями веб-серверы.
- интересные пользователи: например, высокочастотная торговля.
Параллельные коллекции nbody на 32 ядра
В Haskell у вас есть как события, так и потоки, а так же все события под капотом.
Прочитайте статью с описанием реализации.
Во-первых, я не считаю, что node.js делает правильные вещи, выставляя все эти обратные вызовы. Вы заканчиваете тем, что пишете свою программу в CPS (стиль передачи продолжения), и я думаю, что преобразование должно быть задачей компилятора.
События: без манипуляций с потоками, программист предоставляет только обратные вызовы (как в Snap Framework)
Таким образом, имея это в виду, вы можете писать с использованием асинхронного стиля, если вы того пожелаете, но при этом вы упустите запись в эффективном синхронном стиле с одним потоком на запрос. Haskell невероятно эффективен в синхронном коде, особенно по сравнению с другими языками. Это все события внизу.
Обратные вызовы гарантированно выполняются в одном потоке: условия гонки невозможны.
Вы можете по-прежнему иметь состояние гонки в node.js, но это сложнее.
Каждый запрос находится в отдельной теме. Когда вы пишете код, который должен взаимодействовать с другими потоками, очень просто сделать его потокобезопасным благодаря примитивам haskell для параллелизма.
Хороший и простой UNIX-дружественный API. Бонус: отличная поддержка HTTP. DNS также доступен.
Взгляните на hackage и убедитесь сами.
Каждый ввод / вывод по умолчанию является асинхронным (хотя иногда это может раздражать). Это позволяет избежать замков. Однако слишком большая загрузка ЦП в обратном вызове повлияет на другие соединения (в этом случае задача должна быть разбита на более мелкие подзадачи и перепланирована).
У вас нет таких проблем, GHC будет распределять вашу работу среди реальных потоков ОС.
Один и тот же язык для клиентской и серверной сторон. (Однако я не вижу в этом особого смысла. JQuery и Node.js разделяют модель программирования событий, но остальное сильно отличается. Я просто не вижу, как совместное использование кода на стороне сервера и на стороне клиента может быть полезным на практике.)
Хаскелл не может выиграть здесь... верно? Подумайте еще раз, http://www.haskell.org/haskellwiki/Haskell_in_web_browser.
Все это упаковано в одном продукте.
Скачать GHC, запустить клику. Есть пакет для каждой потребности.
Я лично вижу Node.js и программирование с обратными вызовами как излишне низкоуровневую и немного неестественную вещь. Зачем программировать с обратными вызовами, если хорошая среда выполнения, такая как в GHC, может обрабатывать обратные вызовы для вас и делать это довольно эффективно?
Между тем, время выполнения GHC значительно улучшилось: теперь в нем есть "новый новый менеджер ввода-вывода" под названием MIO, где "M" означает многоядерный, как я полагаю. Он основан на существующем диспетчере ввода-вывода, и его основная цель - устранить причину снижения производительности ядра на 4+. Показатели производительности, представленные в этой статье, впечатляют. Увидеть себя:
Благодаря реалистичным HTTP-серверам Mio в Haskell можно масштабировать до 20 процессорных ядер, достигая пиковой производительности в 6,5 раз по сравнению с теми же серверами, использующими предыдущие версии GHC. Задержка серверов Haskell также улучшена: [...] при умеренной нагрузке уменьшает ожидаемое время отклика в 5,7 раза по сравнению с предыдущими версиями GHC
А также:
Мы также показываем, что с Mio McNettle (контроллер SDN, написанный на Haskell) может эффективно масштабироваться до 40+ ядер, достигать полной производительности более 20 миллионов новых запросов в секунду на одной машине и, следовательно, стать самым быстрым из всех существующих контроллеров SDN,
Mio сделал это в выпуске GHC 7.8.1. Я лично рассматриваю это как важный шаг вперед в исполнении Haskell. Было бы очень интересно сравнить производительность существующих веб-приложений, скомпилированных с предыдущей версией GHC и 7.8.1.
ИМХО события хорошие, а программирование с помощью обратных вызовов - нет.
Большинство проблем, которые делают специальное кодирование и отладку веб-приложений, связано с тем, что делает их масштабируемыми и гибкими. Самое главное, природа HTTP без сохранения состояния. Это улучшает навигацию, но навязывает инверсию управления, когда элемент IO (в данном случае веб-сервер) вызывает разные обработчики в коде приложения. Эта модель событий, или, точнее сказать, модель обратного вызова, является кошмаром, поскольку обратные вызовы не разделяют переменные области действия, и интуитивное представление о навигации теряется. Среди прочих проблем очень трудно предотвратить все возможные изменения состояния, когда пользователь перемещается назад и вперед.
Можно сказать, что проблемы аналогичны программированию в графическом интерфейсе, где модель событий работает нормально, но в графическом интерфейсе нет навигации и кнопки возврата. Это умножает возможные переходы между состояниями в веб-приложениях. Результатом попытки решить эту проблему являются тяжелые фреймворки со сложными конфигурациями, множество распространенных магических идентификаторов, не ставящих под сомнение корень проблемы: модель обратного вызова и присущее ей отсутствие совместного использования переменных областей, а также отсутствие последовательности, поэтому последовательность должна быть построены путем связывания идентификаторов.
Существуют последовательные структуры, такие как ocsigen (ocaml) seaside (smalltalk) WASH (прекращено, Haskell) и mflow (Haskell), которые решают проблему управления состоянием, сохраняя при этом удобство навигации и полноту REST. в этих рамках программист может выразить навигацию как обязательную последовательность, когда программа отправляет страницы и ожидает ответов в одном потоке, переменные находятся в области видимости, а кнопка "назад" работает автоматически. По своей сути это приводит к созданию более короткого, более безопасного и более удобочитаемого кода, где навигация четко видна программисту. (честное предупреждение: я разработчик mflow)
Вопрос довольно нелепый, потому что 1) Хаскелл уже решил эту проблему намного лучше и 2) примерно так же, как Эрланг. Вот эталонный тест по отношению к узлу: http://www.yesodweb.com/blog/2011/03/preliminary-warp-cross-language-benchmarks
Предоставьте Haskell 4 ядра, и он сможет выполнять 100 тыс. (Простых) запросов в секунду в одном приложении. Node не может делать столько же, и не может масштабировать одно приложение по ядрам. И вам не нужно ничего делать, чтобы пожинать это, потому что среда исполнения Haskell не блокируется. Единственный другой (относительно распространенный) язык, который имеет неблокирующий ввод-вывод, встроенный во время выполнения, - это Erlang.
Подобно тому, как nodejs удалил libev, Snap Haskell Web Framework также отбросил libev.