快速响应的命令行脚本

4 投票
2 回答
1117 浏览
提问于 2025-04-16 08:42

我写命令行的Python脚本已经有一段时间了,但最近我对速度感到非常沮丧。

我说的不是处理速度、任务调度或其他命令行工具特有的过程(这些通常是设计或实现上的问题),而是指简单地运行一个工具来获取帮助菜单,或者显示一些基本信息。

举个例子,Mercurial的响应时间大约是0.080秒,而GIT的响应时间是0.030秒。

我查看了Mercurial的源代码(毕竟它是用Python写的),但仍然找不到让脚本快速响应的解决办法。

我认为imports(导入模块)以及你如何管理它们是导致初始速度慢的一个重要原因。那么,在Python中有没有什么最佳实践可以让命令行脚本快速反应呢?

比如,一个简单的Python脚本导入了os和optparse,并执行main()来解析一些参数选项,仅仅显示帮助菜单就花了我机器上0.160秒……

这比直接运行git慢了5倍!

编辑:

我不应该提到git,因为它是用C语言写的。但Mercurial的部分仍然成立,而且,pyc对我来说并没有感觉到很大的提升。

编辑2:

虽然懒加载(lazy imports)在Mercurial中是加速的关键,但在普通的Python脚本中,导致慢的关键是没有使用pkg_resources自动生成的脚本,比如:

from pkg_resources import load_entry_point

如果你手动生成的脚本没有使用pkg_resources,你应该能看到至少2倍的速度提升。

不过!要注意,pkg_resources确实提供了一种很好的版本依赖管理方式,所以确保你知道不使用它可能会导致版本冲突。

2 个回答

0

抱歉,但我觉得你担心的并不是那0.08秒。虽然你没有说,但感觉你是在运行一个“外部”脚本(或者其他语言的脚本),这个脚本在一个循环里调用了好几百个Python脚本。只有这样,启动时间才会有明显的影响。所以,要么你在问题中隐瞒了这个重要信息,要么你父亲就是这个家伙

假设你有一个外部脚本,它调用了大约几百个Python进程:你可以把这个外部脚本用Python写,然后在同一个进程中导入你需要的Python内容,从那里运行。这样,你就能减少每次执行脚本时解释器的启动时间和模块的导入时间。

这对Mercurial也是适用的。例如,你可以导入“mercurial”及其相关的子模块,然后调用里面的函数,这样就能实现和命令行参数相同的功能。

7

除了编译Python文件,Mercurial还把导入模块的方式改成了按需导入,这样确实能减少启动时间。它把__builtin__.__import__设置成了自己在demandimport模块里的导入函数。

如果你查看一下/usr/lib/目录下的hg脚本(或者在你电脑上的其他地方),你可以在以下几行看到这个设置:

try:
    from mercurial import demandimport; demandimport.enable()
except ImportError:
    import sys
    sys.stderr.write("abort: couldn't find mercurial libraries in [%s]\n" %
                     ' '.join(sys.path))
    sys.stderr.write("(check your install and PYTHONPATH)\n")
    sys.exit(-1)

如果你把demandimport那一行改成pass,你会发现启动时间会明显增加。在我的电脑上,启动时间大约翻倍。

我建议你研究一下demandimport.py,看看怎么在自己的项目中应用类似的技巧。

顺便说一下,Git是用C语言写的,所以它启动速度快我也不感到惊讶。

撰写回答