320 likes | 599 Views
Thin Slice のサイズに関する統計的評価. ○ 秦野 智臣,鹿島 悠,石尾 隆,井上 克郎 大阪大学大学院情報科学研究科. 概要. Thin Slicing [1] が抽 出するプログラムスライスの大きさを 計測 し,統計的評価を行う Thin Slicing :プログラムスライシングの一種 Thin Slice : Thin Slicing が抽 出するスライス 7 つ の Java プログラムを対象とした実験で, Thin Slice のサイズが十分小さくなることを確認した.
E N D
Thin Slice のサイズに関する統計的評価 ○ 秦野 智臣,鹿島 悠,石尾 隆,井上 克郎 大阪大学大学院情報科学研究科
概要 • Thin Slicing [1] が抽出するプログラムスライスの大きさを計測し,統計的評価を行う • Thin Slicing:プログラムスライシングの一種 • Thin Slice:Thin Slicing が抽出するスライス • 7つのJavaプログラムを対象とした実験で,Thin Sliceのサイズが十分小さくなることを確認した [1] Sridharan, M., Fink, S. J. and Bodik, R.: Thin slicing, Proc. of PLDI2007, pp. 112–122 (2007).
発表内容 • 背景 2. プログラムスライシング 3. Thin Slicing 4. 実験 5. Thin Slicing の実装 6. 計測指標 7. 計測結果
背景 • 開発者はプログラムの保守作業に多くの時間を費やしている • プログラムの保守作業において,データ依存関係を探索する必要があり,この作業に多くの時間がかかる [2] [2] LaToza, T. D. and Myers, B. A.: Developers ask reachability questions, Proc. of ICSE, pp. 185–194 (2010).
プログラムスライシング • プログラム内のある文の変数を基準として,その変数の値に影響を与える可能性のあるすべての文を抽出する ソースコード 1 void main() { 2 int sum = 0; 3 inti= 1; 4 while (i <= 10) { 5 sum = sum + i; 6 i = i + 1; 7 } 8 print(i); 9 print(sum); 10 } スライス 1 void main() { 3 inti= 1; 4 while (i <= 10) { 6i = i + 1; 7 } 8 print(i); 10 } プログラム スライシング スライシング基準
スライスのサイズ • スライスサイズの平均値は,プログラム全体の約30%である [3] • 100万行のプログラムの場合,平均で約30万行のスライスが抽出される • 大規模プログラムではスライスサイズが大きくなってしまう 開発者がスライスを閲覧する用途において プログラムスライシングの利用は困難 [3] Binkley, D., Gold, N. and Harman, M.: An empirical study of static program slice size, ACM TOSEM, Vol. 16, No. 2, pp. 1–32 (2007).
Thin Slicing[1] • 制御フローを無視して,限定したデータフローのみに着目する • スライスサイズが小さくなる • データの生成元が特定できる • プログラムの保守にかかる時間が減少する • プログラム理解の時間が減少した22個の例 [1]
Thin Slicing の定義 • 基準となる文がデータ依存する文を抽出する • 手続き内のデータ依存関係 • ポインタ変数のデータ依存関係は無視する • 文 y = p.fにおいて,変数 p のデータ依存関係は無視 • 手続き間のデータ依存関係 • 仮パラメータが実パラメータにデータ依存する • 呼び出し元が呼び出し先の返り値にデータ依存する • ヒープ領域のデータ依存関係 • 同一のフィールドまたは配列の内容を代入,参照する可能性のある文の組について,所属する手続きに関係なく,参照する文が代入する文にデータ依存する
Thin Slicing の例 ソースコード 1 void main() { 2 A x, z, w; 3 inta, b; 4 a = 1; 5 x = new A(); 6 z = x; 7 b = inc(a); 8 w = x; 9 w.f = b; 10 if (w == z) { 11 intv = z.f; 12 } 13 } 14 intinc(int x) { 15 return x + 1; 16 } 17 class A { int f; } ソースコード 1 void main() { 2 A x, z, w; 3 inta, b; 4 a = 1; 5 x = new A(); 6 z = x; 7 b = inc(a); 8 w = x; 9 w.f = b; 10 if (w == z) { 11 int v = z.f; 12 } 13 } 14 intinc(int x) { 15 return x + 1; 16 } 17 class A { int f; } ソースコード 1 void main() { 2 A x, z, w; 3 inta, b; 4 a = 1; 5 x = new A(); 6 z = x; 7 b = inc(a); 8 w = x; 9 w.f = b; 10 if (w == z) { 11 intv = z.f; 12 } 13 } 14 intinc(int x) { 15 return x + 1; 16 } 17 class A { int f; } Thin Slice 4 a = 1; 7 b = inc(a); 9 w.f = b; 11 int v = z.f; 14 intinc(int x) { 15 return x + 1; 16 } 変数vがデータ依存する 文が抽出されている Thin Slicing 変数zがデータ依存する 文は抽出されていない スライシング基準
調査すべきこと • Thin Slicing が一般的に有効かどうかは明らかではない • 平均的に Thin Slice のサイズは小さくなるのか • Thin Slicing が有効な場面は多いのか
Thin Slicing が有効であると考えられる例 1 package sample; 7 id = 1; 9 id = 0; 10 a.addData(id); 16 void addData(int id) { 17 b.addData(id); } 20 class B { 21 intmax = 4; 22 X x = new X(); 23 void addData(int id) { 24 if (id >= 0 && id <= max) 25 x.addData(id); 26 } 27 } 28 class X { 29 int[] idList = new int[16]; 30 intcount = 0; 31 void addData(int id) { 32 System.out.println(id); 33 idList[count] = id; 34 count = count + 1; 35 } 36 intgetData(int index) { 37 return idList[index]; 38 } 39 } 1 package sample; 2 public class Main { public static void main(String[] args) { 4 A a = new A(); 5 intid; 6 if (args.length > 0) 7 id = 1; 8 else 9 id = 0; 10 a.addData(id); 11 System.out.println(a); 12 } 13 } 14 class A { 15 B b = new B(); 16 void addData(int id) { 17 b.addData(id); 18 } 19 } 23 void addData(int id) { 25 x.addData(id); 26 } 31 void addData(int id) { 32 System.out.println(id); 35 } Thin Slice が複数のメソッド にまたがっている データの生成元 開発者がメソッドをたどって データ依存関係を探索する 作業をThin Slicingによって 置き換える
Thin Slicing が有効でないと考えられる例 20 class B { 21 intmax = 4; 22 X x = new X(); 23 void addData(int id) { 24 if (id >= 0 && id <= max) 25 x.addData(id); 26 } 27 } 28 class X { 29 int[] idList = new int[16]; 30 intcount = 0; 31 void addData(int id) { 32 System.out.println(id); 33 idList[count] = id; 34 count = count + 1; 35 } 36 intgetData(int index) { 37 return idList[index]; 38 } 39 } 1 package sample; 2 public class Main { public static void main(String[] args) { 4 A a = new A(); 5 intid; 6 if (args.length > 0) 7 id = 1; 8 else 9 id = 0; 10 a.addData(id); 11 System.out.println(a); 12 } 13 } 14 class A { 15 B b = new B(); 16 void addData(int id) { 17 b.addData(id); 18 } 19 } Thin Slice が1つのメソッドで 閉じている ソースコードを見るだけで すぐにデータの生成元が 特定できる
Thin Slicing が有効でないと考えられる例 20 class B { 21 intmax = 4; 22 X x = new X(); 23 void addData(int id) { 24 if (id >= 0 && id <= max) 25 x.addData(id); 26 } 27 } 28 class X { 29 int[] idList = new int[16]; 30 intcount = 0; 31 void addData(int id) { 32 System.out.println(id); 33 idList[count] = id; 34 count = count + 1; 35 } 36 intgetData(int index) { 37 return idList[index]; 38 } 39 } 1 package sample; 2 public class Main { public static void main(String[] args) { 4 A a = new A(); 5 intid; 6 if (args.length > 0) 7 id = 1; 8 else 9 id = 0; 10 a.addData(id); 11 System.out.println(a); 12 } 13 } 14 class A { 15 B b = new B(); 16 void addData(int id) { 17 b.addData(id); 18 } 19 } Thin Slice が1つのメソッドで 閉じている どちらのケースが多いかはわかっていない ソースコードを見るだけで すぐにデータの生成元が 特定できる
リサーチクエスチョン • Thin Slicing が一般的に有効であるか • RQ1:Thin Slice のサイズは,平均的に十分小さいものであるか • スライスサイズの平均値を計測して,その有効性を評価する研究が行われている [3,4] • RQ2:データ依存関係の調査において有効であると考えられるThin Sliceはどの程度存在するか • プログラムの保守作業において,データ依存関係を探索する必要があることが知られている [2] [4] Jasz, J., Arpad Beszedes, Gyimothy, T. and Rajlich, V.: Static Execute After/Before as a Replacement of Traditional Software Dependencies, Proc. of ICSM,pp. 137–146 (2008).
Thin Slicing の実装 • Java のバイトコードを解析する • グラフを作成し,探索する • 頂点:バイトコードの1命令 • 辺:命令間のデータ依存関係
Java のバイトコード ソースコード 1 package example; 2 public class Sample { 3 public static void main(String[] args) { 4 A x, z, w; 5 inta, b; 6 a = 1; 7 x = new A(); 8 z = x; 9 b = inc(a); 10 w = x; 11 w.f= b; 12 if (w == z) { 13 intv = z.f; 14 System.out.println(v); 15 } 16 } 17 static intinc(int x) { 18 return x + 1; 19 } 20 } 21 class A { int f; } 16: ALOAD 3 (w) 17: ALOAD 2 (z) 18: IF_ACMPNE 19: ALOAD 2 (z) 20: GETFIELD example/A#f: int 21: ISTORE 6 (v) 22: GETSTATIC java/lang/System#out: java/io/PrintStream 23: ILOAD 6 (v) 24: INVOKEVIRTUAL java/io/PrintStream#println(I)V 25: RETURN example/Sample#inc 0: ILOAD 0 (x) 1: ICONST_1 2: IADD 3: IRETURN example/Sample#main 0: ICONST_1 1: ISTORE 4 (a) 2: NEW 3: DUP 4: INVOKESPECIAL example/A#<init>()V 5: ASTORE 1 (x) 6: ALOAD 1 (x) 7: ASTORE 2 (z) 8: ILOAD 4 (a) 9: INVOKESTATIC example/Sample#inc(I)I 10: ISTORE 5 (b) 11: ALOAD 1 (x) 12: ASTORE 3 (w) 13: ALOAD 3 (w) 14: ILOAD 5 (b) 15: PUTFIELD example/A#f: int 定数の生成 書き込み 引数xの読み込み 定数の生成 加算命令 値を返す
グラフの作成 16: ALOAD 3 (w) 17: ALOAD 2 (z) 18: IF_ACMPNE 19: ALOAD 2 (z) 20: GETFIELD example/A#f: int 21: ISTORE 6 (v) 22: GETSTATIC java/lang/System#out: java/io/PrintStream 23: ILOAD 6 (v) 24: INVOKEVIRTUAL java/io/PrintStream#println(I)V 25: RETURN example/Sample#inc 0: ILOAD 0 (x) 1: ICONST_1 2: IADD 3: IRETURN example/Sample#main 0: ICONST_1 1: ISTORE 4 (a) 2: NEW 3: DUP 4: INVOKESPECIAL example/A#<init>()V 5: ASTORE 1 (x) 6: ALOAD 1 (x) 7: ASTORE 2 (z) 8: ILOAD 4 (a) 9: INVOKESTATIC example/Sample#inc(I)I 10: ISTORE 5 (b) 11: ALOAD 1 (x) 12: ASTORE 3 (w) 13: ALOAD 3 (w) 14: ILOAD 5 (b) 15: PUTFIELD example/A#f: int
main 0: ICONST_1 15: PUTFIELD 20: GETFIELD 2: NEW 11: ALOAD 1 21: ISTORE 6 1: ISTORE 4 3: DUP 14: ILOAD 5 12: ASTORE 3 23: ILOAD 6 8: ILOAD 4 10: ISTORE 5 4: INVOKESPECIAL 13: ALOAD 3 呼び出し元 実パラメータ actual 24 actual 9 result 9 5: ASTORE 1 16: ALOAD 3 9: INVOKESTATIC 17: ALOAD 2 6: ALOAD 1 24: INVOKEVIRTUAL 19: ALOAD 2 22: GETSTATIC 7: ASTORE 2 inc formal return 18: IF_ACMPNE 仮パラメータ 返り値 0: ILOAD 0 3: IRETURN 手続き内のデータ依存辺 手続き間のデータ依存辺 1: ICONST_1 2: IADD ヒープ領域のデータ依存辺 25: RETURN
main 0: ICONST_1 15: PUTFIELD 20: GETFIELD 0: ICONST_1 15: PUTFIELD 20: GETFIELD 2: NEW 11: ALOAD 1 21: ISTORE 6 21: ISTORE 6 1: ISTORE 4 14: ILOAD 5 1: ISTORE 4 3: DUP 14: ILOAD 5 12: ASTORE 3 23: ILOAD 6 23: ILOAD 6 8: ILOAD 4 10: ISTORE 5 8: ILOAD 4 10: ISTORE 5 4: INVOKESPECIAL 13: ALOAD 3 actual 24 actual 24 actual 9 result 9 actual 9 result 9 5: ASTORE 1 16: ALOAD 3 9: INVOKESTATIC 9: INVOKESTATIC 17: ALOAD 2 6: ALOAD 1 24: INVOKEVIRTUAL 19: ALOAD 2 22: GETSTATIC 7: ASTORE 2 inc inc formal return formal return 25: RETURN 18: IF_ACMPNE 0: ILOAD 0 3: IRETURN 0: ILOAD 0 3: IRETURN スライシング基準:actual 24の後ろ向き Thin Slice 1: ICONST_1 2: IADD 1: ICONST_1 2: IADD 赤色の頂点の集合
スライシング基準の決定 • スライシング基準を決定するために,バイトコード命令を分類する • source:データを生成する • 定数の生成(ICONST_1),オブジェクトの生成(NEW),メソッドの返り値 • sink:データを使用する • 比較演算(IFEQ),メソッドの実引数 • transfer:データを伝播する • 変数の代入(ISTORE),参照(ILOAD) • (前向き Thin Slice) • (後ろ向き Thin Slice)
計測する指標 • あるsink に対する後ろ向き Thin Slice • に含まれる source 頂点の集合 • ある source に対する前向き Thin Slice • に含まれる sink 頂点の集合 • Thin Slice Stsの頂点が所属するメソッドの集合 sink に関する指標 source に関する指標 sinkとsource に関する指標 以上の指標をプログラム中のすべての sink,source に対して計測する
例:指標の計測方法 16: ALOAD 3 (w) 17: ALOAD 2 (z) 18: IF_ACMPNE 19: ALOAD 2 (z) 20: GETFIELD example/A#f: int 21: ISTORE 6 (v) 22: GETSTATIC java/lang/System#out: java/io/PrintStream 23: ILOAD 6 (v) 24: INVOKEVIRTUAL java/io/PrintStream#println(I)V 25: RETURN example/Sample#inc 0: ILOAD 0 (x) 1: ICONST_1 2: IADD 3: IRETURN example/Sample#main 0: ICONST_1 1: ISTORE 4 (a) 2: NEW 3: DUP 4: INVOKESPECIAL example/A#<init>()V 5: ASTORE 1 (x) 6: ALOAD 1 (x) 7: ASTORE 2 (z) 8: ILOAD 4 (a) 9: INVOKESTATIC example/Sample#inc(I)I 10: ISTORE 5 (b) 11: ALOAD 1 (x) 12: ASTORE 3 (w) 13: ALOAD 3 (w) 14: ILOAD 5 (b) 15: PUTFIELD example/A#f: int
main 0: ICONST_1 15: PUTFIELD 20: GETFIELD 0: ICONST_1 15: PUTFIELD 20: GETFIELD 2: NEW 11: ALOAD 1 0: ICONST_1 21: ISTORE 6 21: ISTORE 6 1: ISTORE 4 14: ILOAD 5 1: ISTORE 4 3: DUP 14: ILOAD 5 12: ASTORE 3 23: ILOAD 6 23: ILOAD 6 8: ILOAD 4 10: ISTORE 5 8: ILOAD 4 10: ISTORE 5 4: INVOKESPECIAL 13: ALOAD 3 actual 24 actual 24 actual 9 result 9 actual 9 result 9 5: ASTORE 1 16: ALOAD 3 9: INVOKESTATIC 9: INVOKESTATIC 17: ALOAD 2 6: ALOAD 1 24: INVOKEVIRTUAL 19: ALOAD 2 22: GETSTATIC 7: ASTORE 2 inc inc formal return formal return 25: RETURN 18: IF_ACMPNE スライシング基準:sink actual 24の後ろ向き Thin Slice 0: ILOAD 0 3: IRETURN 0: ILOAD 0 3: IRETURN 赤色の頂点の集合 ⇒ 中の source 命令 = { 0: ICONST_1, 1: ICONST_1, 2: IADD } ⇒ 1: ICONST_1 2: IADD 1: ICONST_1 1: ICONST_1 2: IADD 2: IADD
main 0: ICONST_1 15: PUTFIELD 20: GETFIELD 0: ICONST_1 15: PUTFIELD 20: GETFIELD 2: NEW 11: ALOAD 1 21: ISTORE 6 21: ISTORE 6 1: ISTORE 4 14: ILOAD 5 1: ISTORE 4 3: DUP 14: ILOAD 5 12: ASTORE 3 23: ILOAD 6 23: ILOAD 6 8: ILOAD 4 10: ISTORE 5 8: ILOAD 4 10: ISTORE 5 4: INVOKESPECIAL 13: ALOAD 3 actual 24 actual 24 actual 9 result 9 actual 9 result 9 5: ASTORE 1 16: ALOAD 3 9: INVOKESTATIC 9: INVOKESTATIC 17: ALOAD 2 6: ALOAD 1 24: INVOKEVIRTUAL 19: ALOAD 2 22: GETSTATIC 7: ASTORE 2 inc inc formal return formal return 25: RETURN 18: IF_ACMPNE スライシング基準:sink actual 24の後ろ向き Thin Slice 0: ILOAD 0 3: IRETURN 0: ILOAD 0 3: IRETURN 赤色の頂点の集合 ⇒ = { main, inc } 1: ICONST_1 2: IADD 1: ICONST_1 2: IADD
main 0: ICONST_1 15: PUTFIELD 20: GETFIELD 2: NEW 11: ALOAD 1 2: NEW 11: ALOAD 1 21: ISTORE 6 1: ISTORE 4 3: DUP 14: ILOAD 5 12: ASTORE 3 12: ASTORE 3 23: ILOAD 6 8: ILOAD 4 10: ISTORE 5 4: INVOKESPECIAL 13: ALOAD 3 13: ALOAD 3 actual 24 actual 9 result 9 5: ASTORE 1 16: ALOAD 3 16: ALOAD 3 5: ASTORE 1 9: INVOKESTATIC 17: ALOAD 2 6: ALOAD 1 6: ALOAD 1 17: ALOAD 2 24: INVOKEVIRTUAL 7: ASTORE 2 19: ALOAD 2 19: ALOAD 2 22: GETSTATIC 7: ASTORE 2 inc formal return 25: RETURN 18: IF_ACMPNE 18: IF_ACMPNE 18: IF_ACMPNE スライシング基準:source 2: NEW の前向き Thin Slice 0: ILOAD 0 3: IRETURN 赤色の頂点の集合 ⇒ 1: ICONST_1 2: IADD 中の sink 命令 = { 18: IF_ACMPNE } ⇒
実験対象 • DaCapo benchmark (バージョン 9.12) • 7つのJava プログラム
RQ1:実験結果(1/3) • RQ1:Thin Slice のサイズは,平均的に十分小さいものであるか
RQ1:実験結果(2/3) • 7つのプログラムでの平均 • |𝐵𝑎𝑐𝑘𝑤𝑎𝑟𝑑(𝑣)|の平均:2.5% • |𝐹𝑜𝑟𝑤𝑎𝑟𝑑(𝑤)|の平均:1.9% • |𝐵𝑎𝑐𝑘𝑤𝑎𝑟𝑑(𝑣)|と|𝐹𝑜𝑟𝑤𝑎𝑟𝑑(𝑤)|の平均:2.2% Thin Slice のサイズは平均で約2.2% 従来のスライスサイズは約30%
RQ1:実験結果(3/3) • 60から80%の Thin Slice は,そのサイズが0.1%以下である • 残りの20から40%のThin Slice は,最大値付近に分布している 0.1% 10% 18%
RQ2:実験結果 • RQ2:データ依存関係の調査において有効であると考えられるThin Sliceはどの程度存在するか • 2 である後ろ向きThin Slice の割合:全スライス中の60% • 約60%の後ろ向きThin Slice は複数のメソッドにまたがっている • 3 かつ 2 であるThin Sliceの割合:全スライス中の10% • 約10%の後ろ向き Thin Slice が表すデータフローは複数のメソッドにまたがっており,データの生成元の数が3以下である
考察 • RQ1:Thin Slice のサイズについて • Thin Slice のサイズは平均的に十分小さい • 分布は非常に小さいものと大きいものの2つに分かれている • RQ2:Thin Slicing の有効性について • 複数のメソッドにまたがるものが約60% • 複数のメソッドにまたがり,データの生成元が少ないものが約10% • データフローを複数のメソッドに渡って追跡する作業をThin Slicing によって置き換えられる
まとめと今後の課題 • Thin Slice のサイズを計測し,統計的な評価を行った • サイズが平均して小さくなることを確認した • サイズが大きいものが20から40%存在する • Thin Slice のサイズが大きくなる原因を調査する • サイズが大きいスライスはどのようなデータフローであるか