質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

OS

OS(オペレーティングシステム)は、システムソフトウェアの一種であり、一般的に、ハードウェアを直接的に管理・操作する最も中心的な機能を有するソフトウェアがオペレーティングシステムとして呼ばれます。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Lua

Luaは、汎用のスクリプト言語の一つで、 移植性が高く、高速な実行速度などの特徴を持ち 手続き型・オブジェクト指向言語としても利用可能で 関数型言語、データ駆動型の要素も併せ持っている言語です。

Q&A

解決済

2回答

4401閲覧

c10k問題:第二次プリエンプティブ戦争

mit0223

総合スコア3401

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

OS

OS(オペレーティングシステム)は、システムソフトウェアの一種であり、一般的に、ハードウェアを直接的に管理・操作する最も中心的な機能を有するソフトウェアがオペレーティングシステムとして呼ばれます。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Lua

Luaは、汎用のスクリプト言語の一つで、 移植性が高く、高速な実行速度などの特徴を持ち 手続き型・オブジェクト指向言語としても利用可能で 関数型言語、データ駆動型の要素も併せ持っている言語です。

5グッド

3クリップ

投稿2016/10/29 02:20

編集2016/10/31 23:58

また、具体的な課題が含まれていないご意見募集型の質問で失礼します。

####昔話
Windows 3.1 や Mac OS System 4 など昔のパソコンのOSはノンプリエンプティブ・マルチタスク(以降、協調的マルチタスクと呼びます)でした。
これらのOSではアプリケーションが自発的にCPUを開放しないとタスクが切り替わらず、一つでもCPU の開放のしかたが悪いプログラムがあると、OSごと凍りついてしまうものでした。そのことが不評で、今や全面的にプリエンプティブ・マルチタスクのOSに置き換えられました。

####帰ってきた協調的マルチタスク
今、マルチスレッドのアーキテクチャが抱えるC10K問題に対応するために node.js、 nginx+lua などイベント駆動型やコルーチン型(=協調的マルチタスク)で処理する処理系が出始めました。そして、従来の一つのリクエストを一つのスレッドが処理するプリエンプティブなマルチスレッド処理系に比べて高い性能が出るということが定説になっています。

####また戦いが
その状況の中で、最近のWebでの議論を見ていると、Linux のマルチスレッドが非常に軽量・高速で協調的マルチタスクじゃなくてもマルチスレッドで十分性能が出るというベンチマーク結果が出てきているようです(※1)。そうなると、協調的マルチタスクの処理系はまたなくなってしまうのでしょうか。
前述の昔話において、パソコンのOSがプリエンプティブマルチタスクに置き換えられるときも、OSの実装者は「プリエンプティブマルチタスクなんて必要ない。プログラマが時間のかかる処理を実装するときに処理の途中経過を報告したり、中断したりする機能をちゃんと入れてれば、協調的マルチタスクのほうが効率が高いに決まっている」と執拗に粘ったのですが、世の中の流行には逆らえず(というよりも質の悪いプログラムの多さに負けて)、負けてしまいました。
今回もプリエンプティブマルチタスク(マルチスレッド派)対協調的マルチタスク(イベント駆動型やコルーチン派)の戦いが始まっているように思います。これを第二次プリエンプティブ戦争と呼びましょう。

####協調的マルチタスク派
(いくつか回答いただいて、自分の好みの問題とC10K問題が直結していないことに気が付き、書き直しました)
私は個人的にはシングルスレッドイベント駆動型でコーディングするのが好きで、c10K問題を理由にマルチスレッドプログラミングのパラダイムを駆逐して欲しいくらいに思っています。たとえば、DBへの問い合わせが長い時間かかる場合に、ユーザからの要求やシステムのシャットダウンに対応して問い合わせを中断できるようにする場合、マルチスレッドで割り込みを気にしながら順次的に書くよりも自分で状態遷移を管理するほうが簡単ではないでしょうか。マルチスレッドの場合、
(DBへの問い合わせを中止できるようなドライバがあるかどうかはおいておいて)

