From 022a9c372e3fe8c83384e2cb2362a70b951dce1f Mon Sep 17 00:00:00 2001 From: Grigorii Kirgizov Date: Thu, 13 Jun 2019 21:27:46 +0300 Subject: [PATCH] Avoid searches of principal occurrence by utilizing MatchJournal.Index --- .../reactor/core/internal/MatchJournal.kt | 47 ++++++++++++++----- .../core/internal/ProcessingStateImpl.kt | 28 ++++------- 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt index 2786f035..134ee32b 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt @@ -46,6 +46,18 @@ interface MatchJournal : MutableIterable { interface Index : Comparator { fun activatingChunkOf(occ: Id): Chunk? + fun principalOccurrenceOf(chunk: Chunk): OccurrencePos? + + // just go through two maps + fun principalPos(occ: Id): OccurrencePos? = + activatingChunkOf(occ)?.let { principalOccurrenceOf(it) } + + // The latest matched occurrence from match's head is (by definition) + // the occurrence which activated this match. + fun activationPos(match: RuleMatchEx): OccurrencePos? = + match.signature().mapNotNull { occSig -> + occSig?.let { principalPos(it) } + }.maxWith(this) // compare positions: find latest } data class View(val chunks: List, val nextChunkId: Int) @@ -85,22 +97,23 @@ interface MatchJournal : MutableIterable { && other.entriesInChunk() == entriesInChunk() } - data class OccurrencePos(val occ: Occurrence, private val chunk: MatchJournal.Chunk, private val offset: Int): MatchJournal.Pos() + data class OccurrencePos(val occ: Occurrence, private val chunk: Chunk, private val offset: Int): MatchJournal.Pos() { companion object { fun fromIndex(index: MatchJournal.Index, occ: Occurrence): OccurrencePos? { val idOcc = Id(occ) - val chunk = index.activatingChunkOf(idOcc) ?: return null - val i = chunk.entriesLog().indexOfFirst { entry -> + val chunk = index.activatingChunkOf(idOcc) + ?: return null + + val offset = chunk.entriesLog().indexOfFirst { entry -> Id(entry.occ) == idOcc && !entry.isDiscarded } - val offset = i + 1 return if (offset >= 0) OccurrencePos(occ, chunk, offset) else null } } override fun chunk(): MatchJournal.Chunk = chunk - override fun entriesInChunk(): Int = offset + override fun entriesInChunk(): Int = offset + 1 } } @@ -232,9 +245,9 @@ internal open class MatchJournalImpl( private class ChunkImpl(match: RuleMatch, id: Int, justifications: Justs) : MatchJournal.Chunk(match, id, justifications) { - var occurrences: MutableList = mutableListOf() + var occurrences: MutableList = mutableListOf() - override fun entriesLog(): List = occurrences + override fun entriesLog(): List = occurrences } class IndexImpl(ispec: IncrementalProgramSpec, chunks: Iterable): MatchJournal.Index @@ -243,24 +256,32 @@ internal open class MatchJournalImpl( // only for principal constraints private val parentChunks: Map, MatchJournal.Chunk> + private val principalOccurrences: Map + init { chunkOrder = HashMap().apply { chunks.forEachIndexed { index, chunk -> put(chunk.id, index) } } val m = HashMap, MatchJournal.Chunk>() - chunks.forEach {chunk -> - chunk.activatedLog().filter { occ -> - ispec.isPrincipal(occ.constraint) - }.forEach {occ -> - m[Id(occ)] = chunk + val m2 = HashMap() + chunks.forEach { chunk -> + // actually there should be only a single principal occurrence, 'find' is enough + chunk.entriesLog().forEachIndexed { index, e -> + if (ispec.isPrincipal(e.occ.constraint) && !e.isDiscarded) { + m[Id(e.occ)] = chunk + m2[chunk.id] = MatchJournal.OccurrencePos(e.occ, chunk, index) + } } } parentChunks = m + principalOccurrences = m2 } // todo: for non-principal find manually, walk through the whole journal... need it? - override fun activatingChunkOf(occ: Id): MatchJournal.Chunk? = parentChunks[occ] + override fun activatingChunkOf(occ: Id) = parentChunks[occ] + + override fun principalOccurrenceOf(chunk: MatchJournal.Chunk) = principalOccurrences[chunk.id] // todo: throw for invalid positions? override fun compare(lhs: MatchJournal.Pos, rhs: MatchJournal.Pos): Int { diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt index da10409c..23d25387 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt @@ -19,7 +19,6 @@ package jetbrains.mps.logic.reactor.core.internal import jetbrains.mps.logic.reactor.core.* import jetbrains.mps.logic.reactor.evaluation.EvaluationTrace import jetbrains.mps.logic.reactor.evaluation.RuleMatch -import jetbrains.mps.logic.reactor.program.Constraint import jetbrains.mps.logic.reactor.program.Rule import jetbrains.mps.logic.reactor.util.Id import jetbrains.mps.logic.reactor.util.Profiler @@ -144,7 +143,7 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp while (true) { // Does this chunk have principal occurrence and can activate anything at all? - val pOcc = chunk.principalOccurrence() + val pOcc = journalIndex.principalOccurrenceOf(chunk)?.occ if (pOcc != null) { for (rule in rules) { @@ -254,10 +253,11 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp val matches = dispatchingFront.matches().toList() val newCurrentMatches = // todo: assert that there can be no future matches on non-principal occurrences? - if (isCurrent() || !active.isPrincipal()) + if (isCurrent() || !active.isPrincipal()) { matches - else + } else { postponeFutureMatches(matches) + } val currentMatches = withPostponedMatches(active, newCurrentMatches) val outStatus = currentMatches.fold(inStatus) { status, match -> @@ -286,10 +286,13 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp // Determines, filters out and enqueues (to execution queue) future matches. Returns only current matches. private fun postponeFutureMatches(matches: List): List { - val currentMatches = mutableListOf() + assert(matches.all { ispec.isPrincipal(it.rule()) }) + val currentMatches = mutableListOf() for (m in matches) { - val pos = latestMatchedOccurrence(m) + // Returns null for matches with occurrences only from this session + // because journalIndex indexes only previous session. + val pos = journalIndex.activationPos(m) // if it is a future match if (pos != null && journalIndex.compare(lastIncrementalRootPos, pos) < 0) { val idOcc = Id(pos.occ) @@ -302,16 +305,6 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp return currentMatches } - // The latest matched occurrence from match's head is (by definition) the occurrence which activated this match. - // Returns null for matches with occurrences only from this session - // (because journalIndex indexes only previous session). - private fun latestMatchedOccurrence(match: RuleMatchEx): MatchJournal.OccurrencePos? = - match.signature().mapNotNull { occSig -> - occSig?.wrapped?.let { occ -> - MatchJournal.OccurrencePos.fromIndex(journalIndex, occ) - } - }.maxWith(journalIndex) // compare positions: find latest - private inline fun FeedbackStatus.then(action: (FeedbackStatus) -> FeedbackStatus) : FeedbackStatus = if (operational) action(this) else this @@ -352,9 +345,6 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp } - private fun MatchJournal.Chunk.principalOccurrence(): Occurrence? = - activatedLog().find { ispec.isPrincipal(it.constraint) } - private fun Occurrence.isPrincipal() = ispec.isPrincipal(this.constraint()) private fun Justs.intersects(other: Iterable): Boolean = other.any { this.contains(it) }