通过protobuf消息传递numpy数组的更快方法

wmvff8tz  于 2023-11-18  发布在  其他
关注(0)|答案(2)|浏览(183)

我有一个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毫秒。我不确定我是否能接近这个目标,但是,有没有什么方法可以更快地做到这一点?

holgip5t

holgip5t1#

如果你的目标只是通过gRPC发送一些东西到你控制的另一个程序,那么你实际上不必将所有东西都转换为“原生”protobuf消息;你可以使用protobuf bytes字段来存储另一种序列化格式,比如numpy tobytes()输出,或者Arrow。这会快得多。

xj3cbfub

xj3cbfub2#

重复的字段写为[key:value][key:value][key:value]。对于基本类型,您可以设置[packed=true],这会将其更改为[key,length,array-of-values]。
使用单个memcopy可以更有效地实现打包的变体。

相关问题