Repository navigation
perf: guard ObjectTable with a monitor instead of StampedLock - #2103
Conversation
StampedLock lives in the boot classpath but outside the boot image, so on device it runs interpreted, and an app's baseline profile can't compile it. A monitor's uncontended enter/exit is compiled into every caller. Reads now take the lock too, which also removes the torn-read handling the optimistic path needed.
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
ObjectTable was only concurrent where StampedLock exists (API 24+). With a monitor there is no reason to keep API 21-23 on the slower paths: argument objects resolved natively under the isolate lock, and new ids registered through a separate path. findIdOptimistic is now findId, and getOrCreateObjectId and packageArgs register through findOrRegister alone.
Description
ObjectTable(added in #2099) guarded its tables withjava.util.concurrent.locks.StampedLock: write locks for mutations, optimistic reads elsewhere. On device,StampedLockruns interpreted. It lives in the boot classpath (core-oj) but outside the boot image, and an app's baseline profile can't compile boot-classpath methods. A profile of NativeScript core'sapps/automatedsuite showedStampedLock.tryWriteLock/casState/writeLock/unlockWriteInternalrunning in the interpreter on the main thread.This replaces it with
synchronized (this). An uncontended monitor enter/exit is compiled into every caller. Reads now take the lock too, so the torn-read handling the optimistic path needed (findIdUnlocked,getUnlocked) is gone. A second commit makes the table concurrent on every API level. It was limited to API 24+ only becauseStampedLockneeds it. On API 21–23, argument objects were resolved natively under the isolate lock and new ids took a separate path; now every level registers throughfindOrRegister.findIdOptimisticis merged intofindId, andgetOrCreateObjectIdtakes one lock instead of a lookup followed by a registration.JNI microbenchmarks (
debug.ns.jnibench, optimized release, SM-G780G;mainand this branch interleaved, 2 passes × 3 runs; median ns):new java.lang.Object()new java.lang.Object()[alloc, kept alive]JniBench.newObjectInJava()[alloc, kept alive]static.newObjectOfManyClasses()[16 classes]static.newObjectInJava()[new wrapper]static.newInstance(extended class)[Java-created]new Object.extend({})()callback.noArgs()The other rows are within ±2%. Even after the JIT has compiled it,
StampedLock's write path was costly.Known trade-off: when 2–4 Java threads call into JS at the same time, their object-id lookups used to be lock-free optimistic reads and now contend on the monitor: +3..+6% in those benchmarks. Apps run JS on one thread in the common case, where this is a clear win.
Real apps: no measurable difference on the template app's cold start (497/491 vs 503/494 ms) or on a 1000-tile flex-wrap mount. The phone was thermally throttling during that comparison, so those numbers only rule out a large effect.
Does your commit message include the wording below to reference a specific issue in this repo?
No issue.
Related Pull Requests
ObjectTable.Does your pull request have unit tests?
Yes. The
ObjectTableJVM test for concurrent readers and a writer now requires every read of a stable entry to hit; it used to allow optimistic misses. The test for disabled optimistic reads is gone with the flag. JVM tests: 29/29.Full device suite:
mainfails on that emulator:ByteBuffer.toString()'s newercap=format;On API 23 the harness also reports "App has crashed - ErrorReportActivity" for both
mainand this PR.run-as ... lsexits 0 there even when the file is missing, so the harness's checks misfire; the results were read from the XML directly.Summary by CodeRabbit