Разница между параллельной картой и параллельным циклом for

когда я читал документ Джулии о многоядерных параллельных вычислениях, я заметил, что есть как параллельная карта pmap, так и цикл for @distributed for.

Из документации: «Julia's pmap предназначена для случая, когда каждый вызов функции выполняет большой объем работы. В отличие от этого, @distributed for может обрабатывать ситуации, когда каждая итерация крошечная».

В чем разница между pmap и @distributed for? Почему @distributed for тормозит при большом объеме работы?

Спасибо


person Tianjing Zhao    schedule 15.04.2019    source источник


Ответы (2)


Проблема в том, что pmap выполняет балансировку нагрузки, а @distributed for разбивает задания на равные части. Вы можете убедиться в этом, запустив эти два примера кода:

julia> @time res = pmap(x -> (sleep(x/10); println(x)), [10;ones(Int, 19)]);
      From worker 2:    1
      From worker 3:    1
      From worker 4:    1
      From worker 2:    1
      From worker 3:    1
      From worker 4:    1
      From worker 3:    1
      From worker 2:    1
      From worker 4:    1
      From worker 4:    1
      From worker 2:    1
      From worker 3:    1
      From worker 2:    1
      From worker 3:    1
      From worker 4:    1
      From worker 4:    1
      From worker 3:    1
      From worker 2:    1
      From worker 4:    1
      From worker 5:    10
  1.106504 seconds (173.34 k allocations: 8.711 MiB, 0.66% gc time)

julia> @time @sync @distributed for x in [10;ones(Int, 19)]
       sleep(x/10); println(x)
       end
      From worker 4:    1
      From worker 3:    1
      From worker 5:    1
      From worker 4:    1
      From worker 5:    1
      From worker 3:    1
      From worker 5:    1
      From worker 3:    1
      From worker 4:    1
      From worker 3:    1
      From worker 4:    1
      From worker 5:    1
      From worker 4:    1
      From worker 5:    1
      From worker 3:    1
      From worker 2:    10
      From worker 2:    1
      From worker 2:    1
      From worker 2:    1
      From worker 2:    1
  1.543574 seconds (184.19 k allocations: 9.013 MiB)
Task (done) @0x0000000005c5c8b0

И вы можете видеть, что большая работа (значение 10) заставляет pmap выполнять все мелкие работы на рабочих, отличных от той, которая получила большую работу (в моем примере рабочий 5 выполнял только работу 10, а рабочие с 2 по 4 выполняли все остальные работы). С другой стороны, @distributed for назначило одинаковое количество рабочих мест каждому работнику. Таким образом, рабочий, получивший работу 10 (рабочий 2 во втором примере), по-прежнему должен был выполнять четыре коротких задания (поскольку каждый рабочий в среднем должен выполнять 5 заданий — в моем примере всего 20 заданий и 4 рабочих).

Преимущество @distributed for заключается в том, что если работа недорогая, то равномерное распределение работы между работниками позволяет избежать необходимости выполнять динамическое планирование, которое также не является бесплатным.

Таким образом, как указано в документации, если задание дорогое (и особенно если время его выполнения может сильно различаться), лучше использовать pmap, так как он выполняет балансировку нагрузки.

person Bogumił Kamiński    schedule 16.04.2019

pmap имеет аргумент batch_size, который по умолчанию равен 1. Это означает, что каждый элемент коллекции будет отправлен один за другим доступным рабочим процессам или задачам, которые будут преобразованы предоставленной вами функцией. Если каждый вызов функции выполняет большой объем работы и, возможно, каждый вызов отличается по времени, которое требуется, использование pmap имеет то преимущество, что не позволяет рабочим процессам бездействовать, в то время как другие рабочие процессы выполняют работу, потому что, когда рабочий процесс завершает одно преобразование, он запрашивает следующий элемент для преобразования. Таким образом, pmap эффективно распределяет нагрузку между работниками/задачами.

Однако @distributed цикл for разделяет заданный диапазон между рабочими процессами один раз в начале, не зная, сколько времени займет каждый раздел диапазона. Рассмотрим, например, набор матриц, где первые сто элементов набора являются матрицами 2 на 2, следующие сто элементов являются матрицами 1000 на 1000, и мы хотели бы взять обратную каждую матрицу, используя @distributed for-loops и 2 рабочих процесса.

@sync @distributed for i = 1:200
    B[i] = inv(A[i])
end

Первый рабочий получит все матрицы 2 на 2, а второй получит матрицы 1000 на 1000. Первый рабочий очень быстро завершит все преобразования и будет простаивать, а другой будет продолжать выполнять работу очень долго. Несмотря на то, что вы используете 2 рабочих процесса, большая часть всей работы будет эффективно выполняться последовательно на втором рабочем потоке, и вы почти не получите никакой выгоды от использования более одного рабочего процесса. Эта проблема известна как балансировка нагрузки в контексте параллельных вычислений. Проблема также может возникнуть, например, когда один процессор работает медленно, а другой быстро, даже если работа, которую необходимо выполнить, является однородной.

Однако для очень небольших рабочих преобразований использование pmap с небольшим размером пакета создает коммуникационные издержки, которые могут быть значительными, поскольку после каждого пакета процессор должен получить следующий пакет от вызывающего процесса, тогда как с @distributed циклами for каждый рабочий процесс знать, в начале, за какую часть диапазона он отвечает.

Выбор между pmap и @distributed циклом for зависит от того, чего вы хотите достичь. Если вы собираетесь преобразовать коллекцию, как в map, и каждое преобразование требует большого объема работы, и этот объем варьируется, то вам, вероятно, лучше выбрать pmap. Если каждое преобразование очень маленькое, то вам, вероятно, лучше выбрать @distributed цикла for.

Обратите внимание, что если вам нужна операция сокращения после преобразования, @distributed цикл for уже предоставляет ее, большинство сокращений будет применяться локально, а окончательное сокращение будет выполняться в вызывающем процессе. Однако с pmap вам придется самостоятельно обрабатывать сокращение.

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

https://docs.julialang.org/en/v1/manual/parallel-computing/

person hckr    schedule 16.04.2019