Итерация над unordered_map параллельно, без вспомогательного контейнера для ключей [duplicate]

Очень простой подход, не зависящий от версии python, отсутствовал в уже заданных ответах, которые вы можете использовать большую часть времени (по крайней мере, я):

new_list = my_list * 1       #Solution 1 when you are not using nested lists

Однако, если my_list содержит другие контейнеры ( например, вложенных списков), вы должны использовать функцию глубокой печати, как другие, предложенные в ответах выше, из библиотеки копий. Например:

import copy
new_list = copy.deepcopy(my_list)   #Solution 2 when you are using nested lists

.Bonus: Если вы не хотите копировать элементы, используйте (ака мелкой копии):

new_list = my_list[:]

Давайте понимать разницу между решением # 1 и Solution # 2

>>> a = range(5)
>>> b = a*1
>>> a,b
([0, 1, 2, 3, 4], [0, 1, 2, 3, 4])
>>> a[2] = 55 
>>> a,b
([0, 1, 55, 3, 4], [0, 1, 2, 3, 4])

Как вы можете видеть, решение № 1 отлично работало, когда мы не использовали вложенные списки. Давайте проверим, что произойдет, когда мы применим решение №1 к вложенным спискам.

>>> from copy import deepcopy
>>> a = [range(i,i+4) for i in range(3)]
>>> a
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
>>> b = a*1
>>> c = deepcopy(a)
>>> for i in (a, b, c): print i   
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
>>> a[2].append('99')
>>> for i in (a, b, c): print i   
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5, 99]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5, 99]]   #Solution#1 didn't work in nested list
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]       #Solution #2 - DeepCopy worked in nested list
2
задан Christian 25 September 2014 в 10:09
поделиться

2 ответа

Вы можете разбить цикл на диапазоны индексов ковша, а затем создать итератор с внутренним байтом для обработки элементов. unordered_map имеет .bucket_count() и специфичные для ведра значения итератора begin(bucket_number), end(bucket_number), которые позволяют это. Предполагая, что вы не изменили значение по умолчанию max_load_factor() с 1.0 и имеете разумную хэш-функцию, вы будете average 1 элемент на каждый ковш и не должны тратить слишком много времени на пустые ведра.

1
ответ дан Tony Delroy 21 August 2018 в 01:34
поделиться
  • 1
    Спасибо, это работает! Я полагаю, что основная проблема с пустыми ведрами заключается в том, что поток, имеющий дело с большим количеством пустых ведер, намного быстрее, чем другие потоки, и поэтому тратит некоторое время на простоя. Или есть другие проблемы? Хотя ваша идея работает, мне все равно интересно, почему мой подход выше не работает для unordered_maps. – Christian 25 September 2014 в 12:54
  • 2
    "... много пустых ведер намного быстрее ... & quot; - правые, кластеры пустых ведер или чрезмерно конусные ведра, но с разумным хешем, который должен все усреднить. Что касается "почему" - как вы говорите в своем вопросе, итераторы unordered_map не являются случайным доступом ... это заслуживающее доверия объяснение, так как процедуры параллелизации, вероятно, предполагают, что накладные расходы итераций являются значительными по сравнению с обработкой данных для каждого элемента данных, поэтому некоторые неизвестные количество смещения будет хотеть создавать последовательно меньшие партии, так как итерация прогрессирует так, что они заканчиваются примерно в одно и то же время. – Tony Delroy 25 September 2014 в 16:35
  • 3
    Конечно, если вы знаете, что время обработки каждого элемента является доминирующим, вы можете перебирать первые указатели на копирование элемента в вектор, а затем параллельно обрабатывать его. – Tony Delroy 25 September 2014 в 16:36

Канонический подход с контейнерами, которые не поддерживают случайные итераторы, заключается в использовании явных задач OpenMP:

std::unordered_map<size_t, double> hastTable;

#pragma omp parallel
{
   #pragma omp single
   {
      for(auto it = hastTable.begin(); it != hastTable.end(); it++) {
         #pragma omp task
         {
            //do something
         }
      }
   }
}

Это создает отдельную задачу для каждой итерации, которая приносит некоторые накладные расходы и поэтому имеет смысл только тогда, когда //do something на самом деле означает //do quite a bit of work.

5
ответ дан Hristo Iliev 21 August 2018 в 01:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: