joblib.Parallel () медленнее, чем single для лыжного мага - PullRequest
1 голос
/ 04 ноября 2019

Мне нужно применить 2D-фильтр для каждого среза стопки изображений, и я хотел бы распараллелить анализ. Однако приведенный ниже код работает медленнее, чем обычный цикл for. Кроме того, увеличение n_jobs также увеличивает время обработки, которое быстрее для n_jobs = 1 и медленнее для n_jobs = 6.

import numpy as np 
from joblib import Parallel, delayed
from skimage.restoration import denoise_tv_chambolle

arr = np.random.rand(50,50,50)

def f(arr):
    arr_h = denoise_tv_chambolle(arr, weight=0.1, multichannel=True)
    return arr_h

Parallel(n_jobs=6, backend="threading")(delayed(f)(i) for i in arr)

1 Ответ

0 голосов
/ 04 ноября 2019

Q : (Почему) ... работает медленнее , чем обычный цикл for (?)

>>> import numpy as np; _ = np.random.rand( 50, 50, 50)
>>> from zmq import Stopwatch; aClk = Stopwatch()
>>> 
>>> aClk.start(); r = denoise_tv_chambolle( _, weight = 0.1, multichannel = True ); b = aClk.stop(); print( "The code took {0: > 9d}[us]".format( b ) )
The code took    679749[us]
The code took    683137[us]
The code took    678925[us]
The code took    688936[us]

Учитывая миниатюрную форму данных (50,50,50) -of- float64, вычисления в кеше являются ключом к производительности. Использование joblib.Parallel с бэкэндом 'threading' является довольно анти-паттерном (python использует GIL -блок для повторного вычисления [SERIAL] один за другим , поскольку это позволяет избежать любого общего, связанного с параллелизмом столкновения). Такой последовательный поток вычислений здесь еще хуже, потому что «переключение» один за другим идет за дополнительную плату (не улучшая оригинал чисто - [SERIAL]выполнение кода - так вы платите больше, чтобы получить то же самое (но через некоторое время))

Q : увеличение n_jobsтакже увеличьте время обработки

Конечно, это увеличивает количество потерянного времени на накладные расходы на повторное использование GIL-блокировки, так как их больше one-step-after-another GIL-направленное предотвращение столкновений " переключение " - переходы.


Последнее, но не менее важное

Даже если идти вПолноценный параллелизм, использующий параллелизм, основанный на процессах (избегает затрат на GIL-блокировку), он приходит (опять же за счет затрат - затрат на создание экземпляров процессов (полная копия памяти в 1: 1 процесса интерпретатора python n_jobs - в Win O / S, аналогично в Linux O / S - как описано в joblibмодуль, вкл. рекомендации, чтобы избежать некоторых других форм порождения параллельных процессов), параметр data-Transfer-Cost, result-Transfer-Cost).

Если добавить все эти дополнительные расходы для n_jobs = 6 и если эти затраты были начислены во имя только миниатюрной вычислительной задачи (всего лишь ~ 680 [ms] по продолжительности), то вскоре вы получите оплату намного больше настроить параллельную обработку , которую когда-либо получит обратно (поскольку другой эффект - как худшее, чем оригинальное повторное использование кэша - не "увеличит" скорость вычислений).

реальных затрат (и должный учет затрат каждого класса (всех таких)) вычислительных нагрузок - причина (почему) ... работает медленнее

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...