mirror of
https://github.com/netbirdio/gomobile-tvos-fork.git
synced 2026-05-22 18:43:29 -07:00
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:
+31
-88
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user