javascript

1class 問い合わせ { 2 do() { 3 try { 4 DBへの問い合わせ 5 } catch (CancelSignal cs) { 6 if (微妙なタイミング判定) { 7 問い合わせ中止処理 8 } 9 } 10 } 11 onCancel() { 12 問い合わせ実行中スレッドへ CancelSignal を送信 13 } 14}

となり、問い合わせの終了とキャンセルシグナルの入力との微妙なタイミング判定(排他制御)が必要ですが、シングルスレッドであれば、

javascript

1class 問い合わせ { 2 do() { 3 DBへの問い合わせ((event) => this.handler(event)); 4 } 5 handler (event) { 6 ... 7 } 8 onCancel() { 9 問い合わせ中止処理 10 } 11}

と書けて、シングルスレッドなので、handler 呼び出しと onCancel の呼び出しは早い者勝ちとなるわけです。

そもそも、OSレベルでは多重入力はイベント駆動で実装されているわけで、そのイベント駆動をそのままコーディングできるシングルスレッドイベント駆動プログラミングって気持ち良くないですか?

####質問:シングルスレッドプログラミングは好きですか?残ると思いますか?
いくつか回答いただいて、シングルスレッドプログラミングとC10K問題が直結していないことに気が付きましたが、質問としてはC10K問題で復権したシングルスレッドプログラミングをどう思うかというポイントに変更して続けさせていただきたいと思います。
JavaScript のコールバックは嫌いですか? lua などコルーチンの yield って懐かしくないですか?マルチスレッドプログラミングって難しくないですか?

####補足1:マルチコアについて
それほど性能がCPUに依存するアプリが少ないのではないかとは思いますが、、CPU依存が強い場合でもワーカプロセスに負荷分散することで、プログラム自身はシングルスレッド・協調的マルチタスクで書くことが可能だと思います。特に並列演算で問題が解決するような場合は、ワーカプロセス側で GPUをつかったり、SIMD命令を使うなど、マルチコア以上に並列度をあげる工夫が必要ではないかと考えます。この場合は、マルチスレッドプログラミングというよりも並列処理プログラミングになってくると思います。となると、やはりマルチスレッドプログラミングは不要なのではないかと思えてきます。

####補足2:イベント駆動型プログラミングの順次的記述
回答でイベント駆動型プログラミングのデメリットとしてサブルーチンのネスト構造などモジュール化がやりにくいという指摘がありました。 Javascript では、サブルーチンの呼び出し後の継続処理をコールバック関数(というかクロージャ)に記述するという技法で回避し、非同期でありながらモジュール化を行うことに成功しています。しかし、この技法はコールバック地獄と呼ばれており、モジュール化には成功しましたが、順次処理を記述すると、ネストがどんどん深くなるという欠点がありました。次世代 Javascript である es6 では、この問題を Promise という技法でさらに回避しております。
ここで、普通に順次的に記述したプログラムと Promiseを使って非同期処理可能でありながら順次的に記述したプログラムを比較してみます。

普通の順次的記述

Javascript

1function main() { 2 let x = 'default value of x'; 3 let y = 'default value of y'; 4 let z; 5 try { 6 z = getValuesForZ(x); 7 z = z.map(item => processItem(item)); 8 if (z.includes(processItem(x))) { 9 y = processY(y); 10 } 11 console.log("x = " + x + ", y= " + y + ", z= " + z); 12 } catch (e) { 13 console.log(e); 14 } 15} 16 17function getValuesForZ(arg) 18{ 19 return ['value1', 'value2'].concat(arg); 20} 21 22function processItem(item) { 23 return 'processed:' + item; 24} 25 26function processY(y) { 27 return processYnest(y); 28} 29 30function processYnest(y) { 31 return 'processed in nested function:' + y; 32} 33 34main()

Promise を使った非同期処理可能な順次的記述

Javascript

