220 likes | 340 Views
An Extension of AspectJ to Weave Aspect into an Arbitrary Code Region. 赤井駿平 08M37025 千葉研究室. 目的 : 同期の粒度を切り替えたい. ライブラリが2種類の同期を提供 : 細かい粒度の同期 高並列性 粗い粒度の同期 低オーバーヘッド ライブラリのユーザが最適な粒度を選ぶ 環境に応じて 良い性能 同期のコードの付替が容易でなければならない アスペクトを利用してはどうか?. ライブラリ. 粗い同期. 細かい 同期. Motivation: ライブラリにおける同期処理.
E N D
An Extension of AspectJ to Weave Aspect into an Arbitrary Code Region 赤井駿平 08M37025 千葉研究室
目的:同期の粒度を切り替えたい • ライブラリが2種類の同期を提供: • 細かい粒度の同期高並列性 • 粗い粒度の同期低オーバーヘッド • ライブラリのユーザが最適な粒度を選ぶ • 環境に応じて • 良い性能 • 同期のコードの付替が容易でなければならない • アスペクトを利用してはどうか? ライブラリ 粗い同期 細かい同期
Motivation: ライブラリにおける同期処理 public classProxyFactory { public Class createClass() { if (thisClass == null) { ClassLoadercl = getClassLoader(); synchronized(proxyCache) { if (useCache){createClass2(cl);} else {createClass3(cl);} }} return thisClass;} private voidcreateClass2(ClassLoader cl) { CacheKey key = new CacheKey(…); HashMapcacheForTheLoader =…; if (cacheForTheLoader == null) { cacheForTheLoader = newHashMap(); proxyCache.put(cl, cacheForTheLoader); cacheForTheLoader.put(key, key); }else {...} Class c = isValidEntry(key); if (c == null) { createClass3(cl); key.proxyClass = newWeakReference(…); }else{thisClass = c;} }} • 2006年にJavassistに対してバグレポート • Not thread-safe! • 単純な修正は簡単 • synchronized文を追加
修正によるパフォーマンスへの影響 public classProxyFactory { public Class createClass() { if (thisClass == null) { ClassLoadercl = getClassLoader(); synchronized (proxyCache) { if (useCache){createClass2(cl);} else {createClass3(cl);} }} return thisClass;} private voidcreateClass2(ClassLoader cl) { CacheKey key = new CacheKey(…); synchronized (proxyCache) { HashMapcacheForTheLoader =…; if (cacheForTheLoader == null) { cacheForTheLoader = newHashMap(); proxyCache.put(cl, cacheForTheLoader); cacheForTheLoader.put(key, key); }else {...}} synchronized (key) { Class c = isValidEntry(key); if (c == null) { createClass3(cl); key.proxyClass = newWeakReference(…); }else{thisClass = c;} }}} Coarse-grained • synchronized文の付け方が問題 • 細かい粒度 • 高い並列性 • オーバーヘッドが多い • 粗い粒度 • 並列性が低い • 少コアのマシンでは早い Fine-grained
アスペクト指向で同期を切り替えたい • 横断的関心事をモジュール化 • join point: プログラム実行中の実行点(メソッド呼び出し,フィールドアクセス…) • pointcut: join point を選ぶ手段 • advice: 選択したjoin point で実行するコード pointcut advice public String toString(){ foo(); return …; } void around (): call(* *.foo()) {bar(); proceed() ;} call join point
アスペクト指向で同期を切り替えたい public classProxyFactory { public Class createClass() { if (thisClass == null) { ClassLoadercl = getClassLoader(); synchronized(proxyCache) { if (useCache){createClass2(cl);} else {createClass3(cl);} }} return thisClass;} private voidcreateClass2(ClassLoader cl) { CacheKey key = new CacheKey(…); synchronized (proxyCache) { HashMapcacheForTheLoader =…; if (cacheForTheLoader == null) { cacheForTheLoader = newHashMap(); proxyCache.put(cl, cacheForTheLoader); cacheForTheLoader.put(key, key); }else {...}} synchronized (key) { Class c = isValidEntry(key); if (c == null) { createClass3(cl); key.proxyClass = newWeakReference(…); }else{thisClass = c;} }}} • 領域に対して around adviceを 織り込みたい void around (): pointcut(coarse_grained_region) { synchronized(…) { proceed(); } } void around (): pointcut(fine_grained_regions) { synchronized(…) { proceed(); } }
Our Proposal • Regioncut • 領域を選択する指定子 • Assertion for Advice • fragile性の問題を解決するため • 指定したアドバイスが織り込まれていないと警告 void around(): region[ call(* Map.get(..)), call(* Map.put(..)) ] { …; proceed(); …;} @AssertAdvised(“lock_map”) void foo(){ … } @SolveProblem(“Foo.lock_map”) void around(): … { … }
AspectJでは実現不可 • Pointcutは領域を選択できない • execution “point”のみ選択可能 • Call: メソッド呼び出しを選択 • Get, set: フィールドアクセスを選択 • Execution: メソッドボディを選択 • around advice を使えばメソッドボディ全体は同期可能 • メソッドボディが最適な粒度とは限らない→リファクタリングが必要 • 全ての粒度の同期の箇所をメソッドに抽出するのは大変
なぜAspectJは同期/領域を扱えないか • Fragile性を減らせない • コードが変更されると選択できなくなる可能性 • 領域にはpointcut より指定に多くの情報が必要 • pointcut より変更に弱い • 同期は必須の関心事 • 同期アスペクトを織り込み忘れると正しく動かない • AspectJでは織り込み忘れを発見できない
Our Proposal:Regioncut • 新しいポイントカット指定子のようなもの • 領域を選択 • 領域を指定するには • 領域内のexecution pointの中で重要なものを列挙 • call, get, set, が使える • 選択したい典型的な領域はそれで指定できる voidfoo(Map map){ Object o=map.get(key); println(o); o=nextValue(o); map.put(key,o);} region[ call(* Map.get(..)), call(ObjectnextValue(Object)), call(* Map.put(..)) ]
領域の拡大 • 領域と制御構造が衝突 • around advice を織り込めない • 領域を拡大: • 元の領域を含み • 制御構造を含み • かつできるだけ小さく voidfoo(Map map){ … Object o=map.get(key); println(o); o=nextValue(o); if(…){ map.put(key,o); … } … } Conflict Expanded
Fragile性を 減らすため Our Proposal: Assertion for Advice • 織り込まれなくなったアドバイスを見つけて警告 • @AssertAdvised(“<concern_name>”) • adviceが必要なメソッドに付加.関心事の名前を付ける • @SolveProblem(“<ClassName>.<concern_name>”) • 対象の関心事を解決するadviceに付加 class Foo{ @AssertAdvised(“lock_map”) voidfoo(Map map){ … } } @SolveProblem(“Foo.lock_map”) void around(): region[ … ]{ synchronize(…){ proceed(); } }
Assertion for Advice チェッカー • @AssertAdvicedの付いたメソッドが対応するadviceを持つかチェック • 以下の場合警告なし: • adviceがメソッドを呼んでいる • メソッドがadviceを呼んでいる • 間接的にでもOK @SolveProblem(“Foo.lock_map”) void around(): … { … } Call the method @AssertAdvised(“lock_map”) void foo(){ … } Call the advice @SolveProblem(“Foo.lock_map”) void around(): … { … }
2つのチェッカーを開発 • 静的なチェッカー • クラスファイルを解析 • コールグラフを調べる • 制限 • リフレクションに対応できない • 動的なチェッカー • 実行時に(呼び出した|呼ばれた)かを調べる • 実際に実行された部分のみチェック可能
Implementation • the AspectBench Compiler (abc)を拡張 • Regioncut • 中間言語Jimpleを解析/変換 • Assertion for Advice • dynamic: ASTを変換してダイナミックにチェック • static: Sootを利用 • 10,000+ LOC
Implementation Issue(1/2): Around advice • abcで around adviceを織り込む場合 • join points/regions を抽出しメソッドに分ける • 問題 • ローカル変数を共有できない • 領域の外へジャンプできない public void toBeAdvised(intx){ a(); b(x); c(); } public void toBeAdvised(intx){ advice(x); } public static void advice(intx){ beforeJoinPoint(); shadow(x); afterJoinPoint(); } public static shadow(intx){ a(); b(x); c(); }
Implementation Issue(2/2): 解決法 • ローカル変数を保存するオブジェクト • 引数経由で共有して対処 • ジャンプ先を示す値を変数に入れて共有 • 領域を抜けてからジャンプするように変換 { intx; intjmpTgt; } share public void toBeAdvised(intx){ advice(x); } public static void advice(Objo){ beforeJoinPoint(); shadow(x); afterJoinPoint(); } public static shadow(Objo){ a(); b(x); c(); }
評価: regioncutの記述力 • Hadoopのsynchronized文をアスペクトに分離 • regioncutが領域を十分に選択できるかを評価 • Hadoop 0.16.4 • 通常のpointcutで選択できなかったものは全て選択できた • 12の内の8領域が拡大が必要
評価: Assertion が有効に働くか • Hadoop を 0.16.4 から 0.18.3に更新 • dynamic版を使用 • Hadoopのユニットテストを動かしてチェック • 6個が正しく織り込まれなかった • 2個は検知 • 残りは実行されていなかったので検知不可
Current Limitation • 選択した2つ以上の領域が重なると • around adviceをweaveできない • 何らかの優先順位を付ける必要がある void withConflict(){ beginA(); beginB(); endA(); endB(); }
Related Work • 領域へのpointcutは重要な問題 • 限定的な解 • Loop join point: ループボディにマッチ • Synchronized join point: synchronized文にマッチ • 同等の解 • Transactional pointcut • 同じ会議で同時にaccept
Conclusion • アスペクト指向言語で領域を選択を実現 • Regioncut • Assertion for advice • これまでの成果 • 査読付き • GPCE'09, 2009-10, Denver • 査読なし/ワークショップ • ソフトウェア科学会大会,2008-07,東京 • ACP4IS’09, 2009-03,Charlottesville