Python“子进程”模块比模块“命令”慢得多(已弃用)

问题描述 投票:0回答:2

我编写了一个脚本,在命令行上使用 nc 访问一堆服务器,最初我使用 Python 的命令模块并调用 commands.getoutput()。该脚本运行时间约为 45 秒。由于 commands 已弃用,我想将所有内容更改为使用 subprocess 模块,但现在脚本需要 2 分 45 秒才能运行。为什么会这样?

我之前拥有的:

output = commands.getoutput("echo get file.ext | nc -w 1 server.com port_num")

现在我有了

p = Popen('echo get file.ext | nc -w 1 server.com port_num', shell=True, stdout=PIPE)
output = p.communicate()[0]
python performance command subprocess
2个回答
20
投票

我预计

subprocess
会比
command
慢。无意暗示这是您的脚本运行缓慢的唯一原因,您应该查看
commands
源代码。不到 100 行,大部分工作都委托给
os
中的函数,其中许多直接取自 c posix 库(至少在 posix 系统中)。请注意,
commands
仅适用于unix,因此它不必做任何额外的工作来确保跨平台兼容性。

现在看看

subprocess
。有超过 1500 行,全部是纯 Python,进行各种检查以确保一致的跨平台行为。基于此,我预计
subprocess
的运行速度会比
commands
慢。

我对这两个模块进行了计时,在一些非常基本的事情上,

subprocess
几乎是
commands
慢的两倍。

>>> %timeit commands.getoutput('echo "foo" | cat')
100 loops, best of 3: 3.02 ms per loop
>>> %timeit subprocess.check_output('echo "foo" | cat', shell=True)
100 loops, best of 3: 5.76 ms per loop

Swiss 建议了一些很好的改进,这将有助于提高脚本的性能。但即使在应用它们之后,请注意

subprocess
仍然较慢。

>>> %timeit commands.getoutput('echo "foo" | cat') 100 loops, best of 3: 2.97 ms per loop >>> %timeit Popen('cat', stdin=PIPE, stdout=PIPE).communicate('foo')[0] 100 loops, best of 3: 4.15 ms per loop

假设您连续多次执行上述命令,这会累加起来,并至少解释部分性能差异。

无论如何,我将您的问题解释为关于

subprocess

command
 的相对性能,而不是关于如何加快脚本速度。对于后一个问题,Swiss的回答比较好。


20
投票
这里似乎至少有两个独立的问题。

首先,您使用Popen的方式不当。以下是我看到的问题:

    用一个 Popen 生成多个进程。
  1. 将一个字符串作为参数传入,而不是拆分参数。
  2. 使用 shell 将文本传递给进程而不是内置的通信方法。
  3. 使用 shell 而不是直接生成进程。
这是您的代码的更正版本

from subprocess import PIPE args = ['nc', '-w', '1', 'server.com', 'port_num'] p = subprocess.Popen(args, stdin=PIPE, stdout=PIPE) output = p.communicate("get file.ext") print output[0]

其次,您建议手动运行时比通过子进程运行时结束得更快这一事实表明,这里的问题是您没有将正确的字符串传递给

nc

。可能发生的情况是服务器正在等待终止字符串来结束连接。如果您没有传递此参数,则连接可能会保持打开状态,直到超时。

手动运行

nc

,找出终止字符串是什么,然后更新传递给
communicate
的字符串。通过这些更改,它应该运行得更快。

© www.soinside.com 2019 - 2024. All rights reserved.