1function main() { 2 Promise.resolve({ 3 x: 'default value of x', 4 y: 'default value of y' 5 }) 6 .then(getValuesForZ()) 7 .then(state => { 8 return Promise.all(state.z.map(item => Promise.resolve(item).then(processItem()))) 9 .then(z => Object.assign(state, {z})); 10 }) 11 .then(state => { 12 return Promise.resolve(state.x).then(processItem()).then(r => { 13 if (state.z.includes(r)) { 14 return Promise.resolve(state).then(processY()); 15 } 16 return state; 17 }); 18 }) 19 .then(state => { 20 console.log("x = " + state.x + ", y= " + state.y + ", z= " + state.z); 21 }) 22 .catch(e => { 23 console.log(e); 24 }); 25} 26 27function getValuesForZ() 28{ 29 return state => Object.assign(state, {z:['value1', 'value2'].concat(state.x)}); 30} 31 32function processItem() { 33 return item => 'processed:' + item; 34} 35 36function processY() { 37 return state => { 38 return Promise.resolve(state.y) 39 .then(processYnest()) 40 .then(y => Object.assign(state,{y})); 41 }; 42} 43 44function processYnest() { 45 return y => 'processed in nested function:' + y; 46} 47 48main()

普通版は 34行に対して、Promise 版は48行と行数は増えてしまいましたが、処理順にプログラムを書くことができて、かつ、サブルーチンによるモジュール化もできていると思います。
モジュール化のみそは、サブルーチンが演算結果を返すのではなく、演算を行う関数を返すことです。このようにすることで、呼び出し側は非同期にその関数を呼び出すことができるわけです。
プログラムの詳細な説明は避けますが、順次処理、条件判定、繰り返し処理、サブルーチンのネスト呼び出しを含める例となっています。プログラムの処理内容には何の意味もありません。また、両方を比較しやすいように非同期処理は入っていませんが、Promise版の方ではいくらでも非同期処理を挿入できますが、普通版では非同期処理を挿入することは不可能です。

※1の参考:
TCP/IP - Solving the C10K with the thread per client approach
「サーバ書くなら epoll 使うべき」は、今でも正しいのか
いずれも、協調的マルチタスクに比べれば性能は出ないと思いますが、「協調的マルチタスクがないと1万コネクションはできないのかというとそうではない」という主張だと思います。桁が上がって10万コネクションになれば、やはり協調的マルチタスクが必要なのかもしれません。

sharow, i50, LLman, pack, sanai👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yohhoy

2016/10/29 04:42

「その状況の中で、最近のWebでの議論を見ていると、Linux のマルチスレッドが非常に軽量・高速で協調的マルチタスクじゃなくてもマルチスレッドで十分性能が出るというベンチマーク結果が出てきているようです。」は具体的な情報ソースありますか?C10Kに耐えうる"OSスレッド"というのは聞いたことがありません...
mit0223

2016/10/29 10:50

yohhoy様、一応ソースを追加しましたが、補足のとおり、OSスレッドで協調的マルチタスクと同等に性能が出るという話ではありませんでした。誤解を招く表現ですみませんでした。
yohhoy

2016/10/29 15:38

参考URLありがとうございました。思っていた以上に"動く"ものなのですね(私の知識が古かったようです)。とはいえStackOverflow回答にあるように、OS側のリソース制限値をチューニングする必要はあるようですね。
guest

回答2

0

ベストアンサー

こんにちは。

質問:どっちが好きですか・どっちが勝つと思いますか

マルチ・スレッド型が好きです。そしてマルチ・スレッドの経験の方が遥かに多いです。少なくとも私の経験上は、多くの処理は「流れ」で把握するのでイベント駆動型が適しているなケースは稀でした。
ヒョイっと「待ち」を作れないイベント駆動型でシーケンス制御的な処理を行うのはかなり辛いです。

