Add more split benchmarking

Add benchmarks for
  Calls of bigger methods
  Callbacks from the split to the code in the base
  Constructor calls
  Instantiation of classes that inherit across the split boundary

Change-Id: Iff45d60434d29f7d70786208ac2bd14231a4d30f
diff --git a/src/test/sampleApks/split/split.spec b/src/test/sampleApks/split/split.spec
index a06e259..ed40b98 100644
--- a/src/test/sampleApks/split/split.spec
+++ b/src/test/sampleApks/split/split.spec
@@ -1 +1,2 @@
 com.android.tools.r8.sample.split.SplitClass:split
+com.android.tools.r8.sample.split.SplitInheritBase:split
diff --git a/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/BaseClass.java b/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/BaseClass.java
new file mode 100644
index 0000000..eef119c
--- /dev/null
+++ b/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/BaseClass.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.sample.split;
+
+
+public class BaseClass {
+
+  int initialValue;
+
+  public BaseClass(int initialValue) {
+    this.initialValue = initialValue;
+  }
+
+  public int calculate(int x) {
+    int result = 2;
+    for (int i = 0; i < 42; i++) {
+      result += initialValue + x;
+    }
+    return result;
+  }
+
+   public int largeMethod(int x, int y) {
+    int a = x + y;
+    int b;
+    int c;
+    double d;
+    String s;
+    if (a < 22) {
+      b = a * 42 / y;
+      c = b - 80;
+      d = a + b + c * y - x * a;
+      s = "foobar";
+    } else if (a < 42) {
+      b = x * 42 / y;
+      c = b - 850;
+      d = a + b + c * x - x * a;
+      s = "foo";
+    } else {
+      b = x * 42 / y;
+      c = b - 850;
+      d = a + b + c * x - x * a;
+      s = "bar";
+    }
+    if (s.equals("bar")) {
+      d = 49;
+      s += b;
+    } else {
+      b = 63;
+      s = "barbar" + b;
+    }
+
+    if (a < 22) {
+      b = a * 42 / y;
+      c = b - 80;
+      d = a + b + c * y - x * a;
+      s = "foobar";
+    } else if (a < 42) {
+      b = x * 42 / y;
+      c = b - 850;
+      d = a + b + c * x - x * a;
+      s = "foo";
+    } else {
+      b = x * 42 / y;
+      c = b - 850;
+      d = a + b + c * x - x * a;
+      s = "bar";
+    }
+    if (s.equals("bar")) {
+      d = 49;
+      s += b;
+    } else {
+      b = 63;
+      s = "barbar" + b;
+    }
+
+    if (a < 22) {
+      b = a * 42 / y;
+      c = b - 80;
+      d = a + b + c * y - x * a;
+      s = "foobar";
+    } else if (a < 42) {
+      b = x * 42 / y;
+      c = b - 850;
+      d = a + b + c * x - x * a;
+      s = "foo";
+    } else {
+      b = x * 42 / y;
+      c = b - 850;
+      d = a + b + c * x - x * a;
+      s = "bar";
+    }
+    if (s.equals("bar")) {
+      d = 49;
+      s += b;
+    } else {
+      b = 63;
+      s = "barbar" + b;
+    }
+
+    return a + b - c * x;
+  }
+}
diff --git a/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/R8Activity.java b/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/R8Activity.java
index a80cf55..307cac9 100644
--- a/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/R8Activity.java
+++ b/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/R8Activity.java
@@ -8,33 +8,201 @@
 import android.os.Bundle;
 import com.android.tools.r8.sample.split.R;
 import com.android.tools.r8.sample.split.SplitClass;
+import java.util.ArrayList;
+import java.util.List;
+
 
 public class R8Activity extends Activity {
+  // Enables easy splitting of iterations to better see effect of jit in later versions of art
+  public static final int ITERATIONS = 100000;
+  public static final int SPLITS = 1;
+
   private int res = 0;
 
   public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setTheme(android.R.style.Theme_Light);
     setContentView(R.layout.main);
-    // Currently this is split up into 100 iterations to be able to better see
-    // the impact of the jit on later versions of art.
     long total = 0;
-    for (int i = 0; i < 100; i++) {
+    for (int i = 0; i < SPLITS; i++) {
+      total += benchmarkCallBaseline();
+    }
+    System.out.println("CallBaseline Total: " + total);
+
+    total = 0;
+    for (int i = 0; i < SPLITS; i++) {
       total += benchmarkCall();
     }
-    System.out.println("Total: " + total);
+    System.out.println("Call Total: " + total);
+
+    total = 0;
+    for (int i = 0; i < SPLITS; i++) {
+      total += benchmarkCallLocal();
+    }
+    System.out.println("CallLocal Total: " + total);
+
+    total = 0;
+    for (int i = 0; i < SPLITS; i++) {
+      total += benchmarkSplitCallback();
+    }
+    System.out.println("Callback Total: " + total);
+
+    total = 0;
+    for (int i = 0; i < SPLITS; i++) {
+      total += benchmarkConstructor();
+    }
+    System.out.println("Constructor Total: " + total);
+
+    total = 0;
+    for (int i = 0; i < SPLITS; i++) {
+      total += benchmarkConstructorLocal();
+    }
+    System.out.println("ConstructorLocal Total: " + total);
+
+    total = 0;
+    for (int i = 0; i < SPLITS; i++) {
+      total += benchmarkInheritanceConstructor();
+    }
+    System.out.println("InheritanceConstructor Total: " + total);
+
+    total = 0;
+    for (int i = 0; i < SPLITS; i++) {
+      total += benchmarkLargeMethodCall();
+    }
+    System.out.println("LargeMethodCall Total: " + total);
+
+    total = 0;
+    for (int i = 0; i < SPLITS; i++) {
+      total += benchmarkSplitCallbackLong();
+    }
+    System.out.println("CallbackLarge Total: " + total);
+
+    total = 0;
+    for (int i = 0; i < SPLITS; i++) {
+      total += benchmarkLargeMethodCallLocally();
+    }
+    System.out.println("LargeMethodCallLocal Total: " + total);
+
   }
 
-  public long benchmarkCall() {
+  private long benchmarkCall() {
     SplitClass split = new SplitClass(3);
     long start = System.nanoTime();
-    for (int i = 0; i < 1000; i++) {
+    for (int i = 0; i < ITERATIONS / SPLITS; i++) {
       // Ensure no dead code elimination.
       res = split.calculate(i);
     }
     long finish = System.nanoTime();
-    long timeElapsed = finish - start;
-    System.out.println("Took: " + timeElapsed);
+    long timeElapsed = (finish - start) / 1000;
     return timeElapsed;
   }
+
+  private long benchmarkCallLocal() {
+    long start = System.nanoTime();
+    for (int i = 0; i < ITERATIONS / SPLITS; i++) {
+      // Ensure no dead code elimination.
+      res = calculate(i);
+    }
+    long finish = System.nanoTime();
+    long timeElapsed = (finish - start) / 1000;
+    return timeElapsed;
+  }
+
+  private long benchmarkCallBaseline() {
+    SplitClass split = new SplitClass(3);
+    long start = System.nanoTime();
+    for (int i = 0; i < ITERATIONS / SPLITS; i++) {
+      int result = 2;
+      for (int j = 0; j < 42; j++) {
+        result += result + i;
+      }
+      res = result;
+    }
+    long finish = System.nanoTime();
+    long timeElapsed = (finish - start) / 1000;
+    return timeElapsed;
+  }
+
+  private long benchmarkInheritanceConstructor() {
+    List<SplitInheritBase> instances = new ArrayList<SplitInheritBase>();
+    long start = System.nanoTime();
+    for (int i = 0; i < ITERATIONS / SPLITS; i++) {
+      instances.add(new SplitInheritBase(i));
+    }
+    long finish = System.nanoTime();
+    long timeElapsed = (finish - start) / 1000;
+    return timeElapsed;
+  }
+
+  private long benchmarkConstructor() {
+    List<SplitClass> instances = new ArrayList<SplitClass>();
+    long start = System.nanoTime();
+    for (int i = 0; i < ITERATIONS / SPLITS; i++) {
+      instances.add(new SplitClass(i));
+    }
+    long finish = System.nanoTime();
+    long timeElapsed = (finish - start) / 1000;
+    return timeElapsed;
+  }
+
+  private long benchmarkConstructorLocal() {
+    List<BaseClass> instances = new ArrayList<BaseClass>();
+    long start = System.nanoTime();
+    for (int i = 0; i < ITERATIONS / SPLITS; i++) {
+      instances.add(new BaseClass(i));
+    }
+    long finish = System.nanoTime();
+    long timeElapsed = (finish - start) / 1000;
+    return timeElapsed;
+  }
+
+  private long benchmarkLargeMethodCall() {
+    SplitClass split = new SplitClass(3);
+    long start = System.nanoTime();
+    for (int i = 0; i < ITERATIONS / SPLITS; i++) {
+      // Ensure no dead code elimination.
+      res = split.largeMethod(i, i + 1);
+    }
+    long finish = System.nanoTime();
+    long timeElapsed = (finish - start) / 1000;
+    return timeElapsed;
+  }
+
+  private long benchmarkSplitCallback() {
+    SplitClass split = new SplitClass(3);
+    long start = System.nanoTime();
+    res = split.callBase();
+    long finish = System.nanoTime();
+    long timeElapsed = (finish - start) / 1000;
+    return timeElapsed;
+  }
+
+  private long benchmarkSplitCallbackLong() {
+    SplitClass split = new SplitClass(3);
+    long start = System.nanoTime();
+    res = split.callBaseLarge();
+    long finish = System.nanoTime();
+    long timeElapsed = (finish - start) / 1000;
+    return timeElapsed;
+  }
+
+  private long benchmarkLargeMethodCallLocally() {
+    BaseClass base = new BaseClass(3);
+    long start = System.nanoTime();
+    for (int i = 0; i < ITERATIONS / SPLITS; i++) {
+      // Ensure no dead code elimination.
+      res = base.largeMethod(i, i + 1);
+    }
+    long finish = System.nanoTime();
+    long timeElapsed = (finish - start) / 1000;
+    return timeElapsed;
+  }
+
+  public int calculate(int x) {
+    int result = 2;
+    for (int i = 0; i < 42; i++) {
+      result += res + x;
+    }
+    return result;
+  }
 }
diff --git a/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/SplitClass.java b/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/SplitClass.java
index 9b6990d..b493326 100644
--- a/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/SplitClass.java
+++ b/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/SplitClass.java
@@ -4,13 +4,17 @@
 
 package com.android.tools.r8.sample.split;
 
+import com.android.tools.r8.sample.split.R8Activity;
+
 public class SplitClass {
-  final int initialValue;
+  int initialValue;
 
   public SplitClass(int initialValue) {
     this.initialValue = initialValue;
   }
 
+
+
   public int calculate(int x) {
     int result = 2;
     for (int i = 0; i < 42; i++) {
@@ -18,4 +22,104 @@
     }
     return result;
   }
+
+  public int largeMethod(int x, int y) {
+    int a = x + y;
+    int b;
+    int c;
+    double d;
+    String s;
+    if (a < 22) {
+      b = a * 42 / y;
+      c = b - 80;
+      d = a + b + c * y - x * a;
+      s = "foobar";
+    } else if (a < 42) {
+      b = x * 42 / y;
+      c = b - 850;
+      d = a + b + c * x - x * a;
+      s = "foo";
+    } else {
+      b = x * 42 / y;
+      c = b - 850;
+      d = a + b + c * x - x * a;
+      s = "bar";
+    }
+    if (s.equals("bar")) {
+      d = 49;
+      s += b;
+    } else {
+      b = 63;
+      s = "barbar" + b;
+    }
+
+    if (a < 22) {
+      b = a * 42 / y;
+      c = b - 80;
+      d = a + b + c * y - x * a;
+      s = "foobar";
+    } else if (a < 42) {
+      b = x * 42 / y;
+      c = b - 850;
+      d = a + b + c * x - x * a;
+      s = "foo";
+    } else {
+      b = x * 42 / y;
+      c = b - 850;
+      d = a + b + c * x - x * a;
+      s = "bar";
+    }
+    if (s.equals("bar")) {
+      d = 49;
+      s += b;
+    } else {
+      b = 63;
+      s = "barbar" + b;
+    }
+
+    if (a < 22) {
+      b = a * 42 / y;
+      c = b - 80;
+      d = a + b + c * y - x * a;
+      s = "foobar";
+    } else if (a < 42) {
+      b = x * 42 / y;
+      c = b - 850;
+      d = a + b + c * x - x * a;
+      s = "foo";
+    } else {
+      b = x * 42 / y;
+      c = b - 850;
+      d = a + b + c * x - x * a;
+      s = "bar";
+    }
+    if (s.equals("bar")) {
+      d = 49;
+      s += b;
+    } else {
+      b = 63;
+      s = "barbar" + b;
+    }
+
+    return a + b - c * x;
+  }
+
+  public int callBase() {
+    BaseClass base = new BaseClass(initialValue);
+    for (int i = 0; i < R8Activity.ITERATIONS / R8Activity.SPLITS; i++) {
+      // Ensure no dead code elimination.
+      initialValue = base.calculate(i);
+    }
+    return initialValue;
+  }
+
+  public int callBaseLarge() {
+    BaseClass base = new BaseClass(initialValue);
+    for (int i = 0; i < R8Activity.ITERATIONS / R8Activity.SPLITS; i++) {
+      // Ensure no dead code elimination.
+      initialValue = base.largeMethod(i, i + 1);
+    }
+    return initialValue;
+  }
+
 }
diff --git a/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/SplitInheritBase.java b/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/SplitInheritBase.java
new file mode 100644
index 0000000..6c5ed1e
--- /dev/null
+++ b/src/test/sampleApks/split/src/com/android/tools/r8/sample/split/SplitInheritBase.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.sample.split;
+
+public class SplitInheritBase extends BaseClass {
+
+  public SplitInheritBase(int initialValue) {
+    super(initialValue);
+    initialValue = calculate(initialValue);
+  }
+
+  public int calculate(int x) {
+    return super.calculate(x) * 2;
+  }
+}
diff --git a/tools/build_sample_apk.py b/tools/build_sample_apk.py
index 90e34ed..f0c74d9 100755
--- a/tools/build_sample_apk.py
+++ b/tools/build_sample_apk.py
@@ -26,7 +26,7 @@
 DEFAULT_KEYSTORE = os.path.join(os.getenv('HOME'), '.android', 'debug.keystore')
 PACKAGE_PREFIX = 'com.android.tools.r8.sample'
 STANDARD_ACTIVITY = "R8Activity"
-BENCHMARK_ITERATIONS = 100
+BENCHMARK_ITERATIONS = 30
 
 SAMPLE_APKS = [
     'simple',
@@ -161,11 +161,15 @@
   utils.PrintCmd(command)
   subprocess.check_call(command)
 
-def run_adb(args):
+def run_adb(args, ignore_exit=False):
   command = ['adb']
   command.extend(args)
   utils.PrintCmd(command)
-  subprocess.check_call(command)
+  # On M adb install-multiple exits 0 but succeed in installing.
+  if ignore_exit:
+    subprocess.call(command)
+  else:
+    subprocess.check_call(command)
 
 def adb_install(apks):
   args = [
@@ -173,7 +177,7 @@
       '-r',
       '-d']
   args.extend(apks)
-  run_adb(args)
+  run_adb(args, ignore_exit=True)
 
 def create_temp_apk(app, prefix):
   temp_apk_path = os.path.join(get_bin_path(app), '%s.ap_' % app)
@@ -192,7 +196,7 @@
   run_adb(args)
 
 def start_logcat():
-  return subprocess.Popen(['adb', 'logcat'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+  return subprocess.Popen(['adb', 'logcat'], bufsize=1024*1024, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
 def start(app):
   args = ['shell', 'am', 'start', '-n', get_qualified_activity(app)]
@@ -211,34 +215,35 @@
   return lines
 
 def store_or_print_benchmarks(lines, output):
-  single_runs = []
-  total_time = None
-  # We assume that the individual runs are prefixed with 'Took: ' and the total time is
-  # prefixed with 'Total: '. The logcat lines looks like:
-  # 06-28 12:22:00.991 13698 13698 I System.out: Took: 61614
+  results = {}
+  overall_total = 0
+  # We assume that the total times are
+  # prefixed with 'NAME Total: '. The logcat lines looks like:
+  # 06-28 12:22:00.991 13698 13698 I System.out: Call Total: 61614
   for l in lines:
-    if 'Took: ' in l:
-      timing = l.split('Took: ')[1]
-      single_runs.append(timing)
     if 'Total: ' in l:
-      timing = l.split('Total: ')[1]
-      total_time = timing
-  assert len(single_runs) > 0
-  assert total_time
-  if not output:
-    print 'Individual timings: \n%s' % ''.join(single_runs)
-    print 'Total time: \n%s' % total_time
-    return
+      split = l.split('Total: ')
+      time = split[1]
+      name = split[0].split()[-1]
+      overall_total += int(time)
+      print '%s: %s' % (name, time)
+      results[name] = time
 
+  print 'Total: %s' % overall_total
+  if not output:
+    return overall_total
+  results['total'] = str(overall_total)
   output_dir = os.path.join(output, str(uuid.uuid4()))
   os.makedirs(output_dir)
-  single_run_file = os.path.join(output_dir, 'single_runs')
-  with open(single_run_file, 'w') as f:
-    f.writelines(single_runs)
-  total_file = os.path.join(output_dir, 'total')
-  with open(total_file, 'w') as f:
-    f.write(total_time)
-  print 'Result stored in %s and %s' % (single_run_file, total_file)
+  written_files = []
+  for name, time in results.iteritems():
+    total_file = os.path.join(output_dir, name)
+    written_files.append(total_file)
+    with open(total_file, 'w') as f:
+      f.write(time)
+
+  print 'Result stored in: \n%s' % ('\n'.join(written_files))
+  return overall_total
 
 def benchmark(app, output_dir):
   # Ensure app is not running
@@ -248,9 +253,14 @@
   start(app)
   # We could do better here by continiously parsing the logcat for a marker, but
   # this works nicely with the current setup.
-  time.sleep(3)
+  time.sleep(8)
   kill(app)
-  store_or_print_benchmarks(stop_logcat(logcat), output_dir)
+  return float(store_or_print_benchmarks(stop_logcat(logcat), output_dir))
+
+def ensure_no_logcat():
+  output = subprocess.check_output(['ps', 'aux'])
+  if 'adb logcat' in output:
+    raise Exception('You have adb logcat running, please close it and rerun')
 
 def Main():
   (options, args) = parse_options()
@@ -285,9 +295,12 @@
   print('Generated apks available at: %s' % ' '.join(apks))
   if options.install:
     adb_install(apks)
+  grand_total = 0
   if options.benchmark:
+    ensure_no_logcat()
     for _ in range(BENCHMARK_ITERATIONS):
-      benchmark(options.app, options.benchmark_output_dir)
+      grand_total += benchmark(options.app, options.benchmark_output_dir)
+  print 'Combined average: %s' % (grand_total/BENCHMARK_ITERATIONS)
 
 if __name__ == '__main__':
   sys.exit(Main())