diff --git a/bind/benchmarkpkg/benchmark.go b/bind/benchmarkpkg/benchmark.go new file mode 100644 index 0000000..86873c0 --- /dev/null +++ b/bind/benchmarkpkg/benchmark.go @@ -0,0 +1,159 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin linux + +// Package benchmarkpkg contains functions for benchmarking bound functions. +package benchmarkpkg + +import ( + "log" + "time" +) + +type Benchmarks interface { + // It seems to be much faster to call a native function from Java + // when there is already a native call earlier in the stack. + // + // Run runs a named benchmark from a different thread, with + // no native call prior in the stack. + Run(name string, n int) + // RunDirect runs a named benchmark directly, with the native + // context from the call itself. + RunDirect(name string, n int) + + // Callbacks for Go benchmarks + NewI() I + Noargs() + Onearg(_ int) + Oneret() int + Ref(_ I) + Manyargs(_, _, _, _, _, _, _, _, _, _ int) + String(_ string) + Slice(_ []byte) +} + +type ( + I interface { + F() + } + + AnI struct { + } +) + +func (_ *AnI) F() { +} + +func NewI() I { + return new(AnI) +} + +func runBenchmark(name string, f func(n int)) { + // Run once for warmup + f(1) + n := 1000 + var dt time.Duration + minDuration := 1 * time.Second + for dt < minDuration { + n *= 2 + t0 := time.Now() + f(n) + dt = time.Since(t0) + } + log.Printf("Benchmark%s %d %d ns/op\n", name, n, dt.Nanoseconds()/int64(n)) +} + +func runGoBenchmark(name string, f func()) { + runBenchmark("Go"+name, func(n int) { + for i := 0; i < n; i++ { + f() + } + }) + runBenchmark("Go"+name+"Direct", func(n int) { + done := make(chan struct{}) + go func() { + for i := 0; i < n; i++ { + f() + } + close(done) + }() + <-done + }) +} + +func RunBenchmarks(b Benchmarks) { + names := []string{ + "Empty", + "Noargs", + "Onearg", + "Oneret", + "Manyargs", + "Refjava", + "Refgo", + "StringShort", + "StringLong", + "StringShortUnicode", + "StringLongUnicode", + "SliceShort", + "SliceLong", + } + for _, name := range names { + runBenchmark("Java"+name, func(n int) { + b.Run(name, n) + }) + runBenchmark("Java"+name+"Direct", func(n int) { + b.RunDirect(name, n) + }) + } + runGoBenchmark("Empty", func() {}) + runGoBenchmark("Noarg", func() { b.Noargs() }) + runGoBenchmark("Onearg", func() { b.Onearg(0) }) + runGoBenchmark("Oneret", func() { b.Oneret() }) + javaRef := b.NewI() + runGoBenchmark("Refjava", func() { b.Ref(javaRef) }) + goRef := NewI() + runGoBenchmark("Refgo", func() { b.Ref(goRef) }) + runGoBenchmark("Manyargs", func() { b.Manyargs(0, 0, 0, 0, 0, 0, 0, 0, 0, 0) }) + runGoBenchmark("StringShort", func() { b.String(ShortString) }) + runGoBenchmark("StringLong", func() { b.String(LongString) }) + runGoBenchmark("StringShortUnicode", func() { b.String(ShortStringUnicode) }) + runGoBenchmark("StringLongUnicode", func() { b.String(LongStringUnicode) }) + runGoBenchmark("SliceShort", func() { b.Slice(ShortSlice) }) + runGoBenchmark("SliceLong", func() { b.Slice(LongSlice) }) +} + +func Noargs() { +} + +func Onearg(_ int) { +} + +func Manyargs(_, _, _, _, _, _, _, _, _, _ int) { +} + +func Oneret() int { + return 0 +} + +func String(_ string) { +} + +func Slice(_ []byte) { +} + +func Ref(_ I) { +} + +var ( + ShortSlice = make([]byte, 10) + LongSlice = make([]byte, 100000) +) + +const ( + ShortString = "Hello, World!" + LongString = "Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World!" + ShortStringUnicode = "Hello, 世界!" + LongStringUnicode = "Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界!" +) diff --git a/bind/java/SeqBench.java b/bind/java/SeqBench.java new file mode 100644 index 0000000..5930459 --- /dev/null +++ b/bind/java/SeqBench.java @@ -0,0 +1,146 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package go; + +import android.test.InstrumentationTestCase; +import android.util.Log; + +import java.util.Map; +import java.util.HashMap; + +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; + +import go.benchmarkpkg.Benchmarkpkg; + +public class SeqBench extends InstrumentationTestCase { + + public static class AnI extends Benchmarkpkg.I.Stub { + @Override public void F() { + } + } + + private static class Benchmarks extends Benchmarkpkg.Benchmarks.Stub { + private static Map benchmarks; + private static ExecutorService executor = Executors.newSingleThreadExecutor(); + + static { + benchmarks = new HashMap(); + benchmarks.put("Empty", new Runnable() { + @Override public void run() { + } + }); + benchmarks.put("Noargs", new Runnable() { + @Override public void run() { + Benchmarkpkg.Noargs(); + } + }); + benchmarks.put("Onearg", new Runnable() { + @Override public void run() { + Benchmarkpkg.Onearg(0); + } + }); + benchmarks.put("Manyargs", new Runnable() { + @Override public void run() { + Benchmarkpkg.Manyargs(0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + } + }); + benchmarks.put("Oneret", new Runnable() { + @Override public void run() { + Benchmarkpkg.Oneret(); + } + }); + final Benchmarkpkg.I javaRef = new AnI(); + benchmarks.put("Refjava", new Runnable() { + @Override public void run() { + Benchmarkpkg.Ref(javaRef); + } + }); + final Benchmarkpkg.I goRef = Benchmarkpkg.NewI(); + benchmarks.put("Refgo", new Runnable() { + @Override public void run() { + Benchmarkpkg.Ref(goRef); + } + }); + benchmarks.put("StringShort", new Runnable() { + @Override public void run() { + Benchmarkpkg.String(Benchmarkpkg.ShortString); + } + }); + benchmarks.put("StringLong", new Runnable() { + @Override public void run() { + Benchmarkpkg.String(Benchmarkpkg.LongString); + } + }); + benchmarks.put("StringShortUnicode", new Runnable() { + @Override public void run() { + Benchmarkpkg.String(Benchmarkpkg.ShortStringUnicode); + } + }); + benchmarks.put("StringLongUnicode", new Runnable() { + @Override public void run() { + Benchmarkpkg.String(Benchmarkpkg.LongStringUnicode); + } + }); + final byte[] shortSlice = Benchmarkpkg.getShortSlice(); + benchmarks.put("SliceShort", new Runnable() { + @Override public void run() { + Benchmarkpkg.Slice(shortSlice); + } + }); + final byte[] longSlice = Benchmarkpkg.getLongSlice(); + benchmarks.put("SliceLong", new Runnable() { + @Override public void run() { + Benchmarkpkg.Slice(longSlice); + } + }); + } + + public void RunDirect(String name, final long n) { + final Runnable r = benchmarks.get(name); + try { + executor.submit(new Runnable() { + @Override public void run() { + for (int i = 0; i < n; i++) { + r.run(); + } + } + }).get(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public void Run(String name, long n) { + final Runnable r = benchmarks.get(name); + for (int i = 0; i < n; i++) { + r.run(); + } + } + + @Override public Benchmarkpkg.I NewI() { + return new AnI(); + } + @Override public void Ref(Benchmarkpkg.I i) { + } + @Override public void Noargs() { + } + @Override public void Onearg(long i) { + } + @Override public long Oneret() { + return 0; + } + @Override public void Manyargs(long p0, long p1, long p2, long p3, long p4, long p5, long p6, long p7, long gp8, long p9) { + } + @Override public void String(String s) { + } + @Override public void Slice(byte[] s) { + } + } + + public void testBenchmark() { + Benchmarkpkg.RunBenchmarks(new Benchmarks()); + } +} diff --git a/bind/java/seq_test.go b/bind/java/seq_test.go index 646ec46..7006cdf 100644 --- a/bind/java/seq_test.go +++ b/bind/java/seq_test.go @@ -19,6 +19,24 @@ import ( // This requires the gradle command in PATH and // the Android SDK whose path is available through ANDROID_HOME environment variable. func TestJavaSeqTest(t *testing.T) { + runTest(t, "golang.org/x/mobile/bind/java/testpkg", "SeqTest") +} + +// TestJavaSeqBench runs java test SeqBench.java, with the same +// environment requirements as TestJavaSeqTest. +// +// The benchmarks runs on the phone, so the benchmarkpkg implements +// rudimentary timing logic and outputs benchcmp compatible runtimes +// to logcat. Use +// +// adb logcat -v raw GoLog:* *:S +// +// while running the benchmark to see the results. +func TestJavaSeqBench(t *testing.T) { + runTest(t, "golang.org/x/mobile/bind/benchmarkpkg", "SeqBench") +} + +func runTest(t *testing.T, pkgName, javaCls string) { if _, err := run("which gradle"); err != nil { t.Skip("command gradle not found, skipping") } @@ -57,20 +75,20 @@ func TestJavaSeqTest(t *testing.T) { } } - buf, err := run("gomobile bind golang.org/x/mobile/bind/java/testpkg") + buf, err := run("gomobile bind -o pkg.aar " + pkgName) if err != nil { t.Logf("%s", buf) t.Fatalf("failed to run gomobile bind: %v", err) } - fname := filepath.Join(tmpdir, "libs", "testpkg.aar") - err = cp(fname, filepath.Join(tmpdir, "testpkg.aar")) + fname := filepath.Join(tmpdir, "libs", "pkg.aar") + err = cp(fname, filepath.Join(tmpdir, "pkg.aar")) if err != nil { - t.Fatalf("failed to copy testpkg.aar: %v", err) + t.Fatalf("failed to copy pkg.aar: %v", err) } - fname = filepath.Join(tmpdir, "src/androidTest/java/go/SeqTest.java") - err = cp(fname, filepath.Join(cwd, "SeqTest.java")) + fname = filepath.Join(tmpdir, "src/androidTest/java/go/"+javaCls+".java") + err = cp(fname, filepath.Join(cwd, javaCls+".java")) if err != nil { t.Fatalf("failed to copy SeqTest.java: %v", err) } @@ -149,6 +167,6 @@ repositories { } dependencies { compile 'com.android.support:appcompat-v7:19.0.0' - compile(name: "testpkg", ext: "aar") + compile(name: "pkg", ext: "aar") } `