Что лучше использовать для распаковки тысячи zip архивов: многопоточность или многопроцессорность
Что проверяют
Умеете ли вы определить, где узкое место, и знаете ли, что расширения на C отпускают GIL. Ответ «CPU-bound, значит процессы» заучен и здесь неточен.
Как отвечать
Начать с разбора самой задачи: она смешанная. Чтение архива и запись файлов это диск, распаковка это процессор, но считает её zlib, то есть C-код, который GIL на время работы отпускает. Отсюда:
ThreadPoolExecutorдействительно распараллеливает распаковку, хотя потоки питоновские. Потоки дешевле, ничего не пиклится, задача получает только путь к файлу;ProcessPoolExecutorнужен, если упираетесь именно в процессор при быстром диске или если вокруг распаковки есть заметный код на чистом Python (разбор, перекодировка, проверки);- гранулярность: один архив на задачу, а не один файл внутри архива, иначе накладные расходы сравняются с работой;
- размер пула подбирается замером. На обычном диске ускорение упрётся в него уже на двух-трёх воркерах, на NVMe дойдёт до числа ядер.
Итог: начать с пула потоков, померить, уходить на процессы только если профиль показал упор в код на Python.
Где спотыкаются
Передают в ProcessPoolExecutor лямбду или вложенную функцию. Аргументы и функция уходят через pickle, лямбда не сериализуется, и код падает на первой же задаче. Нужна функция верхнего уровня, а параметры доклеиваются через functools.partial.
Второе: распаковывают всё в одну папку. Одинаковые имена файлов из разных архивов перетирают друг друга, и результат тихо теряет данные. Каждому архиву нужен свой подкаталог.
Третье: не ограничивают распаковку недоверенных архивов. Содержимое раздувается на порядки, и диск кончается посреди работы, поэтому суммарный file_size из оглавления проверяют до извлечения.