【发布时间】:2017-01-22 12:14:56
【问题描述】:
在我的 Python GAE 应用程序中,以下 sn-p 代码在生产中比在本地运行时慢得多。处理过程如下:
- 在 POST 中加载了大约 1 MB 的文本文件。文本文件的每一行都是一个“项目”。
- 我的代码从文本文件创建项目列表并检查重复项和有效性(通过与已编译的 RE 进行比较)。
代码如下:
def process_items(self, text):
item_list = text.split()
item_set = set()
n_valid = 0
n_invalid = 0
n_dups = 0
out = ""
for item in item_list:
if item in item_set:
n_dups += 1
out += "DUPLICATE: %s\n" % item
elif valid_item(item): # This compares against a compiled RE
item_set.add(item)
n_valid += 1
out += "%s\n" % item
else:
n_invalid += 1
out += "INVALID: %s\n" % item
return out
当我在本地开发服务器上运行此程序时,处理一个 50,000 行的 1MB 文件需要 5 秒。
当我在生产环境中运行它时,同一个文件会占用一分钟多的时间,并且请求会超时。文件上传只需要大约一秒钟,所以我知道瓶颈是上面的代码。
过去,生产代码的速度与我的本地代码差不多。我认为这段代码没有改变,所以我怀疑 Google 端可能发生了变化。
知道为什么这段代码现在在生产中要慢得多吗? 我能做些什么来让这段代码更快?我需要向用户返回一个带注释的文件,指出哪些行是重复的,哪些行是无效的。
编辑:
针对 mgilson 的评论,我尝试了以下代码,它在执行时间上产生了巨大的差异!之前在一分钟后超时的处理现在只需要大约 5 秒。 GAE 仍然比预期慢(即使考虑到相对较慢的服务器 CPU),但是有了改进的算法,现在对我来说已经无所谓了。
def process_items(self, text):
item_list = text.split()
item_set = set()
n_valid = 0
n_invalid = 0
n_dups = 0
for i, item in enumerate(item_list):
item = item.strip()
if item in item_set:
n_dups += 1
item_list[i] = "DUPLICATE: %s" % item
elif valid_item(item): # This compares against a compiled RE
item_set.add(item)
n_valid += 1
item_list[i] = item
else:
n_invalid += 1
item_list[i] = "INVALID: %s" % item
return "\n".join(item_list)
【问题讨论】:
-
比较本地运行和在 GAE 上运行并不公平。根据您设置的instance class,您的 CPU 限制可能低至 600MHz。大多数个人计算机比现在快显着。 可能 有帮助的一个显而易见的优化是将结果累积在一个列表中,并在末尾累积
return "".join(results),而不是使用+=。例如,请参阅Why is ''.join() faster than += in Python? ... -
使
process_items生成器yielding一次一行也可以通过避免缓慢的+=来加快其整体处理时间 -
@mgilson,我的 Mac 是 2.2 GHz,因此它比 GAE 快 3.7 倍。对于这个 Python 代码,我的 Mac 至少比 GAE 快 12 倍。尽管我意识到许多因素使它成为不完美的比较,但这似乎仍然是一个很大的差异。
-
从运行到运行所用的时间是否一致。也是一个实例正在运行,或者您是否有启动时间。尝试创建一个新的 appengine 实例并在那里测试你的性能。您可能始终处于受到其他服务冲击的节点上。这是一个直接的 CPU 任务。
-
@TimHoffman,我在一个仅供我使用的实例上连续执行了几次,因此启动时间不是一个因素。从昨天到今天,以及每次后续的几次运行,它都是一致的。
标签: python performance google-app-engine