Skip to content

Instantly share code, notes, and snippets.

@hryk
Created August 3, 2010 11:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save hryk/506219 to your computer and use it in GitHub Desktop.
Save hryk/506219 to your computer and use it in GitHub Desktop.

マルチスレッドの魔法

要旨

この文書ではPieter HintjensとMartin Sustrikが並列アプリケーションの構築の難しさとそれがエンタープライズコンピューティングにおいて何を意味するか、ということについて解説します。

The authors argue that a lack of good tools for software designers means that neither chip vendors not large businesses will be able to fully benefit from more than 16 cores per CPU, let alone 64 or more.

まず著者らはチップベンダでもなく巨大なビジネスでもないソフトウェアエンジニアが、コア数が64以上はもちろん、16以上のコアが入ったCPUの恩恵を受けられるよいツールが不足している事を主張します。

They then examine an ideal solution, and explain how the 0MQ framework for concurrent software design is becoming this ideal solution. Finally they explain 0MQ's origins, and the team behind it.

そして理想的な解決方法の検討を行い、並列ソフトウェアのデザインのためのフレームワークである0MQがどのようにその理想的な解決法となったのかを解説します。最後に、0MQの起源及びチームに関して説明します。

マルチコアへ

数年前まで、並列プログラミングは高性能計算(HPC)と同義であり、マルチスレッディングはワードプロセッサがあなたが文書を編集している間に同時に文書のページ番号の振り直しを行う為にしていることだった。 マルチコアCPUは希少かつ高価であり、ハイエンドなサーバにしか入っていなかった。我々はスピードを得る為にシングルコアのクロックサイクルを挙げ続け、CPUは熱くなり続けていた。

今日ではマルチコアCPUは日用品になっている。クロック数は2-3ギガヘルツで安定しており、一つのチップが載せるコアの数は18から24ヶ月で倍になっている。ムーアの法則はまだ当てはまる。データセンターの中から外へと、マルチコアCPUの広がりは続いている。ネットブックやポータブルデバイスは2から4コアのCPUを持っているし、ハイエンドなサーバは64コアを持っている。この成長は無限に続いていくだろう。

いくつかの要因がこの進化を加速している。第一に、CPUベンダへの競争の要求がある。我々がその力を使えようと使えまいと、我々はより能力の高いCPUを買うのを好むだろう。第二に、クロックサイクルが頭打ちした事だ。CPUの設計者は彼らにより競争力をもたらし、彼らの設計をスケールさせる次の道をマルチコアに見いだしている。 三つ目に、ローエンドにおけるアンドロイドのようなマルチタスクOSの広がりによって、コア数の増加がすなわちパフォーマンスの増加につながるという事が挙げられる。 そして最後に、ハイエンドにおいて、ブレードコンピューターのスロットが非常に高価である為に(ある投資銀行によれば1年あたり50000ドルと見積もられている)ユーザがブレード当たりよりコア数が多いものを求めるようになっているという事だ。

ここ数年、未来が大規模なマルチコアの時代となる事は明白であったが、ソフトウェア工業は遅れをとっている。 "High Performance on Wall Street 2008"イベントでは、スピーカーは誰も彼も同じ事を言っていた。曰く、「我々のソフトウェアはハードウェアの進歩についていく事ができていない」と。

最も広く利用されているC, C++は並列化のサポートを提供していない。プログラマー達はスレッディングAPIを使って自分たちでなんとかしている。 並列処理をサポートしているJavaやPython, .Net, Rubyのような言語では、総当たり方式でやられている。 言語の実装に依存している。1ダースを超えるRubyのインタプリタがあり、例えば、"グリーンスレッド"を提供しているかもしれないし、あるいは本当のマルチスレッディングかもしれない。ロックに頼っているから、スケールしない。 Erlangのように新奇に振り切れた言語は正しくやっている。我々はこれが意味する所を後で詳しく見ることになるだろう。

