Optimize code for fewer transient memory allocations.

This commit is contained in:
Fedor Isakov 2019-05-17 17:10:20 +02:00
parent 1f6fbd0c0f
commit 0697437ec7
5 changed files with 57 additions and 55 deletions

View File

@ -20,9 +20,7 @@ import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
import jetbrains.mps.logic.reactor.logical.Logical
import jetbrains.mps.logic.reactor.logical.MetaLogical
import jetbrains.mps.logic.reactor.program.*
import jetbrains.mps.logic.reactor.util.TermTrie
import jetbrains.mps.logic.reactor.util.allSetBits
import jetbrains.mps.logic.reactor.util.termTrie
import jetbrains.mps.logic.reactor.util.*
import jetbrains.mps.unification.Term
import java.util.*
import kotlin.collections.ArrayList
@ -47,6 +45,12 @@ class RuleIndex(handlers: Iterable<RulesList>) : Iterable<Rule>, RuleLookup {
// invariant: rulesList.size == slotMasksList.size
private val slotMasksList = ArrayList<SlotMask>()
/** the bit set that is going to be reused for all calls to [select] */
private val ruleIndices = BitSet(rulesList.size)
/** the bit set to be used for temporary processing within [select]*/
private val andRuleIndices = BitSet(rulesList.size)
init {
buildIndex(handlers)
}
@ -58,7 +62,10 @@ class RuleIndex(handlers: Iterable<RulesList>) : Iterable<Rule>, RuleLookup {
*/
fun forOccurrence(occ: ConstraintOccurrence): Iterable<Rule> {
val ruleIndices = symbol2index[occ.constraint().symbol()]?.select(occ) ?: return emptyList()
return ruleIndices.allSetBits().map { idx -> rulesList[idx] }
val result = ArrayList<Rule>()
val it = ruleIndices.allSetBits()
while (it.hasNext()) result.add(rulesList[it.next()])
return result
}
/**
@ -66,9 +73,13 @@ class RuleIndex(handlers: Iterable<RulesList>) : Iterable<Rule>, RuleLookup {
*/
fun forOccurrenceWithMask(occ: ConstraintOccurrence): Iterable<Pair<Rule, BitSet>> {
val ruleBits = symbol2index[occ.constraint().symbol()]?.select(occ) ?: return emptyList()
return ruleBits.allSetBits().mapNotNull { ruleBit ->
slotMasksList[ruleBit][occ]?.let { mask -> rulesList[ruleBit] to mask }
val result = ArrayList<Pair<Rule, BitSet>>()
val it = ruleBits.allSetBits()
while (it.hasNext()) {
val ruleBit = it.next()
slotMasksList[ruleBit][occ]?.let { mask -> result.add(rulesList[ruleBit] to mask) }
}
return result
}
override fun iterator(): Iterator<Rule> = rulesList.iterator()
@ -107,12 +118,12 @@ class RuleIndex(handlers: Iterable<RulesList>) : Iterable<Rule>, RuleLookup {
fun update(cst: Constraint, posInHead: Int) {
symbol2mask.getOrPut(cst.symbol()) { BitSet() }.set(posInHead)
}
/**
* When null is returned, there can be no match for this constraint
*/
operator fun get(occ: ConstraintOccurrence): BitSet? =
symbol2mask[occ.constraint().symbol()]
}
/**
@ -162,7 +173,6 @@ class RuleIndex(handlers: Iterable<RulesList>) : Iterable<Rule>, RuleLookup {
// initially select all rules
val upToBit = rulesList.size
val ruleIndices = BitSet(rulesList.size)
ruleIndices.set(0, upToBit)
for ((idx, arg) in occ.arguments().withIndex()) {
@ -174,35 +184,20 @@ class RuleIndex(handlers: Iterable<RulesList>) : Iterable<Rule>, RuleLookup {
continue
}
andRuleIndices.clear(0, upToBit)
andRuleIndices.or(wildcardIndices)
val argVal = if (arg is Logical<*>) arg.findRoot().value() else arg
when (argVal) {
is Term -> {
val bits = wildcardIndices.clone() as BitSet
for (i in termIndices.lookupValues(argVal)) {
bits.set(i)
}
ruleIndices.and(bits)
}
is Any -> {
if (value2indices.containsKey(argVal)) {
// ensure only rules with either matching values or wildcard arguments get selected
val bits = wildcardIndices.clone() as BitSet
bits.or(value2indices[argVal])
ruleIndices.and(bits)
} else {
ruleIndices.and(wildcardIndices)
}
}
else -> {
/* never happens */
throw NullPointerException()
}
is Term ->
termIndices.lookupValues(argVal).forEach { andRuleIndices.set(it) }
is Any ->
// ensure only rules with either matching values or wildcard arguments get selected
if (value2indices.containsKey(argVal)) andRuleIndices.or(value2indices[argVal])
}
ruleIndices.and(andRuleIndices)
if (ruleIndices.isEmpty) {
break
}
if (ruleIndices.isEmpty) break
}
return ruleIndices

