我有一个921000 x 3 numpy数组(921 k 3D点,每行一个点),我试图将其打包到protobuf消息中,但遇到了性能问题。我可以控制协议,并可以根据需要更改它。我使用Python 3.10和numpy 1.26.1。我使用protocol buffers是因为我使用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 ms,即921000点。
这与我的性能目标相差甚远,我的目标是每个副本大约12毫秒。我不确定我是否能接近这个目标,但是,有没有什么方法可以更快地做到这一点?
2条答案
按热度按时间holgip5t1#
如果你的目标只是通过gRPC发送一些东西到你控制的另一个程序,那么你实际上不必将所有东西都转换为“原生”protobuf消息;你可以使用protobuf
bytes
字段来存储另一种序列化格式,比如numpytobytes()
输出,或者Arrow。这会快得多。xj3cbfub2#
重复的字段写为[key:value][key:value][key:value]。对于基本类型,您可以设置[packed=true],这会将其更改为[key,length,array-of-values]。
使用单个memcopy可以更有效地实现打包的变体。