同期処理のモジュール化を 可能にする アスペクト指向言語 理学部 情報科学科 04_00290 赤井駿平 指導教員 千葉滋
同期処理の分離の必要性 同期処理を適用する範囲を切り替えたい public class ProxyFactory { public Class createClass() { if (thisClass == null) { ClassLoader cl = getClassLoader(); synchronized (proxyCache) { if (useCache){createClass2(cl);} else {createClass3(cl);} }} return thisClass; } private void createClass2(ClassLoader cl) { CacheKey key = new CacheKey(…); HashMap cacheForTheLoader =…; if (cacheForTheLoader == null) { cacheForTheLoader = new HashMap(); proxyCache.put(cl, cacheForTheLoader); cacheForTheLoader.put(key, key); }else {...}} synchronized (key) { Class c = isValidEntry(key); if (c == null) { createClass3(cl); key.proxyClass = new WeakReference(…); }else{thisClass = c;} 同期処理を適用する範囲を切り替えたい 同期処理は計算機の特性により最適な粒度が変化 Javassistのバグレポート [JASSIST-28 : http://jira.jboss.org/jira/browse/JASSIST-28] 細かい粒度と粗い粒度の同期処理を切り替えて,動作速度を調べる 同期を行う箇所が散らばっているので切り替えが大変 粗い粒度 細かい粒度
アスペクト指向言語による同期の分離 - コード範囲をポイントカットすることの困難さ Fragile pointcut problem ソースコードを変更により 意図してしない場所をポイントカットしてしまう 意図した場所をポイントカットできなくなってしまう 範囲のポイントカットはソースコードの変更に弱い 例:行番号の指定 改行を追加するだけで意図した場所をポイントカットできなくなる ソースコードの変更に強い指定方法が必要
Fragile pointcut problemの要因 プログラムのブロック構造の変化 GluonJ/R [熊原ら ’07] 始点・終点をポイントカットで指定 ブロックの構造を無視したポイントカットを行えてしまう 適用できるアドバイスは例外処理のみ 類似したコード範囲の出現 LoopsAJ [Harbulot et al. ’06] ループのみポイントカット可能 メソッド内のループの区別ができない 取得するコンテキストに含まれる変数の名前・順序の変更 ローカル変数をアドバイスで利用したい ローカル変数の指定方法を工夫しないと,fragile pointcut
提案:ブロックポイントカット ブロックのような複数の実行点を含む範囲をジョインポイントとして指定 ソースコードの変更に強い範囲の指定方法 その範囲に対しアドバイスを実行できる 同期処理の分離や差し替えが容易 ソースコードの変更に強い範囲の指定方法 Fragile pointcut problemに対処可能 ポイントカットする範囲に条件を指定する 取得するコンテキストの指定
ブロック構造を考慮した範囲の指定 begin/endポイントカット void foo(){ … start(); if(n>0){ finish(); }else{ ... } begin/endポイントカット 始点,終点となるジョインポイントを指定し,その2点間がポイントカットされる ブロックの深さが違う場合は,同じ深さまでブロックをさかのぼる 制御構造やブロックと重ならないように範囲を決定 ×If文と重なる If文 void around(): begin(call(* *.start())) && end(call(* *.finish())) {proceed();}
類似したコード範囲の区別 include/excludeポイントカットを利用して絞り込む void bar(){ start(); a(); finish(); … b(); } include/excludeポイントカットを利用して絞り込む 範囲の中に含まれなければいけない,含まれてはいけないジョインポイントを指定 後から追加 void around(): begin(* *.start()) && end(* *.finish()) && include(call(* *.a())) { proceed(); }
コンテキストの取得 同期処理を行う場合,ローカル変数を取得できなければならない 型を指定して取得するローカル変数を選ぶ 変数の名前や宣言順で指定すると fragile pointcut problem が起こりやすい List list = (List)map.get("list"); synchronize(list){ start(); list.remove(0); finish(); } void around(List l) : begin(* *.start()) && end(* *.finish()) && args(List) && args(l) { synchronized(l){ proceed(l); }
実装 AspectJを拡張 中間表現上にブロック,制御構造,文の情報を記録する AspectBench Compiler を改造して実装 開始・終了を表す命令を新たに用意し,前後に挿入 ブロックを解析し,ポイントカットで指定された条件にマッチする範囲を選ぶ
適用例 Javassistのバグレポートにおける同期処理の粒度を切り替える 粗い粒度の場合 static WeakHashMap proxyCache; public Class createClass() { if (thisClass == null) { ClassLoader cl = …; if (useCache) createClass2(cl); else createClass3(cl); } return thisClass; void around(): begin(call(* *.createClass2(..)))&& end(call(* *.createClass3(..))) { synchronized( ProxyFactory.proxyCache){ proceed(); }
適用例(続き) 細かい粒度の場合 static WeakHashMap proxyCache; void around(): private void createClass2(ClassLoader cl) { CacheKey key =…; HashMap cacheForTheLoader = (HashMap)proxyCache.get(cl); if (cacheForTheLoader == null) { proxyCache.put(...); cacheForTheLoader.put(...); }else{...} Class c = isValidEntry(key); if (c == null) { createClass3(cl); key.proxyClass = new WeakReference(thisClass); } void around(): begin(call(* WeakHashMap.get(..))) && end(call(* WeakHashMap.put(..))){ synchronized( ProxyFactory.proxyCache){ proceed(); } void around(CacheKey key): begin(call(* *.isValidEntry(..))) && end(call(WeakReference.new(..))) && args(CacheKey) && args(key){ synchronized(key){ proceed(key);
速度の測定 Javassistの同期の粒度をアスペクトを用いて変更し,バグレポートに投稿されたマイクロベンチマークを使用して実行時間を測定(1000回試行) 4コア,40スレッド 細かい粒度 : 平均実行時間 8.97秒,標準偏差 0.36 粗い粒度 : 平均実行時間 11.94秒,標準偏差 0.17 2コア,40スレッド 細かい粒度 : 平均実行時間 13.31秒,標準偏差 1.02 粗い粒度 : 平均実行時間 13.08秒,標準偏差 0.26 実験環境 CPU:Xeon 3.00GHz 最大4コア メモリ: 2GB OS: Linux 2.6.22-14 JVM: java 1.6.0_03 アスペクトを用いて粒度を切り替えた方が性能が良くなる
粒度の切り替えによる性能の向上 2コアで40スレッドの場合,粗い粒度の方が安定した速度 速度の安定性を考えると,粗い粒度の方が良い性能を出すことが分かる
まとめと今後の課題 範囲をポイントカットする機構を提案 今後の課題 同期処理を分離して記述することができる AspectBench Compilerを改造して実装 Javassistに適用して実験 今後の課題 コンテキスト渡しの指定方法の改善 より柔軟な範囲の指定方法