Модуль подпроцесса Python намного медленнее команд (не рекомендуется)

Поэтому я написал сценарий, который обращается к группе серверов с помощью nc в командной строке, и первоначально я использовал модуль команд Python и вызывал command.getoutput (), и сценарий выполнялся примерно через 45 секунд. Поскольку команды устарели, я хочу изменить все на использование модуля подпроцесса, но теперь сценарию требуется 2m45s для запуска. У кого-нибудь есть идея, почему это будет?

Что у меня было раньше:

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]

Заранее спасибо за помощь!

 wroniasty04 июн. 2012 г., 23:49
сколько времени занимает команда, когда вы запускаете из оболочки?
 thedrick05 июн. 2012 г., 00:01
Нет, не из того, что я вижу. Также, когда я запускаю те же вещи (внутри интерпретатора Python), как и выше, но со временем, подпроцесс на самом деле немного быстрее. Не уверен, почему это будет медленнее, если он будет включен в мой код ...
 wroniasty05 июн. 2012 г., 00:03
похоже проблема не в модуле подпроцесса ...
 thedrick04 июн. 2012 г., 23:55
До тех пор, пока сервер не истекает время ожидания, результат почти мгновенный, реальный 0m0.274s, пользователь 0m0.071s, sys 0m0.134s.
 wroniasty04 июн. 2012 г., 23:58
выполнять другие команды, такие как «ls», «uname -a»; и т. д. также занимает много времени для выполнения с модулем подпроцесса?

Ответы на вопрос(2)

Решение Вопроса

subprocess быть медленнее, чемcommand, Не имея смысла предполагать, что этоonly причина того, что ваш скрипт работает медленно, вы должны взглянуть на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

швейцарцы предлагает несколько хороших улучшений, которые помогут улучшить производительность вашего скрипта. Но даже после их применения учтите, чтоsubprocess являетсяstill помедленнее.

>>> %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, а не о том, как ускорить ваш сценарий. На последний вопрос швейцарский ответ лучше.

 05 июн. 2012 г., 00:27
Я подозреваю, что это на самом деле не проблема здесь.
 05 июн. 2012 г., 04:45
@ user1436110, просто чтобы уточнить: ты загружаешь большие файлы? О каком размере? А ты звонишьPopen несколько раз в вашем сценарии? Является ли среда выполнения, которую вы упоминаете в комментариях, временем выполнения для одного вызоваnc или много?
 05 июн. 2012 г., 01:27
@senderle: Это может быть правдой, что подпроцесс медленнее. Однако разница в 1 или 2 мс чрезвычайно мала и не учитывает разницу в 2+ минуты, о которой упоминается в первоначальном вопросе.
 05 июн. 2012 г., 01:11
@ Свисс, я согласен, что лучше не использовать оболочку. Я предполагаю, что предположил, что user1436110 делал это по необходимости, и ответил соответственно. При более внимательном рассмотрении я вижу, что это, вероятно, не так. Но даже после применения предложенных вами улучшений,subprocess медленнее. Смотрите новые сроки выше.
 thedrick05 июн. 2012 г., 00:19
Ах, спасибо! Я рад, что я не просто схожу с ума. Я на Python 2.6, поэтому у меня даже нет возможности использовать check_output.

Во-первых, вы неправильно используете Popen. Вот проблемы, которые я вижу:

Spawning multiple processes with one Popen. Passing one string in as args instead of splitting args. Using the shell to pass text to process rather than the builtin communicate method. Using shell rather than directly spawning processes.

Вот исправленная версия вашего кода

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, С этими изменениями он должен работать намного быстрее.

 16 июн. 2018 г., 18:24
Есть ли причина для 2? Почему строковая версия считается неэффективной?
 15 янв. 2019 г., 17:09
Привет @Mooncrater, я не OP, но я считаю, что рекомендация для 2), потому что вы хотите избежать использования Shell = True. Поэтому вам нужно добавить команду в виде списка, а не строки.

Ваш ответ на вопрос