2011/04/30

Java排他処理のボトルネックの発見方法

Javaメソッドトレース”を自作すれば、処理時間が長いメソッドを絞り込むことができることを紹介しました。
今回は排他処理によるボトルネックを解析する方法を紹介します。

複数スレッドで同時に処理を行うとデータが壊れてしまうような場合に排他制御を行います。Javaでは排他制御をsynchronized節を用いて簡単に実現することができるため、マルチスレッドプログラミングが比較的簡単にできるわけですね。

しかし外注先で、64個のコア(CPU)を搭載したマシンを使って、64個のスレッドをフルに動かして大量処理を行うJavaアプリケーションの性能測定を行った結果、思うように性能が上がらず、CPUの使用率も60%程度に留まることが判り、問題視された経験があります。

“-verbose:gc”オプションを付けてもFullGCが走った形跡がなく、何が原因で性能低下が発生したのかを誰も実証することができませんでした。Javaメソッドトレースではメソッドの実行時間を測定することができても、CPU使用率の低下を実証することはできません。外注先はとうとう原因を究明することができず、私にヘルプを求めてきました。

私も少し苦しみましたが、“JDK 5.0 Documentation”と睨めっこしていたら、java.lang.managementにThreadMXBeanとThreadInfoがあり、スレッドの状態、スレッドのブロック時間、ロックのモニタ、そして、スタックトレースなどを取得することができそうではありませんか!早速、プロファイリングを行うツールを自作しました。

性能測定の対象となるJavaアプリケーションを“-Dcom.sun.management.jmxremote”などのオプションを付けてJMXエージェントを有効にさせれば、別マシンからプロファイリングを行うことができます。自作プロファイリングツールでは、ThreadMXBean.getAllThreadIds()で全スレッドのIDを取得し、ThreadMXBean.getThreadInfo(long)でThreadInfoオブジェクトを取得し、ログ出力を行うといった処理を、長時間、何回も繰り返して行いました。ThreadInfo#getThreadState()がThread.State.BLOCKEDを返す場合は、スタックトレースも採取しました。つまりBLOCKED状態となった箇所の統計を取ることにより、排他制御が発生しやすい箇所が判るわけです。

次に、排他制御が発生しやすい処理をスキップする(処理しない)ようにJavaアプリケーションを仮修正して、再度、性能測定を行ったところ、なんとCPU使用率が約60%→約95%に向上し、もちろん処理性能も著しく向上しました。

Java SE 5.0で追加されたJMX機能およびjava.lang.managementパッケージにあるMXBeanを使ってプロファイリングを行うのは、非常に有効な手段です。MXBeanを使ってアプリケーションのメモリ消費量を監視するツールも自作しましたが、これまた有効でした。ちなみにJDK 6以降では“-Dcom.sun.management.jmxremote”オプションを付けなくてもtools.jarにあるAPIを使って、プロファイリングツールから特定のJava VMにアタッチしてJMX通信を行うことができます。

またテスト用MBeanを自作して、外部ツールからそのMBeanと通信してJavaアプリケーションのテストを行うことも容易にできるようになります。アイデア次第ではJavaアプリケーションの可能性が広がりそうです。ぜひJMX、MBeanおよび、MXBeanに関心を持っていただきたいと思います。

2011/04/20

Javaメソッドトレース

ソフトウェアの二大ボトルネック ”で2種類のボトルネックがあることを述べました。

Javaアプリケーションについて前者のボトルネックを見つける1つの手法として、“メソッドトレース”があります。具体的には、メソッドの入口と出口に次のようなログ出力のコードを埋め込み、ログからメソッドの実行時間を知るという手法です。(ちなみに精密な性能測定を行う場合は、System.currentTimeMillis()よりもSystem.nanoTime()がお勧め)
System.out.println("a: start: "+System.nanoTime());
小さなアプリケーションならメソッドが少なく、上記コードをゴリゴリ手書きすればよいです。しかし巨大なアプリケーションになると手書きする箇所が膨大になり、現実的ではありません。

そのようなときには、Spring FrameworkなどにあるAOP(Aspect Oriented Programming)機能が役に立ちそうです。AOPを使ってバイトコードにログ出力のコードを埋め込ませ、メソッドトレースを実現するというものです。
 
しかし私はSpring Frameworkをまだ試したことがありません。が、次を使用してメソッドトレースを実現しました。
  1. Java SE 5.0で追加されたjava.lang.instrumentパッケージ
  2. Javassist
java.lang.instrumentパッケージは、平たく言うと次を提供してくれています。
  • アプリケーションのmainメソッドを呼ぶ前に、エージェントのpremainメソッドを呼ぶ仕組み
  • アプリケーションの実行に必要なクラスをJava VMが定義する前にクラスファイルを変換する機会
ClassFileTransformerの実装クラスを作って、premainメソッドを持つエージェントでInstrumentation#addTransformer()を使ってClassFileTransformerの実装クラスを登録するだけでよいのです。ところがクラスファイルを変換するAPIはJava SEには含まれていないのです。

そこで必要となるのが、バイトコードを変換するAPIであるjavassistです。バイトコードを変換するAPIとして、他にBCELもありますが、私はjavassistを選択しました。ClassFileTransformerの実装クラスからjavaassistを使ってバイトコードを変換し、ログを出力するコードを埋め込ませるだけです。具体的な作り方は記載しませんが、意外と簡単そうでしょう?

私はこうしてメソッドトレース機能を作り、さらに別途、メソッドトレース機能が出力するログを解析するプログラムも作って、処理時間が長いメソッドを特定できるようになり、巨大なJavaアプリケーションのボトルネック箇所をいくつか見つけることができました。

市販のプロファイラツールは高価だし、自分の思うようなデータを出力してくれない可能性もありますから、メソッドトレース機能を2、3日間で自作したのですが、このメソッドトレース機能は意外と応用範囲が広かったのです。ボトルネック箇所の特定だけでなく、メソッドの呼び出しシーケンス分析や、メソッド実行網羅率の採取などにも活用することができ、非常に便利なツールになっています。

2011/04/13

ソフトウェアの二大ボトルネック

ソフトウェアのボトルネックを調べる目的で、性能測定を行うことがありますが、ボトルネックには、大きく2種類があると考えています。

  • 不適切な処理ロジックによるボトルネック
  • 排他処理によるボトルネック
前者は、一般的に言われているような、狭い意味でのデータ構造やアルゴリズムの問題や、単純に不適切な順次処理をコーディングをした場合のボトルネックです。このケースでは、ボトルネック箇所が判明されば、比較的、修正が簡単です。

後者は、マルチスレッドで動作するソフトウェアにおいて、ハードウェアまたはソフトウェアによる排他処理の影響で、処理待ちが発生し、CPUパワーを十分に活用できないケースです。“アムダールの法則の紹介”に記載したアムダールの法則を使って、排他率(並列処理ができない部分の割合)を求めればよいかと思います。算出した排他率を見てソフトウェアの修正が必要かどうかを判断します。

どちらのボトルネックにしても、ボトルネック箇所を正確に見極めるのが難しい場合が多いですね。経験豊かな開発者なら経験に裏付けられたカンでボトルネック箇所をある程度絞れますが、どの箇所どのくらい性能劣化が起きているのかを知りたいこともあります。

Javaアプリケーションでボトルネック箇所を特定する市販製品が結構出回っていますが、Java SE 5.0以降なら市販製品を使わずにボトルネック箇所を特定するツールを比較的簡単に自作できます。後日、簡単にご紹介したいと思います。

2011/04/04

「ろう者のICT技術交流をやりたい!」のコメント返し

ろう者のICT技術交流をやりたい! 」についていくつかのコメントをTwitterでいただきました。
某団体との違いは?
某団体でできないのが残念!
そういうコメントをいただきました。某団体との活動に結び付けられたらいいという気も無きにしも非ずで、頭の片隅にはあります。ただ私自身、某団体の役員との関わりがほとんどなく、「某団体ありき」で話し合うことはちょっと難しい立場にあります。スミマセン。

「私の趣旨に同意する」とのコメントもいただいております。ところが今一度、自分の状況を振り返ってみると、社外のろう者との関わりがあまりにも少なすぎるのです。それではダメですね。人間はナマで会うのが交流の基本ですし、マジでリフレッシュもできますね。そういうのをもっと増やさなきゃイカンとも思っているのです。

私は今日までにソフトウェア開発の一翼を担ってきて、連日の辛い辛いオーバーワークにも耐え、軽いうつ病も経験し、なんとかそれなりに頑張ってまいりましたが、振り返ってみるともう少し社外活動にも取り組んでおけばよかったと思うことがあります。独学で専門性を培うだけでなく、同業種・異業種の人との交流を通して「あぁそんな考え方や見方もあるのか!」といった「気づき」を得ながら、業務以外の経験値をもっと増やしてよいのです。その点では私は大失敗を犯しました。人生をやり直したい気分にも駆られますです。でも、そんな悔いを持つのは私くらいかもしれませんが。

