为什么我的 Python 应用程序因 'system'/内核 CPU 时间而停滞不前



首先,我不确定我是否应该将其作为 Ubuntu 问题发布或在此处发布。但我猜这更像是一个Python问题,而不是一个操作系统问题。

我的 Python 应用程序在 64 核 AMD 服务器上的 Ubuntu 上运行。它通过网络从 5 台 GigE 摄像机中提取图像,方法是通过 ctypes 调用 .so,然后对其进行处理。我看到我的应用程序中经常暂停,导致外部相机库中的帧被删除。

为了调试它,我使用了流行的 psutil Python 包,我使用它在单独的线程中每 0.2 秒注销一次 CPU 统计信息。我在那个线程中睡了0.2秒,当睡眠时间长得多时,我还会看到相机帧被丢弃。我见过长达 17 秒的暂停!我的大部分处理要么在OpenCV或Numpy(两者都发布GIL)中,要么在应用程序的一部分中multiprocessing.Pool 有 59 个进程(这是为了绕过 Python GIL)。

当暂停发生时,我的调试日志记录显示我的许多进程线程上的"系统"(即内核)CPU 时间非常高。

例如。我看到 CPU 时间如下(通常每 0.2 秒一次),然后突然大幅跳跃("进程"数字以 CPU 利用率表示,即 1 个 CPU 完全使用将是 1,Linux top显示 123% 将是 1.2):

Process user | Process system | OS system % | OS idle %
19.9         | 10.5           | 6           | 74 
5.6          | 2.3            | 4           | 87
6.8          | 1.7            | 11          | 75
4.6          | 5.5            | 43          | 52
0.5          | 26.4           | 4           | 90

我不知道为什么在匹配高进程系统使用率之前报告一行高操作系统使用率。两者匹配,因为 64 个内核中的 26.4 个 = 41%。此时,我的应用程序经历了大约 3.5 秒的暂停(由我的 CPU 信息日志记录线程使用 OpenCV 的cv2.getTickCount()以及 Python 日志记录输出中时间戳的跳跃确定)导致多个相机帧丢失。

发生这种情况时,我还记录了进程每个线程的 CPU 信息。对于上面的示例,25 个线程以 0.9 的"系统"CPU 利用率运行,还有一些线程以 0.6 的 CPU 利用率运行,这与上述进程 26.4 的总数相匹配。此时大约有 183 个线程在运行。

这种暂停通常在使用多处理池(用于短时间突发)后发生,但绝不是每次使用池时都会发生。此外,如果我将需要在池外进行的处理量减半,则不会发生相机跳过。

问题:如何确定为什么操作系统"系统"/内核时间突然飙升?为什么在 Python 应用程序中会发生这种情况?

更重要的是:为什么会发生这种情况以及如何避免它的任何想法?

笔记:

  • 这以 root 身份运行(不幸的是,它必须用于相机库)来自upstart
  • 当相机关闭时,应用程序重新启动(在upstart中使用respawn),并且这种情况每天发生多次,因此不是由于长时间运行,我也看到这种情况在该过程开始后不久就发生了
  • 这是一遍
  • 又一遍地运行的相同代码,这不是由于运行我的代码的不同分支
  • 目前nice为 -2,我尝试删除nice而没有影响
  • 优麒麟 12.04.5 LTS
  • 蟒蛇 2.7
  • 机器有128GB的内存,我还没有使用过

好的。我有自己的问题的答案。是的,我花了 3 个多月的时间才走到这一步。

这似乎是Python中的GIL捶打,这是大规模"系统"CPU峰值和相关暂停的原因。这是对捶打的来源的一个很好的解释。那次介绍也为我指明了正确的方向。

Python 3.2 引入了一个新的 GIL 实现来避免这种破坏。结果可以通过一个简单的线程示例显示(取自上面的演示文稿):

from threading import Thread
import psutil
def countdown():
    n = 100000000
    while n > 0:
        n -= 1
t1 = Thread(target=countdown)
t2 = Thread(target=countdown)
t1.start(); t2.start()
t1.join(); t2.join()
print(psutil.Process().cpu_times())    

在装有Python 2.7.9的Macbook Pro上,它使用14.7秒的"用户"CPU和13.2秒的"系统"CPU。

Python 3.4 使用了 15.0 秒的"用户"(略多),但只有 0.2 秒的"系统"。

因此,GIL 仍然存在,它仍然只与代码单线程运行时一样快,但它避免了 Python 2 的所有 GIL 争用,这些争用表现为内核("系统")CPU 时间。我认为,这种争论是导致原始问题的原因。

更新

CPU问题的另一个原因是OpenCV/TBB。在这个SO问题中完全记录。

最新更新