我终于在我的代码中发现了一个性能瓶颈,但对于原因是什么感到困惑.为了解决这个问题,我改变了所有的调用numpy.zeros_like来代替使用numpy.zeros.但为什么zeros_likesooooo慢得多?
例如(注意e-05在zeros电话):
>>> timeit.timeit('np.zeros((12488, 7588, 3), np.uint8)', 'import numpy as np', number = 10)
5.2928924560546875e-05
>>> timeit.timeit('np.zeros_like(x)', 'import numpy as np; x = np.zeros((12488, 7588, 3), np.uint8)', number = 10)
1.4402990341186523
Run Code Online (Sandbox Code Playgroud)
但奇怪的是,写入创建的数组zeros明显比使用以下创建的数组慢zeros_like:
>>> timeit.timeit('x[100:-100, 100:-100] = 1', 'import numpy as np; x = np.zeros((12488, 7588, 3), np.uint8)', number = 10)
0.4310588836669922
>>> timeit.timeit('x[100:-100, 100:-100] = 1', 'import numpy as np; x = np.zeros_like(np.zeros((12488, 7588, 3), np.uint8))', …Run Code Online (Sandbox Code Playgroud) 在64核Linux机器上运行的我的Python应用程序通常运行没有问题.然后经过一段随机的时间(通常约0.5到1.5天),我突然开始频繁停顿/锁定超过10秒!在这些锁定期间,系统CPU时间(即内核中的时间)可能超过90%(是的:所有64个核心中的90%,而不仅仅是一个CPU).
我的应用程序经常在一天内重新启动.重新启动应用程序无法解决问题.但是,重新启动机器会.
问题1:什么可能导致90%的系统CPU时间10秒?所有系统CPU时间都在我的父Python进程中,而不是在通过Python的多处理或其他进程创建的子进程中.所以这意味着在内核中花费10+秒的60多个线程的顺序.我甚至不确定这是Python问题还是Linux内核问题.
问题2:重新启动修复问题必须是一个重要的线索.在我的应用程序重新启动之间,系统上的Linux资源可能会耗尽,但不会在重新启动之间耗尽,这可能会导致此问题陷入困境?
下面我将提到很多处理.这是因为应用程序在一个循环中运行,而多处理仅在循环的一个部分中使用.所有多处理调用完成后,高CPU几乎总会立即发生.我不确定这是暗示原因还是红鲱鱼.
psutil每0.5秒注销一次进程和系统CPU统计信息.我已经独立确认了它的报道内容top.forkserver和spawn多语境.我试过它们,没有区别.lsof输出列出所有资源在这里strace一个运行缓慢的线程(它不能在所有线程上运行,因为它会使应用程序运行得太慢).以下是我得到的,并没有告诉我太多.ltrace不起作用,因为你不能使用-p线程ID.即使只是在主线程上运行它(否-f)会使应用程序变得如此缓慢以至于问题不会出现.环境/注意事项:
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(尽管此内核更新仅在今天应用)forkserver和spawn我已经试过了.首先,我不确定是否应将此作为Ubuntu问题发布或在此处.但我猜它更像是一个Python问题,而不是一个OS问题.
我的Python应用程序在64核AMD服务器上运行在Ubuntu之上.它通过调用.so来从网络上的5 GigE摄像机中提取图像ctypes,然后处理它们.我看到我的应用程序经常暂停,导致相机的帧被外部相机库丢弃.
为了调试这个,我使用了流行的psutilPython包,我在一个单独的线程中每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
Run Code Online (Sandbox Code Playgroud)
我不知道为什么在匹配高流程系统使用之前报告一行高OS系统使用情况.两者相比,64核中的26.4 = 41%.此时,我的应用程序经历了大约3.5秒的暂停(由我的CPU信息记录线程使用OpenCV确定,cv2.getTickCount()以及Python日志记录输出中的时间戳跳转)导致多个摄像机帧被丢弃.
发生这种情况时,我还记录了我的进程的每个线程的CPU信息.对于上面的示例,25个线程在"系统"CPU利用率为0.9时运行,并且在0.6处运行更多,这与上面26.4的进程的总数相匹配.那时大约有183个线程在运行.
在使用多处理池(它用于短脉冲串)之后,这种暂停通常似乎很接近,但每次使用池时都不会发生.此外,如果我将需要在池外进行的处理量减半,则不会发生相机跳过.
问题:如何确定操作系统'系统'/内核时间突然出现的原因?为什么会在Python应用程序中发生?
更重要的是:任何想法为什么会发生这种情况以及如何避免它?
笔记:
upstartrespawn在upstart中使用)并且这种情况每天发生多次,因此不是由于长时间运行,我也看到这在进程开始后很快发生python linux performance python-multithreading python-multiprocessing
我喜欢typing.NamedTuplePython 3.6中的。但是通常情况下,namedtuple包含非哈希属性,而我想将其用作dict键或set成员。如果一个namedtuple类使用对象标识(id()for __eq__和__hash__)有意义,那么将这些方法添加到该类中就可以正常工作。
但是,现在我的代码在多个地方都有这个模式,我想摆脱样板__eq__和__hash__方法定义。我知道namedtuple'不是普通班,我还无法弄清楚如何使它正常工作。
这是我尝试过的:
from typing import NamedTuple
class ObjectIdentityMixin:
def __eq__(self, other):
return self is other
def __hash__(self):
return id(self)
class TestMixinFirst(ObjectIdentityMixin, NamedTuple):
a: int
print(TestMixinFirst(1) == TestMixinFirst(1)) # Prints True, so not using my __eq__
class TestMixinSecond(NamedTuple, ObjectIdentityMixin):
b: int
print(TestMixinSecond(2) == TestMixinSecond(2)) # Prints True as well
class ObjectIdentityNamedTuple(NamedTuple):
def __eq__(self, other):
return self is …Run Code Online (Sandbox Code Playgroud)