Что произойдет при запуске двух корутин с вычислением факториала через asyncio.gather и будет ли это оптимально
Что проверяют
Различаете ли конкурентность и параллелизм, и понимаете ли, что asyncio не ускоряет вычисления, а только организует ожидание.
Как отвечать
asyncio.gather обернёт корутины в задачи, дождётся всех и вернёт результаты в порядке аргументов. Но выполняются они в одном потоке и в одном событийном цикле: пока корутина не дошла до ожидания чего-то внешнего, она держит цикл целиком. Факториал это чистый счёт, точек передачи управления внутри нет, поэтому две корутины посчитаются одна за другой, ровно как два обычных вызова, только с накладными расходами на создание задач. Ускорения не будет, а всё остальное в цикле на это время замрёт.
Что предложить вместо:
math.factorialвместо своего цикла: реализация на C работает заметно быстрее;- настоящий параллелизм для счёта дают процессы, то есть
ProcessPoolExecutor, а из корутины они запускаются черезrun_in_executorу текущего цикла; asyncio.to_threadдля вычислений не спасает: потоки борются за глобальную блокировку интерпретатора. Выигрыш есть только там, где библиотека эту блокировку отпускает, например у численных пакетов на C.
loop = asyncio.get_running_loop()
with ProcessPoolExecutor() as pool:
a, b = await asyncio.gather(
loop.run_in_executor(pool, math.factorial, 50000),
loop.run_in_executor(pool, math.factorial, 60000),
)Где спотыкаются
Говорят «gather запускает параллельно». Он запускает конкурентно, а это не одно и то же: задачи чередуются на точках ожидания, одновременно в интерпретаторе исполняется одна.
Второе: пишут корутину рекурсивно, в стиле return n * await factorial(n - 1). Работает, но каждый шаг создаёт корутину, и на нескольких тысячах прилетит RecursionError, потому что глубина ограничена.
Третье: не знают поведения при ошибке. По умолчанию gather пробрасывает первое же исключение, но остальные задачи при этом не отменяются и продолжают выполняться в фоне. Чтобы собрать все результаты вместе с ошибками, нужен return_exceptions=True, а для гарантированной отмены остальных подходит группа задач.