mobile/bind: replace seq serialization with direct calls

The seq serialization machinery is a historic artifact from when Go
mobile code had to run in a separate process. Now that Go code is running
in-process, replace the explicit serialization with direct calls and pass
arguments on the stack.

The benefits are a much smaller bind runtime, much less garbage (and, in
Java, fewer objects with finalizers), less argument copying, and faster
cross-language calls.
The cost is a more complex generator, because some of the work from the
bind runtime is moved to generated code. Generated code now handles
conversion between Go and Java/ObjC types, multiple return values and memory
management of byte slice and string arguments.

To overcome the lack of calling C code between Go packages, all bound
packages now end up in the same (fake) package, "gomobile_bind", instead of
separate packages (go_<pkgname>). To avoid name clashes, the package name is
added as a prefix to generated functions and types.

Also, don't copy byte arrays passed to Go, saving call time and
allowing read([]byte)-style interfaces to foreign callers (#12113).

Finally, add support for nil interfaces and struct pointers to objc.

This is a large CL, but most of the changes stem from changing testdata.

The full benchcmp output on the CL/20095 benchmarks on my Nexus 5 is
reproduced below. Note that the savings for the JavaSlice* benchmarks are
skewed because byte slices are no longer copied before passing them to Go.

benchmark                                 old ns/op     new ns/op     delta
BenchmarkJavaEmpty                        26.0          19.0          -26.92%
BenchmarkJavaEmptyDirect                  23.0          22.0          -4.35%
BenchmarkJavaNoargs                       7685          2339          -69.56%
BenchmarkJavaNoargsDirect                 17405         8041          -53.80%
BenchmarkJavaOnearg                       26887         2366          -91.20%
BenchmarkJavaOneargDirect                 34266         7910          -76.92%
BenchmarkJavaOneret                       38325         2245          -94.14%
BenchmarkJavaOneretDirect                 46265         7708          -83.34%
BenchmarkJavaManyargs                     41720         2535          -93.92%
BenchmarkJavaManyargsDirect               51026         8373          -83.59%
BenchmarkJavaRefjava                      38139         21260         -44.26%
BenchmarkJavaRefjavaDirect                42706         28150         -34.08%
BenchmarkJavaRefgo                        34403         6843          -80.11%
BenchmarkJavaRefgoDirect                  40193         16582         -58.74%
BenchmarkJavaStringShort                  32366         9323          -71.20%
BenchmarkJavaStringShortDirect            41973         19118         -54.45%
BenchmarkJavaStringLong                   127879        94420         -26.16%
BenchmarkJavaStringLongDirect             133776        114760        -14.21%
BenchmarkJavaStringShortUnicode           32562         9221          -71.68%
BenchmarkJavaStringShortUnicodeDirect     41464         19094         -53.95%
BenchmarkJavaStringLongUnicode            131015        89401         -31.76%
BenchmarkJavaStringLongUnicodeDirect      134130        90786         -32.31%
BenchmarkJavaSliceShort                   42462         7538          -82.25%
BenchmarkJavaSliceShortDirect             52940         17017         -67.86%
BenchmarkJavaSliceLong                    138391        8466          -93.88%
BenchmarkJavaSliceLongDirect              205804        15666         -92.39%
BenchmarkGoEmpty                          3.00          3.00          +0.00%
BenchmarkGoEmptyDirect                    3.00          3.00          +0.00%
BenchmarkGoNoarg                          40342         13716         -66.00%
BenchmarkGoNoargDirect                    46691         13569         -70.94%
BenchmarkGoOnearg                         43529         13757         -68.40%
BenchmarkGoOneargDirect                   44867         14078         -68.62%
BenchmarkGoOneret                         45456         13559         -70.17%
BenchmarkGoOneretDirect                   44694         13442         -69.92%
BenchmarkGoRefjava                        55111         28071         -49.06%
BenchmarkGoRefjavaDirect                  60883         26872         -55.86%
BenchmarkGoRefgo                          57038         29223         -48.77%
BenchmarkGoRefgoDirect                    56153         27812         -50.47%
BenchmarkGoManyargs                       67967         17398         -74.40%
BenchmarkGoManyargsDirect                 60617         16998         -71.96%
BenchmarkGoStringShort                    57538         22600         -60.72%
BenchmarkGoStringShortDirect              52627         22704         -56.86%
BenchmarkGoStringLong                     128485        52530         -59.12%
BenchmarkGoStringLongDirect               138377        52079         -62.36%
BenchmarkGoStringShortUnicode             57062         22994         -59.70%
BenchmarkGoStringShortUnicodeDirect       62563         22938         -63.34%
BenchmarkGoStringLongUnicode              139913        55553         -60.29%
BenchmarkGoStringLongUnicodeDirect        150863        57791         -61.69%
BenchmarkGoSliceShort                     59279         20215         -65.90%
BenchmarkGoSliceShortDirect               60160         21136         -64.87%
BenchmarkGoSliceLong                      411225        301870        -26.59%
BenchmarkGoSliceLongDirect                399029        298915        -25.09%

Fixes golang/go#12619
Fixes golang/go#12113
Fixes golang/go#13033

Change-Id: I2b45e9e98a1248e3c23a5137f775f7364908bec7
Reviewed-on: https://go-review.googlesource.com/19821
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
This commit is contained in:
Elias Naur
2016-03-03 15:03:45 +00:00
parent 988d17d203
commit 6fca37c69e
100 changed files with 6574 additions and 6307 deletions
+31 -88
View File
@@ -12,6 +12,12 @@ import java.util.logging.Logger;
public class Seq {
private static Logger log = Logger.getLogger("GoSeq");
// also known to bind/seq/ref.go and bind/objc/seq_darwin.m
private static final int NULL_REFNUM = 41;
// use single Ref for null Seq.Object
public static final Ref nullRef = new Ref(NULL_REFNUM, null);
static {
// Look for the shim class auto-generated by gomobile bind.
// Its only purpose is to call System.loadLibrary.
@@ -26,70 +32,34 @@ public class Seq {
} catch (IllegalAccessException e) {
log.severe("LoadJNI class bad field: " + e);
}
initSeq();
init();
}
@SuppressWarnings("UnusedDeclaration")
private long memptr; // holds C-allocated pointer
private static native void init();
public Seq() {
ensure(64);
// Empty method to run class initializer
public static void touch() {}
private Seq() {
}
private static void throwException(String msg) throws Exception { // JNI helper
throw new Exception(msg);
}
// ctx is an android.context.Context.
static native void setContext(java.lang.Object ctx);
// Ensure that at least size bytes can be written to the Seq.
// Any existing data in the buffer is preserved.
public native void ensure(int size);
// Moves the internal buffer offset back to zero.
// Length and contents are maintained. Data can be read after a reset.
public native void resetOffset();
public native void log(String label);
public native boolean readBool();
public native byte readInt8();
public native short readInt16();
public native int readInt32();
public native long readInt64();
public long readInt() { return readInt64(); }
public native float readFloat32();
public native double readFloat64();
public native String readUTF16();
public String readString() { return readUTF16(); }
public native byte[] readByteArray();
public native void writeBool(boolean v);
public native void writeInt8(byte v);
public native void writeInt16(short v);
public native void writeInt32(int v);
public native void writeInt64(long v);
public void writeInt(long v) { writeInt64(v); }
public native void writeFloat32(float v);
public native void writeFloat64(double v);
public native void writeUTF16(String v);
public void writeString(String v) { writeUTF16(v); }
public native void writeByteArray(byte[] v);
public void writeRef(Ref ref) {
if (ref == null)
ref = RefTracker.nullRef;
public static int incRef(Seq.Object o) {
Ref ref = o.ref();
tracker.inc(ref);
writeInt32(ref.refnum);
return ref.refnum;
}
public Ref readRef() {
int refnum = readInt32();
public static Ref getRef(int refnum) {
return tracker.get(refnum);
}
static native void initSeq();
// Informs the Go ref tracker that Java is done with this ref.
static native void destroyRef(int refnum);
@@ -98,30 +68,9 @@ public class Seq {
return tracker.createRef(o);
}
// sends a function invocation request to Go.
//
// Blocks until the function completes.
// If the request is for a method, the first element in src is
// a Ref to the receiver.
public static native void send(String descriptor, int code, Seq src, Seq dst);
protected void finalize() throws Throwable {
super.finalize();
free();
}
private native void free();
public static Seq recv(Seq in, int code, int refnum) {
Seq out = new Seq();
if (code == -1) {
// Special signal from seq.FinalizeRef.
tracker.dec(refnum);
return out;
}
Ref r = tracker.get(refnum);
r.obj.call(code, in, out);
return out;
// decRef is called from seq.FinalizeRef
static void decRef(int refnum) {
tracker.dec(refnum);
}
// An Object is a Java object that matches a Go object.
@@ -133,7 +82,6 @@ public class Seq {
// generated abstract Stub.
public interface Object {
public Ref ref();
public void call(int code, Seq in, Seq out);
}
// A Ref is an object tagged with an integer for passing back and
@@ -148,11 +96,11 @@ public class Seq {
public static final class Ref {
// refnum < 0: Go object tracked by Java
// refnum > 0: Java object tracked by Go
int refnum;
public final int refnum;
int refcnt; // for Java obj: track how many times sent to Go.
public Seq.Object obj; // for Java obj: pointers to the Java obj.
public final Seq.Object obj; // for Java obj: pointers to the Java obj.
Ref(int refnum, Seq.Object o) {
this.refnum = refnum;
@@ -174,10 +122,6 @@ public class Seq {
static final class RefTracker {
private static final int REF_OFFSET = 42;
private static final int NULL_REFNUM = 41; // also known to bind/seq/ref.go
// use single Ref for null Seq.Object
private static final Ref nullRef = new Ref(NULL_REFNUM, null);
// Next Java object reference number.
//
@@ -200,7 +144,7 @@ public class Seq {
// We don't keep track of the Go object.
return;
}
if (refnum == nullRef.refnum) {
if (refnum == NULL_REFNUM) {
return;
}
// Count how many times this ref's Java object is passed to Go.
@@ -225,7 +169,7 @@ public class Seq {
log.severe("dec request for Go object "+ refnum);
return;
}
if (refnum == nullRef.refnum) {
if (refnum == Seq.nullRef.refnum) {
return;
}
// Java objects are removed on request of Go.
@@ -241,7 +185,7 @@ public class Seq {
synchronized Ref createRef(Seq.Object o) {
if (o == null) {
return nullRef;
return Seq.nullRef;
}
if (next == Integer.MAX_VALUE) {
throw new RuntimeException("createRef overflow for " + o);
@@ -262,10 +206,9 @@ public class Seq {
//
// When we have real code, examine the tradeoffs.
synchronized Ref get(int refnum) {
if (refnum > 0) {
if (refnum == nullRef.refnum) {
return nullRef;
}
if (refnum == NULL_REFNUM) {
return nullRef;
} else if (refnum > 0) {
Ref ref = javaObjs.get(refnum);
if (ref == null) {
throw new RuntimeException("unknown java Ref: "+refnum);