Создание основного процесса для цикла for

#python #python-3.x #process #python-multiprocessing #moviepy

#python #python-3.x #процесс #python-многопроцессорная обработка #moviepy

Вопрос:

Эта программа возвращает разрешение видео, но поскольку мне нужен крупномасштабный проект, мне нужна многопроцессорная обработка. Я пробовал использовать и параллельную обработку с использованием другой функции, но это просто запускало бы ее несколько раз, не делая ее эффективной. Я публикую весь код. Можете ли вы помочь мне создать основной процесс, который использует все ядра.

 import os
from tkinter.filedialog import askdirectory
from moviepy.editor import VideoFileClip


if __name__ == "__main__":
    dire = askdirectory()
    d = dire[:]
    print(dire)
    death = os.listdir(dire)
    print(death)
    for i in death: #multiprocess this loop
        dire = d
        dire  = f"/{i}"
        v = VideoFileClip(dire)
        print(f"{i}: {v.size}")
 

Этот код работает нормально, но мне нужна помощь в создании основного процесса (использует все ядра) только для цикла for. можете ли вы извинить имена переменных, я был зол на многопроцессорность. Также, если у вас есть советы по повышению эффективности кода, я был бы признателен.

Комментарии:

1. вы должны использовать потоки или многопроцессорные внутренние for циклы для запуска каждого VideoFileClip отдельного процесса, а не для for .

Ответ №1:

Я полагаю, вы предполагаете, что каждый файл в каталоге представляет собой видеоклип. Я предполагаю, что обработка видеоклипа — это связанный с вводом-выводом «процесс», для которого подходит потоковая передача. Здесь я довольно произвольно создал пул потоков размером 20 потоков таким образом:

 MAX_WORKERS = 20 # never more than this
N_WORKERS = min(MAX_WORKERS, len(death))
 

Вам придется поэкспериментировать с тем, насколько большими могут быть MAX_WORKERS, прежде чем производительность снизится. Это может быть низкое число не потому, что ваша система не может поддерживать много потоков, а потому, что одновременный доступ к нескольким файлам на вашем диске, которые могут быть распределены по среде, может быть неэффективным.

 import os
from tkinter.filedialog import askdirectory
from moviepy.editor import VideoFileClip
from concurrent.futures import ThreadPoolExecutor as Executor
from functools import partial


def process_video(parent_dir, file):
    v = VideoFileClip(f"{parent_dir}/{file}")
    print(f"{file}: {v.size}")


if __name__ == "__main__":
    dire = askdirectory()
    print(dire)
    death = os.listdir(dire)
    print(death)
    worker = partial(process_video, dire)
    MAX_WORKERS = 20 # never more than this
    N_WORKERS = min(MAX_WORKERS, len(death))
    with Executor(max_workers=N_WORKERS) as executor:
        results = executor.map(worker, death) # results is a list: [None, None, ...]
 

Обновить

Согласно @Reishin, moviepy приводит к выполнению ffmpeg исполняемого файла и, таким образом, в конечном итоге создает процесс, в котором выполняется работа. Поэтому здесь нет смысла также использовать многопроцессорную обработку.

Комментарии:

1. в этом нет необходимости, moviepy lib — это просто оболочка вокруг исполняемого файла ffmpeg. Зачем создавать столько бесполезных потоков / процессов, если в результате все равно будет создан новый процесс? Здесь лучше иметь что-то, что используется одним потоком и наблюдателем для открытых подпроцессов

2. @Reishin Ну, вы, кажется, знаете больше, moviepy чем я. Я бы посоветовал вам написать свой собственный ответ, чтобы точно объяснить, какова ваша стратегия по созданию нескольких подпроцессов. Разговоры — это здорово, код — лучше.

3. не нужно осваивать библиотеку, просто быстрый поиск и просмотр кода на GitHub. Основная цель библиотеки — редактировать клипы, а не быстро обрабатывать кучу метаинформаций. Вердикт — использование неправильной библиотеки

Ответ №2:

moviepy это просто оболочка ffmpeg , предназначенная для редактирования клипов, таким образом, работающая с одним файлом за раз — производительность довольно низкая. Каждый раз, когда вызывается новый процесс для нескольких файлов, требуется много времени. В конце концов, необходимость в нескольких процессах может быть результатом выбора неправильной библиотеки.

Я хотел бы порекомендовать вместо этого использовать pyAV lib, который обеспечивает прямую привязку py для ffmpeg и хорошую производительность:

 import av
import os
from tkinter.filedialog import askdirectory
import multiprocessing
from concurrent.futures import ThreadPoolExecutor as Executor

MAX_WORKERS = int(multiprocessing.cpu_count() * 1.5)

def get_video_resolution(path):
  container = None
  try:
    container = av.open(path)
    frame = next(container.decode(video=0))
    return path, f"{frame.width}x{frame.height}"
  finally:
    if container:
      container.close()

def files_to_proccess():
  video_dir = askdirectory()
  return (full_file_path for f in os.listdir(video_dir) if (full_file_path := os.path.join(video_dir, f)) and not os.path.isdir(full_file_path))


def main():   
 for f in files_to_proccess():
    print(f"{os.path.basename(f)}: {get_video_resolution(f)[1]}")


def main_multi_threaded():
  with Executor(max_workers=MAX_WORKERS) as executor:
    for path, resolution in executor.map(get_video_resolution, files_to_proccess()):
        print(f"{os.path.basename(path)}: {resolution}")


if __name__ == "__main__":
  #main()
  main_multi_threaded()
 

Выше приведены однопоточные и многопоточные реализации с оптимальной настройкой параллелизма (на случай, если многопоточность является чем-то абсолютно необходимым)

Комментарии:

1. Я не могу использовать второе решение, потому что библиотека av не загружается из-за какой-либо ошибки у вас есть какие-либо советы

2. @ViditAggarwal библиотека av для поддерживаемых версий python поставляется в виде whl файлов, готовых к использованию. Убедитесь, что вы используете Python 3.5, 3.6, 3.7 или 3.8 или напрямую загружаете файл из PyPI: pypi.org/project/av/#files