Simplify how speedy returns its final result. (#5890)

- Have a special continuation `KFinished` which is pushed on the initially empty `kontStack`.
- `KFinished.execute` throws `SpeedyHungry` when it has the `SResultFinalValue`.
- This unifies the behaviour w.r.t other kinds of `SResult`.
- The `kontStack` is never empty during evaluation.
- So we no longer require an`isFinal` test, and so the inner loop is a little tighter.

The performance improvement here is marginal (1% maybe). But the clarity is better. Also, it's a nice invariant that there is always at least one continuation on the stack. An invariant which will be useful in a change planned to improve handling of `KPop` and `KLocation`.

changelog_begin
changelog_end
This commit is contained in:
nickchapman-da 2020-05-07 13:01:13 +01:00 committed by GitHub
parent 11a2fc3c2d
commit 25cb022eed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 31 additions and 22 deletions

View File

@ -133,9 +133,14 @@ object Speedy {
case _ =>
}
/** Reuse an existing speedy machine to evaluate a new expression.
Do not use if the machine is partway though an existing evaluation.
i.e. run() has returned an `SResult` requiring a callback.
*/
def setExpressionToEvaluate(expr: SExpr): Unit = {
ctrl = expr
returnValue = null
kontStack = initialKontStack()
env = emptyEnv
}
/** Run a machine until we get a result: either a final-value or a request for data, with a callback */
@ -151,7 +156,8 @@ object Speedy {
while (result == null) {
// note: exception handler is outside while loop
try {
while (!isFinal) {
// normal exit from this loop is when KFinished.execute throws SpeedyHungry
while (true) {
if (returnValue != null) {
val value = returnValue
returnValue = null
@ -162,11 +168,6 @@ object Speedy {
expr.execute(this)
}
}
if (returnValue != null) {
result = SResultFinalValue(returnValue)
} else {
throw SErrorCrash(s"Unexpected ctrl on final machine $ctrl")
}
} catch {
case SpeedyHungry(res: SResult) => result = res //stop
case serr: SError =>
@ -230,14 +231,6 @@ object Speedy {
}
}
/** Returns true when the machine has finished evaluation.
* The machine is considered final when the kont stack
* is empty, and we have a `returnValue`, and hence `ctrl` is null.
*/
def isFinal(): Boolean = {
kontStack.isEmpty && returnValue != null
}
def enterFullyAppliedFunction(prim: Prim, args: util.ArrayList[SValue]): Unit = {
prim match {
case PClosure(expr, vars) =>
@ -418,7 +411,7 @@ object Speedy {
ctrl = null,
returnValue = null,
env = emptyEnv,
kontStack = new util.ArrayList[Kont](128),
kontStack = initialKontStack(),
lastLocation = None,
ptx = PartialTransaction.initial(submissionTime, initialSeeding),
committers = Set.empty,
@ -513,6 +506,15 @@ object Speedy {
//
// Kontinuation
//
// Whilst the machine is running, we ensure the kontStack is *never* empty.
// We do this by pushing a KFinished continutaion on the initially empty stack, which
// returns the final result (by raising it as a SpeedyHungry exception).
def initialKontStack(): util.ArrayList[Kont] = {
val kontStack = new util.ArrayList[Kont](128)
kontStack.add(KFinished)
kontStack
}
/** Kont, or continuation. Describes the next step for the machine
* after an expression has been evaluated into a 'SValue'.
@ -523,6 +525,13 @@ object Speedy {
def execute(v: SValue, machine: Machine): Unit
}
/** Final continuation; machine has computed final value */
final case object KFinished extends Kont {
def execute(v: SValue, _machine: Machine) = {
throw SpeedyHungry(SResultFinalValue(v))
}
}
/** Pop 'count' arguments from the environment. */
final case class KPop(count: Int) extends Kont {
def execute(v: SValue, machine: Machine) = {
@ -710,7 +719,8 @@ object Speedy {
}
}
/** Internal exception thrown when a continuation result needs to be returned. */
/** Internal exception thrown when a continuation result needs to be returned.
Or machine execution has reached a final value. */
final case class SpeedyHungry(result: SResult) extends RuntimeException with NoStackTrace
def deriveTransactionSeed(

View File

@ -477,14 +477,13 @@ class OrderingSpec
private def initMachine(expr: SExpr) = Speedy.Machine fromSExpr (
sexpr = expr,
compiledPackages = PureCompiledPackages(Map.empty, Map.empty),
Time.Timestamp.now(),
InitialSeeding(Some(txSeed)),
Set.empty,
submissionTime = Time.Timestamp.now(),
seeding = InitialSeeding(Some(txSeed)),
globalCids = Set.empty,
)
private def translatePrimValue(v: Value[Value.AbsoluteContractId]) = {
val expr = SEImportValue(v)
val machine = initMachine(expr)
val machine = initMachine(SEImportValue(v))
machine.run() match {
case SResultFinalValue(value) => value
case _ => throw new Error(s"error while translating value $v")