Страницы

Поиск по вопросам

воскресенье, 1 марта 2020 г.

Правильная работа с asyncio (корутинами)

#python #python_3x #асинхронность #asyncio


Захотелось мне поразбираться с механизмом асинхронной работы через корунтины (сопрограммы)
используя стандартную библиотеку asyncio.

Написал тестовый пример и удивился тому, что асинхронный код не такой эффективный
как я думал.

Возможно, я неправильно работал с asyncio и есть более эффективный способ.

Прошу помочь с этим вопросом.



Код:

from timeit import default_timer as clock
from multiprocessing.dummy import Pool as ThreadPool
import asyncio
import hashlib


def my_algo(*args):
    next_value = b'HELLO WORLD!' * 10000000

    value = hashlib.sha1(next_value).hexdigest()
    next_value = bytes(value, 'utf-8')

    value = hashlib.sha256(next_value).hexdigest()
    next_value = bytes(value, 'utf-8')

    value = hashlib.sha512(next_value).hexdigest()
    next_value = bytes(value, 'utf-8')

    value = hashlib.md5(next_value).hexdigest()

    return value


@asyncio.coroutine
def foo(i):
    return my_algo(i)


def my_sync(items):
    new_items = []

    for i in items:
        x = my_algo(i)
        new_items.append(x)

    # print(min(new_items), max(new_items))


def my_threaded(items):
    pool = ThreadPool()
    new_items = pool.map(my_algo, items)

    # print(min(new_items), max(new_items))


def my_asyncio(items):
    ioloop = asyncio.get_event_loop()

    tasks = [foo(i) for i in items]

    wait_tasks = asyncio.wait(tasks)
    results = ioloop.run_until_complete(wait_tasks)[0]

    new_items = [x.result() for x in results]
    # print(min(new_items), max(new_items))

    ioloop.close()


if __name__ == '__main__':
    t = clock()
    print('my_algo')
    my_algo()
    print('  Elapsed:', clock() - t)
    print('\n')

    items = [1] * 100

    t = clock()
    print('my_sync')
    my_sync(items)
    print('  Elapsed:', clock() - t)
    print()

    t = clock()
    print('my_threaded')
    my_threaded(items)
    print('  Elapsed:', clock() - t)
    print()

    t = clock()
    print('my_asyncio')
    my_asyncio(items)
    print('  Elapsed:', clock() - t)


Результаты:

my_algo
  Elapsed: 0.22824488105526344


my_sync
  Elapsed: 23.38838779162658

my_threaded
  Elapsed: 6.768628799269269

my_asyncio
  Elapsed: 22.96235436593546

    


Ответы

Ответ 1



Хэширование - это CPU-bound операция, в то время как асинхронность позволяет получить преимущество только в I/O-bound операциях. Предположим, что на всех графиках ось time - это константа. Последовательное выполнение my_algo трижды будет выглядеть так Асинхронное выполнение трёх задач, выполняющих ту же функцию my_algo так То есть это не настоящая параллельность, выполнение задач просто чередуется в том же промежутке времени. Обычно, это получается ещё и немного медленнее из-за необходимости переключения контекста. Выигрыш появился бы в том случае, если бы внутри my_algo осуществлялся ввод\вывод, блокирующий выполнение. Тогда заблокированная задача могла бы на время ожидания отдать управление другой, ещё не заблокированной. Графики взяты из "Введения в Twisted"

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

Отправить комментарий