Повышение скорости импорта модуля 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 найти нужные пакеты.

Смотрите также:

Импорт gtk вручную завершается неудачно: модуль не найден

Вы можете использовать ленивый импорт, но это зависит от вашего варианта использования.

Если это приложение, вы можете запускать необходимые модули для графического интерфейса, а затем после загрузки окна вы можете импортировать все свои модули.

Если это модуль и пользователь не использует все зависимости, вы можете импортировать внутреннюю функцию.

[предупреждение] Я думаю, что это против 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 (например, , и т. д.) не используйте ленивую загрузку, время импорта модулей 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

Исходя из этого, модуль предварительно загружается для быстрого использования.

Другие вопросы по тегам