しかし、どちらが勝つというものではないです。
例えば、GUIにはイベント駆動型は適していると思います。そして、イベント駆動型やコ・ルーチンもスレッドの上で動作します。マルチ・コアを活かすためにスレッドを複数同時起動します。

そして、今後も、マルチ・コアが主流のまま、コアの数が増えていくと思います。
熱問題のためCPUの周波数を大きく上げることは困難だそうです。その状況で処理性能を上げるためのマルチ・コアですね。そして、マルチ・コアを有効に利用するためのマルチ・スレッドです。
本質的にノンプリエンプティブなプログラミング・モデルだけではマルチ・コアを有効利用できませんから、これらの基本的な部分にブレークスルーが起きない限り、状況は変わらないと考えています。

更に、このあたりに革新が起きても、OSがノンプリエンプティブに戻ることはないと考えています。
「プログラマが時間のかかる処理を実装するときに処理の途中経過を報告したり、中断したりする機能をちゃんと入れてれば」の前提条件をアプリが満たしていることを検証する必要が有ります。
そもそも検証出来るわけがないデバッグ中は特に辛いです。暴走→システム再起動。二昔前によくやってました。その悪夢の復活です。
また、高信頼性を求められないちょっと便利なアプリもあります。そのようなアプリを本番稼働中のシステムで起動するためだけのために、追加でOS並の密なテストを行いCPU開放を適切に行うことの検証を行わないと、システム全体の信頼性を損なうことになります。
そんな面倒な運用が必要なシステムより、プリエンプティブでOS側がアプリに引きずられて落ちないようなシステムが主流になるのは必然と思います。


【C10K問題について】
現場を知らないのですが、C10K問題については、64bit OSでスタックフルなコ・ルーチンがバランスの取れた解のように感じてます。実際のところ、どの方向が主流なのでしょう?
64bit OS全盛の現在でも未だにスタックレスが主流でしょうか?

投稿2016/10/29 08:00

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mit0223

2016/10/29 11:08

回答ありがとうございました。 > どちらが勝つというものではないです。 おっしゃるとおりだと思います。Java Servlet 3.0 の非同期ソリューションもリクエストの受付だけシングルスレッドで実際の処理は worker スレッドのハイブリッド型ですよね。Goもハイブリッドみたいですね。また、node.js もマルチコアを活すために負荷分散すればハイブリッドと言えそうです。 ただ、個別の話として node.js, lua などは主流になりえるのかという疑問は残ります。ハイブリッドになるとしても、プログラマが意識するのはどっちかいう問題は残るかなと。
Chironian

2016/10/29 18:21

> プログラマが意識するのはどっちかいう問題は残るかなと。 シーケンス制御的な処理(処理の途中で例えばI/Oアクセス等の「待ち」が入り、待っている間他の処理を行い、待ちから戻ったらそれまでの処理を継続するような処理)を行う必要がある場合は、スレッド、もしくは、スタックフルなコルーチンでないと、プログラム開発がたいへん過ぎると考えます。 イベント駆動型やスタックレスなコルーチンの場合、その待ちが発生した時、その間に他の処理を行うために、「メイン・ループ」へ戻って他の処理をこなします。そして、待ちが終了したら中断していた処理へ戻ります。 複数の処理を纏めためものがサブルーチンですが、多段階のサブルーチン呼び出しの奥の方で待ちが発生する時どうします? イベント駆動型の場合、せっかくまとめられるものを、全ての「待ち」で分割してフラットにするしかありません。なかなか辛いです。 ですので、シーケンス制御的な処理が必要なケースで、イベント駆動型でプログラミングするのは出来る限り避けた方が良いと考えています。 スタックを使えば、多段階に呼び出されたサブルーチンの奥の方で待ちに入り、その中で他のスタックへ切り替えて別の処理をこなし、そして、元のスタックへ戻して処理を継続することができますから、「普通」にサブルーチンを使えます。 従って、待ちの間に他の処理要求をこなすためには、マルチ・スレッド、ないし、スタックフルなコルーチンを使うことが現実的な解と思います。
mit0223

