Skip to content

Do not unnecessarily break sharing during renaming #4286

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

Closed
wants to merge 5 commits into from

Conversation

tautschnig
Copy link
Collaborator

@tautschnig tautschnig commented Feb 27, 2019

Not all subexpressions (or types) change during renaming. Only actually write to
the result when there was a change. This preserves sharing. On SV-COMP's
ReachSafety-ECA benchmarks the time spent in goto_symex_state::rename(exprt &,
...) is now 1944 seconds, when previously it was 2326 seconds. On the other
hand, the time spent in merge_ireps did not improve, the number of recursive
operator== calls in fact was higher (6930718243 rising to from 5710509892).

The time spent in goto_symex_statet::rename(typet &, ...) increases from 530
seconds to 568 seconds, which suggests there is room for improvement.

I will keep this PR in Draft status to make sure #3996 won't need a rebase.

  • Each commit message has a non-empty body, explaining why the change was made.
  • Methods or procedures I have added are documented, following the guidelines provided in CODING_STANDARD.md.
  • n/a The feature or user visible behaviour I have added or modified has been documented in the User Guide in doc/cprover-manual/
  • n/a Regression or unit tests are included, or existing tests cover the modified code (in this case I have detailed which ones those are in the commit message).
  • My commit message includes data points confirming performance improvements (if claimed).
  • My PR is restricted to a single feature or bugfix.
  • n/a White-space or formatting changes outside the feature-related changed lines are in commits of their own.

@romainbrenguier
Copy link
Contributor

I think for review it would be nice to split the PR in three commits: on that makes rename_address return an optional, one for rename_type and one for rename_expr.

@tautschnig tautschnig force-pushed the rename-optional branch 2 times, most recently from 5ef434d to 7cf4875 Compare February 27, 2019 16:49
@tautschnig
Copy link
Collaborator Author

Split into three commits done and rebased onto current develop - releasing for review.

Copy link
Contributor

@allredj allredj left a comment

Choose a reason for hiding this comment

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

✔️
Passed Diffblue compatibility checks (cbmc commit: 7cf4875).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/102536309

Copy link
Contributor

@allredj allredj left a comment

Choose a reason for hiding this comment

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

⚠️
This PR failed Diffblue compatibility checks (cbmc commit: 5ef434d).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/102537067
Status will be re-evaluated on next push.
Common spurious failures:

  • the cbmc commit has disappeared in the mean time (e.g. in a force-push)

  • the author is not in the list of contributors (e.g. first-time contributors).

  • the compatibility was already broken by an earlier merge.

Copy link
Contributor

@smowton smowton left a comment

Choose a reason for hiding this comment

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

Looks good -- my comments about redundant quality tests may in fact be subsumed by later commits.

Question: does this render the rename-required pre-pass redundant? Should we combine the two methods into one walk at this point?

Pattern question: mild preference for something that takes an exprt & as at present, carefully turns that into a const exprt &, uses that to determine if anything needs doing, and then writes to it if appropriate, rather than having the if(ret) val = *ret; pattern at every callsite

@tautschnig tautschnig changed the title Do not unnecessarily break sharing during renaming Do not unnecessarily break sharing during renaming [depends-on: #4264] Feb 27, 2019
@tautschnig
Copy link
Collaborator Author

I'm taking the liberty to mark this dependent-on-#4264.

@romainbrenguier
Copy link
Contributor

@tautschnig #4286 has been merged, and this needs rebasing

@tautschnig tautschnig changed the title Do not unnecessarily break sharing during renaming [depends-on: #4264] Do not unnecessarily break sharing during renaming Mar 7, 2019
@tautschnig tautschnig added the Needs data This PR claims improvements that require further data to substantiate the claims. label Mar 8, 2019
Instead of editing the object in place, return an optional<typet> that
is set if, and only if, a modification took place.
requires_renaming must not recurse into array subtypes in structs.
We return a renamed type if, and only if, renaming was necessary. Thus
we can do the is-it-necessary and the actual renaming in a single pass.
…sses)

Instead of editing the object in place, return an optional<exprt> that
is set if, and only if, a modification took place.
Not all subexpressions (or types) change during renaming. Only actually
write to the result when there was a change. This preserves sharing.

Performance results for this and parts 1 and 2 of the renaming combined:
On SV-COMP's ReachSafety-ECA benchmarks the time spent in
goto_symex_state::rename(exprt &, ...) is now 1944 seconds, when
previously it was 2326 seconds. On the other hand, the time spent in
merge_ireps did not improve, the number of recursive operator== calls in
fact was higher (6930718243 rising to from 5710509892).

The time spent in goto_symex_statet::rename(typet &, ...) increases from
530 seconds to 568 seconds, which suggests there is room for
improvement.
Copy link
Contributor

@allredj allredj left a comment

Choose a reason for hiding this comment

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

✔️
Passed Diffblue compatibility checks (cbmc commit: 71aec16).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/103873701

@tautschnig
Copy link
Collaborator Author

I'm currently running benchmarks of develop vs. this branch to back up these changes by performance data. If those do not yield any negative results I'm inclined to merge this as-is and do the cleanup proposed by @romainbrenguier and @smowton in follow-up work.

@tautschnig
Copy link
Collaborator Author

Here are my profiling results (on ReachSafety-ECA from SV-COMP):
develop performs 2959.21 symex steps per second, applying this PR we achieve 2844.20 steps per second (approximately 4% slowdown). The number of calls to detach reduces from 25,442,953,846 to 18,862,575,940. The amount of time spent in rename<L2>(exprt, ...) seems to increase from 434 seconds to 464 seconds, but it's not entirely clear what that time is spent on other than just doing recursive calls - it seems the recursive calls got a bit more expensive, which would seem fair given that we now have a return value to manipulate and some branching to do.

In my opinion this is no clear indication either way. I think the approach of not breaking sharing is cleaner, but it's certainly no performance win by itself.

@kroening @peterschrammel @romainbrenguier @smowton Can you please voice your opinions on whether we should do this or not?

@tautschnig tautschnig removed the Needs data This PR claims improvements that require further data to substantiate the claims. label Mar 11, 2019
@romainbrenguier
Copy link
Contributor

Since it doesn't have a positive impact on performances and makes the code more complicated, I would be in favor of not merging it.

Copy link
Contributor

@romainbrenguier romainbrenguier left a comment

Choose a reason for hiding this comment

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

I'm "requesting changes" so that this doesn't get merged by mistake.

@smowton
Copy link
Contributor

smowton commented Mar 12, 2019

Agreed that the sharing breaking is a great thing, and it seems like it shouldn't be too difficult to get that without incurring a lot of extra cost tracking that. Could you find out if it reduces the lost time to return exprt rather than optionalt<exprt> and use &old_expr.read() == &new_expr.read() to determine if there has been a change? In particular this will enable return elision as we'll be directly returning an exprt.

@tautschnig
Copy link
Collaborator Author

Closing as all this code has been substantially rewritten already and uses move semantics today.

@tautschnig tautschnig closed this Nov 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants