Параллельно для l oop в диапазоне индексов массива в C ++ 17 - PullRequest
3 голосов
/ 10 июля 2020

Мне нужно обновить массив из 100 миллионов элементов, и я хочу сделать это параллельно. std::for_each(std::execution::par, ...) отлично подходит для этого, за исключением того, что обновление требует доступа к элементам других массивов в зависимости от индекса, который я обновляю. Минимальный серийный рабочий пример того, что я пытаюсь распараллелить, может выглядеть так:

for (size_t i = 0; i < 100'000'000; i++)
    d[i] = combine(d[i], s[2*i], s[2*i+1]);

Конечно, я мог бы вручную создавать потоки, но это намного больше code, чем std::for_each, поэтому было бы здорово найти элегантный способ сделать это с помощью стандартной библиотеки. Пока что я нашел несколько не очень элегантных способов использования for_each, например:

  • Вычислить индекс, используя арифметику указателя c в адресе элемента массива.

  • Реализовать мой собственный фиктивный итератор в духе counting_range.

Есть ли лучший способ сделать это?

Ответы [ 2 ]

3 голосов
/ 10 июля 2020

std::ranges может помочь, если у вас есть доступ к c ++ 20, вы можете перебирать индексы, а не свои данные:

#include <ranges>
#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> d(100);
    std::ranges::iota_view indexes((size_t)0, d.size());
    std::for_each(indexes.begin(), indexes.end(), [&d](size_t i)
    {
        std::cout << i << "," << d[i] << "\n";
    });
    return 0;
}
2 голосов
/ 10 июля 2020

Вы должны иметь возможность перебирать индексы , а не элементы . Я думаю, что C ++ 20 std::ranges дает вам простой способ сделать это, или вы можете использовать один из методов Boost range. Я не уверен, почему вы могли бы подумать о том, чтобы использовать дух Boost counting_range, когда вы могли бы просто использовать Boost: -)

Сказав это, я фактически выбрал этот индивидуальный подход, просто чтобы сделать код самодостаточным без C ++ 20 и Boost: не стесняйтесь заменять paxrange одним из других методов в зависимости от ваши потребности:

#include <iostream>
#include <algorithm>

// Seriously, just use Boost :-)

class paxrange {
    public:
        class iterator {
            friend class paxrange;
            public:
                long int operator *() const { return value; }
                const iterator &operator ++() { ++value; return *this; }
                iterator operator ++(int) { iterator copy(*this); ++value; return copy; }

                bool operator ==(const iterator &other) const { return value == other.value; }
                bool operator !=(const iterator &other) const { return value != other.value; }

            protected:
                iterator(long int start) : value (start) { }

            private:
                unsigned long value;
        };

        iterator begin() const { return beginVal; }
        iterator end() const { return endVal; }
        paxrange(long int  begin, long int end) : beginVal(begin), endVal(end) {}
    private:
        iterator beginVal;
        iterator endVal;
};
int main() {
    // Create a source and destination collection.

    std::vector<int> s;
    s.push_back(42); s.push_back(77); s.push_back(144);
    s.push_back(12); s.push_back(6);
    std::vector<int> d(5);

    // Shows how to use indexes with multiple collections sharing index.

    auto process = [s, &d](const int idx) { d[idx] = s[idx] + idx; };
    paxrange x(0, d.size());
    std::for_each(x.begin(), x.end(), process); // add parallelism later.

    // Debug output.

    for (const auto &item: s) std::cout << "< " << item << '\n';
    std::cout << "=====\n";
    for (const auto &item: d) std::cout << "> " << item << '\n';
}

«Основой» решения являются три строки в середине main(), где вы настраиваете функцию для обратных вызовов, которая принимает индекс, а не сам элемент.

Внутри этой функции вы используете этот индекс плюс столько коллекций, сколько необходимо, чтобы настроить целевую коллекцию, очень похожую на то, что вы хотите.

В моем случае я просто хотел, чтобы выходной вектор был входным вектором, но с индексом, добавленным к каждому элементу, в соответствии с выходом:

< 42
< 77
< 144
< 12
< 6
=====
> 42
> 78
> 146
> 15
> 10
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...