Hatena::ブログ(Diary)

shutdown -r now このページをアンテナに追加 RSSフィード Twitter

2011-04-07

Node.jsと非同期I/Oと混乱した私

最近、Node.jsに興味があります。Node.jsを調べていると、I/O関係で、非同期I/O、ノンブロッキング、コールバック等、いろいろとキーワードがでてきて混乱してきます。

そこで、ブログに書いて整理しよう思います。間違いがあれば是非、指摘してください。

言葉の定義

◇非同期呼び出し(Asynchronous Call)と同期呼び出し(Synchronous Call)の違い
同期呼び出し(Synchronous Call)
通常メソッドを呼び出すとメソッド内の処理が完了するまで、呼び出し元には戻ってこない。このようなメソッド呼び出しのこと。
非同期呼び出し(Asynchronous Call)
メソッドを呼び出した瞬間に呼び出し元に処理が戻ってくるような呼び出しのこと。非同期で呼び出されたメソッドは、環境によって処理されるタイミングが変わる。
◇マルチスレッド(multithread)とコールバック(callback)の非同期処理の違い
マルチスレッド(multithread)による非同期処理
呼び元と並行に処理が行われる。
コールバック(callback)による非同期処理
呼び元がプロセッサを使用していないときに処理が行われる。
◇ブロッキングI/O(blocking I/O)とノンブロッキングI/O(non-blocking I/O)の違い
ブロッキングI/O(blocking I/O)
データ処理が完了するまで待たされること。
ノンブロッキングI/O(non-blocking I/O)
データ処理の完了を待たされずに、他の処理を行えること。
◇非同期通知(Asynchronous Notification)とは
非同期に処理された結果を通知すること。実装的には、コールバックやシグナルで受け取る。

I/Oモデル

W. Richard Stevensの「UNIXネットワークプログラミング 第2版 Vol.1」では、I/Oモデルは以下のように分類できるそうです。

  • Synchronous I/O Operation (同期I/O操作)
    • blocking I/O (ブロッキングI/O)
    • non-blocking I/O (非ブロッキングI/O)
    • I/O multiplexing (I/O多重化)
    • signal driven I/O (シグナル駆動I/O)
  • Asynchronous I/O Operation (非同期I/O操作)
    • Asynchronous I/O (非同期I/O)

また、developerWorksの「Boost application performance using asynchronous I/O」によると

以下の図のように分類できるようです。

f:id:forest1040:20110407135041p:image

この図では、I/O multiplexingがAsynchronousに分類されていますね。

なぜでしょうか。。このAsynchronousは、I/O Operationのことではなく、通知のことであるI/Oが可能になった通知を非同期に受ける(非同期通知)という見方もあるようですが、私は違うような気がします。

例えば、select()を使った場合、I/O可能になるまで待たされるか、タイムアウトをつけて定期的にチェックすることになります。その場合、同期的に通知を受けることになると思います。

developerWorksの記事では、I/O multiplexingについて、あまり触れられておりませんので、

なぜこうなっているのかよくわかりません。。

自分が非同期通知について、勘違いしているのでしょうか。。

また、「C10K問題」というのがあります。C10Kとは、クライアント1万台のことであり、ハードウェアの性能上は問題がなくても、あまりにもクライアントの数が多くなるとサーバがパンクする問題のことです。

詳しくは、リンク先を読んで欲しいのですが、解決方法として、以下の方法が挙げられています。

  1. 各スレッドが複数のクライアントを受け付ける。 そしてノンブロッキング I/O と レベル・トリガ型の完了通知(level-triggered readiness notification)を利用する。
  2. 各スレッドが複数のクライアントを受け付ける。 そしてノンブロッキング I/O と 変更型の完了通知(readiness change notification)を利用する。
  3. 各スレッドが複数のクライアントを受けつける。 そして非同期 I/O を使う。
  4. 各スレッドが一つのクライアントを受けつける。 そしてブロッキング I/O を使う
  5. サーバのコードをカーネルに組込む。

Node.jsでは、「3. 各スレッドが複数のクライアントを受けつける。 そして非同期 I/O を使う」を採用しているようです。

Node.jsでは、「libev」と「libeio」を使用して、非同期I/O環境を実装しています。

「libev」は、C言語で書かれたイベントループライブラリです。

イベントループとは、無限ループを行いながら、I/Oを監視し、利用可能やI/O完了等のイベントが発生するとコールバックにより通知します。

I/Oの監視には、I/O multiplexingモデルを使用し、環境によって最適なシステムコール(Linuxであれば、epoll、FreeBSDでは、kqueue)を使用します。

先程の「Boost application performance using asynchronous I/O」の図の左下(Asynchronous + Blocking)に「libev」は位置するのではないかと思います。

koichikkoichik 2011/04/07 17:00 力作ですねー。
まずはじめに、この分野も例によって用語の使われ方は曖昧なことが多いので、あまり厳密に考えてもしょうがない面があります。たとえば英語 Wikipedia の「Asynchronous I/O」。

http://en.wikipedia.org/wiki/Asynchronous_I/O
> Asynchronous I/O, or non-blocking I/O,

「Asynchronous I/O」と「non-blocking I/O」は同義語という扱いです。英語でもこの調子な上に、日本語になると synchronous/asynchronous も blocking/non-blocking も、さらには syncrhonized/non-synchronized にも訳語として「同期」「非同期」が使われることが多々あります。

んで、Node の JavaScript コードから見える世界に関しては blocking かつ synchronous と、non-blocking かつ asynchronous な API しかありません。よって、JavaScript 世界に関しては「同期」「非同期」という使い分けだけで事足ります。@IT の記事なんかであれば、「同期」「非同期」と「ブロッキング」「ノンブロッキング」を使い分けなくてもいいんじゃないかなーと思います。

以下、本文へのコメント。

> ◇非同期呼び出し(Asynchronous Call)と同期呼び出し(Synchronous Call)の違い

この説明は blocking/non-blocking になってますが、呼び出しという文脈では synchronous/asynchronous と同義語な使われ方が多いので、まぁいいのかなぁ。

> ◇マルチスレッド(multithread)とコールバック(callback)の非同期処理の違い

マルチスレッドとコールバックを対比するのは変。あと、以前もコメントした気がしますが、この場合は「非同期処理」ではなく「並行処理」でしょう (ただし並列処理にあらず、またややこしいかもw)。「マルチスレッドによる並行処理」「シングルスレッドによる並行処理」という対比が適切かと。

そもそも、この文脈で並行処理が出てくるのが変な気もします。連載の 1 回目でも「普通、非同期というと、サーバサイド側でマルチスレッド」と出てきましたが、「非同期」という用語から「サーバサイドでマルチスレッド」が出てくるのは不思議な感じです。と書いていて気づきましたが、AJax の A が指す非同期からの連想?

> この図では、I/O multiplexingがAsynchronousに分類されていますね。

気にしない方が吉かな。

> 例えば、select()を使った場合、I/O可能になるまで待たされるか、タイムアウトをつけて定期的にチェックすることになります。

それは select() が blocking だということを言ってるだけで、synchronous/asynchronous とは関係ないですね。select() 系の API は通常、入出力が「可能になった」時点で通知されるのに対し、POSIX 定義では入出力が「完了した」時点で通知されるのが asynchronous です。なので、select() 系は POSIX 定義の asynchronous ではありません。

ちなみに、ファイルはいつでも読み書き可能なくせにいざ読み書きすると時間がかかるので select() 系の API では実用になりません (libeio が必要な理由)。select() 系が synchronous であることを如実に語っています。

> 先程の「Boost application performance using asynchronous I/O」の図の左下(Asynchronous + Blocking)に「libev」は位置するのではないかと思います。

libev は中で select() 系を使ってるだけなので、select() が asynchronous じゃないなら libev も違いますよね。どこに差があると考えたのか気になります。

libev は主にネットワーク (ソケット I/O) に使われますが、この場合は asynchronous ではありません。libeio と組み合わせるファイル I/O は (libev のスレッドから見れば) asynchronous ですが、libev 単体では asynchronous と位置づけるのは適切ではないでしょう。libev + libeio のファイル I/O であれば blocking + asynchronous です。この場合も libeio のワーカスレッド上ではただの blocking I/O だったりするので、視点によって全然違ってくるのがややこしいですね。

なお、Node の Windows 対応がらみで Ryan が libol という libev より上位レイヤのライブラリを作ってます。Unix (POSIX) では libol 経由で従来同様 libev + libeio が使われますが、Windows では直接 Windows の API (IOCP) が使われることになるようです。

https://github.com/joyent/libol
http://tinyclouds.org/iocp-links.html

forest1040forest1040 2011/04/08 13:12 koichikさん、ありがとうございます。

> この分野も例によって用語の使われ方は曖昧なことが多いので、あまり厳密に考えてもしょうがない面があります。
> 「Asynchronous I/O」と「non-blocking I/O」は同義語という扱いです。

なるほど。そうだったんですね。ってきり、別物と考えていました。
「non-blocking I/O」は、O_NONBLOCKを付けることだぐらいに思っていました。
確かに、資料によっては、「non-blocking I/O」を「Asynchronous I/O」と
同じように説明しているが多くありました。
おかしいなぁと思ってたんですけれどそういうことだったんですね。。

> > ◇非同期呼び出し(Asynchronous Call)と同期呼び出し(Synchronous Call)の違い
> この説明は blocking/non-blocking になってます

なるほど。そういうことですか!たしかにこの説明だと「blocking/non-blocking」があいますね。

