lyng/notes/perf_patch_slotB_A_current.diff
2026-01-25 11:35:08 +03:00

247 lines
12 KiB
Diff

diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjInt.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjInt.kt
index 0671102..d4cb6ba 100644
--- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjInt.kt
+++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjInt.kt
@@ -55,7 +55,11 @@ class ObjInt(val value: Long, override val isConst: Boolean = false) : Obj(), Nu
override suspend fun compareTo(scope: Scope, other: Obj): Int {
if (other !is Numeric) return -2
- return value.compareTo(other.doubleValue)
+ return if (other is ObjInt) {
+ value.compareTo(other.value)
+ } else {
+ doubleValue.compareTo(other.doubleValue)
+ }
}
override fun toString(): String = value.toString()
@@ -192,4 +196,4 @@ class ObjInt(val value: Long, override val isConst: Boolean = false) : Obj(), Nu
}
fun Int.toObj() = ObjInt.of(this.toLong())
-fun Long.toObj() = ObjInt.of(this)
\ No newline at end of file
+fun Long.toObj() = ObjInt.of(this)
diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRange.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRange.kt
index 0c631d0..dc61c66 100644
--- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRange.kt
+++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRange.kt
@@ -96,6 +96,30 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
if (other is ObjRange)
return containsRange(scope, other)
+ if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
+ if (start is ObjInt && end is ObjInt && other is ObjInt) {
+ val s = start.value
+ val e = end.value
+ val v = other.value
+ if (v < s) return false
+ return if (isEndInclusive) v <= e else v < e
+ }
+ if (start is ObjChar && end is ObjChar && other is ObjChar) {
+ val s = start.value
+ val e = end.value
+ val v = other.value
+ if (v < s) return false
+ return if (isEndInclusive) v <= e else v < e
+ }
+ if (start is ObjString && end is ObjString && other is ObjString) {
+ val s = start.value
+ val e = end.value
+ val v = other.value
+ if (v < s) return false
+ return if (isEndInclusive) v <= e else v < e
+ }
+ }
+
if (start == null && end == null) return true
if (start != null) {
if (start.compareTo(scope, other) > 0) return false
@@ -241,4 +265,3 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
}
}
}
-
diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt
index 2dd0ae6..76e56d4 100644
--- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt
+++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt
@@ -152,6 +152,90 @@ class BinaryOpRef(private val op: BinOp, private val left: ObjRef, private val r
// Primitive fast paths for common cases (guarded by PerfFlags.PRIMITIVE_FASTOPS)
if (PerfFlags.PRIMITIVE_FASTOPS) {
+ // Fast range equality: avoid compareTo/equals for ObjRange when possible
+ if ((op == BinOp.EQ || op == BinOp.NEQ) && a is ObjRange && b is ObjRange) {
+ val eq = (a.start == b.start && a.end == b.end)
+ if (PerfFlags.PIC_DEBUG_COUNTERS) PerfStats.primitiveFastOpsHit++
+ return if (op == BinOp.EQ) {
+ if (eq) ObjTrue else ObjFalse
+ } else {
+ if (eq) ObjFalse else ObjTrue
+ }
+ }
+ // Fast membership for common containers
+ if (op == BinOp.IN || op == BinOp.NOTIN) {
+ val inResult: Boolean? = when (b) {
+ is ObjList -> {
+ if (a is ObjInt) {
+ var i = 0
+ val sz = b.list.size
+ var found = false
+ while (i < sz) {
+ val v = b.list[i]
+ if (v is ObjInt && v.value == a.value) {
+ found = true
+ break
+ }
+ i++
+ }
+ found
+ } else {
+ b.list.contains(a)
+ }
+ }
+ is ObjSet -> b.set.contains(a)
+ is ObjMap -> b.map.containsKey(a)
+ is ObjRange -> {
+ when (a) {
+ is ObjInt -> {
+ val s = b.start as? ObjInt
+ val e = b.end as? ObjInt
+ val v = a.value
+ if (s == null && e == null) null
+ else {
+ if (s != null && v < s.value) false
+ else if (e != null) if (b.isEndInclusive) v <= e.value else v < e.value else true
+ }
+ }
+ is ObjChar -> {
+ val s = b.start as? ObjChar
+ val e = b.end as? ObjChar
+ val v = a.value
+ if (s == null && e == null) null
+ else {
+ if (s != null && v < s.value) false
+ else if (e != null) if (b.isEndInclusive) v <= e.value else v < e.value else true
+ }
+ }
+ is ObjString -> {
+ val s = b.start as? ObjString
+ val e = b.end as? ObjString
+ val v = a.value
+ if (s == null && e == null) null
+ else {
+ if (s != null && v < s.value) false
+ else if (e != null) if (b.isEndInclusive) v <= e.value else v < e.value else true
+ }
+ }
+ else -> null
+ }
+ }
+ is ObjString -> when (a) {
+ is ObjString -> b.value.contains(a.value)
+ is ObjChar -> b.value.contains(a.value)
+ else -> null
+ }
+ else -> null
+ }
+ if (inResult != null) {
+ if (PerfFlags.PIC_DEBUG_COUNTERS) PerfStats.primitiveFastOpsHit++
+ return if (op == BinOp.IN) {
+ if (inResult) ObjTrue else ObjFalse
+ } else {
+ if (inResult) ObjFalse else ObjTrue
+ }
+ }
+ }
// Fast boolean ops when both operands are ObjBool
if (a is ObjBool && b is ObjBool) {
val r: Obj? = when (op) {
@@ -604,7 +688,37 @@ class AssignOpRef(
else -> null
}
if (inPlace != null) return inPlace.asReadonly
- val result: Obj = when (op) {
+ val fast: Obj? = if (PerfFlags.PRIMITIVE_FASTOPS) {
+ when {
+ x is ObjInt && y is ObjInt -> {
+ val xv = x.value
+ val yv = y.value
+ when (op) {
+ BinOp.PLUS -> ObjInt.of(xv + yv)
+ BinOp.MINUS -> ObjInt.of(xv - yv)
+ BinOp.STAR -> ObjInt.of(xv * yv)
+ BinOp.SLASH -> if (yv != 0L) ObjInt.of(xv / yv) else null
+ BinOp.PERCENT -> if (yv != 0L) ObjInt.of(xv % yv) else null
+ else -> null
+ }
+ }
+ (x is ObjInt || x is ObjReal) && (y is ObjInt || y is ObjReal) -> {
+ val xv = if (x is ObjInt) x.doubleValue else (x as ObjReal).value
+ val yv = if (y is ObjInt) y.doubleValue else (y as ObjReal).value
+ when (op) {
+ BinOp.PLUS -> ObjReal.of(xv + yv)
+ BinOp.MINUS -> ObjReal.of(xv - yv)
+ BinOp.STAR -> ObjReal.of(xv * yv)
+ BinOp.SLASH -> ObjReal.of(xv / yv)
+ BinOp.PERCENT -> ObjReal.of(xv % yv)
+ else -> null
+ }
+ }
+ x is ObjString && op == BinOp.PLUS -> ObjString(x.value + y.toString())
+ else -> null
+ }
+ } else null
+ val result: Obj = fast ?: when (op) {
BinOp.PLUS -> x.plus(scope, y)
BinOp.MINUS -> x.minus(scope, y)
BinOp.STAR -> x.mul(scope, y)
@@ -632,7 +746,15 @@ class IncDecRef(
// We now treat numbers as immutable and always perform write-back via setAt.
// This avoids issues where literals are shared and mutated in-place.
// For post-inc: return ORIGINAL value; for pre-inc: return NEW value.
- val result = if (isIncrement) v.plus(scope, one) else v.minus(scope, one)
+ val result = if (PerfFlags.PRIMITIVE_FASTOPS) {
+ when (v) {
+ is ObjInt -> if (isIncrement) ObjInt.of(v.value + 1L) else ObjInt.of(v.value - 1L)
+ is ObjReal -> if (isIncrement) ObjReal.of(v.value + 1.0) else ObjReal.of(v.value - 1.0)
+ else -> if (isIncrement) v.plus(scope, one) else v.minus(scope, one)
+ }
+ } else {
+ if (isIncrement) v.plus(scope, one) else v.minus(scope, one)
+ }
target.setAt(atPos, scope, result)
return (if (isPost) v else result).asReadonly
}
@@ -1246,8 +1368,8 @@ class IndexRef(
val i = idx.toInt()
return ObjChar(base.value[i]).asMutable
}
- // Map[String] fast path (common case); return ObjNull if absent
- if (base is ObjMap && idx is ObjString) {
+ // Map[String/Int/Char] fast path (common cases); return ObjNull if absent
+ if (base is ObjMap && (idx is ObjString || idx is ObjInt || idx is ObjChar)) {
val v = base.map[idx] ?: ObjNull
return v.asMutable
}
@@ -1321,8 +1443,8 @@ class IndexRef(
val i = idx.toInt()
return ObjChar(base.value[i])
}
- // Map[String] fast path
- if (base is ObjMap && idx is ObjString) {
+ // Map[String/Int/Char] fast path
+ if (base is ObjMap && (idx is ObjString || idx is ObjInt || idx is ObjChar)) {
return base.map[idx] ?: ObjNull
}
if (PerfFlags.INDEX_PIC) {
@@ -1393,7 +1515,7 @@ class IndexRef(
return
}
// Direct write fast path for ObjMap + ObjString
- if (base is ObjMap && idx is ObjString) {
+ if (base is ObjMap && (idx is ObjString || idx is ObjInt || idx is ObjChar)) {
base.map[idx] = newValue
return
}