質問編集履歴

4

シングルスレッドプログラミングでの順次的記述方法を追加+マルチコアに対する考察の追加

2016/10/31 23:58

投稿

mit0223
mit0223

スコア3401

test CHANGED
File without changes
test CHANGED
@@ -118,9 +118,205 @@
118
118
 
119
119
 
120
120
 
121
- * 補足:マルチコアについて
122
-
123
- 一つは、それほど性能がCPUに依存するアプリが少ないというのがありますが、CPU依存が強い場合でも複数のシングルスレッドプロセスにロードバランサなどで負荷分散することで、プログラム自身はシングルスレッド・協調的マルチタスクで書くことが可能だと思います。
121
+ ####補足1:マルチコアについて
122
+
123
+ それほど性能がCPUに依存するアプリが少ないのではないかは思いますが、CPU依存が強い場合でもワーカプロセスに負荷分散することで、プログラム自身はシングルスレッド・協調的マルチタスクで書くことが可能だと思います。特に並列演算で問題が解決するような場合は、ワーカプロセス側で GPUをつかったり、SIMD命令を使うなど、マルチコア以上に並列度をあげる工夫が必要ではないかと考えます。この場合は、マルチスレッドプログラミングというよりも並列処理プログラミングになってくると思います。となると、やはりマルチスレッドプログラミングは不要なのではないかと思えてきます。
124
+
125
+
126
+
127
+ ####補足2:イベント駆動型プログラミングの順次的記述
128
+
129
+ 回答でイベント駆動型プログラミングのデメリットとしてサブルーチンのネスト構造などモジュール化がやりにくいという指摘がありました。 Javascript では、サブルーチンの呼び出し後の継続処理をコールバック関数(というかクロージャ)に記述するという技法で回避し、非同期でありながらモジュール化を行うことに成功しています。しかし、この技法はコールバック地獄と呼ばれており、モジュール化には成功しましたが、順次処理を記述すると、ネストがどんどん深くなるという欠点がありました。次世代 Javascript である es6 では、この問題を Promise という技法でさらに回避しております。
130
+
131
+ ここで、普通に順次的に記述したプログラムと Promiseを使って非同期処理可能でありながら順次的に記述したプログラムを比較してみます。
132
+
133
+
134
+
135
+ 普通の順次的記述
136
+
137
+ ```Javascript
138
+
139
+ function main() {
140
+
141
+ let x = 'default value of x';
142
+
143
+ let y = 'default value of y';
144
+
145
+ let z;
146
+
147
+ try {
148
+
149
+ z = getValuesForZ(x);
150
+
151
+ z = z.map(item => processItem(item));
152
+
153
+ if (z.includes(processItem(x))) {
154
+
155
+ y = processY(y);
156
+
157
+ }
158
+
159
+ console.log("x = " + x + ", y= " + y + ", z= " + z);
160
+
161
+ } catch (e) {
162
+
163
+ console.log(e);
164
+
165
+ }
166
+
167
+ }
168
+
169
+
170
+
171
+ function getValuesForZ(arg)
172
+
173
+ {
174
+
175
+ return ['value1', 'value2'].concat(arg);
176
+
177
+ }
178
+
179
+
180
+
181
+ function processItem(item) {
182
+
183
+ return 'processed:' + item;
184
+
185
+ }
186
+
187
+
188
+
189
+ function processY(y) {
190
+
191
+ return processYnest(y);
192
+
193
+ }
194
+
195
+
196
+
197
+ function processYnest(y) {
198
+
199
+ return 'processed in nested function:' + y;
200
+
201
+ }
202
+
203
+
204
+
205
+ main()
206
+
207
+ ```
208
+
209
+
210
+
211
+ Promise を使った非同期処理可能な順次的記述
212
+
213
+ ```Javascript
214
+
215
+ function main() {
216
+
217
+ Promise.resolve({
218
+
219
+ x: 'default value of x',
220
+
221
+ y: 'default value of y'
222
+
223
+ })
224
+
225
+ .then(getValuesForZ())
226
+
227
+ .then(state => {
228
+
229
+ return Promise.all(state.z.map(item => Promise.resolve(item).then(processItem())))
230
+
231
+ .then(z => Object.assign(state, {z}));
232
+
233
+ })
234
+
235
+ .then(state => {
236
+
237
+ return Promise.resolve(state.x).then(processItem()).then(r => {
238
+
239
+ if (state.z.includes(r)) {
240
+
241
+ return Promise.resolve(state).then(processY());
242
+
243
+ }
244
+
245
+ return state;
246
+
247
+ });
248
+
249
+ })
250
+
251
+ .then(state => {
252
+
253
+ console.log("x = " + state.x + ", y= " + state.y + ", z= " + state.z);
254
+
255
+ })
256
+
257
+ .catch(e => {
258
+
259
+ console.log(e);
260
+
261
+ });
262
+
263
+ }
264
+
265
+
266
+
267
+ function getValuesForZ()
268
+
269
+ {
270
+
271
+ return state => Object.assign(state, {z:['value1', 'value2'].concat(state.x)});
272
+
273
+ }
274
+
275
+
276
+
277
+ function processItem() {
278
+
279
+ return item => 'processed:' + item;
280
+
281
+ }
282
+
283
+
284
+
285
+ function processY() {
286
+
287
+ return state => {
288
+
289
+ return Promise.resolve(state.y)
290
+
291
+ .then(processYnest())
292
+
293
+ .then(y => Object.assign(state,{y}));
294
+
295
+ };
296
+
297
+ }
298
+
299
+
300
+
301
+ function processYnest() {
302
+
303
+ return y => 'processed in nested function:' + y;
304
+
305
+ }
306
+
307
+
308
+
309
+ main()
310
+
311
+ ```
312
+
313
+
314
+
315
+ 普通版は 34行に対して、Promise 版は48行と行数は増えてしまいましたが、処理順にプログラムを書くことができて、かつ、サブルーチンによるモジュール化もできていると思います。
316
+
317
+ モジュール化のみそは、サブルーチンが演算結果を返すのではなく、演算を行う関数を返すことです。このようにすることで、呼び出し側は非同期にその関数を呼び出すことができるわけです。
318
+
319
+ プログラムの詳細な説明は避けますが、順次処理、条件判定、繰り返し処理、サブルーチンのネスト呼び出しを含める例となっています。プログラムの処理内容には何の意味もありません。また、両方を比較しやすいように非同期処理は入っていませんが、Promise版の方ではいくらでも非同期処理を挿入できますが、普通版では非同期処理を挿入することは不可能です。
124
320
 
125
321
 
126
322
 

3

論点をシングルスレッドプログラミングに変更

2016/10/31 23:58

投稿

mit0223
mit0223

スコア3401

test CHANGED
File without changes
test CHANGED
@@ -28,17 +28,99 @@
28
28
 
29
29
  ####協調的マルチタスク派
30
30
 
31
- 私は個人的にはイベント駆動型が好きで、性能問題でマルチスレッド派を駆逐して欲しいくらいに思っています。そもそも、長時間の処理に入るのに、何時間かかるか予想もつかず、中断もできなようなプログラムの存在が良くなと思います。すべてのプログラムはそユースケースに応じて、時間かかりすぎる場合には中断できるとか、かかる時間を予測して途中経過を報告する機能が具備されてるべきだと思ます。それで、そのような機能を実装しようすると、スレッドで割り込みをにしなら順次的にくよりも自分で状態遷移を管理するほうが簡単ではないでょうか。
31
+ いくつか回答ただいて、自分好み問題とC10K問題直結してい気が付き、き直ました)
32
+
33
+ 私は個人的にはシングルスレッドイベント駆動型でコーディングするのが好きで、c10K問題を理由にマルチスレッドプログラミングのパラダイムを駆逐して欲しいくらいに思っています。たとえば、DBへの問い合わせが長い時間かかる場合に、ユーザからの要求やシステムのシャットダウンに対応して問い合わせを中断できるようにする場合、マルチスレッドで割り込みを気にしながら順次的に書くよりも自分で状態遷移を管理するほうが簡単ではないでしょうか。マルチスレッドの場合、
34
+
35
+ (DBへの問い合わせを中止できるようなドライバがあるかどうかはおいておいて)
32
36
 
33
37
 
34
38
 
35
- ####質問:どっちが好きですか・どっちが勝つと思いますか
39
+ ```javascript
36
40
 
37
- JavaScript のコールバックは嫌いですか? lua などコルーチンの yield って懐かしくないですか?
41
+ class 問い合わせ {
38
42
 
39
- みなさんはどちらが好きですか?どちらが勝つと予想しますか?
43
+ do() {
40
44
 
45
+ try {
46
+
47
+ DBへの問い合わせ
48
+
49
+ } catch (CancelSignal cs) {
50
+
51
+ if (微妙なタイミング判定) {
52
+
53
+ 問い合わせ中止処理
54
+
55
+ }
56
+
57
+ }
58
+
59
+ }
60
+
61
+ onCancel() {
62
+
63
+ 問い合わせ実行中スレッドへ CancelSignal を送信
64
+
65
+ }
66
+
67
+ }
68
+
69
+ ```
70
+
71
+
72
+
73
+ となり、問い合わせの終了とキャンセルシグナルの入力との微妙なタイミング判定(排他制御)が必要ですが、シングルスレッドであれば、
74
+
75
+
76
+
77
+ ```javascript
78
+
79
+ class 問い合わせ {
80
+
81
+ do() {
82
+
83
+ DBへの問い合わせ((event) => this.handler(event));
84
+
85
+ }
86
+
87
+ handler (event) {
88
+
89
+ ...
90
+
91
+ }
92
+
93
+ onCancel() {
94
+
95
+ 問い合わせ中止処理
96
+
97
+ }
98
+
99
+ }
100
+
101
+ ```
102
+
103
+
104
+
105
+ と書けて、シングルスレッドなので、handler 呼び出しと onCancel の呼び出しは早い者勝ちとなるわけです。
106
+
107
+
108
+
109
+ そもそも、OSレベルでは多重入力はイベント駆動で実装されているわけで、そのイベント駆動をそのままコーディングできるシングルスレッドイベント駆動プログラミングって気持ち良くないですか?
110
+
111
+
112
+
41
- そもそもこの題提起自身正しいと思いますか?
113
+ ####質:シングルスレッドプログラミングは好きですか?残ると思いますか?
114
+
115
+ いくつか回答いただいて、シングルスレッドプログラミングとC10K問題が直結していないことに気が付きましたが、質問としてはC10K問題で復権したシングルスレッドプログラミングをどう思うかというポイントに変更して続けさせていただきたいと思います。
116
+
117
+ JavaScript のコールバックは嫌いですか? lua などコルーチンの yield って懐かしくないですか?マルチスレッドプログラミングって難しくないですか?
118
+
119
+
120
+
121
+ * 補足:マルチコアについて
122
+
123
+ 一つは、それほど性能がCPUに依存するアプリが少ないというのがありますが、CPU依存が強い場合でも複数のシングルスレッドプロセスにロードバランサなどで負荷分散することで、プログラム自身はシングルスレッド・協調的マルチタスクで書くことが可能だと思います。
42
124
 
