Node.jsに対するHaskellの応答とは何ですか?


217

Erlangコミュニティは、非ブロッキングI / Oをネイティブで実行し、デプロイメントを複数のプロセッサー(Node.jsに組み込まれていないもの)まで簡単に拡張できる方法があるため、Node.jsにうらやましくないと思います。詳細については、http://journal.dedasys.com/2010/04/29/erlang-vs-node-jsおよびNode.jsまたはErlangをご覧ください。

ハスケルはどうですか?HaskellはNode.jsのいくつかの利点、つまりマルチスレッドプログラミングに頼ることなくI / Oのブロックを回避するクリーンなソリューションを提供できますか?


Node.jsには魅力的なものがたくさんあります

  1. イベント:スレッド操作はありません。プログラマーはコールバックのみを提供します(Snapフレームワークの場合と同様)。
  2. コールバックは単一のスレッドで実行されることが保証されており、競合状態は発生しません。
  3. 素晴らしくシンプルなUNIXフレンドリーなAPI。ボーナス:優れたHTTPサポート。DNSも利用できます。
  4. すべてのI / Oはデフォルトで非同期です。これにより、ロックを回避しやすくなります。ただし、コールバックでのCPU処理が多すぎると、他の接続に影響します(この場合、タスクはより小さなサブタスクに分割され、再スケジュールされます)。
  5. クライアント側とサーバー側で同じ言語。(ただし、この例ではあまり価値を見ていません。jQueryとNode.jsはイベントプログラミングモデルを共有していますが、残りは大きく異なります。サーバー側とクライアント側の間でコードを共有する方法がわかりません。実際に役立つ。)
  6. これらすべてが1つの製品にパッケージ化されています。

17
代わりにプログラマにこの質問をするべきだと思います。
Jonas

47
コードを含めなくても、主観的な質問にはなりません。
gawi

20
私はnode.jsについてはあまり知りませんが、質問について私を驚かせました:なぜスレッドの見込みがそれほど不愉快であると思うのですか?スレッドは、マルチプレキシングI / Oのまったく正しいソリューションである必要があります。ここでは、Erlangのプロセスを含め、スレッドという用語を広く使用しています。おそらく、ロックや変更可能な状態について心配していますか?その方法で行う必要はありません。アプリケーションでより適切な場合は、メッセージパッシングまたはトランザクションを使用してください。
Simon Marlow、

9
@gawiプログラミングは非常に簡単に聞こえるとは思いません-プリエンプションなしでは、飢餓と長い待ち時間の可能性に対処する必要があります。基本的にスレッドはWebサーバーの正しい抽象概念です。非同期I / Oとそれに伴うすべての困難に対処する必要はなく、スレッドで実行するだけです。ちなみに、私はHaskellのWebサーバーに関する論文を書いています。これは興味深いかもしれません。haskell.org
Simon Marlow

3
「コールバックは単一のスレッドで実行されることが保証されています。競合状態は起こり得ません。」違う。Node.jsで簡単に競合状態が発生する可能性があります。あるI / Oアクションが別のI / Oアクションより先に完了し、BOOMすると仮定します。何実際に不可能は、レースコンディション、メモリ内の同じバイトへ、すなわち同時非同期アクセスの一つの特定の種類です。
2015

回答:


219

OK、@ gawiが指摘したnode.jsのプレゼンテーションを少し見てきたので、Haskellとnode.jsの比較についてもう少し説明できます。プレゼンテーションでは、ライアンはグリーンスレッドのいくつかの利点について説明しますが、スレッドの抽象化がないことは不利であるとは言いません。特にHaskellのコンテキストでは、彼の立場に同意しません。スレッドが提供する抽象化は、サーバーコードをより簡単に、より堅牢にするために不可欠であると思います。特に:

  • 接続ごとに1つのスレッドを使用すると、すべてのクライアントを同時に処理するコードを記述するのではなく、単一のクライアントとの通信を表すコードを記述できます。このように考えてみてください。複数のクライアントをスレッドで処理するサーバーは、単一のクライアントを処理するサーバーとほとんど同じように見えます。主な違いはfork、前者のどこかにあることです。実装しているプロトコルがまったく複雑な場合、複数のクライアントの状態マシンを同時に管理するのは非常に難しくなりますが、スレッドでは単一のクライアントとの通信をスクリプト化するだけで済みます。コードは、正しく理解しやすく、理解と保守が容易です。

  • 単一のOSスレッドでのコールバックは、スレッドで得られるプリエンプティブマルチタスクとは対照的に、協調的なマルチタスクです。協調型マルチタスクの主な欠点は、飢餓がないことを確認する責任がプログラマーにあることです。モジュール性が失われます。1つの場所でミスをすると、システム全体が台無しになります。これは本当に心配したくないものであり、プリエンプションは簡単な解決策です。さらに、コールバック間の通信は不可能です(デッドロックになります)。

  • Haskellでは並行性は難しくありません。なぜなら、ほとんどのコードは純粋であり、構造上スレッドセーフだからです。シンプルな通信プリミティブがあります。制限のない副作用のある言語よりも、Haskellで同時実行して自分自身を撃つのははるかに困難です。


42
わかりましたので、node.jsが2つの問題の解決策であることがわかります。1-ほとんどの言語では同時実行が困難です。2-OSスレッドの使用は拡張性があります。Node.jsソリューションは、イベントベースの同時実行(w / libev)を使用して、スレッド間の通信を回避し、OSスレッドのスケーラビリティの問題を回避することです。Haskellは純粋さのために問題#1を抱えていません。#2の場合、Haskellには軽量スレッド+イベントマネージャーがあり、GHCで最近大規模コンテキスト向けに最適化されました。また、Javascriptを使用することは、Haskell開発者にとってプラスとは言えません。Snap Frameworkを使用している一部の人々にとって、Node.jsは「ただ悪い」だけです。
gawi

4
リクエスト処理は、ほとんどの場合、相互に依存する一連の操作です。すべてのブロッキング操作にコールバックを使用するのは面倒な場合があることに同意します。これには、スレッドがコールバックより適しています。
gawi

10
うん!また、GHC 7の新しいI / O多重化により、Haskellでのサーバーの作成がさらに向上します。
andreypopp

3
(アウトサイダーとして)最初のポイントは私にはあまり意味がありません... node.jsでリクエストを処理するとき、コールバックは単一のクライアントを扱います。状態を管理することは、複数のプロセスにスケーリングするときに気になることだけになり、それでも利用可能なライブラリを使用するのは非常に簡単です。
Ricardo Tomasi

12
それは別の問題ではありません。この質問がHaskellでの仕事に最適なツールの純粋な検索である場合、またはHaskellでその仕事に最適なツールが存在するかどうかを確認する場合、マルチスレッドプログラミングは不適切であるという暗黙の仮定に挑戦する必要があります。ドン・スチュワートが指摘するように、スレッドはかなり異なります。HaskellコミュニティもNode.jsに嫉妬しない理由を説明する回答は、この質問の非常に話題になっています。ガウィの応答は、それが彼の質問に対する適切な答えであったことを示唆しています。
AndrewC 2012

154

HaskellはNode.jsのいくつかの利点、つまりマルチスレッドプログラミングに頼ることなくI / Oのブロックを回避するクリーンなソリューションを提供できますか?

はい、実際、イベントとスレッドはHaskellで統合されています。

  • 明示的な軽量スレッドでプログラミングできます(たとえば、1つのラップトップで数百万のスレッド)。
  • または; スケーラブルなイベント通知に基づいて、非同期のイベント駆動型でプログラミングできます。

スレッドは実際にはイベントの観点から実装され、複数のコアにまたがって実行され、シームレスなスレッド移行、ドキュメント化されたパフォーマンス、およびアプリケーションを備えています。

たとえば

32コアの同時収集nbody

代替テキスト

Haskellでは、イベントとスレッドの両方があり、すべてのイベントが内部にあります。

実装について説明した論文読んでください


2
ありがとう。このすべてを消化する必要があります...これはGHC固有のようです。大丈夫だと思います。GHCがコンパイルできるものなら何でもHaskell言語が使われることがあります。同様に、Haskellの「プラットフォーム」は、多かれ少なかれGHCランタイムです。
gawi

