Задать параллельный запуск подпроцесса Python

Задача, сделать несколько рендеров параллельно. Из инструментов Python и Blender.

Есть скрипт, который запускает рендер Blender через консоль один за другим. Один рендер длиться приблизительно 1 минуту. Этот скрипт можно свести сейчас к двум строкам:

sh = ['blender', "-b", 'file.blend', "-P", 'script.py')]
proc = subprocess.run(sh)

Где script.py это файл где хранятся настройки сцены для одного рендера.

Так как рендеринг это довольно затратное дело, то я хочу запустить скрипт в отдельном процессе, по этому использую библиотеку multiprocessing. В итоге код стал выглядеть примерно так:

procs = []
for num in range(len(camera_points)):
    sh = ['blender', "-b", 'file.blend', "-P", f'script_{num}.py')]
    proc = Process(target=subprocess.run, args=(sh,))
    procs.append(proc)
    proc.start()

for proc in procs:
    proc.join()

После того как я получу рендеры, я хочу их загрузить на s3 bucket, по этому я жду окончания этих процессов.

Я ожидал получить прирост скорости хотя бы в х4, так как теперь вместо одного рендера на одну минуту, будет проходить условно 5 рендеров за одну минуту. Однако в реальности 5 процессов запустились одновременно, один за другим записали результат в папку и суммарное время выполнение, составило 5 минут. В то время когда я ожидал что время выполнения сократиться хотя бы до 2.

Рендеринг проводился на AWS на сервере g3.16xlarge, с установленными и активированными драйверами GRID. В процессе рендеринга, половина GPU большую часть времени простаивала.


Ответы (1 шт):

Автор решения: Stanislav Volodarskiy

Не надо запускать процессы по числу кадров. Они конкурируют за ресурсы которых явно меньше чем нужно. multiprocessing.Pool запустит несколько процессов и распределит кадры между ними:

import multiprocessing
import subprocess


def render(num):
    print('start', num)
    subprocess.run(['blender', '-b', 'file.blend', "-P", f'script_{num}.py'])
    print('stop', num)


if __name__ == '__main__':
    n_cameras = 100
    with multiprocessing.Pool() as p:
        p.map(render, range(n_cameras))
    print('done')
→ Ссылка