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

__internal::__lazy_and needs to be more lazy #46946

Closed
jwakely mannequin opened this issue Sep 21, 2020 · 3 comments
Closed

__internal::__lazy_and needs to be more lazy #46946

jwakely mannequin opened this issue Sep 21, 2020 · 3 comments
Assignees
Labels
bugzilla Issues migrated from bugzilla pstl Issues related to the C++17 Parallel STL

Comments

@jwakely
Copy link
Mannequin

jwakely mannequin commented Sep 21, 2020

Bugzilla Link 47602
Resolution FIXED
Resolved on Mar 03, 2021 08:37
Version unspecified
OS All
Fixed by commit(s) 053146a

Extended Description

In pstl/include/pstl/internal/execution_impl.h these function templates are defined:

template
std::false_type __lazy_and(_Tp, std::false_type)
{
return std::false_type{};
};

template
inline _Tp
__lazy_and(_Tp __a, std::true_type)
{
return __a;
}

(There are also __lazy_or function template which seem to be completely unused).

It gets used in code like this:

template <typename _ExecutionPolicy, typename... _IteratorTypes>
auto
__is_vectorization_preferred(_ExecutionPolicy&& __exec)
-> decltype(__internal::__lazy_and(__exec.__allow_vector(),
typename __internal::__is_random_access_iterator<_IteratorTypes...>::type()))
{
return __internal::__lazy_and(__exec.__allow_vector(),
typename __internal::__is_random_access_iterator<_IteratorTypes...>::type());
}

Where __allow_vector is a specialization of a relatively inexpensive class template, and __is_random_access_iterator is a recursive class template:

template <typename _IteratorType, typename... _OtherIteratorTypes>
struct __is_random_access_iterator
{
static constexpr bool value = __internal::__is_random_access_iterator<_IteratorType>::value &&
__internal::__is_random_access_iterator<_OtherIteratorTypes...>::value;
typedef std::integral_constant<bool, value> type;
};

template
struct __is_random_access_iterator<_IteratorType>
: std::is_same<typename std::iterator_traits<_IteratorType>::iterator_category, std::random_access_iterator_tag>
{
};

Unless I'm mistaken, the only thing that is lazy here is the instantiation of __allow_vector::value, which is cheap anyway.

In order to choose an overload of __lazy_and the second argument needs to be complete, which instantiates __is_random_access_iterator<_IteratorTypes...>::type which requires the instantiation of __is_random_access_iterator<_IteratorTypes...>::value, which is VERY UN-LAZY. It requires instantiating __is_random_access_iterator for every element of the pack, even if the first element is not a random access iterator.

A better implementation of __is_random_access_iterator would be:

template <typename _IteratorType, typename... _OtherIteratorTypes>
struct __is_random_access_iterator;

template
struct __is_random_access_iterator<_IteratorType>
: std::is_same<typename std::iterator_traits<_IteratorType>::iterator_category, std::random_access_iterator_tag>
{
};

template <typename _IteratorType, typename... _OtherIteratorTypes>
struct __is_random_access_iterator
: std::conjunction<__is_random_access_iterator<_IteratorType>,
__is_random_access_iterator<_OtherIteratorTypes>...>::type
{
};

Personally I'd get rid of __lazy_and completely, make __is_random_access_iterator non-variadic, and write the functions using it like this (assuming bug 47601 gets fixed so the __alow_vector alias template works):

template <typename _ExecutionPolicy, typename... _IteratorTypes>
typename std::conjunction<__allow_vector<_ExecutionPolicy>,
__is_random_access_iterator<_IteratorTypes>...>::type
__is_vectorization_preferred(_ExecutionPolicy&&)
{
return {};
}

This is actually lazy. It never instantiates any __is_random_access_iterator specialization if the __allow_vector trait is false, and only instantiates as many as needed to determine the answer.

(N.B. the __exec parameter is redundant, since the _ExecutionPolicy type has to be given explicitly anyway, and that already provides all the information required, but changing that would require changes to every caller).

@jwakely
Copy link
Mannequin Author

jwakely mannequin commented Sep 21, 2020

assigned to @ldionne

@ldionne
Copy link
Member

ldionne commented Mar 2, 2021

Up for review in https://reviews.llvm.org/D97808

@ldionne
Copy link
Member

ldionne commented Mar 3, 2021

commit 053146a
Author: Louis Dionne ldionne.2@gmail.com
Date: Tue Mar 2 16:53:07 2021 -0500

[pstl] Fix broken policy_traits and clean up unused code

llvm/llvm-project#46946 
llvm/llvm-project#46945 

Differential Revision: https://reviews.llvm.org/D97808

@llvmbot llvmbot transferred this issue from llvm/llvm-bugzilla-archive Dec 10, 2021
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugzilla Issues migrated from bugzilla pstl Issues related to the C++17 Parallel STL
Projects
None yet
Development

No branches or pull requests

1 participant