1
@gawi:それとすぐにバンドルされる他のすべてのパッケージ。すぐに使えるようになります。これは、CSコースで見たのと同じ画像です。そして、最良の部分は、Haskellでは独自のプログラムで同様の素晴らしい結果を達成することは難しくないということです。
ロバートマサイオリ

1
こんにちはドン、このような質問に答えるときに最高の(ワープ)を実行するhaskell Webサーバーにリンクできると思いますか?Node.jsとの関連性が非常に高いベンチマークは次のとおり
グレッグウェバー

4
理論的には。Haskellの「軽量スレッド」は、思ったほど軽量ではありません。いわゆるグリーンスレッドをスケジュールするよりも、epollインターフェイスでコールバックを登録する方がはるかに安価です。もちろん、OSスレッドよりも安価ですが、無料ではありません。それらの100.000を作成すると、約が使用されます。350 MBのメモリと少し時間がかかります。node.jsで100.000接続を試してください。全く問題無い 。ghcはフードの下でepollを使用するので、それが高速でない場合は魔法になります。ただし、スレッドインターフェイスを使用したプログラミングは非常に優れています。
Kr0e 2013

3
さらに、新しいIOマネージャー(ghc)は、(m log n)の複雑さを持つスケジューリングアルゴリズムを使用します(mは実行可能なスレッドの数、nはスレッドの総数)。Epollは複雑さk(kは読み取り/書き込み可能なfd's =の数です。したがって、ghcはすべての複雑度にわたってO(k * m log n)を持ちます。これは、トラフィックの多い接続に直面している場合はあまり良くありません。Node.jsは線形の複雑さを引き起こしますWindowsのパフォーマンスについて話さないでください... Node.jsはIOCPを使用しているため、はるかに高速です
Kr0e

20

最初に、node.jsがこれらすべてのコールバックを公開して正しいことをしているという見方を私は保持していません。あなたは最終的にCPS(継続渡しスタイル)でプログラムを書くことになり、その変換を行うのはコンパイラーの仕事であると私は思います。

イベント:スレッド操作はありません。プログラマーはコールバックのみを提供します(Snapフレームワークの場合と同様)。

したがって、これを念頭に置いて、必要に応じて非同期スタイルを使用して書き込むことができますが、そうすることにより、リクエストごとに1つのスレッドを使用して、効率的な同期スタイルで書き込むことができなくなります。Haskellは、特に他の言語と比較すると、同期コードで途方もなく効率的です。その下にあるすべてのイベントです。

コールバックは単一のスレッドで実行されることが保証されており、競合状態は発生しません。

それでもnode.jsで競合状態が発生する可能性がありますが、さらに困難です。

すべてのリクエストはそれ自身のスレッドにあります。他のスレッドと通信する必要があるコードを記述する場合、haskellの並行処理プリミティブにより、スレッドセーフにすることは非常に簡単です。

素晴らしくシンプルなUNIXフレンドリーなAPI。ボーナス:優れたHTTPサポート。DNSも利用できます。

ハッキングを見て、自分の目で確かめてください。

すべてのI / Oはデフォルトで非同期です(ただし、これはときどき煩わしい場合があります)。これにより、ロックを回避しやすくなります。ただし、コールバックでのCPU処理が多すぎると、他の接続に影響します(この場合、タスクはより小さなサブタスクに分割され、再スケジュールされます)。

そのような問題はありません。ghcは実際のOSスレッド間で作業を分散します。

クライアント側とサーバー側で同じ言語。(ただし、これはあまり価値がありません。JQueryとNode.jsはイベントプログラミングモデルを共有しますが、残りは大きく異なります。サーバー側とクライアント側の間でコードを共有する方法がわかりません。実際に役立つ。)

Haskellはここでは勝てないかもしれません... もう一度考えてみてください。http://www.haskell.org/haskellwiki/Haskell_in_web_browser

これらすべてが1つの製品にパッケージ化されています。

ghcをダウンロードし、cabalを起動します。あらゆるニーズに対応するパッケージがあります。


私は悪魔の擁護者を演じていました。だから、はい、私はあなたのポイントに同意します。クライアント側とサーバー側の言語統合を除きます。技術的には実現可能だとは思いますが、最終的には現在実装されているすべてのJavaScriptエコシステム(JQueryとその仲間)に取って代わることはないと思います。これはNode.jsサポーターによって提唱された議論ですが、あまり重要ではないと思います。あなたは本当にあなたのプレゼンテーション層とあなたのバックエンドの間で多くのコードを共有する必要がありますか?プログラマーが1つの言語だけを知っていることを本当に目的としていますか?
ガウィ

本当のメリットは、サーバー側とクライアント側の両方でページをレンダリングして、リアルタイムページを簡単に作成できることです。
dan_waterworth

@dan_waterworth正確に、meteorまたはderby.jsを
mb21

1
@gawiコードの85%がクライアントとサーバー間で共有されるプロダクションサービスがあります。これは、コミュニティではユニバーサルJavaScriptとして知られています。Reactを使用してサーバー上でコンテンツを動的にレンダリングし、クライアントで最初に役立つレンダリングまでの時間を短縮しています。ブラウザでHaskellを実行できることは承知していますが、同じコードベースを使用してサーバー側とクライアント側のレンダリングを可能にする「ユニバーサルHaskell」のベストプラクティスのセットについては知りません。
エリックエリオット

8

私は個人的に、Node.jsとコールバックを使用したプログラミングを、不必要に低レベルで少し不自然なものと見なしています。GHCで見られるような優れたランタイムがコールバックを処理し、かなり効率的に処理する可能性があるのに、なぜコールバックを使用してプログラミングするのですか?

それまでの間、GHCランタイムは大幅に改善されました。これは、MIOと呼ばれる「新しい新しいIOマネージャー」を特徴としています。ここで、「M」はマルチコアを表します。既存のIOマネージャーの基盤の上に構築され、その主な目標は、4つ以上のコアのパフォーマンス低下の原因を克服することです。このペーパーで提供されているパフォーマンスの数値は非常に印象的です。自分を見る:

Mioを使用すると、Haskellの現実的なHTTPサーバーが20 CPUコアに拡張され、GHCの以前のバージョンを使用する同じサーバーと比較して、最大6.5倍のピークパフォーマンスを実現します。Haskellサーバーの待ち時間も改善されています:[...]適度な負荷の下で、GHCの以前のバージョンと比較して予想される応答時間を5.7倍短縮します

そして:

また、Mioを使用すると、McNettle(Haskellで記述されたSDNコントローラー)が40以上のコアに効率的にスケーリングし、1台のマシンで毎秒2,000万を超える新しいリクエストのスループットに到達できるため、既存のすべてのSDNコントローラーの中で最速になることも示しています。

MioはそれをGHC 7.8.1リリースにしました。私はこれをHaskellのパフォーマンスにおける大きな前進だと個人的に見ています。以前のGHCバージョンと7.8.1でコンパイルされた既存のWebアプリケーションのパフォーマンスを比較することは非常に興味深いでしょう。


6

IMHOイベントは良いですが、コールバックによるプログラミングは良くありません。

Webアプリケーションのコーディングとデバッグを特別にする問題のほとんどは、Webアプリケーションをスケーラブルで柔軟性のあるものにすることに起因しています。最も重要なのは、HTTPのステートレスな性質です。これにより、操作性が向上しますが、これにより、IO要素(この場合はWebサーバー)がアプリケーションコード内の異なるハンドラーを呼び出す制御が逆になります。コールバックは変数のスコープを共有せず、ナビゲーションの直感的なビューが失われるため、このイベントモデル(より正確には、コールバックモデル)は悪夢です。ユーザーが前後にナビゲートするときに、考えられるすべての状態変化を防ぐことは非常に困難です。

問題は、イベントモデルが正常に機能するGUIプログラミングに似ていますが、GUIにはナビゲーションと戻るボタンがありません。これにより、Webアプリケーションで可能な状態遷移が増加します。これらの問題を解決する試みの結果は、複雑な構成の重いフレームワークであり、問​​題の根本に疑問を投げかけることなく、広範なマジック識別子がたくさんあります。識別子をリンクすることによって構築されます。

ocsigen(ocaml)seaside(smalltalk)WASH(廃止、Haskell)やmflow(Haskell)などの順次ベースのフレームワークがあり、ナビゲート性とRESTフルネスを維持しながら状態管理の問題を解決します。これらのフレームワーク内では、プログラマはナビゲーションを命令シーケンスとして表現できます。プログラムはページを送信し、シングルスレッドで応答を待ちます。変数はスコープ内にあり、戻るボタンは自動的に機能します。これにより、本質的に短く、より安全で読みやすいコードが生成され、ナビゲーションがプログラマーにはっきりと見えます。(公正な警告:私はmflowの開発者です)


node.jsでは、コールバックは、データベースなどへの非同期I / Oの処理に使用されます。あなたは興味深いが、質問には答えない別のことについて話しています。
Robin Green

あなたが正しいです。異議申し立てに応えるのに3年かかりました:github.com/transient-haskell
agocorona

Nodeは非同期関数をサポートするようになりました。つまり、実際に非同期の命令型コードを記述できます。それは内部で約束を使います。
エリックエリオット

5

1)Haskellはすでにこの問題をはるかに良い方法で解決しており、2)Erlangとほぼ同じ方法で問題を解決しています。ノードに対するベンチマークは次のとおりです。http//www.yesodweb.com/blog/2011/03/preliminary-warp-cross-language-benchmarks

Haskellに4つのコアを与えると、単一のアプリケーションで毎秒10万(シンプル)リクエストを実行できます。ノードは多くのことを行うことはできず、単一のアプリケーションをコア全体に拡張することはできません。そして、Haskellランタイムはノンブロッキングなので、これを取得するために何もする必要はありません。ランタイムに非ブロッキングIOが組み込まれている他の(比較的一般的な)言語は、Erlangだけです。


14
ばかげている?問題は、「Haskellに応答があるか」ではなく、「Haskellの応答とは何か」です。質問が出された時点では、GHC 7はまだリリースされていなかったため、Haskellはまだ「ゲーム内」にいませんでした(Snapのようなlibevを使用するフレームワークを除いて)。それ以外は同意します。
ガウィ

1
この回答を投稿したときにこれが本当だったかどうかはわかりませんが、実際には、ノードアプリをコア間で簡単にスケーリングできるようにするノードモジュールがあります。また、そのリンクは、単一コアで実行されているnode.jsと4コアで実行されているhaskellを比較しています。私はそれがよりきれいな構成で再び実行されることを望みますが、悲しいことに、githubリポジトリはなくなっています。
Tim Gautier、2012年

2
Haskellが4つ以上のコアを使用すると、アプリケーションのパフォーマンスが低下します。この問題に関する論文があり、積極的に取り組んでいますが、それでも問題があります。したがって、16個のコアサーバーでNode.jsの16個のインスタンスを実行することは、+ RTS -N16を使用する単一のghcアプリケーションよりもはるかに優れています。多くのOSスレッドで使用すると速度が低下するIOManagerを1つだけ使用するためです。私は彼らがこのバグを修正することを望みますが、それは今まで存在していたのであまり望んでいませんでした...
Kr0e

この答えを見ている人は、Nodeが1つのコアで100kの単純なリクエストを簡単に処理でき、ステートレスNodeアプリケーションを多くのコアに簡単にスケーリングできることを知っておく必要があります。pm2 -i max path/to/app.js使用可能なコアに基づいて、インスタンスの最適な数に自動的にスケーリングします。さらに、ノードもデフォルトで非ブロッキングです。
エリックエリオット

1

1
これはどのように質問に答えますか?
dfeuer 2015年

1
@dfeuerリンクは、Snap Haskell Web Frameworkがlibevを削除したため、フォーマットが失敗する理由がわかりません。ノードサーバーランタイムは、それが始まったときはすべてLinux libevであり、Snap Web FrameWorkも同様でした。Haskell with SnapはECMAscript with nodejsに似ているため、nodejsとともにSnapがどのように進化するかはHaskellよりも重要であり、このコンテキストではECMAscriptと比べてより正確です。
Chawathe Vipul S
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.