【问题标题】:How to *write* a C++17 algorithm with a parallel execution policy如何*编写*具有并行执行策略的 C++17 算法
【发布时间】:2019-09-01 19:04:08
【问题描述】:

我想写一个 C++17 并行执行算法,但是遇到了一些麻烦。让我们从代码开始:

#if __has_include(<execution>)
#include <execution>
#include <thread>
#include <future>
#endif

template<class RandomAccessIterator>
inline auto mean(RandomAccessIterator first, RandomAccessIterator last)
{
    auto it = first;
    auto mu = *first;
    decltype(mu) i = 2;
    while(++it != last)
    {
        mu += (*it - mu)/i;
        i += 1;
    }
    return mu;
}


#if __has_include(<execution>)
template<class ExecutionPolicy, class RandomAccessIterator>
inline auto mean(ExecutionPolicy&& exec_pol, RandomAccessIterator first, RandomAccessIterator last) {
    using Real = typename std::iterator_traits<RandomAccessIterator>::value_type;
    //static_assert(std::is_execution_policy_v<ExecutionPolicy>, "First argument must be an execution policy.");
    if (exec_pol == std::execution::par) {
        size_t elems = std::distance(first, last);
        if (elems*sizeof(Real) < /*guestimate*/ 4096) {
            return mean(first, last);
        }

        unsigned threads = std::thread::hardware_concurrency();
        if (threads == 0) {
            threads = 2;
        }
        std::vector<std::future<Real>> futures;
        size_t elems_per_thread = elems/threads;
        auto it = first;
        for (unsigned i = 0; i < threads -1; ++i) {

            futures.push_back(std::async(std::launch::async, &mean<RandomAccessIterator>, it, it + elems_per_thread));
            it += elems_per_thread;
        }
        futures.push_back(std::async(std::launch::async, &mean<RandomAccessIterator>, it, last));

        Real mu = 0;
        for (auto fut : futures) {
            mu += fut.get();
        }
        mu /= threads;
        return mu;
    }
    else { // should have else-if for various types of execution policies, but let's save that for later.
         return mean(first, last);
    }
}
#endif

好的,所以问题:

  • 我首先通过const &amp; 传递ExecutionPolicy 参数。 static_assert 通过了,但后来我在if (exec_pol == std::execution::par) 上挂了一个编译错误,即:
 error: no match for ‘operator==’ (operand types are ‘const __pstl::execution::v1::parallel_policy’ and ‘const __pstl::execution::v1::parallel_policy’)
  117 |     if (exec_pol == std::execution::par) {
      |         ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~

然后我查看了/usr/include/c++/9/pstl/algorithm_impl.h,在其中,他们通过移动和转发ExecutionPolicy 并将其转发到各个地方,所以我想我应该这样做。但这并没有解决任何问题,所以我查看了/usr/include/c++/9/pstl/parallel_backend_tbb.h。在那个文件中,他们甚至不检查并行执行策略是什么!例如,上述文件中的几行:

//! Evaluation of brick f[i,j) for each subrange [i,j) of [first,last)
// wrapper over tbb::parallel_for
template <class _ExecutionPolicy, class _Index, class _Fp>
void
__parallel_for(_ExecutionPolicy&&, _Index __first, _Index __last, _Fp __f)
{
    tbb::this_task_arena::isolate([=]() {
        tbb::parallel_for(tbb::blocked_range<_Index>(__first, __last), __parallel_for_body<_Index, _Fp>(__f));
    });
}

那么我是否从根本上误解了如何使用 C++17 并行执行策略编写并行算法?如果没有,如何检查执行策略并正确使用?

【问题讨论】:

  • 是你真正想要的意思吗?然后这一行:auto mean = std::reduce(std::execution::par, v.begin(), v.end()) / v.size(); 会做你想做的事。将标准算法用于各种执行策略可以并且将使您的生活更轻松。
  • @MichaëlRoy:我根本不在乎刻薄。这只是一个简单的例子供大家理解。

标签: parallel-processing c++17 tbb


【解决方案1】:

您需要检查政策的类型,可能使用类似

if constexpr(std::is_same_v
               <std::remove_reference_t<ExecutionPolicy>,
                std::execution::parallel_policy>)

【讨论】:

  • 等等,那为什么它作为函数参数而不是模板参数传递呢?
  • (显然还有一个模板参数。)普通参数允许有状态的策略(但标准的三个不允许)。
  • 好的,所以我想随着时间的推移会添加更多的功能。 . .,比如说C++20/C++23?
  • 这正是“标签调度”模式。您可以通过超载来限制策略。
  • @DavisHerring:刚刚尝试了您的解决方案。使用 g++-9,std::execution::parstd::execution::par_unseqstd::execution::seq 都不会导致 std::is_same_v&lt;&gt; 评估为真。
【解决方案2】:

按值取ExecutionPolicy&amp;&amp; exec_polExecutionPolicy exec_pol。它是一个标签。通过转发引用获取只会混淆事物。

类型测试或标签调度:

if constexpr(std::is_same_v<ExecutionPolicy,
                        std::execution::parallel_policy>)

正如@Davis 的回答所暗示的那样。

如果您不想按值取值(并且应该按值取值),则可以使用std::decay_tstd::remove_ref_t&lt; std::remove_cv_t&lt; ExecutionPolicy &gt; &gt; 剥离完美转发存储在类型中的 cv/ref。

但同样,不要那样做。

【讨论】:

    猜你喜欢
    • 2019-04-06
    • 2017-07-22
    • 1970-01-01
    • 2021-03-04
    • 1970-01-01
    • 2021-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多