Introduced Profiler to track bottlenecks. Some obvious optimizations. Refactoring for cleaner code.

This commit is contained in:
Fedor Isakov 2016-01-21 11:42:05 +01:00
parent 1dd3294483
commit b8aa435eb4
8 changed files with 507 additions and 142 deletions

View File

@ -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)
}

View File

@ -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.

View File

@ -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()

View File

@ -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 }
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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())
}
}
}

View File

@ -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)
}
}
}