Допустим, у меня есть следующее:
- Система с 4 графическими процессорами.
- Функция
foo
, которая может запускаться до 2 раз одновременно на каждомGPU. - Список
files
, который необходимо обработать, используя foo
в любом порядке.Однако для обработки каждого файла требуется непредсказуемое количество времени.
Я хотел бы обработать все файлы, поддерживая все графические процессоры как можно более занятыми, гарантируя, что всегда есть 8 экземпляров foo
запуск в любое время (2 экземпляра на каждом графическом процессоре) до тех пор, пока не останется менее 8 файлов.
Фактические детали вызова графического процессора не являются моей проблемой.Я пытаюсь понять, как записать распараллеливание, чтобы я мог сохранить 8 экземпляров foo
в рабочем состоянии, но каким-то образом убедиться, что ровно 2 из каждого идентификатора GPU используются постоянно.
IМы придумали один способ решения этой проблемы, используя multiprocessing.Pool
, но решение довольно хрупкое и опирается на (AFAIK) недокументированные функции.Он основан на том факте, что процессы в Pool
имеют имена в формате FormPoolWorker-%d
, где %d
- это число между одним и числом процессов в пуле.Я беру это значение и модифицирую его количеством графических процессоров, и это дает мне действительный идентификатор графического процессора.Однако было бы гораздо приятнее, если бы я мог как-то дать идентификатор GPU непосредственно каждому процессу, возможно, при инициализации, вместо того, чтобы полагаться на строковый формат имен процессов.
Одна вещь, которую я рассмотрел, заключается в том, что если The initializer
и initargs
параметры Pool.__init__
разрешенный список initargs
так, что каждый процесс может быть инициализирован с другим набором аргументов, то проблема была бы спорной.К сожалению, это не работает.
Кто-нибудь может порекомендовать более надежное или питонное решение этой проблемы?
Хакерское решение (Python 3.7):
from multiprocessing import Pool, current_process
def foo(filename):
# Hacky way to get a GPU id using process name (format "ForkPoolWorker-%d")
gpu_id = (int(current_process().name.split('-')[-1]) - 1) % 4
# run processing on GPU <gpu_id>
ident = current_process().ident
print('{}: starting process on GPU {}'.format(ident, gpu_id))
# ... process filename
print('{}: finished'.format(ident))
pool = Pool(processes=4*2)
files = ['file{}.xyz'.format(x) for x in range(1000)]
for _ in pool.imap_unordered(foo, files):
pass
pool.close()
pool.join()