Python IPC 与 matplotlib

Python IPC with matplotlib

项目描述:

将现有的 "C" 程序(主控件)连接到 Python GUI/Widget。为此,我使用了 FIFO。 C 程序旨在查看基于帧的遥测。

Python GUI 执行两个功能:

  1. Runs/creates 根据用户需要通过 GUI 小部件绘制(可能通过 matplotlib 创建)(单独的 .py 文件,不同用户编写的脚本)
  2. 在 python 绘图脚本创建后将帧号转发给它们,这样它们就可以 "update" 在从主程序中获得帧号后自行

我有几个问题——了解多处理与多线程的优缺点,参见此处:Multiprocessing vs Threading Python

实施注意事项:

  1. 我猜,在基于信号的架构中通过线程创建的图太多可能会在更新方面变得滞后。我不确定它们什么时候变成 CPU 绑定...大多数图会更新一些线系列,有些可能会更新图像。无论我选择哪种方式,无论创建方法如何,都可能会很卡。

  2. 我不确定打开 30 个 python 进程是什么,其中每个进程使用 matplotlib 对机器或其资源进行一两个绘图。我在我的系统上看到一个简单的 matplotlib 图有一个 117M 的 RSS(分配内存),所以我认为如果通过为每个图打开单独的进程来完成绘制 30 个图的单个用户不会限制系统内存。 (16 GB,32 核 Linux 盒子,同时有多个用户)

问题:

  1. 我应该通过线程还是进程打开图,一个比另一个更慢?
  2. 如果我使用线程,有人知道在单个线程上变得迟钝之前需要更新多少个 matplotlib 图形吗?
  3. 如果我将绘图创建为进程,我应该使用 multiprocessing 包吗?我猜这 API 可以直接在进程之间传递帧号?
  4. 考虑到我可以使用多进程,尝试通过 POpen 打开进程可能很愚蠢,对吧?我猜是这种情况,因为如果我这样做,我将不得不自己设置所有 piping/IPC,这会更有效吗?

所以我能够以两种方式实施这个项目——使用和不使用多进程。

  1. 我在 PyQt GUI 中有一个主进程,它有一个线程,该线程从控制 C 程序的帧号的管道中读取。
  2. 当用户选择图(.py 脚本)时,可以选择在一批图上按 "execute" 按钮,使它们保持在主进程中。从这一点开始,如果更新框架,则绘图将连续更新。减速几乎立即开始发生在少数地块之后,但对于 10-20 个简单的 time-series 地块来说并不令人望而却步。
  3. 有一个替代按钮允许使用另一个进程进行处理。我能够使用 POpen 和命名管道或多处理和多处理队列来做到这一点。最干净的方法是让我的其他进程创建 QObjects 并使用 pyqt 信号,其中每个其他进程都通过在该进程中创建 QApplications 结束,但我必须使用 ctx = mp.get_context('spawn') 在 Linux 上,因为默认情况下 Linux 使用一个分支,当我创建 QApplication 时它认为 QApplication 已经 运行 在主进程中。这是我能够获得可预测的多处理行为的唯一方法,其中所有 matplotlib 图都会在替代过程中更新。

我在网上看到 matplotlib 不是 thread-safe,但是,从等待队列读取的线程发出的 pyqt 槽似乎没问题。

我选择实施是为了让用户可以灵活地在同一进程中打开图或在另一个进程中打开批次图,而不是每个进程预先确定数量的图,因为我认为某些图可能具有复杂的更新,这可能是创建的,那些应该有自己的过程,并且可以这样选择。这也比简单图的每个进程图浪费更少@每个进程最少 100MB,同一进程中每个额外的图只需要 3MB 左右的额外内存。

最后一个细节是用户可能会非常快速地切换框架。我让接收进程在 non-blocking 守护线程中读取并清空队列,只获取最新信息。一旦发送了更新绘图的信号,绘图更新循环就会获取线程锁,读取守护进程在更新方法释放线程锁后再次能够发出更新。

实现基本思想的一些示例代码: