[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[jfriends-ml 10582] レガシーコレク ションクラス (Re: リファクタリングを読む会第 6 回議事録)



(株)ネットジーンの村山です.

> >   Vectorを使うべきでないときがある
> >     -->synchronizedによるオーバーヘッドがある
> >        -->最近のJVMでは無視できるほど少ないらしい
> >           -->比較するBenchmarkは 宿題!!

私の意見としては,

話すと長くなりますが,結論からいうと
「Vector,Hashtableなどのレガシーコレクションクラスは使うべきではない.」
です.特にJ2EE分野では.

理由は同期化されているため.ある意味でパフォーマンスの問題ですが,それは
synchronizedメソッドと普通のメソッドとの(僅かな)速度の差によるものでは
ありません.同期化そのものの持つ危険性ゆえです.極端な話,synchronized
メソッドの方が普通のメソッドより(多少)高速だったとしても,同期化は避ける
べきです.


同期化の危険性が理解できる人は,トラブルを未然に防ぐために可能な限り
使用を避けるでしょう.(私もJava2登場以前からVector/Hashtableの危険性
は嫌というほど知っており,それを知った上でだましだまし使っていました.)
これらを使っても危険を回避することは不可能ではないにせよ,マゾヒストでも
無い限り好き好んで危ない橋を渡ることはないので.

同期化の危険性が理解できない人の場合は,トラブルを未然に防ぐ術が無いので
使うべきではありません.「駄目で元々」「当たって,砕けろ」「後は野となれ
山となれ」「3歩進んで2歩(或いは10歩)下がる」式の開発手法を取ったり,
「不具合の後始末で苦労するのは自分じゃないから」という立場の人の場合は,
まあ好きなようにしても構いませんけれど.

--------
・Vectorがトラブルになる可能性はかなり低い.現在問題なく動いているプログラム
を無理に書き直す必要はまず無い.

・大きなトラブルを起こすのは,複数のCPUを持つ環境でマルチスレッドで使用する
場合に,ほぼ限られると思う.1CPUで問題になることはまずないはず.J2EEはマルチ
スレッドが基本だから,J2EEではレガシーコレクションクラスの使用は原則禁止と
考えるべき.開発当初は1CPUマシンしか使わない場合でさえも,将来ユーザー数が
増えた結果マルチCPUマシンに移行する可能性もあるし,それこそHyperThreadingを
使う可能性もある.
#HTではキャッシュが共有されるし,本当に同時並列実行される訳ではないだろうから,
#かなり特殊な挙動を示すと思うが.

・マルチCPU上でも実際にトラブルになる可能性は低い.問題を起こしていても,
高々数十〜200%遅くなるくらいなら,問題の発生に気付かないことも多いだろう.

・マルチスレッド周りの動作は挙動が予測し難いので,トラブルが発生しないことを
保証するのは難しい.また発生していても,それをデバッガやプロファイラが認識
できる保証は無い.(デバッガを使うこと自体が挙動を変化させるため.このため,
並列プログラミングではデバッガはほとんど頼りにならない.)ある環境では
トラブルにならなくても,他の環境に持っていくとトラブルになることもありうる.

・確実に言えることは「トラブルを避けたければ不要な同期は取らない方が無難」.
インスタンス生成やデータベースのコネクションなどと同じ.無駄に使ったからと
いって即トラブルになるとは限らないが,無駄に作れば作るほどトラブルになる
可能性は高まる.

・同期周りのパフォーマンスバグが恐いのは,台数効果が帳消しになることが
あるという点.上手く作った場合でさえも,CPU数に比例した性能が出ないのは
並列プログラムでは当たり前.しかし同期周りにバグがあると,最悪ではCPUを
増やせば増やすほど遅くなり逐次の方がまだ早いという場合も起こりうる.

通常はマルチCPUを求めるのはシングルCPUでは処理しきれないほどの高い性能が
必要な場合なので,シングルCPUより遅くなるというのは実用に耐えないことを
意味する.

・このようなトラブルは滅多に発生しないと思う.だが,万が一発生した場合は,
プログラムは一から書き直す危険性さえある.

マルチスレッドを理解していれば,このようなトラブルを未然に防ぐのはさほど難しく
ない.これに対し,このようなトラブルが発生した後で,それが簡単に取り除けるという
保証は全く無い.トラブルが判明したときには,同期周りの設計がスパゲティプログラム
と化していて既に手遅れという可能性もある.

・同期化は,アルゴリズムの一部であり,それを追加したり削除したりすることは,
アルゴリズムの変更を意味する.このためJavaVMの最適化がどんなに進歩しても,
同期周りのパフォーマンスバグを自動的に取り除くことはおそらく不可能.

------

もう一つ重要なのは,レガシーコレクションクラスを使う積極的な理由がないという点.

たとえば不変オブジェクトなどは様々なメリットがある.(特にマルチスレッドでは
パフォーマンス的にも有利になることも多い.)Stringを頻繁に変更する場合に
ボトルネックになることがあるのは事実だが,その危険を考慮してもメリットを
捨てるのは勿体ない.これに対しレガシーコレクションクラスを使うメリットは
まずない.敢えて挙げるなら,既にレガシーコレクションクラスを使っている古い
コードをそのまま再利用する場合くらい.


>   下記の件ですが、JavaHouseには次のような話もあったようです。
>   最近はどうなんでしょう? 
> 
>   [Vector と ArrayList]
>     http://java-house.jp/ml/archive/j-h-b/033343.html#body

バージョンを重ねるごとに改良が加えられてるでしょうから,昔のデータは
あまり当てにはならないと思います.

それに,いずれにせよ逐次での結果は並列プログラミングの参考にはなりません.
またこういった微妙なパフォーマンスはJavaVMやOSやハードウエア構成やアプリケー
ションやグリーンスレッド/ネイティブスレッド等により変化するものなので,
あくまで参考と考えておく方が無難です.

#グリーンスレッドで,シングルスレッドの1CPUマシンならば,多分ほとんど
#誤差みたいなものにしかならないはず.2CPU以上でネイティブスレッドの
#場合では,多分,OSのシステムコールが必要になるので,それなりのオーバー
#ヘッドは避けれない.

##でも真に恐いのは,メソッド自体のオーバーヘッドではない.


-- 
村山敏清 株式会社ネットジーン 
〒169-0051 東京都新宿区西早稲田2-15-10 2F 
E-mail:murayama@xxxxxxxxxxxxx 
TEL:03-5155-3720 FAX:03-5155-3723
http://www.netgene.co.jp/