そういうわけで、ともあれ、みなさんとお会いしてちょっとICT限定で話し合ってみたいと、私の気持ちがくすぶっているのです(笑) 性急に事を進めようということはせず、みなさんの企画にも参加させていただきたいと思います。

よろしくお願いします。

アムダールの法則の紹介

はじめに

ソフトウェア開発において、性能測定を行うことがあります。
特にエンタープライズ(大企業)向けアプリケーションは、コア(CPU)数が多いマシン上で動作させることがよくあります。そこでコア数が1のときの処理性能を100としたとき、コア数2なら処理性能は2倍の200、コア数4なら処理性能は4倍の400となることを期待したいところですが、実際はそうなることはまずありません。たとえアプリケーションがマルチスレッドプログラミングを適切に実装していても、です。その主な理由を、次に列挙します。
  1. OS(オペレーティングシステム)や他のソフトウェアによる割り込み処理
  2. ハードウェア構造による排他処理(ファイルI/O、ネットワークI/Oなど)
  3. ソフトウェアによる排他処理(グローバル変数あるいはデータベースに対する排他処理など)
マシンではOSを含むさまざまなソフトウェアが動作し、これらのソフトウェアの処理にもコアを割り当てられます。つまり、該当アプリケーションの処理にコアが100%消費されるわけではありません。これが1です。

次に、たとえばハードディスクは同時に複数のデータを読み書きすることはできません。あるソフトウェアがハードディスクにアクセスしている間は、他のソフトウェアがハードディスクにアクセスしたくても待たされることになります。これが2です。

最後に、多くのソフトウェアはメモリ上あるいはデータベース上にデータを保持していて、必要に応じて更新したり参照したりします。そのとき、あるスレッド上でデータを更新している間は、他のスレッドはそのデータへのアクセスを待たなければなりません。さもないとデータに矛盾が起きるからです。これが3です。

ここでは、1~3の処理をひっくるめて「排他処理」とします。これらの排他処理のために、コア数を2倍、4倍に増やしても処理性能が2倍、4倍にならないわけです。これを計算式で示したものとして、アムダールの法則があります。

アムダールの法則

 アムダールの法則の詳細は、Wikipedia(英語/日本語)を参照してください。


十分なコア数を備えたマシン上で、排他処理の割合が20%のアプリケーションをスレッド多重度4で動作させた場合、スレッド多重度1の(並列処理がない)場合と比べて何倍の処理性能の向上が見込めるでしょうか?上のアムダールの法則の計算式を使って、次のように計算します。

= 1÷(0.2 + (1-0.2)÷4)
= 1÷(0.2 + 0.2)
= 1÷0.4
= 2.5
スレッド多重度を4倍にしても、処理性能が2.5倍しか向上しないことが判ります。Windows上で動作するアプリケーションの場合、マシンおよびアプリケーションの状況によりますが、タスクマネージャのCPU使用率が100%近くにならず、80%前後になっているかもしれません(Unix系マシンならiostatコマンドなどでCPU使用率を確認できる)。

アムダールの法則の「逆関数」を活用すれば、アプリケーションの排他率(並列処理ができない割合)を求めることができ、さらに、任意のスレッド多重度におけるアプリケーションの処理性能を予測することができるようになります。

2011/04/03

二大プログラミング言語

プログラミング言語には、いろいろな分類があります。
  • 低級言語(コンピュータに近い言語) vs 高級言語(人間が理解しやすい言語)
  • スクリプト言語(小規模向け) vs プログラミング言語(大規模向け)
  • インタープリト(interpret) vs コンパイル(compile)
高級 プログラミング言語は、大きく次の3つに分類されると考えています。
  • 構造化プログラミング言語(C、Pascal、Fortran、BASIC、Perlなど)
  • オブジェクト指向プログラミング言語(Java、C++など)
  • その他(COBOL、Lispなど)
構造化プログラミング言語に属する言語が最も多く「順次処理」「反復処理」「条件分岐」が構造化プログラミングの特徴とされています。これに加えて、オブジェクト指向の考え方を導入したものが、オブジェクト指向プログラミング言語になります。
プログラミングをやってみたいが、どの言語から始めたらよいかという問い合わせをよく見かけます。これには正解はないのですが、2つだけに絞るなら、C/C++言語とJava言語をお勧めします。なぜなら、これらのプログラミング言語のうち、C/C++とJavaは特別な存在だからです。

C/C++にはポインタという概念があり、ハードウェア寄りの知識が要求されます。また多くのOS(オペレーティングシステム)はCで開発されている関係でCから使用できるライブラリが多くあります。そのような性格を持つ構造化プログラミング言語は他にはあまりありません。Javaほどではないと云え、#ifdef~#endifマクロで複数OS対応のアプリケーションを開発できる場合も少なくありません。

次にJavaですが、プログラミング言語としての「Java言語」と、仮想マシンである「JavaVM」の2つの面を持っています。Java言語は分かりやすさ、メモリ管理の煩わしさを考慮し、多くの制約を科した言語仕様となりました。自由度が低く、マルチスレッドプログラミングを容易にした強みがあります。その反面、エンタープライズ向け(大企業向け)アプリケーションを開発するにあたっては、ガーベジコレクション、インタープリタとコンパイラの両刀使いであるJavaVMの知識も要求されます。Java言語の誕生時に「Write once, Run everywhere」と云うスローガンを掲げただけあって、複数OS対応のアプリケーションを容易に開発できるのも強みです。


一夜漬けの経験も許されるなら、私はC、Pascal、Fortran、BASIC、Perl、PHP、Ruby、Java、C++、COBOL、Lispプログラミング言語を経験済みです。でも、いくつの言語を知っているのかは、あまり重要ではありません。新しい物好きのソフトウェア開発者にしてみれば、C/C++とJavaは共にレガシーという見方もありますが、我々に多くのことを教えてくれる存在であることには間違いなかろうと思います。この2つの言語を出発点にし、他のさまざまな言語を学ぶことが比較的簡単にできると考えているからです。

時代はリッチクライアントサーバアプリケーション(Webアプリケーション)、さらにクラウドと流れていきますが、エンタープライズ向けアプリケーションの開発基盤としては、C/C++(.NET Framework)とJava(Java SE/Java EE)の2大勢力は当面続くのではないでしょうか。またAndroidの台頭もJavaの存在を後押ししていますね。

2011/04/02

ろう者のICT技術交流をやりたい!

個人的に「ソフトウェア開発者」のスキル向上の場が欲しいなと思っています。

ソフトウェア開発と言っても、
  • さまざまなプログラミング言語があり、
  • さまざまなソフトウェアがある
ろう者の人口が圧倒的に少ないのですが(マイノリティ)、特定のプログラミング言語にこだわるのではなく、また、幅広くソフトウェア開発に共通するような勉強会や技術交流の場を開催できないものか、というふうに考えております。

最近の世界は「モノがあふれている」状態です。
たとえば、東電が電気消費量をCSV形式で配布すると発表したとたん、多くの人が、電気消費量を表示する似たようなソフトウェアをどんどん作りました。そして、あなたはこれらのソフトウェアの中から、どれを選びますか?
そういう時代になりました。 つまり、特に対策を練らなければ、誰でも同じようなものを作っちゃうんですね。自分が作ったソフトウェアが選ばれるチャンスが少なくなるのです。
  • 短期間でどんな付加価値をソフトウェアに付けるのか、
  • ソフトウェアをどうやってみなさんに使ってもらえるのか
などを考えなければ、自分も企業も世間の荒波にもまれて、すぐ落ちぶれてしまいます。

そのため、ソフトウェア開発も外部からの刺激を受けて、お互いにフィードバッグし、お互いに切磋琢磨していかなければなりません。

人によっては、将来、何らかの理由で、ソフトウェア開発の作業から抜け、マネジメントやマーケティングなどに移るかもしれません。 その時点で、あなたにどのくらいの知識があるかどうかで、新しい仕事での可能性の幅がずいぶん違ってきます。 たとえば、ろくなプログラミング経験がない上司に「プログラミング技法が悪い」と指摘されても、部下は「プログラミングを知らないくせに」と心底で考え、上司についてきてくれません。

最後に、日本では、ICT技術者に要求されるスキルは、研究職や専門家に比べると低く見られがちですが、とんでもないです。非常に高いレベルの専門性と、幅広いスキルが要求されます。専門分野のスキルだけでなく、幅広いICTの知識もさることながら、プロジェクトを円滑に進めていくために、コミュニケーション能力問題解決能力リーダーシップといった人間性(人間力)も求められます。

ろう者のみなさんで勉強会や交流会の場を、「努力して」設けて、「努力して」継続させませんか?