我有一个 921000 x 3 numpy 数组(921k 3D 点,每行一个点),我试图将其打包到 protobuf 消息中,但遇到了性能问题。我可以控制协议并可以根据需要更改它。我正在使用 Python 3.10 和 numpy 1.26.1。我使用协议缓冲区是因为我使用 gRPC。
对于第一次未优化的尝试,我使用了以下消息结构:
message Point {
float x = 1;
float y = 2;
float z = 3;
}
message BodyData {
int32 id = 1;
repeated Point points = 2;
}
一次将点打包为一个(令
data
为大 numpy 数组):
body = BodyData()
for row in data:
body.points.append(Point(x=row[0], y=row[1], z=row[2]))
这大约需要 1.6 秒,这太慢了。
在下一次尝试中,我放弃了
Point
结构,并决定将点作为 X/Y/Z 三元组的平面数组进行传输:
message Points {
repeated float xyz = 1;
}
message BodyData {
int32 id = 1;
Points points = 2;
}
我做了一些性能测试,以确定将 2D numpy 数组附加到列表的最快方法,并得到以下结果:
# Time: 80.1ms
points = []
points.extend(data.flatten())
# Time: 96.8ms
points = []
points.extend(data.reshape((data.shape[0] * data.shape[1],)))
# Time: 76.5ms - FASTEST
points = []
points.extend(data.flatten().tolist())
由此我确定
.extend(data.flatten().tolist())
是最快的。
但是,当我将其应用于 protobuf 消息时,它的速度减慢了:
# Time: 436.0ms
body = BodyData()
body.points.xyz.extend(data.flatten().tolist())
因此,我能够弄清楚如何将 numpy 数组打包到任何 protobuf 消息中的最快速度是 436 毫秒(921000 个点)。
这远远低于我的性能目标,即每个副本约 12 毫秒。我不确定我是否可以接近这个目标,但是有什么方法可以更快地做到这一点吗?
如果您的目标只是通过 gRPC 将某些内容发送到您控制的另一个程序,那么您实际上不必将所有内容都转换为“本机”protobuf 消息;您可以使用 protobuf
bytes
字段来存储其他序列化格式,例如 numpy tobytes()
输出或 Arrow。这样会快很多。