この文章の最後に、全ての人がErlangを使うようにすればよいと言う事はできるが、それは現実的な解ではないだろう。多くの賢いツールがそうであるように、これは本当に頭のいい開発者だけが使いこなせるものだろう。しかし多くの開発者、これから益々マルチスレッドアプリケーションを書く機会が多くなる開発者は、平均的だ。

これから、伝統的な方法の何がいけなかったのか、Erlangの何が正しいのか、そして我々はこうした教訓をどのように全てのプログラミング言語に適用できたかについて話そう。全ての平均的なプログラマの為の並列性について。

痛みを伴う最新式

2008年10月、Microsoftは"マルチスレッド・プログラムにありがちな11の問題への対処法"という記事を発表した。「最新式の技術がいかに痛みを伴うか」を示したこの記事は、.NET Frameworkを利用した開発について述べたものだ。しかし実際のところ、一般的なプログラミング言語でマルチスレッド・プログラムを書く者は皆同じような問題を抱えている。

The article says, "correctly engineered concurrent code must live by an extra set of rules when compared to its sequential counterpart.". This is putting it mildly. The developer enters a minefield of processor code reordering, data atomicity, and worse. Let's look at what those "extra rules" are. Note the rafts of new terminology the poor developer has to learn.

この記事によれば「並列処理をきちんと書くには、逐次処理を書くのとは異なり、いくつかの制約を守る必要がある」。 これはかなり控え目な言い方で、「マルチスレッド・プログラムを書く」ということは、プロセッサによる演算順序の入れ替えや、データの原子性の維持、さらにはもっと恐ろしい罠が潜む地雷原に足を踏み入れるということだ。 ではここで、その「恐ろしい罠」がどんなものか見てみよう。 専門用語がたくさん出てくるので、知らないものはちゃんと調べること。

  • 同期忘れ: 複数のスレッドがひとつのデータを共有する場合、各スレッドは別のスレッドの処理に口出しをすることになる。このことは**「競合状態」**を引き起こす: 不可解な無限ループがはじまったり、フリーズしたり、データが化けたりする。これらはタイミングや負荷に依存して発生するため非決定性を帯びていて、デバッグはおろか再現することすら難しい。開発者はロックやセマフォを使うか、コードをクリティカル・セクションとするか、そういった対処に迫られることになる。そのため、共有データに安全にアクセスできるのは一度にひとつのスレッドだけ、ということになる。
  • 間違った粒度設定: 「共有メモリにアクセスするすべてのコードをクリティカル・セクションにする」という安易な対処だけでは、まだうまくいかない。クリティカル・セクションが大きすぎれば他のスレッドの動作を極度に遅くしてしまうし、小さすぎれば共有データの整合性を保つことができなくなる。
  • 過剰なメモリアクセス: 32bit/64bitの値の読み書きは大抵は原子性を保って行なわれるが、常にというわけではない。開発者はコードの中の何もかもをロックするかクリティカル・セクションにするかして回ることもできるものの、そうするとアプリケーションは遅くなる。効率の良いコードを書くには、システムのメモリ・モデルとコンパイラがそれをどう利用するかを学ぶ必要がある。
  • ロック・フリーと演算順序の入れ替え: 近年のマルチスレッド開発者はよく訓練されていて、コード内のロックの数を減らす方法を探すことに自信を持っている。こういうコードを「ロック・フリー」とか「省ロック」なコードと呼ぶ。だがコンパイラやCPUは、ひそかに命令の実行順序を入れ替えることができる。計算の効率を上げるために行なわれるこの措置は、命令は必ずしも一貫した順番どおりに実行されるとは限らないということを意味する。動作中のコードが急に中断されることもある。これに対する解決策は、"メモリバリア"を追加するか、プロセッサがメモリをどのように使うかをより深くまで学ぶことだ。
  • ロックの護送船団: あまりにも多くのスレッドが同じデータへのロックを要求すると、アプリケーション全体のパフォーマンスは低下し、ほとんど停止したような状態になってしまう場合がある。ロックという仕組みは――いくつものロックをコードに仕込んではじめてわかることだが――本質的にスケールしない。Just when we need things to work properly, they do not. ロックの回数を減らすか、「ホットなロック」を減らすようにコードを再構成する(つまり実際に発生するコンフリクトを減らす)、といったことのほかに現実的な対策はない。
  • 2ステップ・ダンス: waking状態とwating状態の間を移り変わることを繰り返すだけで、何の処理も実行しないスレッドのこと。シグナルの実装に依存して、さまざまな原因で発生する。我々開発者としては幸運なことに、どのような対処法も存在しない。これが原因でアプリケーションの動作が極度に遅くなったら、上司に「Nステップ・ダンスですね」といって肩をすくめるしかない。
  • 優先度の逆転: 低優先度のスレッドが高優先度のスレッドの動作を止めてしまうこと。Microsoftの記事によれば、「このお話の教訓は、スレッドの優先度の変更はできるだけ避ける、ということ」だ。

この一覧では、ロックの隠れたコストには言及していない―― コンテキスト・スイッチやキャッシュの有効性の確認がいかに劇的にパフォーマンスを低下させるかには。

Erlangを学ぶことがとても良いことのように思えてきたはずだ。

伝統的なアプローチのコスト

こうした神秘的な知識を学び、正しく使う事ができる開発者を見つける事の難しさはさておき、最新式の方法は他の展でもコストが高い。

  • そうしたコードを正しく書き、メンテナンスする事は著しく高価である。我々は自分たちの経験からマルチスレッドのコードを書いてメンテナンスする事が単一スレッドのコードを扱うときの少なくとも10倍、あるいは100倍以上コストがかかると見積もっている。

  • このアプローチでは少数のスレッド以上ではスケールしない。多くのマルチスレッドアプリケーションは2つ、もしくは3,4本のスレッドを使う。このことは16コアやそれ以上コアのあるCPUを最大限に使う事ができないという事を意味している。 その為、例えばワードプロセッサ用の並列性は得られるかもしれないが、スケーラビリティを得る事はできない。

  • コードがマルチスレッドになったからといって、無条件にマルチコアの恩恵にあずかれる訳ではない。各々のスレッドはお互いにブロックすることがよくある。そして開発者は自分の作ったかわいいマルチスレッドアプリケーションが本質的にはシングルスレッドで十分だったということに気づかない。 インテルのスレッドチェッカーのようなツールはこの事を調べる助けにはなるだろうが、そのアプリケーションをデザインし直す以外に実用的な解決方法はない。

  • もっとも良いケース、つまりアプリケーションが広範囲に渡ってロックを避けるように設計されている状態であっても、10コア以上スケールしないだろう。疎でありよく設計されていたとしても、ロックはスケールしない。より多くのスレッドとCPUが関係していても、それほど効果的ではない。見返りが少なければ少ない程特に難しい、という法則が適用される。

  • よりスケールする為には、私たちの開発者は100%ロックフリーなデータ共有アルゴリズムにスイッチするしかない。そして彼は黒魔術の領域に足を踏み入れる。彼はcompare-and-swap(CAS)のようなハードウェアインストラクションを駆使して、ロック無しでデータ構造のインスタンスを交換する。 そうしたコードは段違いに難しいし、(コードをメンテナンスする事は)より希少なスキルセットとなってしまう。1人の開発者を失えば、アプリケーションは終わりだ。

こうした事全てが山のようなコストを生み出す。確かに、我々は愛すべきマルチコアのマシンをデータセンターに設置することはできるだろう。しかし、開発チームにこうした方法を使ってコードを書けるかと訪ねた時、そして彼らが工業的な実践に従った時、その結果は恐ろしいものになるだろう。

CPU生産者にとって、このコストは厳しい上限だろう。客は増えた分のコアの力を取り出す事ができない為に、より新しいCPUを購入するのをやめるかもしれない。

さて、どうやって理想的な解決方がうまくいくか、それをJava, C, C++や.NETの現実世界に適用できるのかどうかを見ていこう。

理想的な解決法に向けて

0MQ (Zero MQ)フレームワーク

iMatixについて

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment