【问题标题】:Call a member function of a vector of elements with a vector of arguments in parallel使用参数向量并行调用元素向量的成员函数
【发布时间】:2020-06-09 06:19:48
【问题描述】:

鉴于这段代码:

struct T
{
  void f(int const);
};

void f(std::vector<T> &u, std::vector<int> const &v)
{
  for (std::size_t i = 0; i < u.size(); ++i)
    u[i].f(v[i]);
}

有没有标准的方法来并行化void f(std::vector&lt;T&gt; &amp;u, std::vector&lt;int&gt; const &amp;v) 的主体?

这碰巧奏效了 (https://godbolt.org/z/gRv9Ze):

void f(std::vector<T> &u, std::vector<int> const &v)
{
  auto const indices = std::views::iota(0u, u.size()) | std::views::common;

  std::for_each(std::execution::par_unseq, std::begin(indices), std::end(indices),
                [&](std::size_t const i) { u[i].f(v[i]); });
}

但据报道,依赖这种行为是错误的(参见bug report 还有这个answer)。实际上,这不是并行运行的 (https://godbolt.org/z/MPGdHF):

void f(std::vector<T> &u, std::vector<int> const &v)
{
  std::ranges::iota_view<std::size_t, std::size_t> const indices(0u, u.size());

  std::for_each(std::execution::par_unseq, std::begin(indices), std::end(indices),
                [&](std::size_t const i) { u[i].f(v[i]); });
}

我很确定应该有一种标准方法可以让类似的函数并行运行。我可能错过了一个明显的 算法,但是std::transform在这里似乎不合适,其他的更不合适。

【问题讨论】:

  • 第一个示例“这碰巧起作用”--> 根据cppreference:“如果实现无法并行化或矢量化(例如,由于缺乏资源),所有标准执行策略都可能失效回到顺序执行。”第二个示例“确实,这不能并行运行”-> 如您链接的错误报告中一样,将 iota_view 模板参数更改为有符号整数类型以满足要求就可以了。
  • @gkhaos 我知道我可以让它在 GCC 中工作。问题是iota_view::iterator 没有对Cpp17ForwardIterator 建模,因此实现静默地回退到顺序版本可能是完美的。事实上,例如,MSVC 团队声称在他们的实现中没有尝试支持此类迭代器:github.com/oneapi-src/oneDPL/issues/22#issuecomment-442180158

标签: c++ c++17 c++20 stl-algorithm


【解决方案1】:

留在std 内,最好的选择是std::transform,其输出迭代器会忽略给予它的内容

struct unit_iterator {
    using difference_type = std::ptrdiff_t;
    using value_type = std::tuple<>;
    using pointer = std::tuple<> *;
    using const_pointer = const std::tuple<> *;
    using reference = std::tuple<> &;
    using const reference = const std::tuple<> &;
    using iterator_category = std::random_access_iterator_tag;

    reference operator*() { return value; }
    const_reference operator*() const { return value; }
    reference operator[](difference_type) { return value; }
    const_reference operator[](difference_type) const { return value; }
    pointer operator->() { return &value; }
    const_pointer operator->() const { return &value; }

    unit_iterator& operator++() { return *this; }
    unit_iterator operator++(int) { return *this; }
    unit_iterator& operator+=(difference_type) { return *this; }
    unit_iterator operator+(difference_type) const { return *this; }

    unit_iterator& operator--() { return *this; }
    unit_iterator operator--(int) { return *this; }
    unit_iterator& operator-=(difference_type) { return *this; }
    unit_iterator operator-(difference_type) const { return *this; }

    difference_type operator-(unit_iterator) const { return 0; }

    bool operator==(unit_iterator) const { return true; }
    bool operator!=(unit_iterator) const { return false; }
    bool operator<(unit_iterator) const { return false; }
    bool operator<=(unit_iterator) const { return true; }
    bool operator>(unit_iterator) const { return false; }
    bool operator>=(unit_iterator) const { return true; }
private:
    static value_type value;
};


void f(std::vector<T> &u, std::vector<int> const &v)
{
  std::transform(std::execution::par_unseq, begin(u), end(u), begin(v), unit_iterator{},
                 [](T & u, int v) { u.f(v); return std::tuple<>{}; });
}

【讨论】:

  • 非常感谢您的回答。这是一个证明它有效的版本(在修复了一些错别字之后):godbolt.org/z/C9mqSM。但是,我看不到 iota_view&lt;int, int&gt;::iterator 的改进。 unit_iterator 不是和 iota_view&lt;int, int&gt;::iterator 一样隐藏吗? vector&lt;bool&gt;::iterator 声称是随机访问,但它在许多标准算法中都失败了。 unit_iterator 不受比利奥尼尔在此评论中提出的担忧的影响:github.com/oneapi-src/oneDPL/issues/22#issuecomment-442180158
  • @PilarLatiesa 我认为这是可以解决的。您只需要一个 value 用于所有 unit_iterators
  • 好点!但我认为应该将其声明为thread_local 以防止出现竞争条件,不是吗?
  • 是的,我不确定分配一个空元组是否“写入内存位置”。明确忽略其赋值参数的空结构肯定是安全的。
  • 我也不知道...无论如何我认为注释它thread_local 没有害处。总的来说,我真的很喜欢这个主意。附注:operator-(difference_type)operator+(difference_type) 必须是 const,并且 lamda 不能采用 int &amp;,而是 int const &amp;auto &amp; 或简单的 int
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-23
  • 1970-01-01
  • 1970-01-01
  • 2011-08-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多