Ответ 1
Как я упоминал в комментарии, когда вы не можете заставить cProfile
работать извне, вы можете часто использовать его внутри. Это не так сложно.
Например, когда я запускаю с -m cProfile
в моем Python 2.7, я получаю те же результаты, что и вы. Но когда я вручную использую вашу примерную программу:
import fileinput
import cProfile
pr = cProfile.Profile()
pr.enable()
for line in fileinput.input():
for i in range(10):
y = int(line.strip()) + int(line.strip())
pr.disable()
pr.print_stats(sort='time')
... вот что я получаю:
22002533 function calls (22001691 primitive calls) in 3.352 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
20000000 2.326 0.000 2.326 0.000 {method 'strip' of 'str' objects}
1000001 0.646 0.000 0.700 0.000 fileinput.py:243(next)
1000000 0.325 0.000 0.325 0.000 {range}
842 0.042 0.000 0.042 0.000 {method 'readlines' of 'file' objects}
1684/842 0.013 0.000 0.055 0.000 fileinput.py:292(readline)
1 0.000 0.000 0.000 0.000 fileinput.py:197(__init__)
1 0.000 0.000 0.000 0.000 fileinput.py:91(input)
1 0.000 0.000 0.000 0.000 {isinstance}
1 0.000 0.000 0.000 0.000 fileinput.py:266(nextfile)
1 0.000 0.000 0.000 0.000 fileinput.py:240(__iter__)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
Это намного полезнее: он говорит вам, что вы, вероятно, уже ожидали, что более половины вашего времени потрачено на вызов str.strip()
.
Также обратите внимание, что если вы не можете отредактировать файл, содержащий код, который вы хотите профилировать (mwe.py
), вы всегда можете сделать это:
import cProfile
pr = cProfile.Profile()
pr.enable()
import mwe
pr.disable()
pr.print_stats(sort='time')
Даже это не всегда работает. Если ваша программа вызывает exit()
, например, вам придется использовать обертку try:
/finally:
и/или atexit
. И это он называет os._exit()
, или segfaults, вы, вероятно, полностью hosed. Но это не очень распространено.
Однако, что-то, что я обнаружил позже: если вы переместите весь код из глобальной области видимости, -m cProfile
, похоже, работает, по крайней мере, для этого случая. Например:
import fileinput
def f():
for line in fileinput.input():
for i in range(10):
y = int(line.strip()) + int(line.strip())
f()
Теперь вывод из -m cProfile
включает, среди прочего:
2000000 4,819 0,000 4,819 0,000: 0 (полоса) 100001 0.288 0.000 0.295 0.000 fileinput.py:243(next)
Я понятия не имею, почему это также делало его в два раза медленнее... или, может быть, это просто эффект кеша; это было несколько минут с тех пор, как я в последний раз его запускал, и я много раз просматривал веб-страницы. Но это не важно, важно то, что большую часть времени взимается с разумных мест.
Но если я изменю это, чтобы переместить внешний цикл на глобальный уровень, а только его тело в функцию, большую часть времени снова исчезает.
Другая альтернатива, которую я бы не предложил, кроме как в крайнем случае...
Я замечаю, что если я использую профиль вместо cProfile, он работает как внутри, так и снаружи, время зарядки до нужных вызовов. Однако эти вызовы также примерно в 5 раз медленнее. И, похоже, дополнительные 10 секунд постоянных накладных расходов (которые заряжаются до import profile
, если они используются внутри, независимо от того, что включено в строке 1, если они используются снаружи). Итак, чтобы узнать, что split
использует 70% моего времени, вместо того, чтобы ждать 4 секунды и делать 2.326/3.352, я должен ждать 27 секунд и делать 10.93/(26.34 - 10.01). Не очень весело...
Последнее: я получаю те же результаты с результатами построения CPython 3.4 dev, когда они используются внутри, и все они загружаются в первую строку кода при использовании извне. Но PyPy 2.2/2.7.3 и PyPy3 2.1b1/3.2.3 оба, кажется, дают мне правильные результаты с -m cProfile
. Это может означать, что PyPy cProfile
подделывается поверх profile
, потому что код с чистым Python достаточно быстр.
В любом случае, если кто-то может понять/объяснить, почему -m cProfile
не работает, это было бы здорово... но в остальном это обычно отлично подходит для решения проблемы.