43
125
 
44
126
 

2

typo修正

2016/10/30 00:18

投稿

mit0223
mit0223

スコア3401

test CHANGED
File without changes
test CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  前述の昔話において、パソコンのOSがプリエンプティブマルチタスクに置き換えられるときも、OSの実装者は「プリエンプティブマルチタスクなんて必要ない。プログラマが時間のかかる処理を実装するときに処理の途中経過を報告したり、中断したりする機能をちゃんと入れてれば、協調的マルチタスクのほうが効率が高いに決まっている」と執拗に粘ったのですが、世の中の流行には逆らえず(というよりも質の悪いプログラムの多さに負けて)、負けてしまいました。
24
24
 
25
- 今回もプリエンプティブマルチタスク(マルチスレッド派)対協調的マルチタスク(イベント駆動型やコルーチン派)の戦いが始まっているように思います。これを第二次プリンティブ戦争と呼びましょう。
25
+ 今回もプリエンプティブマルチタスク(マルチスレッド派)対協調的マルチタスク(イベント駆動型やコルーチン派)の戦いが始まっているように思います。これを第二次プリティブ戦争と呼びましょう。
26
26
 
27
27
 
28
28
 

1

スレッド/リクエストによるc10k についての情報を追加

2016/10/29 11:15

投稿

mit0223
mit0223

スコア3401

test CHANGED
File without changes
test CHANGED
@@ -18,7 +18,7 @@
18
18
 
19
19
  ####また戦いが
20
20
 
21
- その状況の中で、最近のWebでの議論を見ていると、Linux のマルチスレッドが非常に軽量・高速で協調的マルチタスクじゃなくてもマルチスレッドで十分性能が出るというベンチマーク結果が出てきているようです。そうなると、協調的マルチタスクの処理系はまたなくなってしまうのでしょうか。
21
+ その状況の中で、最近のWebでの議論を見ていると、Linux のマルチスレッドが非常に軽量・高速で協調的マルチタスクじゃなくてもマルチスレッドで十分性能が出るというベンチマーク結果が出てきているようです(※1)。そうなると、協調的マルチタスクの処理系はまたなくなってしまうのでしょうか。
22
22
 
23
23
  前述の昔話において、パソコンのOSがプリエンプティブマルチタスクに置き換えられるときも、OSの実装者は「プリエンプティブマルチタスクなんて必要ない。プログラマが時間のかかる処理を実装するときに処理の途中経過を報告したり、中断したりする機能をちゃんと入れてれば、協調的マルチタスクのほうが効率が高いに決まっている」と執拗に粘ったのですが、世の中の流行には逆らえず(というよりも質の悪いプログラムの多さに負けて)、負けてしまいました。
24
24
 
@@ -39,3 +39,13 @@
39
39
  みなさんはどちらが好きですか?どちらが勝つと予想しますか?
40
40
 
41
41
  そもそもこの問題提起自身正しいと思いますか?
42
+
43
+
44
+
45
+ ※1の参考:
46
+
47
+ [TCP/IP - Solving the C10K with the thread per client approach](http://stackoverflow.com/questions/17593699/tcp-ip-solving-the-c10k-with-the-thread-per-client-approach)
48
+
49
+ [「サーバ書くなら epoll 使うべき」は、今でも正しいのか](http://developer.cybozu.co.jp/archives/kazuho/2009/09/epoll-bac0.html)
50
+
51
+ いずれも、協調的マルチタスクに比べれば性能は出ないと思いますが、「協調的マルチタスクがないと1万コネクションはできないのかというとそうではない」という主張だと思います。桁が上がって10万コネクションになれば、やはり協調的マルチタスクが必要なのかもしれません。