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

not_null<shared_ptr> overhead #550

Closed
cconverse711 opened this issue Aug 15, 2017 · 11 comments
Closed

not_null<shared_ptr> overhead #550

cconverse711 opened this issue Aug 15, 2017 · 11 comments
Assignees
Labels

Comments

@cconverse711
Copy link

What is the intended usage of not_null with std::shared_ptr? There is a significant overhead to wrapping std::shared_ptr in not_null because every access results in a costly copy of the shared_ptr. As not_null cannot be used with unique_ptr, it seems that not_null is not really usable with smart pointers in general.

@neilmacintosh neilmacintosh self-assigned this Aug 17, 2017
ericLemanissier added a commit to ericLemanissier/GSL that referenced this issue May 2, 2018
allows support of supporting std::unique_ptr
based on @xaxxon's work

fixes microsoft#89
fixes microsoft#550
fixes microsoft#651
ericLemanissier added a commit to ericLemanissier/GSL that referenced this issue May 2, 2018
allows support of supporting std::unique_ptr
based on @xaxxon's work

fixes microsoft#89
fixes microsoft#550
fixes microsoft#651
ericLemanissier added a commit to ericLemanissier/GSL that referenced this issue May 2, 2018
allows support of supporting std::unique_ptr
based on @xaxxon's work

fixes microsoft#89
fixes microsoft#550
fixes microsoft#651
ericLemanissier added a commit to ericLemanissier/GSL that referenced this issue May 2, 2018
allows support of supporting std::unique_ptr
based on @xaxxon's work

fixes microsoft#89
fixes microsoft#550
fixes microsoft#651
@janwilmans
Copy link

janwilmans commented Jul 29, 2018

@neilmacintosh can this issue #550, #415 and #89 be closed now that not_null<smartptr> works? or is the support still debated?

@asfernandes
Copy link

@neilmacintosh can this issue #550, #415 and #89 be closed now that not_null<smartptr> works? or is the support still debated?

Is the problem resolved or not?

@jwakely
Copy link
Contributor

jwakely commented Aug 13, 2020

Using shared_ptr still results in lots of unnecessary copies, so I don't think this is resolved at all.

C++17 guaranteed elision helps with some of them, e.g. not_null<shared_ptr<T>>::operator-> will be less expensive, but operator* still makes an unnecessary copy by calling get() (apparently just to avoid repeating the Expects check in operator*).

@daschuer
Copy link

daschuer commented Oct 4, 2022

The issue is that in case of std:shared_ptr get() does a deep copy, because it is is_copy_constructible

constexpr std::conditional_t<std::is_copy_constructible<T>::value, T, const T&> get() const

can we do here a std::is_pod check instead?

@hsutter
Copy link
Collaborator

hsutter commented Oct 4, 2022

Thanks for pinging this old issue, sorry for the lag.

I agree that’s reasonable, and is_pod would be fine, though technically it's deprecated as of C++20 in favor of is_trivially copyable so perhaps use that.

FWIW, personally, if I were writing the code I’d write this general type alias (name can be bikeshedded; this is copied from cppfront where I use this for "in" parameters and it's called in<T>):

template<typename T>
using value_access_t =
    std::conditional_t <
        sizeof(T) < 2*sizeof(void*) && std::is_trivially_copy_constructible_v<T>,
        T const,
        T const&
    >;

which is generally useful, and change get()’s return from

std::conditional_t<std::is_copy_constructible<T>::value, T, const T&>

to

value_access_t<T>

Note this also solves the slight incongruence that right now the return value is sometimes const and sometimes not... here it's always const.

Caveat: I haven't tried it on not_null's .get().

What do you think?

@daschuer
Copy link

daschuer commented Oct 4, 2022

What is your reason for sizeof(T) < 2*sizeof(void*) ?
Or will just std::is_pointer work?

@hsutter
Copy link
Collaborator

hsutter commented Oct 4, 2022

@daschuer Good question!

The direct answer is "generality": The decision to return by value or by const& here felt the same to me as for "in" parameter passing. So I defaulted to using the same rule I use for that, which gives the right/expected result for raw pointers and shared_ptr, and didn't see a reason to invent a special rule.

The secondary answer is that I then saw doing it that way had (actual or potential) extra benefits:

  • This way, as noted above, we got the const right for free. A minor improvement, but a nice one, and makes the use of the returned type more consistent.
  • This way, other user-defined pointerlike types that are small and trivially copyable (maybe like std::experimental::observer_ptr? (*) ) will also naturally use the cheap copy -- if they happen to be indeed cheap to copy. We'll just do the right thing.

(*) I wonder if observer_ptr might go the same route as string_view... pre-C++23 string_view was not guaranteed to be trivially copyable, in C++23 it will be guaranteed to be trivially copyable. Right now observer_ptr is only required to be copyable, but not trivially copyable, so I wonder if it too will go through a similar evolution (if it's ever added to the standard), and this formulation will just do the right thing both ways as the type or its implementations may evolve.

@daschuer
Copy link

daschuer commented Oct 5, 2022

Ah, thank you for explaination.
So will you create a PR with this to solve the issue?

@beinhaerter
Copy link
Contributor

What is your reason for sizeof(T) < 2*sizeof(void*) ?

I think Herb is following F.16: For “in” parameters, pass cheaply-copied types by value and others by reference to const.

From the Reason section:

What is “cheap to copy” depends on the machine architecture, but two or three words (doubles, pointers, references) are usually best passed by value.

From the Enforcement section:

(Simple) ((Foundation)) Warn when a parameter being passed by value has a size greater than 2 * sizeof(void*). Suggest using a reference to const instead.
(Simple) ((Foundation)) Warn when a parameter passed by reference to const has a size less than 2 * sizeof(void*). Suggest passing by value instead.

@dmitrykobets-msft
Copy link
Member

@daschuer sorry for the delay - I'll make a PR to implement Herb's suggestion and resolve this issue.

@daschuer
Copy link

daschuer commented Oct 7, 2022

Cool, thank you.

dmitrykobets-msft added a commit that referenced this issue Oct 11, 2022
Closes issue #550, which highlighted overhead in not_null::get for larger types such as shared_ptr. Every call to get would return a copy of the contained value.
This PR implements Herb's suggestion for changing the return type of not_null::get. The not_null's value will now only be copied if it is "trivially copyable"; otherwise, it will be returned by const reference.
Note: this change also forces the returned copy to be const.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
9 participants