レベル: 中級 高橋 賢司 (woody@jp.ibm.com), ICPアドバイザリーITアーキテクト, 日本アイ・ビー・エム株式会社
2008年 9月 24日 Rubyを使ってXMLデータを扱おうとした時、私たちは幾つかの選択肢からその方法を選ぶことができますが、実用面から考えた時にはパフォーマンス面での性能が利用可否に大きく影響します。本稿では特にREXMLとLibXml-Rubyの2つのライブラリーを取り上げ、XMLデータに対して処理を行った際のパフォーマンスについて確認します。その結果から、それぞれのライブラリーがどのようなシーンに向いているかについて考察します。
Rubyを使ってXMLデータを扱おうとした時、私たちは幾つかの選択肢からその方法を選ぶことができます。それぞれのライブラリーには特徴がありますが、実用面から考えた時にはパフォーマンス面での性能が利用可否に大きく影響します。本稿では主要なライブラリーとして特にREXMLとLibXml-Rubyの2つに注目して、それらのパフォーマンスについて考察します。
一般的には、DOMを使うよりもストリーミング処理を行った方がパフォーマンスに優れると言われています。また、REXMLよりもLibXml-Rubyの方がパフォーマンスに優れるといわれ、簡単なベンチマークの結果がLibXml-Rubyのホームページにも紹介されています。
本稿では実際にXMLデータに対して処理を行い、パフォーマンス面でこの2つのライブラリーにどの程度の違いがあるかについて確認し、それぞれを利用する上でのスイートスポットを探したいと思います。処理を行うXMLデータには、現実的なデータとしてXBRLを利用します。
Ruby向けXMLライブラリー
RubyでXMLを扱う方法の一つはRubyの標準添付ライブラリーに含まれるREXMLを使う方法です。REXMLはRuby 1.8からのフィーチャーで、添付ライブラリー故にインストールなどを気にしなくていいというメリットが有ります。また、REXMLはpure Rubyで作られているという特徴があります。REXMLには、XMLデータを扱うために次のようなAPIが用意されています。
- ツリー・ベースのAPI(DOM)
- SAXライクなストリームAPI
- SAX2ベースのAPI
- プル・パーサーAPI
また、REXML以外に外部ライブラリーを利用することができます。そのようなライブラリーの一つにLibXml-Rubyがあります。LibXml-RubyはCで作られているGNOME LibXml2 XMLツールキットのRubyバインディングです。LibXml-Rubyには、XMLデータを扱うために次のようなAPIが用意されています。
- ツリー・ベースのAPI(DOM)
- Reader(プル・パーサー)API
- SAX API
REXMLのインストール
REXMLはRuby 1.8以降で標準添付ライブラリーとして配布されています。RubyをインストールするだけでOKです。
LibXml-Rubyのインストール
LibXml-RubyはRubyGemsを利用してインストールすることができます。必要に応じてRubyGemsのupdateを行います。特にRuby One-Click Installerを利用している場合には、この作業が必要です。
リスト1. RubyGmesのアップデート
それからLibXml-Rubyのインストールを行います。
リスト2. LibXml-Rubyのインストール
パフォーマンス測定を行った処理
今回、REXMLとLibXml-Rubyでパフォーマンスを比較するに当たり、現実的なXMLデータとしてXBRLデータを用い、次のような処理を含むプログラムについて処理時間を測定しました。
- 処理1:XBRLインスタンスのXMLデータを読み取り、{ノード名->値}のハッシュに読み取る
- 処理2:XBRLインスタンスに対応するXMLスキーマと、各ノードのラベル名を定義するXMLファイルを読み取り、XBRLインスタンスの各ノード名とラベル名のマッチングを行い、{ノード名->ラベル名}のハッシュに読み取る
処理1は1つのXML文書のみを扱い、比較的簡単な処理のみを行うという特徴があります。
処理2は複数のXML文書を扱い、XML文書の中の複数のデータのマッチングを行うなど、処理1に比べ、より多くのXML文書を扱い、処理内容も複雑という特徴があります。
対象とするデータは東京証券取引所により提供されている決算短信のサンプル・データ、および、U.S. GAAPの2008年度のタクソノミーのサンプルとして提供されているデータを利用しました。
また、REXMLとLibXml-Rubyのそれぞれのライブラリーについて、XML文書をツリーとして扱うDOMと、ストリーム処理を行う方法とで処理時間を比較しました。ストリーム処理には大きく分けてプッシュ型とプル型の2種類があります。プッシュ型はパーサーがXML文書を解析していく中で開始タグの出現や終了タグの出現をそれぞれイベントとして扱い、リスナー・プログラム中でそれぞれのイベントに応じた処理を行うものです。プル型はXML文書の読み込みをプログラム内で制御し、それぞれのノードに応じた処理を行うものです。LibXml-Rubyでは、機能の豊富さとプログラミングのしやすさからプル型のLibXML::XML::Readerを用いました。一方で、REXMLではPullパーサーは現時点APIドキュメント上に“This API is experimental, and subject to change.”と記載されており、まだ開発途上であるため、プッシュ型のSAX2をストリーミング・パーサーとして利用しました。DOMとストリーム処理の特徴および長所、短所は表 1に示すとおりです。
表1. DOMとストリーミング・パーサーの特徴
| DOM | ストリーム処理 |
|---|
| 特徴 | XMLデータをツリー構造でメモリーに展開して扱う | XMLデータの各行を順に読み込み処理を行う |
|---|
| 長所 |
- XMLツリー内を自在に移動できる
- XPathが利用できる
- XML文書の作成が容易
|
- 各行単独で処理が完結するような場合には、シンプルなプログラムを書くことができる
- メモリー消費が比較的少ない
- パフォーマンスに優れる
|
|---|
| 短所 |
|
- XMLツリー内を自在に移動するような処理ができない
- XML文書の作成には向かない
|
|---|
パフォーマンス測定結果
上述の処理1および処理2における、各パーサーが要した処理時間を表2、表3に示します。なお、これらの測定は著者のノートPC上で実施しています。テストした環境は次の通りです。
- OS : Windows XP
- CPU : Core2Duo T7300 2.0GHz
- Memory : 2GB
- Ruby : Ruby 1.8.6
- LibXml-Ruby : 1.1.3
表2. 処理1の処理時間
| XMLライブラリー | REXML | libXML |
|---|
| パーサー | DOM | SAX2 | DOM | Reader |
|---|
| 処理時間(東証)[s] | 0.65 | 0.94 | 0.07 | 0.05 |
|---|
| 処理時間(U.S. GAAP)[s] | 0.69 | 1.06 | 0.07 | 0.06 |
|---|
まず処理1(表2) について見てみましょう。単独のXML文書を扱い、必要な要素のデータを単に上から順に読み出すような処理において、DOMとストリーミング・パーサーを比較した場合、REXML、LibXMLいずれの場合もそれほど大きな違いは出ていません。特にREXMLにおいてはDOMよりむしろSAX2の方が時間が掛かっています。これは、今回の測定した時間の中にリスナーの生成時間も含めたためと考えられます。また、REXMLとLibXMLを比較すると、LibXMLの方がDOMにおいておよそ1/10、ストリーミング・パーサーにおいておよそ1/20の処理時間で済んでいます。たいていの業務においてはREXMLにより提供されるパフォーマンスで十分かもしれませんが、それにしてもLibXMLの方が高速に処理を実行できることが読み取れます。
表3. 処理2の処理時間
| XMLライブラリー | REXML | libXML |
|---|
| パーサー | DOM | SAX2 | DOM | Reader |
|---|
| 処理時間(東証)[s] | 894.35 | 35.73 | 26.16 | 1.53 |
|---|
| 処理時間(U.S. GAAP)[s] | 4750.89 | 73.02 | 94.24 | 2.81 |
|---|
続いて、処理2(表3)を見てみましょう。処理1同様にDOMとストリーミング・パーサーで比較すると、いずれのXMLライブラリーでもストリーミング・パーサーの方が大幅に短時間で処理を行うことができています。これらの例では、DOMの方が17~65倍の時間が掛かる結果となっています。また、REXMLとLibXMLで比較すると、DOMでは30~50倍程度、ストリーミング・パーサーでは25倍程度REXMLの方が処理時間が掛かっています。このようにより多くのXML文書を一連の処理の中で扱う必要があり、ここのXML文書の中でもより複雑な処理を行うことにすると、顕著に処理時間の差が現れてきます。
表4. それぞれのパーサーのメモリー使用量
| XMLライブラリー | REXML | libXML |
|---|
| パーサー | DOM | SAX2 | DOM | Reader |
|---|
| 最大メモリー使用(東証) [MB] | 109 | 9 | 31 | 12 |
|---|
| 最大メモリー使用(U.S. GAAP)[MB] | 275 | 17 | 71 | 17 |
|---|
さらに、この時のメモリーの使用量を表4から確認してみましょう。ストリーミング・パーサーに比べてDOMの方が3~10倍と大幅に大きなメモリーを必要とすることが分かります。また、DOMを利用する場合でもLibXMLに比べてREXMLの方がより多くのメモリーを必要としています。
まとめ
以上のことから、REXMLとLibXMLそれぞれが向いているシチュエーションをまとめると次のようになります。
REXML:
- 簡単にXMLデータを扱う処理を試してみたい時
- 一度に扱うXMLデータの数が少ない時
- XMLデータに対する処理が簡単な時
LibXML:
- 少しでもパフォーマンスを良くしたい時
- 一度に複数のXMLデータを扱う時
- XMLデータに対して複雑な処理が必要な時
また、今回の一連の作業で同様の処理を行うプログラムについてREXMLとLibXml-Rubyの間、およびDOMとReaderまたはSAX2の間でポーティングを行っています。そこから、DOM同士であればREXMLからLibXmlへの移行は思ったより簡単にできることが分かりました。またDOMからReaderへの移行も、ちょっとの手間はありますがそれほど難しくありませんでした。SAX2については、イベント処理型のコーディングとなるため、それなりにプログラムを書き換える必要がありました。
参考文献 学ぶために
製品や技術を入手するために
著者について  | |  | 高橋賢司は日本IBM入社後、都銀のお客様へのシステム構築、データベース関連製品の提案時技術支援を経て、現在、ソフトウェアITアーキテクトとしてお客様のシステム・アーキテクチャー構築をソフトウェアの観点からお手伝いしています。DB2のpureXMLフィーチャー・リリース後、DB2におけるXMLの利用について検討を続けており、最近はRubyを用いてその検討を深めています。 |
記事の評価
|