View File

@ -72,7 +72,9 @@ internal class ReteRuleMatcherImpl(val rule: Rule) : RuleMatcher {
if (thisMetaIndices == null || thatMetaIndices == null) return true
if (thisMetaIndices.cardinality() != 0 && thatMetaIndices.cardinality() != 0) {
for (shared in thisMetaIndices.copyApply { and(thatMetaIndices) }.allSetBits()) {
val it = thisMetaIndices.copyApply { and(thatMetaIndices) }.allSetBits()
while (it.hasNext()) {
val shared = it.next()
if (!createOccurrenceMatcher().match(this.getSubst(shared), that.getSubst(shared))) return false
}
}
@ -327,8 +329,10 @@ internal class ReteRuleMatcherImpl(val rule: Rule) : RuleMatcher {
skipOccIndices.clear(occIdx)
val alphaNodes = arrayListOf<AlphaNode>()
for (posInHead in mask.allSetBits()) {
val it = mask.allSetBits()
while (it.hasNext()){
val posInHead = it.next()
val matcher = createOccurrenceMatcher(emptySubst())
if (matcher.matches(head[posInHead], occ)) {
alphaNodes.add(AlphaNode(occ, posInHead, matcher.substitution()))

View File

@ -108,7 +108,9 @@ internal class RuleMatcherImpl(private val ruleLookup: RuleLookup,
fun expand(occ: Occurrence, genId: Int, matchingVacant: BitSet): List<ActiveMatchNode> =
unrelatedOrNull(occ)?.let { n ->
ArrayList<ActiveMatchNode>().also { expanded ->
for (headIdx in matchingVacant.allSetBits()) {
val it = matchingVacant.allSetBits()
while (it.hasNext()) {
val headIdx = it.next()
createOccurrenceMatcher(subst).run {
if (matches(head[headIdx], occ)) {
expanded.add(ActiveMatchNode(substitution(), n, occ, headIdx, genId))

View File

@ -45,9 +45,9 @@ internal class StateFrame private constructor(val state: ProcessingStateImpl) :
private var observers: PersMap<Id<Logical<*>>, ConsList<LogicalObserver>> = Maps.of()
private val activatedOccurrences = ArrayList<Occurrence>()
private val activatedOccurrences = ArrayList<Occurrence>(4)
private val deactivatedOccurrences = ArrayList<Occurrence>()
private val deactivatedOccurrences = ArrayList<Occurrence>(4)
constructor(state: ProcessingStateImpl, dispatcher: Dispatcher) : this(state) {
this.dispatchingFront = dispatcher.front()

View File

@ -16,7 +16,9 @@
package jetbrains.mps.logic.reactor.util
import gnu.trove.iterator.TIntIterator
import java.util.*
import kotlin.NoSuchElementException
/**
* @author Fedor Isakov
@ -28,7 +30,7 @@ inline fun BitSet.copyApply (f: BitSet.() -> Unit): BitSet =
fun bitSetOfOnes(size: Int): BitSet =
BitSet(size).apply { set(0, size) }
fun bitSet(setBit: Int): BitSet=
fun bitSet(setBit: Int): BitSet =
BitSet().apply { set(setBit) }
fun bitSet(setBits: Iterable<Int>): BitSet=
@ -40,22 +42,21 @@ fun BitSet.setBit(bit: Int): BitSet =
fun BitSet.clearBit(bit: Int): BitSet =
(clone() as BitSet).apply { clear(bit) }
fun BitSet.allSetBits(): Iterable<Int> = object : Iterable<Int> {
override fun iterator(): Iterator<Int> = object : Iterator<Int> {
var next = nextSetBit(0);
fun BitSet.allSetBits(): TIntIterator = object : TIntIterator {
override fun hasNext(): Boolean = next != -1;
var next = nextSetBit(0);
override fun next(): Int =
if (next != -1) {
val ret = next
next = nextSetBit(next + 1)
ret
} else {
throw NoSuchElementException()
}
}
override fun hasNext(): Boolean = next != -1;
override fun next(): Int =
if (next != -1) {
val ret = next
next = nextSetBit(next + 1)
ret
} else {
throw NoSuchElementException()
}
override fun remove() =
throw UnsupportedOperationException()
}