2016/10/30 00:49

> 複数の処理を纏めためものがサブルーチンですが、多段階のサブルーチン呼び出しの奥の方で待ちが発生する時どうします? はい、これが問題です。Javascript ではコールバック地獄と呼ばれており、 es6 で Promise と呼ばれるパラダイムの導入により、緩和されてはいますが、個人的にはこのパラダイムは好きではありません。 イベント駆動で気持ちよくなれるのは多重入力を受け入れるプログラムを書くときだけなのですが、私は、サーバのシャットダウンまで考えると、ほとんどの入出力は多重入力を考えないといけないのではないかと思っています。 確かに最初はスタックフルなコードが気持ち良いかもしれませんが、例外処理や多重入力を丁寧に対応しようとすると、やはり、スタック(コンテキスト)を遡って処理を熟考する必要があり、実は、コールバック地獄対 throw-catch 地獄の対決なのかなとも思います。 あと、コールバックもオブジェクト指向にすれば、それほどコード量も扱いやすさも変わらないのではないかという話もあります。サブルーチンのローカル変数はオブジェクトのメンバ変数にして、サブルーチンの呼び出しをオブジェクトのメソッド呼び出しにするのではどうでしょう? sub(xxx); ↓ new sub(xxx).do(); って感じです。
mit0223

2016/10/30 00:56

あ、いずれにせよ、Chironian様には丁寧に概念レベルで議論していただき、考えを整理するのに助かっております。感謝です。
Chironian

2016/10/30 06:21

> サーバのシャットダウンまで考えると、ほとんどの入出力は多重入力を考えないといけないのではないかと思っています。 > 例外処理や多重入力を丁寧に対応しようとすると、やはり、スタック(コンテキスト)を遡って処理を熟考する必要があり リソース開放を気にされているように感じました。 > 実は、コールバック地獄対 throw-catch 地獄の対決なのかなとも思います。 そしてリソース開放のための「throw-catch 地獄」にハマっているのですね。なるほど。 確かにリソース開放処理の記述の難易度は高いです。特に例外等で処理を中断する時はいつも苦労します。 それでも、私はtry-catch地獄を取ることが多いです。(理由は後述します。) (最近はRAII(https://ja.wikipedia.org/wiki/RAII)を使ってtry-catch地獄を回避してます。) > あと、コールバックもオブジェクト指向にすれば、それほどコード量も扱いやすさも変わらないのではないかという話もあります。 いや、それは幻想と思います。 例えば、ファイル・アクセスは全てI/O待ちが発生します。 それらの待ちの間にイベント駆動方式で他の処理を行うためには、非同期I/Oを用いることになります。 今、仮にopen→read→write→closeと言う一連の処理をincrementFileItem(ファイル名, 項目名)のようなサブルーチンに纏めたいとします。 しかし、イベント駆動方式でincrementFileItem()サブルーチンに纏めてそれを呼び出せば良いようにすることは無理です。 要求エリアに、incrementFileItem処理要求、パラメータ、継続処理Aを書き込んでreturnしますね。 そして、incrementFileItem処理は非同期I/Oでopen→read→write→closeするので、5つ程のコールバックに分割されてます。 この処理の間、incrementFileItemの要求元が指定した継続処理Aが書かれている領域を上書きしてはいけませんので退避する必要があります。このような「サブルーチン呼び出し」が多段階で発生することを想定すると、その退避先はスタック構造になります。 処理中断時はスタックの巻き戻しが必要になり、それに伴うリソース開放も発生するでしょう。処理要求エリアには処理中断時の呼び出し先の指定も必要になりますね。 結局、内部で「待ち」が発生する多段階のサブルーチン呼び出しを実装するにはスタックから逃れられません。スタックを使わないのなら、多段階のサブルーチンを使う術はありません。 つまり、「待ち」が発生しない処理にキレイに分割できる時のみ、イベント駆動型は使えるのではないでしょうか? 私の場合、キレイに分割できることが事前に分かる案件を担当することがなかったので、私はスレッド方式を使うことがほとんどです。
mit0223

2016/10/30 13:44

すみません。少し時間をください。まだ、返信したいのですが、ちょっと考える時間が欲しいです。場合によっては来週とかになるかもです。
Chironian

2016/10/30 14:53

いえいえ、急ぐ必要は全くないですよ。 私も自分の考えを整理できて有用でした。
mit0223

2016/11/01 00:10

質問を編集しました。そこに記載しましたが、Javascript ではコールバック関数に続きの処理を記述することで、非同期でありながらサブルーチン化・モジュール化して記述することに成功しております。さらに es6 ではコールバック地獄を回避するための Promise という技法も新設されました。もう、充分にご意見は頂いておりますが、おてすきのおりに、感想などをいただけましたら幸いです。
Chironian

2016/11/02 14:43

> CPU依存が強い場合でもワーカプロセスに負荷分散することで、プログラム自身はシングルスレッド・協調的マルチタスクで書くことが可能だと思います。 ワーカプロセスとワーカスレッドの相違はメモリ空間が分離されているかいないかが主です。 前者は空間切り替えが発生するため、あまり密に排他制御すると効率が悪くなりますので疎結合で使います。密なやり取りをする時には空間切り替えのないワーカスレッドを用いると性能が改善します。 そしてワーカプロセスもスレッドを持っていますから、ワーカプロセスを使うということはマルチ・スレッドでもあります。 ワーカプロセスとのやり取りで共有メモリを使う場合、その共有メモリに関してはマルチ・スレッドの場合と同様な排他制御が必要になります。逆に、実際に使うかどうかは別ですが、プロセス間通信を使ってスレッド間でも通信できます。 Promiseの例は良く分かりませんでしたが、「普通の順次的記述」の方がメンテナンス性は高いと感じます。そして、マルチ・スレッドを使えばこのような記述を使いつつ複数の要求を処理できます。 更に、getValuesForZ()、processY()、processYnest()のような処理は直接main()の中に記述することもできます。1箇所からしか呼ばれない短い処理はサブルーチンにしない方がメンテナンス性は高いのでより好ましいです。 しかし、イベント駆動型でこれらを非同期に実行するためにはサブルーチンにせざる得ないでしょう。 こんなに簡単な例でこれだけ差が付くのでしたら、実プロジェクトで付く差は凄いことにならないでしょうか?
mit0223

2016/11/03 00:24

マルチスレッドのメリットは理解しているつもりです。しかし、c10kにおけるマルチスレッドの問題は、高性能なネットワーク環境とハードウェアがもたらす大量のリクエストのルーティングで、スレッドごとのメモリとコンテキストスイッチが性能ボトルネックになることです。 それで、リクエストのルーティングとリソースクリティカルな処理(DBMS、HPC系ワーカ)でスレッド/プロセスを分けることで回避するわけですが、一般のプログラマが主にコーディングするのは、ビジネスルールであり、リクエストのルーティングのほうに含まれるのではないでしょうか? DBMSを開発する人は当然マルチスレッドでプログラミングすべきですし、HPC系のワーカをコーディングする場合は、マルチスレッドのみならず、並列処理プログラミングが扱える必要があります。しかし、これらの仕事をするプログラマは少数ではないでしょうか。 というのが出発点です。 > こんなに簡単な例でこれだけ差が付くのでしたら、実プロジェクトで付く差は凄いことにならないでしょうか? 否定しません。といいますか、この質問の論点そのものであり、「この Promise ってどうなのよ!」っていうのが私の気持ちです。 Java Servlet 3.0 のようにリクエスト受付用非同期シングルスレッドとリソースクリティカルな処理を受け持つ同期的ワーカマルチスレッドで構成するのが良いかもしれません。 同じ協調的マルチタスクでも Goや erlang のように軽量スレッドで、プログラマから見るとマルチスレッド的に見えても性能が出るというのもいいかもしれません。(余談ですが、 Go は関数を呼び出すと自動的に yield がかかるらしい) しかし、Webアプリにおいては、クライアント側は Javascript しかなく(プログラマに選択権はない)、Webアプリのプログラマは、どのみち Promise 的な技を覚える必要があるわけです。で、今のところ、私は他人に聞かれたら、クライアント側と共通で使えるフレームワークであるイベント駆動型シングルスレッドプログラミングを覚えて、node.js でサーバ側のビジネスロジックもそれで書けば良いのでは?と答えています。
Chironian

2016/11/03 02:31 編集

> スレッドごとのメモリとコンテキストスイッチが性能ボトルネックになることです。 そのボトルネックをアプリケーション・プログラム開発工数を大きく増やして回避するのがベストなのかについては議論があると思います。アプリ開発に投資するより、ハードウェアやミドルウェアに投資してより高速なサーバやよりたくさんのサーバを導入した方が合理的と判断するマネージャも居ると思います。 エコではないですし、プログラマの仕事が減るので必ずしも嬉しい判断ではないですが、開発費の方が大きい場合は妥当な判断と思います。 > それで、リクエストのルーティングとリソースクリティカルな処理(DBMS、HPC系ワーカ)でスレッド/プロセスを分けることで回避するわけですが、一般のプログラマが主にコーディングするのは、ビジネスルールであり、リクエストのルーティングのほうに含まれるのではないでしょうか? マルチ・スレッドの場合、ビジネスルール部分を同期型でコーディングできますのでメンテナンスしやすいです。 上級のプログラマがマルチ・スレッド対応する部分を設計・開発し、プログラミング規約を定めておけば一般のプログラマはマルチ・スレッド設計から開放されます。 といいますか、従来はその部分はミドルウェアが担っていたのですよね? そのままで良いのでは? 見積もりには量が少なすぎますがPromiseのサンプルの場合は同期型に比べて4割もステップ数が増えているので4割近く開発費が増える可能性もあります。(直感的には妥当な印象です。) その分、ハードウェアに投資した方が安く済みそうな印象を受けます。
guest

0

質問:どっちが好きですか・どっちが勝つと思いますか

一旦個人の好き嫌いは脇におきます。

マルチスレッド/非協調的なプリエンティブ並行処理モデル と イベント駆動/協調的なノン・プリエンティブ並行処理モデル は勝ち負けではなく、両者を組み合わせて利用すべきものと考えます。また、一般論としてはマルチスレッド・モデルの方が、イベント駆動に比べてプログラミングが簡単になるはずです(私の経験則とも合致します)。

私は個人的にはイベント駆動型が好きで、性能問題でマルチスレッド派を駆逐して欲しいくらいに思っています。

性能問題にフォーカスした場合、(あなたの言うイベント駆動型=シングルスレッド・モデルであれば、)イベント駆動型はプロセッサのマルチコア化に追従できません。イベント駆動でもマルチプロセス・アプリケーションであればスケーラビリティを得られますが、そもそもマルチプロセスは非協調並行処理ですからマルチスレッドと組み合わせるのと同じことです。結局のところ、マルチプロセス化はイベント駆動と非協調処理を組み合わせているにすぎません。

JavaScript のコールバックは嫌いですか?lua などコルーチンの yield って懐かしくないですか?

上記の追加質問と、それまでの論点との関連性がイマイチ不明瞭ですが...


改訂後の質問内容へのコメント:

OSレベルでは多重入力はイベント駆動で実装されているわけで、そのイベント駆動をそのままコーディングできるシングルスレッドイベント駆動プログラミングって気持ち良くないですか?

JavaScript/Node.jsのように綺麗なイベント駆動モデルなら(それなりに)賛同できますが、OSレベルにより近いselectやepollによるイベント駆動処理は、とにかく面倒でプログラミングも苦痛です。このあたりはlibuv (Node.jsも同ライブラリ利用)のようなイベント駆動フレームワークが、低レイヤにおける暗部を覆い隠している恩恵と思います。

一方でマルチスレッドモデルとして挙げている例は、「微妙なタイミング判定」で確かに排他制御が必要なものの、本質的には「いつ割込判定を行うか」という点のみを問題視しているように見えました。イベント駆動フレームワークは、この判定タイミングを「イベントループ」という形で標準化しただけとも言えます。

そういう意味では、マルチスレッドで性能と柔軟性を優先する(設計・デバッグ難易度は上がる)か、イベント駆動で標準化された構造を好む(自由度は低くマルチコア非対応)かという話なのかもしれません。

マルチスレッドプログラミングって難しくないですか?

はい。非同期プログラミングは 本質的に 難解です。これはイベント駆動モデルであっても、複数I/Oを制御するような非同期処理メインでは似たような難しさがあると思います。イベント駆動をマルチスレッド処理と比べたときの利点は、非決定的要因(≒タイミングに依存した処理結果)が比較的少ないという点でしょうか。

投稿2016/10/29 16:11

編集2016/10/31 08:28
yohhoy

総合スコア6191

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mit0223

2016/10/30 00:25

ご指摘のとおりと思いました。回答いただいてからの質問の変更で恐縮ですが、プログラミングとしてのシングルスレッドイベント駆動型のプログラミングをどう思うかに変更させていただきました。 > 一般論としてはマルチスレッド・モデルの方が、イベント駆動に比べてプログラミングが簡単になるはずです(私の経験則とも合致します) とのことですので、この回答はマルチスレッドのプログラミングパラダイムに一票となりますでしょうか。
yohhoy

2016/10/31 06:22 編集

> この回答はマルチスレッドのプログラミングパラダイムに一票となりますでしょうか。 うーん。自分の考えとしては「絶対的な優劣比較ではなく、トレードオフ問題に過ぎないため、前提条件により答えは異なる」でしょうか。どっちが好きという話ならば、マルチスレッドに1票入れておきます。 シングルスレッド・イベント駆動型モデルにも一定のメリットがあると思います。(逆説的ですが)マルチスレッド処理ではないため非決定的動作な並行性バグに悩まされる確率が下がりますし、CPU boundではなくIO boundな処理をしたいならイベント駆動でも性能的に必要十分なケースはあるでしょう。
mit0223

2016/10/31 09:37

回答を編集いただきありがとうございます。大変参考になります。まだ、少し議論を続けたいのですが、その前に1点引っかかる部分がありまして、ご回答いただければと思います。 > マルチスレッドで性能と柔軟性を優先する 現在の化物みたいなCPUで、ソートや結合はDBMSで実行される前提で、アプリケーションサーバ上のビジネスロジックの演算がCPU依存になるようなアプリケーションは少ないのではないかと思いますが、いかがでしょうか?また、CPU依存の性能問題が出たとして、それがマルチコアでスケールすることで解決する(=ちょうど一桁くらい足りない)ようなケースも稀ではないかと思いますが、いかがでしょうか? teratail でも演算性能を重視するような質問をよく見かけるのですが、いつも疑問に思っています。
yohhoy

2016/10/31 09:59

対象とする分野に依存するとしか答えられませんね... いわゆる3階層システムな「業務アプリ」(という表現が適切かはわかりませんが)ではCPU boundなケースは少ないと思います。一方で、大量のデータをもとに"演算"するアプリケーション領域は確かに存在しており、そこではマルチコアによる水平スケーリング性能が非常に重要なファクタになります。程度問題はありますが、後者はHPC(High Performance Computing)寄りですね。
mit0223

2016/10/31 10:13

ありがとうございます。たしかに銀の弾丸はないとは思います。しかし、巷でよく見る性能問題がSQLのTips系とかコンテキストスイッチ多発系のものが多いにもかかわらず、まだCPUに性能を求めている人が多いなぁと思いまして・・・
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問