Краткий ответ
Алгоритм пула chunksize - это эвристический алгоритм.Он предоставляет простое решение для всех возможных проблемных сценариев, которые вы пытаетесь внедрить в методы пула.Как следствие, он не может быть оптимизирован для любого конкретного сценария.
Алгоритм произвольно делит итерируемое примерно на четыре раза больше фрагментов, чем наивный подход.Чем больше блоков, тем больше накладных расходов, но повышается гибкость планирования.Как этот ответ покажет, это приводит к более высокой загрузке работника в среднем, но без гарантии более короткого общего времени вычислений для каждого случая.
«Приятно знать, - подумаете вы, - но как знание этого помогает мне в моих конкретных проблемах с многопроцессорностью?»Ну, это не так.Более честный короткий ответ: «короткого ответа нет», «многопроцессорная обработка сложна» и «это зависит».Наблюдаемый симптом может иметь разные корни даже для сходных сценариев.
Этот ответ пытается дать вам основные понятия, помогающие вам получить более четкое представление о планируемом черном ящике пула.Он также пытается дать вам некоторые базовые инструменты для распознавания и предотвращения потенциальных обрывов, поскольку они связаны с размером фрагмента.
Содержание
ЧастьI
- Определения
- Цели распараллеливания
- Сценарии распараллеливания
- Риски Chunksize> 1
- Алгоритм размера пула
Количественная эффективность алгоритма
6.1 Модели
6.2 Параллельное расписание
6.3 Эффективности
6.3.1 Абсолютная эффективность распределения (ADE)
6.3.2 Относительная эффективность распределения (RDE)
Часть II
Наивный и Алгоритм Chunksize-пула Проверка реальности Заключение
Сначала необходимо уточнить некоторые важные термины.
1.Определения
Чанк
Чанк здесь является частью iterable
-аргумента, указанного в вызове метода пула.Как вычисляется размер фрагмента и какие последствия это может иметь, - тема этого ответа.
Задача
Физическое представление задачи в рабочем процессес точки зрения данных можно увидеть на рисунке ниже.
![figure0](https://i.stack.imgur.com/7nT9z.png)
На рисунке показан пример вызова pool.map()
, отображаемого вдоль строки кода, взятой из функции multiprocessing.pool.worker
, где заданиепрочитанное с inqueue
распаковывается.worker
- основная функция main в MainThread
пула-рабочего процесса.Аргумент func
, указанный в методе пула, будет совпадать только с переменной func
внутри функции worker
для методов с одним вызовом, таких как apply_async
и для imap
с chunksize=1
.Для остальных методов пула с параметром chunksize
функция обработки func
будет функцией отображения (mapstar
или starmapstar
).Эта функция отображает указанный пользователем func
-параметр для каждого элемента передаваемого фрагмента итерируемого (-> "map-tasks").Время, которое требуется, определяет задачу также как единицу работы .
Taskel
В то время как использование слова "задача" для обработки целого одного фрагмента соответствует коду внутри multiprocessing.pool
, нет указания, как следует ссылаться на одиночный вызов на указанный пользователем func
, с одним элементом блока в качестве аргумента (ов).Чтобы избежать путаницы, возникающей из-за конфликтов имен (подумайте о maxtasksperchild
-параметре для __init__
-метода пула), этот ответ будет относиться к отдельным единицам работы внутри задачи как taskel .
A taskel (из task + el ement) - это наименьшая единица работы в task .Это однократное выполнение функции, указанной с помощью func
-параметра Pool
-метода, вызываемой с аргументами, полученными из одного элемента переданного чанка .Задача состоит из chunksize
Taskels .
Затраты на параллелизацию (PO)
PO состоит из внутренних издержек Python и служебных данных для межпроцессного взаимодействия (IPC).Служебная нагрузка для каждой задачи в Python поставляется с кодом, необходимым для упаковки и распаковки задач и их результатов.Служебная нагрузка IPC сопровождается необходимой синхронизацией потоков и копированием данных между различными адресными пространствами (требуется два шага копирования: parent -> queue -> child).Объем накладных расходов IPC зависит от операционной системы, аппаратного обеспечения и размера данных, что затрудняет обобщение воздействия.
2.Цели распараллеливания
При использовании многопроцессорной обработки нашей общей целью (очевидно) является минимизация общего времени обработки для всех задач.Чтобы достичь этой общей цели, наша техническая цель должна быть , оптимизирующей использование аппаратных ресурсов .
Некоторые важные подцели для достижения технической цели:
- минимизируют издержки распараллеливания (наиболее известные, но не одни: IPC )
- высокая загрузка во всех процессорных ядрах
- ограничение использования памяти для предотвращения чрезмерного подкачки ОС ( trashing )
Сначала задачи требуютчтобы быть достаточно сложным (интенсивным) в вычислительном отношении, чтобы заработать ПО, которое мы должны заплатить за распараллеливание.Актуальность ПО уменьшается с увеличением абсолютного времени вычислений на одно задание.Или, другими словами, чем больше абсолютное время вычислений на одно задание для вашей задачи, тем менее значимым становится потребность в сокращении ПО.Если ваши вычисления займут несколько часов на одну задачу, накладные расходы IPC будут незначительными по сравнению с ними.Основной задачей здесь является предотвращение простоя рабочих процессов после распределения всех задач.Держа все ядра загруженными, значит, мы распараллеливаемся как можно больше.
3.Сценарии распараллеливания
Какие факторы определяют оптимальный аргумент размера фрагмента для таких методов, как multiprocessing.Pool.map ()
Основным вопросом является то, сколько времени может вычисляться варьируются по нашим разным задачам.Чтобы назвать это, выбор оптимального размера фрагмента определяется ...
Коэффициент вариации ( CV ) для времени вычисления на одно задание.
Два экстремальных сценария в масштабе, следующие из степени этого отклонения:
- Для всех заданий требуется одинаковое время вычисления.
- Задание может занять несколько секунд или дней.
Для лучшей запоминаемости я буду ссылаться на эти сценарии как:
- Плотный сценарий
- Широкий сценарий
Плотный сценарий
В Плотный сценарий это будетЖелательно распределить все задачи одновременно, чтобы свести к минимуму необходимые IPC и переключение контекста.Это означает, что мы хотим создать только столько кусков, сколько рабочих процессов существует.Как уже говорилось выше, вес PO увеличивается с сокращением времени вычислений на одну задачу.
Для максимальной пропускной способности мы также хотим, чтобы все рабочие процессы были заняты до тех пор, пока не будут выполнены все задачи (без рабочих на холостом ходу).Для этой цели распределенные чанки должны быть одинакового размера или близки к.
Широкий сценарий
Основным примером для Wide Scenario будет проблема оптимизации, когда результаты либо быстро сходятся, либо вычисление может занять часы, если не дни.Обычно неясно, какую смесь «легких задач» и «тяжелых задач» будет содержать задание в таком случае, поэтому не рекомендуется распределять слишком много задач одновременно в пакете задач.Распределение меньшего количества задач одновременно, чем это возможно, означает увеличение гибкости планирования.Это необходимо здесь для достижения нашей подцели высокой эффективности использования всех ядер.
Если Pool
методы, по умолчанию, будут полностью оптимизированы для плотного сценария, они будут все чаще создавать субоптимальные временные характеристики для каждой проблемы.расположен ближе к широкому сценарию.
4.Риски Chunksize> 1
Рассмотрим этот упрощенный пример псевдокода с Wide Scenario -записью, который мы хотим передать в метод пула:
good_luck_iterable = [60, 60, 86400, 60, 86400, 60, 60, 84600]
Вместо реальных значений мы делаем вид, что видим необходимое время вычислений в секундах, для простоты только 1 минута или 1 день.Мы предполагаем, что в пуле четыре рабочих процесса (на четырех ядрах), а chunksize
имеет значение 2
.Поскольку порядок будет соблюден, куски, отправляемые рабочим, будут следующими:
[(60, 60), (86400, 60), (86400, 60), (60, 84600)]
Поскольку у нас достаточно рабочих и время вычислений достаточно велико, мы можем сказать, что каждый рабочий процесс получитПрежде всего, над тем, чтобы работать над этим.(Это не должно иметь место для быстрого выполнения задач).Кроме того, можно сказать, что вся обработка займет около 86400 + 60 секунд, поскольку это наибольшее общее время вычислений для чанка в этом искусственном сценарии, и мы распределяем чанки только один раз.
Теперь рассмотрим эту итерацию, которая имееттолько один элемент меняет свою позицию по сравнению с предыдущим итерируемым:
bad_luck_iterable = [60, 60, 86400, 86400, 60, 60, 60, 84600]
... и соответствующими кусками:
[(60, 60), (86400, 86400), (60, 60), (60, 84600)]
Просто неудача с сортировкой нашего итерируемого почти удвоилась (86400 + 86400) наше общее время обработки!Рабочий, получивший порочный (86400, 86400) -часть, блокирует вторую тяжелую задачу в своей задаче из-за того, что ее не распределили одному из рабочих на холостом ходу, уже покончившим с (60, 60) -частями.Мы, очевидно, не рискнули бы таким неприятным исходом, если бы установили chunksize=1
.
Это риск больших кусков.С более высокими размерами блоков мы торгуем гибкостью планирования за меньшие накладные расходы, и в случаях, как указано выше, это плохая сделка.
Как мы увидим в главе 6.Количественная оценка эффективности алгоритма , большие размеры также могут привести к неоптимальным результатам для плотных сценариев .
5.Алгоритм пула Chunksize-Algorithm
Ниже вы найдете слегка измененную версию алгоритма внутри исходного кода.Как видите, я обрезал нижнюю часть и обернул ее в функцию для внешнего вычисления аргумента chunksize
.Я также заменил 4
параметром factor
и передал вызовы len()
на внешний подряд.
# mp_utils.py
def calc_chunksize(n_workers, len_iterable, factor=4):
"""Calculate chunksize argument for Pool-methods.
Resembles source-code within `multiprocessing.pool.Pool._map_async`.
"""
chunksize, extra = divmod(len_iterable, n_workers * factor)
if extra:
chunksize += 1
return chunksize
Чтобы убедиться, что мы все на одной странице, вот что делает divmod
:
divmod(x, y)
- встроенная функция, которая возвращает (x//y, x%y)
.x // y
- это деление по полу, которое возвращает округленное вниз число от x / y
, тогда как x % y
- это операция по модулю, возвращающая остаток от x / y
.Следовательно, например, divmod(10, 3)
возвращает (3, 1)
.
Теперь, когда вы посмотрите на chunksize, extra = divmod(len_iterable, n_workers * 4)
, вы заметите n_workers
здесь есть делитель y
в x / y
и умножение на 4
, бездальнейшая корректировка через if extra: chunksize +=1
позже приводит к начальному размеру , по крайней мере, , в четыре раза меньшему (для len_iterable >= n_workers * 4
), чем было бы в противном случае.
Для просмотра эффекта умножения на4
для промежуточного результата размера фрагмента рассмотрим эту функцию:
def compare_chunksizes(len_iterable, n_workers=4):
"""Calculate naive chunksize, Pool's stage-1 chunksize and the chunksize
for Pool's complete algorithm. Return chunksizes and the real factors by
which naive chunksizes are bigger.
"""
cs_naive = len_iterable // n_workers or 1 # naive approach
cs_pool1 = len_iterable // (n_workers * 4) or 1 # incomplete pool algo.
cs_pool2 = calc_chunksize(n_workers, len_iterable)
real_factor_pool1 = cs_naive / cs_pool1
real_factor_pool2 = cs_naive / cs_pool2
return cs_naive, cs_pool1, cs_pool2, real_factor_pool1, real_factor_pool2
Приведенная выше функция вычисляет наивный размер фрагмента (cs_naive
) и размер фрагмента первого шага алгоритма размера фрагмента пула (cs_pool1
), а также размер фрагмента для полного алгоритма пула (cs_pool2
).Далее он вычисляет действительные факторы rf_pool1 = cs_naive / cs_pool1
и rf_pool2 = cs_naive / cs_pool2
, которые говорят нам, во сколько раз наивно рассчитанные размеры фрагментов больше, чем внутренние версии пула.
Ниже вы видите две фигуры, созданные с помощью этой функции.Левый рисунок просто показывает размеры фрагментов для n_workers=4
вплоть до итерируемой длины 500
.На правом рисунке показаны значения для rf_pool1
.Для итерируемой длины 16
реальный коэффициент становится >=4
(для len_iterable >= n_workers * 4
), а его максимальное значение составляет 7
для итерируемой длины 28-31
.Это значительное отклонение от исходного фактора 4
, к которому сходится алгоритм для более длинных итераций.'Longer' здесь является относительным и зависит от числа указанных рабочих.
![figure1](https://i.stack.imgur.com/DlDQa.png)
Помните, что для размера cs_pool1
по-прежнему отсутствует настройка extra
состаток от divmod
содержится в cs_pool2
из полного алгоритма.
Алгоритм продолжается с:
if extra:
chunksize += 1
Теперь в случаях, когда было , это aОстальное (extra
от операции divmod), увеличение размера фрагмента на 1, очевидно, не может сработать для каждой задачи.В конце концов, если бы это было так, не было бы остатка для начала.
Как вы можете видеть на рисунках ниже, " дополнительная обработка " приводит к тому, что реальный коэффициент для rf_pool2
теперь сходится к 4
от ниже 4
, и отклонение несколько плавнее.Стандартное отклонение для n_workers=4
и len_iterable=500
падает с 0.5233
для rf_pool1
до 0.4115
для rf_pool2
.
![figure2](https://i.stack.imgur.com/DKDzL.png)
В конце концовувеличение chunksize
на 1 приводит к тому, что последнее переданное задание имеет размер len_iterable % chunksize or chunksize
.
Чем интереснее и как мы увидим позже, тем более значимым будет эффект дополнительная обработка однако может наблюдаться для числа генерируемых кусков (n_chunks
).Для достаточно длинных итераций завершенный алгоритм пула chunksize (n_pool2
на рисунке ниже) стабилизирует количество кусков на n_chunks == n_workers * 4
.Напротив, наивный алгоритм (после первоначальной отрыжки) продолжает чередоваться между n_chunks == n_workers
и n_chunks == n_workers + 1
по мере увеличения длины повторяемого элемента.
![figure3](https://i.stack.imgur.com/zVjBq.png)
Ниже вы найдете две улучшенные информационные функции для пула и простой алгоритм chunksize.Вывод этих функций будет необходим в следующей главе.
# mp_utils.py
from collections import namedtuple
Chunkinfo = namedtuple(
'Chunkinfo', ['n_workers', 'len_iterable', 'n_chunks',
'chunksize', 'last_chunk']
)
def calc_chunksize_info(n_workers, len_iterable, factor=4):
"""Calculate chunksize numbers."""
chunksize, extra = divmod(len_iterable, n_workers * factor)
if extra:
chunksize += 1
# `+ (len_iterable % chunksize > 0)` exploits that `True == 1`
n_chunks = len_iterable // chunksize + (len_iterable % chunksize > 0)
# exploit `0 == False`
last_chunk = len_iterable % chunksize or chunksize
return Chunkinfo(
n_workers, len_iterable, n_chunks, chunksize, last_chunk
)
Не смущайтесь, вероятно, неожиданному взгляду calc_naive_chunksize_info
.extra
из divmod
не используется для вычисления размера фрагмента.
def calc_naive_chunksize_info(n_workers, len_iterable):
"""Calculate naive chunksize numbers."""
chunksize, extra = divmod(len_iterable, n_workers)
if chunksize == 0:
chunksize = 1
n_chunks = extra
last_chunk = chunksize
else:
n_chunks = len_iterable // chunksize + (len_iterable % chunksize > 0)
last_chunk = len_iterable % chunksize or chunksize
return Chunkinfo(
n_workers, len_iterable, n_chunks, chunksize, last_chunk
)
6.Количественная оценка эффективности алгоритма
Теперь, после того, как мы увидели, как выходные данные алгоритма chunksize Pool
выглядят иначе, чем выходные данные наивного алгоритма ...
- Как определить, действительно ли подход Пула улучшает что-то?
- И что именно может это что-то быть?
Как показано в предыдущей главе, для более длинных итераций (большее количество задач) алгоритм пула chunksize-алгоритм приблизительно делит итерируемое на четыре раза больше фрагментов, чемнаивный метод.Меньшие куски означают больше задач, а больше задач означают больше Накладные расходы на параллелизацию (PO) , стоимость, которую необходимо сопоставить с преимуществом повышенной гибкости планирования (вспомните "Риски размера кусков> 1" ).
По довольно очевидным причинам основной алгоритм пула chunksize не может сравниться с нами по гибкости планирования и PO .Издержки IPC зависят от операционной системы, аппаратного обеспечения и размера данных.Алгоритм не может знать, на каком оборудовании мы запускаем наш код, и не имеет понятия, сколько времени займет выполнение задачи.Это эвристическое обеспечение основных функций для всех возможных сценариев.Это означает, что он не может быть оптимизирован для какого-либо конкретного сценария.Как упоминалось ранее, PO также становится все менее и менее важным с увеличением времени вычислений на одно задание (отрицательная корреляция).
Когда вы вспоминаете Цели распараллеливания из главы 2одной из ключевых точек было:
- высокая загрузка всех процессорных ядер
Ранее упомянутое что-то , алгоритм пула chunksize-алгоритм может попытаться улучшить - это минимизация бездействующих рабочих процессов , соответственно использование процессорных ядер .
Повторяющийся вопрос о SO в отношении multiprocessing.Pool
спрашивают люди, интересующиеся неиспользуемыми ядрами / простоями рабочих процессов в ситуациях, когда можно ожидать, что все рабочие процессы заняты.Хотя на это может быть много причин, простаивающие рабочие процессы ближе к концу вычислений - это наблюдение, которое мы часто можем сделать, даже с плотными сценариями (равное время вычислений на одно задание) в тех случаях, когда число работников составляетне делитель количества кусков (n_chunks % n_workers > 0
).
Вопрос теперь таков:
Как мы можем практически перевести наше понимание размеров кусков во что-то, что позволяет нам объяснить наблюдаемое использование работником или даже сравнить эффективность различных алгоритмов вчто касается?
6.1 Модели
Для более глубокого понимания здесь нам нужна форма абстракции параллельных вычислений, которая упрощает слишком сложную реальность до управляемой степенисложность, сохраняя значение в определенных границах.Такая абстракция называется модель .Реализация такой « модели распараллеливания» (PM) генерирует метаданные (временные метки), отображаемые рабочим, как реальные вычисления, если бы данные собирались.Сгенерированные моделью метаданные позволяют прогнозировать метрики параллельных вычислений при определенных ограничениях.
![figure4](https://i.stack.imgur.com/4gjoC.png)
Одна из двух подмоделей в рамках определенного здесь PM - это модель распределения (DM) . DM объясняет, как атомные единицы работы (задачи) распределяются по параллельным рабочим и времени , когда нет других факторов, кроме соответствующего алгоритма chunksize, количества рабочих, входных данных.повторяемость (количество задач) и продолжительность их вычисления.Это означает, что любая форма издержек не включена.
Для получения полной PM , DM расширяется с издержкамиМодель (OM) , представляющая различные формы издержки распараллеливания (PO) .Такая модель должна быть откалибрована для каждого узла индивидуально (аппаратные, ОС-зависимости).Сколько форм служебных данных представлено в OM , оставлено открытым, и поэтому может существовать несколько OM с различной степенью сложности.Какой уровень точности необходим для реализации OM , определяется общим весом PO для конкретного вычисления.Более короткие задачи приводят к увеличению веса PO , что, в свою очередь, требует более точного OM , если мы пытались предсказать Эффективность распараллеливания (PE).
6.2 Параллельное расписание (PS)
Параллельное расписание - это двумерное представление параллельных вычислений, где ось X представляет время, а ось Y представляет пул параллельных рабочих.Число рабочих и общее время вычислений отмечают протяженность прямоугольника, в котором нарисованы меньшие прямоугольники. Эти меньшие прямоугольники представляют атомные единицы работы (задачи).
Ниже вы видите визуализацию PS , построенный с использованием данных DM алгоритма размера пула для плотного сценария .
![figure5](https://i.stack.imgur.com/zgfRF.png)
- Ось X разделена на равные единицы времени, где каждая единица обозначает время вычисления, необходимое для задачи.
- Ось Y делится на количество рабочих процессов, которые использует пул.
- В данном случае таскел отображается как наименьший прямоугольник голубого цвета, помещенный во временную шкалу (график)анонимного рабочего процесса.
- Задача - это одна или несколько задач на временной шкале рабочего, непрерывно выделенных одним и тем же оттенком.
- Единицы времени холостого хода представлены с помощью красных плиток.
- Параллельное расписание разбито на секции.Последний раздел является хвостовым.
Названия составных частей можно увидеть на рисунке ниже.
![figure6](https://i.stack.imgur.com/QsIE9.png)
В комплекте PM , включая OM , Холостой ход не ограничен хвостом, но также включает пространство между задачами и даже между заданиями.
6.3 Эффективность
Примечание:
С более ранних версий этого ответа "Эффективность распараллеливания (PE)" былапереименован в «Эффективность распределения (DE)». PE теперь относится к эффективности, включающей накладные расходы.
Представленные выше модели позволяют количественно оценить коэффициент использования работника.Мы можем различить:
- Эффективность распределения (DE) - рассчитывается с помощью DM (или упрощенного метода для плотного сценария ).
- Эффективность распараллеливания (PE) - либо рассчитывается с помощью калиброванного PM (прогноз), либо рассчитывается на основе метаданных реальных вычислений.
Важно отметить, что вычисленные коэффициенты полезного действия не автоматически коррелируют с более быстрым общим вычислением для данной проблемы распараллеливания.Использование работника в этом контексте различает только работника, у которого есть начальная, но незаконченная задача, и работника, у которого нет такой «открытой» задачи.Это означает, что возможный холостой ход в течение промежутка времени для зарегистрированной задачи не зарегистрирован.
Все вышеупомянутые эффективности в основном получены путем вычисления коэффициента деления Busy Share / Параллельное расписание .Разница между DE и PE заключается в том, что занятый ресурс занимает меньшую часть общего параллельного расписания для расширенных служебных данных PM .
В этом ответе далее будет обсуждаться только простой метод расчета DE для плотного сценария.Этого достаточно для сравнения различных алгоритмов chunksize, поскольку ...
- ... DM является частью PM , которая изменяетсяс различными используемыми алгоритмами chunksize.
- ... Плотный сценарий с равными длительностями вычислений на одно задание изображает «стабильное состояние», для которого эти промежутки времени выпадают из уравнения.Любой другой сценарий может привести к случайным результатам, так как порядок задач будет иметь значение.
6.3.1 Абсолютная эффективность распределения (ADE)
Эту базовую эффективность можно рассчитатьв общем случае путем деления занятой доли на весь потенциал параллельного расписания :
Абсолютная эффективность распределения (ADE) = Занятое распределение / Параллельное расписание
Для Плотного сценария , упрощенный код расчета выглядит следующим образом:
# mp_utils.py
def calc_ade(n_workers, len_iterable, n_chunks, chunksize, last_chunk):
"""Calculate Absolute Distribution Efficiency (ADE).
`len_iterable` is not used, but contained to keep a consistent signature
with `calc_rde`.
"""
if n_workers == 1:
return 1
potential = (
((n_chunks // n_workers + (n_chunks % n_workers > 1)) * chunksize)
+ (n_chunks % n_workers == 1) * last_chunk
) * n_workers
n_full_chunks = n_chunks - (chunksize > last_chunk)
taskels_in_regular_chunks = n_full_chunks * chunksize
real = taskels_in_regular_chunks + (chunksize > last_chunk) * last_chunk
ade = real / potential
return ade
Если нет Холостой ход , Занятая доля будет равна до Параллельное расписание , следовательно, мы получаем ADE 100%.В нашей упрощенной модели это сценарий, в котором все доступные процессы будут заняты все время, необходимое для обработки всех задач.Другими словами, вся работа фактически распараллеливается на 100 процентов.
Но почему я продолжаю ссылаться на PE как absolute PE здесь?
Чтобы понять это, мы должны рассмотреть возможный случай для размера фрагмента (cs), который обеспечивает максимальную гибкость планирования (также может быть число горцев. Совпадение?):
___________________________________ ~ ONE ~ ___________________________________
Если у нас, например, будет четыре рабочих процесса и 37 задач, рабочие будут работать на холостом ходу даже с chunksize=1
, простопотому что n_workers=4
не является делителем 37. Остальная часть деления 37/4 равна 1. Эту единственную оставшуюся задачу придется обрабатывать одному работнику, а остальные три - на холостом ходу.
Аналогично,все еще будет один работающий на холостом ходу с 39 задачами, как вы можете видеть на рисунке ниже.
![figure7](https://i.stack.imgur.com/Ysu7Y.png)
Когда вы сравниваете верхний ПараллПо расписанию для chunksize=1
с приведенной ниже версией для chunksize=3
вы заметите, что верхнее Параллельное расписание меньше, временная шкала на оси X короче.Теперь должно стать очевидным, как большие куски неожиданно также могут привести к увеличению общего времени вычислений, даже для Плотных сценариев .
Но почему бы просто не использоватьдлина оси X для расчетов эффективности?
Поскольку накладные расходы не содержатся в этой модели.Это будет отличаться для обоих кусков, следовательно, ось X не является прямо сопоставимой.Накладные расходы все еще могут привести к увеличению общего времени вычислений, как показано в case 2 на рисунке ниже.
![figure8](https://i.stack.imgur.com/EzZaX.png)
6.3.2 Относительная эффективность распределения (RDE)
Значение ADE не содержит информации, если возможно распределение задач лучше с размером фрагмента 1. Лучше здесь все еще означает меньшую Холостую долю .
Чтобы получить значение DE , настроенное на максимально возможное значение DE мы должны разделить рассмотренную ADE на ADE , полученную нами для chunksize=1
.
Относительная эффективность распределения (RDE) = ADE_cs_x / ADE_cs_1
Вот как это выглядит в коде:
# mp_utils.py
def calc_rde(n_workers, len_iterable, n_chunks, chunksize, last_chunk):
"""Calculate Relative Distribution Efficiency (RDE)."""
ade_cs1 = calc_ade(
n_workers, len_iterable, n_chunks=len_iterable,
chunksize=1, last_chunk=1
)
ade = calc_ade(n_workers, len_iterable, n_chunks, chunksize, last_chunk)
rde = ade / ade_cs1
return rde
RDE Как определено здесь, по сути это рассказ о хвосте Параллельное расписание . RDE зависит от максимально эффективного размера кусочка, содержащегося в хвосте.(Этот хвост может иметь длину оси x chunksize
или last_chunk
.) Это приводит к тому, что RDE естественным образом сходится к 100% (даже) для всех видов "хвостов", напримерпоказано на рисунке ниже.
![figure9](https://i.stack.imgur.com/3rKsK.png)
Низкий RDE ...
- является сильным намеком на потенциал оптимизации.
- естественно становится менее вероятным для более длинных итераций, потому что относительная хвостовая часть общего Параллельного расписания уменьшается.
найти часть II этого ответа здесь ниже .