Какие факторы определяют оптимальный аргумент chunksize
для таких методов, как multiprocessing.Pool.map()
? Метод .map()
кажется, использует произвольную эвристику для размера по умолчанию (объяснено ниже); что мотивирует этот выбор и есть ли более продуманный подход, основанный на конкретной ситуации/настройке?
Пример - скажи, что я
- Передача
iterable
в.map()
, содержащего ~ 15 миллионов элементов; - Работа на машине с 24 ядрами и использование
processes = os.cpu_count()
по умолчаниюprocesses = os.cpu_count()
вmultiprocessing.Pool()
.
Мое наивное мышление состоит в том, чтобы дать каждому из 24 работников одинаковый размер, то есть 15_000_000/24
или 625 000. Большие куски должны уменьшить текучесть кадров/накладные расходы при полном использовании всех работников. Но, похоже, что в нем отсутствуют некоторые потенциальные недостатки предоставления больших партий каждому работнику. Это неполная картина, и что мне не хватает?
Часть моего вопроса проистекает из логики по умолчанию для if chunksize=None
: оба .map()
и .starmap()
вызывают .map_async()
, который выглядит следующим образом:
def _map_async(self, func, iterable, mapper, chunksize=None, callback=None,
error_callback=None):
# ... (materialize 'iterable' to list if it an iterator)
if chunksize is None:
chunksize, extra = divmod(len(iterable), len(self._pool) * 4) # ????
if extra:
chunksize += 1
if len(iterable) == 0:
chunksize = 0
Какая логика стоит за divmod(len(iterable), len(self._pool) * 4)
? Это означает, что размер фрагмента будет ближе к 15_000_000/(24 * 4) == 156_250
. Каково намерение умножить len(self._pool)
на 4?
Это делает полученный размер фрагмента в 4 раза меньше, чем моя "наивная логика" сверху, которая состоит из простого деления длины итерируемого на количество работников в pool._pool
.
Наконец, есть еще один фрагмент из документации Python по .imap()
который еще больше .imap()
мое любопытство:
Аргумент
chunksize
аргументом, используемым методомmap()
. Для очень длинных итераций использование большого значения дляchunksize
может сделать задание намного быстрее, чем использование значения по умолчанию 1.
Соответствующий ответ, который полезен, но слишком высокоуровневый: многопроцессорность Python: почему большие куски медленнее?,