> > ◇マルチスレッド(multithread)とコールバック(callback)の非同期処理の違い
> この場合は「非同期処理」ではなく「並行処理」でしょう (ただし並列処理にあらず、またややこしいかもw)。
> 「マルチスレッドによる並行処理」「シングルスレッドによる並行処理」という対比が適切かと。

最初、「並行処理」と「並列処理」の違いを載せようと思っていたんですけど、
「並列処理」が記事にあまり関係ないので、やめましたw

「シングルスレッドによる並行処理」というのが、イメージしにくいですね。。
プロセッサをOSがタイムシェアリングして、マルチタスクしているようなイメージでしょうか。

> > 先程の「Boost application performance using asynchronous I/O」の
> > 図の左下(Asynchronous + Blocking)に「libev」は位置するのではないかと思います。
> >
> libev は中で select() 系を使ってるだけなので、select() が asynchronous じゃないなら
> libev も違いますよね。どこに差があると考えたのか気になります。

libevは、「Asynchronous I/O」ではないのですが、「Asynchronous Notification」ではあるのかなと思い
図の左下(Asynchronous + Blocking)位置すると思いました。

> なお、Node の Windows 対応がらみで Ryan が libol という libev より上位レイヤのライブラリを作ってます。

へぇー。libol知りませんでした。見てみます。

koichikkoichik 2011/04/08 17:00 > 「シングルスレッドによる並行処理」というのが、イメージしにくいですね。。

それは「並行処理」より「並列処理」のイメージが強いせいではないでしょうか。

> プロセッサをOSがタイムシェアリングして、マルチタスクしているようなイメージでしょうか。

今時の OS は CPU を割り当てる単位がスレッドなので、OS がタイムシェエアリングするというとマルチスレッドのイメージになってしまのではないかと。

> libevは、「Asynchronous I/O」ではないのですが、「Asynchronous Notification」ではあるのかなと思い

元々この図のタイトルは「Simplified matrix of basic Linux I/O models」で、この文脈における Asynchronous は I/O のことです。「Asynchronous I/O」でないなら図の左下に位置づけられるはずがありません。select() はもちろん blocking I/O だってシグナルで割り込まれる (これも「Asynchronous Notification」ですよね) ことはあり得ますが、それを理由に「Asynchronous」に位置づけたりはしません。
この手の話題で「Asynchronous I/O」と「Asynchronous Notification」を区別するケースもほとんど見かけませんし (Asynchronous I/O は I/O の完了を Asynchronous Notification されるものだから)、「Asynchronous Notification」なんて用語も含めて忘れた方が吉かと思います。

forest1040forest1040 2011/04/08 17:36 ありがとうございます。忘れます。。
しかし、左下の位置づけが、すっきりせずに気持ち悪いので、以下のイメージで考えてみます。

select()にタイムアウトを付けて、定期的にチェックするため、I/O自体は、blockingである。
しかし、タイムアウト後に他の処理はできるので、Asynchronousである。

まぁ、あまり気にしないようにします。

「シングルスレッドによる並行処理」がイメージできるように修行します。。

koichikkoichik 2011/04/08 19:00 > しかし、タイムアウト後に他の処理はできるので、Asynchronousである。

それ Asynchronous (非同期) じゃなくて Concurrency (並行)。。。

forest1040forest1040 2011/04/09 15:45 > それ Asynchronous (非同期) じゃなくて Concurrency (並行)。。。
ヾ(゜□゜;)ノ そっ、そうでした。。

badatmathbadatmath 2011/04/10 15:35 > 1. 各スレッドが複数のクライアントを受け付ける。 そしてノンブロッキング I/O と レベル・トリガ型の完了通知(level-triggered readiness notification)を利用する。
> 2. 各スレッドが複数のクライアントを受け付ける。 そしてノンブロッキング I/O と 変更型の完了通知(readiness change notification)を利用する。

ミクロな視点で見てみるとNodeで採用しているのはこちらとも言えますね。
libevが利用できるシステムコールによって1か2かが分かれます。

例えば最近のlinuxではepoll()が利用可能なのでlibevはreadiness change notificationを
利用しますよね。select()を利用するのであれば1のほう。
この、微細な視点で見るか大局的な視点でみるかによって解釈が変わる
(非同期やらNonBlockingの受け取り方)のがNodeのようなアーキテクチャの
難しいところだと思います。

forest1040forest1040 2011/04/11 10:32 badatmathさん、コメントありがとうございます。
> ミクロな視点で見てみるとNodeで採用しているのはこちらとも言えますね。
> libevが利用できるシステムコールによって1か2かが分かれます。
> この、微細な視点で見るか大局的な視点でみるかによって解釈が変わる
> (非同期やらNonBlockingの受け取り方)のがNodeのようなアーキテクチャの
> 難しいところだと思います。

なるほど。ややこしいですけど、面白いですね。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証