使用derive(Eq)与derive(Serialize)比较结构体的性能差异探究
向量比较:
Eq::eq与JSON序列化字符串比较的性能差异分析 测试背景
我好奇调用Eq::eq比较两个大型向量,与将两个向量序列化为字符串后再比较输出字符串的速度差异。在release模式下针对含10万个简单结构体的向量完成基础性能测试,结果如下:
- 调用
Eq::eq(x, y)比较:20ms - 用
serde_json::to_value(x) == serde_json::to_value(y)比较:172ms - 用
serde_json::to_string(x) == serde_json::to_string(y)比较:34ms
这些结果出乎意料——完全序列化为字符串的速度接近derive实现的Eq::eq,因此想明确两个问题:性能测试是否存在缺陷?若无误,为何Eq::eq(x, y)未体现预期的速度优势?
一、测试是否存在缺陷
你的测试逻辑没有明显硬伤,但有几个细节会影响结果的严谨性:
- 单次测试的偶然性:仅一次测试的结果容易受系统调度、内存缓存状态影响,建议增加测试次数取平均值,或使用
criterion这类专业基准测试框架消除误差。 - 测试对象的简单性:你使用的是仅含基本类型的简单结构体,
Eq::eq的工作量本身极小,序列化的开销被大幅稀释。如果换成嵌套层次深、包含复杂类型(如长字符串、枚举)的结构体,两者的性能差距会显著拉大。 - JSON序列化的优化差异:
serde_json在release模式下有大量编译优化,to_string直接生成字节流,复用缓冲区且批量处理;而to_value需要构建中间的Value对象,额外的内存分配和结构转换导致开销骤增,这也解释了to_string比to_value快5倍的原因。
二、为何Eq::eq的速度优势不明显
核心原因是两者的实际工作量差距远小于直觉判断,再加上序列化的优化进一步缩小了差距:
Eq::eq的实际开销:derive生成的Eq::eq会逐个比较结构体的每个字段,对于10万个元素的向量,需要遍历两个向量共20万个结构体的所有字段。如果结构体有多个字段,这个过程会产生大量随机内存访问,当数据未被CPU缓存命中时,耗时会显著增加。- 字符串比较的短路特性:字符串比较是短路操作——一旦发现某个字符不同就立即终止。如果测试中两个向量存在靠前的差异,字符串比较的实际工作量会远小于完整序列化的开销;即使向量完全相同,
serde_json的序列化速度极快,且现代CPU的SIMD指令能高效处理连续字符串,让整个过程的耗时接近内存遍历的开销。 - 内存布局与缓存效率:
Eq::eq需要随机访问结构体的各个字段,而JSON序列化是顺序写入字符串,顺序内存访问的缓存命中率远高于随机访问,CPU能更高效地流水线处理,进一步缩小了与Eq::eq的耗时差距。
补充验证建议
- 测试存在前置差异的向量:修改向量中第一个元素的字段,观察两种比较方式的耗时变化——字符串比较会在第一个差异处停止,耗时会大幅降低;
Eq::eq同样会短路停止,此时两者的差距可能反转。 - 测试复杂结构体:使用包含嵌套结构体、长字符串、枚举的对象,此时
Eq::eq的逐个字段比较开销会远小于序列化的开销,性能差距会清晰体现。 - 改用专业基准工具:使用
criterion进行测试,它会自动处理热身、多次运行统计、误差分析,结果更具可信度。
内容的提问来源于stack exchange,提问作者Venryx
相关产品推荐
相关产品推荐

