Python / OpenCV应用程序锁定问题

时间:2015-05-14 22:09:25

标签: python multithreading opencv multiprocessing

在64核Linux机器上运行的我的Python应用程序通常运行没有问题。然后经过一段随机的时间(通常约0.5到1.5天),我突然开始频繁停顿/锁定超过10秒!在这些锁定期间,系统CPU时间(即内核中的时间)可能超过90%(是的:所有64个核心中的90%,而不仅仅是一个CPU)。

我的应用程序通常会在一天内重新启动。重新启动应用程序无法解决问题。但是,重新启动机器会。

问题1 :10秒内可能导致90%的系统CPU时间?所有系统CPU时间都在我的父Python进程中,而不是在通过Python的多处理或其他进程创建的子进程中。所以这意味着在内核中花费10+秒的60多个线程的顺序。我甚至不确定这是Python问题还是Linux内核问题。

问题2 :重新启动修复问题必须是原因的一个重要线索。在我的应用程序重新启动之间,系统上的Linux资源可能会耗尽,但不会在重新启动之间耗尽,这可能会导致此问题停滞不前?

到目前为止我已经尝试过解决这个问题/想出来

下面我将提到很多的多处理。这是因为应用程序在一个循环中运行,而多处理仅在循环的一个部分中使用。在所有多处理调用完成后,高CPU几乎总是立即发生。我不确定这是暗示原因还是红鲱鱼。

  • 我的应用运行一个线程,该线程使用psutil每0.5秒注销一次进程和系统CPU统计信息。我已经通过top独立确认了报告的内容。
  • 我已经将我的应用程序从Python 2.7转换为Python 3.4,因为Python 3.2得到了一个新的GIL实现,而3.4已经重写了多处理。虽然这改善了一些事情但它并没有解决问题(请参阅我要离开的my previous SO question因为它仍然是一个有用的答案,如果不是全部答案)。
  • 我已经取代了操作系统。最初它是Ubuntu 12 LTS,现在它是CentOS 7.没有区别。
  • 事实证明,Python / Linux中的多线程和多处理冲突并不是一起推荐的,Python 3.4现在有forkserverspawn多处理上下文。我试过它们,没有区别。
  • 我检查了/ dev / shm,看看我的共享内存是否用完(Python 3.4用来管理多处理),没有
  • lsof输出列出了所有资源here
  • 很难在其他机器上进行测试,因为我运行了一个包含59个孩子的多进程池,而我没有任何其他64个核心机器只是躺在那里
  • 我无法使用线程而不是进程运行它,因为它因GIL而无法快速运行(因此我首先切换到多处理)
  • 我尝试在一个运行缓慢的线程上使用strace(它无法在所有线程上运行,因为它会使应用程序运行得太慢)。以下是我得到的,并没有告诉我太多。
  • ltrace不起作用,因为您无法在线程ID上使用-p。即使只是在主线程上运行它(没有-f)也会使应用程序变得非常慢,以至于问题不会出现。
  • 问题与负载无关。它有时在满载时运行良好,然后在半载时,它会突然出现这个问题。
  • 即使我每晚重启机器,问题也会每隔几天就会发生。

环境/笔记:

  • 从源代码编译的Python 3.4.3
  • CentOS 7完全是最新的。 uname -a:Linux 3.10.0-229.4.2.el7.x86_64#1 SMP Wed May 13 10:06:09 UTC 2015 x86_64 x86_64 x86_64 GNU / Linux(虽然此内核更新仅在今天应用)
  • 机器有128GB内存并且有足够的免费
  • 我使用numpy链接到ATLAS。我知道OpenBLAS与Python多处理冲突,但ATLAS没有,而且我已经尝试过Python 3.4 forkserverspawn的冲突。
  • 我使用的OpenCV也可以做很多并行工作
  • 我使用ctypes访问由相机制造商提供的C .so库
  • App以root身份运行(我链接到的C库的要求)
  • Python多处理Pool是在由if __name__ == "__main__":和主线程
  • 保护的代码中创建的

更新了strace结果

有几次我设法在100%'系统'CPU上运行一个线程。但只有一次我从中获得了任何有意义的东西。请参阅下面的电话:10:24:12.446614,需要1.4秒。鉴于它是相同的ID(0x7f05e4d1072c),你在大多数其他调用中看到我的猜测是这是Python的GIL同步。这个猜测有意义吗?如果是这样,那么问题是为什么等待需要1.4秒?有人不发布GIL吗?

10:24:12.375456 futex(0x7f05e4d1072c, FUTEX_WAIT, 2, NULL) = 0 <0.000823>
10:24:12.377076 futex(0x7f05e4d1072c, FUTEX_WAIT, 2, NULL) = 0 <0.002419>
10:24:12.379588 futex(0x7f05e4d1072c, FUTEX_WAIT, 2, NULL) = 0 <0.001898>
10:24:12.382324 sched_yield()           = 0 <0.000186>
10:24:12.382596 futex(0x7f05e4d1072c, FUTEX_WAIT, 2, NULL) = 0 <0.004023>
10:24:12.387029 sched_yield()           = 0 <0.000175>
10:24:12.387279 futex(0x7f05e4d1072c, FUTEX_WAIT, 2, NULL) = 0 <0.054431>
10:24:12.442018 sched_yield()           = 0 <0.000050>
10:24:12.442157 futex(0x7f05e4d1072c, FUTEX_WAIT, 2, NULL) = 0 <0.003902>
10:24:12.446168 futex(0x7f05e4d1022c, FUTEX_WAKE, 1) = 1 <0.000052>
10:24:12.446316 futex(0x7f05e4d11cac, FUTEX_WAKE, 1) = 1 <0.000056>
10:24:12.446614 futex(0x7f05e4d1072c, FUTEX_WAIT, 2, NULL) = 0 <1.439739>
10:24:13.886513 futex(0x7f05e4d1072c, FUTEX_WAIT, 2, NULL) = 0 <0.002381>
10:24:13.889079 sched_yield()           = 0 <0.000016>
10:24:13.889135 sched_yield()           = 0 <0.000049>
10:24:13.889244 futex(0x7f05e4d1072c, FUTEX_WAIT, 2, NULL) = 0 <0.032761>
10:24:13.922147 sched_yield()           = 0 <0.000020>
10:24:13.922285 sched_yield()           = 0 <0.000104>
10:24:13.923628 futex(0x7f05e4d1072c, FUTEX_WAIT, 2, NULL) = 0 <0.002320>
10:24:13.926090 sched_yield()           = 0 <0.000018>
10:24:13.926244 futex(0x7f05e4d1072c, FUTEX_WAIT, 2, NULL) = 0 <0.000265>
10:24:13.926667 sched_yield()           = 0 <0.000027>
10:24:13.926775 sched_yield()           = 0 <0.000042>
10:24:13.926964 futex(0x7f05e4d1072c, FUTEX_WAIT, 2, NULL) = -1 EAGAIN (Resource temporarily unavailable) <0.000117>
10:24:13.927241 futex(0x7f05e4d110ac, FUTEX_WAKE, 1) = 1 <0.000099>
10:24:13.927455 futex(0x7f05e4d11d2c, FUTEX_WAKE, 1) = 1 <0.000186>
10:24:13.931318 futex(0x7f05e4d1072c, FUTEX_WAIT, 2, NULL) = 0 <0.000678>

1 个答案:

答案 0 :(得分:0)

我已经设法从gdb获得了一个线程转储,直到40多个线程显示100%&#39; system&#39; CPU时间。

这里的回溯对于每个线程都是相同的:

#0  0x00007fffebe9b407 in cv::ThresholdRunner::operator()(cv::Range const&) const () from /usr/local/lib/libopencv_imgproc.so.3.0
#1  0x00007fffecfe44a0 in tbb::interface6::internal::start_for<tbb::blocked_range<int>, (anonymous namespace)::ProxyLoopBody, tbb::auto_partitioner const>::execute() () from /usr/local/lib/libopencv_core.so.3.0
#2  0x00007fffe967496a in tbb::internal::custom_scheduler<tbb::internal::IntelSchedulerTraits>::local_wait_for_all(tbb::task&, tbb::task*) () from /lib64/libtbb.so.2
#3  0x00007fffe96705a6 in tbb::internal::arena::process(tbb::internal::generic_scheduler&) () from /lib64/libtbb.so.2
#4  0x00007fffe966fc6b in tbb::internal::market::process(rml::job&) () from /lib64/libtbb.so.2
#5  0x00007fffe966d65f in tbb::internal::rml::private_worker::run() () from /lib64/libtbb.so.2
#6  0x00007fffe966d859 in tbb::internal::rml::private_worker::thread_routine(void*) () from /lib64/libtbb.so.2
#7  0x00007ffff76e9df5 in start_thread () from /lib64/libpthread.so.0
#8  0x00007ffff6d0e1ad in clone () from /lib64/libc.so.6

我最初的问题是将Python和Linux放在前面和中心,但问题似乎在于TBB和/或OpenCV。由于OpenCV与TBB的使用如此广泛,我认为它还必须以某种方式涉及与我的特定环境的相互作用。也许是因为它是64核机器?

我已经重新编译了OpenCV并关闭了TBB,到目前为止问题还没有再出现。但我的应用程序现在运行速度较慢。

我有posted this as a bug to OpenCV,并会用来自该内容的任何内容更新此答案。