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())