【问题标题】:How to measure Python's asyncio code performance?如何衡量 Python 的 asyncio 代码性能?
【发布时间】:2016-04-21 23:02:06
【问题描述】:

我不能使用普通的工具和技术来衡量协程的性能,因为不应该考虑在await 所花费的时间(或者它应该只考虑从等待而不是从IO 延迟)。

那么如何测量协程所花费的时间呢?如何比较 2 个实现并找到更有效的?我使用什么工具?

【问题讨论】:

    标签: python performance-testing trace python-asyncio


    【解决方案1】:

    这个答案最初包含两种不同的解决方案:第一个基于猴子补丁,第二个不适用于 python 3.7 及更高版本。这个新版本有望提供一种更好、更强大的方法。

    首先,time 等标准计时工具可用于确定程序的 CPU 时间,这通常是我们在测试异步应用程序性能时感兴趣的内容。这些测量也可以在 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
    

    在异步应用程序中,可能会发生程序的某些同步部分最终执行阻塞调用,从而有效地阻止事件循环运行其他任务。所以我们可能想分别记录事件循环等待的时间和其他 IO 任务花费的时间。

    这可以通过继承default selector 来执行一些计时操作并使用custom event loop policy 来设置一切来实现。 This code snippet 提供了这样的策略以及用于打印不同时间指标的上下文管理器。

    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 debug mode 可以检测到那些阻塞操作:

    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
    

    【讨论】:

    • 我会投赞成票,因为它会工作,但它非常 hacky,可能会在下一次 python 升级时中断。
    • @e-satis 查看我的编辑以了解另一种方法,不那么骇人听闻。
    【解决方案2】:

    如果您只想测量“您的”代码的性能,您可以使用类似于单元测试的方法 - 只需猴子补丁(甚至补丁 + 模拟)具有预期结果未来的最近 IO 协程。

    主要缺点是,例如http 客户端相当简单,但假设 momoko(pg 客户端)......在不了解其内部结构的情况下可能很难做到,它不包括库开销。

    pro 就跟普通测试一样:

    • 很容易实现,
    • 它测量一些东西 ;),主要是在没有第三方库开销的情况下实现,
    • 性能测试是独立的,易于重新运行,
    • 它可以与许多有效负载一起运行

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-10
      • 2014-05-26
      • 2022-11-11
      • 1970-01-01
      • 2011-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多