-
Notifications
You must be signed in to change notification settings - Fork 274
Do not unnecessarily sort guard conjuncts [blocks: #3486] #1998
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
Conversation
src/util/guard.cpp
Outdated
if(it1!=op1.end() && *it1==*it2) | ||
it1=op1.erase(it1); | ||
else |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this works? For example, a & b
- b
would not erase the b
, since it1
now has no way of moving except when erasing an element.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Relatedly, for vectors this has worst-case quadratic cost. How about making a utility inplace-set-difference that provides a good place to benchmark this sort of repeated single-element erase vs. std::set_difference
followed by swap?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that this would not work with a & b
- b
, but as the commit message says: we never construct those. My proposal for a full solution is #310: use BDDs. I'm open to all other ideas. But right now it's just computational overhead...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case I suggest renaming this remove_prefix_conjuncts
, since its behaviour is much more specific than a generic operator-=
, and add a doxy comment specifying that's all it can do. Also consider using std::mismatch
?
1e4fd71
to
6bd4556
Compare
There was a problem hiding this 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: 6bd4556).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/104021437
For the record: on ReachSafety-ECA this increases the number of symex steps per second from 2961.83 to 3580.92 - a speedup of approximately 20%. This is all due to saving approximately 2500 seconds in Similar savings may be possible when using BDDs, in particular with CUDD. Benchmarking thereof started just now. |
Adding data on BDDs (using CUDD): the |
The guards sometimes become much bigger with BDDs than with exprt. On my benchmarks I also noticed that while the global execution was 30% faster, the execution with |
The above data was all based on profiling information, which I prefer not to exclusively rely on. The optimised, competition-setting runs yield (more == better): develop: 116 points This of course is only a single (and somewhat peculiar: basically no pointers, but otherwise heavily stressing goto-symex) category. |
I'll try to do some benchmarking with |
Oh yes I always use HASH_CODE. For the merging of ireps it seems redundant for BDDs since all nodes that are logically equivalent are already "merged". So maybe the merge and the conversion from BDD to exprt should be done together. I will try some things. |
I'm not sure I understand what these steps represent. Could you explain? If BDDs lead to fewer steps being executed for analyzing the same program, you could have fewer steps per second but still be faster? |
Yes, this is certainly possible, hence me also posting the scores achieved in that category. What I'm looking at in the profiles is:
For develop and ReachSafety-ECA those numbers were:
I do agree and emphasise that this is by no means a complete picture. For me this mainly is about having reproducible data that I can key an eye on over time. |
6bd4556
to
f4a99f0
Compare
There was a problem hiding this 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: f4a99f0).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/105613658
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.
There is something significant in that graph to do with the number of time-outs. There is a vertical "smear" near the X axis time-out which suggests that there is a serious scalability difference there so I am surprised the numbers are so similar. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the patch implements what is described in the PR. Whether we want it is a different question. I can see that this is performance critical and I don't think the current behaviour is optimal, even for this design. The "common prefix" comments give me pause for concern because that doesn't describe what I think is going on. Could we simply add (and document) some invariants on the guard expressions always being in (some) sorted order? It feels like that should be do-able, would give the performance benefits of this patch with some extra certainty of what is going on.
( I agree that BDDs are actually the way of solving this in full generality but I do wonder whether what we have is sufficiently restricted that we can solve it with a less heavy-weight solution. Regardless, I think pinning down what is going on here in terms of order with help the BDD case as well. )
With latest develop (and this PR rebased on top of it) profiling suggest that we save approximately 30% in |
On Mon, 2019-03-25 at 04:35 -0700, Michael Tautschnig wrote:
With latest develop (and this PR rebased on top of it) profiling
suggest that we save approximately 30% in `guard_exprt::operator-=`
(reducing time spent in there from 1000s to 700s). For the overall
result, however, there is no notable difference on ReachSafety-ECA.
Do we generate the same results? I can believe that we can save time
in guard_exprt but generate more complex guards and thus loose time
over-all.
|
As far as I can tell we generate exactly the same results. |
Guards are (no longer) generic conjunctions. They are constructed in a consistent order. If a more generic set-up is required, use BDD-backed guards.
f4a99f0
to
eabd67c
Compare
Codecov Report
@@ Coverage Diff @@
## develop #1998 +/- ##
===========================================
- Coverage 75.53% 75.45% -0.09%
===========================================
Files 1447 1447
Lines 158116 158087 -29
===========================================
- Hits 119431 119277 -154
- Misses 38685 38810 +125
Continue to review full report at Codecov.
|
The following two flame graphs demonstrate what made me look into this (observe how That being said, the overall impact is fairly small (total CPU time saved is a few seconds), and this benchmark category of SV-COMP (Reachsafety-Recursive) is the only one where this was notable at all. I do maintain that this sorting is unnecessary given the way guards are constructed by goto-symex (AFAIK the only user), but I don't think the performance data alone provide a great deal of support for this cleanup. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the code is redundant and removing it 1. causes no harm, 2. improves things a bit then... why not?
They are constructed in a consistent order anyway.