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

<functional> Avoid layers of forwards in invoke #585

Merged
merged 17 commits into from
Mar 5, 2020

Conversation

BillyONeal
Copy link
Member

Description

This change removes the last "nonessential" stack frame that would be encountered for threads, and makes the debugging experience for several standard components, like std::function, nicer by not needing so many step into / step out of sequences.

image

  1. CRT's thread entry point
  2. std::thread's entry point that staples the parameters on
  3. invoke
  4. already user code yay!

Hopefully this makes debug codegen better too, particularly now that ranges hammers invoke even harder than we used to.

I didn't change any of the metaprogramming for deciding which strategy to use -- I like that we don't use SFINAE to make that decision, and don't really consider myself competent enough a metaprogrammer to confidently make changes there. I just make _Invoker_xyz also supply a strategy value that is fed into if constexpr. @CaseyCarter suggested some larger changes which might have sped up metaprogramming I tried, but ran into issues because one can't deduce the calling convention of pointers-to-member-function.

I've also made a one time exception to our usual policy of using std::forward rather than static_cast, with the rationale that invoke is hammered everywhere, and also by traits, and we want the debugging experience of that to be as nice as possible.

(Also drive-by removed unnecessary compilation of iostreams from the Dev10_729003_bind_reference_wrapper I noticed debugging a test case failure)

Checklist

Be sure you've read README.md and understand the scope of this repo.

If you're unsure about a box, leave it unchecked. A maintainer will help you.

  • Identifiers in product code changes are properly _Ugly as per
    https://eel.is/c++draft/lex.name#3.1 or there are no product code changes.
  • The STL builds successfully and all tests have passed (must be manually
    verified by an STL maintainer before automated testing is enabled on GitHub,
    leave this unchecked for initial submission).
  • These changes introduce no known ABI breaks (adding members, renaming
    members, adding virtual functions, changing whether a type is an aggregate
    or trivially copyable, etc.).
  • These changes were written from scratch using only this repository,
    the C++ Working Draft (including any cited standards), other WG21 papers
    (excluding reference implementations outside of proposed standard wording),
    and LWG issues as reference material. If they were derived from a project
    that's already listed in NOTICE.txt, that's fine, but please mention it.
    If they were derived from any other project (including Boost and libc++,
    which are not yet listed in NOTICE.txt), you must mention it here,
    so we can determine whether the license is compatible and what else needs
    to be done.

@BillyONeal BillyONeal requested a review from a team as a code owner March 4, 2020 08:52
@BillyONeal BillyONeal self-assigned this Mar 4, 2020
stl/inc/type_traits Outdated Show resolved Hide resolved
stl/inc/type_traits Outdated Show resolved Hide resolved
stl/inc/type_traits Outdated Show resolved Hide resolved
stl/inc/type_traits Show resolved Hide resolved
Copy link
Contributor

@CaseyCarter CaseyCarter left a comment

Choose a reason for hiding this comment

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

Some little tweaky things - and thanks for the investigation with my tiny invoke implementation.

stl/inc/type_traits Outdated Show resolved Hide resolved
stl/inc/type_traits Outdated Show resolved Hide resolved
stl/inc/type_traits Outdated Show resolved Hide resolved
stl/inc/type_traits Show resolved Hide resolved
stl/inc/type_traits Outdated Show resolved Hide resolved
stl/inc/xatomic.h Show resolved Hide resolved
stl/inc/type_traits Outdated Show resolved Hide resolved
return _Invoker1<_Callable, _Ty1>::_Call(
static_cast<_Callable&&>(_Obj), static_cast<_Ty1&&>(_Arg1), static_cast<_Types2&&>(_Args2)...);
#endif // _HAS_IF_CONSTEXPR
}

#if _HAS_CXX17
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we consider #define _C_invoke invoke in C++20 mode to potentially reduce total instantiations when a program calls both invoke(args...) and _C_invoke(args...)? Or:

#if _HAS_CXX20
#define _C_INVOKE invoke
#else
#define _C_INVOKE _C_invoke
// define _C_invoke here
#endif

and replace _C_invoke with _C_INVOKE at the callsites?

Copy link
Member

Choose a reason for hiding this comment

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

I'm inclined to think that we should implement #51 unconditionally and just have invoke be constexpr. WG21-P0859 "Core Issue 1581: When are constexpr member functions defined?" was voted in as a Defect Report in Albuquerque, which means that it applies retroactively to C++14 (so we can report any issues as compiler bugs). As for the library side, our general policy is to guard features for Standard modes, unless the feature is non-disruptive and guarding it would increase complexity significantly. So far, our experience with constexpr has been that while it can trigger compiler bugs, it almost always does so when the user is attempting to use it in a constant expression, not ordinary calls (that's why we were able to constexprize so much of the STL without the world exploding). Additionally, making a function constexpr is non-disruptive to people who aren't expecting it - it introduces no new identifiers, doesn't change lookup or throughput, etc. (The Library's prohibition on constexpr strengthening does not make sense anymore.) At most, people might take a dependency on the feature and then notice that other implementations do guard it - but that seems like a minor risk (entirely at compile time).

The throughput cost of having two invoke implementations is nonzero, and the complexity cost (horrible macro) is significant, so I think it should just be unconditionally constexpr.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto "I shouldn't be trying to trick you into implementing #51, nevermind for now."

Copy link
Member Author

Choose a reason for hiding this comment

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

Reactivating: I'm going to do just the std::invoke itself only part of #51 unconditionally.

Copy link
Member Author

Choose a reason for hiding this comment

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

Implementing that makes this change depend on #565 since you want to land that out-of-band in 16.6, so I'll hold off merging this until you've done that out of cycle stuff.

Copy link
Member Author

Choose a reason for hiding this comment

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

s/unconditionally/in C++17/g because our rationale is "apply" and friends added in 17

@StephanTLavavej StephanTLavavej added the enhancement Something can be improved label Mar 4, 2020
stl/inc/type_traits Outdated Show resolved Hide resolved
stl/inc/type_traits Outdated Show resolved Hide resolved
stl/inc/type_traits Outdated Show resolved Hide resolved
stl/inc/type_traits Show resolved Hide resolved
stl/inc/type_traits Show resolved Hide resolved
stl/inc/type_traits Outdated Show resolved Hide resolved
stl/inc/type_traits Outdated Show resolved Hide resolved
stl/inc/type_traits Outdated Show resolved Hide resolved
stl/inc/type_traits Outdated Show resolved Hide resolved
* de-std::
* _C_invoke => _STD invoke
* Note that the LWG issue part of P1065R2 is implemented under '17.
* Remove decay of _Pmf/_Pmd
* PMD noexcept
* Remove forwarding of reference_wrapper.
* Strengthen noexcept and punch through reference_wrapper.
Copy link
Member

@StephanTLavavej StephanTLavavej 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, two comment changes requested.

stl/inc/yvals_core.h Show resolved Hide resolved
public:
template <class... _Types>
auto operator()(_Types&&... _Args) const
noexcept(noexcept(_STD invoke(*_Ptr, static_cast<_Types&&>(_Args)...))) // Strengthened
Copy link
Member

Choose a reason for hiding this comment

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

We conventionally say strengthened in lowercase.

@@ -1032,21 +1060,76 @@ void test_reference_wrapper_invocation() {


// Test C++17 invoke().
#if _HAS_CXX17
constexpr bool test_invoke_constexpr() {
// MSVC++ implements LWG-2894 as a DR back to C++17
Copy link
Member

Choose a reason for hiding this comment

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

We usually refer to ourselves as MSVC without ++.

Copy link
Member Author

Choose a reason for hiding this comment

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

You don't work for "MSVD"?

@BillyONeal BillyONeal merged commit 0d75fc5 into microsoft:master Mar 5, 2020
@BillyONeal BillyONeal deleted the fast_invoke branch March 5, 2020 23:36
StephanTLavavej added a commit to lozinska/STL that referenced this pull request Mar 26, 2020
@StephanTLavavej
Copy link
Member

This resolved Microsoft-internal VSO-231629 "<functional>: Investigate reducing the number of helper functions instantiated by std::function".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Something can be improved
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants