-
Notifications
You must be signed in to change notification settings - Fork 273
Use small intrusive pointer in irep #786
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
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.
A few questions; I overall like this approach, but what we really need for this to move forward is a solid performance testing set up that has long been talked about.
src/util/irep.h
Outdated
{ | ||
std::swap(irep.data, data); | ||
using std::swap; | ||
swap(data, rhs.data); |
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.
What is the rationale for this change?
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.
See following comment.
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.
Same here: keep it simple. We don't need to solve problems we don't have.
src/util/small_shared_ptr.h
Outdated
void swap(small_shared_ptrt &rhs) | ||
{ | ||
using std::swap; | ||
swap(t_, rhs.t_); |
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.
What's the idea of the separate using
directive, instead of just doing std::swap(t_, rhs.t_);
?
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 the general case, using std::swap
allows you to place a custom swap
for your type in a namespace other than std
, because manually overloading std::swap
isn't allowed. This overload can be found by Koenig lookup, but if not found will fall back to std::swap
. In this case, this behaviour isn't strictly required, so I could just use std::swap
directly.
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.
Keep it simple.
} | ||
|
||
template <typename T, typename... Ts> | ||
small_shared_ptrt<T> make_small_shared_ptr(Ts &&... ts) |
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.
What is the use case of this one?
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.
Like std::make_unique
and std::make_shared
, this has exception-safety benefits.
In the expression my_func(small_shared_ptr<X>(new X), small_shared_ptr<Y>(new Y))
, the compiler is allowed to sequence the operations like so:
new X
new Y <-- This may throw and leak previously-allocated X
construct small_shared_ptr<X>
construct small_shared_ptr<Y>
my_func
Whereas my_func(make_small_shared_ptr<X>(), make_small_shared_ptr<Y>())
cannot leak because the calls to make_small_shared_ptr
won't be interleaved.
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.
Shouldn't that be my_func(make_small_shared_ptr<X, Y>())
?
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.
No, empty parameter packs are valid. You can see at irep.cpp:30
and irep.h:242
how the function is used in practice.
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.
Ok, I think I just misunderstood: your argument was about the order of invoking new
vs the construction of small_shared_ptr
, and not the order of the small_shared_ptr
constructs (which is still unspecified). Fair enough. You may wish to add a comment to the constructor of small_shared_ptr
to say that make_small_shared_ptr
should be used in case of a new T
argument.
What would be a use case where the parameter pack is non-empty? Is this just to support types other than dt
that require >= 1 argument in their constructor?
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.
Yes, if you have a type with a constructor like my_type(Foo, Bar, Baz)
then you can create a small_shared_ptr
to it using make_small_shared_ptr<my_type>(Foo(), Bar(), Baz())
(for example).
This also avoids repetition of the identifier my_type
(the alternative would be small_shared_ptr<my_type>(new my_type(Foo(), Bar(), Baz())
which is needlessly repetitive).
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.
Ok, thanks!
As said before, this looks good to me, and includes quite a bit of long overdue cleanup. Yet I don't dare setting any approval here in absence of a performance test suite. |
With 22170b7 my looks-good-to-me no longer applies. |
Yes, sorry about that - I think the copy-on-write implementation currently in irep is buggy because it allows you to make changes which affect copies in unexpected ways:
I'm guessing this is why
I'll close the PR and re-open when I have something that fixes this bug. |
I don't necessarily think you have to go as far as closing this one. How about making |
Follows up #685. Rather than using
std::shared_ptr
which uses more memory than is strictly necessary (an extra pointer to the control block in the shared_ptr itself, plus the control block memory) we use a customsmall_shared_ptr
which is based on boost'sintrusive_ptr
.This approach uses exactly the same amount of memory as was used previously, but separating the resource management into a separate class makes the logic a bit clearer, and greatly simplifies the copy- and move-assignment operators. It also means we don't need to explicitly redeclare custom move/copy constructors/assignment-ops in
irep.h
.