将微服务(使用 ZeroMQ、C 和 Python 进程)从 64 位硬件移动到 32 位硬件后的延迟,但标称 cpu 使用
Latency after moving micro-service (using ZeroMQ, C, & Python processes) from 64 bit hardware to 32 bit hardware, but nominal cpu usage
我有两个用 C 编写的进程,它们设置 PUSH/PULL
ZeroMQ 套接字和 Python 进程中的两个线程,镜像 PUSH/PULL
套接字。每秒大约有 80 - 300 条轻量级(<30 字节)消息从 C 进程发送到 Python 进程,10-30 条类似消息从 Python 进程发送到 C 进程。
我在 64 位 ARMv8(Ubuntu 基础)和 AMD64(Ubuntu 18.04)上 运行 这些服务,没有明显的延迟。我在基于 Linux 的 32 位系统上尝试了 运行 完全相同的服务,并且震惊地看到消息在 30 多秒后才通过,即使在终止 C 服务之后也是如此。检查 CPU 使用情况时,30-40% 的使用率非常平稳,似乎不是瓶颈。
我的 ZeroMQ 套接字设置在系统之间没有变化,我将 LINGER
设置为 0,我尝试 RCVTIMEO
在 0 到 100 毫秒之间,我尝试在 0 之间改变 BACKLOG
和 50,两者都没有区别。我尝试使用多个 IO 线程并设置套接字线程亲和性,但也无济于事。对于 PUSH
套接字,我将连接 tcp://localhost:#####
上的套接字并将 PULL
套接字绑定到 tcp://*:#####
。我也用了ipc:///tmp/...
,消息正在发送和接收,但是在32位系统上仍然存在延迟。
我调查了接收消息之间的其他 Python 个步骤,它们似乎最多不会超过 1 毫秒。当我为 socket.recv(0)
计时时,它高达 0.02 秒,即使该套接字的 RCVTIMEO
设置为 0。
为什么我会在新的 32 位平台上而不是在其他平台上看到这种行为,有什么建议吗?我可能找错地方了吗?
这里有一些代码可以帮助解释:
连接和_recv()
class-方法大致描述如下:
def _connect(self):
self.context = zmq.Context(4)
self.sink = self.context.socket(zmq.PULL)
self.sink.setsockopt(zmq.LINGER, 0)
self.sink.setsockopt(zmq.RCVTIMEO, 100)
self.sink.setsockopt(zmq.BACKLOG, 0)
self.sink.bind("tcp://*:55755")
def _recv(self):
while True:
msg = None
try:
msg = self.sink.recv(0) # Use blocking or zmq.NOBLOCK, still appears to be slow
except zmq.Error
... meaningful exception handle here
# This last step, when timed usually takes less than a millisecond to process
if msg:
msg_dict = utils.bytestream_to_dict(msg) # unpacking step (negligible)
if msg_dict:
self.parser.parse(msg_dict) # parser is a dict of callbacks also negligible
在 C 进程端
zmq_init (4);
void *context = zmq_ctx_new ();
/* Connect the Sender */
void *vent = zmq_socket (context, ZMQ_PUSH);
int timeo = 0;
int timeo_ret = zmq_setsockopt(vent, ZMQ_SNDTIMEO, &timeo, sizeof(timeo));
if (timeo_ret != 0)
error("Failed to set ZMQ recv timeout because %s", zmq_strerror(errno));
int linger = 100;
int linger_ret = zmq_setsockopt(vent, ZMQ_LINGER, &linger, sizeof(linger));
if (linger_ret != 0)
error("Failed to set ZMQ linger because %s", zmq_strerror(errno));
if (zmq_connect (vent, vent_port) == 0)
info("Successfully initialized ZeroMQ ventilator on %s", vent_port);
else {
error("Failed to initialize %s ZeroMQ ventilator with error %s", sink_port,
zmq_strerror(errno));
ret = 1;
}
...
/* When a message needs to be sent it's instantly hitting this where msg is a char* */
ret = zmq_send(vent, msg, msg_len, ZMQ_NOBLOCK);
On docker 运行 在目标 32 位系统上
lstopo - -v --no-io
Machine (P#0 local=1019216KB total=1019216KB HardwareName="Freescale i.MX6 Quad/DualLite (Device Tree)" HardwareRevision=0000 HardwareSerial=0000000000000000 Backend=Linux LinuxCgroup=/docker/d2b0a3b3a5eedb7e10fc89fdee6e8493716a359597ac61350801cc302d79b8c0 OSName=Linux OSRelease=3.10.54-dey+g441c8d4 OSVersion="#1 SMP PREEMPT RT Tue Jan 28 12:11:37 CST 2020" HostName=db1docker Architecture=armv7l hwlocVersion=1.11.12 ProcessName=lstopo)
Package L#0 (P#0 CPUModel="ARMv7 Processor rev 10 (v7l)" CPUImplementer=0x41 CPUArchitecture=7 CPUVariant=0x2 CPUPart=0xc09 CPURevision=10)
Core L#0 (P#0)
PU L#0 (P#0)
Core L#1 (P#1)
PU L#1 (P#1)
Core L#2 (P#2)
PU L#2 (P#2)
Core L#3 (P#3)
PU L#3 (P#3)
depth 0: 1 Machine (type #1)
depth 1: 1 Package (type #3)
depth 2: 4 Core (type #5)
depth 3: 4 PU (type #6)
编辑:
通过禁用几乎所有其他工作线程,我们能够使目标机器上的延迟消失。
Q : roughly 80 - 300 light weight (<30 bytes) messages per second being sent from the C process to the Python process, and 10-30 similar messages from the Python process to the C process.
a )关于从 python 向 C 发送任何消息的信息为零(不包含在发布的源代码中,只有 C PUSH
-es 到 python )
b ) 300 [Hz]
< 30 B 有效负载在 ZeroMQ 能力方面不算什么
c ) python 是,从那时起(而且几乎可以肯定会一直如此),一个纯粹的[SERIAL]
在意义上,无论有多少Thread
-实例,所以任何执行都必须等到它获得 POSACK'ed GIL 锁所有权,阻止任何其他工作,从而重新设置一个纯- [SERIAL]
一个接一个地工作……但要增加 GIL 锁握手的额外成本。
d ) 在同一硬件平台上给定所有进程 运行 (请参阅 tcp://
localhost
...
specified ), 没有理由生成多达 ( 4 + 4 )-IO 线程(其中 python 不能 "harness"-em 一次只读一个线程(慢动作),只给出 4-CPU-cores 上面由 [= 报告20=] 摘录:
Machine (995MB)
+Package L#0
Core L#0 +PU L#0 (P#0)
Core L#1 +PU L#1 (P#1)
Core L#2 +PU L#2 (P#2)
Core L#3 +PU L#3 (P#3)
e) ISO-OSI-L2/L3 参数调整是有意义的,但毕竟更大的延迟源被削减了。
f) 最后但同样重要的是,运行 python pystone
测试(在两个原始平台上和目标硬件平台),以查看 i.MX6
-CPU-powered python 的实际相对性能,以便能够进行比较苹果对苹果
Running pystone on the target machine results in: This machine benchmarks at 10188.5 pystones/second and my host machine is 274264 pystones/second
所以,部署到 i.MX6
-target 的问题不仅仅是它的 32 位 O/S 本身,还有 27x
超额订阅 IO 线程的处理速度较慢 (线程 4+4 多于 4-CPU-cores)不会改善消息流。
更好的视野,由 lstopo-no-graphics -.ascii
提供
┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Machine (31876MB) │
│ │
│ ┌────────────────────────────────────────────────────────────┐ ┌───────────────────────────┐ │
│ │ Package P#0 │ ├┤╶─┬─────┼┤╶───────┤ PCI 10ae:1F44 │ │
│ │ │ │ │ │ │
│ │ ┌────────────────────────────────────────────────────────┐ │ │ │ ┌────────────┐ ┌───────┐ │ │
│ │ │ L3 (8192KB) │ │ │ │ │ renderD128 │ │ card0 │ │ │
│ │ └────────────────────────────────────────────────────────┘ │ │ │ └────────────┘ └───────┘ │ │
│ │ │ │ │ │ │
│ │ ┌──────────────────────────┐ ┌──────────────────────────┐ │ │ │ ┌────────────┐ │ │
│ │ │ L2 (2048KB) │ │ L2 (2048KB) │ │ │ │ │ controlD64 │ │ │
│ │ └──────────────────────────┘ └──────────────────────────┘ │ │ │ └────────────┘ │ │
│ │ │ │ └───────────────────────────┘ │
│ │ ┌──────────────────────────┐ ┌──────────────────────────┐ │ │ │
│ │ │ L1i (64KB) │ │ L1i (64KB) │ │ │ ┌───────────────┐ │
│ │ └──────────────────────────┘ └──────────────────────────┘ │ ├─────┼┤╶───────┤ PCI 10bc:8268 │ │
│ │ │ │ │ │ │
│ │ ┌────────────┐┌────────────┐ ┌────────────┐┌────────────┐ │ │ │ ┌────────┐ │ │
│ │ │ L1d (16KB) ││ L1d (16KB) │ │ L1d (16KB) ││ L1d (16KB) │ │ │ │ │ enp2s0 │ │ │
│ │ └────────────┘└────────────┘ └────────────┘└────────────┘ │ │ │ └────────┘ │ │
│ │ │ │ └───────────────┘ │
│ │ ┌────────────┐┌────────────┐ ┌────────────┐┌────────────┐ │ │ │
│ │ │ Core P#0 ││ Core P#1 │ │ Core P#2 ││ Core P#3 │ │ │ ┌──────────────────┐ │
│ │ │ ││ │ │ ││ │ │ ├─────┤ PCI 1002:4790 │ │
│ │ │ ┌────────┐ ││ ┌────────┐ │ │ ┌────────┐ ││ ┌────────┐ │ │ │ │ │ │
│ │ │ │ PU P#0 │ ││ │ PU P#1 │ │ │ │ PU P#2 │ ││ │ PU P#3 │ │ │ │ │ ┌─────┐ ┌─────┐ │ │
│ │ │ └────────┘ ││ └────────┘ │ │ └────────┘ ││ └────────┘ │ │ │ │ │ sr0 │ │ sda │ │ │
│ │ └────────────┘└────────────┘ └────────────┘└────────────┘ │ │ │ └─────┘ └─────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │ └──────────────────┘ │
│ │ │
│ │ ┌───────────────┐ │
│ └─────┤ PCI 1002:479c │ │
│ └───────────────┘ │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
我有两个用 C 编写的进程,它们设置 PUSH/PULL
ZeroMQ 套接字和 Python 进程中的两个线程,镜像 PUSH/PULL
套接字。每秒大约有 80 - 300 条轻量级(<30 字节)消息从 C 进程发送到 Python 进程,10-30 条类似消息从 Python 进程发送到 C 进程。
我在 64 位 ARMv8(Ubuntu 基础)和 AMD64(Ubuntu 18.04)上 运行 这些服务,没有明显的延迟。我在基于 Linux 的 32 位系统上尝试了 运行 完全相同的服务,并且震惊地看到消息在 30 多秒后才通过,即使在终止 C 服务之后也是如此。检查 CPU 使用情况时,30-40% 的使用率非常平稳,似乎不是瓶颈。
我的 ZeroMQ 套接字设置在系统之间没有变化,我将 LINGER
设置为 0,我尝试 RCVTIMEO
在 0 到 100 毫秒之间,我尝试在 0 之间改变 BACKLOG
和 50,两者都没有区别。我尝试使用多个 IO 线程并设置套接字线程亲和性,但也无济于事。对于 PUSH
套接字,我将连接 tcp://localhost:#####
上的套接字并将 PULL
套接字绑定到 tcp://*:#####
。我也用了ipc:///tmp/...
,消息正在发送和接收,但是在32位系统上仍然存在延迟。
我调查了接收消息之间的其他 Python 个步骤,它们似乎最多不会超过 1 毫秒。当我为 socket.recv(0)
计时时,它高达 0.02 秒,即使该套接字的 RCVTIMEO
设置为 0。
为什么我会在新的 32 位平台上而不是在其他平台上看到这种行为,有什么建议吗?我可能找错地方了吗?
这里有一些代码可以帮助解释:
连接和_recv()
class-方法大致描述如下:
def _connect(self):
self.context = zmq.Context(4)
self.sink = self.context.socket(zmq.PULL)
self.sink.setsockopt(zmq.LINGER, 0)
self.sink.setsockopt(zmq.RCVTIMEO, 100)
self.sink.setsockopt(zmq.BACKLOG, 0)
self.sink.bind("tcp://*:55755")
def _recv(self):
while True:
msg = None
try:
msg = self.sink.recv(0) # Use blocking or zmq.NOBLOCK, still appears to be slow
except zmq.Error
... meaningful exception handle here
# This last step, when timed usually takes less than a millisecond to process
if msg:
msg_dict = utils.bytestream_to_dict(msg) # unpacking step (negligible)
if msg_dict:
self.parser.parse(msg_dict) # parser is a dict of callbacks also negligible
在 C 进程端
zmq_init (4);
void *context = zmq_ctx_new ();
/* Connect the Sender */
void *vent = zmq_socket (context, ZMQ_PUSH);
int timeo = 0;
int timeo_ret = zmq_setsockopt(vent, ZMQ_SNDTIMEO, &timeo, sizeof(timeo));
if (timeo_ret != 0)
error("Failed to set ZMQ recv timeout because %s", zmq_strerror(errno));
int linger = 100;
int linger_ret = zmq_setsockopt(vent, ZMQ_LINGER, &linger, sizeof(linger));
if (linger_ret != 0)
error("Failed to set ZMQ linger because %s", zmq_strerror(errno));
if (zmq_connect (vent, vent_port) == 0)
info("Successfully initialized ZeroMQ ventilator on %s", vent_port);
else {
error("Failed to initialize %s ZeroMQ ventilator with error %s", sink_port,
zmq_strerror(errno));
ret = 1;
}
...
/* When a message needs to be sent it's instantly hitting this where msg is a char* */
ret = zmq_send(vent, msg, msg_len, ZMQ_NOBLOCK);
On docker 运行 在目标 32 位系统上 lstopo - -v --no-io
Machine (P#0 local=1019216KB total=1019216KB HardwareName="Freescale i.MX6 Quad/DualLite (Device Tree)" HardwareRevision=0000 HardwareSerial=0000000000000000 Backend=Linux LinuxCgroup=/docker/d2b0a3b3a5eedb7e10fc89fdee6e8493716a359597ac61350801cc302d79b8c0 OSName=Linux OSRelease=3.10.54-dey+g441c8d4 OSVersion="#1 SMP PREEMPT RT Tue Jan 28 12:11:37 CST 2020" HostName=db1docker Architecture=armv7l hwlocVersion=1.11.12 ProcessName=lstopo)
Package L#0 (P#0 CPUModel="ARMv7 Processor rev 10 (v7l)" CPUImplementer=0x41 CPUArchitecture=7 CPUVariant=0x2 CPUPart=0xc09 CPURevision=10)
Core L#0 (P#0)
PU L#0 (P#0)
Core L#1 (P#1)
PU L#1 (P#1)
Core L#2 (P#2)
PU L#2 (P#2)
Core L#3 (P#3)
PU L#3 (P#3)
depth 0: 1 Machine (type #1)
depth 1: 1 Package (type #3)
depth 2: 4 Core (type #5)
depth 3: 4 PU (type #6)
编辑:
通过禁用几乎所有其他工作线程,我们能够使目标机器上的延迟消失。
Q : roughly 80 - 300 light weight (<30 bytes) messages per second being sent from the C process to the Python process, and 10-30 similar messages from the Python process to the C process.
a )关于从 python 向 C 发送任何消息的信息为零(不包含在发布的源代码中,只有 C PUSH
-es 到 python )
b ) 300 [Hz]
< 30 B 有效负载在 ZeroMQ 能力方面不算什么
c ) python 是,从那时起(而且几乎可以肯定会一直如此),一个纯粹的[SERIAL]
在意义上,无论有多少Thread
-实例,所以任何执行都必须等到它获得 POSACK'ed GIL 锁所有权,阻止任何其他工作,从而重新设置一个纯- [SERIAL]
一个接一个地工作……但要增加 GIL 锁握手的额外成本。
d ) 在同一硬件平台上给定所有进程 运行 (请参阅 tcp://
localhost
...
specified ), 没有理由生成多达 ( 4 + 4 )-IO 线程(其中 python 不能 "harness"-em 一次只读一个线程(慢动作),只给出 4-CPU-cores 上面由 [= 报告20=] 摘录:
Machine (995MB)
+Package L#0
Core L#0 +PU L#0 (P#0)
Core L#1 +PU L#1 (P#1)
Core L#2 +PU L#2 (P#2)
Core L#3 +PU L#3 (P#3)
e) ISO-OSI-L2/L3 参数调整是有意义的,但毕竟更大的延迟源被削减了。
f) 最后但同样重要的是,运行 python pystone
测试(在两个原始平台上和目标硬件平台),以查看 i.MX6
-CPU-powered python 的实际相对性能,以便能够进行比较苹果对苹果
Running pystone on the target machine results in: This machine benchmarks at 10188.5 pystones/second and my host machine is 274264 pystones/second
所以,部署到 i.MX6
-target 的问题不仅仅是它的 32 位 O/S 本身,还有 27x
超额订阅 IO 线程的处理速度较慢 (线程 4+4 多于 4-CPU-cores)不会改善消息流。
更好的视野,由 lstopo-no-graphics -.ascii
┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Machine (31876MB) │
│ │
│ ┌────────────────────────────────────────────────────────────┐ ┌───────────────────────────┐ │
│ │ Package P#0 │ ├┤╶─┬─────┼┤╶───────┤ PCI 10ae:1F44 │ │
│ │ │ │ │ │ │
│ │ ┌────────────────────────────────────────────────────────┐ │ │ │ ┌────────────┐ ┌───────┐ │ │
│ │ │ L3 (8192KB) │ │ │ │ │ renderD128 │ │ card0 │ │ │
│ │ └────────────────────────────────────────────────────────┘ │ │ │ └────────────┘ └───────┘ │ │
│ │ │ │ │ │ │
│ │ ┌──────────────────────────┐ ┌──────────────────────────┐ │ │ │ ┌────────────┐ │ │
│ │ │ L2 (2048KB) │ │ L2 (2048KB) │ │ │ │ │ controlD64 │ │ │
│ │ └──────────────────────────┘ └──────────────────────────┘ │ │ │ └────────────┘ │ │
│ │ │ │ └───────────────────────────┘ │
│ │ ┌──────────────────────────┐ ┌──────────────────────────┐ │ │ │
│ │ │ L1i (64KB) │ │ L1i (64KB) │ │ │ ┌───────────────┐ │
│ │ └──────────────────────────┘ └──────────────────────────┘ │ ├─────┼┤╶───────┤ PCI 10bc:8268 │ │
│ │ │ │ │ │ │
│ │ ┌────────────┐┌────────────┐ ┌────────────┐┌────────────┐ │ │ │ ┌────────┐ │ │
│ │ │ L1d (16KB) ││ L1d (16KB) │ │ L1d (16KB) ││ L1d (16KB) │ │ │ │ │ enp2s0 │ │ │
│ │ └────────────┘└────────────┘ └────────────┘└────────────┘ │ │ │ └────────┘ │ │
│ │ │ │ └───────────────┘ │
│ │ ┌────────────┐┌────────────┐ ┌────────────┐┌────────────┐ │ │ │
│ │ │ Core P#0 ││ Core P#1 │ │ Core P#2 ││ Core P#3 │ │ │ ┌──────────────────┐ │
│ │ │ ││ │ │ ││ │ │ ├─────┤ PCI 1002:4790 │ │
│ │ │ ┌────────┐ ││ ┌────────┐ │ │ ┌────────┐ ││ ┌────────┐ │ │ │ │ │ │
│ │ │ │ PU P#0 │ ││ │ PU P#1 │ │ │ │ PU P#2 │ ││ │ PU P#3 │ │ │ │ │ ┌─────┐ ┌─────┐ │ │
│ │ │ └────────┘ ││ └────────┘ │ │ └────────┘ ││ └────────┘ │ │ │ │ │ sr0 │ │ sda │ │ │
│ │ └────────────┘└────────────┘ └────────────┘└────────────┘ │ │ │ └─────┘ └─────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │ └──────────────────┘ │
│ │ │
│ │ ┌───────────────┐ │
│ └─────┤ PCI 1002:479c │ │
│ └───────────────┘ │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