【问题标题】:improving speed of Python module import提高 Python 模块导入的速度
【发布时间】:2013-05-04 10:54:53
【问题描述】:

如何加快导入 Python 模块的问题之前已经提出过(Speeding up the python "import" loaderPython -- Speed Up Imports?),但没有具体示例,也没有产生公认的解决方案。因此,我将在这里再次讨论这个问题,但这次是一个具体的例子。

我有一个 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 模块的 .pyc 版本 - 加载这些版本要快一些。但即使有 pyc 文件,这些数字也是相当合理的。
  • 另外,如果sys.path 上有很多 .egg 目录,它会在每个目录中查找模块,这会减慢速度。使用分发包管理器或 pip 以更好的布局安装它们。不过,您不太可能获得大幅提速。
  • 我在较早的问题中注意到了 pyc 建议,但我不知道在哪里可以找到模块的 pyc 版本。现在我在 Mac 上。
  • 如果您使用的是 3.2 或更高版本,请在模块中查找 __pycache__ 目录(即 .../site-packages/matplotlib/__pycache__)。对于旧版本,.pyc 文件紧挨着.py 文件。它们通常是自动创建的,但在某些情况下 Python 没有存储模块的写入权限。
  • 也许获得显着加速的唯一方法是将模块缓存在内存中 - 使用 ramdisk 或使用正在运行的 Python 进程,您只需发出信号以重做计算。

标签: python performance import module


【解决方案1】:

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

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

【讨论】:

  • 整洁!这也应该在其他问题和答案中提及。
  • 我想尝试在我的项目中使用这个可视化工具来查看导入速度慢的地方,但我有一个问题。我的一堆模块位于不同的文件夹(相同的父文件夹)中,例如有Code\Widgets\ButtonCode\Gui\Color。我通过修改 sys.path 来导入它们:sys.path.insert(0,os.path.abspath(".."))。但是,当我在 CMD 中运行 tuna 程序时,我收到一条错误消息,指出 No module named ...。我相信它没有使用 sys.path 修改。你知道快速解决这个问题吗?谢谢
【解决方案2】:

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

我根据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()

和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 秒。

【讨论】:

  • 顺便说一句,对于绘图,您可以查看chacopypi.python.org/pypi/chaco
  • 谢谢,我认为您的解决方案可能是可行的方法。我已经将我的代码切换到 PyQtGraph,因为它在生成我正在生成的动态图方面比 Matplotlib 更快。查科当然也值得一看。
【解决方案3】:

您可以改为手动导入模块,使用imp。见documentation here

例如,import numpy as np 可以写成

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

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

另见:

Manually importing gtk fails: module not found

【讨论】:

    【解决方案4】:

    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。

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

    【讨论】:

      【解决方案5】:

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

      如果是应用程序,你可以为GUI运行必要的模块,然后在窗口加载后,你可以导入你所有的模块。

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

      [警告] 我认为它是针对 pep8 的,并且在某些地方不推荐,但这背后的所有原因主要是可读性(尽管我可能是错的......)和一些构建器(例如 pyinstaller)捆绑(可以通过添加缺少的依赖项参数来解决)规范)

      如果您使用惰性导入,请使用 cmets,以便用户知道存在额外的依赖项。

      例子:

      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 learn 上。如果您导入 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 模块导入时间接近于零,不需要根据我的经验进行优化...

      【讨论】:

        猜你喜欢
        • 2017-07-01
        • 2015-06-21
        • 1970-01-01
        • 1970-01-01
        • 2021-09-18
        • 2014-12-12
        • 2023-03-03
        • 2016-08-14
        • 1970-01-01
        相关资源
        最近更新 更多