Повышение скорости импорта модуля Python
Вопрос о том, как ускорить импорт модулей Python, был задан ранее (" Ускоряет загрузчик" импорта "" Python и Python - Speed Up Imports?), Но без конкретных примеров и не дал приемлемых решений. Поэтому я снова подниму этот вопрос, но уже на конкретном примере.
У меня есть скрипт Python, который загружает стек с трехмерного изображения, сглаживает его и отображает как фильм. Я вызываю этот скрипт из командной строки системы, когда хочу быстро просмотреть свои данные. Я в порядке с 700 мс, что требуется для сглаживания данных, поскольку это сопоставимо с MATLAB. Однако для импорта модулей требуется дополнительно 650 мс. Таким образом, с точки зрения пользователя, код Python работает на половине скорости.
Это серия модулей, которые я импортирую:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import scipy.ndimage
import scipy.signal
import sys
import os
Конечно, не все модули одинаково медленно импортируются. Главные виновники:
matplotlib.pyplot [300ms]
numpy [110ms]
scipy.signal [200ms]
Я экспериментировал с использованием from
, но это не быстрее. Так как Matplotlib является основным виновником и имеет репутацию медленного обновления экрана, я искал альтернативы. Одним из них является PyQtGraph, но для его импорта требуется 550 мс.
Мне известно об одном очевидном решении, которое заключается в вызове моей функции из интерактивного сеанса Python, а не из командной строки системы. Это хорошо, но это слишком похоже на MATLAB, я бы предпочел элегантность, когда моя функция доступна из системного приглашения.
Я новичок в Python, и я не уверен, как действовать на этом этапе. Поскольку я новичок, я буду благодарен за ссылки о том, как реализовать предложенные решения. В идеале я ищу простое решение (не так ли!), Потому что код должен быть переносимым между несколькими компьютерами Mac и Linux.
6 ответов
Вы могли бы создать простой сервер / клиент, сервер, работающий непрерывно, создавая и обновляя график, а клиент просто передавал следующий файл для обработки.
Я написал простой пример сервер / клиент, основанный на базовом примере из socket
модульные документы: http://docs.python.org/2/library/socket.html
вот server.py:
# expensive imports
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import scipy.ndimage
import scipy.signal
import sys
import os
# Echo server program
import socket
HOST = '' # Symbolic name meaning all available interfaces
PORT = 50007 # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
while 1:
conn, addr = s.accept()
print 'Connected by', addr
data = conn.recv(1024)
if not data: break
conn.sendall("PLOTTING:" + data)
# update plot
conn.close()
и client.py:
# Echo client program
import socket
import sys
HOST = '' # The remote host
PORT = 50007 # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall(sys.argv[1])
data = s.recv(1024)
s.close()
print 'Received', repr(data)
вы просто запускаете сервер:
python server.py
который выполняет импорт, тогда клиент просто отправляет через сокет имя файла нового файла для построения:
python client.py mytextfile.txt
Затем сервер обновляет сюжет.
На моем компьютере ваш импорт занимает 0,6 секунды, а работает client.py
0,03 секунды.
Не фактический ответ на вопрос, но подсказка о том, как можно профилировать скорость импорта с помощью Python 3.7 и тунца (мой небольшой проект):
python3.7 -X importtime -c "import foobar" 2> foobar.log
tuna foobar.log
Вместо этого вы можете импортировать свои модули вручную, используя imp
, Смотрите документацию здесь.
Например, import numpy as np
возможно, можно записать как
import imp
np = imp.load_module("numpy",None,"/usr/lib/python2.7/dist-packages/numpy",('','',5))
Это избавит питона от просмотра всего sys.path
найти нужные пакеты.
Смотрите также:
Вы можете использовать ленивый импорт, но это зависит от вашего варианта использования.
Если это приложение, вы можете запускать необходимые модули для графического интерфейса, а затем после загрузки окна вы можете импортировать все свои модули.
Если это модуль и пользователь не использует все зависимости, вы можете импортировать внутреннюю функцию.
[предупреждение] Я думаю, что это против pep8, и в некоторых местах это не рекомендуется, но вся причина этого в основном в удобочитаемости (хотя я могу ошибаться...) и в комплекте некоторых сборщиков (например, pyinstaller) (что можно решить, добавив отсутствует параметр зависимостей для спецификации)
Если вы используете ленивый импорт, используйте комментарии, чтобы пользователь знал, что есть дополнительные зависимости.
Пример:
import numpy as np
# Lazy imports
# import matplotlib.pyplot as plt
def plot():
import matplotlib.pyplot as plt
# Your function here
# This will be imported during runtime
Для некоторых конкретных библиотек я думаю, что это необходимо.
Вы также можете создать некоторые, назовем это API в
__init__.py
Например на scikit узнайте. Если вы импортируете sklearn, а затем вызываете какую-либо модель, она не будет найдена и вызовет ошибку. Тогда вам нужно быть более конкретным и импортировать непосредственно подмодуль. Хотя это может быть неудобно для пользователей, это хорошая практика, которая может значительно сократить время импорта.
Обычно 10% импортируемых библиотек стоят 90% времени импорта. Очень простой инструмент для анализа — line_profiler.
import line_profiler
import atexit
profile = line_profiler.LineProfiler()
atexit.register(profile.print_stats)
@profile
def profiled_function():
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
profiled_function()
Это дает результаты
Line # Hits Time Per Hit % Time Line Contents
==============================================================
20 @profile
21 def profiled_function():
22
23 1 2351852.0 2351852.0 6.5 import numpy as np
24 1 6545679.0 6545679.0 18.0 import pandas as pd
25 1 27485437.0 27485437.0 75.5 import matplotlib.pyplot as plt
75% из трех библиотек время импорта - это matplotlib (это не значит, что она плохо написана, просто ей нужно много всего для графического вывода)
Примечание:
Если вы импортируете библиотеку в один модуль, другой импорт ничего не стоит, он доступен глобально...
Еще одно примечание:
Если вы используете импорт непосредственно из python (например,
1,35 секунды не долго, но я полагаю, если вы привыкли вдвое меньше для "быстрой проверки", то, возможно, это так.
Андреа предлагает простую настройку клиент / сервер, но мне кажется, что вы могли бы так же легко вызвать очень небольшую модификацию вашего скрипта и оставить окно консоли открытым во время работы:
- Вызовите скрипт, который выполняет импорт, затем ожидает ввода
- Сверните окно консоли, переключитесь на свою работу, что угодно: *Do work*
- Выберите консоль снова
- Предоставить сценарию какой-то ввод
- Получите результаты без затрат на импорт
- Отключитесь от сценария снова, пока он с радостью ожидает ввода
Я предполагаю, что ваш скрипт каждый раз идентичен, то есть вам не нужно каждый раз указывать расположение стека изображений или какие-либо конкретные команды (но это также легко сделать!).
Пример RAAC's_Script.py:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import scipy.ndimage
import scipy.signal
import sys
import os
print('********* RAAC\'s Script Now Running *********')
while True: # Loops forever
# Display a message and wait for user to enter text followed by enter key.
# In this case, we're not expecting any text at all and if there is any it's ignored
input('Press Enter to test image stack...')
'''
*
*
**RAAC's Code Goes Here** (Make sure it's indented/inside the while loop!)
*
*
'''
Чтобы завершить сценарий, закройте окно консоли или нажмите Ctrl+ C.
Я сделал это настолько простым, насколько это возможно, но потребовалось бы совсем немного дополнительного, чтобы справиться с такими вещами, как приятный выход, делать немного другие вещи на основе ввода и т. Д.
Я сделал только базовый тест ниже, но он показывает, чтоrunpy
можно использовать для решения этой проблемы, когда вам нужно, чтобы весь скрипт Python работал быстрее (вы не хотите помещать какую-либо логику в test_server.py).
test_server.py
import socket
import time
import runpy
import matplotlib.pyplot
HOST = 'localhost'
PORT = 50007
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
serversocket.bind((HOST, PORT))
except:
print("Server is already running")
exit(1)
# Start server with maximum 100 connections
serversocket.listen(100)
while True:
connection, address = serversocket.accept()
buf = connection.recv(64)
if len(buf) > 0:
buf_str = str(buf.decode("utf-8"))
now = time.time()
runpy.run_path(path_name=buf_str)
after = time.time()
duration = after - now
print("I received " + buf_str + " script and it took " + str(duration) + " seconds to execute it")
test_client.py
import socket
import sys
HOST = 'localhost'
PORT = 50007
clientsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientsocket.connect((HOST, PORT))
message = sys.argv[1].encode()
clientsocket.send(message)
test_lag.py
import matplotlib.pyplot
Тестирование:
$ python3 test_client.py test_lag.py
I received test_lag.py script and it took 0.0002799034118652344 seconds to execute it
$ time python3 test_lag.py
real 0m0.624s
user 0m1.307s
sys 0m0.180s
Исходя из этого, модуль предварительно загружается для быстрого использования.