【发布时间】:2026-01-15 12:00:01
【问题描述】:
我有两个性能截然不同的简单代码块:
void testProto() {
demo::Person* person = new demo::Person();
person->set_data(data, BUFFER_LEN);
}
void testMemcpy() {
demo::Person* person = new demo::Person();
memcpy(memcpy_dest, data, BUFFER_LEN);
}
proto 文件如下所示:
message Person {
bytes data = 1;
}
根据Protobuf encoding docs,设置长度分隔数据看起来就像复制带有几个标题字节的数据一样简单。为什么第一个函数比第二个函数花费的时间多 5-10 倍?
我制作了一个完整的、易于运行的示例here。
附加说明/上下文:
- Flatbuffers 和 protobufs 的替代品没有这个问题
-
Here's my attempt 使用调试器。我不能低于
Set方法。 - 这种性能对我来说很重要的原因是我正在将一些高吞吐量/低延迟的网络代码转换为 protobuf。由于我在每个数据包中多次运行上述代码,因此 protobufs 会严重影响性能。
- 我在 -O3 下运行,但即使在 -O0 下,仍然存在巨大的性能差异
- 函数调用开销不是问题,因为性能不佳会随着数据的大小而变化。函数调用只是一个恒定的开销。
- 我尝试了多种方法来确保 memcpy 不会被优化掉(-O0,使用数组)。我非常有信心 memcpy 没有被优化掉。
- 我在
testMemcpy中尝试了malloc。这让事情变慢了一点,但仍然至少差了 5 倍。 - 我在 Macbook M1 和 Ubuntu Intel 机器上试过这个
【问题讨论】:
-
你在使用优化代码吗?
-
是的,您可以在我链接的示例中看到。我正在运行 -O3。
-
所有相关细节都需要在问题本身中,而不是在外部链接中
-
memcpy复制是什么以及复制到哪里?这显然不是demo::Person。也许memcpy只是因为没有人查看结果而被优化掉了? -
Protobuf 不仅仅是字节复制。例如,您的 memcpy 已经有一个缓冲区(静态!)要复制到。如果我修改你的基准,那么 memcpy 也必须分配一个存储字节的位置,就像 protobuf 一样,那么差异就会变得更小。尽管如此,proto 还是比 memcpy 慢约 2 倍——或者具体而言,每次迭代慢约 127 微秒。鉴于它还管理一个分配器领域,varint 编码长度,并跟踪其他消息头,这似乎有点合理。如果你的瓶颈是复制单字节缓冲区,protobuf 不是最快的。
标签: c++ protocol-buffers protobuf-c