繁体   English   中英

提高 Python 模块导入的速度

[英]improving speed of Python module import

之前已经问过如何加速 Python 模块的导入的问题( 加速 python “导入”加载器Python -- 加速导入? ),但没有具体示例,也没有产生公认的解决方案。 因此,我将在这里再次讨论这个问题,但这次是一个具体的例子。

我有一个 Python 脚本,它从磁盘加载 3-D 图像堆栈,对其进行平滑处理,然后将其显示为电影。 当我想快速查看我的数据时,我从系统命令提示符调用这个脚本。 我对平滑数据所需的 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 机器之间移植。

不是问题的实际答案,而是关于如何使用 Python 3.7 和金枪鱼(我的一个小项目)分析导入速度的提示:

python3 -X importtime -c "import scipy" 2> scipy.log
tuna scipy.log

在此处输入图片说明

您可以构建一个简单的服务器/客户端,服务器运行不断地制作和更新绘图,而客户端只是通信下一个要处理的文件。

我根据socket模块文档中的基本示例编写了一个简单的服务器/客户端示例: http : //docs.python.org/2/library/socket.html#example

这是 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()

和客户端.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 秒。

您可以使用imp手动导入模块。 请参阅此处的文档

例如, import numpy as np可能可以写成

import imp
np = imp.load_module("numpy",None,"/usr/lib/python2.7/dist-packages/numpy",('','',5))

这将使 python sys.path浏览整个sys.path来查找所需的包。

也可以看看:

手动导入 gtk 失败:找不到模块

1.35 秒并不长,但我想如果您习惯于将其减半进行“快速检查”,那么也许看起来如此。

Andrea 建议了一个简单的客户端/服务器设置,但在我看来,您可以轻松调用对脚本的非常轻微的修改,并在工作时保持控制台窗口打开:

  • 调用脚本,该脚本执行导入然后等待输入
  • 最小化控制台窗口,切换到你的工作,无论如何:*做工作*
  • 再次选择控制台
  • 为脚本提供某种输入
  • 在没有导入开销的情况下接收结果
  • 在它愉快地等待输入时再次切换脚本

我假设您的脚本每次都是相同的,即您不需要每次都给它图像堆栈位置或任何特定命令(但这些也很容易做到!)。

示例 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。

我已经使这尽可能简单,但是它只需要很少的额外处理就可以很好地退出,根据输入做一些稍微不同的事情等。

您可以使用延迟导入,但这取决于您的用例。

如果它是一个应用程序,您可以运行 GUI 所需的模块,然后在加载窗口后,您可以导入所有模块。

如果它是一个模块并且用户不使用所有依赖项,则可以在函数内部导入。

[警告]我认为这是针对 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 

对于某些特定的图书馆,我认为这是必要的。

您还可以在__init__.py创建一些我们称之为 api 的

例如关于 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导入(如pathlibsubprocess等)不使用延迟加载,Python模块导入时间接近于零,也不需要从我的经验,优化...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM