#c #multithreading
#c #Многопоточность
Вопрос:
Я новичок в c , и я пытаюсь повторить результат equal_range(key)
применения к multimap, который является итератором. Прямо сейчас я могу выполнить итерацию по результату, используя цикл for result.first
и result.second
значения and, как в этом примере:
#include <iostream>
#include <map>
int main ()
{
std::multimap<char,int> mymm;
mymm.insert(std::pair<char,int>('a',10));
mymm.insert(std::pair<char,int>('b',20));
mymm.insert(std::pair<char,int>('b',30));
mymm.insert(std::pair<char,int>('b',40));
mymm.insert(std::pair<char,int>('c',50));
mymm.insert(std::pair<char,int>('c',60));
mymm.insert(std::pair<char,int>('d',60));
auto ret = mymm.equal_range('b');
for (auto it=ret.first; it!=ret.second; it)
std::cout << ' ' << it->second;
}
В моем реальном случае мне нужно выполнять итерации быстрее (более тысячи строк за короткий промежуток времени), поэтому я пытаюсь выполнять итерации с использованием нескольких потоков. Однако я не могу этого сделать, потому что моя идея заключалась в том, чтобы разделить итератор на разные части, а затем выполнить итерацию по каждой части в разных потоках. Но это невозможно, потому что я не могу сделать что-то вроде it = it 10
запуска итерации по разным позициям.
Поэтому, по сути, мне нужно разделить диапазон итераторов неслучайного доступа на несколько меньших диапазонов. Есть ли какой-нибудь безопасный способ сделать это?
Комментарии:
1. Почему вам нужно выполнять итерации быстрее? Сколько совпадающих ключей вы ожидаете? В этом небольшом примере стоимость развертывания новых потоков значительно перевесит любые потенциальные выгоды.
2. Я думаю, что лучший способ задать вопрос — «Разделить диапазон итераторов неслучайного доступа на несколько меньших диапазонов» или что-то в этом роде. Вопрос по своей сути не связан с многопоточностью.
3. @0x5453 это всего лишь пример. Мне нужно перебрать тысячи ключей. Я обновил вопрос.
4. Помните, что если у вас есть несколько меньших диапазонов, вам все равно нужно быть осторожным при синхронизации результатов ваших отдельных потоков.
Ответ №1:
Как было указано в комментарии, ваш вопрос сводится к тому, «Как разделить диапазон итераторов неслучайного доступа на несколько меньших диапазонов». Как только вы это получите, вы можете передать эти меньшие диапазоны нескольким потокам.
Вы не можете it 10
, когда it
не является итератором произвольного доступа. Однако вы можете использовать std::advance
для продвижения итератора.
Чтобы проиллюстрировать это:
#include <iostream>
#include <map>
#include <vector>
template <typename IT>
std::vector<IT> split_range(IT begin, IT end,size_t chunksize){
std::vector<IT> resu<
auto it = begin;
result.push_back(it);
size_t total = 0;
size_t size = std::distance(begin,end);
while (total chunksize < size){
std::advance(it,chunksize);
result.push_back(it);
total = chunksize;
}
if (it != end) result.push_back(end);
return resu<
}
int main ()
{
std::multimap<char,int> mymm;
mymm.insert(std::pair<char,int>('a',10));
mymm.insert(std::pair<char,int>('b',20));
mymm.insert(std::pair<char,int>('b',30));
mymm.insert(std::pair<char,int>('b',40));
mymm.insert(std::pair<char,int>('c',50));
mymm.insert(std::pair<char,int>('c',60));
mymm.insert(std::pair<char,int>('d',60));
auto ret = mymm.equal_range('b');
auto splitted = split_range(ret.first,ret.second,1);
for (auto it = splitted.begin(); std::next(it) != splitted.end(); it){
std::cout << "printing one chunkn";
for (auto it2 = *it; it2 != *std::next(it); it2){
std::cout << ' ' << it2->second << "n";
}
}
}
split_range
вероятно, не самая эффективная. std::advance
имеет свою цену, вам нужно выполнить итерацию всего диапазона один раз в одном потоке. А также std::distance
не является бесплатной для итераторов с неслучайным доступом (в основном требуется повторение всего диапазона во второй раз). Хотя, как только вы разделили диапазон, вы можете использовать несколько потоков для параллельной обработки поддиапазонов.
Комментарии:
1. Важно отметить, что одному потоку все равно придется выполнять итерации по всему диапазону, поскольку
std::advance
он имеет линейную сложность. Просто работа, которую должен выполнять поток, предположительно намного меньше, чем то, что на самом деле должно быть сделано для каждого элемента.2. @FrancoisAndrieux юп, только что добавил примечание. Я предположил, что OP знает о накладных расходах, и рабочая нагрузка достаточна для оправдания начальных накладных расходов
Ответ №2:
Один из способов распараллелить работу — использовать <execution>
политики, добавленные в C 17.
Используется с std::for_each
ним может выглядеть следующим образом:
#include <algorithm>
#include <execution>
#include <iostream>
#include <map>
int main() {
std::multimap<char, int> mymm;
mymm.insert(std::pair<char, int>('a', 10));
mymm.insert(std::pair<char, int>('b', 20));
mymm.insert(std::pair<char, int>('b', 30));
mymm.insert(std::pair<char, int>('b', 40));
mymm.insert(std::pair<char, int>('c', 50));
mymm.insert(std::pair<char, int>('c', 60));
mymm.insert(std::pair<char, int>('d', 60));
auto [begin, end] = mymm.equal_range('b');
std::for_each(std::execution::par, begin, end,
[](const autoamp; p)
{
std::cout << ' ' << p.second;
}
);
}