Introduced Profiler to track bottlenecks. Some obvious optimizations. Refactoring for cleaner code.
This commit is contained in:
parent
1dd3294483
commit
b8aa435eb4
|
|
@ -14,36 +14,43 @@ import java.util.*
|
|||
* @author Fedor Isakov
|
||||
*/
|
||||
|
||||
class Handler {
|
||||
class Handler : Matcher.AuxOccurrences {
|
||||
|
||||
val sessionSolver: SessionSolver
|
||||
|
||||
val trace: EvaluationTrace
|
||||
|
||||
val profiler: Profiler?
|
||||
|
||||
private val rules = ArrayList<Rule>()
|
||||
|
||||
private val stored = ArrayList<ConstraintOccurrence>()
|
||||
private val occurrenceStore = OccurrenceStore()
|
||||
|
||||
private val activeQueue = LinkedList<ConstraintOccurrence>()
|
||||
|
||||
private val activationStack = LinkedList<PartialMatch>()
|
||||
private val activationStack = LinkedList<Matcher.PartialMatch>()
|
||||
|
||||
private val matcher: Matcher
|
||||
|
||||
constructor(
|
||||
sessionSolver: SessionSolver,
|
||||
programRules: Iterable<Rule>,
|
||||
trace: EvaluationTrace = EvaluationTrace.NULL,
|
||||
profiler: Profiler? = null,
|
||||
// for testing purposes only
|
||||
occurrences: Iterable<ConstraintOccurrence>? = null)
|
||||
{
|
||||
this.sessionSolver = sessionSolver
|
||||
this.trace = trace
|
||||
this.profiler = profiler
|
||||
this.rules.addAll(programRules)
|
||||
this.matcher = Matcher(rules, this, profiler)
|
||||
if (occurrences != null) {
|
||||
this.stored.addAll(occurrences)
|
||||
this.occurrenceStore.addAll(occurrences)
|
||||
}
|
||||
}
|
||||
|
||||
fun occurrences(): Set<ConstraintOccurrence> = stored.toSet()
|
||||
fun occurrences(): Set<ConstraintOccurrence> = occurrenceStore.allOccurrences().toSet()
|
||||
|
||||
fun tell(constraint: Constraint) {
|
||||
try {
|
||||
|
|
@ -64,75 +71,96 @@ class Handler {
|
|||
}
|
||||
}
|
||||
|
||||
private fun process(active: ConstraintOccurrence) {
|
||||
if (!active.isStored()) {
|
||||
store(active)
|
||||
trace.activate(active)
|
||||
}
|
||||
else {
|
||||
trace.reactivate(active)
|
||||
}
|
||||
override fun findOccurrences(
|
||||
constraint: Constraint,
|
||||
acceptable: (ConstraintOccurrence) -> Boolean): Iterable<ConstraintOccurrence>
|
||||
{
|
||||
return profiler.profile<Iterable<ConstraintOccurrence>>("findOccurrences", {
|
||||
return occurrenceStore.allFor(constraint).filter {
|
||||
|
||||
val matcher = object : Matcher(rules) {
|
||||
override fun findOccurrences(constraint: Constraint, acceptable: (ConstraintOccurrence) -> Boolean):
|
||||
Iterable<ConstraintOccurrence> =
|
||||
stored.filter { co -> constraint.matches(co) && acceptable(co) }
|
||||
}
|
||||
co -> constraint.matches(co, profiler) && acceptable(co)
|
||||
|
||||
for (match in matcher.lookupMatches(active).filter { pm -> pm.rule.checkGuard(pm.logicalContext()) }) {
|
||||
if (!active.isStored()) break
|
||||
if (match.occurrences().any{ co -> !co.isStored() }) continue
|
||||
|
||||
activationStack.push(match)
|
||||
trace.trigger(match)
|
||||
|
||||
for ((cst, occ) in match.discarded) {
|
||||
discard(occ)
|
||||
trace.discard(occ)
|
||||
}
|
||||
|
||||
for (item in match.rule.body()) {
|
||||
activate(item, match.logicalContext())
|
||||
}
|
||||
|
||||
trace.exit(match.rule)
|
||||
activationStack.pop()
|
||||
}
|
||||
|
||||
if (active.isStored()) {
|
||||
trace.suspend(active)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun store(occ: ConstraintOccurrence) {
|
||||
stored.add(occ)
|
||||
occurrenceStore.add(occ)
|
||||
}
|
||||
|
||||
private fun discard(occ: ConstraintOccurrence) {
|
||||
stored.remove(occ)
|
||||
occurrenceStore.remove(occ)
|
||||
occ.terminate()
|
||||
}
|
||||
|
||||
private fun activate(item: AndItem, logicalContext: LogicalContext) {
|
||||
when(item) {
|
||||
is Constraint -> process(item.occurrence(this@Handler, logicalContext))
|
||||
is Predicate -> tellPredicate(item.invocation(logicalContext))
|
||||
else -> throw IllegalArgumentException("unknown item ${item}")
|
||||
}
|
||||
profiler.profile("activate", {
|
||||
|
||||
when (item) {
|
||||
is Constraint -> process(item.occurrence(this@Handler, logicalContext))
|
||||
is Predicate -> tellPredicate(item.invocation(logicalContext))
|
||||
else -> throw IllegalArgumentException("unknown item ${item}")
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private fun process(active: ConstraintOccurrence) {
|
||||
profiler.profile("process_${active.constraint().symbol()}", {
|
||||
|
||||
if (!active.isStored()) {
|
||||
store(active)
|
||||
trace.activate(active)
|
||||
} else {
|
||||
trace.reactivate(active)
|
||||
}
|
||||
|
||||
for (match in matcher.lookupMatches(active).filter { pm -> pm.rule.checkGuard(pm.logicalContext()) }) {
|
||||
if (!active.isStored()) break
|
||||
if (match.occurrences().any { co -> !co.isStored() }) continue
|
||||
|
||||
activationStack.push(match)
|
||||
trace.trigger(match)
|
||||
|
||||
for ((cst, occ) in match.discarded) {
|
||||
discard(occ)
|
||||
trace.discard(occ)
|
||||
}
|
||||
|
||||
for (item in match.rule.body()) {
|
||||
activate(item, match.logicalContext())
|
||||
}
|
||||
|
||||
trace.exit(match.rule)
|
||||
activationStack.pop()
|
||||
}
|
||||
|
||||
if (active.isStored()) {
|
||||
trace.suspend(active)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private fun Rule.checkGuard(logicalContext: LogicalContext): Boolean =
|
||||
guard().all { prd -> askPredicate(prd.invocation(logicalContext)) }
|
||||
profiler.profile<Boolean>("checkGuard", {
|
||||
return guard().all { prd -> askPredicate(prd.invocation(logicalContext)) }
|
||||
})
|
||||
|
||||
private fun askPredicate(invocation: PredicateInvocation): Boolean =
|
||||
sessionSolver.ask(invocation.predicate().symbol(), * invocation.arguments().toTypedArray())
|
||||
profiler.profile<Boolean>("ask_${invocation.predicate().symbol()}", {
|
||||
return sessionSolver.ask(invocation.predicate().symbol(), * invocation.arguments().toTypedArray())
|
||||
})
|
||||
|
||||
private fun tellPredicate(invocation: PredicateInvocation) {
|
||||
sessionSolver.tell(invocation.predicate().symbol(), * invocation.arguments().toTypedArray())
|
||||
profiler.profile("tell_${invocation.predicate().symbol()}", {
|
||||
sessionSolver.tell(invocation.predicate().symbol(), * invocation.arguments().toTypedArray())
|
||||
})
|
||||
}
|
||||
|
||||
private fun ConstraintOccurrence.isStored(): Boolean =
|
||||
stored.contains(this)
|
||||
occurrenceStore.isStored(this)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,125 +17,185 @@ import java.util.*
|
|||
* @author Fedor Isakov
|
||||
*/
|
||||
|
||||
abstract class Matcher(val rules: Collection<Rule>) {
|
||||
class Matcher {
|
||||
|
||||
interface AuxOccurrences {
|
||||
|
||||
fun findOccurrences(constraint: Constraint, acceptable: (ConstraintOccurrence) -> Boolean):
|
||||
Iterable<ConstraintOccurrence>
|
||||
|
||||
}
|
||||
|
||||
val rules: RuleIndex
|
||||
val aux: AuxOccurrences
|
||||
val profiler: Profiler?
|
||||
|
||||
constructor(rules: Collection<Rule>, aux: AuxOccurrences, profiler: Profiler? = null) {
|
||||
this.rules = RuleIndex(rules)
|
||||
this.aux = aux
|
||||
this.profiler = profiler
|
||||
}
|
||||
|
||||
fun lookupMatches(occ: ConstraintOccurrence): Iterable<PartialMatch> {
|
||||
val partialMatches = rules.flatMap { r ->
|
||||
val matchedKept = r.headKept().filter { cst -> cst.matches(occ) }
|
||||
val matchedDiscarded = r.headReplaced().filter { cst -> cst.matches(occ) }
|
||||
return profiler.profile<Iterable<PartialMatch>>("lookupMatches", {
|
||||
|
||||
matchedKept.map { cst -> PartialMatch(r).keep(cst, occ) } +
|
||||
matchedDiscarded.map { cst -> PartialMatch(r).discard(cst, occ) }
|
||||
}
|
||||
return partialMatches.flatMap { pm -> completeMatch(pm) }.filter { pm -> pm.matches() }
|
||||
val partialMatches = rules.forSymbol(occ.constraint().symbol())?.flatMap { r ->
|
||||
val matchedKept = r.headKept().filter { cst -> cst.matches(occ) }
|
||||
val matchedDiscarded = r.headReplaced().filter { cst -> cst.matches(occ) }
|
||||
|
||||
matchedKept.map { cst -> PartialMatch(r).keep(cst, occ) } +
|
||||
matchedDiscarded.map { cst -> PartialMatch(r).discard(cst, occ) }
|
||||
}
|
||||
|
||||
partialMatches?.flatMap { pm -> completeMatch(pm) }?.filter { pm -> pm.matches() } ?: emptyList()
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private fun completeMatch(match: PartialMatch) : Iterable<PartialMatch> {
|
||||
if (!match.isPartial()) return listOf(match)
|
||||
|
||||
val keptToSatisfy = match.rule.headKept().filter { cst -> !match.kept.any { p -> cst === p.first } }
|
||||
val matchesFromKept = keptToSatisfy.flatMap { cst ->
|
||||
findOccurrences(cst, { occ -> !match.hasOccurrence(occ) }).flatMap { occ -> completeMatch(match.keep(cst, occ)) }
|
||||
return profiler.profile<Iterable<PartialMatch>>("completeMatch", {
|
||||
|
||||
val keptToSatisfy = match.rule.headKept().filter { cst -> !match.kept.any { p -> cst === p.first } }
|
||||
val matchesFromKept = keptToSatisfy.flatMap { cst ->
|
||||
aux.findOccurrences(cst, { occ ->
|
||||
profiler.profile<Boolean>("acceptable", {
|
||||
|
||||
!match.hasOccurrence(occ)
|
||||
|
||||
})
|
||||
}).flatMap { occ -> completeMatch(match.keep(cst, occ)) }
|
||||
}
|
||||
|
||||
val discardedToSatisfy = match.rule.headReplaced().filter { cst -> !match.discarded.any { p -> cst === p.first } }
|
||||
val matchesFromDiscarded = discardedToSatisfy.flatMap { cst ->
|
||||
aux.findOccurrences(cst, { occ ->
|
||||
profiler.profile<Boolean>("acceptable", {
|
||||
|
||||
!match.hasOccurrence(occ)
|
||||
|
||||
})
|
||||
}).flatMap { occ -> completeMatch(match.discard(cst, occ)) }
|
||||
}
|
||||
|
||||
matchesFromKept + matchesFromDiscarded
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
inner class PartialMatch(val rule: Rule) : MatchRule {
|
||||
|
||||
var kept = ConsList.empty<Pair<Constraint, ConstraintOccurrence>>()
|
||||
private set
|
||||
var discarded = ConsList.empty<Pair<Constraint, ConstraintOccurrence>>()
|
||||
private set
|
||||
private lateinit var logicalContext : LogicalContext
|
||||
|
||||
private constructor(
|
||||
original : PartialMatch,
|
||||
keep: Pair<Constraint, ConstraintOccurrence>?,
|
||||
discard: Pair<Constraint, ConstraintOccurrence>?) : this(original.rule)
|
||||
{
|
||||
kept = if (keep != null) original.kept.append(keep) else original.kept
|
||||
discarded = if (discard != null) original.discarded.append(discard) else original.discarded
|
||||
}
|
||||
|
||||
val discardedToSatisfy = match.rule.headReplaced().filter { cst -> !match.discarded.any { p -> cst === p.first } }
|
||||
val matchesFromDiscarded = discardedToSatisfy.flatMap { cst ->
|
||||
findOccurrences(cst, { occ -> !match.hasOccurrence(occ) }).flatMap { occ -> completeMatch(match.discard(cst, occ)) }
|
||||
fun keep (constraint: Constraint, occ: ConstraintOccurrence) = PartialMatch(this, Pair(constraint, occ), null)
|
||||
|
||||
fun discard (constraint: Constraint, occ: ConstraintOccurrence) = PartialMatch(this, null, Pair(constraint, occ))
|
||||
|
||||
fun hasOccurrence(occ: ConstraintOccurrence): Boolean {
|
||||
// return profiler.profile<Boolean>("hasOccurrence", {
|
||||
|
||||
return kept.any { p -> p.second === occ } || discarded.any { p -> p.second === occ }
|
||||
|
||||
// })
|
||||
}
|
||||
|
||||
return matchesFromKept + matchesFromDiscarded
|
||||
}
|
||||
fun isPartial() : Boolean {
|
||||
// return profiler.profile<Boolean>("isPartial", {
|
||||
|
||||
abstract fun findOccurrences(constraint: Constraint, acceptable: (ConstraintOccurrence) -> Boolean):
|
||||
Iterable<ConstraintOccurrence>
|
||||
return rule.headKept().any { cst -> !kept.any { p -> p.first === cst } } ||
|
||||
rule.headReplaced().any { cst -> !discarded.any { p -> p.first === cst } }
|
||||
|
||||
}
|
||||
// })
|
||||
}
|
||||
|
||||
class PartialMatch(val rule: Rule) : MatchRule {
|
||||
fun occurrences(): Collection<ConstraintOccurrence> {
|
||||
// return profiler.profile<Collection<ConstraintOccurrence>>("occurrences", {
|
||||
|
||||
var kept = ConsList.empty<Pair<Constraint, ConstraintOccurrence>>()
|
||||
private set
|
||||
var discarded = ConsList.empty<Pair<Constraint, ConstraintOccurrence>>()
|
||||
private set
|
||||
private lateinit var logicalContext : LogicalContext
|
||||
return rule.headKept().map { cst -> (kept.find { p -> p.first === cst }?.second) ?: TODO() } +
|
||||
rule.headReplaced().map { cst -> discarded.find { p -> p.first === cst }?.second ?: TODO() }
|
||||
|
||||
private constructor(
|
||||
original : PartialMatch,
|
||||
keep: Pair<Constraint, ConstraintOccurrence>?,
|
||||
discard: Pair<Constraint, ConstraintOccurrence>?) : this(original.rule)
|
||||
{
|
||||
kept = if (keep != null) original.kept.append(keep) else original.kept
|
||||
discarded = if (discard != null) original.discarded.append(discard) else original.discarded
|
||||
}
|
||||
// })
|
||||
}
|
||||
|
||||
fun keep (constraint: Constraint, occ: ConstraintOccurrence) = PartialMatch(this, Pair(constraint, occ), null)
|
||||
fun matches(): Boolean {
|
||||
return profiler.profile<Boolean>("matches", {
|
||||
|
||||
fun discard (constraint: Constraint, occ: ConstraintOccurrence) = PartialMatch(this, null, Pair(constraint, occ))
|
||||
val subst = Unification.unify(PartialMatchTerm(this), RuleTerm(this.rule))
|
||||
if (!subst.isSuccessful) return false
|
||||
|
||||
fun hasOccurrence(occ: ConstraintOccurrence): Boolean {
|
||||
return kept.any { p -> p.second === occ} || discarded.any { p -> p.second === occ }
|
||||
}
|
||||
// variables come from LogicalPattern instances in rules
|
||||
// any successful binding results in either new Logical with associated value,
|
||||
// or a new value for a Logical already existing in this context
|
||||
|
||||
fun isPartial() : Boolean =
|
||||
rule.headKept().any { cst -> !kept.any { p -> p.first === cst } } ||
|
||||
rule.headReplaced().any { cst -> !discarded.any { p -> p.first === cst } }
|
||||
// only one parameter of the unification can contain variables,
|
||||
// thus triangular form never has variables on the right hand side
|
||||
this.logicalContext = object: LogicalContext {
|
||||
|
||||
fun occurrences(): Collection<ConstraintOccurrence> =
|
||||
rule.headKept().map { cst -> (kept.find { p -> p.first === cst }?.second) ?: TODO() } +
|
||||
rule.headReplaced().map { cst -> discarded.find { p -> p.first === cst }?.second ?: TODO() }
|
||||
// invariant: the variables in substitution bindings can only be instances of LogicalPattern
|
||||
val pattern2value: MutableMap<LogicalPattern<*>, Any?> = HashMap(subst.bindings().map { b ->
|
||||
(b.`var`().symbol() as LogicalPattern<Any>).to(b.term().toValue()) }.toMap())
|
||||
|
||||
fun matches(): Boolean {
|
||||
val subst = Unification.unify(PartialMatchTerm(this), RuleTerm(this.rule))
|
||||
if (!subst.isSuccessful) return false
|
||||
val pattern2logical: MutableMap<LogicalPattern<*>, Logical<*>> = HashMap()
|
||||
|
||||
// variables come from LogicalPattern instances in rules
|
||||
// any successful binding results in either new Logical with associated value,
|
||||
// or a new value for a Logical already existing in this context
|
||||
|
||||
// only one parameter of the unification can contain variables,
|
||||
// thus triangular form never has variables on the right hand side
|
||||
this.logicalContext = object: LogicalContext {
|
||||
|
||||
// invariant: the variables in substitution bindings can only be instances of LogicalPattern
|
||||
val pattern2value: MutableMap<LogicalPattern<*>, Any?> = HashMap(subst.bindings().map { b ->
|
||||
(b.`var`().symbol() as LogicalPattern<Any>).to(b.term().toValue()) }.toMap())
|
||||
|
||||
val pattern2logical: MutableMap<LogicalPattern<*>, Logical<*>> = HashMap()
|
||||
|
||||
override fun <V : Any> variable(logicalPattern: LogicalPattern<V>): Logical<V> {
|
||||
if (!pattern2logical.containsKey(logicalPattern)) {
|
||||
if (pattern2value.containsKey(logicalPattern)) {
|
||||
val value = pattern2value[logicalPattern]
|
||||
pattern2logical[logicalPattern] = if (value is Logical<*>) value else MemLogical(value)
|
||||
}
|
||||
else {
|
||||
pattern2logical[logicalPattern] = logicalPattern.logical()
|
||||
override fun <V : Any> variable(logicalPattern: LogicalPattern<V>): Logical<V> {
|
||||
if (!pattern2logical.containsKey(logicalPattern)) {
|
||||
if (pattern2value.containsKey(logicalPattern)) {
|
||||
val value = pattern2value[logicalPattern]
|
||||
pattern2logical[logicalPattern] = if (value is Logical<*>) value else MemLogical(value)
|
||||
}
|
||||
else {
|
||||
pattern2logical[logicalPattern] = logicalPattern.logical()
|
||||
}
|
||||
}
|
||||
return pattern2logical[logicalPattern] as Logical<V>
|
||||
}
|
||||
}
|
||||
return pattern2logical[logicalPattern] as Logical<V>
|
||||
}
|
||||
return true
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
return true
|
||||
fun logicalContext(): LogicalContext = logicalContext
|
||||
|
||||
override fun rule(): Rule = rule
|
||||
|
||||
override fun matchHeadKept(): Iterable<ConstraintOccurrence> = kept.map { p -> p.second }
|
||||
|
||||
override fun matchHeadReplaced(): Iterable<ConstraintOccurrence> = discarded.map { p -> p.second }
|
||||
|
||||
}
|
||||
|
||||
fun logicalContext(): LogicalContext = logicalContext
|
||||
|
||||
override fun rule(): Rule = rule
|
||||
|
||||
override fun matchHeadKept(): Iterable<ConstraintOccurrence> = kept.map { p -> p.second }
|
||||
|
||||
override fun matchHeadReplaced(): Iterable<ConstraintOccurrence> = discarded.map { p -> p.second }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* True iff the constraint matches the occurrence.
|
||||
*/
|
||||
fun Constraint.matches(that: ConstraintOccurrence): Boolean =
|
||||
Unification.unify(ConstraintTerm(this), ConstraintOccurrenceTerm(that)).isSuccessful
|
||||
fun Constraint.matches(that: ConstraintOccurrence, profiler: Profiler? = null): Boolean {
|
||||
val constraintTerm = ConstraintTerm(this)
|
||||
val constraintOccurrenceTerm = ConstraintOccurrenceTerm(that)
|
||||
return profiler.profile<Boolean>("unifyConstraintOccurrence", {
|
||||
|
||||
return Unification.unify(constraintTerm, constraintOccurrenceTerm).isSuccessful
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
/** Function term with arguments == constraints converted to terms. May contain variables. */
|
||||
class RuleTerm(rule: Rule) :
|
||||
|
|
@ -149,7 +209,7 @@ class ConstraintTerm(constraint: Constraint) :
|
|||
constraint.arguments().map { arg -> if (arg is LogicalPattern<*>) Variable(arg) else asTerm(arg) }) {}
|
||||
|
||||
/** Function term with arguments == terms corresponding to constraint occurrences. Never contains variables. */
|
||||
class PartialMatchTerm(pm : PartialMatch) :
|
||||
class PartialMatchTerm(pm : Matcher.PartialMatch) :
|
||||
Function(pm.rule.tag(), pm.occurrences().map { co -> ConstraintOccurrenceTerm(co) }) {}
|
||||
|
||||
/** Function term with arguments == constraint occurrence arguments converted to terms.
|
||||
|
|
|
|||
|
|
@ -52,11 +52,14 @@ class MemEvaluationSession : EvaluationSession {
|
|||
session = MemEvaluationSession(program, myEvaluationTrace)
|
||||
ourBackend.ourSession.set(session)
|
||||
|
||||
val durations = myParameters.get("profiling.data") as MutableMap<String, String>?
|
||||
val profiler = durations?.let{ Profiler() }
|
||||
try {
|
||||
session.launch(myParameters["main"] as Constraint)
|
||||
session.launch(myParameters["main"] as Constraint, profiler)
|
||||
}
|
||||
finally {
|
||||
ourBackend.ourSession.set(null)
|
||||
profiler?.formattedData()?.entries?.forEach { e -> durations?.put(e.key, e.value) }
|
||||
}
|
||||
|
||||
return session
|
||||
|
|
@ -74,11 +77,9 @@ class MemEvaluationSession : EvaluationSession {
|
|||
this.trace = trace
|
||||
}
|
||||
|
||||
fun launch(main: Constraint) {
|
||||
this.handler = Handler(sessionSolver(), program.rules(), trace)
|
||||
fun launch(main: Constraint, profiler: Profiler?) {
|
||||
this.handler = Handler(sessionSolver(), program.rules(), trace, profiler)
|
||||
handler.tell(main)
|
||||
// FIXME: shutdown the session properly
|
||||
ourBackend.ourSession.set(null)
|
||||
}
|
||||
|
||||
override fun sessionSolver(): SessionSolver = program.sessionSolver()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
package jetbrains.mps.logic.reactor.core
|
||||
|
||||
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
|
||||
import jetbrains.mps.logic.reactor.program.Constraint
|
||||
import jetbrains.mps.logic.reactor.program.ConstraintSymbol
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* @author Fedor Isakov
|
||||
*/
|
||||
|
||||
class OccurrenceStore {
|
||||
|
||||
val symbol2list = HashMap<ConstraintSymbol, MutableList<ConstraintOccurrence>>()
|
||||
|
||||
fun addAll(all: Iterable<ConstraintOccurrence>): Unit {
|
||||
for(occ in all) {
|
||||
add(occ)
|
||||
}
|
||||
}
|
||||
|
||||
fun add(occ: ConstraintOccurrence): Unit {
|
||||
val symbol = occ.constraint().symbol()
|
||||
if (!symbol2list.containsKey(symbol)) {
|
||||
symbol2list[symbol] = ArrayList<ConstraintOccurrence>()
|
||||
}
|
||||
symbol2list[symbol]!!.add(occ)
|
||||
}
|
||||
|
||||
fun remove(occ: ConstraintOccurrence): Unit {
|
||||
val symbol = occ.constraint().symbol()
|
||||
if (symbol2list.containsKey(symbol)) {
|
||||
symbol2list[symbol]!!.remove(occ)
|
||||
}
|
||||
}
|
||||
|
||||
fun isStored(occ: ConstraintOccurrence): Boolean {
|
||||
val symbol = occ.constraint().symbol()
|
||||
if (symbol2list.containsKey(symbol)) {
|
||||
return symbol2list[symbol]!!.contains(occ)
|
||||
}
|
||||
else
|
||||
return false
|
||||
}
|
||||
|
||||
fun allFor(cst: Constraint): Iterable<ConstraintOccurrence> {
|
||||
val symbol = cst.symbol()
|
||||
if (symbol2list.containsKey(symbol)) {
|
||||
return symbol2list[symbol]!!
|
||||
}
|
||||
else
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
fun allOccurrences(): Iterable<ConstraintOccurrence> =
|
||||
symbol2list.values.flatMap { it }
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
package jetbrains.mps.logic.reactor.core
|
||||
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* @author Fedor Isakov
|
||||
*/
|
||||
|
||||
class Profiler {
|
||||
|
||||
companion object {
|
||||
private var lastTokenId = 0
|
||||
}
|
||||
|
||||
private val tokenStack = LinkedList<Token>()
|
||||
|
||||
constructor() {
|
||||
tokenStack.push(Token("_", ++lastTokenId))
|
||||
}
|
||||
|
||||
fun start(name: String): Token {
|
||||
val tok = Token(name, ++lastTokenId)
|
||||
tokenStack.peek().addChild(tok)
|
||||
tokenStack.push(tok)
|
||||
return tok
|
||||
}
|
||||
|
||||
fun end(tok: Token? = null) {
|
||||
val pop = tokenStack.pop()
|
||||
if (tok != null && pop !== tok) throw IllegalStateException("wrong token")
|
||||
pop.end()
|
||||
}
|
||||
|
||||
// name -> (duration, freq)
|
||||
fun rawProfilingData(): Map<String, Pair<Long,Int>> {
|
||||
val name2duration = HashMap<String, Pair<Long,Int>>()
|
||||
tokenStack.peek().mergeDurations(name2duration)
|
||||
return name2duration
|
||||
}
|
||||
|
||||
fun formattedData(): Map<String, String> {
|
||||
return rawProfilingData().entries.sortedBy { e -> -(e.value.first) }.map { e ->
|
||||
val (dur, freq) = e.value
|
||||
val millis= dur / 1000000
|
||||
e.key.to("%1\$Ts.%1\$TLs (%2\$d times)".format(millis, freq))
|
||||
}.toMap()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Token(val name: String, val id: Int) {
|
||||
|
||||
val startNano: Long = System.nanoTime()
|
||||
|
||||
var endNano: Long? = null
|
||||
private set
|
||||
|
||||
private val children = ArrayList<Token>()
|
||||
|
||||
fun addChild(ch: Token) {
|
||||
children.add(ch)
|
||||
}
|
||||
|
||||
fun end() {
|
||||
if (endNano != null) throw IllegalStateException("already ended")
|
||||
this.endNano = System.nanoTime()
|
||||
}
|
||||
|
||||
private fun duration(): Long? = endNano?.minus(startNano)
|
||||
|
||||
private fun ownDuration(): Long? = duration()?.minus(children.map { ch -> ch.duration() ?: 0 }.sum())
|
||||
|
||||
fun mergeDurations(name2duration: MutableMap<String, Pair<Long,Int>>) {
|
||||
children.groupBy { ch -> ch.name }.entries.forEach { e ->
|
||||
val (dur, freq) = name2duration[e.key] ?: Pair(0L, 0)
|
||||
name2duration[e.key] = (dur + e.value.map { ch -> ch.ownDuration() ?: 0 }.sum()).to(freq + e.value.size)
|
||||
}
|
||||
for (c in children) {
|
||||
c.mergeDurations(name2duration)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline fun Profiler?.profile(name: String, proc: () -> Unit): Unit {
|
||||
val tok = this?.start(name)
|
||||
try {
|
||||
proc.invoke()
|
||||
}
|
||||
finally {
|
||||
this?.end(tok)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <R> Profiler?.profile(name: String, function: () -> R): R {
|
||||
val tok = this?.start(name)
|
||||
try {
|
||||
return function.invoke()
|
||||
}
|
||||
finally {
|
||||
this?.end(tok)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package jetbrains.mps.logic.reactor.core
|
||||
|
||||
import jetbrains.mps.logic.reactor.program.Constraint
|
||||
import jetbrains.mps.logic.reactor.program.ConstraintSymbol
|
||||
import jetbrains.mps.logic.reactor.program.Rule
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* @author Fedor Isakov
|
||||
*/
|
||||
|
||||
class RuleIndex : Iterable<Rule> {
|
||||
|
||||
private val symbol2rules = HashMap<ConstraintSymbol, MutableList<Rule>>()
|
||||
|
||||
private var rules: Collection<Rule>
|
||||
|
||||
constructor(rules: Collection<Rule>) {
|
||||
this.rules = rules
|
||||
buildIndex(rules)
|
||||
}
|
||||
|
||||
fun forSymbol(symbol: ConstraintSymbol): Iterable<Rule>? = symbol2rules[symbol]
|
||||
|
||||
override fun iterator(): Iterator<Rule> = rules.iterator()
|
||||
|
||||
private fun buildIndex(rules: Collection<Rule>) {
|
||||
for (r in rules) {
|
||||
for (c in r.headKept()) {
|
||||
updateIndex(c, r)
|
||||
}
|
||||
for (c in r.headReplaced()) {
|
||||
updateIndex(c, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateIndex(cst: Constraint, rule: Rule) {
|
||||
if (!symbol2rules.containsKey(cst.symbol())) {
|
||||
symbol2rules[cst.symbol()] = ArrayList()
|
||||
}
|
||||
val rs = symbol2rules[cst.symbol()]!!
|
||||
if (!rs.contains(rule)) rs.add(rule)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import jetbrains.mps.logic.reactor.core.matches
|
|||
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
|
||||
import jetbrains.mps.logic.reactor.logical.Logical
|
||||
import jetbrains.mps.logic.reactor.program.Constraint
|
||||
import jetbrains.mps.logic.reactor.program.ConstraintSymbol
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
|
||||
|
|
@ -15,11 +16,12 @@ class TestMatcher {
|
|||
|
||||
private fun Builder.matcher(vararg occurrence: ConstraintOccurrence): Matcher {
|
||||
val stored = occurrence.toList()
|
||||
return object : Matcher(rules) {
|
||||
val aux = object : Matcher.AuxOccurrences {
|
||||
override fun findOccurrences(constraint: Constraint, acceptable: (ConstraintOccurrence) -> Boolean):
|
||||
Iterable<ConstraintOccurrence> =
|
||||
stored.filter { co -> constraint.matches(co) && acceptable(co) }
|
||||
}
|
||||
return Matcher(rules, aux)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -64,7 +66,7 @@ class TestMatcher {
|
|||
).matcher().run{
|
||||
lookupMatches(occurrence("main")).let { matches ->
|
||||
assertFalse(matches.any { m -> m.isPartial() })
|
||||
assertEquals(rules, matches.map { m -> m.rule })
|
||||
assertEquals(rules.toSet(), matches.map { m -> m.rule }.toSet())
|
||||
matches.forEach { m -> assertTrue(m.kept.size() + m.discarded.size() == 1) }
|
||||
matches.flatMap { m -> m.kept + m.discarded }.forEach { pair ->
|
||||
val (cst, occ) = pair
|
||||
|
|
@ -97,7 +99,7 @@ class TestMatcher {
|
|||
).matcher().run{
|
||||
lookupMatches(occurrence("main")).let { matches ->
|
||||
assertFalse(matches.any { m -> m.isPartial() })
|
||||
assertEquals(rules.drop(1), matches.map { m -> m.rule })
|
||||
assertEquals(rules.drop(1).toSet(), matches.map { m -> m.rule }.toSet())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -125,7 +127,7 @@ class TestMatcher {
|
|||
).matcher(occurrence("aux")).run {
|
||||
lookupMatches(occurrence("main")).let { matches ->
|
||||
assertFalse(matches.any { m -> m.isPartial() })
|
||||
assertEquals(rules, matches.map { m -> m.rule })
|
||||
assertEquals(rules.toSet(), matches.map { m -> m.rule }.toSet())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
import jetbrains.mps.logic.reactor.core.Profiler
|
||||
import jetbrains.mps.logic.reactor.core.profile
|
||||
import org.junit.Test
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* @author Fedor Isakov
|
||||
*/
|
||||
|
||||
|
||||
class TestProfiler {
|
||||
|
||||
@Test
|
||||
fun testFooBar() {
|
||||
|
||||
val profiler = Profiler()
|
||||
|
||||
val foo = profiler.start("foo")
|
||||
|
||||
Thread.sleep(10)
|
||||
|
||||
val bar1 = profiler.start("bar")
|
||||
|
||||
Thread.sleep(20)
|
||||
|
||||
profiler.end(bar1)
|
||||
|
||||
val bar2 = profiler.start("bar")
|
||||
|
||||
Thread.sleep(20)
|
||||
|
||||
profiler.end(bar2)
|
||||
|
||||
profiler.end(foo)
|
||||
|
||||
val durationsMap = profiler.rawProfilingData()
|
||||
|
||||
assertEquals(1, durationsMap["foo"]!!.first / 10000000L)
|
||||
assertEquals(4, durationsMap["bar"]!!.first / 10000000L)
|
||||
assertEquals(1, durationsMap["foo"]!!.second)
|
||||
assertEquals(2, durationsMap["bar"]!!.second)
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testProfile() {
|
||||
Profiler().run {
|
||||
profile("foo", {
|
||||
Thread.sleep(10)
|
||||
profile("bar", {
|
||||
Thread.sleep(20)
|
||||
})
|
||||
profile("bazz", {
|
||||
Thread.sleep(30)
|
||||
})
|
||||
})
|
||||
|
||||
val durationsMap = rawProfilingData()
|
||||
|
||||
assertEquals(1, durationsMap["foo"]!!.first / 10000000L)
|
||||
assertEquals(2, durationsMap["bar"]!!.first / 10000000L)
|
||||
assertEquals(3, durationsMap["bazz"]!!.first / 10000000L)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue