perf: While-loop stdlib optimizations + base64DecodeBytes unsigned fix#700
perf: While-loop stdlib optimizations + base64DecodeBytes unsigned fix#700He-Pin wants to merge 1 commit intodatabricks:masterfrom
Conversation
- visibleKeyNames: replace forEach lambda with entrySet iterator while-loop to avoid per-key closure allocation overhead - base64DecodeBytes: replace .map() with while-loop + 0xff unsigned mask (fixes latent bug: high bytes 128-255 were sign-extended to negatives) - sum/avg: merge separate forall type-check and map+sum into single-pass while-loop that validates and accumulates simultaneously - remove/removeAt: replace slice++slice with System.arraycopy to avoid intermediate array allocation - ObjComp: cache preLocals++postLocals as lazy val allLocals to avoid repeated array concatenation in visitObjComp - Add regression test for base64DecodeBytes unsigned byte round-trip Upstream: jit branch commits 9149654, b833428, 09e2d3a
Code ReviewI found correctness issues in this PR: 1.
|
|
Closing: the base64DecodeBytes unsigned byte fix has been extracted to #705 as a clean bugfix PR. The while-loop performance optimizations will be resubmitted in a consolidated PR with native benchmarks. |
Motivation
Several stdlib functions use functional-style operations (
forEach,map,forall,slice ++ slice) that allocate closures and intermediate arrays on every invocation. These are hot paths in realistic workloads involving object field enumeration, base64 decoding, array sum/avg, and element removal.Additionally,
std.base64DecodeByteshad a latent bug where bytes >= 128 were sign-extended to negative numbers instead of being treated as unsigned (0-255), inconsistent with the Go and C++ Jsonnet implementations.Key Design Decisions
getAllKeys.forEachlambda withentrySet().iterator()while-loop to avoid per-key closure allocationforalltype-check pass andmap+sumcomputation into one while-loop that validates and accumulates simultaneouslyslice ++ slicewith directSystem.arraycopyto avoid intermediate array allocation& 0xffmask to convert signed Java bytes to unsigned (0-255), matching Go-jsonnet behaviorpreLocals ++ postLocalsas lazy val to avoid repeated array concatenation invisitObjCompModification
getAllKeys.forEachwithentrySet().iterator()while-loop invisibleKeyNamessum/avg;System.arraycopyforremove/removeAt& 0xffunsigned mask forbase64DecodeByteslazy val allLocalstoObjBody.ObjCompe.allLocalsinstead ofe.preLocals ++ e.postLocalsBenchmark Results
JMH A/B (bench.02, JVM,
-wi 3 -i 5 -f 1 -t 1)base64DecodeBytes (no significant change expected — decode-time dominates)
Analysis
& 0xfffix corrects a latent bug:Val.Num(pos, (byte)0xFF)would produce -1 instead of 255References
91496549,b833428e,09e2d3adfloat64(int(decodedBytes[i]))which gives unsigned 0-255Result
All tests pass (46/46). No regressions. -6.8% improvement on bench.02 with a bug fix for base64DecodeBytes high-byte handling.