Как измерить производительность асинхронного кода Python?

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

Так как же измерить время, которое занимает сопрограмма? Как сравнить две реализации и найти более эффективную? Какие инструменты я использую?


person e-satis    schedule 16.01.2016    source источник


Ответы (2)


Этот ответ изначально содержал два разных решения: первое было основано на исправлении обезьян, а второе не работает для Python 3.7 и более поздних версий. Мы надеемся, что эта новая версия представляет собой лучший и более надежный подход.

Во-первых, стандартные инструменты измерения времени, такие как время, можно использовать для определения времени процессора программа, которая обычно нас интересует при тестировании производительности асинхронного приложения. Эти измерения также можно выполнить в python с помощью time.process_time() функция:

import time

real_time = time.time()
cpu_time = time.process_time()

time.sleep(1.)
sum(range(10**6))

real_time = time.time() - real_time
cpu_time = time.process_time() - cpu_time

print(f"CPU time: {cpu_time:.2f} s, Real time: {real_time:.2f} s")

См. ниже аналогичный вывод, полученный обоими методами:

$ /usr/bin/time -f "CPU time: %U s, Real time: %e s" python demo.py
CPU time: 0.02 s, Real time: 1.02 s  # python output
CPU time: 0.03 s, Real time: 1.04 s  # `time` output

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

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

async def main():
    print("~ Correct IO management ~")
    with print_timing():
        await asyncio.sleep(1)
        sum(range(10**6))
    print()

    print("~ Incorrect IO management ~")
    with print_timing():
        time.sleep(0.2)
        await asyncio.sleep(0.8)
        sum(range(10**6))
    print()

asyncio.set_event_loop_policy(TimedEventLoopPolicy())
asyncio.run(main(), debug=True)

Обратите внимание на разницу между этими двумя прогонами:

~ Correct IO management ~
CPU time:      0.016 s
Select time:   1.001 s
Other IO time: 0.000 s
Real time:     1.017 s

~ Incorrect IO management ~
CPU time:      0.016 s
Select time:   0.800 s
Other IO time: 0.200 s
Real time:     1.017 s

Также обратите внимание, что режим отладки asyncio может обнаруживать эти блокирующие операции:

Executing <Handle <TaskWakeupMethWrapper object at 0x7fd4835864f8>(<Future finis...events.py:396>) created at ~/miniconda/lib/python3.7/asyncio/futures.py:288> took 0.243 seconds
person Vincent    schedule 16.01.2016
comment
Я проголосую, потому что он будет работать, но он очень хакерский и может сломаться при следующем обновлении Python. - person e-satis; 16.01.2016
comment
@e-satis См. мое редактирование для другого подхода, менее хакерского. - person Vincent; 16.01.2016

Если вы хотите измерить производительность только «вашего» кода, вы можете использовать подход, аналогичный модульному тестированию — просто патчить (даже патч + макет) ближайшую сопрограмму ввода-вывода с будущим ожидаемого результата.

Основным недостатком является то, что, например. http-клиент довольно прост, но, скажем, momoko (клиент pg)... это может быть сложно сделать, не зная его внутренностей, он не будет включать накладные расходы библиотеки.

Pro такие же, как и в обычном тестировании:

  • это легко реализовать,
  • он что-то измеряет;), в основном свою реализацию без накладных расходов на сторонние библиотеки,
  • тесты производительности изолированы, легко перезапускаются,
  • это работать со многими полезными нагрузками
person kwarunek    schedule 17.01.2016