gpt4 book ai didi

google-app-engine - AppEngine Query.fetch_async不是很异步吗?

转载 作者:太空宇宙 更新时间:2023-11-03 15:23:19 24 4
gpt4 key购买 nike

我试图通过使用query.fetch_async()异步运行多个子查询来减少AppEngine查询的执行时间。但是,与串行运行查询相比,增益似乎很小。

以下是一些最小的示例代码(在Python中)说明了问题-首先是异步运行的函数:

def run_parallel(self, repeats):
start = datetime.utcnow()

futures = []
for i in xrange(0, repeats):
q = User.query()
f = q.fetch_async(300, keys_only=True)
futures.append(f)

while futures:
f = ndb.Future.wait_any(futures)
futures.remove(f)
results = f.get_result()
delta_secs = (datetime.utcnow() - start).total_seconds()
self.response.out.write("Got %d results, delta_sec: %f<br>\n" %(len(results), delta_secs))

然后是用于相应串行运行的函数:
def run_serial(self, repeats):
start = datetime.utcnow()
for i in xrange(0, repeats):
q = User.query()
results = q.fetch(300, keys_only=True)
delta_secs = (datetime.utcnow() - start).total_seconds()
self.response.out.write("Got %d results, delta_sec: %f<br>\n" %(len(results), delta_secs))

运行这两个函数的输出各10次(不在dev-server上),即以下调用:
run_parallel(10)
run_serial(10)

如下:

正在运行并行查询...
得到300个结果,delta_sec:0.401090
获得300个结果,delta_sec:0.501700
得到300个结果,delta_sec:0.596110
获得300个结果,delta_sec:0.686120
获得300个结果,delta_sec:0.709220
获得300个结果,delta_sec:0.792070
得到300个结果,delta_sec:0.816500
获得300个结果,delta_sec:0.904360
获得300个结果,delta_sec:0.993600
得到300个结果,delta_sec:1.017320

正在运行串行查询...
得到300个结果,delta_sec:0.114950
得到300个结果,delta_sec:0.269010
获得300个结果,delta_sec:0.370590
得到300个结果,delta_sec:0.472090
获得300个结果,delta_sec:0.575130
得到300个结果,delta_sec:0.678900
获得300个结果,delta_sec:0.782540
得到300个结果,delta_sec:0.883960
获得300个结果,delta_sec:0.986370
获得300个结果,delta_sec:1.086500

因此,并行和串行版本大约需要1秒钟的时间。 Appstat如下,其中前10个查询是并行查询,随后的10个是串行查询:

从这些统计数据来看,前十个查询确实确实在并行运行,但是与单个串行查询相比,它们每个都花费了不成比例的时间。看来他们可能会以某种方式阻止对方,等待对方完成。

所以我的问题是:我的运行异步查询的代码有什么问题吗?还是在AppEngine上异步查询的效率存在固有的局限性?

我想知道是否该行为可能是由以下原因之一引起的:
  • 在相同实体类型上运行异步查询。但是,使用多个不同实体类型的类似示例显示了相似的结果。
  • 运行相同的查询,以某种方式锁定索引的各个部分。但是,在一个相似的示例中,每个查询都不同(返回不相交的结果集)会产生相似的结果。

  • 所以,我有点茫然。任何建议将不胜感激。

    更新1

    按照Bruyere的建议,我尝试使用db而不是ndb,并且尝试交换并行和串行版本的顺序。结果是一样的。

    更新2

    这是与同一问题有关的相关文章;关于并行查询为何如此低效的原因仍然没有答案:

    Best practice to query large number of ndb entities from datastore

    更新3

    使用Java SDK的相应代码非常整齐地并行化。这是Java appstats:

    确切地说,此Java实现是显式多线程的,在单独的线程中运行查询。这是必要的,因为与AppEngine文档所声称的相反,使用查询迭代器实际上不会导致查询并行执行。

    我尝试在Python版本中使用显式多线程与同步查询调用,但结果与原始Python版本相同。

    Java版本按预期运行的事实表明,不良的Python异步性能不是由AppEngine CPU瓶颈引起的。

    我能想到的唯一替代解释是Python的Global Interpreter Lock导致了崩溃。减少GIL检查间隔(使用sys.setcheckinterval)会加剧不良的异步性能,这一事实得到了支持。

    但是,这是令人惊讶的:鉴于查询是受IO约束的,GIL不应产生如此严重的影响。我推测,也许RPC输入缓冲区足够小,以至于异步调用在结果检索期间会频繁恢复,这可能会导致GIL崩溃。我看过Python AppEngine库代码,但是低级RPC调用是由_apphosting_runtime ___ python__apiproxy.MakeCall()进行的,它似乎是封闭源代码。

    conclusion,我的结论是Python AppEngine运行时不适合我所需要的那种并行查询,除了迁移到Java运行时外,别无选择。我真的很想避免这种情况,所以我真的希望我错了,错过了一些明显的事情。任何建议或指示,将不胜感激。

    谢谢!

    最佳答案

    主要问题是您的示例主要是CPU绑定(bind)的,而不是IO绑定(bind)的。特别是,大多数时间可能花费在解码RPC结果上,由于GIL,在python中效率不高。 Appstats的问题之一是,它测量从发送RPC到调用get_result()的RPC计时。这意味着在调用get_result之前花费的时间似乎来自RPC。

    如果您改为发出IO绑定(bind)RPC(即使数据存储区更难工作的查询),您将开始看到并行查询的性能提升。

    关于google-app-engine - AppEngine Query.fetch_async不是很异步吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25796142/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com