Одинаковые результаты Asyncio и Синхронного кода

написал код, запустил, результаты примерно одинаковы:
4.6002631187438965 ----- 7.045402765274048.

Почему так, ведь, я думаю asyncio запускает всё вместе? А синхронный код в данном случае просто перебирает все по очереди.

import asyncio
from time import time


predictions = []


async def is_simple(num):
    global predictions
    dividers_list = []
    for divider in range(1, num + 1):
        if num % divider == 0:
            dividers_list.append(divider)
    if len(dividers_list) == 2: predictions.append([num, dividers_list])


async def starter(ranges):
    tasks_list = []
    for num in range(ranges):
        tasks_list.append(asyncio.create_task(is_simple(num)))
    await asyncio.gather(*tasks_list)


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    t0 = time()
    loop.run_until_complete(starter(ranges = 10000))
    print(predictions)
    print(time() - t0)


t0 = time()
snums = []
for element in range(10000):
    slist_nums = []
    for divider in range(1, element + 1):
        if element % divider == 0:
            slist_nums.append(divider)
    if len(slist_nums) == 2:
        snums.append([element, slist_nums])


print(snums)
print(time() - t0)

print(f'{len(snums)} ----- {len(predictions)}')

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

Автор решения: eri

asyncio не запускает всё вместе. Оно лишь прерывает функцию в момент ожидания await и в это время выполняет другие таски. Возвращается в функцию если ожидание завершено в момент когда другой таск прервется на await .

Чтоб ускорить Ваш код нужно использовать ProcessPoolExecutor в асинкио или обычный multiprocessing.

asyncio нужен для того чтоб выполнять таски пока что-то скачивается по сети, из базы данных или какой-то таск ждет таймера.

→ Ссылка
Автор решения: CrazyElf

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

        if num % divider == 0:
            # Если уже есть два делителя, то их будет 3
            # и дальше можно уже не проверять
            if len(dividers_list) >= 2:
                break
            dividers_list.append(divider)

Во-вторых асинхронность, как вам уже сказали в комментариях - для IO bound задач, т.е. задач, ожидающих окончания ввода-вывода. Для задач вида CPU bound, т.е. занимающихся вычислениями, как ваша задача, нужно использовать многопоточность. Хотя не факт, что в питоне это даст какой-то эффект из-за GIL, нужно проверять. И в этом случае нужно избавляться от global переменных, результат из функции нужно возвращать в явном виде и потом собирать его в список. Иначе, например, при использовании мультипроцессности, у вас не получится единый результат - у каждого процесса будет своя отдельная глобальная переменная, в которую он и добавит данные.

→ Ссылка