Merge "Add more split benchmarking"
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())