Avoid searches of principal occurrence by utilizing MatchJournal.Index

This commit is contained in:
Grigorii Kirgizov 2019-06-13 21:27:46 +03:00
parent 65f1198a60
commit 022a9c372e
2 changed files with 43 additions and 32 deletions

View File

@ -46,6 +46,18 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk> {
interface Index : Comparator<Pos> {
fun activatingChunkOf(occ: Id<Occurrence>): Chunk?
fun principalOccurrenceOf(chunk: Chunk): OccurrencePos?
// just go through two maps
fun principalPos(occ: Id<Occurrence>): 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<Chunk>, val nextChunkId: Int)
@ -85,22 +97,23 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk> {
&& 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<MatchJournal.Chunk.Entry> = mutableListOf()
var occurrences: MutableList<Entry> = mutableListOf()
override fun entriesLog(): List<MatchJournal.Chunk.Entry> = occurrences
override fun entriesLog(): List<Entry> = occurrences
}
class IndexImpl(ispec: IncrementalProgramSpec, chunks: Iterable<MatchJournal.Chunk>): MatchJournal.Index
@ -243,24 +256,32 @@ internal open class MatchJournalImpl(
// only for principal constraints
private val parentChunks: Map<Id<Occurrence>, MatchJournal.Chunk>
private val principalOccurrences: Map<Int, MatchJournal.OccurrencePos>
init {
chunkOrder = HashMap<Int, Int>().apply {
chunks.forEachIndexed { index, chunk -> put(chunk.id, index) }
}
val m = HashMap<Id<Occurrence>, MatchJournal.Chunk>()
chunks.forEach {chunk ->
chunk.activatedLog().filter { occ ->
ispec.isPrincipal(occ.constraint)
}.forEach {occ ->
m[Id(occ)] = chunk
val m2 = HashMap<Int, MatchJournal.OccurrencePos>()
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<Occurrence>): MatchJournal.Chunk? = parentChunks[occ]
override fun activatingChunkOf(occ: Id<Occurrence>) = 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 {

View File

@ -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<RuleMatchEx>): List<RuleMatchEx> {
val currentMatches = mutableListOf<RuleMatchEx>()
assert(matches.all { ispec.isPrincipal(it.rule()) })
val currentMatches = mutableListOf<RuleMatchEx>()
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<Int>): Boolean = other.any { this.contains(it) }