Skip to content

Commit fef13a1

Browse files
authored
perf: cheaper object construction; drop markingMode full (#2093)
* test: benchmark the object lifecycle phases and many live wrappers Splits a wrapped object's cost into allocation (kept alive), release through a V8 GC and the Java GC that follows, for JS-constructed, JS-extended and Java-returned objects; and times an object return with 50k live wrappers. * perf: trim per-construction work in RegisterInstance - EncodeSignature formatted the argument count through a std::stringstream on every constructor and dynamic method resolution; it now appends std::to_string into a reserved buffer. - The instance's class is the global ref ResolveClass already returned from the class cache, so the second FindClass(fullClassName) lookup is gone. - JsArgConverter skips parsing the signature of a zero-argument constructor. - makeInstanceStrong skips storeClass, which takes two locks, when the previous registration stored the same class. * refactor: remove the markingMode "full" machinery Only markingMode "none" is supported. "full" was already non-functional: its weak callback queued wrappers for processing that no longer existed, so they were revived forever. - ObjectManager always arms the finalizer path; the full-mode weak callback, its GC bookkeeping, the batch DirectBuffer plumbing and the unused declarations behind it are gone, as are DirectBuffer itself and the Java makeInstanceWeak(ByteBuffer)/checkWeakObjectAreAlive/getMarkingModeOrdinal. - AppConfig drops the key (native config indices shift accordingly) and logs a warning when an app still sets markingMode to anything but "none". - The __markingMode global is gone; testGC.js no longer gates on it, so its specs now run (they pass under the finalizer path). * refactor: pass the native startup options by name initNativeScript received AppConfig's whole value array and the native side read five entries by KnownKeys ordinal, so removing or reordering a key silently shifted them (and read past the end under CheckJNI). The five values are now explicit initNativeScript parameters, gathered into a Runtime::StartupOptions on the native side. * test: keep the 50k wrappers pinned while their benchmark is timed
1 parent 132f5cf commit fef13a1

17 files changed

Lines changed: 140 additions & 540 deletions

‎test-app/app/src/main/assets/app/package.json‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
"gcThrottleTime": 500,
77
"memoryCheckInterval": 10,
88
"freeMemoryRatio": 0.50,
9-
"markingMode": "none",
109
"maxLogcatObjectSize": 1024,
1110
"forceLog": false,
1211
"suppressCallJSMethodExceptions": false,

‎test-app/app/src/main/assets/app/tests/benchmarks/jniBenchmarks.js‎

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,41 @@ function measureCallback(name, cb, kind, iterations) {
5353
return report(name, samples);
5454
}
5555

56+
57+
// Splits an object's lifecycle into allocation (objects kept alive), release through a
58+
// V8 GC (wrapper finalization and the Java-side strong -> weak bookkeeping), and the
59+
// Java GC that follows. Each phase is reported per object.
60+
function measureLifecycle(name, count, create) {
61+
if (FILTER && name.indexOf(FILTER) < 0) return null;
62+
count *= SCALE;
63+
var javaRuntime = java.lang.Runtime.getRuntime();
64+
var alloc = [], release = [], javaGc = [];
65+
var warm = [];
66+
for (var w = 0; w < Math.max(1000, count >> 2); w++) warm.push(create());
67+
warm = null;
68+
gc();
69+
javaRuntime.gc();
70+
for (var r = 0; r < ROUNDS; r++) {
71+
var kept = new Array(count);
72+
var start = performance.now();
73+
for (var i = 0; i < count; i++) kept[i] = create();
74+
alloc.push((performance.now() - start) * 1e6 / count);
75+
76+
kept = null;
77+
start = performance.now();
78+
gc();
79+
release.push((performance.now() - start) * 1e6 / count);
80+
81+
start = performance.now();
82+
javaRuntime.gc();
83+
javaGc.push((performance.now() - start) * 1e6 / count);
84+
}
85+
report(name + " [alloc, kept alive]", alloc);
86+
report(name + " [release: V8 gc]", release);
87+
return report(name + " [release: Java gc]", javaGc);
88+
}
89+
90+
5691
function buildBenchmarks() {
5792
var bench = new JniBench();
5893
var other = bench.getOther();
@@ -244,6 +279,35 @@ function buildBenchmarks() {
244279
return s;
245280
});
246281
},
282+
function () {
283+
return measureLifecycle("lifecycle new java.lang.Object()", 20000, function () {
284+
return new java.lang.Object();
285+
});
286+
},
287+
function () {
288+
return measureLifecycle("lifecycle new Object.extend({})()", 20000, function () {
289+
return new Extended();
290+
});
291+
},
292+
function () {
293+
return measureLifecycle("lifecycle JniBench.newObjectInJava()", 20000, function () {
294+
return JniBench.newObjectInJava();
295+
});
296+
},
297+
function () {
298+
if (FILTER && "instance.self() [50k live wrappers]".indexOf(FILTER) < 0) return null;
299+
var live = new Array(50000);
300+
for (var i = 0; i < live.length; i++) live[i] = new java.lang.Object();
301+
// Pinned from a global: an optimized frame may drop a local after its last use.
302+
globalThis.__jniBenchLive = live;
303+
var result = measureJs("instance.self() [50k live wrappers]", 50000, function (n) {
304+
var s;
305+
for (var i = 0; i < n; i++) s = bench.self();
306+
return s;
307+
});
308+
globalThis.__jniBenchLive = null;
309+
return result;
310+
},
247311
function () {
248312
return measureCallback("callback.noArgs()", callback, 0, 20000);
249313
},

‎test-app/app/src/main/assets/app/tests/testGC.js‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
// Run GC tests only in Full marking mode
2-
let describeFunc = __markingMode == 0 ? describe : xdescribe;
3-
4-
describeFunc("Tests garbage collection", function () {
1+
describe("Tests garbage collection", function () {
52
var myCustomEquality = function(first, second) {
63
return first == second;
74
};

‎test-app/runtime/CMakeLists.txt‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,6 @@ add_library(
182182
src/main/cpp/ConcurrentQueue.cpp
183183
src/main/cpp/Constants.cpp
184184
src/main/cpp/CrashBreadcrumbs.cpp
185-
src/main/cpp/DirectBuffer.cpp
186185
src/main/cpp/ErrorEvents.cpp
187186
src/main/cpp/EventLoop.cpp
188187
src/main/cpp/Events.cpp

‎test-app/runtime/src/main/cpp/CallbackHandlers.cpp‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ bool CallbackHandlers::RegisterInstance(Isolate *isolate, const Local<Object> &j
154154
success = !localInstance.IsNull();
155155

156156
if (success) {
157-
jclass instanceClass = env.FindClass(fullClassName);
158-
objectManager->SetJavaClass(jsObject, instanceClass);
157+
// ResolveClass returned the class cache's global ref for fullClassName.
158+
objectManager->SetJavaClass(jsObject, generatedJavaClass);
159159
} else {
160160
DEBUG_WRITE_FORCE("RegisterInstance failed with null new instance class: %s",
161161
fullClassName.c_str());

‎test-app/runtime/src/main/cpp/DirectBuffer.cpp‎

Lines changed: 0 additions & 56 deletions
This file was deleted.

‎test-app/runtime/src/main/cpp/DirectBuffer.h‎

Lines changed: 0 additions & 30 deletions
This file was deleted.

‎test-app/runtime/src/main/cpp/JsArgConverter.cpp‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,10 @@ JsArgConverter::JsArgConverter(const v8::FunctionCallbackInfo<Value> &args,
8585
m_error(Error()) {
8686
m_argsLen = args.Length();
8787

88-
JniSignatureParser parser(methodSignature);
89-
m_ownedTokens = parser.Parse();
88+
if (m_argsLen > 0) {
89+
JniSignatureParser parser(methodSignature);
90+
m_ownedTokens = parser.Parse();
91+
}
9092

9193
for (int i = 0; i < m_argsLen; i++) {
9294
m_isValid = ConvertArg(args[i], i);

‎test-app/runtime/src/main/cpp/MethodCache.cpp‎

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
#include "NativeScriptException.h"
1414
#include "Runtime.h"
1515
#include "ObjectManager.h"
16-
#include <sstream>
1716

1817
using namespace v8;
1918

@@ -111,19 +110,14 @@ MethodCache::CacheMethodInfo MethodCache::ResolveConstructorSignature(const Args
111110

112111
// Encoded signature <className>.S/I.<methodName>.<argsCount>.<arg1class>.<...>
113112
string MethodCache::EncodeSignature(const string& className, const string& methodName, const FunctionCallbackInfo<Value>& args, bool isStatic) {
114-
string sig(className);
115-
sig.append(".");
116-
if (isStatic) {
117-
sig.append("S.");
118-
} else {
119-
sig.append("I.");
120-
}
113+
int len = args.Length();
114+
string sig;
115+
sig.reserve(className.size() + methodName.size() + 8 + len * 12);
116+
sig.append(className);
117+
sig.append(isStatic ? ".S." : ".I.");
121118
sig.append(methodName);
122119
sig.append(".");
123-
int len = args.Length();
124-
stringstream s;
125-
s << len;
126-
sig.append(s.str());
120+
sig.append(std::to_string(len));
127121

128122
for (int i = 0; i < len; i++) {
129123
sig.append(".");

0 commit comments

Comments
 (0)