Hatena::Groupdann

dann's blog このページをアンテナに追加 RSSフィード

Fork me on GitHub

2011-11-28

Javaの標準Collectionフレームワーク代替としてのfastutil/HPPCの使用のすすめ

Javaの標準Collectionフレームワーク代替としてのfastutil/HPPCの使用のすすめ  - dann's blog を含むブックマーク はてなブックマーク - Javaの標準Collectionフレームワーク代替としてのfastutil/HPPCの使用のすすめ  - dann's blog Javaの標準Collectionフレームワーク代替としてのfastutil/HPPCの使用のすすめ  - dann's blog のブックマークコメント

Goな人が最適化しているエントリ(http://blog.golang.org/2011/06/profiling-go-programs.html

)を読んで、ちょっと面白いなと思ったので、元の論文を読んでみました。読んでみたところ、Java 64bit版は標準のC++の5.8倍遅いとなっていたので本当かな?と思い、元のプログラムを見てみました。

http://research.google.com/pubs/pub37122.html

プログラムをざっと見て、オリジナルのJavaの標準のコレクションフレームワークの使用時の罠にはまっていることに気づいたので、以下のチューニングを施してみました。

  • -XX:+UseCompressedOopsオプションを使っても、Integerなどのオブジェクトはプリミティブ型の数倍のメモリ消費量を使ってしまうことから、整数オブジェクトの配列ではなく、プリミティブ型専用のコレクションを使うこと
  • 内部のストレージにダイレクトにアクセスが可能なオーバーヘッドの少ないコレクションを使うこと
  • Iteratorでのアクセスが遅いことから、拡張for文を使うのではなくリストにダイレクトにアクセスすること

たった数十行の修正で、C++のバージョンよりも約2割ほど早くすることができました。また、このコードは、オリジナルのmulti-language-benchmarkプロジェクトのJavaでの最適化バージョン(java-pro)のものよりも1割程度高速です。測定環境(CPU, Memory, Javaのversionなどなど)により性能値は変わるので、測定可能なコードを以下においておきました。

https://github.com/dann/java-multi-language-benchmark

ここで使ったのはfastutilなんですが、fastutil/HPPCは、Javaのコレクションフレームワークに近い(一部互換の)APIを持つ、上記のことを実現するのに適したコレクションフレームワークで、比較的簡単に標準コレクションフレームワークを使ったコードの置き換えが可能です。上記のような理由で、Javaのコレクションフレームワーク使用時に性能が出せない場合にはとてもお勧めできます。

また、Javaで性能を出すためのプログラムをどのように書けばよいのかという点で、とても学ぶことの多いソースであるので、アプリケーションとしてのJavaのコードを読み飽きた方にも非常におすすめです。何故Javaのコレクションフレームワークが遅いのかをJDKのコード、メモリの使用量などを調べながら読み比べてみると面白いです。

# YourKitでプロファイルした感じだと、もう少しチューニングできそうなので、暇な時にもう少しやってみようかと思ってます。

2011-11-19

PerlプロジェクトでテストカバレッジのCIをする方法

PerlプロジェクトでテストカバレッジのCIをする方法 - dann's blog を含むブックマーク はてなブックマーク - PerlプロジェクトでテストカバレッジのCIをする方法 - dann's blog PerlプロジェクトでテストカバレッジのCIをする方法 - dann's blog のブックマークコメント

前回から大分時間が経ってしまいましたが、今回はPerlのテストカバレッジ結果のサマリをJenkinsにIntegrationする方法について説明します。

f:id:dann:20111119141038j:image

概要

以前、proveのJUnit用のFormatterを使ってテスト結果をJUnit形式のXMLに変換する方法を紹介しましたが(http://dann.g.hatena.ne.jp/dann/20100404/p1)、カバレッジについても、Java用のプラグインの形式にフォーマットを変換して使うことで、Jenkinsに簡単に統合することができます。

Java界隈ではCloverというテストカバレッジ用ツールがあり、Jenkins用にプラグインが用意されています。一方、PerlではDevel::Coverという有名なテストカバレッジ取得用ツールをがあります。要するに、Devel::Coverの出力結果を、Clover用のXMLに変換できればいいわけです。

Devel::CoverにはReport機能が用意されており、それによって出力方法を変更することが可能になっています。これは、proveのformatterの機能と同等のものになります。テスト結果を、proveのJUnit形式のformatterでJenkinsに統合したように、Devel::CoverもReport機能で出力形式をClover形式にあわせることで統合できます。

Jenkinsへの統合方法

既にCPANには、Devel::Cover::Report::CloverというClover用のレポートモジュールが用意されているので、これを使えばよいだけです。

build stepを追加して、以下のようなコマンドを追加します。coverコマンドの後のreportオポションがポイントです。

perl Makefile.PL
cover -delete
HARNESS_PERL_SWITCHES=-MDevel::Cover=+ignore,inc prove -lv t
cover -report clover

これにより、プロジェクトのトップページにテストカバレッジのサマリが表示されるようになります。そして、各ビルドには各ファイル毎のカバレッジ詳細が見えます。

f:id:dann:20111119140852j:image

これに加えて、Devel::CoverのHTMLも出力して詳細を確認できるようにしておくとよいでしょう。カバレッジが極端に低い場合には、サマリだけでなく詳細を確認していきたくなるからです。以前、やり方を紹介してますので、ご興味あれば参考にしてみてください。

http://dann.g.hatena.ne.jp/dann/20100331/p1

テストカバレッジ取得のCIの重要性

品質は、人が作り込むもので、毎日の積み重ねが重要です。それを実現するためには、品質が高まっていることが目に見えるということがモチベーションを高める上で大事になります。従って、CIによりテストカバレッジのサマリがプロジェクトのトップページから簡単にみれるというのは重要なわけです。

高いテストカバレッジになっていることで、リファクタリングの安心度があがり、プロダクトの改善を進めやすくなります。これにより、プロダクト規模が大きくなってきた場合にスピードを維持しながら機能改善・リファクタリングを行っていくことが可能になります。結果として、高いカバレッジが効果として実感できるようになり、よりカバレッジが高まっていくというよいサイクルを生み、結果として品質を高めていきます。

このようにテストカバレッジをCIに組み込むことで、高いカバレッジを維持しながら楽しいリファクタリングライフを送ることが可能になります。皆さんもJenkinsさんのアメリカンな笑顔を見ながら、テストカバレッジを楽しんでみるのはいかがでしょうか。

Enjoy!

See also

2011-10-30

大規模(AP/DB) サーバーでのLarge Pageの使用のすすめ

大規模(AP/DB) サーバーでのLarge Pageの使用のすすめ  - dann's blog を含むブックマーク はてなブックマーク - 大規模(AP/DB) サーバーでのLarge Pageの使用のすすめ  - dann's blog 大規模(AP/DB) サーバーでのLarge Pageの使用のすすめ  - dann's blog のブックマークコメント

近年はメモリも安くなり、性能を向上させるために多量のメモリを使うことで性能を維持するのは、ある程度の大規模環境であれば一般的なことではないかと思います。特に、JVMOracleなどで多量のメモリを使うアプリケーションでは、ヒープサイズが数Gb-数十Gbだったり、buffer cacheのサイズが数十Gbだったりといったことも多くなっているのではないかと思います。

このような場合には、Large Pageの使用により、以下の3点の理由から性能が向上するケースがあります。

  • TLBのキャッシュヒット率の向上
  • メモリアロケーションの高速化
  • TLBミスヒットした際のアドレス変換の高速化

1ページのサイズが512倍と大きくなるため、TLBでカバーされる範囲が大きくなるため、キャッシュヒット率が向上します。ミスヒット時のペナルティは大きいので、これは一つ大きな効果があります。

また、1pageあたり4kで管理されていたメモリが、HugePageにより1pageあたり2mbになるわけで、メモリ容量をアロケーションする回数は1/512になるので、メモリアロケーションが高速になります。

さらに、TLBミスヒットした際に、2Mbの場合にページ階層が4->3になるため、アドレス変換も高速になるというわけです。

Large Pageの使用は、I/O性能の問題は解決できていて、問題がCPU/メモリに移っているケースでないとあまり効果は見られません。非常に早いディスクを使っているケースやほぼ全ての処理がメモリ上で行われるケースでは大きな効果が出ます。例えば、JVMではGC回数/時間の削減という形で非常に効果が見えるので、x64環境でGCに困っている方は試してみられることをお勧めします。

Large Pageを使用するための設定方法

仮に6Gb程度のヒープサイズを確保したとしましょう。

この場合、/etc/sysctl.confを変更します。

次のように設定します。

8Gb shared memory segment sizeを確保すると、1024 * 1024 * 1024 * 8 = 8589934592 のbyte数が必要なので、

kernel.shmmax = 8589934592

と設定します

また、hugetlbが使えるように、groupのIDを設定します。

java, mysql, oracleなどのユーザーのgroup idを次のように設定します。

vm.hugetlb_shm_group = <group id>

次にlarge pageのページ数を設定します。確保するメモリは6Gbなので、次のように計算します。

6Gb/ 2Mb = 3072 pages

設定は次のようにして設定します

vm.nr_hugepages = 3072

最後に物理メモリにpinします

javauser       soft  memlock     6291456
javauser       hard  memlock     6291456

最後にsysctl -p で設定を反映して、cat /proc/meminfo でHugePage数が設定できているかを確認して、3072が設定できていれば完了です。このページ数はサンプルですので、ページ数は実際の規模にあわせて設定してください。

Javaで使う場合には、上記に加えて、 XX:+UseLargePagesをJVMオプションに加えます。

Oracleなどで使う場合には、初期化パラメータにuse_large_pages=onlyを設定して使います。

また、この設定により、HugePageがRAMにPINされるため、ページアウトが起きなくなります。非常に大きなSGAサイズを確保しており、OSでのpage out回数の問題で性能が劣化するような大規模なトランザクションを扱っているケースなどでも効果があります。

2011-10-23

大規模DBサーバーでのInfiniBandの利用のすすめ

大規模DBサーバーでのInfiniBandの利用のすすめ  - dann's blog を含むブックマーク はてなブックマーク - 大規模DBサーバーでのInfiniBandの利用のすすめ  - dann's blog 大規模DBサーバーでのInfiniBandの利用のすすめ  - dann's blog のブックマークコメント

大規模なトランザクションが求められるシステムでは、ストレージには一定以上の投資をしたほうが全体の運用コストは下がります。アーキテクチャをシンプルに出来、かつ台数を少なくできることでオペレーションコストが劇的に下がるからです。

従来はHDDを中心にしたストレージサブシステムが利用されていたため、ネットワークの帯域やレイテンシがDBのレイヤで問題になるほどの性能が求められるハードを調達できるケースは少なかったのですが、FusionI/OをはじめとするEnterpriseレベルのSSDの登場でネットワークのレイテンシ/帯域を考慮する意味のある場面が出だしています。これは、ストレージレイヤでの問題が解消されることで、ネットワークおよびCPUにボトルネックが移ってきているためです。

このような場合、Infinibandは10Gbps Ethernetとも比較してレイテンシが低いため、非常に高いトランザクション量が求められるDatabase Serverには適しています。

この低いレイテンシの低さは、次の2点の機構で実現されています。

1点目は、InfiniBandのRDSをUDPソケットとして使用する機能を用いていることです。これにより、例えば、Oracleでは以下のようにTCP/IPのレイヤをバイパスしています。

 Oracle/RDS (User Mode) -> RDS(Kernel Mode)                          -> HCA
 Oracle/UDS(User Mode) -> (TCP -> IP -> IPoIB (Kernel Mode)) -> HCA

2点目は、RDS のゼロコピー実装である ZDP(Zero-loss Zero-copy Datagram Protocol)を使うことで、ブロックの不要なコピーを削減していることです。

OSのシステムバッファーへのデー タコピーを抑え、カーネルの仕事量を減らすことでCPU使用率を下げ、ネットワークを効率的に利用できるようにしているわけです。

このようなプロトコルのオーバーヘッドの削減とメモリコピー回数の削減により、非常に低いネットワークのレイテンシを実現しています。このように、Infinibandの低レイテンシを活かすことにより、ネットワーク のレイテンシおよび帯域幅が問題となるアプリケーションでは大幅に性能を向上することが可能になります。

特に非常に大規模なトランザクションが求められるシステムでは、アプリケーションだけの改善では十分な効果を得られないことがあり、ハードウェア、ミドルウェア、アプリケーションまでを含めてトータルな改善が必要になります。

Infinibandとそれに対応したミドルウェアの利用はそのような場合の一つの解になります。特にストレージサブシステムの進化が目覚ましい昨今では、利用により効果がみられるシーンも増えてきており、大規模トランザクションに困っているケースには十分検討の価値があります。

2011-08-15

OracleでSQLのParse回数を軽減するための方法

OracleでSQLのParse回数を軽減するための方法  - dann's blog を含むブックマーク はてなブックマーク - OracleでSQLのParse回数を軽減するための方法  - dann's blog OracleでSQLのParse回数を軽減するための方法  - dann's blog のブックマークコメント

OracleではSQLの実行手順は、次の3フェーズから構成されます。

  • Parse
  • Execute
  • Fetch

その中でParse処理は、次のような手順で行われます。

  • セッションにキャッシュされたカーソルが存在しない場合
    • カーソルをオープン
      • 共有プール上に共有カーソルがある場合
        • soft parse
      • 共有プール上に共有カーソルが無い場合
        • Hard Parse
  • セッションにキャッシュされたカーソルが存在する場合
    • そのカーソルを利用

特にCPU負荷の高いHard Parseの処理では

  • SQLの文法的チェック
  • SQLの意味的チェック
  • 実行計画生成
  • 解析済みSQLの共有プールへのキャッシュ

という処理をします。処理を見ればわかるように、この処理が毎回走ってしまってはとてもCPUコストの高い処理になってしまいます。

そのため、共有プールに既に解析済みSQL情報があれば、Hard ParseはパスしてSoft Parseをし、共有プールから解析済みSQL情報を取得します。セッションにキャッシュされたカーソルがあれば、その部分についてはSoft Parseもパスできます。

従って、Parse回数の削減のためには、共有プールサイズに解析結果を乗せてHard Parseが発生しないようにし、Soft Parseになるようにするようにすればいいわけです。それに加えて、Session Cursor CacheでSoft ParseもさせないSofter Soft Parseをするようにすることで共有プールのチェックもしないようにすれば、よりCPUには優しくなります。

Parseについては、OracleAPの話と両方あるのでまとめると、SQLのパース処理は次のように削減することができます。

  • Oracle
    • ハードパースの削減
      • 共有プールにキャッシュ
    • ソフトパースの削減
      • Session Cursor Cacheの利用
  • AP
    • Prepared Statemenc CacheRDBMS側のPrepare作業をさせないようにすることでParse回数を削減する

このような方法で、SQLのParse回数を削減することで、CPU負荷は確実に削減できます。ここでのパース回数の削減結果は、StatspackのInstance Efficiency Sectionで確認できます。I/O性能に余裕があり高負荷の場合には、CPU側がネックになるということもあり、少しでもOracleCPU Timeを削減したいということがあります。このチューニングは容易に出来るものでもあるので、あらかじめやっておくのがいいでしょう。

ここからは余談ですが、この話を書いていた時に、DeNAのHandler Socketでは、このSQLのパース処理がコスト高ということで、パース処理をショートカットすることでQPSを高めるということをやっているという話を思い出しました。Prepated Statementなどでキャッシュできる場合を考慮すると、Handler Socketでパース処理コストを削減と書いているのは、正確には上記で書いたパース処理の範囲より広範囲なものをさしていると推測されますが、SQL関連処理をばっさり切ってしまっていることでCPU Timeの削減を図るというのは面白いなと思いました。KVS的に使う場合には、SQLをほぼ捨てることで、このように過激に性能改善をする方法もあるのは面白いと思ったのと、ここらあたりの仕組みを外からプラガブルに組み込めるmysqlは非常に素晴らしいなと思ったのでした。

# 追記: Handler Socketの存在意義について

なんで、そんな何回もHard Parseする必要があるのだろうと思ってたんですが、sh2さんのコメントをみてわかりました。「MySQLには共有プールなんてないしPrepared Statementをキャッシュしても実行計画は都度生成する、など考えるとHandlerSocketの存在意義がよく分かる」と。

Persistent Connection + Prepared Statementを使いにくいという事情があるのはConsumer系ではよくある話なので、それで難しい部分はあるのかなとは思ってました。ただ、共有プールもないとすると、そもそもMySQLでは毎回Hard Parseするしかないということなんだなあと。これはちょっといけてないですね。MySQLも、ここらはまだこれからって感じなのかな。

2011-08-14

DevOpsに優しいSQLの管理方法

DevOpsに優しいSQLの管理方法  - dann's blog を含むブックマーク はてなブックマーク - DevOpsに優しいSQLの管理方法  - dann's blog DevOpsに優しいSQLの管理方法  - dann's blog のブックマークコメント

性能チューニングは、Hardware, OS, Middlewareに対する理解があるかないかで大分かわってくるところがありますが、Hardware, OS, Middlewareといったインフラに詳しいだけでは、踏み込んだチューニングは行えません。それにも関わらず、AP開発と運用は比較的分離されていることが多いため、DevとOpsが協力できるような体制を築くことが必要になります。最近はDevOpsという流れもあり、インフラに詳しいDevOpsを担う人がチューニングを行うケースもよくあるのではないかと思います。ここでは、インフラ側に軸足を置く開発者をDevOpsという用語で書いていくことにします。

DevOps側からみたときに、DBアクセスが中心のAPの場合、テーブル構造、データのライフサイクル、実際のSQLSQLの実行計画を見れば大概のことはわかるので、SQLを中心にした管理にすることで多くの性能チューニングが可能になります。従って、SQLを中心にした管理が重要になります。そのためには、以下の3点が重要になります。

  • SQLを中心にしたDBアクセスラッパ/ORマッパ を使用する
  • SQLには管理コメントをつけ、どこで使われる物なのかがわかるようにする
  • ER図/CRUD図を用意し、SQLでのデータの使われ方がわかるようにする

SQLを中心にしたDBアクセスラッパ/ORマッパ を使用する

よくあるのは、ORマッパを使用しているが、どこでそのSQLが生成されているのかを特定するのに時間がかかるというケースではないかと思います。さらに、場所が特定できた場合にも、特定のSQLをORマッパでどのように生成ればいいのかというのを、ORマッパが生成するSQLを確認しながらデバッグをするという本末転倒な状況が割とおきがちです。これは割と色々なところで見られるよくあるORマッパ使用のアンチパターンなケースです。

従って、どの部分でどのSQLが発行されうるのかを、誰が見ても一目瞭然にしておく必要があります。そのためには、SQLを一元管理でき、発行されるSQLを管理ができるようにすることが重要です。これを実現するためには、色々な選択肢がありえますが、大体以下の3つの選択肢または組み合わせに集約されるのではないかと思います。

  • 外部ファイルにSQLを書いてSQLの一元管理をできるORマッパ/DBアクセスラッパを使う
  • ストアドプロシージャを使う
  • シンプルなSQLはORマッパにまかせて、その他のクエリはORマッパから直接SQLの発行をできるようにする

SQLに管理コメントをつける

どこが発行したものなのかをわかるように規則をもうけて、SQL文そのものにコメントを埋め込みます。

こうすることで、発行されたSQLの箇所がわかるようになるため、StatspackなどでSQLを見た時にどの部位が発行した物なのかがわかるようになります。こうしておくと、問題が起きた時にはDevOps側がStatspackなどのレポートを見てどの部位で問題が起きたかがわかるようになるため原因を追及することが出来、DevOps側はAPのドメインの言葉でAP開発者とコミュニケーションがとれるようになります。

ER図/CRUD図を用意し、データの使われ方がわかるようにする

データのライフサイクルがわかるようにすると、どこでどのようにデータが使われるかが分かり、APの分析が容易になります。ER図はリバースしておこすこともできますが、CRUD図はSQLとテーブル定義を突き合わせる必要があるため、文書としてあったほうがDevとOpsでのコミュニケーションはしやすく、文書として残しておくのがおすすめです。(トランザクション量の分析も事前に出来るとよいですが、それは統計データからも分析可能なので、無くても性能分析のためのコミュニケーションは成立します。)

まとめ

DevとOpsが共同で性能改善に取り組んでいくためには、SQLを中心にした管理が重要です。また、AP開発者とDevOpsの連携を促進するために、SQL管理を容易にするためのSQLの管理コメントの導入のすすめSQLの使われ方を示すためのER図/CRUD図の作成 をすることにより、AP開発者とDevOpsのコミュニケーションを容易にすることが重要であるということになります。

OracleのSQLのチューニングに使うツール

OracleのSQLのチューニングに使うツール  - dann's blog を含むブックマーク はてなブックマーク - OracleのSQLのチューニングに使うツール  - dann's blog OracleのSQLのチューニングに使うツール  - dann's blog のブックマークコメント

Oracleでは性能に関連するデータが色々と取れるため、性能問題の解決がかなり容易になっています。OracleSQLのチューニングによく使うツールと使い方のポイントをまとめてみました。

性能状況の統計的分析 (Statspack)

問題箇所の全体に占める割合によって性能改善の効果が決まるため、測定されたデータから統計的に性能比率を分析し、チューニングすべき箇所を見つけるのは、SQLレベルでのチューニングではなくてもよくやることだと思います。このために使うのはStatspackです。

ある期間における統計をとれるので、全体の中から問題になるSQLを発見することができます。Level7でStatspackを取得すれば大体チューニングに必要な情報が取れます。Standard EditionでもEnterprise Editionでも利用できるため、よく使われているツールです。

では早速、Statspackをセクション別に簡単にポイントを説明していきます。

  • Load Profile
    • Hard Parseが多発してないか、Physical Readが多発してIOPS不足になってないか、生成されるREDOサイズなど基礎的な状況の確認などをします
  • Instance Efficiency
    • バッファキャッシュのヒット率が低くないか、Hard Parse回数が多くないかなど
    • Load ProfileやInstance Efficencyのセクションで問題になるケースは、殆どのケースでDBA不在といっても過言ではないです。
    • 大概がサーバーパラメータファイルの設定で解決できるので、このレベルでは少し知っている人がみれば解決できることが殆どで、通常はあまり問題にはなりません
  • Top 5 Timed Events
    • どの待機イベントがTopか
    • CPU Timeが一番上に来ているか(CPUを使えているか)、上位の待機イベントが何か(何で待ってしまっているか?)などを見ます
    • CPU Timeが長過ぎたり、待機イベントが長過ぎたりするという場合は問題になります。待機イベントの合計時間である程度の問題の推測ができます。CPU Timeについては、SQL orderedのセクションをみないと詳細はわからないので、CPU Timeが長過ぎる場合にはSQL orderedを見て問題を見つけていきます
  • SQL ordered(SQL ordered by CPU Time, Elapsed Time, Executionsなど)
    • CPU時間がかかりすぎているもの, Elapsed Timeが長すぎるもの(待機イベントが殆ど)、Executionsが多すぎるもの(実行回数が多いもの)はここを見ればわかります。全体の中でどれだけのCPU Timeを占めているかなどの割合がわかるので、どこを改善すればどの程度性能向上の余地があるのかがわかるという点で、何をチューニングする必要があるのかがわかるので、とても貴重です。
    • CPU TimeやElapsed Timeが長い場合
      • CPU実行時間やElapsed Timeが長いSQLについては、Old Hash Valueの値を元にStatspack SQLレポートでSQLの実行計画を確認します。実行計画に問題がある場合には、SQLだけでなく統計情報の確認もします。
    • Executionsで実行回数が多い場合
      • 1回あたりのCPU Timeは短いけれど、実行回数が多く性能が劣化しているケースはよくあります。良くあるのはN+1問題などで、無駄なSQLの発行回数が増えてしまっていることはあり、そのような問題を見つけやすいとはいえます。
  • Segments
    • どの表、どの索引の負荷が問題になっているかがわかります。

このように、Statspackによる統計データは、問題の全体像、問題箇所、問題の割合を明確にできるという点でとても貴重なレポートです。しかし、Statspackで問題の大体の推測は出来ても、ある一時点での問題や複数の待機イベントの時間が突出して問題が混在している場合などには、Statspackでは完全に問題の原因を突き止めるのは難しいケースがあります。

ある時点での状況分析 (EM or ASH Viewer)

Statspackは統計データによる分析ですが、問題は統計だけではわからないというのはよくあります。今その瞬間でどのSQLがどの程度CPU Timeを使っていて、どの程度の待機イベントが発生しているのかを見なければ問題を特定できないことがあります。例えば、Statspackで待機イベントの待ち時間が長いものが複数存在している場合に、どのSQLがいつどの程度の割合で待機イベントで待っているかを判断しようとしてもわかりません。全体の中での統計とある一時点の状況は異なるからです。

このようなときに使うのが Enterprise Manager(or ASH Viewer) です。

Oracleがとても素晴らしいのは、どのSQLの実行時にどの待機イベントでどれだけ待っていたかというのを、Enterprise Managerを使うと時系列で見ることができます。Statspackのような期間での統計情報ではなく、まさにその遅延している状況で、特定のSQLについて待機イベントを把握することができるというのがポイントです。

発生する待機イベントの問題をどう解決するかはOracleの内部のアーキテクチャに密接に関連するため、チューニング方法は待機イベントによって異なります。しかし、Oracleのアーキテクチャをよく理解していれば、何が問題で待機イベントが発生しているかは想像がつきやすいものでもあります。

OSの性能データの分析(iostat, vmstat, mpstat, sar)

Statspackである期間の統計をとっても、ASHでみても分からないのは、OSの性能データです。

Oracleのようなミドルではなく、Hardwareのレベルでボトルネックになってしまっている場合には、ミドルレベルでいくら改善しようとしてもHardewareの性能限界からそれ以上の性能を出すことはできません。

OracleというMiddlewareの問題なのか、Hardeareの問題なのかの切り分けは、このレイヤで判断する必要があります。まずMiddleの性能を使い切れるようなHardwareを選定できているかを検証することは重要です。

Hardewareの基礎性能を測るツールについては先日のエントリに書いたので参考にしてみてください。

ディスクのIOPSレベルで問題が出ているために、待機イベントが発生しているのか、CPUリソースの不足で待機イベントが発生してしまっているのかなどは、OS側の基礎情報もつきあわせないと分からないことは、高負荷環境ではよくあるのではないかと思います。OSレベルで問題がない場合は、ASHを中心に特定のSQLでの待機イベントを中心にして、ボトルネックの推定をOracleのアーキテクチャを考量した上でしていくことになります。

基本的に、OSの性能データは、iostat, vmstat, mpstat, sarで見ます。これを時系列で記録し、EMなどと突き合わせるとどのような状況で問題がおきているかはわかりやすくなります。

ストレージのI/O周りでよくあるのは、Random Read/Write周りが限界にくることです。これはディスクの仕組みからくる物で、Random WriteだとSSDにするか、BBWCを使うか、Random ReadだとSSDにするかなど、はたまたディスク本数や回転数を上げるといったことでIOPSをあげ、Hardwareレベルでの限界値をあげておくというのはよくやるのではないかと思います。このIOPSを解決すると、CPU周りで詰まることが多い訳ですが、ここは色んなケースで詰まるのでCase by Caseです。このような場合、SQL発行回数の削減や非正規化などは、比較的よく使われるテクニックではないかと思います。

まとめ

全体の統計データと時系列データの2点を組みあせて、全体の中からどこを解決しなければいけないかを見つけた上で、問題の全体像が見えたら、問題を深追いしてボトルネックの原因を見つけるという手順になります。

それを実現するためには、OracleではStatspack + ASH + OSの基礎情報(iostat, vmstat, mpsta)を使って性能分析をします。これらのツールで詳細に性能に関する詳細データが統計的にも時系列的にも取ることが可能なため、ブラックボックスであってもある程度内部の動作の推測はできるので、ボトルネックの問題分析が容易になっています。

# 最近はMySQLもPerformance Schemaという近い物がでてきたので、もう少し時間が立つとOracleと同じような形になっていくのではないかと思います。今後のOracleの動向に注目したいところです。

2011-08-06

Hardwareの基礎性能を測定するためのツール

Hardwareの基礎性能を測定するためのツール - dann's blog を含むブックマーク はてなブックマーク - Hardwareの基礎性能を測定するためのツール - dann's blog Hardwareの基礎性能を測定するためのツール - dann's blog のブックマークコメント

インフラの性能でアプリケーションの性能限界が決まってしまうため、Hardwareの基礎性能を把握しておくことはとても重要です。以下では、Hardwareの基礎性能を測るための各種ツールを紹介します。

ディスク用ツール - fio

DBサーバーなどのI/O intensiveなサーバーで、Random Read/WriteのIOPSの限界でアプリケーション性能が決まってしまうというのは良くあるのではないかと思います

ディスクの基礎性能を測定するためのツールとしては、fio があります。

このツールでは、read/write, DirectI/O, I/O sizeなど、色々なI/Oパターンを指定して測定ができます。様々なI/Oパターンで、ディスクのIOPS・転送速度を簡単に測定できるので、とても便利です。

メモリ用ツール - stream

CPUの処理性能の向上率に比べてメモリ帯域はそれほど伸びていないという現状があり、コア数が増えていくにつれてそれが問題になってきます。特にコア数が多く・memory-intensiveなアプリケーションでは、実際にどの程度のメモリ帯域があるかの目安を知っておくことは重要になります。

メモリの帯域幅を測定するためのツールとしては、「stream」があります。

このツールでは、Copy, Scaleなどいくつかのメモリのアクセスパターンに応じてメモリの帯域幅を測定することができます。

-O3、openmp使用でコンパイルし、帯域幅を使いきれる状態にして測定しましょう。

ネットワーク用ツール - nuttcp, nepim

多量のトランザクション、IMDBなどの使用、大きなサイズのファイルの転送など、ネットワークのスループット・レイテンシが重要になるケースなどは、割とあるのではないでしょうか。

スループットなどのネットワークの性能測定用のツールとしては、nuttcp, nepimがあります。

これらのツールでは、バッファサイズやキューサイズなどを変更したテストが簡単にできるため、重宝しています。

# 他に、このツール便利だよ!というのがあれば、是非教えてください!

2011-08-04

継続的プロファイリング

継続的プロファイリング - dann's blog を含むブックマーク はてなブックマーク - 継続的プロファイリング - dann's blog 継続的プロファイリング - dann's blog のブックマークコメント

Google-Wide Profiling: A Continuous Profiling Infrastructure for Data Centers

http://research.google.com/pubs/pub36575.html

継続的プロファイリングの仕組み

  • 測定はOprofileとGoogle Perf Tools
    • OprofileでのHPMのプロファイル
    • Google Perf Toolsを使ってリモートからHTTPでプロファイル結果をとれる仕組み
      • ProfilerStart(), ProfilerStop()などのAPIがあるので、C++のアプリの場合、任意の部分をリモートからプロファイルできそうですね。こういう仕組みは、一部のサーバーのデバッグ用などにもとても良さそうです。
  • 回収・解析・表示の仕組み
    • Oprofileなどの結果をCollectorで集めて、集めた結果をMapReduceで処理してProfile Serverにいれて、Web Serverから表示できるようにしています

Googleの継続的プロファイリングの面白い点

 Hardwareの違いも含めてプロファイリングを通じて最適化するための仕組みを用意しているのはとても面白いなと思いました。処理性能も、コア数、メモリのバンド幅、メモリの構成、L1, L2, L3キャッシュなどの量などの関係、さらにアプリケーションの処理特性(CPU-intensiveな処理か、Memory-intensiveな処理なのかなど)になどによって、まるで性能は変わってしまい、それを完全に予測するのは難しいのは事実です。だからこそ、ハードに近いレイヤで、それを運用を通じて測定することで、性能改善をかけられる仕組みを用意しているのはGoogleらしいと思いました。

また、最適化した結果のコスト削減結果も、‘‘dollar amount per performance change,’’ has become popular among Google engineers. と書かれているように、コスト視点のメトリクスで見られ、性能改善結果を最終的なコストに直結させている点も面白いです

2011-07-19

perfの使い方

perfの使い方 - dann's blog を含むブックマーク はてなブックマーク - perfの使い方 - dann's blog perfの使い方 - dann's blog のブックマークコメント

CPUネックのサーバーの状況をリアルタイムに知りたいケースというのは、科学計算、暗号処理、画像処理などのCPU boundなアプリケーションでは良くあるんじゃないかと思います。

perfはoprofileのリアルタイム版といった感じで、hardware event, software eventの測定をでき、その場の状況を測定できるのでとても便利です。リアルタイムに状況を知りたい場合の使い方を中心にまとめてみました。

全体の測定

まずは、最初何も状況がわからなければ。

perf top

特定関数の測定

perf top実行後、s keyを押して、perf topで表示されたfunction名を入力。

これで、特定関数が測定できる。perf topで眺めながら、問題のある関数が把握できたらその関数だけ見るといった使い方が便利。

特定プロセス・スレッドの測定

問題のあるプロセス・スレッドが特定できているという場合には、それだけをモニタリングするというのは良くやりたくなることの一つです。以下で確認できました。

特定プロセスの測定

perf stat -e cycles -p プロセスID sleep 5
  • e で指定するイベントなどは適宜。

特定スレッドの測定

特定イベントのperformance counterの取得。

ps -L axなどでスレッドIDは決めて、以下を実行。

perf stat -t <スレッドID> sleep 5
  • e で指定するイベントなどは適宜。

特定イベントの測定

問題が特定イベントに絞り込まれている場合に。これは上記のいずれのものとも組み合わせられるので、適宜使うこと。

  • イベントはperf listで確認
  • perf stat -e event1,event2 で絞り込んで測定

call chainを表示してbottleneckを発見

これは、perf top, perf statと違って記録を残して分析するための使い方なのですが、とても便利なので書いておきます。

sudo perf record -g yourprogram
sudo  perf report -g

2011-04-16

TwitterのBlenderのアーキテクチャのポイント

 TwitterのBlenderのアーキテクチャのポイント - dann's blog を含むブックマーク はてなブックマーク -  TwitterのBlenderのアーキテクチャのポイント - dann's blog  TwitterのBlenderのアーキテクチャのポイント - dann's blog のブックマークコメント

http://engineering.twitter.com/2011/04/twitter-search-is-now-3x-faster_1656.html

「バックエンドサービスのI/Oが同期になると、I/O待ちで遅くなり、フロントが詰まってしまうので遅い。だから、Non BlockingなI/O呼び出しをしI/Oを多重化し、その呼び出し結果を集約することで高速化した」というのがBlenderのポイントのようです。工夫としては、Back Endサービスの呼び出しに依存関係があるので、全部を並列に呼び出すことはできないので、サービス呼び出しの依存関係を考慮して、一部並列にするところは並列に呼び出して、それ以外の部分はシーケンシャルに呼び出していると。そうすることで、I/Oを多重化できる単位を分割して呼び出すことを実現しています。

Non Blocking I/O系サーバーはイベントモデルのなじみにくさから、比較的処理がシンプルな、このようなAPIサーバーの呼び出し結果の集約など、I/Oの多重化をすることで並列化できるところ以外に適用部分はないんだろうなあとは思ってました。ちょっと前だとFriendFeedが、Tornado使って近いことをやってましたよね。

TwitterBlenderは、そのアイデアから一歩踏み込んで、ワークフロー内のサービスの依存関係を考慮して、サービス呼び出しのI/Oを多重化して、一部サービス呼び出しを並列に実行するのを汎用のミドルとして実現したのは面白いですね。

# 実装が透けて見えるレベルで解説されているので、そのうちOSS化されるのかもしれません。

TwitterのBlenderに近いアーキテクチャになっているMessage Pack RPCのJava版の実装を読んでみた

 TwitterのBlenderに近いアーキテクチャになっているMessage Pack RPCのJava版の実装を読んでみた - dann's blog を含むブックマーク はてなブックマーク -  TwitterのBlenderに近いアーキテクチャになっているMessage Pack RPCのJava版の実装を読んでみた - dann's blog  TwitterのBlenderに近いアーキテクチャになっているMessage Pack RPCのJava版の実装を読んでみた - dann's blog のブックマークコメント

TwitterBlenderはNIOの使い方として面白い使い方だなあと思っていたので、それに比較的近い実装をしているMessagePack-RPCJava版の実装を読んでみました(MessagePack RPCJava版でも同様にNettyを使っています)。

読んでみた感じだと、MessagePack RPCJava版の実装のポイントは、以下の3つでした。

  • NettyによるNon Blocking I/Oサーバーのイベントループ
  • 入力と出力のMessagePackによるエンコード/デコード
  • 入力のメッセージからのRPC先の実装の決定方法(Reflection, JavaAssistの実装でのInvoker実装)

基本的なこの流れは、NettyのPiplineを使って実現しています。コードだと以下のような感じになってますね。ServlerやWSGI, PSGIなどを使っている方は理解しやすいんじゃないかと思います。

	public ChannelPipeline getPipeline() throws Exception {
		ChannelPipeline p = Channels.pipeline();
		p.addLast("msgpack-decode-stream", new MessagePackStreamDecoder());
		p.addLast("msgpack-encode", new MessagePackEncoder());
		p.addLast("message", new MessageHandler(handler));
		return p;
	}

簡単に動作をまとめると、「Nettyでイベントループをまわして、メッセージを受け取ったらMessagePackでデコードして、その後でMessageHandlerで呼び出し先を決めて(Reflection or JavaAssist)、オブジェクトのメソッドを呼び出しをして、結果はMessagePackのフォーマットで従って返す」ということになります。

Nettyはとても綺麗にNIO実装が抽象化されており、さらにPipelineやPiplineへのメッセージのエンコーダ・デコーダを切り替えられる仕組みなどを提供しており、サーバー実装に必要なものを簡単に書けるようになっているので、MesagePack RPCJava実装もとてもシンプルな実装になってますね。MessagePack RPCJava実装は、RPC呼び出しをJavaのメソッド呼び出しに変換する部分(Invoker)と、MessagePack形式のメッセージのエンコード/デコード部分が基本で、それをNettyにつなぐという形の実装になっています。

TwitterBlenderでは、メッセージのエンコード/デコードにはThriftを使っているようなので同じようにProxy Layerの部分ではPiplineに、OneToOneDecoder, OneToOneEncoderを実装したThrift用のDecoder, Encoderを実装して実現しているのでしょう。また、Blenderでは、バックエンドサービスの呼び出し手順を規定したワークフローは、NettyのPipelineとして実現されているということなので実装も大分イメージはしやすいですね。シーケンシャルにしたい部分をNettyのPipelineとして実装してるんだろうなと。