From 53321104bdefb0862a881524d5446f00f16bc7f9 Mon Sep 17 00:00:00 2001 From: He-Pin Date: Thu, 21 May 2026 10:53:08 +0800 Subject: [PATCH] perf: use hybrid sort for inline object order Motivation: Large inline objects produced by strict JSON imports can exceed the small-object shape that computeSortedInlineOrder was originally tuned for. Native sampling on kube-prometheus showed sorted inline-order computation as a materialization hotspot, and insertion sort becomes quadratic on those wider objects. Modification: Keep insertion sort for small inline objects, and use an in-place quicksort with median-of-three pivot and insertion-sort cleanup for larger visible field sets. Result: Kube-prometheus Native A/B improved on top of strict JSON byte imports, with forward mean 145.3ms -> 140.0ms and reverse mean 151.6ms -> 148.9ms. Formatting and the full test suite pass. References: Upstream-base: databricks/sjsonnet@cedc083b4676be43e01bdd6f6cb5d7f4432d0d32 Prior optimization: 883fca5f perf: parse strict JSON imports from bytes --- sjsonnet/src/sjsonnet/Materializer.scala | 86 ++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 5 deletions(-) diff --git a/sjsonnet/src/sjsonnet/Materializer.scala b/sjsonnet/src/sjsonnet/Materializer.scala index 9892e3da..f0767b8e 100644 --- a/sjsonnet/src/sjsonnet/Materializer.scala +++ b/sjsonnet/src/sjsonnet/Materializer.scala @@ -619,20 +619,96 @@ object Materializer extends Materializer { } i += 1 } - // Insertion sort by key name (optimal for 2-8 elements) - i = 1 - while (i < visCount) { + sortInlineOrder(order, keys, visCount) + order + } + + private def sortInlineOrder(order: Array[Int], keys: Array[String], len: Int): Unit = { + if (len <= 1) return + if (len <= 16) insertionSortInlineOrder(order, keys, 0, len - 1) + else quickSortInlineOrder(order, keys, 0, len - 1) + } + + private def insertionSortInlineOrder( + order: Array[Int], + keys: Array[String], + left: Int, + right: Int): Unit = { + var i = left + 1 + while (i <= right) { val pivotIdx = order(i) val pivotKey = keys(pivotIdx) var j = i - 1 - while (j >= 0 && Util.compareStringsByCodepoint(keys(order(j)), pivotKey) > 0) { + while (j >= left && Util.compareStringsByCodepoint(keys(order(j)), pivotKey) > 0) { order(j + 1) = order(j) j -= 1 } order(j + 1) = pivotIdx i += 1 } - order + } + + private def quickSortInlineOrder( + order: Array[Int], + keys: Array[String], + left0: Int, + right0: Int): Unit = { + var left = left0 + var right = right0 + while (right - left > 16) { + val pivotKey = medianOfThreeKey(order, keys, left, right) + var i = left + var j = right + while (i <= j) { + while (Util.compareStringsByCodepoint(keys(order(i)), pivotKey) < 0) i += 1 + while (Util.compareStringsByCodepoint(keys(order(j)), pivotKey) > 0) j -= 1 + if (i <= j) { + val tmp = order(i) + order(i) = order(j) + order(j) = tmp + i += 1 + j -= 1 + } + } + if (j - left < right - i) { + if (left < j) quickSortInlineOrder(order, keys, left, j) + left = i + } else { + if (i < right) quickSortInlineOrder(order, keys, i, right) + right = j + } + } + insertionSortInlineOrder(order, keys, left, right) + } + + /** + * Median-of-three pivot selection for [[quickSortInlineOrder]]. Returns the median key among + * `keys(order(left))`, `keys(order(mid))`, and `keys(order(right))` (where `mid = (left + right) + * >>> 1`). Compared to a fixed mid-element pivot, this reduces worst-case behaviour on inputs + * that are already sorted, reverse-sorted, or contain runs — patterns that occur frequently in + * Jsonnet object key sets. + */ + private def medianOfThreeKey( + order: Array[Int], + keys: Array[String], + left: Int, + right: Int): String = { + val mid = (left + right) >>> 1 + val a = keys(order(left)) + val b = keys(order(mid)) + val c = keys(order(right)) + val ab = Util.compareStringsByCodepoint(a, b) + val ac = Util.compareStringsByCodepoint(a, c) + val bc = Util.compareStringsByCodepoint(b, c) + if (ab <= 0) { + if (bc <= 0) b // a <= b <= c + else if (ac <= 0) c // a <= c < b + else a // c < a <= b + } else { + if (ac <= 0) a // b < a <= c + else if (bc <= 0) c // b <= c < a + else b // c < b < a + } } /**