プログラムの領域を ポイントカットすることが可能な アスペクト指向言語 赤井駿平 千葉滋 東京工業大学 背景:同期処理の粒度 並列処理は同期処理の粒度により実行速度が 変化する – 粒度が細かい方が並列に実行されやすい – 通常はこちらが速い スレッドの数が多い場合 同期処理自体やコンテキストスイッチのオーバーヘッドが増 加 粒度が粗い方が速くなる場合がある – 状況に応じて最適な粒度が異なる Javassistでの同期処理の例 Javassistのバグレポート [JASSIST-28 : http://jira.jboss.org/jira/browse/JASSIST-28] – 粗い粒度を選択 同期処理をアスペクトと して分離して記述できる とよい – 状況に応じて最適な粒度 が選択可能 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(…); synchronized (proxyCache) { 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;} }}} 既存のアスペクト指向言語の問題点 同期処理を分離しづらい – メソッド内の一部の領域をポイ ントカットすることは不可 – メソッド呼び出し,フィールドアク セス等の実行点またはメソッド全 体のみ before,afterアドバイスと mutexの組み合わせ 同期を解放できない場合が存在 before(): …{ mutex.lock(); } public void foo() { … obj.criticalSectionBegin();//ここから同期 throw new RuntimeException(); obj.criticalSectionEnd();//ここまで …. } after(): …{ mutex.unlock(); //実行されない } 提案:領域のポイントカット AspectJの言語仕様を拡張 メソッド内の領域をポイントカットできる – – 選択した領域にbefore,after,aroundアドバイスを適 用可能 制御構造と矛盾を起こさないように領域を選択 ポイントカットしたい領域の指定方法 4つのポイントカット指定子 – begin – end – 領域の末尾のjoin pointを指定 include – ポイントカットしたい領域の先頭のjoin pointを指定 領域の中に含まれなければいけないjoin pointを指定 exclude 領域の中に含まれてはいけないjoin pointを指定 begin/endポイントカット beginとendにマッチする join pointとその間をポイン トカット – ただし最短マッチを行う public void foo() { … obj.criticalSectionBegin();//ここから … obj.criticalSectionEnd();//ここまで … obj.criticalSectionEnd(); … } void around(): begin(call(* *. criticalSectionBegin())) && end(call(* *. criticalSectionEnd())) { synchronized(someObject){ proceed(); } } include/excludeポイントカット begin(call(* *. criticalSectionStart())) && end(call(* *. criticalSectionEnd())) && include(call(* *.a())) begin,endが同じである 領域を区別 – 最短マッチでは困る場合 にも利用 begin(call(* *. criticalSectionStart())) && end(call(* *. criticalSectionEnd())) && include(call(* *.a())) && include(call(* *.b())) public void foo() { … obj.criticalSectionBegin(); a(); obj.criticalSectionEnd(); … obj.criticalSectionBegin(); b(); obj.criticalSectionEnd(); … } begin(call(* *. criticalSectionStart())) && end(call(* *. criticalSectionEnd())) && exclude(call(* *.a())) 制御構造と重なる場合 ifやループをまたがって begin,endが指定された 場合 – そのままポイントカットする とプログラムが壊れる begin,endを両方含む最 小のブロックの中から選 択 public void foo() { … if(x.equals(y)){ obj.criticalSectionBegin(); } obj.criticalSectionEnd(); … } begin(call(* *. criticalSectionStart())) && end(call(* *. criticalSectionEnd())) コンテキストの取得 同期処理を行うには対象となるオブジェクトが必 要 – アドバイス内で元のメソッド内のコンテキストを取得で きなければならない レシーバやレシーバのフィールドはthisポイントカットを利用 して取得できる ローカル変数も取得できなければならない ローカル変数の取得 変数名での指定はよ くない – ローカル変数名が変 わるとポイントカットで きなくなる 型で指定 – args(型,...)を付ける public void foo() { SharedObject obj= …; … obj.criticalSectionBegin(); … obj.criticalSectionEnd(); … } void around(SharedObject o): begin(call(* *. criticalSectionStart())) && end(call(* *. criticalSectionEnd())) && args(o) && args(SharedObject) { synchronized(o){ proceed(o);} } 実装 Aspectbench Compiler (abc) に実装 – – Jimple(Javaバイトコードの中間言語)の状態でパ ターンマッチを行う aroundアドバイスを利用するために工夫 領域外のローカル変数への代入 aroundではjoin point が別のメソッドに移動 する – ローカル変数への代入 が領域の外に伝わらな い void joinpoint(String str){ obj.criticalSectionBegin(); str =”bar”; obj.criticalSectionEnd(); } public void foo() { String str= ”foo”; … obj.criticalSectionBegin(); advice(str); str =”bar”; obj.criticalSectionEnd(); System.out.println(str);//=>foo … } void around(): begin(call(* *. criticalSectionStart())) && end(call(* *. criticalSectionEnd())) {proceed();} 領域外のローカル変数への代入への対処 各ローカル変数をフィー ルドに格納するオブジェ クトを作成 – public void foo() { String str= ”foo”; … $localStore.str=str; これを切り出されたメソッ ドの引数として渡す 領域の前でフィールドに 格納 領域の先頭でローカル 変数へ代入 末尾でフィールドに格納 領域の後でローカル変 数へ 別のメソッド str=$localStore.str; obj.criticalSectionBegin(); str =”bar”; obj.criticalSectionEnd(); $localStore.str=str; str=$localStore.str; System.out.println(str);//=>bar … } 領域の外へのジャンプ 代入と同様に領域外への ジャンプも行えない – return, break, continueな ど – ジャンプ先は複数存在 対処 ジャンプ先毎にIDを割り 当てる 領域の末尾までジャンプ した後IDを見てジャンプ public void foo() {… label: while(…){ 別のメソッド while(…){ obj.criticalSectionBegin(); if(…) break; goto i=0;goto label0; endLabel; else if(…) goto i=1;goto label1; endLabel; break label; obj.criticalSectionEnd(); endLabel: if(i==0)goto label0; if(i==1)goto label1; } label0: } label1: … } 適用例 (粗い粒度の場合) 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; private void createClass2(ClassLoader cl) { CacheKey key =…; HashMap cacheForTheLoader = (HashMap)proxyCache.get(cl); if (cacheForTheLoader == null) { proxyCache.put(...); }else{...} Class c = isValidEntry(key); if (c == null) { createClass3(cl); key.proxyClass = new WeakReference(thisClass); }else{...} } 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スレッドの場合,粗い粒度の方が安定した速度 – 速度の安定性を考えると,粗い粒度の方が良い性能を出す ことが分かる 細かい粒度(2コア,40スレッド) 粗い粒度(2コア,40スレッド) 700 600 600 500 500 400 400 度数 度数 700 300 300 200 200 100 100 0 0 10~11 11~12 12~13 13~14 14~15 15~16 16~17 実行時間(秒) 平均:13.31秒 17~18 21~22 10~11 11~12 12~13 13~14 14~15 15~16 16~17 実行時間(秒) 平均:13.08秒 17~18 21~22 関連研究 GluonJ/R [熊原ら ’07] – – – LoopsAJ [Harbulot et al. ’06] – – 始点・終点をポイントカットで指定 ブロックの構造を無視したポイントカットを行えてしまう 適用できるアドバイスは例外処理のみ ループのみポイントカット可能 同一メソッド内の複数のループを区別ができない synchronized block join point [Xi et al. 08] – – synchronizedブロックをポイントカット 我々の研究は同期処理以外にも応用可能 まとめと今後の課題 領域をポイントカットする機構を提案 – – – 同期処理などをアスペクトを用いて分離することがで きる AspectBench Compilerを改造して実装 Javassistに適用 今後の課題 – – コンテキストの取得方法の改善 より柔軟な領域の指定方法
© Copyright 2024 ExpyDoc