diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Handler.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Handler.kt index 2c380c0a..599017ec 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Handler.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Handler.kt @@ -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() - private val stored = ArrayList() + private val occurrenceStore = OccurrenceStore() private val activeQueue = LinkedList() - private val activationStack = LinkedList() + private val activationStack = LinkedList() + + private val matcher: Matcher constructor( sessionSolver: SessionSolver, programRules: Iterable, trace: EvaluationTrace = EvaluationTrace.NULL, + profiler: Profiler? = null, // for testing purposes only occurrences: Iterable? = 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 = stored.toSet() + fun occurrences(): Set = 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 + { + return profiler.profile>("findOccurrences", { + return occurrenceStore.allFor(constraint).filter { - val matcher = object : Matcher(rules) { - override fun findOccurrences(constraint: Constraint, acceptable: (ConstraintOccurrence) -> Boolean): - Iterable = - 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("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("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) } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Matcher.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Matcher.kt index 459b8ec6..7e4269ef 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Matcher.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Matcher.kt @@ -17,125 +17,185 @@ import java.util.* * @author Fedor Isakov */ -abstract class Matcher(val rules: Collection) { +class Matcher { + + interface AuxOccurrences { + + fun findOccurrences(constraint: Constraint, acceptable: (ConstraintOccurrence) -> Boolean): + Iterable + + } + + val rules: RuleIndex + val aux: AuxOccurrences + val profiler: Profiler? + + constructor(rules: Collection, aux: AuxOccurrences, profiler: Profiler? = null) { + this.rules = RuleIndex(rules) + this.aux = aux + this.profiler = profiler + } fun lookupMatches(occ: ConstraintOccurrence): Iterable { - 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>("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 { 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>("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("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("acceptable", { + + !match.hasOccurrence(occ) + + }) + }).flatMap { occ -> completeMatch(match.discard(cst, occ)) } + } + + matchesFromKept + matchesFromDiscarded + + }) + } + + inner class PartialMatch(val rule: Rule) : MatchRule { + + var kept = ConsList.empty>() + private set + var discarded = ConsList.empty>() + private set + private lateinit var logicalContext : LogicalContext + + private constructor( + original : PartialMatch, + keep: Pair?, + discard: Pair?) : 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("hasOccurrence", { + + return kept.any { p -> p.second === occ } || discarded.any { p -> p.second === occ } + +// }) } - return matchesFromKept + matchesFromDiscarded - } + fun isPartial() : Boolean { +// return profiler.profile("isPartial", { - abstract fun findOccurrences(constraint: Constraint, acceptable: (ConstraintOccurrence) -> Boolean): - Iterable + 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 { +// return profiler.profile>("occurrences", { - var kept = ConsList.empty>() - private set - var discarded = ConsList.empty>() - 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?, - discard: Pair?) : 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("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 = - 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, Any?> = HashMap(subst.bindings().map { b -> + (b.`var`().symbol() as LogicalPattern).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, 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, Any?> = HashMap(subst.bindings().map { b -> - (b.`var`().symbol() as LogicalPattern).to(b.term().toValue()) }.toMap()) - - val pattern2logical: MutableMap, Logical<*>> = HashMap() - - override fun variable(logicalPattern: LogicalPattern): Logical { - 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 variable(logicalPattern: LogicalPattern): Logical { + 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 } } - return pattern2logical[logicalPattern] as Logical - } + return true + + }) } - return true + fun logicalContext(): LogicalContext = logicalContext + + override fun rule(): Rule = rule + + override fun matchHeadKept(): Iterable = kept.map { p -> p.second } + + override fun matchHeadReplaced(): Iterable = discarded.map { p -> p.second } + } - fun logicalContext(): LogicalContext = logicalContext - - override fun rule(): Rule = rule - - override fun matchHeadKept(): Iterable = kept.map { p -> p.second } - - override fun matchHeadReplaced(): Iterable = 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("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. diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/MemEvaluationSession.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/MemEvaluationSession.kt index bc27fb9f..50f77d09 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/MemEvaluationSession.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/MemEvaluationSession.kt @@ -52,11 +52,14 @@ class MemEvaluationSession : EvaluationSession { session = MemEvaluationSession(program, myEvaluationTrace) ourBackend.ourSession.set(session) + val durations = myParameters.get("profiling.data") as MutableMap? + 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() diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/OccurrenceStore.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/OccurrenceStore.kt new file mode 100644 index 00000000..a690c0bb --- /dev/null +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/OccurrenceStore.kt @@ -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>() + + fun addAll(all: Iterable): Unit { + for(occ in all) { + add(occ) + } + } + + fun add(occ: ConstraintOccurrence): Unit { + val symbol = occ.constraint().symbol() + if (!symbol2list.containsKey(symbol)) { + symbol2list[symbol] = ArrayList() + } + 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 { + val symbol = cst.symbol() + if (symbol2list.containsKey(symbol)) { + return symbol2list[symbol]!! + } + else + return emptyList() + } + + fun allOccurrences(): Iterable = + symbol2list.values.flatMap { it } + +} \ No newline at end of file diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Profiler.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Profiler.kt new file mode 100644 index 00000000..f6c18389 --- /dev/null +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Profiler.kt @@ -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() + + 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> { + val name2duration = HashMap>() + tokenStack.peek().mergeDurations(name2duration) + return name2duration + } + + fun formattedData(): Map { + 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() + + 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>) { + 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 Profiler?.profile(name: String, function: () -> R): R { + val tok = this?.start(name) + try { + return function.invoke() + } + finally { + this?.end(tok) + } +} diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleIndex.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleIndex.kt new file mode 100644 index 00000000..c72a6267 --- /dev/null +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleIndex.kt @@ -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 { + + private val symbol2rules = HashMap>() + + private var rules: Collection + + constructor(rules: Collection) { + this.rules = rules + buildIndex(rules) + } + + fun forSymbol(symbol: ConstraintSymbol): Iterable? = symbol2rules[symbol] + + override fun iterator(): Iterator = rules.iterator() + + private fun buildIndex(rules: Collection) { + 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) + } + +} \ No newline at end of file diff --git a/reactor/Test/test/TestMatcher.kt b/reactor/Test/test/TestMatcher.kt index 4fde53aa..bfed1f25 100644 --- a/reactor/Test/test/TestMatcher.kt +++ b/reactor/Test/test/TestMatcher.kt @@ -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 = 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()) } } } diff --git a/reactor/Test/test/TestProfiler.kt b/reactor/Test/test/TestProfiler.kt new file mode 100644 index 00000000..7e2f75fd --- /dev/null +++ b/reactor/Test/test/TestProfiler.kt @@ -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) + } + } + +} +