-
Notifications
You must be signed in to change notification settings - Fork 273
Restore irept sharing #1855
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
Restore irept sharing #1855
Conversation
@reuk perhaps you'd like to give an opinion, even if you're unable to contribute code at the moment? |
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.
Looks good!
@martin-cs I know you've done memory profiling of cbmc before, do you have any opinion on the importance of irep sharing? |
52b52e1
to
66fe004
Compare
@tautschnig - I believe you may have some performance tests. Could you check that this improves performance? If it doesn't then something has gone wrong. |
66fe004
to
75d6146
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.
tl;dr I'm wondering whether #1742 should just be reverted?
- Apart from the fact that AppVeyor shows that this isn't quite right, why do we need changes to expr.{h,cpp}? This must be about
irept
only as otherwise it is no longer safe to inherit fromirept
. - The test seems really useful, but what is it that actually is broken right now?
- Which change specifically (among those in Owen jones diffblue/small shared ptr #1742) was it that caused the problem?
We had a data structure that lived nearly untouched for about 15 years. And now we have to touch it for a second time in a fairly short time frame. That's not a way of earning trust.
The breakage: functions that leak non const references to sub structures (whether the operands vector or a sub-irep in that vector (or the named sub etc)) forbid sharing the irep, for fear those references could have been retained and then used to mutate a shared irep without causing a CoW break. This introduces some utilities that perform some useful mutation and then drop all references to internals (eg. add-to-operands), so they can safely leave sharing enabled for the mutated irep. |
I'd go for reverting #1742 for the moment. The proposed change can live as a branch for some time, until we've done proper benchmarking. |
In line with what @kroening said I'd propose:
This will enable us to get back to a known-good baseline, even though it might not be as safe as what #1742 had provided. From that baseline we can then try to get to a solution that is also safe. The unit test is a pretty big win of this process as it will ensure that we stay on a good route. |
131 lines, of which 60ish were a unit test?
I've addressed this idea on #1860
The error there doesn't make sense to me yet, anyone got any helpful pointers on how to address it?
The test asserts that sharing can be maintained whenever it is safe and not when it isn't safe.
Addressed in my comment on #1860 |
I thought it was now impossible to use methods that are unsafe to use in conjunction with sharing? |
You can use them, but they break sharing! Sorry for my bad explanation. If we don't alter For example, you can add an operand as a single operation that doesn't leak any references that could later be updated, so this is sharing-safe, but if you implement it in terms of |
@smowton : memory profiling and sharing. Sharing is vital; we need it and it must work correctly and efficiently. |
operandst &op=operands(); | ||
op.push_back(static_cast<const exprt &>(get_nil_irep())); | ||
op.back().swap(expr); | ||
move_to_sub(expr); |
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 looks nice in a "use the API, not the internals" kind of way.
src/util/expr.cpp
Outdated
{ | ||
operands().push_back(expr); | ||
move_to_operands(expr); |
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 rather suggests that the semantics of "move" and "copy" are the same, which, for a reference counted thing, is ... a little concerning.
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.
The difference is that copy takes its argument by value and moves the copied value into the operands. If I was rewriting the API I'd have a single call that took by value and allowed you to move or copy into that but this PR isn't to update the API.
Let me clarify this with example code:
Preferred API:
void add(value by_value) { impl.add(std::move(by_value)); }
// Usage
value value;
add(value); // Copies value in, the copy is moved into the internals
add(std::move(value)); // Moves value in, which is then moved again into the internals
Existing API:
void copy(value by_value) { move(by_value); }
void move(value &by_ref) { impl.add(std::move(by_ref)); } // Interface left over from pre-std::move days
// Usage
value value;
copy(value); // Copies value in
move(value); // Moves value in without std::move at call site :/
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.
Added comment line to draw attention to the copy by value in the arguments.
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 guess I was thinking about the impact on reference counting.
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.
The pass by value in the argument causes an increase in the reference count. The move version doesn't have the copy caused by the pass by value and so doesn't increase the reference count. Another comment required to say this?
@@ -160,47 +160,23 @@ const irept &irept::find(const irep_namet &name) const | |||
return it->second; | |||
} | |||
|
|||
irept &irept::add(const irep_namet &name) | |||
irept &irept::add(const irep_namet &name, bool mark_sharable) |
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.
The flag is kinda critical; any chance of some documentation for it?
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.
The documentation is now in the header file.
75d6146
to
8b371ab
Compare
8b371ab
to
d84af9f
Compare
The AppVeyor crash was caused by existing unsafe behaviour in CBMC shown up by this optimisation - this is fixed by #1896 and cherry-picking that commit into this PR (just until that PR is merged) has fixed the problem. |
b258b5c
to
68507c3
Compare
@tautschnig - this PR is now rebased on the head of develop that includes the fix that was causing AppVeyor problems and so is passing CI. How are you feeling about moving forwards with this, having had a couple of weeks to consider it? Are your initial objections satisfied or do you want to keep a request for changes review on this? |
What's the urgency on this one? I'm happy to be overridden if there is a pressing need to get this merged in the next days. Otherwise I'd like to take a detailed look in the second half of next week, if that's ok? |
I'd rather you had a detailed look. We'll wait for you. |
@tautschnig - still no urgency but a little reminder to look at this when you get time. |
My apologies for having been quiet about this. In my opinion, the following needs to happen:
#1860 partly addressed 1 and 2; I have started working on more unit tests. I'll try to get this done ASAP. Problems must be fixed by first rolling back - rolling forward is not a way to address bugs introduced in a particular change. |
That way ahead won't work - even after this PR there will be less |
Yes, I am aware of that choice, but they have different impact: one necessarily affects the customer through increased memory usage, while the other one just requires more care by developers. In my world, it's always about the customer. |
@NathanJPhillips While this proposal addresses some bugs, I am worried about the fact that these went undetected before (in particular in the performance test that I did myself). Of course you've added a nice unit test to put checks in place, but could you please explain how you noticed the problem in the first place? |
It was while writing unit tests for the mutable extension to the depth-first iterator. I needed to check that the mutate operation had broken sharing so that we were mutating a new copy but in doing so I put a sanity check up front that sharing was already in place and noticed it wasn't. |
I still find it really weird that you keep calling the intended behaviour of a PR that you approved a bug. You can say that you didn't understand the intended behaviour and that you think that behaviour is undesirable, but a bug to me implies something unintentional. #1742 intentionally broke sharing in order to make things safer. |
Yes, I did not understand the intended behaviour; the unintentional part about it is that I was not expecting that failure to understand. |
Quoting from #834: "The observable effect of this patch is that code such as the following no longer has unexpected behaviour." -- so that's what I expected to be the observable effect; it did not say that "less sharing" was among the observable effects/intended behaviour. |
1769504
to
6a529d2
Compare
A PR by @reuk some time ago prevented sharing when references are held to sub-parts but didn't re-enable sharing in situations where it is actually safe to do so. This PR amends that. This should improve performance, particularly in memory usage.
6a529d2
to
dd5ab8f
Compare
Hello. I am closing this PR as it appears stale and no longer in a viable state to be merged. If you would like this to be merged at some point in the future, feel free to reopen the PR and rebase on top of |
A PR by @reuk some time ago prevented sharing when references are held to sub-parts but didn't re-enable sharing in situations where it is actually safe to do so. This PR amends that.
This should improve performance, particularly in memory usage.