Python "条件" 异步方法

3
我很好奇当我有一个像这样的函数时,它的行为/性能开销是多少:

async def slow_function(some_resource):
    if some_resource in cache.keys():
        return cache[some_resource]
    return await requests.get(some_resource)

在我们以某种方式缓存“some_resource”的情况下,没有执行“await”。在这种情况下,“async”的开销是多少?由于没有到达“await”语句,开销是否真正为零?或者无论何时调用“async”函数都存在一些不可避免的开销?

3
像这样的性能问题其实比你想象的要简单。建立一个测试案例,进行测量即可。 - Tomalak
2个回答

3

几乎没有开销。

异步函数会像普通函数一样继续执行。当你到达链中的一个生成器,它会产生一个await的开销。

实际上,每次你await一个未完成的Future,事件循环都必须绑定它并继续一个新的周期,其中包括一堆操作。如果你的await立即返回而不等待未来,事件循环不会循环,函数会像任何其他生成器函数一样正常继续执行。

其余的开销是普通函数调用和生成器初始化之间的差异(协程的实现方式基本相同)。虽然有一些开销,可能甚至是普通函数调用的两倍或三倍(主要与生成器本身的创建有关),但与每个事件循环周期中发生的大约50或60个完整函数调用以及其中的许多指令相比,这些开销是微不足道的。


0

抱歉,这不是对实际问题的答案,但我认为已经涵盖了。

您的代码存在一个不可忽略的性能问题,但与异步无关。

检查缓存中是否存在键,然后再查找缓存中的键比访问键并通过 KeyError 捕获要慢得多。

(这只是查看缓存具有键的“正常”路径)

>>> import timeit
>>> timeit.timeit('try: cache[x]\nexcept KeyError: pass', setup='cache = {i: i for i in range(500)}; x = 5')
0.0273395610274747
>>> timeit.timeit('if x in cache.keys(): cache[x]', setup='cache = {i: i for i in range(500)}; x = 5')
0.08461441402323544

这样做可能会稍微提高性能。

async def slow_function(some_resource):
    try:
        return cache[some_resource]
    except KeyError:
        return await requests.get(some_resource)

没错,我想要阐述的例子是有一个“await”并不总是被执行。 - jordan
1
此外,这高度取决于是否在缓存中存在该键。如果少于80%或90%的概率,则最好使用.get()。异常生成__traceback__的开销很大。 - Bharel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接