ブロゴスフィアでは最近Scala対Erlangの議論がややヒートし続けている。マルチコアの世界(参考記事)が訪れようとしている今、いかにマルチコア危機を解決するかが問題になっている。ScalaとErlangはその解決法たらんとしている言語だが、少し違いがある。それぞれのアプローチの長所短所はなんだろうか?
問題
ムーアの法則はもう通用しない。かつてのようなクロック周波数の向上(リンク)は見込めず、かわりにコアの数を増やそうとしている。今の時代、あなたのノートPCでさえも2つコアを搭載していることだろう。
2つ以上のコアを有効に使うには、アプリケーションが並列処理を意識してないといけない。もしあなたの顧客が8コアのマシンを買ったとしたら、たとえそれをあるアプリケーションの専用マシンにしたとしても、そのアプリケーションがCPU性能の12%しか使えなくてもおかしくない、とあなたは顧客にどうにかして分かってもらわないといけない羽目になるだろう。
この事態は将来さらに悪くなるだろう。並列的でないコードは速く実行できないばかりか、むしろ実行するのが遅くなってしまうだろう(リンク)。なぜならコアが増えるにつれ、電力と発熱の理由から各コアの性能が下がるからだ。この数年でIntelは32コアを実現したが(リンク)、この流れだとあっという間に数千コアができてしまう可能性がある。しかし1つ1つのコアは今のコアに比べてかなり低速なものになるはずだ。
並列処理コード
この問題を解決するための分かりきった方法はソフトウェアを並列処理するように書く(あるいは書き直す)ことだ。そのための一番一般的な方法はスレッドを使うことだが、ほとんどの開発者はスレッドベースのアプリケーションを書くのはかなり大変なことだと考えている。デッドロック、スターベーション(優先度の低いプロセスがいつまでも待たされること)、競合状態などは並列処理を行う開発者の大半にとってあまりに慣れ親しんだ概念だ。この点でErlangと Scalaは多くの苦しみを取り除いてくれる。
ErlangとScalaの概要
Scala(リンク)は次の主要なJVM言語(参考記事)となるのではないかと見なされることがある。オブジェクト指向のパラダイムと関数型のパラダイムを併せ持ち、Javaに比べ簡潔な構文で、静的な型付けができ、Javaと同じくらいか時にはそれ以上の実行速度を備える(リンク)。Scalaに真剣に目を向けるべき理由はいくつもある(参考記事)。
Erlang(リンク)はロバストネス(耐障害性)を持つよう設計された言語だが、そのことでスケーリングに強い言語(参考記事・英語)となっている。ErlangはJavaより前に現れた言語だが、将来の並列処理言語(参考記事)として語られることが多い。この言語は動的な型付けを関数型言語で、連続稼動時間に関して優れた実績がある。
論点
ではScala対Erlangという議論の論点はいったい何なのだろうか?結局ところ、パフォーマンスとスケール性なのだが、論点となっているものにはスタイルや言語機能やライブラリの充実度なども含まれてる。この議論のきっかけは、故意ではないのだが、Ted Neward氏(リンク)がいくつもの言語に対して意見を述べ、その中で「悪いのはErlangがそれ自身のインタプリタ上で実行されることです」(リンク)と書いたことだった。
Steve Vinoski氏(リンク)とTed Neward氏は数回議論を行ったが、その議論はいくつかの別のブログに飛び火し、そこではScalaとErlangとの関心をひくような違いあるいは共通点が強調されている。今回はそれぞれの言語の長所と短所を示す興味深い点をかいつまんで挙げ、そしていくつかの問題について違った見方をしてみようと思う。
信頼性
Steve Vinoski氏は、Ted Neward氏がEarlangが自身のインタプリタ上で実行されてるという見解を示した(リンク)記事に対してこう反応している。
自身のインタプリタで実行されるのは良いことです。そうでなければ信頼性はなくなるでしょうし、別の好奇心を誘うでしょうが、Erlangは使い道のない並列指向の実験的言語となってしまうでしょう。
Steve Vinoski氏が述べているのは、言語自体が信頼性をもっているのなら、それを成り立せるすべてのものも信頼性をもつようでないとけない、ということだ。 Erlangは始めから信頼性とそれに関連して並列性のために設計されたため、並列性が関与してきた時、特に基盤となるライブラリが並列環境でもきちんと振舞う必要がある時にも困ることはない。
一方ScalaはJVMの上で動き、その重要な売り込み要素の一つは既存のJavaコードをすべて扱うことができるということだ。しかしながら多くのJavaコードは並列処理向けにはなっていなく、このことをScalaのコードでは考慮する必要がある。
軽量プロセス
並列性のあるアプリケーションを大規模に動かすには、多くの並行処理の実行が必要になる。これにはいろいろな方法があるが、一般的な方法としては、複数のスレッドを使うのとプロセスを使うのがある。この2つの違いは、スレッドがスレッド間でメモリを共有するのに対し、プロセスは何も共有しないところだ。そのためスレッドはミューテックスのように2つのスレッドが同じメモリを同時に操作しないようにするロッキングのメカニズムが必要になる。プロセスではこの問題に困ることはなく、他のプロセスとのコミュニケーションを行うためにメッセージを送るといったことを行う。ただプロセスの方が大抵はパフォーマンスとメモリ使用の面でコストがかかり、そのために人々はスレッドベースの並列を選ぶことが多い。たとえプログラミングモデルがややこしくなってもだ。
Steve Vinoski氏はこう書いている。
軽量で何も共有しないプロセスを使えるアーキテクチャで設計するとずっと簡単に大規模な並列を行えるようになります。しかしこれはそのように設計してしまえば、あとは単純にプログラミングの問題だけ、となることではありません。
Erlangは並列処理のためにこの方法を取っている。Erlangのスレッドは非常に軽量で、Erlangアプリケーションは大抵万単位かそれ以上のプロセスを走らせている。
一方Scalaは同じような軽量プロセスをイベントベースのアクタと呼ばれるもの実現している。Yariv Sadan氏(リンク)はこう説明している。
Scala には2つのタイプのアクタがあります。スレッドベースのアクタとイベントベースのアクタです。スレッドベースのアクタは重いOSのスレッドによって実行されます。お互いにブロックをすることはないのですが、ひとつのVMにつき数千のアクタ以上にスケールさせることができません。イベントベースのアクタはシンプルなオブジェクトです。これはとても軽く、新しいマシンでならErlangのプロセスのように数百万のアクタを生成することができます。
それでもある違いがあるとYariv Sadan氏は説明する。
Erlangのプロセスとの違いは、各OSのスレッド内で、イベントベースのアクタがプリエンプティブ(OSが優先度の高い実行可能なタスクへ処理を切り替える)スケジューリングなしに実行されることです。このことによりイベントベースのアクタは自身を動かしているOSスレッドを長い間(おそらく無期限に)ブロックすることができます。
不変性
Erlang は関数型言語だ。このことはJavaの文字列のようにデータが不変であり、副作用のおそれがないことを意味している。データにどのような操作をおこなっても、変更の結果としての別のデータが作られ、元のデータはそのままだ。不変性は他のデータが依存しているデータを誤って変更してしまうことがないため、ロバストネスを備えるための重要な要素とされるが、並列性の観点からも重要な特性だ。もしデータが不変であれば2つの並列処理によって変更されるということは起き得ず、変更が起きないため他のマシンへコピーでき、同期を取る必要もない。
ScalaはJVMの上に作られていて、またオブジェクト指向と関数型のアプローチを結合させているため、純粋な関数型言語のような不変性の保障がない。しかしYariv氏のコメント欄ではYariv Sadan氏とDavid Pollack氏(リンク)によって2つの言語の興味深い違いについて興味深い議論が行われていた。David Pollack氏はScalaのウェブフレームワークであるLiftの作者(参考記事)で、不変性についての彼の考え方を次のように表している。
不変性 ー Erlangはこれを実現し、これについて他にすることはほとんどありません。しかし唯一の型を守らせるScalaの驚くべき強力な型付けシステムのもつ他の利点を失うことになります。私の場合Scalaのアクタを不変なデータを使ってコーディングして、型に関することはScalaの型付けシステムで対処しています。
Yariv Sadan氏はこう問う。
不変な型だけを送るようにするのは大きな制限ではないでしょうか?たとえばシンプルなビーンをHibernateから読み込んで、それを他のアクタに送るようなことはできないのではないですか?
David Pollack氏はこう答える。
私はScalaのアクタベースの商用システムを多く作ってきました。不変性の問題を扱うのは実際にはあまり手がかかりません。入れ物となるクラス(メッセージクラス)を不変であるように定義して、それを送ればいいのです。
型付けシステム
Erlang は動的に型付けが行われる。Scalaは静的に型付けされ、Javaよりも強力な型付けシステムを持つ。しかしJavaとScalaを比べた時の大きな違いのひとつは型インターフェースがあることだ。これにより数多くの型アノテーションを除いてコードをクリーンにすることができ、それでいながらコンパイラは全てをチェックすることができる。
動的型付けシステムと静的型付けシステムの長所と短所については議論が果てしなく続きがちだが、これはErlangとScalaの顕著な相違点だ。
末尾再帰(Tail Recursion)またはループ
Yariv Sadan氏はこう述べている。
関数型プログラミングと再帰は密接に関連しています。実際Erlangをプログラムする上で末尾再帰を使わないでいられることはほとんどないでしょう。それはErlangがループを持たないからで、Erlangは全てのことに対して再帰を使っています(私はこれが良いことだと信じています :) )。
これはErlangがScalaと異なる大きく異なる点に間違いない。Scalaは反復に関してずっと伝統的なスタイルを取っている。しかしDavid Pollack氏は次の意味で末尾再帰が利点を持っているとは考えていない。
末尾再帰 ー イベントベースのアクタでは関係ありません。
この場合、設定の仕方と採用するスタイルが問題になるのだ。
ホットスワッピングコード
Erlangは信頼性をもつよう設計されているため、ホットスワッピングコード(実行中にコードを置換する)(リンク)の機能が最初から搭載されている。
JVMもホットスワッピングコードについていくらかサポートをおこなっていて、クラスは変更可能だ。しかし静的な型付けシステムのためメソッドの追加や名前変更はできず、メソッドの中身を変えることしかできない。これに関して、サードパーティのツール(リンク)や実行中のシステムでクラスをスワッピングするのを容易にするためのフレームワーク(リンク)があるが、Scalaの場合はアクタの作り方によって、JVMの実行中にもスワッピングを行える。Jonas Bonér氏はそれをどのようにすればいいかサンプルを使って説明している(リンク)。
まとめ
ScalaとErlangはいずれもマルチコア危機をターゲットにした言語だ。いずれも違ったバックグランドと時間に現れ、そのためにいくつかの問題について異なるアプローチを取るが、多くの点で違いより共通していることが多い。少なくとも並列性の問題に関してはそうだ。
Erlangは登場してから約20年たっていて、多くのクリティカルな現実のシステムでその力を証明している。Erlangの不利な点のひとつは、少しばかり閉鎖的で、最近の多言語プログラミングのトレンド(参考記事)においてもErlangのコミュニティがあまり反応を示してないことだ。
一方Scalaはマルチコア向けのアプリケーションにとっては新顔だ。実際のScalaアプリケーションはまさに世に出ようとしているところで、Scala に将来を賭ける企業もある。ScalaがErlangと比べ一番有利なのは、JVM上で動き、既存のJavaコード全てやフレームワークや多くのツールをそのまま使えることだ。同時に、JavaコードはScalaのアクタモデルに最適化されてるわけではないので、Scalaの力を発揮するには重荷となる。
両方の言語とも、メインストリームの言語ではディベロッパをあまり救えないような緊急かつ増大中の問題を解決するための似たような方法を提供している。今回の議論をまとめた記事を読んだ後、読者それぞれの状況ではどちらの言語がより詳しく調べてみる必要があるかを読者が知る助けになっていればと願う。
未来はマルチコアの世界だ。ScalaとErlangがよりメジャーになる可能性は十分ある。
原文はこちらです: