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

[draft] [opinions welcome] Change the individual callback functions in run_program to a trait which simplifies lifetime management #398

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

prozacchiwawa
Copy link
Contributor

This makes PreEval a trait which is passed down the stack as a mutable reference. It contains both pre_eval and post_eval methods and calls post_eval when requested based on the result of pre_eval as before.

Because the callback's lifetime is now easily determined to be the lifetime of the run_program call and the other object that could be of use, the allocator is also passed by a mutable reference, the caller has maximal freedom in how their implementation of PreEval works without having to use the Rc<RefCell<...>> pattern and cunning use of move closures to create the callbacks and avoids construction of a Box on each callback for the second closure.

Since this changes the form of the run_program interface, commentary on that is welcome. This is a response to the arch meeting discussion rather than something I'm actively requesting, but I do think this is better overall.

Copy link

coveralls-official bot commented Apr 25, 2024

Pull Request Test Coverage Report for Build 8850932024

Details

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage remained the same at 93.947%

Totals Coverage Status
Change from base Build 8835105043: 0.0%
Covered Lines: 5789
Relevant Lines: 6162

💛 - Coveralls

@prozacchiwawa
Copy link
Contributor Author

thanks @Rigidity for the typedef suggestion and catching the ungated declaration.

&mut self,
_allocator: &mut Allocator,
_pass: PreEvalIndex,
_result: Option<NodePtr>,
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder why I did not just include the result verbatim, ie. use Result instead of Option. Maybe it was trying to be more like the original python? I wouldn't necessarily be opposed to changing this.

fn post_eval(
&mut self,
_allocator: &mut Allocator,
_pass: PreEvalIndex,
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this just a nonce to match with the PreEval sexp/args pair? I suppose _pass means something when this is used with the classic compiler, but I wonder if a more generic name might be better.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm ya, the name could improve. I was thinking 'passthrough' but will give it some thought.

if let Some(pre_eval) = &mut self.pre_eval {
if let PreEvalResult::CallPostEval(pass) =
pre_eval.pre_eval(self.allocator, program, env)?
{
Copy link
Contributor

Choose a reason for hiding this comment

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

This suggests to me that you probably need to do a cargo fmt.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

PreEvalResult::CallPostEval is longer so it re-wraps a bit. This is post fmt.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah fmt can be a bit funky at times, but that's the price of consistency I suppose :P

Copy link
Contributor

Choose a reason for hiding this comment

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

Interesting, I don't recall having seen a lone { like this before.

Operation::Apply => {
let apply_op_res = self.apply_op(cost, effective_max_cost - cost);
augment_cost_errors(apply_op_res, max_cost_ptr)?
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems like this little diff is just the exact same code in a different form, yes? I'm wondering if this change is intentional.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ya changed that back.

#[cfg(feature = "pre-eval")]
/// Implementing this trait allows an object to be notified of clvm operations
/// being performed as they happen.
pub trait PreEval {
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if there's now a better name for this, since it could potentially implement both pre and post eval. Maybe Tracer or EvalLogger or InvocationTracker or CallMonitor or something. (Thanks to ChatGPT for some of these ideas.)

PreEval is maybe more useful for people who know the code already, but that's maybe eight out of eight billion. :)

Copy link
Contributor

@richardkiss richardkiss left a comment

Choose a reason for hiding this comment

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

Besides some naming nits (and possibly turning an Option a Result as it seems like it may have been a mistake in the original API), it seems like a pretty reasonable way for this code to evolve. I think you (Arty) are the only one using this functionality these days, and it seems like a clear win.

Arvid already has gated PreEval behind a feature flag so we don't have to worry much about it affecting performance in production.

I didn't look closely at the tests, but I assume you just made whatever changes you needed to make the tests work.

@Rigidity
Copy link
Contributor

I think performance should still be a consideration, since features are additive - if you depend on clvm_tools_rs and clvmr in your project for instance, and clvm_tools_rs enables the feature, its performance effects will be seen on your usage of clvmr even if you don't specify the feature yourself. So we ideally wouldn't want it to be considerably slower, but I'm guessing the compiler can optimize this to around the same as before.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants