Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor speedy.Compiler.freeVars to be functional #6542

Merged
merged 1 commit into from
Jul 1, 2020

Conversation

garyverhaegen-da
Copy link
Contributor

I was trying to track down what looks like a new source of flakiness in the ANF work (#6440), which manifested in a stack overflow on the recursion of the go function in speedy.Compiler. I spent a bit of time refactoring this to be functional in order to then trampoline it, only to realize afterwards that I had worked on freeVars/go whereas the one blowing up the stack was validate/go.

Obviously, it did nothing to reduce the rate of flakiness on the stack overflow issue I was trying to address, as it's a separate function.

So there isn't really a good reason for this change, except I had done it and I do believe the code looks marginally nicer, so might as well submit it.

CHANGELOG_BEGIN
CHANGELOG_END

@cocreature
Copy link
Contributor

I’ll leave it to someone else to judge whether we want to merge this (code looks reasonable to me) but I can’t help but point to the Ben’s excellent blog post on efficient variable traversals in Haskell https://www.haskell.org/ghc/blog/20190728-free-variable-traversals.html (yes I know Haskell performance characteristics are different so it doesn’t necessarily apply here but it’s still interesting)

Copy link
Collaborator

@remyhaemmerle-da remyhaemmerle-da left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure it is very worthwhile to refactor this piece of code in a functional way.
since the imperative part is properly confined inside the body of a function.

Saying that, I have no objection about rewriting the code in a functional way.

You code seems however quadratic in the number of free variables, because of the set union (++) potentially made at each iterations.

I suggest you add an accumulator to the go method to track the free variables seen so far. I have made some change suggestions to give you an idea of what I mean.

var free = Set.empty[Int]

def go(expr: SExpr): Unit =
def go(expr: SExpr, bound: Int): Set[Int] =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def go(expr: SExpr, bound: Int): Set[Int] =
def go(expr: SExpr, bound: Int, free: Set[Int]): Set[Int] =

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like if you accept all @remyhaemmerle-da's changes, you can add @tailrec to this function too.

Copy link
Collaborator

@remyhaemmerle-da remyhaemmerle-da Jun 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately you can't because we are traversing a tree.

Copy link
Contributor Author

@garyverhaegen-da garyverhaegen-da Jun 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a tree traversal so I don't think that will be possible. args.foldLeft(free)((free, arg) => go(arg, bound, free)) for example is not a (recursive) tail call.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed that; thanks for explaining.

Comment on lines 1151 to 1153
go(scrut, bound) ++ alts
.map { case SCaseAlt(pat, body) => go(body, bound + patternNArgs(pat)) }
.reduce(_ ++ _)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
go(scrut, bound) ++ alts
.map { case SCaseAlt(pat, body) => go(body, bound + patternNArgs(pat)) }
.reduce(_ ++ _)
alts.foldLeft(go(scrut, bound, free)) {
case (free, SCaseAlt(pat, body)) => go(body, bound + patternNArgs(pat), free)
}

@garyverhaegen-da
Copy link
Contributor Author

@remyhaemmerle-da I made the change you requested, AFAICT this is now doing exactly one set insertion operation per free variable (and no set union at all).

Copy link
Collaborator

@remyhaemmerle-da remyhaemmerle-da left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks.

@remyhaemmerle-da remyhaemmerle-da added the component/daml-engine DAML-LF Engine & Interpreter label Jun 30, 2020
@remyhaemmerle-da remyhaemmerle-da added this to the Maintenance milestone Jun 30, 2020
I was trying to track down what looks like a new source of flakiness in
the ANF work (#6440), which manifested in a stack overflow on the
recursion of the `go` function in speedy.Compiler. I spent a bit of time
refactoring this to be functional in order to then trampoline it, only
to realize afterwards that I had worked on `freeVars/go` whereas the one
blowing up the stack was `validate/go`.

Obviously, it did nothing to reduce the rate of flakiness on the stack
overflow issue I was trying to address, as it's a separate function.

So there isn't really a good reason for this change, except I had done
it and I do believe the code looks marginally nicer, so might as well
submit it.

CHANGELOG_BEGIN
CHANGELOG_END
@mergify mergify bot merged commit 72851d8 into master Jul 1, 2020
@mergify mergify bot deleted the random-refactoring branch July 1, 2020 07:26
cocreature added a commit that referenced this pull request Jul 8, 2020
After careful consideration, we decided that the change in evaluation
order that was accidentally introduced by the ANF changes should be
considered a breaking change or arguably even a bug and should not
land in 1.3.0.

Therefore, this PR reverts the following commits:

1. 353d0da
2. a45b510
3. 04c7b2a
4. a624dd7
5. b3aab72

Other PRs mostly had trivial merge conflicts that I resolved. The two
most interesting ones here are probably

1. #6576 which was easy to
   resolve and the change to return SEValue instead of SExpr is still
   nice and useful even if we do not need the guarantees.
2. it #6542 which required
   some changes since the constructors changed. If you want to review
   those changes in detail (they are pretty straightforward so not too
   important), it’s probably easiest to check out this PR and run
   ```
   git diff 2cd2a8f
   daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Compiler.scala
   ```
   to see the diff to the parent commit of the first commit that
   introduced ANF.

changelog_begin
changelog_end
cocreature added a commit that referenced this pull request Jul 8, 2020
* Revert ANF changes and add a testcase for evaluation order

After careful consideration, we decided that the change in evaluation
order that was accidentally introduced by the ANF changes should be
considered a breaking change or arguably even a bug and should not
land in 1.3.0.

Therefore, this PR reverts the following commits:

1. 353d0da
2. a45b510
3. 04c7b2a
4. a624dd7
5. b3aab72

Other PRs mostly had trivial merge conflicts that I resolved. The two
most interesting ones here are probably

1. #6576 which was easy to
   resolve and the change to return SEValue instead of SExpr is still
   nice and useful even if we do not need the guarantees.
2. it #6542 which required
   some changes since the constructors changed. If you want to review
   those changes in detail (they are pretty straightforward so not too
   important), it’s probably easiest to check out this PR and run
   ```
   git diff 2cd2a8f
   daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Compiler.scala
   ```
   to see the diff to the parent commit of the first commit that
   introduced ANF.

changelog_begin
changelog_end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component/daml-engine DAML-LF Engine & Interpreter
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants